Skip to content

Commit

Permalink
Add support for server progress reporting
Browse files Browse the repository at this point in the history
  • Loading branch information
planger committed Jul 13, 2023
1 parent 1dfd9b1 commit ca6620e
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 22 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- [launch] Add a launcher component for starting WebSocket based GLSP servers [#41](https://github.com/eclipse-glsp/glsp-server-node/pull/41)
- [validation] Add explicit support and API for live and batch validation [#43](https://github.com/eclipse-glsp/glsp-server-node/pull/43)
- [launch] Launcher components now auto allocate a free port if the port argument is 0 [#42](https://github.com/eclipse-glsp/glsp-server-node/pull/42)
- [server] Add support for server progress reporting [#52](https://github.com/eclipse-glsp/glsp-server-node/pull/52)

### Breaking Changes

Expand Down
27 changes: 20 additions & 7 deletions packages/server/src/common/di/diagram-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import {
CenterAction,
DeleteMarkersAction,
EndProgressAction,
ExportSvgAction,
FitToScreenAction,
NavigateToExternalTargetAction,
Expand All @@ -39,9 +40,11 @@ import {
SetTypeHintsAction,
SetViewportAction,
SourceModelChangedAction,
StartProgressAction,
TriggerEdgeCreationAction,
TriggerNodeCreationAction,
UpdateModelAction
UpdateModelAction,
UpdateProgressAction
} from '@eclipse-glsp/protocol';
import { interfaces } from 'inversify';
import { ActionDispatcher, DefaultActionDispatcher } from '../actions/action-dispatcher';
Expand Down Expand Up @@ -86,14 +89,15 @@ import { RequestNavigationTargetsActionHandler } from '../features/navigation/re
import { ResolveNavigationTargetsActionHandler } from '../features/navigation/resolve-navigation-targets-action-handler';
import { PopupModelFactory } from '../features/popup/popup-model-factory';
import { RequestPopupModelActionHandler } from '../features/popup/request-popup-model-action-handler';
import { DefaultProgressService, ProgressService } from '../features/progress/progress-service';
import { ModelValidator } from '../features/validation/model-validator';
import { RequestMarkersHandler } from '../features/validation/request-markers-handler';
import { CompoundOperationHandler } from '../operations/compound-operation-handler';
import { OperationActionHandler } from '../operations/operation-action-handler';
import { OperationHandlerConstructor, OperationHandlerFactory } from '../operations/operation-handler';
import { OperationHandlerRegistry, OperationHandlerRegistryInitializer } from '../operations/operation-handler-registry';
import { ClientSessionInitializer } from '../session/client-session-initializer';
import { applyBindingTarget, applyOptionalBindingTarget, BindingTarget } from './binding-target';
import { BindingTarget, applyBindingTarget, applyOptionalBindingTarget } from './binding-target';
import { GLSPModule } from './glsp-module';
import { InstanceMultiBinding, MultiBinding } from './multi-binding';
import {
Expand Down Expand Up @@ -133,6 +137,7 @@ import {
* - {@link ContextActionsProviders} as {@link ClassMultiBinding<ContextActionsProvider>} (empty)
* - {@link ContextActionsProviderRegistry}
* - {@link ActionDispatcher}
* - {@link ProgressService}
* - {@link ClientActionKinds} as {@link InstanceMultiBinding<string>}
* - {@link ActionHandler} as {@link InstanceMultiBinding<ActionHandlerConstructor>}
* - {@link ActionHandlerFactory}
Expand Down Expand Up @@ -217,6 +222,7 @@ export abstract class DiagramModule extends GLSPModule {
this.configureMultiBinding(new MultiBinding<ClientSessionInitializer>(ClientSessionInitializer), binding =>
this.configureClientSessionInitializers(binding)
);
applyBindingTarget(context, ProgressService, this.bindProgressService()).inSingletonScope();
applyOptionalBindingTarget(context, PopupModelFactory, this.bindPopupModelFactory());
applyOptionalBindingTarget(context, LayoutEngine, this.bindLayoutEngine?.());
}
Expand Down Expand Up @@ -308,6 +314,10 @@ export abstract class DiagramModule extends GLSPModule {
binding.add(LayoutOperationHandler);
}

protected bindProgressService(): BindingTarget<ProgressService> {
return DefaultProgressService;
}

protected configureContextActionProviders(binding: MultiBinding<ContextActionsProvider>): void {
// empty as default
}
Expand All @@ -322,16 +332,17 @@ export abstract class DiagramModule extends GLSPModule {

protected configureClientActions(binding: InstanceMultiBinding<string>): void {
binding.add(CenterAction.KIND);
binding.add(ExportSvgAction.KIND);
binding.add(DeleteMarkersAction.KIND);
binding.add(EndProgressAction.KIND);
binding.add(ExportSvgAction.KIND);
binding.add(FitToScreenAction.KIND);
binding.add(SourceModelChangedAction.KIND);
binding.add(NavigateToTargetAction.KIND);
binding.add(NavigateToExternalTargetAction.KIND);
binding.add(NavigateToTargetAction.KIND);
binding.add(RequestBoundsAction.KIND);
binding.add(SelectAction.KIND);
binding.add(SelectAllAction.KIND);
binding.add(ServerMessageAction.KIND);
binding.add(ServerStatusAction.KIND);
binding.add(SetBoundsAction.KIND);
binding.add(SetClipboardDataAction.KIND);
binding.add(SetContextActions.KIND);
Expand All @@ -345,10 +356,12 @@ export abstract class DiagramModule extends GLSPModule {
binding.add(SetResolvedNavigationTargetAction.KIND);
binding.add(SetTypeHintsAction.KIND);
binding.add(SetViewportAction.KIND);
binding.add(ServerStatusAction.KIND);
binding.add(TriggerNodeCreationAction.KIND);
binding.add(SourceModelChangedAction.KIND);
binding.add(StartProgressAction.KIND);
binding.add(TriggerEdgeCreationAction.KIND);
binding.add(TriggerNodeCreationAction.KIND);
binding.add(UpdateModelAction.KIND);
binding.add(UpdateProgressAction.KIND);
}

protected bindContextActionsProviderRegistry(): BindingTarget<ContextActionsProviderRegistry> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
import { Action, RequestModelAction, ServerMessageAction, ServerStatusAction } from '@eclipse-glsp/protocol';
import { Action, RequestModelAction, ServerStatusAction } from '@eclipse-glsp/protocol';
import { inject, injectable } from 'inversify';
import { ActionDispatcher } from '../../actions/action-dispatcher';
import { ActionHandler } from '../../actions/action-handler';
import { Logger } from '../../utils/logger';
import { ProgressMonitor, ProgressService } from '../progress/progress-service';
import { ModelState } from './model-state';
import { ModelSubmissionHandler } from './model-submission-handler';
import { SourceModelStorage } from './source-model-storage';
Expand All @@ -41,27 +42,27 @@ export class RequestModelActionHandler implements ActionHandler {
@inject(ModelSubmissionHandler)
protected submissionHandler: ModelSubmissionHandler;

@inject(ProgressService)
protected progressService: ProgressService;

async execute(action: RequestModelAction): Promise<Action[]> {
this.logger.debug('Execute RequestModelAction:', action);
this.modelState.setAll(action.options ?? {});

this.notifyClient('Model loading in progress');
const progress = this.reportModelLoading('Model loading in progress');
await this.sourceModelStorage.loadSourceModel(action);
// Clear the previous notification.
this.notifyClient();
this.reportModelLoadingFinished(progress);

return this.submissionHandler.submitModel();
}

/**
* Send a message and status notification with the given message to the client.
* An empty message is an indication for the client to clear previously received notifications.
* @param message The message that should be sent to the client
*/
protected notifyClient(message = ''): void {
const severity = message.length > 0 ? 'INFO' : 'NONE';
this.actionDispatcher.dispatchAll(
ServerMessageAction.create(message, { severity }),
ServerStatusAction.create(message, { severity })
);
protected reportModelLoading(message: string): ProgressMonitor {
this.actionDispatcher.dispatch(ServerStatusAction.create(message, { severity: 'INFO' }));
return this.progressService.start(message);
}

protected reportModelLoadingFinished(monitor: ProgressMonitor): void {
this.actionDispatcher.dispatch(ServerStatusAction.create('', { severity: 'NONE' }));
monitor.end();
}
}
74 changes: 74 additions & 0 deletions packages/server/src/common/features/progress/progress-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/********************************************************************************
* Copyright (c) 2023 EclipseSource and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
import { EndProgressAction, StartProgressAction, UpdateProgressAction } from '@eclipse-glsp/protocol';
import { inject, injectable } from 'inversify';
import * as uuid from 'uuid';
import { ActionDispatcher } from '../../actions/action-dispatcher';

export const ProgressService = Symbol('ProgressService');

/**
* Service for starting and monitoring progress reporting to the client.
*/
export interface ProgressService {
/**
* Start a progress reporting.
* @param title The title shown in the UI for the progress reporting.
* @param options Additional optional options for the progress reporting.
* @returns a monitor to update and end the progress reporting later.
*/
start(title: string, options?: ProgressOptions): ProgressMonitor;
}

/**
* Optional progress reporting options.
*/
export interface ProgressOptions {
/** A message shown in the UI. */
message?: string;
/* The percentage (value range: 0 to 100) to show in the progress reporting. */
percentage?: number;
}

/**
* The monitor of a progress reporting, which can be used to update and end the reporting.
*/
export interface ProgressMonitor {
/**
* Updates an ongoing progress reporting.
* @param options Updated message and/or percentage (value range: 0 to 100).
*/
update(options: { message?: string; percentage?: number }): void;
/**
* Ends an ongoing progress reporting.
*/
end(): void;
}

@injectable()
export class DefaultProgressService implements ProgressService {
@inject(ActionDispatcher)
protected actionDispatcher: ActionDispatcher;

start(title: string, options?: ProgressOptions): ProgressMonitor {
const progressId = uuid.v4();
this.actionDispatcher.dispatch(StartProgressAction.create({ progressId, title, ...options }));
return {
update: updateOptions => this.actionDispatcher.dispatch(UpdateProgressAction.create(progressId, updateOptions)),
end: () => this.actionDispatcher.dispatch(EndProgressAction.create(progressId))
};
}
}

0 comments on commit ca6620e

Please sign in to comment.