Skip to content

Commit 8089a33

Browse files
authored
Merge pull request #88 from DenisaCG/publicBuckets
Add support for public buckets
2 parents 7e5396a + 895e2f7 commit 8089a33

File tree

6 files changed

+172
-2
lines changed

6 files changed

+172
-2
lines changed

jupyter_drives/handlers.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ async def post(self, drive: str = "", path: str = ""):
9191
body = self.get_json_body()
9292
if 'location' in body:
9393
result = await self._manager.new_drive(drive, **body)
94+
if 'public' in body:
95+
result = await self._manager.add_public_drive(drive)
9496
else:
9597
result = await self._manager.new_file(drive, path, **body)
9698
self.finish(result)

jupyter_drives/manager.py

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ def __init__(self, config: traitlets.config.Config) -> None:
5252
self._content_managers = {}
5353
self._max_files_listed = 1025
5454
self._drives = None
55+
self._external_drives = {}
5556

5657
# instate fsspec file system
5758
self._file_system = fsspec.filesystem(self._config.provider, asynchronous=True)
@@ -178,7 +179,7 @@ async def list_drives(self):
178179
"""
179180
data = []
180181
if self._config.access_key_id and self._config.secret_access_key:
181-
if self._drives is None:
182+
if self._drives is None and len(self._external_drives) == 0:
182183
raise tornado.web.HTTPError(
183184
status_code= httpx.codes.NOT_IMPLEMENTED,
184185
reason="Listing drives not supported for given provider.",
@@ -187,7 +188,7 @@ async def list_drives(self):
187188
results = []
188189
for drive in self._drives:
189190
try:
190-
results += drive.list_containers()
191+
results += drive.list_containers()
191192
except Exception as e:
192193
raise tornado.web.HTTPError(
193194
status_code=httpx.codes.BAD_REQUEST,
@@ -204,6 +205,23 @@ async def list_drives(self):
204205
"provider": self._config.provider
205206
}
206207
)
208+
209+
if len(self._external_drives) != 0:
210+
for drive in self._external_drives.values():
211+
try:
212+
data.append({
213+
"name": drive['url'],
214+
"region": self._config.region_name,
215+
"creationDate": datetime.now().isoformat(timespec='milliseconds').replace('+00:00', 'Z'),
216+
"mounted": False if result.name not in self._content_managers else True,
217+
"provider": self._config.provider
218+
})
219+
except Exception as e:
220+
raise tornado.web.HTTPError(
221+
status_code=httpx.codes.BAD_REQUEST,
222+
reason=f"The following error occured when listing drives: {e}",
223+
)
224+
207225
else:
208226
raise tornado.web.HTTPError(
209227
status_code= httpx.codes.BAD_REQUEST,
@@ -624,6 +642,26 @@ async def new_drive(self, new_drive_name, location='us-east-1'):
624642

625643
return
626644

645+
async def add_public_drive(self, drive_name):
646+
"""Mount a drive.
647+
648+
Args:
649+
drive_name: name of public bucket to mount
650+
"""
651+
try:
652+
drive = {
653+
"is_public": True,
654+
"url": drive_name
655+
};
656+
self._external_drives[drive_name] = drive;
657+
except Exception as e:
658+
raise tornado.web.HTTPError(
659+
status_code= httpx.codes.BAD_REQUEST,
660+
reason= f"The following error occured when adding the public drive: {e}"
661+
)
662+
663+
return
664+
627665
async def _get_drive_location(self, drive_name):
628666
"""Helping function for getting drive region.
629667

src/contents.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
IRegisteredFileTypes
1111
} from './token';
1212
import {
13+
addPublicDrive,
1314
saveObject,
1415
getContents,
1516
mountDrive,
@@ -662,6 +663,26 @@ export class Drive implements Contents.IDrive {
662663
return data;
663664
}
664665

666+
/**
667+
* Add public drive.
668+
*
669+
* @param options: The options used to add the public drive.
670+
*
671+
* @returns A promise which resolves with the contents model.
672+
*/
673+
async addPublicDrive(driveUrl: string): Promise<Contents.IModel> {
674+
data = await addPublicDrive(driveUrl);
675+
676+
Contents.validateContentsModel(data);
677+
this._fileChanged.emit({
678+
type: 'new',
679+
oldValue: null,
680+
newValue: data
681+
});
682+
683+
return data;
684+
}
685+
665686
/**
666687
* Create a checkpoint for a file.
667688
*

src/plugins/driveBrowserPlugin.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,55 @@ namespace Private {
333333
}
334334
}
335335

336+
/**
337+
* Create the node for adding a public drive handler.
338+
*/
339+
const addPublicDriveNode = (): HTMLElement => {
340+
const body = document.createElement('div');
341+
342+
const drive = document.createElement('label');
343+
drive.textContent = 'Name';
344+
drive.className = CREATE_DRIVE_TITLE_CLASS;
345+
const driveName = document.createElement('input');
346+
347+
body.appendChild(drive);
348+
body.appendChild(driveName);
349+
return body;
350+
};
351+
352+
/**
353+
* A widget used to add a public drive.
354+
*/
355+
export class AddPublicDriveHandler extends Widget {
356+
/**
357+
* Construct a new "add-public-drive" dialog.
358+
*/
359+
constructor(newDriveName: string) {
360+
super({ node: addPublicDriveNode() });
361+
this.onAfterAttach();
362+
}
363+
364+
protected onAfterAttach(): void {
365+
this.addClass(FILE_DIALOG_CLASS);
366+
const drive = this.driveInput.value;
367+
this.driveInput.setSelectionRange(0, drive.length);
368+
}
369+
370+
/**
371+
* Get the input text node for drive name.
372+
*/
373+
get driveInput(): HTMLInputElement {
374+
return this.node.getElementsByTagName('input')[0] as HTMLInputElement;
375+
}
376+
377+
/**
378+
* Get the value of the widget.
379+
*/
380+
getValue(): string {
381+
return this.driveInput.value;
382+
}
383+
}
384+
336385
export function addCommands(
337386
app: JupyterFrontEnd,
338387
drive: Drive,
@@ -373,6 +422,38 @@ namespace Private {
373422
rank: 105
374423
});
375424

425+
app.commands.addCommand(CommandIDs.addPublicDrive, {
426+
isEnabled: () => {
427+
return browser.model.path === 's3:';
428+
},
429+
execute: async () => {
430+
return showDialog({
431+
title: 'Add Public Drive',
432+
body: new Private.AddPublicDriveHandler(drive.name),
433+
focusNodeSelector: 'input',
434+
buttons: [
435+
Dialog.cancelButton(),
436+
Dialog.okButton({
437+
label: 'Add',
438+
ariaLabel: 'Add Drive'
439+
})
440+
]
441+
}).then(result => {
442+
if (result.value) {
443+
drive.addPublicDrive(result.value);
444+
}
445+
});
446+
},
447+
label: 'Add Public Drive',
448+
icon: driveBrowserIcon.bindprops({ stylesheet: 'menuItem' })
449+
});
450+
451+
app.contextMenu.addItem({
452+
command: CommandIDs.addPublicDrive,
453+
selector: '#drive-file-browser.jp-SidePanel .jp-DirListing-content',
454+
rank: 110
455+
});
456+
376457
app.commands.addCommand(CommandIDs.toggleFileFilter, {
377458
execute: () => {
378459
// Update toggled state, then let the toolbar button update

src/requests.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,33 @@ export async function createDrive(
533533
return data;
534534
}
535535

536+
/**
537+
* Add public drive.
538+
*
539+
* @param driveUrl The public drive URL.
540+
*
541+
* @returns A promise which resolves with the contents model.
542+
*/
543+
export async function addPublicDrive(driveUrl: string) {
544+
await requestAPI<any>('drives/' + driveUrl + '/', 'POST', {
545+
public: true
546+
});
547+
548+
data = {
549+
name: driveUrl,
550+
path: driveUrl,
551+
last_modified: '',
552+
created: '',
553+
content: [],
554+
format: 'json',
555+
mimetype: '',
556+
size: 0,
557+
writable: true,
558+
type: 'directory'
559+
};
560+
return data;
561+
}
562+
536563
namespace Private {
537564
/**
538565
* Helping function for renaming files inside

src/token.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export namespace CommandIDs {
88
export const openPath = 'drives:open-path';
99
export const toggleBrowser = 'drives:toggle-main';
1010
export const createNewDrive = 'drives:create-new-drive';
11+
export const addPublicDrive = 'drives:add-public-drive';
1112
export const launcher = 'launcher:create';
1213
export const toggleFileFilter = 'drives:toggle-file-filter';
1314
export const createNewDirectory = 'drives:create-new-directory';

0 commit comments

Comments
 (0)