diff --git a/src/contents.ts b/src/contents.ts index 1b19653..08f864a 100644 --- a/src/contents.ts +++ b/src/contents.ts @@ -2,6 +2,7 @@ import { JupyterFrontEnd } from '@jupyterlab/application'; import { Signal, ISignal } from '@lumino/signaling'; import { Contents, ServerConnection } from '@jupyterlab/services'; import { PathExt } from '@jupyterlab/coreutils'; +import { Notification } from '@jupyterlab/apputils'; import { extractCurrentDrive, @@ -27,6 +28,7 @@ import { includeDrive, addExternalDrive } from './requests'; +import { DrivesResponseError } from './handler'; export class Drive implements Contents.IDrive { /** @@ -186,15 +188,26 @@ export class Drive implements Contents.IDrive { */ async getDownloadUrl(path: string): Promise { let link = ''; - if (path !== '') { - const currentDrive = extractCurrentDrive(path, this._drivesList); + let warning = ''; + let error = ''; + try { + if (path !== '') { + const currentDrive = extractCurrentDrive(path, this._drivesList); + link = await presignedLink(currentDrive.name, { + path: formatPath(path) + }); + } else { + // download URL for drive not supported + warning = 'Operation not supported.'; + } + } catch (err) { + error = (err as DrivesResponseError).message; + } - link = await presignedLink(currentDrive.name, { - path: formatPath(path) + if (error || warning) { + Notification.emit(warning ?? error, warning ? 'warning' : 'error', { + autoClose: 5000 }); - } else { - // download URL for drive not supported - console.warn('Operation not supported.'); } return link; @@ -215,53 +228,62 @@ export class Drive implements Contents.IDrive { localPath: string, options?: Contents.IFetchOptions ): Promise { + let error: string = ''; let data: Contents.IModel; if (localPath !== '') { const currentDrive = extractCurrentDrive(localPath, this._drivesList); - // when accessed the first time, mount drive if (currentDrive.mounted === false) { try { const driveName = currentDrive.name; - await mountDrive(driveName, { + const mounting = await mountDrive(driveName, { provider: currentDrive.provider }); - this._drivesList.filter(x => x.name === driveName)[0].mounted = true; + if (mounting && mounting.error) { + error = mounting.error.message; + } else { + this._drivesList.filter(x => x.name === driveName)[0].mounted = + true; + } } catch (e) { - // it will give an error if drive is already mounted + // it will give an error if drive is already mounted. } } - const currentPath = formatPath(localPath); - const result = await getContents(currentDrive.name, { - path: currentPath, - registeredFileTypes: this._registeredFileTypes - }); + try { + const currentPath = formatPath(localPath); + const result = await getContents(currentDrive.name, { + path: currentPath, + registeredFileTypes: this._registeredFileTypes + }); - data = { - name: result.isDir - ? currentPath - ? PathExt.basename(currentPath) - : currentDrive.name - : PathExt.basename(currentPath), - path: PathExt.join( - currentDrive.name, - result.isDir + data = { + name: result.isDir ? currentPath - ? currentPath + '/' - : '' - : result.response.data.path - ), - last_modified: result.isDir ? '' : result.response.data.last_modified, - created: '', - content: result.isDir ? result.files : result.response.data.content, - format: result.isDir ? 'json' : result.format!, - mimetype: result.isDir ? '' : result.mimetype!, - size: result.isDir ? undefined : result.response.data.size, - writable: true, - type: result.isDir ? 'directory' : result.type! - }; + ? PathExt.basename(currentPath) + : currentDrive.name + : PathExt.basename(currentPath), + path: PathExt.join( + currentDrive.name, + result.isDir + ? currentPath + ? currentPath + '/' + : '' + : result.response.data.path + ), + last_modified: result.isDir ? '' : result.response.data.last_modified, + created: '', + content: result.isDir ? result.files : result.response.data.content, + format: result.isDir ? 'json' : result.format!, + mimetype: result.isDir ? '' : result.mimetype!, + size: result.isDir ? undefined : result.response.data.size, + writable: true, + type: result.isDir ? 'directory' : result.type! + }; + } catch (err) { + error = (err as DrivesResponseError).message; + } } else { // retriving list of contents from root // in our case: list available drives @@ -283,11 +305,8 @@ export class Drive implements Contents.IDrive { type: 'directory' }); } - } catch (error) { - console.log( - 'Failed loading available drives list, with error: ', - error - ); + } catch (err) { + error = (err as DrivesResponseError).message; } data = { @@ -304,8 +323,14 @@ export class Drive implements Contents.IDrive { }; } - Contents.validateContentsModel(data); - return data; + if (error) { + Notification.emit(error, 'error', { + autoClose: 5000 + }); + } + + Contents.validateContentsModel(data!); + return data!; } /** @@ -332,68 +357,82 @@ export class Drive implements Contents.IDrive { type: '' }; const path = options.path ?? ''; + let error = ''; + let warning = ''; if (path !== '') { - const currentDrive = extractCurrentDrive(path, this._drivesList); - - // eliminate drive name from path - const relativePath = - path.indexOf('/') !== -1 ? path.substring(path.indexOf('/') + 1) : ''; - - // get current list of contents of drive - const result = await getContents(currentDrive.name, { - path: relativePath, - registeredFileTypes: this._registeredFileTypes - }); - - const old_data: Contents.IModel = { - name: relativePath ? PathExt.basename(relativePath) : currentDrive.name, - path: PathExt.join( - currentDrive.name, - relativePath ? relativePath + '/' : '' - ), - last_modified: '', - created: '', - content: result.files, - format: 'json'!, - mimetype: '', - size: undefined, - writable: true, - type: 'directory' - }; + try { + const currentDrive = extractCurrentDrive(path, this._drivesList); - if (options.type !== undefined) { - // get incremented untitled name - const name = this.incrementUntitledName(old_data, options); - const currentPath = relativePath - ? PathExt.join(relativePath, name) - : name; + // eliminate drive name from path + const relativePath = + path.indexOf('/') !== -1 ? path.substring(path.indexOf('/') + 1) : ''; - const result = await createObject(currentDrive.name, { - name: name, - path: currentPath, - type: options.type, + // get current list of contents of drive + const result = await getContents(currentDrive.name, { + path: relativePath, registeredFileTypes: this._registeredFileTypes }); - data = { - name: name, - path: PathExt.join(currentDrive.name, currentPath), - last_modified: result.response.data.last_modified, - created: result.response.data.last_modified, - content: result.response.data.content, - format: result.format, - mimetype: result.mimetype, - size: result.response.data.size, + const old_data: Contents.IModel = { + name: relativePath + ? PathExt.basename(relativePath) + : currentDrive.name, + path: PathExt.join( + currentDrive.name, + relativePath ? relativePath + '/' : '' + ), + last_modified: '', + created: '', + content: result.files, + format: 'json'!, + mimetype: '', + size: undefined, writable: true, - type: result.type + type: 'directory' }; - } else { - console.warn('Type of new element is undefined'); + + if (options.type !== undefined) { + // get incremented untitled name + const name = this.incrementUntitledName(old_data, options); + const currentPath = relativePath + ? PathExt.join(relativePath, name) + : name; + + const result = await createObject(currentDrive.name, { + name: name, + path: currentPath, + type: options.type, + registeredFileTypes: this._registeredFileTypes + }); + + data = { + name: name, + path: PathExt.join(currentDrive.name, currentPath), + last_modified: result.response.data.last_modified, + created: result.response.data.last_modified, + content: result.response.data.content, + format: result.format, + mimetype: result.mimetype, + size: result.response.data.size, + writable: true, + type: result.type + }; + } else { + warning = 'Type of new element is undefined'; + } + } catch (err) { + error = (err as DrivesResponseError).message; } } else { // create new element at root would mean creating a new drive - console.warn('Operation not supported.'); + warning = 'Operation not supported.'; + } + + if (error || warning) { + Notification.emit(warning ?? error, warning ? 'warning' : 'error', { + autoClose: 5000 + }); } Contents.validateContentsModel(data); @@ -467,9 +506,15 @@ export class Drive implements Contents.IDrive { async delete(localPath: string): Promise { const currentDrive = extractCurrentDrive(localPath, this._drivesList); - await deleteObjects(currentDrive.name, { - path: formatPath(localPath) - }); + try { + await deleteObjects(currentDrive.name, { + path: formatPath(localPath) + }); + } catch (err) { + Notification.emit((err as DrivesResponseError).message, 'error', { + autoClose: 5000 + }); + } this._fileChanged.emit({ type: 'delete', @@ -496,6 +541,8 @@ export class Drive implements Contents.IDrive { newLocalPath: string, options: Contents.ICreateOptions = {} ): Promise { + let error = ''; + let warning = ''; let data: Contents.IModel = { name: '', path: '', @@ -509,54 +556,69 @@ export class Drive implements Contents.IDrive { type: '' }; if (oldLocalPath !== '') { - const currentDrive = extractCurrentDrive(oldLocalPath, this._drivesList); + try { + const currentDrive = extractCurrentDrive( + oldLocalPath, + this._drivesList + ); - // eliminate drive name from path - const relativePath = formatPath(oldLocalPath); - const newRelativePath = formatPath(newLocalPath); + // eliminate drive name from path + const relativePath = formatPath(oldLocalPath); + const newRelativePath = formatPath(newLocalPath); - // extract new file name - let newFileName = PathExt.basename(newRelativePath); + // extract new file name + let newFileName = PathExt.basename(newRelativePath); - try { - // check if object with chosen name already exists - await checkObject(currentDrive.name, { - path: newRelativePath - }); - newFileName = await this.incrementName( - newRelativePath, - currentDrive.name - ); - } catch (error) { - // HEAD request failed for this file name, continue, as name doesn't already exist. - } finally { - const result = await renameObjects(currentDrive.name, { - path: relativePath, - newPath: newRelativePath, - newFileName: newFileName, - registeredFileTypes: this._registeredFileTypes - }); + try { + // check if object with chosen name already exists + await checkObject(currentDrive.name, { + path: newRelativePath + }); + newFileName = await this.incrementName( + newRelativePath, + currentDrive.name + ); + } catch (error) { + // HEAD request failed for this file name, continue, as name doesn't already exist. + } finally { + const result = await renameObjects(currentDrive.name, { + path: relativePath, + newPath: newRelativePath, + newFileName: newFileName, + registeredFileTypes: this._registeredFileTypes + }); - data = { - name: newFileName, - path: PathExt.join(currentDrive.name, result.formattedNewPath!), - last_modified: - result.response.length > 0 - ? result.response.data.last_modified - : '', - created: '', - content: PathExt.extname(newFileName) !== '' ? null : [], // TODO: add dir check - format: result.format!, - mimetype: result.mimetype!, - size: - result.response.length > 0 ? result.response.data.size : undefined, - writable: true, - type: result.type! - }; + data = { + name: newFileName, + path: PathExt.join(currentDrive.name, result.formattedNewPath!), + last_modified: + result.response.length > 0 + ? result.response.data.last_modified + : '', + created: '', + content: PathExt.extname(newFileName) !== '' ? null : [], // TODO: add dir check + format: result.format!, + mimetype: result.mimetype!, + size: + result.response.length > 0 + ? result.response.data.size + : undefined, + writable: true, + type: result.type! + }; + } + } catch (err) { + error = (err as DrivesResponseError).message; } } else { // create new element at root would mean modifying a drive - console.warn('Operation not supported.'); + warning = 'Operation not supported.'; + } + + if (error || warning) { + Notification.emit(warning ?? error, warning ? 'warning' : 'error', { + autoClose: 5000 + }); } Contents.validateContentsModel(data); @@ -619,6 +681,8 @@ export class Drive implements Contents.IDrive { localPath: string, options: Partial = {} ): Promise { + let error = ''; + let warning = ''; let data: Contents.IModel = { name: '', path: '', @@ -632,30 +696,40 @@ export class Drive implements Contents.IDrive { type: '' }; if (localPath !== '') { - const currentDrive = extractCurrentDrive(localPath, this._drivesList); - const currentPath = formatPath(localPath); + try { + const currentDrive = extractCurrentDrive(localPath, this._drivesList); + const currentPath = formatPath(localPath); - const result = await saveObject(currentDrive.name, { - path: currentPath, - param: options, - registeredFileTypes: this._registeredFileTypes - }); + const result = await saveObject(currentDrive.name, { + path: currentPath, + param: options, + registeredFileTypes: this._registeredFileTypes + }); - data = { - name: currentPath, - path: PathExt.join(currentDrive.name, currentPath), - last_modified: result.response.data.last_modified as string, - created: result.response.data.last_modified as string, - content: result.response.data.content, - format: result.format, - mimetype: result.mimetype, - size: result.response.data.size, - writable: true, - type: result.type - }; + data = { + name: currentPath, + path: PathExt.join(currentDrive.name, currentPath), + last_modified: result.response.data.last_modified as string, + created: result.response.data.last_modified as string, + content: result.response.data.content, + format: result.format, + mimetype: result.mimetype, + size: result.response.data.size, + writable: true, + type: result.type + }; + } catch (err) { + error = (err as DrivesResponseError).message; + } } else { // create new element at root would mean modifying a drive - console.warn('Operation not supported.'); + warning = 'Operation not supported.'; + } + + if (error || warning) { + Notification.emit(warning ?? error, warning ? 'warning' : 'error', { + autoClose: 5000 + }); } Contents.validateContentsModel(data); @@ -719,6 +793,8 @@ export class Drive implements Contents.IDrive { toDir: string, options: Contents.ICreateOptions = {} ): Promise { + let warning = ''; + let error = ''; let data: Contents.IModel = { name: '', path: '', @@ -732,43 +808,53 @@ export class Drive implements Contents.IDrive { type: '' }; if (path !== '') { - const currentDrive = extractCurrentDrive(path, this._drivesList); - const toDrive = extractCurrentDrive(toDir, this._drivesList); - - // eliminate drive name from path - const relativePath = formatPath(path); - const toRelativePath = formatPath(toDir); - - // construct new file or directory name for the copy - const newFileName = await this.incrementCopyName( - relativePath, - toRelativePath, - toDrive.name - ); - - const result = await copyObjects(currentDrive.name, { - path: relativePath, - toPath: toRelativePath, - newFileName: newFileName, - toDrive: toDrive.name, - registeredFileTypes: this._registeredFileTypes - }); + try { + const currentDrive = extractCurrentDrive(path, this._drivesList); + const toDrive = extractCurrentDrive(toDir, this._drivesList); + + // eliminate drive name from path + const relativePath = formatPath(path); + const toRelativePath = formatPath(toDir); + + // construct new file or directory name for the copy + const newFileName = await this.incrementCopyName( + relativePath, + toRelativePath, + toDrive.name + ); - data = { - name: newFileName, - path: PathExt.join(currentDrive.name, result.formattedNewPath!), - last_modified: result.response!.data.last_modified, - created: '', - content: PathExt.extname(newFileName) !== '' ? null : [], // TODO: add dir check - format: result.format! as Contents.FileFormat, - mimetype: result.mimetype!, - size: result.response!.data.size, - writable: true, - type: result.type! - }; + const result = await copyObjects(currentDrive.name, { + path: relativePath, + toPath: toRelativePath, + newFileName: newFileName, + toDrive: toDrive.name, + registeredFileTypes: this._registeredFileTypes + }); + + data = { + name: newFileName, + path: PathExt.join(currentDrive.name, result.formattedNewPath!), + last_modified: result.response!.data.last_modified, + created: '', + content: PathExt.extname(newFileName) !== '' ? null : [], // TODO: add dir check + format: result.format! as Contents.FileFormat, + mimetype: result.mimetype!, + size: result.response!.data.size, + writable: true, + type: result.type! + }; + } catch (err) { + error = (err as DrivesResponseError).message; + } } else { // create new element at root would mean modifying a drive - console.warn('Operation not supported.'); + warning = 'Operation not supported.'; + } + + if (error || warning) { + Notification.emit(warning ?? error, warning ? 'warning' : 'error', { + autoClose: 5000 + }); } this._fileChanged.emit({ @@ -791,9 +877,15 @@ export class Drive implements Contents.IDrive { newDriveName: string, region: string ): Promise { - await createDrive(newDriveName, { - location: region - }); + try { + await createDrive(newDriveName, { + location: region + }); + } catch (err) { + Notification.emit((err as DrivesResponseError).message, 'error', { + autoClose: 5000 + }); + } const data: Contents.IModel = { name: newDriveName, @@ -814,7 +906,6 @@ export class Drive implements Contents.IDrive { oldValue: null, newValue: data }); - return data; } @@ -826,7 +917,13 @@ export class Drive implements Contents.IDrive { * @returns A promise which resolves with the contents model. */ async addPublicDrive(driveUrl: string): Promise { - await addPublicDrive(driveUrl); + try { + await addPublicDrive(driveUrl); + } catch (err) { + Notification.emit((err as DrivesResponseError).message, 'error', { + autoClose: 5000 + }); + } const data: Contents.IModel = { name: driveUrl, @@ -847,7 +944,6 @@ export class Drive implements Contents.IDrive { oldValue: null, newValue: data }); - return data; } @@ -862,7 +958,13 @@ export class Drive implements Contents.IDrive { driveUrl: string, location: string ): Promise { - await addExternalDrive(driveUrl, location); + try { + await addExternalDrive(driveUrl, location); + } catch (err) { + Notification.emit((err as DrivesResponseError).message, 'error', { + autoClose: 5000 + }); + } const data: Contents.IModel = { name: driveUrl, @@ -883,7 +985,6 @@ export class Drive implements Contents.IDrive { oldValue: null, newValue: data }); - return data; } @@ -895,7 +996,13 @@ export class Drive implements Contents.IDrive { * @returns A promise which resolves with the contents model. */ async excludeDrive(driveName: string): Promise { - await excludeDrive(driveName); + try { + await excludeDrive(driveName); + } catch (err) { + Notification.emit((err as DrivesResponseError).message, 'error', { + autoClose: 5000 + }); + } const data: Contents.IModel = { name: driveName, @@ -916,7 +1023,6 @@ export class Drive implements Contents.IDrive { oldValue: data, newValue: null }); - return data; } @@ -928,7 +1034,13 @@ export class Drive implements Contents.IDrive { * @returns A promise which resolves with the contents model. */ async includeDrive(driveName: string): Promise { - await includeDrive(driveName); + try { + await includeDrive(driveName); + } catch (err) { + Notification.emit((err as DrivesResponseError).message, 'error', { + autoClose: 5000 + }); + } const data: Contents.IModel = { name: driveName, @@ -949,7 +1061,6 @@ export class Drive implements Contents.IDrive { oldValue: null, newValue: data }); - return data; }