Skip to content

Commit dce7bb9

Browse files
Adapt the regex for the url for the handler with path to manage to get the content of a drive.
Update index.ts. Update the get method of the Drive class in contents.ts to populate the drive filebrowser with the contents from the respective s3 bucket. Change the icon for the drive filebrowser.
1 parent b28d7ae commit dce7bb9

File tree

8 files changed

+92
-174
lines changed

8 files changed

+92
-174
lines changed

jupyter_drives/handlers.py

+14-5
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ def initialize(self, logger: logging.Logger, manager: JupyterDrivesManager):
5454
async def get(self):
5555
result = await self._manager.list_drives()
5656
self.finish(json.dumps(result))
57+
5758

5859
@tornado.web.authenticated
5960
async def post(self):
@@ -65,10 +66,15 @@ class ContentsJupyterDrivesHandler(JupyterDrivesAPIHandler):
6566
"""
6667
Deals with contents of a drive.
6768
"""
69+
def initialize(self, logger: logging.Logger, manager: JupyterDrivesManager):
70+
return super().initialize(logger, manager)
71+
6872
@tornado.web.authenticated
6973
async def get(self, path: str = "", drive: str = ""):
7074
result = await self._manager.get_contents(drive, path)
71-
self.finish(json.dump(result))
75+
print("result:", result)
76+
self.finish(result)
77+
7278

7379
@tornado.web.authenticated
7480
async def post(self, path: str = "", drive: str = ""):
@@ -92,7 +98,7 @@ async def patch(self, path: str = "", drive: str = ""):
9298
def setup_handlers(web_app: tornado.web.Application, config: traitlets.config.Config, log: Optional[logging.Logger] = None):
9399
host_pattern = ".*$"
94100
base_url = web_app.settings["base_url"]
95-
101+
print('base_url:', base_url)
96102
log = log or logging.getLogger(__name__)
97103

98104
provider = DrivesConfig(config=config).provider
@@ -122,15 +128,18 @@ def setup_handlers(web_app: tornado.web.Application, config: traitlets.config.Co
122128
+ [
123129
(
124130
url_path_join(
125-
base_url, NAMESPACE, pattern, r"(?P<drive>\w+)", path_regex
131+
base_url, NAMESPACE, pattern, r"(?P<drive>(?:[^/]+))"+ path_regex
126132
),
127133
handler,
134+
{"logger": log, "manager": manager}
128135
)
129136
for pattern, handler in handlers_with_path
130137
]
131138
)
132139

133-
134-
log.debug(f"Jupyter-Drives Handlers: {drives_handlers}")
140+
print('**************************************')
141+
log.warn(f"Jupyter-Drives Handlers: {drives_handlers}")
142+
print('**************************************')
135143

136144
web_app.add_handlers(host_pattern, drives_handlers)
145+

jupyter_drives/managers/s3.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,11 @@ async def list_drives(self):
4747
if (self._config.access_key_id and self._config.secret_access_key):
4848
S3Drive = get_driver(Provider.S3)
4949
drives = [S3Drive(self._config.access_key_id, self._config.secret_access_key)]
50-
5150
results = []
51+
5252
for drive in drives:
5353
results += drive.list_containers()
54-
54+
5555
for result in results:
5656
data.append(
5757
{
@@ -91,7 +91,6 @@ async def mount_drive(self, drive_name):
9191
endpoint_url = self._config.api_base_url,
9292
bucket = drive_name
9393
)
94-
9594
# checking if the drive wasn't mounted already
9695
if drive_name not in self.s3_content_managers or self.s3_content_managers[drive_name] is None:
9796
self.s3_content_managers[drive_name] = s3_contents_manager
@@ -106,7 +105,6 @@ async def mount_drive(self, drive_name):
106105

107106
except Exception as e:
108107
response = {"code": 400, "message": e}
109-
110108
return response
111109

112110
async def unmount_drive(self, drive_name):
@@ -140,6 +138,7 @@ async def get_contents(self, drive_name, path = ""):
140138
try:
141139
if drive_name in self.s3_content_managers:
142140
contents = self.s3_content_managers[drive_name].fs.ls(path)
141+
print('contents:', contents)
143142
code = 200
144143
response["contents"] = contents
145144
else:
@@ -152,6 +151,7 @@ async def get_contents(self, drive_name, path = ""):
152151
response["code"] = code
153152
return response
154153

154+
155155
async def new_file(self, drive_name, type = "notebook", path = ""):
156156
'''Create a new file or directory from an S3 drive.
157157

src/contents.ts

+44-26
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import { Signal, ISignal } from '@lumino/signaling';
55
import { Contents, ServerConnection } from '@jupyterlab/services';
66
//import { URLExt } from '@jupyterlab/coreutils';
7+
import { postDriveMounted, getDriveContents } from './s3requests';
78

89
/*
910
* The url for the default drive service.
@@ -32,12 +33,12 @@ const drive1Contents: Contents.IModel = {
3233
{
3334
name: 'voila2.ipynb',
3435
path: 'Drive1/voila2.ipynb',
35-
last_modified: '2022-10-12T21:33:04.798185Z',
36-
created: '2022-11-09T12:37:21.020396Z',
36+
last_modified: '',
37+
created: '',
3738
content: null,
3839
format: null,
3940
mimetype: null,
40-
size: 5377,
41+
size: null,
4142
writable: true,
4243
type: 'notebook'
4344
},
@@ -313,32 +314,49 @@ export class Drive implements Contents.IDrive {
313314
* Uses the [Jupyter Notebook API](https://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter-server/jupyter_server/main/jupyter_server/services/api/api.yaml#!/contents) and validates the response model.
314315
*/
315316
async get(
316-
path: string,
317+
localPath: string,
317318
options?: Contents.IFetchOptions
318319
): Promise<Contents.IModel> {
319-
/*
320-
let url = this._getUrl(localPath);
321-
if (options) {
322-
// The notebook type cannot take an format option.
323-
if (options.type === 'notebook') {
324-
delete options['format'];
325-
}
326-
const content = options.content ? '1' : '0';
327-
const params: PartialJSONObject = { ...options, content };
328-
url += URLExt.objectToQueryString(params);
329-
}
330-
331-
const settings = this.serverSettings;
332-
const response = await ServerConnection.makeRequest(url, {}, settings);
333-
if (response.status !== 200) {
334-
const err = await ServerConnection.ResponseError.create(response);
335-
throw err;
336-
}
337-
const data = await response.json();*/
320+
postDriveMounted(this.name);
321+
const response = await getDriveContents(this.name, localPath);
322+
let fileExtension: string = '';
323+
const driveBrowserContents: Array<object> = [];
324+
const driveContents: Array<string> = response['contents'];
325+
driveContents.forEach(content => {
326+
fileExtension = content.split('.')[1];
327+
driveBrowserContents.push({
328+
name: content,
329+
path: this.name + '/' + content,
330+
last_modified: '',
331+
created: '',
332+
content: null,
333+
format: null,
334+
mimetype: fileExtension === 'txt' ? 'text/plain' : '',
335+
size: undefined,
336+
writable: true,
337+
type:
338+
fileExtension === 'txt'
339+
? 'txt'
340+
: fileExtension === 'ipynb'
341+
? 'notebook'
342+
: 'directory'
343+
});
344+
});
345+
const drivePath: Contents.IModel = {
346+
name: this.name,
347+
path: this.name,
348+
last_modified: '',
349+
created: '',
350+
content: driveBrowserContents,
351+
format: 'json',
352+
mimetype: '',
353+
size: undefined,
354+
writable: true,
355+
type: 'directory'
356+
};
338357

339-
const data = drive1Contents;
340-
//Contents.validateContentsModel(data);
341-
return data;
358+
Contents.validateContentsModel(drivePath);
359+
return drivePath;
342360
}
343361

344362
/**

src/icons.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { LabIcon } from '@jupyterlab/ui-components';
2-
import driveSvgstr from '../style/drive.svg';
2+
import driveSvgstr from '../style/driveIconFileBrowser.svg';
33
export const DriveIcon = new LabIcon({
44
name: '@jupyter/drives:drive',
55
svgstr: driveSvgstr

src/index.ts

+19-80
Original file line numberDiff line numberDiff line change
@@ -17,97 +17,44 @@ import {
1717
} from '@jupyterlab/apputils';
1818

1919
import { SidePanel } from '@jupyterlab/ui-components';
20-
import { IBucket } from './s3requests';
2120
import { Dialog, ICommandPalette, showDialog } from '@jupyterlab/apputils';
2221
import { DriveListModel, DriveListView } from './drivelistmanager';
2322
import { addJupyterLabThemeChangeListener } from '@jupyter/web-components';
24-
import {
25-
getDriveContents,
26-
getDrivesList,
27-
postDriveMounted
28-
} from './s3requests';
29-
30-
/**
31-
* The class name added to the filebrowser filterbox node.
32-
*/
33-
//const FILTERBOX_CLASS = 'jp-FileBrowser-filterBox';
23+
import { getDrivesList, IBucket } from './s3requests';
3424

3525
const FILE_BROWSER_FACTORY = 'DrivePanel';
3626
const FILE_BROWSER_PLUGIN_ID = '@jupyter/drives:widget';
3727

38-
function buildMountedDriveNameList(driveList: Drive[]): string[] {
28+
function buildAddedDriveNameList(driveList: Drive[]): string[] {
3929
const driveNameList: string[] = [];
4030
driveList.forEach(drive => {
4131
driveNameList.push(drive.name);
4232
});
4333
return driveNameList;
4434
}
4535

46-
const s3AvailableBuckets = await getDrivesList();
47-
console.log('List of buckets is:', s3AvailableBuckets);
48-
const driveName = 'jupyter-drive-bucket1';
49-
const path = 'examples';
50-
await postDriveMounted(driveName);
51-
const driveContent = await getDriveContents(driveName, path);
52-
console.log('driveContent:', driveContent);
53-
/*const s3AvailableBuckets1: IBucket[] = [
54-
{
55-
creation_date: '2023-12-15T13:27:57.000Z',
56-
name: 'jupyterDriveBucket1',
57-
provider: 'S3',
58-
region: 'us-east-1',
59-
status: 'active'
60-
},
61-
{
62-
creation_date: '2023-12-19T08:57:29.000Z',
63-
name: 'jupyterDriveBucket2',
64-
provider: 'S3',
65-
region: 'us-east-1',
66-
status: 'inactive'
67-
},
68-
{
69-
creation_date: '2023-12-19T09:07:29.000Z',
70-
name: 'jupyterDriveBucket3',
71-
provider: 'S3',
72-
region: 'us-east-1',
73-
status: 'inactive'
74-
},
75-
{
76-
creation_date: '2023-12-19T09:07:29.000Z',
77-
name: 'jupyterDriveBucket4',
78-
provider: 'S3',
79-
region: 'us-east-1',
80-
status: 'active'
81-
},
82-
{
83-
creation_date: '2024-01-12T09:07:29.000Z',
84-
name: 'jupyterDriveBucket5',
85-
provider: 'S3',
86-
region: 'us-east-1',
87-
status: 'active'
88-
}
89-
];*/
90-
9136
namespace CommandIDs {
9237
export const openDrivesDialog = 'drives:open-drives-dialog';
9338
export const removeDriveBrowser = 'drives:remove-drive-browser';
9439
}
9540

96-
/*async*/ function createDrivesList(bucketList: IBucket[]) {
41+
async function createDrivesList() {
42+
const response = await getDrivesList();
43+
const bucketList: Array<IBucket> = response['data'];
9744
const S3Drives: Drive[] = [];
9845
bucketList.forEach(item => {
9946
const drive = new Drive();
10047
drive.name = item.name;
10148
drive.baseUrl = '';
10249
drive.region = item.region;
103-
drive.status = item.status;
50+
drive.status = 'active';
10451
drive.provider = item.provider;
10552
S3Drives.push(drive);
10653
});
10754
return S3Drives;
10855
}
10956

110-
function camelCaseToDashedCase(name: string) {
57+
export function camelCaseToDashedCase(name: string) {
11158
if (name !== name.toLowerCase()) {
11259
name = name.replace(/[A-Z]/g, m => '-' + m.toLowerCase());
11360
}
@@ -117,14 +64,6 @@ function camelCaseToDashedCase(name: string) {
11764
function restoreDriveName(id: string) {
11865
const list1 = id.split('-file-');
11966
let driveName = list1[0];
120-
for (let i = 0; i < driveName.length; i++) {
121-
if (driveName[i] === '-') {
122-
const index = i;
123-
const char = driveName.charAt(index + 1).toUpperCase();
124-
driveName = driveName.replace(driveName.charAt(index + 1), char);
125-
driveName = driveName.replace(driveName.charAt(index), '');
126-
}
127-
}
12867
return driveName;
12968
}
13069

@@ -161,7 +100,7 @@ const AddDrivesPlugin: JupyterFrontEndPlugin<void> = {
161100
activate: activateAddDrivesPlugin
162101
};
163102

164-
export /*async */ function activateAddDrivesPlugin(
103+
export async function activateAddDrivesPlugin(
165104
app: JupyterFrontEnd,
166105
palette: ICommandPalette,
167106
manager: IDocumentManager,
@@ -174,10 +113,9 @@ export /*async */ function activateAddDrivesPlugin(
174113
addJupyterLabThemeChangeListener();
175114
const selectedDrivesModelMap = new Map<Drive[], DriveListModel>();
176115
let selectedDrives: Drive[] = [];
177-
const availableDrives = createDrivesList(s3AvailableBuckets);
116+
const availableDrives = await createDrivesList();
178117
let driveListModel = selectedDrivesModelMap.get(selectedDrives);
179-
const mountedDriveNameList: string[] =
180-
buildMountedDriveNameList(selectedDrives);
118+
const addedDriveNameList: string[] = buildAddedDriveNameList(selectedDrives);
181119
console.log('AddDrives plugin is activated!');
182120
const trans = translator.load('jupyter-drives');
183121

@@ -211,12 +149,9 @@ export /*async */ function activateAddDrivesPlugin(
211149
}
212150

213151
app.commands.addCommand(CommandIDs.openDrivesDialog, {
214-
execute: /*async*/ () => {
152+
execute: async () => {
215153
if (!driveListModel) {
216-
driveListModel = new DriveListModel(
217-
/*await*/ availableDrives,
218-
selectedDrives
219-
);
154+
driveListModel = new DriveListModel(availableDrives, selectedDrives);
220155
selectedDrivesModelMap.set(selectedDrives, driveListModel);
221156
} else {
222157
selectedDrives = driveListModel.selectedDrives;
@@ -226,9 +161,13 @@ export /*async */ function activateAddDrivesPlugin(
226161
function onDriveAdded(driveList: Drive[]) {
227162
const drive: Drive = driveList[driveList.length - 1];
228163
if (driveListModel) {
229-
if (!mountedDriveNameList.includes(drive.name)) {
164+
if (!addedDriveNameList.includes(drive.name)) {
230165
createDriveFileBrowser(drive);
231-
mountedDriveNameList.push(drive.name);
166+
addedDriveNameList.push(drive.name);
167+
} else {
168+
console.warn(
169+
'The selected drive is already in the list of added drives'
170+
);
232171
}
233172
}
234173
}
@@ -285,5 +224,5 @@ export /*async */ function activateAddDrivesPlugin(
285224
});
286225
}
287226

288-
const plugins: JupyterFrontEndPlugin<any>[] = [plugin, AddDrivesPlugin];
227+
const plugins: JupyterFrontEndPlugin<any>[] = [AddDrivesPlugin];
289228
export default plugins;

src/s3requests.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export async function postDriveMounted(driveName: string) {
2323
}
2424

2525
export async function getDriveContents(driveName: string, path: string) {
26-
return await requestAPI<Contents.IModel>(
26+
return await requestAPI<any>(
2727
'drives' + '/' + driveName + '/' + path,
2828
{
2929
method: 'GET'

0 commit comments

Comments
 (0)