From 5c956328d3017ec922d76f9f03da03d14caac55a Mon Sep 17 00:00:00 2001 From: DenisaCG Date: Wed, 6 Nov 2024 10:44:30 +0100 Subject: [PATCH 01/10] add previously established backend fixes --- jupyter_drives/handlers.py | 16 ++++++++++------ jupyter_drives/managers/s3.py | 13 ++++++++++--- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/jupyter_drives/handlers.py b/jupyter_drives/handlers.py index 7320e59..8912b33 100644 --- a/jupyter_drives/handlers.py +++ b/jupyter_drives/handlers.py @@ -53,33 +53,36 @@ def initialize(self, logger: logging.Logger, manager: JupyterDrivesManager): @tornado.web.authenticated async def get(self): result = await self._manager.list_drives() - self.finish(json.dumps(result)) + self.finish(result) @tornado.web.authenticated async def post(self): body = self.get_json_body() result = await self._manager.mount_drive(**body) - self.finish(json.dump(result.message)) + self.finish(result["message"]) class ContentsJupyterDrivesHandler(JupyterDrivesAPIHandler): """ Deals with contents of a drive. """ + def initialize(self, logger: logging.Logger, manager: JupyterDrivesManager): + return super().initialize(logger, manager) + @tornado.web.authenticated async def get(self, path: str = "", drive: str = ""): result = await self._manager.get_contents(drive, path) - self.finish(json.dump(result)) + self.finish(result) @tornado.web.authenticated async def post(self, path: str = "", drive: str = ""): result = await self._manager.new_file(drive, path) - self.finish(json.dump(result)) + self.finish(result) @tornado.web.authenticated async def patch(self, path: str = "", drive: str = ""): body = self.get_json_body() result = await self._manager.rename_file(drive, path, **body) - self.finish(json.dump(result)) + self.finish(result) handlers = [ ("drives", ListJupyterDrivesHandler) @@ -121,9 +124,10 @@ def setup_handlers(web_app: tornado.web.Application, config: traitlets.config.Co + [ ( url_path_join( - base_url, NAMESPACE, pattern, r"(?P\w+)", path_regex + base_url, NAMESPACE, pattern, r"(?P(?:[^/]+))"+ path_regex ), handler, + {"logger": log, "manager": manager} ) for pattern, handler in handlers_with_path ] diff --git a/jupyter_drives/managers/s3.py b/jupyter_drives/managers/s3.py index 2da68ad..a3e11ae 100644 --- a/jupyter_drives/managers/s3.py +++ b/jupyter_drives/managers/s3.py @@ -85,13 +85,20 @@ async def mount_drive(self, drive_name): S3ContentsManager ''' try : + s3_contents_manager = S3ContentsManager( + access_key_id = self._config.access_key_id, + secret_access_key = self._config.secret_access_key, + endpoint_url = self._config.api_base_url, + bucket = drive_name + ) + # checking if the drive wasn't mounted already - if self.s3_content_managers[drive_name] is None: + if drive_name not in self.s3_content_managers or self.s3_content_managers[drive_name] is None: # dealing with long-term credentials (access key, secret key) if self._config.session_token is None: s3_contents_manager = S3ContentsManager( - access_key = self._config.access_key_id, + access_key_id = self._config.access_key_id, secret_access_key = self._config.secret_access_key, endpoint_url = self._config.api_base_url, bucket = drive_name @@ -100,7 +107,7 @@ async def mount_drive(self, drive_name): # dealing with short-term credentials (access key, secret key, session token) else: s3_contents_manager = S3ContentsManager( - access_key = self._config.access_key_id, + access_key_id = self._config.access_key_id, secret_access_key = self._config.secret_access_key, session_token = self._config.session_token, endpoint_url = self._config.api_base_url, From a84923fde07ddd868e2a2594137c7765b54bdfe1 Mon Sep 17 00:00:00 2001 From: DenisaCG Date: Wed, 6 Nov 2024 13:17:25 +0100 Subject: [PATCH 02/10] merge requestAPI handler files --- src/drives.ts | 81 -------------------------------------------------- src/handler.ts | 54 +++++++++++++++++++++++++++------ 2 files changed, 45 insertions(+), 90 deletions(-) delete mode 100644 src/drives.ts diff --git a/src/drives.ts b/src/drives.ts deleted file mode 100644 index f2b2616..0000000 --- a/src/drives.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { URLExt } from '@jupyterlab/coreutils'; -import { ServerConnection } from '@jupyterlab/services'; -import { ReadonlyJSONObject } from '@lumino/coreutils'; -import { DrivesResponseError } from './drivesError'; - -/** - * Array of Jupyter Drives Auth Error Messages - */ -export const AUTH_ERROR_MESSAGES = [ - 'Invalid access key or secret access key', - 'could not read Access Key', - 'could not read Secret Access Key', - 'could not read Session Token', - 'Authentication error' -]; - -/** - * Call the API extension - * - * @param endPoint API REST end point for the extension; default '' - * @param method HTML method; default 'GET' - * @param body JSON object to be passed as body or null; default null - * @param namespace API namespace; default 'git' - * @returns The response body interpreted as JSON - * - * @throws {ServerConnection.NetworkError} If the request cannot be made - */ -export async function requestAPI( - endPoint = '', - method = 'GET', - body: Partial | null = null, - namespace = 'jupyter-drives' -): Promise { - // Make request to Jupyter API - const settings = ServerConnection.makeSettings(); - const requestUrl = URLExt.join( - settings.baseUrl, - namespace, // API Namespace - endPoint - ); - - const init: RequestInit = { - method, - body: body ? JSON.stringify(body) : undefined - }; - - let response: Response; - try { - response = await ServerConnection.makeRequest(requestUrl, init, settings); - } catch (error: any) { - throw new ServerConnection.NetworkError(error); - } - - let data: any = await response.text(); - let isJSON = false; - if (data.length > 0) { - try { - data = JSON.parse(data); - isJSON = true; - } catch (error) { - console.log('Not a JSON response body.', response); - } - } - - if (!response.ok) { - if (isJSON) { - const { message, traceback, ...json } = data; - throw new DrivesResponseError( - response, - message || - `Invalid response: ${response.status} ${response.statusText}`, - traceback || '', - json - ); - } else { - throw new DrivesResponseError(response, data); - } - } - - return data; -} diff --git a/src/handler.ts b/src/handler.ts index 7c43a3d..b343596 100644 --- a/src/handler.ts +++ b/src/handler.ts @@ -1,45 +1,81 @@ import { URLExt } from '@jupyterlab/coreutils'; - import { ServerConnection } from '@jupyterlab/services'; +import { ReadonlyJSONObject } from '@lumino/coreutils'; + +import { DrivesResponseError } from './drivesError'; + +/** + * Array of Jupyter Drives Auth Error Messages. + */ +export const AUTH_ERROR_MESSAGES = [ + 'Invalid access key or secret access key', + 'could not read Access Key', + 'could not read Secret Access Key', + 'could not read Session Token', + 'Authentication error' +]; /** * Call the API extension * - * @param endPoint API REST end point for the extension - * @param init Initial values for the request + * @param endPoint API REST end point for the extension; default '' + * @param method HTML method; default 'GET' + * @param body JSON object to be passed as body or null; default null + * @param namespace API namespace; default 'git' * @returns The response body interpreted as JSON + * + * @throws {ServerConnection.NetworkError} If the request cannot be made */ export async function requestAPI( endPoint = '', - init: RequestInit = {} + method = 'GET', + body: Partial | null = null, + namespace = 'jupyter-drives' ): Promise { // Make request to Jupyter API const settings = ServerConnection.makeSettings(); const requestUrl = URLExt.join( settings.baseUrl, - 'jupyter-drives', // API Namespace + namespace, // API Namespace endPoint ); + const init: RequestInit = { + method, + body: body ? JSON.stringify(body) : undefined + }; + let response: Response; try { response = await ServerConnection.makeRequest(requestUrl, init, settings); - } catch (error) { - throw new ServerConnection.NetworkError(error as any); + } catch (error: any) { + throw new ServerConnection.NetworkError(error); } let data: any = await response.text(); - + let isJSON = false; if (data.length > 0) { try { data = JSON.parse(data); + isJSON = true; } catch (error) { console.log('Not a JSON response body.', response); } } if (!response.ok) { - throw new ServerConnection.ResponseError(response, data.message || data); + if (isJSON) { + const { message, traceback, ...json } = data; + throw new DrivesResponseError( + response, + message || + `Invalid response: ${response.status} ${response.statusText}`, + traceback || '', + json + ); + } else { + throw new DrivesResponseError(response, data); + } } return data; From 7d22de22affc5f2369ef21b4e5081ad0fa4f9cc5 Mon Sep 17 00:00:00 2001 From: DenisaCG Date: Thu, 7 Nov 2024 12:11:41 +0100 Subject: [PATCH 03/10] add drives list provider plugin --- src/index.ts | 29 ++++++++++++++++++++++++++++- src/requests.ts | 9 +++++++++ src/token.ts | 9 +++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 src/requests.ts create mode 100644 src/token.ts diff --git a/src/index.ts b/src/index.ts index f1ddf16..c10b26f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -26,6 +26,8 @@ import { CommandRegistry } from '@lumino/commands'; import { DriveListModel, DriveListView, IDrive } from './drivelistmanager'; import { DriveIcon, driveBrowserIcon } from './icons'; import { Drive } from './contents'; +import { getDrivesList } from './requests'; +import { IDrivesList } from './token'; /** * The command IDs used by the driveBrowser plugin. @@ -150,6 +152,26 @@ const openDriveDialogPlugin: JupyterFrontEndPlugin = { } }; +/** + * The drives list provider. + */ +const drivesListProvider: JupyterFrontEndPlugin = { + id: '@jupyter/drives:drives-list', + description: 'The drives list provider.', + provides: IDrivesList, + activate: async (_: JupyterFrontEnd): Promise => { + const driveNames: string[] = []; + + const response = await getDrivesList(); + if (response.code === 200) { + for (const d of response.data) { + driveNames.push(d.name); + } + } + return { names: driveNames }; + } +}; + /** * The drive file browser factory provider. */ @@ -161,7 +183,8 @@ const driveFileBrowser: JupyterFrontEndPlugin = { IFileBrowserFactory, IToolbarWidgetRegistry, ISettingRegistry, - ITranslator + ITranslator, + IDrivesList ], optional: [ IRouter, @@ -175,6 +198,7 @@ const driveFileBrowser: JupyterFrontEndPlugin = { toolbarRegistry: IToolbarWidgetRegistry, settingsRegistry: ISettingRegistry, translator: ITranslator, + drivesList: IDrivesList, router: IRouter | null, tree: JupyterFrontEnd.ITreeResolver | null, labShell: ILabShell | null, @@ -185,6 +209,8 @@ const driveFileBrowser: JupyterFrontEndPlugin = { ); const { commands } = app; + console.log('driveslist: ', drivesList.names); + // create drive for drive file browser const drive = new Drive({ name: 'jupyter-drives-buckets' @@ -258,6 +284,7 @@ const driveFileBrowser: JupyterFrontEndPlugin = { const plugins: JupyterFrontEndPlugin[] = [ driveFileBrowser, + drivesListProvider, openDriveDialogPlugin ]; export default plugins; diff --git a/src/requests.ts b/src/requests.ts new file mode 100644 index 0000000..6d16b6a --- /dev/null +++ b/src/requests.ts @@ -0,0 +1,9 @@ +import { requestAPI } from './handler'; + +/** + * Fetch the list of available drives. + * @returns list of drives + */ +export async function getDrivesList() { + return await requestAPI('drives', 'GET'); +} diff --git a/src/token.ts b/src/token.ts new file mode 100644 index 0000000..d94a87a --- /dev/null +++ b/src/token.ts @@ -0,0 +1,9 @@ +import { Token } from '@lumino/coreutils'; + +export const IDrivesList = new Token( + '@jupyter/drives:drives-list-provider' +); + +export interface IDrivesList { + names: string[]; +} From fa5e5c1e27dad4d5a0499b7770b68c3836f9849b Mon Sep 17 00:00:00 2001 From: DenisaCG Date: Thu, 7 Nov 2024 13:15:54 +0100 Subject: [PATCH 04/10] add drives list property --- src/contents.ts | 69 ++++++++++++++++++++++++++++++++++++++++++++++++- src/index.ts | 5 ++-- 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/src/contents.ts b/src/contents.ts index 1db63d1..453bcc8 100644 --- a/src/contents.ts +++ b/src/contents.ts @@ -3,8 +3,10 @@ import { Signal, ISignal } from '@lumino/signaling'; import { Contents, ServerConnection } from '@jupyterlab/services'; +import { PathExt } from '@jupyterlab/coreutils'; +import { IDrivesList } from './token'; -const data: Contents.IModel = { +let data: Contents.IModel = { name: '', path: '', last_modified: '', @@ -26,8 +28,24 @@ export class Drive implements Contents.IDrive { constructor(options: Drive.IOptions = {}) { this._serverSettings = ServerConnection.makeSettings(); this._name = options.name ?? ''; + this._drivesList = options.drivesList ?? { names: [] }; //this._apiEndpoint = options.apiEndpoint ?? SERVICE_DRIVE_URL; } + + /** + * The drives list getter. + */ + get drivesList(): IDrivesList { + return this._drivesList; + } + + /** + * The drives list setter. + * */ + set drivesList(list: IDrivesList) { + this._drivesList = list; + } + /** * The Drive base URL */ @@ -41,6 +59,7 @@ export class Drive implements Contents.IDrive { set baseUrl(url: string) { this._baseUrl = url; } + /** * The Drive name getter */ @@ -170,6 +189,48 @@ export class Drive implements Contents.IDrive { } else { relativePath = localPath; } + + data = { + name: PathExt.basename(localPath), + path: PathExt.basename(localPath), + last_modified: '', + created: '', + content: [], + format: 'json', + mimetype: '', + size: undefined, + writable: true, + type: 'directory' + }; + } else { + const drivesList: Contents.IModel[] = []; + for (const drive of this._drivesList.names) { + drivesList.push({ + name: drive, + path: drive, + last_modified: '', + created: '', + content: [], + format: 'json', + mimetype: '', + size: undefined, + writable: true, + type: 'directory' + }); + } + + data = { + name: this._name, + path: this._name, + last_modified: '', + created: '', + content: drivesList, + format: 'json', + mimetype: '', + size: undefined, + writable: true, + type: 'directory' + }; } console.log('GET: ', relativePath); @@ -532,6 +593,7 @@ export class Drive implements Contents.IDrive { }*/ // private _apiEndpoint: string; + private _drivesList: IDrivesList = { names: [] }; private _serverSettings: ServerConnection.ISettings; private _name: string = ''; private _provider: string = ''; @@ -548,6 +610,11 @@ export namespace Drive { * The options used to initialize a `Drive`. */ export interface IOptions { + /** + * List of available drives. + */ + drivesList?: IDrivesList; + /** * The name for the `Drive`, which is used in file * paths to disambiguate it from other drives. diff --git a/src/index.ts b/src/index.ts index c10b26f..c970596 100644 --- a/src/index.ts +++ b/src/index.ts @@ -209,11 +209,10 @@ const driveFileBrowser: JupyterFrontEndPlugin = { ); const { commands } = app; - console.log('driveslist: ', drivesList.names); - // create drive for drive file browser const drive = new Drive({ - name: 'jupyter-drives-buckets' + name: 'jupyter-drives', + drivesList: drivesList }); app.serviceManager.contents.addDrive(drive); From 8a0fe0b950a3f5716cc5c0c4977648c308e680e3 Mon Sep 17 00:00:00 2001 From: DenisaCG Date: Thu, 7 Nov 2024 15:50:42 +0100 Subject: [PATCH 05/10] update to IDriveInfo interface --- src/contents.ts | 20 ++++++++++---------- src/index.ts | 19 ++++++++++++------- src/token.ts | 9 ++++++--- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/contents.ts b/src/contents.ts index 453bcc8..6ab9b29 100644 --- a/src/contents.ts +++ b/src/contents.ts @@ -4,7 +4,7 @@ import { Signal, ISignal } from '@lumino/signaling'; import { Contents, ServerConnection } from '@jupyterlab/services'; import { PathExt } from '@jupyterlab/coreutils'; -import { IDrivesList } from './token'; +import { IDriveInfo } from './token'; let data: Contents.IModel = { name: '', @@ -28,21 +28,21 @@ export class Drive implements Contents.IDrive { constructor(options: Drive.IOptions = {}) { this._serverSettings = ServerConnection.makeSettings(); this._name = options.name ?? ''; - this._drivesList = options.drivesList ?? { names: [] }; + this._drivesList = options.drivesList ?? []; //this._apiEndpoint = options.apiEndpoint ?? SERVICE_DRIVE_URL; } /** * The drives list getter. */ - get drivesList(): IDrivesList { + get drivesList(): IDriveInfo[] { return this._drivesList; } /** * The drives list setter. * */ - set drivesList(list: IDrivesList) { + set drivesList(list: IDriveInfo[]) { this._drivesList = list; } @@ -204,12 +204,12 @@ export class Drive implements Contents.IDrive { }; } else { const drivesList: Contents.IModel[] = []; - for (const drive of this._drivesList.names) { + for (const drive of this._drivesList) { drivesList.push({ - name: drive, - path: drive, + name: drive.name, + path: drive.name, last_modified: '', - created: '', + created: drive.creationDate, content: [], format: 'json', mimetype: '', @@ -593,7 +593,7 @@ export class Drive implements Contents.IDrive { }*/ // private _apiEndpoint: string; - private _drivesList: IDrivesList = { names: [] }; + private _drivesList: IDriveInfo[] = []; private _serverSettings: ServerConnection.ISettings; private _name: string = ''; private _provider: string = ''; @@ -613,7 +613,7 @@ export namespace Drive { /** * List of available drives. */ - drivesList?: IDrivesList; + drivesList?: IDriveInfo[]; /** * The name for the `Drive`, which is used in file diff --git a/src/index.ts b/src/index.ts index c970596..254d8c0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -27,7 +27,7 @@ import { DriveListModel, DriveListView, IDrive } from './drivelistmanager'; import { DriveIcon, driveBrowserIcon } from './icons'; import { Drive } from './contents'; import { getDrivesList } from './requests'; -import { IDrivesList } from './token'; +import { IDriveInfo, IDrivesList } from './token'; /** * The command IDs used by the driveBrowser plugin. @@ -155,20 +155,25 @@ const openDriveDialogPlugin: JupyterFrontEndPlugin = { /** * The drives list provider. */ -const drivesListProvider: JupyterFrontEndPlugin = { +const drivesListProvider: JupyterFrontEndPlugin = { id: '@jupyter/drives:drives-list', description: 'The drives list provider.', provides: IDrivesList, - activate: async (_: JupyterFrontEnd): Promise => { - const driveNames: string[] = []; + activate: async (_: JupyterFrontEnd): Promise => { + const drives: IDriveInfo[] = []; const response = await getDrivesList(); if (response.code === 200) { for (const d of response.data) { - driveNames.push(d.name); + drives.push({ + name: d.name, + region: d.region, + provider: d.provider, + creationDate: d.creation_date + }); } } - return { names: driveNames }; + return drives; } }; @@ -198,7 +203,7 @@ const driveFileBrowser: JupyterFrontEndPlugin = { toolbarRegistry: IToolbarWidgetRegistry, settingsRegistry: ISettingRegistry, translator: ITranslator, - drivesList: IDrivesList, + drivesList: IDriveInfo[], router: IRouter | null, tree: JupyterFrontEnd.ITreeResolver | null, labShell: ILabShell | null, diff --git a/src/token.ts b/src/token.ts index d94a87a..d8ed932 100644 --- a/src/token.ts +++ b/src/token.ts @@ -1,9 +1,12 @@ import { Token } from '@lumino/coreutils'; -export const IDrivesList = new Token( +export const IDrivesList = new Token( '@jupyter/drives:drives-list-provider' ); -export interface IDrivesList { - names: string[]; +export interface IDriveInfo { + name: string; + region: string; + provider: string; + creationDate: string; } From 26a296e80acba05dd4d3ec7c682bd49542cd6eb0 Mon Sep 17 00:00:00 2001 From: DenisaCG Date: Thu, 7 Nov 2024 16:06:08 +0100 Subject: [PATCH 06/10] iterate on name searcher --- src/index.ts | 2 +- style/base.css | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 254d8c0..9ec2a14 100644 --- a/src/index.ts +++ b/src/index.ts @@ -46,7 +46,7 @@ const FILE_BROWSER_FACTORY = 'DriveBrowser'; /** * The class name added to the drive filebrowser filterbox node. */ -const FILTERBOX_CLASS = 'jp-DriveBrowser-filterBox'; +const FILTERBOX_CLASS = 'jp-drive-browser-search-box'; const openDriveDialogPlugin: JupyterFrontEndPlugin = { id: '@jupyter/drives:widget', diff --git a/style/base.css b/style/base.css index e0d3b8e..eb58e6f 100644 --- a/style/base.css +++ b/style/base.css @@ -64,3 +64,7 @@ li { border-left: 2px; background-color: var(--jp-layout-color2); } + +.jp-drive-browser-search-box { + width: 230px; +} From 967b216443f4e8bf4e86f6dd0efdcfcb91fb1943 Mon Sep 17 00:00:00 2001 From: DenisaCG Date: Thu, 7 Nov 2024 18:12:31 +0100 Subject: [PATCH 07/10] iterate on missing credentials error --- jupyter_drives/managers/s3.py | 6 +----- src/index.ts | 12 +++++++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/jupyter_drives/managers/s3.py b/jupyter_drives/managers/s3.py index a3e11ae..163e86f 100644 --- a/jupyter_drives/managers/s3.py +++ b/jupyter_drives/managers/s3.py @@ -67,11 +67,7 @@ async def list_drives(self): "code": 200 } else: - response = {"code": 400} - raise tornado.web.HTTPError( - status_code= httpx.codes.BAD_REQUEST, - reason="No AWS credentials specified. Please set them in your user jupyter_server_config file.", - ) + response = {"code": 400, "message": "No AWS credentials specified. Please set them in your user jupyter_server_config file."} return response diff --git a/src/index.ts b/src/index.ts index 9ec2a14..2513ed5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -164,14 +164,16 @@ const drivesListProvider: JupyterFrontEndPlugin = { const response = await getDrivesList(); if (response.code === 200) { - for (const d of response.data) { + for (const drive of response.data) { drives.push({ - name: d.name, - region: d.region, - provider: d.provider, - creationDate: d.creation_date + name: drive.name, + region: drive.region, + provider: drive.provider, + creationDate: drive.creation_date }); } + } else { + console.error(response.message); } return drives; } From d7289682a804b0c178aa03eccb0cc37257542346 Mon Sep 17 00:00:00 2001 From: DenisaCG Date: Thu, 7 Nov 2024 18:21:43 +0100 Subject: [PATCH 08/10] iterate on error handling --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 2513ed5..cffab2a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -173,7 +173,7 @@ const drivesListProvider: JupyterFrontEndPlugin = { }); } } else { - console.error(response.message); + console.log(response.message); } return drives; } From f8555cd89118d9e29e1ef8f88715275e2eecc4cb Mon Sep 17 00:00:00 2001 From: DenisaCG Date: Mon, 11 Nov 2024 11:57:07 +0100 Subject: [PATCH 09/10] iterate on listing drives plugin --- jupyter_drives/managers/s3.py | 6 +++++- src/index.ts | 9 ++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/jupyter_drives/managers/s3.py b/jupyter_drives/managers/s3.py index 163e86f..9473128 100644 --- a/jupyter_drives/managers/s3.py +++ b/jupyter_drives/managers/s3.py @@ -68,7 +68,11 @@ async def list_drives(self): } else: response = {"code": 400, "message": "No AWS credentials specified. Please set them in your user jupyter_server_config file."} - + raise tornado.web.HTTPError( + status_code= httpx.codes.BAD_REQUEST, + reason="No AWS credentials specified. Please set them in your user jupyter_server_config file.", + ) + return response async def mount_drive(self, drive_name): diff --git a/src/index.ts b/src/index.ts index cffab2a..0bcfc59 100644 --- a/src/index.ts +++ b/src/index.ts @@ -161,9 +161,8 @@ const drivesListProvider: JupyterFrontEndPlugin = { provides: IDrivesList, activate: async (_: JupyterFrontEnd): Promise => { const drives: IDriveInfo[] = []; - - const response = await getDrivesList(); - if (response.code === 200) { + try { + const response = await getDrivesList(); for (const drive of response.data) { drives.push({ name: drive.name, @@ -172,8 +171,8 @@ const drivesListProvider: JupyterFrontEndPlugin = { creationDate: drive.creation_date }); } - } else { - console.log(response.message); + } catch { + console.log('Failed loading available drives list.'); } return drives; } From b6e30dc0257dc2a00f60d8870e2a22b94607391f Mon Sep 17 00:00:00 2001 From: DenisaCG Date: Mon, 11 Nov 2024 12:03:06 +0100 Subject: [PATCH 10/10] add docstrings --- src/token.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/token.ts b/src/token.ts index d8ed932..2656d36 100644 --- a/src/token.ts +++ b/src/token.ts @@ -1,9 +1,15 @@ import { Token } from '@lumino/coreutils'; +/** + * A token for the plugin that provides the list of drives. + */ export const IDrivesList = new Token( '@jupyter/drives:drives-list-provider' ); +/** + * An interface for the available drives. + */ export interface IDriveInfo { name: string; region: string;