Skip to content

Commit ff3426e

Browse files
committed
add rename functionaltity in backend and frontend content manager
1 parent ef20987 commit ff3426e

File tree

3 files changed

+158
-16
lines changed

3 files changed

+158
-16
lines changed

jupyter_drives/manager.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,14 +309,36 @@ async def save_file(self, drive_name, path, content):
309309
}
310310
return response
311311

312-
async def rename_file(self, drive_name, path, **kwargs):
312+
async def rename_file(self, drive_name, path, new_path):
313313
"""Rename a file.
314314
315315
Args:
316316
drive_name: name of drive where file is located
317317
path: path of file
318318
"""
319-
print('Rename file function called.')
319+
data = {}
320+
try:
321+
# eliminate leading and trailing backslashes
322+
path = path.strip('/')
323+
324+
await obs.rename_async(self._content_managers[drive_name], path, new_path)
325+
metadata = await obs.head_async(self._content_managers[drive_name], new_path)
326+
327+
data = {
328+
"path": new_path,
329+
"last_modified": metadata["last_modified"].isoformat(),
330+
"size": metadata["size"]
331+
}
332+
except Exception as e:
333+
raise tornado.web.HTTPError(
334+
status_code= httpx.codes.BAD_REQUEST,
335+
reason=f"The following error occured when renaming the object: {e}",
336+
)
337+
338+
response = {
339+
"data": data
340+
}
341+
return response
320342

321343
async def delete_file(self, drive_name, path):
322344
"""Delete an object.

src/contents.ts

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ import {
99
getContents,
1010
mountDrive,
1111
createObject,
12+
checkObject,
1213
deleteObjects,
13-
countObjectNameAppearances
14+
countObjectNameAppearances,
15+
renameObjects
1416
} from './requests';
1517

1618
let data: Contents.IModel = {
@@ -441,25 +443,51 @@ export class Drive implements Contents.IDrive {
441443
newLocalPath: string,
442444
options: Contents.ICreateOptions = {}
443445
): Promise<Contents.IModel> {
444-
/*const settings = this.serverSettings;
445-
const url = this._getUrl(oldLocalPath);
446-
const init = {
447-
method: 'PATCH',
448-
body: JSON.stringify({ path: newLocalPath })
449-
};
450-
const response = await ServerConnection.makeRequest(url, init, settings);
451-
if (response.status !== 200) {
452-
const err = await ServerConnection.ResponseError.create(response);
453-
throw err;
446+
// extract current drive name
447+
const currentDrive = this._drivesList.filter(
448+
x =>
449+
x.name ===
450+
(oldLocalPath.indexOf('/') !== -1
451+
? oldLocalPath.substring(0, oldLocalPath.indexOf('/'))
452+
: oldLocalPath)
453+
)[0];
454+
455+
// eliminate drive name from path
456+
const relativePath =
457+
oldLocalPath.indexOf('/') !== -1
458+
? oldLocalPath.substring(oldLocalPath.indexOf('/') + 1)
459+
: '';
460+
const newRelativePath =
461+
newLocalPath.indexOf('/') !== -1
462+
? newLocalPath.substring(newLocalPath.indexOf('/') + 1)
463+
: '';
464+
465+
// extract new file name
466+
let newFileName = PathExt.basename(newLocalPath);
467+
468+
try {
469+
// check if object with chosen name already exists
470+
await checkObject(currentDrive.name, {
471+
path: newLocalPath
472+
});
473+
newFileName = await this.incrementName(newLocalPath, this._name);
474+
} catch (error) {
475+
// HEAD request failed for this file name, continue, as name doesn't already exist.
476+
} finally {
477+
data = await renameObjects(currentDrive.name, {
478+
path: relativePath,
479+
newPath: newRelativePath,
480+
newFileName: newFileName,
481+
registeredFileTypes: this._registeredFileTypes
482+
});
454483
}
455-
const data = await response.json();*/
456484

485+
Contents.validateContentsModel(data);
457486
this._fileChanged.emit({
458487
type: 'rename',
459488
oldValue: { path: oldLocalPath },
460-
newValue: { path: newLocalPath }
489+
newValue: data
461490
});
462-
Contents.validateContentsModel(data);
463491
return data;
464492
}
465493

src/requests.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,77 @@ export async function deleteObjects(
264264
return Private.deleteSingleObject(driveName, options.path);
265265
}
266266

267+
/**
268+
* Rename an object.
269+
*
270+
* @param driveName
271+
* @param options.path The path of object.
272+
*
273+
* @returns A promise which resolves with the contents model.
274+
*/
275+
export async function renameObjects(
276+
driveName: string,
277+
options: {
278+
path: string;
279+
newPath: string;
280+
newFileName: string;
281+
registeredFileTypes: IRegisteredFileTypes;
282+
}
283+
) {
284+
const formattedNewPath =
285+
options.newPath.substring(0, options.newPath.lastIndexOf('/') + 1) +
286+
options.newFileName;
287+
288+
const [fileType, fileMimeType, fileFormat] = getFileType(
289+
PathExt.extname(PathExt.basename(options.newFileName)),
290+
options.registeredFileTypes
291+
);
292+
293+
// get list of contents with given prefix (path)
294+
const response = await requestAPI<any>(
295+
'drives/' + driveName + '/' + options.path,
296+
'GET'
297+
);
298+
299+
// renaming contents of a directory
300+
if (response.data.length !== undefined && response.data.length !== 0) {
301+
await Promise.all(
302+
response.data.map(async (c: any) => {
303+
const remainingFilePath = c.path.substring(options.path.length);
304+
Private.renameSingleObject(
305+
driveName,
306+
PathExt.join(options.path, remainingFilePath),
307+
PathExt.join(formattedNewPath, remainingFilePath)
308+
);
309+
})
310+
);
311+
}
312+
// always rename the object (file or main directory)
313+
try {
314+
const renamedObject = await Private.renameSingleObject(
315+
driveName,
316+
options.path,
317+
formattedNewPath
318+
);
319+
data = {
320+
name: options.newFileName,
321+
path: PathExt.join(driveName, formattedNewPath),
322+
last_modified: renamedObject.data.last_modified,
323+
created: '',
324+
content: PathExt.extname(options.newFileName) !== '' ? null : [], // TODO: add dir check
325+
format: fileFormat as Contents.FileFormat,
326+
mimetype: fileMimeType,
327+
size: renamedObject.data.size,
328+
writable: true,
329+
type: fileType
330+
};
331+
} catch (error) {
332+
// renaming failed if directory didn't exist and was only part of a path
333+
}
334+
335+
return data;
336+
}
337+
267338
/**
268339
* Check existance of an object.
269340
*
@@ -335,4 +406,25 @@ namespace Private {
335406
) {
336407
await requestAPI<any>('drives/' + driveName + '/' + objectPath, 'DELETE');
337408
}
409+
410+
/**
411+
* Helping function for renaming files inside
412+
* a directory, in the case of deleting the directory.
413+
*
414+
* @param driveName
415+
* @param objectPath complete path of object to delete
416+
*/
417+
export async function renameSingleObject(
418+
driveName: string,
419+
objectPath: string,
420+
newObjectPath: string
421+
) {
422+
return await requestAPI<any>(
423+
'drives/' + driveName + '/' + objectPath,
424+
'PATCH',
425+
{
426+
new_path: newObjectPath
427+
}
428+
);
429+
}
338430
}

0 commit comments

Comments
 (0)