Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions jupyter_drives/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ async def post(self, drive: str = "", path: str = ""):
body = self.get_json_body()
if 'location' in body:
result = await self._manager.new_drive(drive, **body)
if 'public' in body:
result = await self._manager.add_public_drive(drive)
else:
result = await self._manager.new_file(drive, path, **body)
self.finish(result)
Expand Down
42 changes: 40 additions & 2 deletions jupyter_drives/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def __init__(self, config: traitlets.config.Config) -> None:
self._content_managers = {}
self._max_files_listed = 1025
self._drives = None
self._external_drives = {}

# instate fsspec file system
self._file_system = fsspec.filesystem(self._config.provider, asynchronous=True)
Expand Down Expand Up @@ -178,7 +179,7 @@ async def list_drives(self):
"""
data = []
if self._config.access_key_id and self._config.secret_access_key:
if self._drives is None:
if self._drives is None and len(self._external_drives) == 0:
raise tornado.web.HTTPError(
status_code= httpx.codes.NOT_IMPLEMENTED,
reason="Listing drives not supported for given provider.",
Expand All @@ -187,7 +188,7 @@ async def list_drives(self):
results = []
for drive in self._drives:
try:
results += drive.list_containers()
results += drive.list_containers()
except Exception as e:
raise tornado.web.HTTPError(
status_code=httpx.codes.BAD_REQUEST,
Expand All @@ -204,6 +205,23 @@ async def list_drives(self):
"provider": self._config.provider
}
)

if len(self._external_drives) != 0:
for drive in self._external_drives.values():
try:
data.append({
"name": drive['url'],
"region": self._config.region_name,
"creationDate": datetime.now().isoformat(timespec='milliseconds').replace('+00:00', 'Z'),
"mounted": False if result.name not in self._content_managers else True,
"provider": self._config.provider
})
except Exception as e:
raise tornado.web.HTTPError(
status_code=httpx.codes.BAD_REQUEST,
reason=f"The following error occured when listing drives: {e}",
)

else:
raise tornado.web.HTTPError(
status_code= httpx.codes.BAD_REQUEST,
Expand Down Expand Up @@ -624,6 +642,26 @@ async def new_drive(self, new_drive_name, location='us-east-1'):

return

async def add_public_drive(self, drive_name):
"""Mount a drive.

Args:
drive_name: name of public bucket to mount
"""
try:
drive = {
"is_public": True,
"url": drive_name
};
self._external_drives[drive_name] = drive;
except Exception as e:
raise tornado.web.HTTPError(
status_code= httpx.codes.BAD_REQUEST,
reason= f"The following error occured when adding the public drive: {e}"
)

return

async def _get_drive_location(self, drive_name):
"""Helping function for getting drive region.

Expand Down
21 changes: 21 additions & 0 deletions src/contents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
IRegisteredFileTypes
} from './token';
import {
addPublicDrive,
saveObject,
getContents,
mountDrive,
Expand Down Expand Up @@ -662,6 +663,26 @@ export class Drive implements Contents.IDrive {
return data;
}

/**
* Add public drive.
*
* @param options: The options used to add the public drive.
*
* @returns A promise which resolves with the contents model.
*/
async addPublicDrive(driveUrl: string): Promise<Contents.IModel> {
data = await addPublicDrive(driveUrl);

Contents.validateContentsModel(data);
this._fileChanged.emit({
type: 'new',
oldValue: null,
newValue: data
});

return data;
}

/**
* Create a checkpoint for a file.
*
Expand Down
81 changes: 81 additions & 0 deletions src/plugins/driveBrowserPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,55 @@ namespace Private {
}
}

/**
* Create the node for adding a public drive handler.
*/
const addPublicDriveNode = (): HTMLElement => {
const body = document.createElement('div');

const drive = document.createElement('label');
drive.textContent = 'Name';
drive.className = CREATE_DRIVE_TITLE_CLASS;
const driveName = document.createElement('input');

body.appendChild(drive);
body.appendChild(driveName);
return body;
};

/**
* A widget used to add a public drive.
*/
export class AddPublicDriveHandler extends Widget {
/**
* Construct a new "add-public-drive" dialog.
*/
constructor(newDriveName: string) {
super({ node: addPublicDriveNode() });
this.onAfterAttach();
}

protected onAfterAttach(): void {
this.addClass(FILE_DIALOG_CLASS);
const drive = this.driveInput.value;
this.driveInput.setSelectionRange(0, drive.length);
}

/**
* Get the input text node for drive name.
*/
get driveInput(): HTMLInputElement {
return this.node.getElementsByTagName('input')[0] as HTMLInputElement;
}

/**
* Get the value of the widget.
*/
getValue(): string {
return this.driveInput.value;
}
}

export function addCommands(
app: JupyterFrontEnd,
drive: Drive,
Expand Down Expand Up @@ -373,6 +422,38 @@ namespace Private {
rank: 105
});

app.commands.addCommand(CommandIDs.addPublicDrive, {
isEnabled: () => {
return browser.model.path === 's3:';
},
execute: async () => {
return showDialog({
title: 'Add Public Drive',
body: new Private.AddPublicDriveHandler(drive.name),
focusNodeSelector: 'input',
buttons: [
Dialog.cancelButton(),
Dialog.okButton({
label: 'Add',
ariaLabel: 'Add Drive'
})
]
}).then(result => {
if (result.value) {
drive.addPublicDrive(result.value);
}
});
},
label: 'Add Public Drive',
icon: driveBrowserIcon.bindprops({ stylesheet: 'menuItem' })
});

app.contextMenu.addItem({
command: CommandIDs.addPublicDrive,
selector: '#drive-file-browser.jp-SidePanel .jp-DirListing-content',
rank: 110
});

app.commands.addCommand(CommandIDs.toggleFileFilter, {
execute: () => {
// Update toggled state, then let the toolbar button update
Expand Down
27 changes: 27 additions & 0 deletions src/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,33 @@ export async function createDrive(
return data;
}

/**
* Add public drive.
*
* @param driveUrl The public drive URL.
*
* @returns A promise which resolves with the contents model.
*/
export async function addPublicDrive(driveUrl: string) {
await requestAPI<any>('drives/' + driveUrl + '/', 'POST', {
public: true
});

data = {
name: driveUrl,
path: driveUrl,
last_modified: '',
created: '',
content: [],
format: 'json',
mimetype: '',
size: 0,
writable: true,
type: 'directory'
};
return data;
}

namespace Private {
/**
* Helping function for renaming files inside
Expand Down
1 change: 1 addition & 0 deletions src/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export namespace CommandIDs {
export const openPath = 'drives:open-path';
export const toggleBrowser = 'drives:toggle-main';
export const createNewDrive = 'drives:create-new-drive';
export const addPublicDrive = 'drives:add-public-drive';
export const launcher = 'launcher:create';
export const toggleFileFilter = 'drives:toggle-file-filter';
export const createNewDirectory = 'drives:create-new-directory';
Expand Down
Loading