Skip to content

Commit

Permalink
Add js api
Browse files Browse the repository at this point in the history
  • Loading branch information
trungleduc committed Nov 4, 2024
1 parent 78a91ca commit f14a083
Show file tree
Hide file tree
Showing 9 changed files with 229 additions and 4 deletions.
1 change: 0 additions & 1 deletion packages/docprovider-extension/src/filebrowser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ export const statusBarTimeline: JupyterFrontEndPlugin<void> = {
DOCUMENT_TIMELINE_URL,
documentPath
);

timelineWidget = new TimelineWidget(
fullPath,
provider,
Expand Down
23 changes: 23 additions & 0 deletions packages/docprovider-extension/src/forkManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ICollaborativeDrive } from '@jupyter/collaborative-drive';
import {
ForkManager,
IForkManager,
IForkManagerToken
} from '@jupyter/docprovider';

import {
JupyterFrontEnd,
JupyterFrontEndPlugin
} from '@jupyterlab/application';

export const forkManagerPlugin: JupyterFrontEndPlugin<IForkManager> = {
id: '@jupyter/docprovider-extension:forkManager',
autoStart: true,
requires: [ICollaborativeDrive],
provides: IForkManagerToken,
activate: (app: JupyterFrontEnd, drive: ICollaborativeDrive) => {
const eventManager = app.serviceManager.events;
const manager = new ForkManager({ drive, eventManager });
return manager;
}
};
4 changes: 3 additions & 1 deletion packages/docprovider-extension/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
statusBarTimeline
} from './filebrowser';
import { notebookCellExecutor } from './executor';
import { forkManagerPlugin } from './forkManager';

/**
* Export the plugins as default.
Expand All @@ -27,7 +28,8 @@ const plugins: JupyterFrontEndPlugin<any>[] = [
defaultFileBrowser,
logger,
notebookCellExecutor,
statusBarTimeline
statusBarTimeline,
forkManagerPlugin
];

export default plugins;
1 change: 0 additions & 1 deletion packages/docprovider/src/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ export const TimelineSliderComponent: React.FC<Props> = ({
setData(data);
setCurrentTimestampIndex(data.timestamps.length - 1);
provider.connectToForkDoc(data.forkRoom, data.sessionId);

sessionRef.current = await requestDocSession(
format,
contentType,
Expand Down
117 changes: 117 additions & 0 deletions packages/docprovider/src/forkManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { ICollaborativeDrive } from '@jupyter/collaborative-drive';
import { URLExt } from '@jupyterlab/coreutils';
import { Event } from '@jupyterlab/services';
import { ISignal, Signal } from '@lumino/signaling';

import { requestAPI, ROOM_FORK_URL } from './requests';
import {
IAllForksResponse,
IForkChangedEvent,
IForkCreationResponse,
IForkManager
} from './tokens';
import { IForkProvider } from './ydrive';

const JUPYTER_COLLABORATION_FORK_EVENTS_URI =
'https://schema.jupyter.org/jupyter_collaboration/fork/v1';

export class ForkManager implements IForkManager {
constructor(options: ForkManager.IOptions) {
const { drive, eventManager } = options;
this._drive = drive;
this._eventManager = eventManager;
this._eventManager.stream.connect(this._handleEvent, this);
}

get isDisposed(): boolean {
return this._disposed;
}
get forkAdded(): ISignal<ForkManager, IForkChangedEvent> {
return this._forkAddedSignal;
}
get forkDeleted(): ISignal<ForkManager, IForkChangedEvent> {
return this._forkDeletedSignal;
}

dispose(): void {
if (this._disposed) {
return;
}
this._eventManager?.stream.disconnect(this._handleEvent);
this._disposed = true;
}
async createFork(options: {
rootId: string;
synchronize: boolean;
label?: string;
description?: string;
}): Promise<IForkCreationResponse | undefined> {
const { rootId, label, description, synchronize } = options;
const init: RequestInit = {
method: 'PUT',
body: JSON.stringify({ label, description, synchronize })
};
const url = URLExt.join(ROOM_FORK_URL, rootId);
const response = await requestAPI<IForkCreationResponse>(url, init);
return response;
}

async getAllForks(rootId: string) {
const url = URLExt.join(ROOM_FORK_URL, rootId);
const init = { method: 'GET' };
const response = await requestAPI<IAllForksResponse>(url, init);
return response;
}

async deleteFork(options: { forkId: string; merge: boolean }): Promise<void> {
const { forkId, merge } = options;
const url = URLExt.join(ROOM_FORK_URL, forkId);
const query = URLExt.objectToQueryString({ merge });
const init = { method: 'DELETE' };
await requestAPI(`${url}${query}`, init);
}
getProvider(options: {
documentPath: string;
format: string;
type: string;
}): IForkProvider | undefined {
const { documentPath, format, type } = options;
const drive = this._drive;
if (drive) {
const docPath = documentPath.slice(drive.name.length + 1);
const provider = drive.providers.get(`${format}:${type}:${docPath}`);
return provider as IForkProvider | undefined;
}
return;
}

private _handleEvent(_: Event.IManager, emission: Event.Emission) {
if (emission.schema_id === JUPYTER_COLLABORATION_FORK_EVENTS_URI) {
switch (emission.action) {
case 'create': {
this._forkAddedSignal.emit(emission as any);
break;
}
case 'delete': {
this._forkDeletedSignal.emit(emission as any);
break;
}
default:
break;
}
}
}

private _disposed = false;
private _drive: ICollaborativeDrive | undefined;
private _eventManager: Event.IManager | undefined;
private _forkAddedSignal = new Signal<ForkManager, IForkChangedEvent>(this);
private _forkDeletedSignal = new Signal<ForkManager, IForkChangedEvent>(this);
}

export namespace ForkManager {
export interface IOptions {
drive: ICollaborativeDrive;
eventManager: Event.IManager;
}
}
2 changes: 2 additions & 0 deletions packages/docprovider/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ export * from './requests';
export * from './ydrive';
export * from './yprovider';
export * from './TimelineSlider';
export * from './tokens';
export * from './forkManager';
41 changes: 41 additions & 0 deletions packages/docprovider/src/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ const DOC_SESSION_URL = 'api/collaboration/session';
const DOC_FORK_URL = 'api/collaboration/undo_redo';
const TIMELINE_URL = 'api/collaboration/timeline';

export const ROOM_FORK_URL = 'api/collaboration/fork';

/**
* Document session model
*/
Expand All @@ -36,6 +38,45 @@ export interface ISessionModel {
sessionId: string;
}

/**
* Call the API extension
*
* @param endPoint API REST end point for the extension
* @param init Initial values for the request
* @returns The response body interpreted as JSON
*/
export async function requestAPI<T = any>(
endPoint = '',
init: RequestInit = {}
): Promise<T> {
// Make request to Jupyter API
const settings = ServerConnection.makeSettings();
const requestUrl = URLExt.join(settings.baseUrl, endPoint);

let response: Response;
try {
response = await ServerConnection.makeRequest(requestUrl, init, settings);
} catch (error) {
throw new ServerConnection.NetworkError(error as any);
}

let data: any = await response.text();

if (data.length > 0) {
try {
data = JSON.parse(data);
} catch (error) {
console.error('Not a JSON response body.', response);
}
}

if (!response.ok) {
throw new ServerConnection.ResponseError(response, data.message || data);
}

return data;
}

export async function requestDocSession(
format: string,
type: string,
Expand Down
43 changes: 43 additions & 0 deletions packages/docprovider/src/tokens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Token } from '@lumino/coreutils';
import { IDisposable } from '@lumino/disposable';
import { ISignal } from '@lumino/signaling';
export interface IForkInfo {
description?: string;
root_roomid: string;
synchronize: boolean;
title?: string;
}

export interface IForkCreationResponse {
fork_info: IForkInfo;
fork_roomid: string;
sessionId: string;
}

export interface IAllForksResponse {
[forkId: string]: IForkInfo;
}

export interface IForkChangedEvent {
fork_info: IForkInfo;
fork_roomid: string;
username?: string;
}

export interface IForkManager extends IDisposable {
createFork(options: {
rootId: string;
synchronize: boolean;
label?: string;
description?: string;
}): Promise<IForkCreationResponse | undefined>;
getAllForks(documentId: string): Promise<IAllForksResponse>;
deleteFork(options: { forkId: string; merge: boolean }): Promise<void>;

forkAdded: ISignal<IForkManager, IForkChangedEvent>;
forkDeleted: ISignal<IForkManager, IForkChangedEvent>;
}

export const IForkManagerToken = new Token<IForkManager>(
'@jupyter/docprovider:IForkManagerToken'
);
1 change: 0 additions & 1 deletion packages/docprovider/src/ydrive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,6 @@ class SharedModelFactory implements ISharedModelFactory {
// the `sharedModel` will be the default one.
return;
}

if (this.documentFactories.has(options.contentType)) {
const factory = this.documentFactories.get(options.contentType)!;
const sharedModel = factory(options);
Expand Down

0 comments on commit f14a083

Please sign in to comment.