Skip to content

Commit

Permalink
GLSP-960: Follow-up
Browse files Browse the repository at this point in the history
 Provide `DiagamLoaderOptions` to support dynamic configuration of the loading behavior

Follow-up for eclipse-glsp/glsp/issues/960
  • Loading branch information
tortmayr committed Sep 4, 2023
1 parent 6284e71 commit 6f8ba43
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 26 deletions.
2 changes: 1 addition & 1 deletion examples/workflow-standalone/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ async function initialize(connectionProvider: MessageConnection, isReconnecting
container = createContainer({ clientId, diagramType, glspClientProvider: async () => glspClient, sourceUri: examplePath });
const actionDispatcher = container.get(GLSPActionDispatcher);
const diagramLoader = container.get(DiagramLoader);
await diagramLoader.load({ isReconnecting });
await diagramLoader.load({ requestModelOptions: { isReconnecting } });

if (isReconnecting) {
const message = `Connection to the ${id} glsp server got closed. Connection was successfully re-established.`;
Expand Down
72 changes: 56 additions & 16 deletions packages/client/src/base/model/diagram-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
Args,
EMPTY_ROOT,
GLSPClient,
InitializeParameters,
MaybePromise,
RequestModelAction,
ServerStatusAction,
Expand All @@ -30,6 +31,7 @@ import {
} from '~glsp-sprotty';
import { GLSPActionDispatcher } from '../action-dispatcher';
import { Ranked } from '../ranked';
import { GLSPModelSource } from './glsp-model-source';

/**
* Configuration options for a specific GLSP diagram instance.
Expand Down Expand Up @@ -92,6 +94,33 @@ export namespace IDiagramStartup {
}
}

export interface DiagramLoadingOptions {
/**
* Optional custom options that should be used the initial {@link RequestModelAction}.
* These options will be merged with the default options (`diagramType` and `sourceUri`).
* Defaults to an empty object if not defined.
*/
requestModelOptions?: Args;

/**
* Optional partial {@link InitializeParameters} that should be used for `initializeServer` request if the underlying
* {@link GLSPClient} has not been initialized yet.
*/
initializeParameters?: Partial<InitializeParameters>;

/**
* Flag to enable/disable client side notifications during the loading process.
* Defaults to `true` if not defined
*/
enableNotifications?: boolean;
}

export interface ResolvedDiagramLoadingOptions {
requestModelOptions: Args;
initializeParameters: InitializeParameters;
enableNotifications: boolean;
}

/**
* The central component responsible for initializing the diagram and loading the graphical model
* from the GLSP server.
Expand All @@ -110,20 +139,34 @@ export class DiagramLoader {
@optional()
protected diagramStartups: IDiagramStartup[] = [];

protected enableLoadingNotifications = true;
@inject(GLSPModelSource)
protected modelSource: GLSPModelSource;

@postConstruct()
protected postConstruct(): void {
this.diagramStartups.sort((a, b) => Ranked.getRank(a) - Ranked.getRank(b));
}

async load(requestModelOptions: Args = {}): Promise<void> {
async load(options: DiagramLoadingOptions = {}): Promise<void> {
const resolvedOptions: ResolvedDiagramLoadingOptions = {
requestModelOptions: {
sourceUri: this.options.sourceUri ?? '',
diagramType: this.options.diagramType,
...options.requestModelOptions
},
initializeParameters: {
applicationId: ApplicationIdProvider.get(),
protocolVersion: GLSPClient.protocolVersion,
...options.initializeParameters
},
enableNotifications: options.enableNotifications ?? true
};
// Set placeholder model until real model from server is available
await this.actionDispatcher.dispatch(SetModelAction.create(EMPTY_ROOT));
await this.invokeStartupHook('preInitialize');
await this.configureGLSPClient();
await this.initialize(resolvedOptions);
await this.invokeStartupHook('preRequestModel');
await this.requestModel(requestModelOptions);
await this.requestModel(resolvedOptions);
await this.invokeStartupHook('postRequestModel');
}

Expand All @@ -133,29 +176,26 @@ export class DiagramLoader {
}
}

protected requestModel(requestModelOptions: Args = {}): Promise<void> {
const options = { sourceUri: this.options.sourceUri, diagramType: this.options.diagramType, ...requestModelOptions } as Args;
const result = this.actionDispatcher.dispatch(RequestModelAction.create({ options }));
if (this.enableLoadingNotifications) {
protected requestModel(options: ResolvedDiagramLoadingOptions): Promise<void> {
const result = this.actionDispatcher.dispatch(RequestModelAction.create({ options: options.requestModelOptions }));
if (options.enableNotifications) {
this.actionDispatcher.dispatch(ServerStatusAction.create('', { severity: 'NONE' }));
}
return result;
}

protected async configureGLSPClient(): Promise<void> {
const glspClient = await this.options.glspClientProvider();

if (this.enableLoadingNotifications) {
protected async initialize(options: ResolvedDiagramLoadingOptions): Promise<void> {
await this.actionDispatcher.initialize();
if (options.enableNotifications) {
this.actionDispatcher.dispatch(ServerStatusAction.create('Initializing...', { severity: 'INFO' }));
}

const glspClient = await this.options.glspClientProvider();

await glspClient.start();

if (!glspClient.initializeResult) {
await glspClient.initializeServer({
applicationId: ApplicationIdProvider.get(),
protocolVersion: GLSPClient.protocolVersion
});
await glspClient.initializeServer(options.initializeParameters);
}
}
}
26 changes: 17 additions & 9 deletions packages/client/src/base/model/glsp-model-source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { inject, injectable, postConstruct } from 'inversify';
import { inject, injectable, postConstruct, preDestroy } from 'inversify';
import {
Action,
ActionHandlerRegistry,
Expand All @@ -26,28 +26,27 @@ import {
InitializeResult,
ModelSource,
SModelRootSchema,
TYPES,
Writable
TYPES
} from '~glsp-sprotty';
import { IDiagramOptions } from './diagram-loader';
/**
* A helper interface that allows the client to mark actions that have been received from the server.
*/
export interface ServerAction extends Action {
_receivedFromServer: true;
__receivedFromServer: true;
}

export namespace ServerAction {
export function is(object: unknown): object is ServerAction {
return Action.is(object) && '_receivedFromServer' in object && object._receivedFromServer === true;
return Action.is(object) && '__receivedFromServer' in object && object.__receivedFromServer === true;
}

/**
* Mark the given action as {@link ServerAction} by attaching the "_receivedFromServer" property
* @param action The action that should be marked as server action
*/
export function mark(action: Action): void {
(action as ServerAction)._receivedFromServer = true;
(action as ServerAction).__receivedFromServer = true;
}
}

Expand All @@ -71,7 +70,8 @@ export class GLSPModelSource extends ModelSource implements Disposable {

protected toDispose = new DisposableCollection();
clientId: string;
readonly glspClient: GLSPClient | undefined;

protected _glspClient: GLSPClient | undefined;
protected _currentRoot: SModelRootSchema;

get diagramType(): string {
Expand All @@ -82,6 +82,10 @@ export class GLSPModelSource extends ModelSource implements Disposable {
return this.options.sourceUri;
}

get glspClient(): GLSPClient | undefined {
return this._glspClient;
}

@postConstruct()
protected postConstruct(): void {
this.clientId = this.options.clientId ?? this.viewerOptions.baseDiv;
Expand Down Expand Up @@ -115,11 +119,14 @@ export class GLSPModelSource extends ModelSource implements Disposable {
}

this.options.glspClientProvider().then(glspClient => {
(this as Writable<GLSPModelSource>).glspClient = glspClient;
this._glspClient = glspClient;
if (glspClient.initializeResult) {
this.configure(registry, glspClient.initializeResult);
} else {
glspClient.onServerInitialized(result => this.configure(registry, result));
const initializeListener = glspClient.onServerInitialized(result => {
this.configure(registry, result);
initializeListener.dispose();
});
}
});
}
Expand Down Expand Up @@ -163,6 +170,7 @@ export class GLSPModelSource extends ModelSource implements Disposable {
return this._currentRoot;
}

@preDestroy()
dispose(): void {
this.toDispose.dispose();
}
Expand Down

0 comments on commit 6f8ba43

Please sign in to comment.