diff --git a/theia-extensions/viewer-prototype/src/browser/preferences-frontend-contribution.ts b/theia-extensions/viewer-prototype/src/browser/preferences-frontend-contribution.ts new file mode 100644 index 000000000..d871dc41e --- /dev/null +++ b/theia-extensions/viewer-prototype/src/browser/preferences-frontend-contribution.ts @@ -0,0 +1,23 @@ +import { FrontendApplicationContribution } from '@theia/core/lib/browser'; +import { inject, injectable } from 'inversify'; +import { PortPreferenceProxy } from '../common/trace-server-url-provider'; +import { TracePreferences, TRACE_PORT } from './trace-server-preference'; + +@injectable() +export class PreferencesFrontendContribution implements FrontendApplicationContribution { + constructor( + @inject(TracePreferences) protected tracePreferences: TracePreferences, + @inject(PortPreferenceProxy) protected portPreferenceProxy: PortPreferenceProxy + ) {} + + async initialize(): Promise { + this.tracePreferences.ready.then(() => { + this.portPreferenceProxy.onPortPreferenceChanged(this.tracePreferences[TRACE_PORT]); + this.tracePreferences.onPreferenceChanged(async event => { + const newValue = typeof event.newValue === 'string' ? parseInt(event.newValue) : event.newValue; + const oldValue = typeof event.oldValue === 'string' ? parseInt(event.oldValue) : event.oldValue; + this.portPreferenceProxy.onPortPreferenceChanged(newValue, oldValue, true); + }); + }); + } +} diff --git a/theia-extensions/viewer-prototype/src/browser/theia-rpc-tsp-proxy.ts b/theia-extensions/viewer-prototype/src/browser/theia-rpc-tsp-proxy.ts new file mode 100644 index 000000000..12bb19b6e --- /dev/null +++ b/theia-extensions/viewer-prototype/src/browser/theia-rpc-tsp-proxy.ts @@ -0,0 +1,371 @@ +import { inject, injectable, interfaces } from '@theia/core/shared/inversify'; +import { ITspClient } from 'tsp-typescript-client/lib/protocol/tsp-client'; +import { TspClientResponse } from 'tsp-typescript-client/lib/protocol/tsp-client-response'; +import { Trace } from 'tsp-typescript-client/lib/models/trace'; +import { Query } from 'tsp-typescript-client/lib/models/query/query'; +import { Experiment } from 'tsp-typescript-client/lib/models/experiment'; +import { GenericResponse } from 'tsp-typescript-client/lib/models/response/responses'; +import { HealthStatus } from 'tsp-typescript-client/lib/models/health'; +import { XyEntry, XYModel } from 'tsp-typescript-client/lib/models/xy'; +import { TimeGraphEntry, TimeGraphArrow, TimeGraphModel } from 'tsp-typescript-client/lib/models/timegraph'; +import { AnnotationCategoriesModel, AnnotationModel } from 'tsp-typescript-client/lib/models/annotation'; +import { TableModel, ColumnHeaderEntry } from 'tsp-typescript-client/lib/models/table'; +import { OutputDescriptor } from 'tsp-typescript-client/lib/models/output-descriptor'; +import { EntryModel } from 'tsp-typescript-client/lib/models/entry'; +import { OutputStyleModel } from 'tsp-typescript-client/lib/models/styles'; +import { MarkerSet } from 'tsp-typescript-client/lib/models/markerset'; +import { DataTreeEntry } from 'tsp-typescript-client/lib/models/data-tree'; + +export const TspClientProxy = Symbol('TspClientProxy') as symbol & interfaces.Abstract; +export type TspClientProxy = ITspClient; + +@injectable() +export class TheiaRpcTspProxy implements ITspClient { + public constructor(@inject(TspClientProxy) protected tspClient: ITspClient) {} + + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types + protected toTspClientResponse(result: any): TspClientResponse { + const tspClientResponse: TspClientResponse = new TspClientResponse( + result.text, + result.statusCode, + result.statusMessage, + result.responseModel + ); + return tspClientResponse; + } + + /** + * Fetch all available traces on the server + * @returns List of Trace + */ + public async fetchTraces(): Promise> { + return this.toTspClientResponse(await this.tspClient.fetchTraces()); + } + + /** + * Fetch a specific trace information + * @param traceUUID Trace UUID to fetch + */ + public async fetchTrace(traceUUID: string): Promise> { + return this.toTspClientResponse(await this.tspClient.fetchTrace(traceUUID)); + } + + /** + * Open a trace on the server + * @param parameters Query object + * @returns The opened trace + */ + public async openTrace(parameters: Query): Promise> { + return this.toTspClientResponse(await this.tspClient.openTrace(parameters)); + } + + /** + * Delete a trace on the server + * @param traceUUID Trace UUID to delete + * @param deleteTrace Also delete the trace from disk + * @param removeCache Remove all cache for this trace + * @returns The deleted trace + */ + public async deleteTrace( + traceUUID: string, + deleteTrace?: boolean, + removeCache?: boolean + ): Promise> { + return this.toTspClientResponse(await this.tspClient.deleteTrace(traceUUID, deleteTrace, removeCache)); + } + + /** + * Fetch all available experiments on the server + * @returns List of Experiment + */ + public async fetchExperiments(): Promise> { + return this.toTspClientResponse(await this.tspClient.fetchExperiments()); + } + + /** + * Fetch a specific experiment information + * @param expUUID Experiment UUID to fetch + * @returns The experiment + */ + public async fetchExperiment(expUUID: string): Promise> { + return this.toTspClientResponse(await this.tspClient.fetchExperiment(expUUID)); + } + + /** + * Create an experiment on the server + * @param parameters Query object + * @returns The created experiment + */ + public async createExperiment(parameters: Query): Promise> { + return this.toTspClientResponse(await this.tspClient.createExperiment(parameters)); + } + + /** + * Update an experiment + * @param expUUID Experiment UUID to update + * @param parameters Query object + * @returns The updated experiment + */ + public async updateExperiment(expUUID: string, parameters: Query): Promise> { + return this.toTspClientResponse(await this.tspClient.updateExperiment(expUUID, parameters)); + } + + /** + * Delete an experiment on the server + * @param expUUID Experiment UUID to delete + * @returns The deleted experiment + */ + public async deleteExperiment(expUUID: string): Promise> { + return this.toTspClientResponse(await this.tspClient.deleteExperiment(expUUID)); + } + + /** + * List all the outputs associated to this experiment + * @param expUUID Experiment UUID + * @returns List of OutputDescriptor + */ + public async experimentOutputs(expUUID: string): Promise> { + return this.toTspClientResponse(await this.tspClient.experimentOutputs(expUUID)); + } + + /** + * Fetch Data tree + * @param expUUID Experiment UUID + * @param outputID Output ID + * @param parameters Query object + * @returns Generic entry response with entries + */ + public async fetchDataTree( + expUUID: string, + outputID: string, + parameters: Query + ): Promise>>> { + return this.toTspClientResponse>>( + await this.tspClient.fetchDataTree(expUUID, outputID, parameters) + ); + } + + /** + * Fetch XY tree + * @param expUUID Experiment UUID + * @param outputID Output ID + * @param parameters Query object + * @returns Generic entry response with entries + */ + public async fetchXYTree( + expUUID: string, + outputID: string, + parameters: Query + ): Promise>>> { + return this.toTspClientResponse>>( + await this.tspClient.fetchXYTree(expUUID, outputID, parameters) + ); + } + + /** + * Fetch XY. model extends XYModel + * @param expUUID Experiment UUID + * @param outputID Output ID + * @param parameters Query object + * @returns XY model response with the model + */ + public async fetchXY( + expUUID: string, + outputID: string, + parameters: Query + ): Promise>> { + return this.toTspClientResponse>( + await this.tspClient.fetchXY(expUUID, outputID, parameters) + ); + } + + /** + * Fetch XY tooltip + * @param expUUID Experiment UUID + * @param outputID Output ID + * @param xValue X value + * @param yValue Optional Y value + * @param seriesID Optional series ID + * @returns Map of key=name of the property and value=string value associated + */ + public async fetchXYToolTip( + expUUID: string, + outputID: string, + xValue: number, + yValue?: number, + seriesID?: string + ): Promise>> { + return this.toTspClientResponse>( + await this.tspClient.fetchXYToolTip(expUUID, outputID, xValue, yValue, seriesID) + ); + } + + /** + * Fetch Time Graph tree, Model extends TimeGraphEntry + * @param expUUID Experiment UUID + * @param outputID Output ID + * @param parameters Query object + * @returns Time graph entry response with entries of type TimeGraphEntry + */ + public async fetchTimeGraphTree( + expUUID: string, + outputID: string, + parameters: Query + ): Promise>>> { + return this.toTspClientResponse>>( + await this.tspClient.fetchTimeGraphTree(expUUID, outputID, parameters) + ); + } + + /** + * Fetch Time Graph states. Model extends TimeGraphModel + * @param expUUID Experiment UUID + * @param outputID Output ID + * @param parameters Query object + * @returns Generic response with the model + */ + public async fetchTimeGraphStates( + expUUID: string, + outputID: string, + parameters: Query + ): Promise>> { + return this.toTspClientResponse>( + await this.tspClient.fetchTimeGraphStates(expUUID, outputID, parameters) + ); + } + + /** + * Fetch Time Graph arrows. Model extends TimeGraphArrow + * @param expUUID Experiment UUID + * @param outputID Output ID + * @param parameters Query object + * @returns Generic response with the model + */ + public async fetchTimeGraphArrows( + expUUID: string, + outputID: string, + parameters: Query + ): Promise>> { + return this.toTspClientResponse>( + await this.tspClient.fetchTimeGraphArrows(expUUID, outputID, parameters) + ); + } + + /** + * Fetch marker sets. + * @returns Generic response with the model + */ + public async fetchMarkerSets(expUUID: string): Promise>> { + return this.toTspClientResponse>(await this.tspClient.fetchMarkerSets(expUUID)); + } + + /** + * Fetch annotations categories. + * @param expUUID Experiment UUID + * @param outputID Output ID + * @param markerSetId Marker Set ID + * @returns Generic response with the model + */ + public async fetchAnnotationsCategories( + expUUID: string, + outputID: string, + markerSetId?: string + ): Promise>> { + return this.toTspClientResponse>( + await this.tspClient.fetchAnnotationsCategories(expUUID, outputID, markerSetId) + ); + } + + /** + * Fetch annotations. + * @param expUUID Experiment UUID + * @param outputID Output ID + * @param parameters Query object + * @returns Generic response with the model + */ + public async fetchAnnotations( + expUUID: string, + outputID: string, + parameters: Query + ): Promise>> { + return this.toTspClientResponse>( + await this.tspClient.fetchAnnotations(expUUID, outputID, parameters) + ); + } + + /** + * Fetch tooltip for a Time Graph element. + * @param expUUID Experiment UUID + * @param outputID Output ID + * @param parameters Query object + * @returns Map of key=name of the property and value=string value associated + */ + public async fetchTimeGraphTooltip( + expUUID: string, + outputID: string, + parameters: Query + ): Promise>> { + return this.toTspClientResponse>( + await this.tspClient.fetchTimeGraphTooltip(expUUID, outputID, parameters) + ); + } + + /** + * Fetch Table columns + * @param expUUID Experiment UUID + * @param outputID Output ID + * @param parameters Query object + * @returns Generic entry response with columns headers as model + */ + public async fetchTableColumns( + expUUID: string, + outputID: string, + parameters: Query + ): Promise>> { + return this.toTspClientResponse>( + await this.tspClient.fetchTableColumns(expUUID, outputID, parameters) + ); + } + + /** + * Fetch Table lines + * @param expUUID Experiment UUID + * @param outputID Output ID + * @param parameters Query object + * @returns Generic response with the model + */ + public async fetchTableLines( + expUUID: string, + outputID: string, + parameters: Query + ): Promise>> { + return this.toTspClientResponse>( + await this.tspClient.fetchTableLines(expUUID, outputID, parameters) + ); + } + + /** + * Fetch output styles + * @param expUUID Experiment UUID + * @param outputID Output ID + * @param parameters Query object + * @returns Generic response with the model + */ + public async fetchStyles( + expUUID: string, + outputID: string, + parameters: Query + ): Promise>> { + return this.toTspClientResponse>( + await this.tspClient.fetchStyles(expUUID, outputID, parameters) + ); + } + + /** + * Check the health status of the server + * @returns The Health Status + */ + public async checkHealth(): Promise> { + return this.toTspClientResponse(await this.tspClient.checkHealth()); + } +} diff --git a/theia-extensions/viewer-prototype/src/browser/trace-explorer/trace-explorer-widget.tsx b/theia-extensions/viewer-prototype/src/browser/trace-explorer/trace-explorer-widget.tsx index ef2188aad..4bcd9afe2 100644 --- a/theia-extensions/viewer-prototype/src/browser/trace-explorer/trace-explorer-widget.tsx +++ b/theia-extensions/viewer-prototype/src/browser/trace-explorer/trace-explorer-widget.tsx @@ -8,7 +8,7 @@ import { TraceExplorerServerStatusWidget } from './trace-explorer-sub-widgets/tr import { TraceExplorerTimeRangeDataWidget } from './trace-explorer-sub-widgets/theia-trace-explorer-time-range-data-widget'; import { signalManager, Signals } from 'traceviewer-base/lib/signals/signal-manager'; import { OpenedTracesUpdatedSignalPayload } from 'traceviewer-base/lib/signals/opened-traces-updated-signal-payload'; -import { TraceServerConnectionStatusService } from '../trace-server-status'; +import { TraceServerConnectionStatusClient } from '../../common/trace-server-connection-status'; @injectable() export class TraceExplorerWidget extends BaseWidget { @@ -24,8 +24,8 @@ export class TraceExplorerWidget extends BaseWidget { @inject(TraceExplorerServerStatusWidget) protected readonly serverStatusWidget!: TraceExplorerServerStatusWidget; @inject(TraceExplorerTimeRangeDataWidget) protected readonly timeRangeDataWidget!: TraceExplorerTimeRangeDataWidget; @inject(ViewContainer.Factory) protected readonly viewContainerFactory!: ViewContainer.Factory; - @inject(TraceServerConnectionStatusService) - protected readonly connectionStatusService: TraceServerConnectionStatusService; + @inject(TraceServerConnectionStatusClient) + protected readonly connectionStatusClient: TraceServerConnectionStatusClient; openExperiment(traceUUID: string): void { return this.openedTracesWidget.openExperiment(traceUUID); @@ -109,10 +109,10 @@ export class TraceExplorerWidget extends BaseWidget { } protected onAfterShow(): void { - this.connectionStatusService.addConnectionStatusListener(); + this.connectionStatusClient.addConnectionStatusListener(); } protected onAfterHide(): void { - this.connectionStatusService.removeConnectionStatusListener(); + this.connectionStatusClient.removeConnectionStatusListener(); } } diff --git a/theia-extensions/viewer-prototype/src/browser/trace-server-status.ts b/theia-extensions/viewer-prototype/src/browser/trace-server-connection-status-client-impl.ts similarity index 52% rename from theia-extensions/viewer-prototype/src/browser/trace-server-status.ts rename to theia-extensions/viewer-prototype/src/browser/trace-server-connection-status-client-impl.ts index eb186262c..1242039cb 100644 --- a/theia-extensions/viewer-prototype/src/browser/trace-server-status.ts +++ b/theia-extensions/viewer-prototype/src/browser/trace-server-connection-status-client-impl.ts @@ -1,23 +1,28 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ -import { injectable } from '@theia/core/shared/inversify'; -import { RestClient, ConnectionStatusListener } from 'tsp-typescript-client/lib/protocol/rest-client'; +import { injectable } from 'inversify'; +import { + TraceServerConnectionStatusBackend, + TraceServerConnectionStatusClient +} from '../common/trace-server-connection-status'; +import { inject } from '@theia/core/shared/inversify'; @injectable() -export class TraceServerConnectionStatusService { - private connectionStatusListener: ConnectionStatusListener; +export class TraceServerConnectionStatusClientImpl implements TraceServerConnectionStatusClient { + constructor( + @inject(TraceServerConnectionStatusBackend) + protected traceServerConnectionStatusProxy: TraceServerConnectionStatusBackend + ) {} - constructor() { - this.connectionStatusListener = (status: boolean) => { - TraceServerConnectionStatusService.renderStatus(status); - }; + updateStatus(status: boolean): void { + TraceServerConnectionStatusClientImpl.renderStatus(status); } public addConnectionStatusListener(): void { - RestClient.addConnectionStatusListener(this.connectionStatusListener); + this.traceServerConnectionStatusProxy.setClient(this); } public removeConnectionStatusListener(): void { - RestClient.removeConnectionStatusListener(this.connectionStatusListener); + this.traceServerConnectionStatusProxy.removeClient(this); } static renderStatus(status: boolean): void { diff --git a/theia-extensions/viewer-prototype/src/browser/trace-viewer/trace-viewer-contribution.ts b/theia-extensions/viewer-prototype/src/browser/trace-viewer/trace-viewer-contribution.ts index 8be211a0f..96d4d420a 100644 --- a/theia-extensions/viewer-prototype/src/browser/trace-viewer/trace-viewer-contribution.ts +++ b/theia-extensions/viewer-prototype/src/browser/trace-viewer/trace-viewer-contribution.ts @@ -21,12 +21,12 @@ import { } from './trace-viewer-commands'; import { PortBusy, TraceServerConfigService } from '../../common/trace-server-config'; import { TracePreferences, TRACE_PATH, TRACE_ARGS } from '../trace-server-preference'; -import { TspClient } from 'tsp-typescript-client/lib/protocol/tsp-client'; import { TspClientProvider } from '../tsp-client-provider-impl'; import { ChartShortcutsDialog } from '../trace-explorer/trace-explorer-sub-widgets/charts-cheatsheet-component'; import { signalManager } from 'traceviewer-base/lib/signals/signal-manager'; -import { TraceServerConnectionStatusService } from '../trace-server-status'; +import { TraceServerConnectionStatusClientImpl } from '../trace-server-connection-status-client-impl'; import { FileStat } from '@theia/filesystem/lib/common/files'; +import { ITspClient } from 'tsp-typescript-client'; interface TraceViewerWidgetOpenerOptions extends WidgetOpenerOptions { traceUUID: string; @@ -37,7 +37,7 @@ export class TraceViewerContribution extends WidgetOpenHandler implements CommandContribution, KeybindingContribution { - private tspClient: TspClient; + private tspClient: ITspClient; constructor(@inject(TspClientProvider) private tspClientProvider: TspClientProvider) { super(); @@ -94,7 +94,7 @@ export class TraceViewerContribution progress.report({ message: 'Trace server started.', work: { done: 100, total: 100 } }); } progress.cancel(); - TraceServerConnectionStatusService.renderStatus(true); + TraceServerConnectionStatusClientImpl.renderStatus(true); signalManager().fireTraceServerStartedSignal(); this.openDialog(rootPath); } @@ -163,7 +163,7 @@ export class TraceViewerContribution } else { progress.report({ message: 'Trace server started.', work: { done: 100, total: 100 } }); } - TraceServerConnectionStatusService.renderStatus(true); + TraceServerConnectionStatusClientImpl.renderStatus(true); signalManager().fireTraceServerStartedSignal(); return super.open(traceURI, options); } @@ -230,7 +230,7 @@ export class TraceViewerContribution } else { progress.report({ message: 'Trace server started.', work: { done: 100, total: 100 } }); } - TraceServerConnectionStatusService.renderStatus(true); + TraceServerConnectionStatusClientImpl.renderStatus(true); signalManager().fireTraceServerStartedSignal(); return; } @@ -261,7 +261,7 @@ export class TraceViewerContribution try { await this.traceServerConfigService.stopTraceServer(); this.messageService.info('Trace server terminated successfully.'); - TraceServerConnectionStatusService.renderStatus(false); + TraceServerConnectionStatusClientImpl.renderStatus(false); } catch (err) { this.messageService.error('Failed to stop the trace server.'); } diff --git a/theia-extensions/viewer-prototype/src/browser/trace-viewer/trace-viewer-frontend-module.ts b/theia-extensions/viewer-prototype/src/browser/trace-viewer/trace-viewer-frontend-module.ts index 362f80243..5a61d30c1 100644 --- a/theia-extensions/viewer-prototype/src/browser/trace-viewer/trace-viewer-frontend-module.ts +++ b/theia-extensions/viewer-prototype/src/browser/trace-viewer/trace-viewer-frontend-module.ts @@ -9,7 +9,6 @@ import { } from '@theia/core/lib/browser'; import { TraceViewerWidget, TraceViewerWidgetOptions } from './trace-viewer'; import { TraceViewerContribution } from './trace-viewer-contribution'; -import { TraceServerUrlProvider } from '../../common/trace-server-url-provider'; import { CommandContribution } from '@theia/core/lib/common'; import 'ag-grid-community/dist/styles/ag-grid.css'; import 'ag-grid-community/dist/styles/ag-theme-balham-dark.css'; @@ -20,21 +19,26 @@ import { TraceExplorerContribution } from '../trace-explorer/trace-explorer-cont import { TraceExplorerWidget } from '../trace-explorer/trace-explorer-widget'; import { TspClientProvider } from '../tsp-client-provider-impl'; import { TheiaMessageManager } from '../theia-message-manager'; -import { TraceServerUrlProviderImpl } from '../trace-server-url-provider-frontend-impl'; import { bindTraceServerPreferences } from '../trace-server-bindings'; -import { TraceServerConfigService, traceServerPath } from '../../common/trace-server-config'; +import { TRACE_SERVER_CLIENT, TraceServerConfigService, traceServerPath } from '../../common/trace-server-config'; import { TabBarToolbarContribution } from '@theia/core/lib/browser/shell/tab-bar-toolbar'; import { TraceViewerToolbarContribution } from './trace-viewer-toolbar-contribution'; -import { LazyTspClientFactory } from 'traceviewer-base/lib/lazy-tsp-client'; import { BackendFileService, backendFileServicePath } from '../../common/backend-file-service'; -import { TraceServerConnectionStatusService } from '../trace-server-status'; +import { TraceServerConnectionStatusClientImpl } from '../trace-server-connection-status-client-impl'; import { bindTraceOverviewPreferences } from '../trace-overview-binding'; +import { ITspClient } from 'tsp-typescript-client/lib/protocol/tsp-client'; +import { PortPreferenceProxy, TRACE_SERVER_PORT } from '../../common/trace-server-url-provider'; +import { PreferencesFrontendContribution } from '../preferences-frontend-contribution'; +import { + TRACE_SERVER_CONNECTION_STATUS, + TraceServerConnectionStatusBackend, + TraceServerConnectionStatusClient +} from '../../common/trace-server-connection-status'; +import { TheiaRpcTspProxy, TspClientProxy } from '../theia-rpc-tsp-proxy'; export default new ContainerModule(bind => { - bind(TraceServerUrlProviderImpl).toSelf().inSingletonScope(); - bind(FrontendApplicationContribution).toService(TraceServerUrlProviderImpl); - bind(TraceServerUrlProvider).toService(TraceServerUrlProviderImpl); - bind(LazyTspClientFactory).toFunction(LazyTspClientFactory); + bind(PreferencesFrontendContribution).toSelf().inSingletonScope(); + bind(FrontendApplicationContribution).toService(PreferencesFrontendContribution); bind(TspClientProvider).toSelf().inSingletonScope(); bind(TheiaMessageManager).toSelf().inSingletonScope(); @@ -42,8 +46,8 @@ export default new ContainerModule(bind => { bind(FrontendApplicationContribution).toService(TraceViewerToolbarContribution); bind(TabBarToolbarContribution).toService(TraceViewerToolbarContribution); bind(CommandContribution).toService(TraceViewerToolbarContribution); - bind(TraceServerConnectionStatusService).toSelf().inSingletonScope(); - bind(FrontendApplicationContribution).toService(TraceServerConnectionStatusService); + bind(TraceServerConnectionStatusClient).to(TraceServerConnectionStatusClientImpl).inSingletonScope(); + bind(FrontendApplicationContribution).toService(TraceServerConnectionStatusClient); bind(TraceViewerWidget).toSelf(); bind(WidgetFactory) @@ -88,4 +92,27 @@ export default new ContainerModule(bind => { return connection.createProxy(backendFileServicePath); }) .inSingletonScope(); + + bind(TspClientProxy) + .toDynamicValue(ctx => { + const connection = ctx.container.get(WebSocketConnectionProvider); + return connection.createProxy(TRACE_SERVER_CLIENT); + }) + .inSingletonScope(); + + bind(PortPreferenceProxy) + .toDynamicValue(ctx => { + const connection = ctx.container.get(WebSocketConnectionProvider); + return connection.createProxy(TRACE_SERVER_PORT); + }) + .inSingletonScope(); + + bind(TraceServerConnectionStatusBackend) + .toDynamicValue(ctx => { + const connection = ctx.container.get(WebSocketConnectionProvider); + return connection.createProxy(TRACE_SERVER_CONNECTION_STATUS); + }) + .inSingletonScope(); + + bind(TheiaRpcTspProxy).toSelf().inSingletonScope(); }); diff --git a/theia-extensions/viewer-prototype/src/browser/trace-viewer/trace-viewer.tsx b/theia-extensions/viewer-prototype/src/browser/trace-viewer/trace-viewer.tsx index 1dfa0379f..416ebcf59 100644 --- a/theia-extensions/viewer-prototype/src/browser/trace-viewer/trace-viewer.tsx +++ b/theia-extensions/viewer-prototype/src/browser/trace-viewer/trace-viewer.tsx @@ -4,7 +4,6 @@ import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget'; import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'; import { OutputDescriptor } from 'tsp-typescript-client/lib/models/output-descriptor'; import { Trace } from 'tsp-typescript-client/lib/models/trace'; -import { TspClient } from 'tsp-typescript-client/lib/protocol/tsp-client'; import { TspClientProvider } from '../tsp-client-provider-impl'; import { TraceManager } from 'traceviewer-base/lib/trace-manager'; import { ExperimentManager } from 'traceviewer-base/lib/experiment-manager'; @@ -34,6 +33,7 @@ import { getSwitchToDefaultViewErrorMessage, OverviewPreferences } from '../trace-overview-preference'; +import { ITspClient } from 'tsp-typescript-client'; export const TraceViewerWidgetOptions = Symbol('TraceViewerWidgetOptions'); export interface TraceViewerWidgetOptions { @@ -49,7 +49,7 @@ export class TraceViewerWidget extends ReactWidget implements StatefulWidget { protected uri: Path; protected openedExperiment: Experiment | undefined; protected outputDescriptors: OutputDescriptor[] = []; - protected tspClient: TspClient; + protected tspClient: ITspClient; protected traceManager: TraceManager; protected experimentManager: ExperimentManager; protected backgroundTheme: string; diff --git a/theia-extensions/viewer-prototype/src/browser/tsp-client-provider-impl.ts b/theia-extensions/viewer-prototype/src/browser/tsp-client-provider-impl.ts index a238ae9b9..6c556d286 100644 --- a/theia-extensions/viewer-prototype/src/browser/tsp-client-provider-impl.ts +++ b/theia-extensions/viewer-prototype/src/browser/tsp-client-provider-impl.ts @@ -1,41 +1,25 @@ -import { injectable, inject } from '@theia/core/shared/inversify'; -import { TraceServerUrlProvider } from '../common/trace-server-url-provider'; -import { TspClient } from 'tsp-typescript-client/lib/protocol/tsp-client'; +import { injectable, inject } from 'inversify'; import { ExperimentManager } from 'traceviewer-base/lib/experiment-manager'; import { TraceManager } from 'traceviewer-base/lib/trace-manager'; import { ITspClientProvider } from 'traceviewer-base/lib/tsp-client-provider'; -import { LazyTspClientFactory } from 'traceviewer-base/lib/lazy-tsp-client'; +import { ITspClient } from 'tsp-typescript-client'; +import { TheiaRpcTspProxy } from './theia-rpc-tsp-proxy'; @injectable() export class TspClientProvider implements ITspClientProvider { - private _tspClient: TspClient; + private _tspClient: ITspClient; private _traceManager: TraceManager; private _experimentManager: ExperimentManager; - private _listeners: ((tspClient: TspClient) => void)[]; + private _listeners: ((tspClient: ITspClient) => void)[]; - constructor( - @inject(TraceServerUrlProvider) private tspUrlProvider: TraceServerUrlProvider, - @inject(LazyTspClientFactory) private lazyTspClientFactory: LazyTspClientFactory - ) { - const traceServerUrlPromise = this.tspUrlProvider.getTraceServerUrlPromise(); - this._tspClient = this.lazyTspClientFactory(traceServerUrlPromise); + constructor(@inject(TheiaRpcTspProxy) protected client: ITspClient) { + this._tspClient = client; this._traceManager = new TraceManager(this._tspClient); this._experimentManager = new ExperimentManager(this._tspClient, this._traceManager); this._listeners = []; - // Skip the first event fired when the Trace Server URL gets initialized. - traceServerUrlPromise.then(() => { - tspUrlProvider.onDidChangeTraceServerUrl(url => { - this._tspClient = new TspClient(url); - this._traceManager = new TraceManager(this._tspClient); - this._experimentManager = new ExperimentManager(this._tspClient, this._traceManager); - for (const listener of this._listeners) { - listener(this._tspClient); - } - }); - }); } - public getTspClient(): TspClient { + public getTspClient(): ITspClient { return this._tspClient; } @@ -52,7 +36,7 @@ export class TspClientProvider implements ITspClientProvider { * @param listener The listener function to be called when the url is * changed */ - addTspClientChangeListener(listener: (tspClient: TspClient) => void): void { + addTspClientChangeListener(listener: (tspClient: ITspClient) => void): void { this._listeners.push(listener); } } diff --git a/theia-extensions/viewer-prototype/src/common/trace-server-config.ts b/theia-extensions/viewer-prototype/src/common/trace-server-config.ts index fbfc15bda..12474acac 100644 --- a/theia-extensions/viewer-prototype/src/common/trace-server-config.ts +++ b/theia-extensions/viewer-prototype/src/common/trace-server-config.ts @@ -1,6 +1,8 @@ import { ApplicationError } from '@theia/core'; export const traceServerPath = '/services/theia-trace-extension/trace-server-config'; +export const TRACE_SERVER_CLIENT = '/services/theia-trace-extension/trace-server-client'; + export const PortBusy = ApplicationError.declare(-32650, code => ({ message: 'Port busy', data: { code } diff --git a/theia-extensions/viewer-prototype/src/common/trace-server-connection-status.ts b/theia-extensions/viewer-prototype/src/common/trace-server-connection-status.ts new file mode 100644 index 000000000..1c569e0f6 --- /dev/null +++ b/theia-extensions/viewer-prototype/src/common/trace-server-connection-status.ts @@ -0,0 +1,34 @@ +export const TraceServerConnectionStatusBackend = Symbol('TraceServerConnectionStatusBackend'); + +export const TRACE_SERVER_CONNECTION_STATUS = '/services/theia-trace-extension/trace-server-connection-status'; + +export interface TraceServerConnectionStatusBackend { + /** + * Set a new TraceServerConnectionStatusClient to be notified on status changes. + * @param client the client to be notified. + */ + setClient(client: TraceServerConnectionStatusClient): void; + /** + * Remove a new TraceServerConnectionStatusClient so it won't no longer be notified on status changes. + * @param client the client to be removed. + */ + removeClient(client: TraceServerConnectionStatusClient): void; +} + +export const TraceServerConnectionStatusClient = Symbol('TraceServerConnectionStatusClient'); + +export interface TraceServerConnectionStatusClient { + /** + * Update the status on the client. + * @param status the new value of the status. + */ + updateStatus(status: boolean): void; + /** + * Subscribe this client to the connection status + */ + addConnectionStatusListener(): void; + /** + * Unsubscribe this client from the connection status + */ + removeConnectionStatusListener(): void; +} diff --git a/theia-extensions/viewer-prototype/src/common/trace-server-url-provider.ts b/theia-extensions/viewer-prototype/src/common/trace-server-url-provider.ts index 8bda617d6..bef17f069 100644 --- a/theia-extensions/viewer-prototype/src/common/trace-server-url-provider.ts +++ b/theia-extensions/viewer-prototype/src/common/trace-server-url-provider.ts @@ -22,3 +22,16 @@ export interface TraceServerUrlProvider { */ onDidChangeTraceServerUrl(listener: (url: string) => void): void; } + +export const TRACE_SERVER_PORT = '/services/theia-trace-extension/trace-server-port'; + +export const PortPreferenceProxy = Symbol('PortPreferenceProxy'); +export interface PortPreferenceProxy { + /** + * Notify the backend about a change of the port preference. + * @param newPort the new value of the port preference. + * @param oldValue the old value of the port preference. + * @param preferenceChanged boolean that indicated whether the preference was changed or initialized. + */ + onPortPreferenceChanged(newPort: number | undefined, oldValue?: number, preferenceChanged?: boolean): Promise; +} diff --git a/theia-extensions/viewer-prototype/src/node/trace-server-connection-status-backend-impl.ts b/theia-extensions/viewer-prototype/src/node/trace-server-connection-status-backend-impl.ts new file mode 100644 index 000000000..c35eed832 --- /dev/null +++ b/theia-extensions/viewer-prototype/src/node/trace-server-connection-status-backend-impl.ts @@ -0,0 +1,29 @@ +import { injectable } from 'inversify'; +import { RestClient } from 'tsp-typescript-client/lib/protocol/rest-client'; +import { + TraceServerConnectionStatusBackend, + TraceServerConnectionStatusClient +} from '../common/trace-server-connection-status'; + +@injectable() +export class TraceServerConnectionStatusBackendImpl implements TraceServerConnectionStatusBackend { + protected clients: TraceServerConnectionStatusClient[] = []; + + constructor() { + const listener = (status: boolean) => { + this.clients.forEach(client => client.updateStatus(status)); + }; + RestClient.addConnectionStatusListener(listener); + } + + setClient(client: TraceServerConnectionStatusClient): void { + this.clients.push(client); + } + + removeClient(client: TraceServerConnectionStatusClient): void { + const index = this.clients.indexOf(client); + if (index > -1) { + this.clients.splice(index, 1); + } + } +} diff --git a/theia-extensions/viewer-prototype/src/browser/trace-server-url-provider-frontend-impl.ts b/theia-extensions/viewer-prototype/src/node/trace-server-url-provider-impl.ts similarity index 60% rename from theia-extensions/viewer-prototype/src/browser/trace-server-url-provider-frontend-impl.ts rename to theia-extensions/viewer-prototype/src/node/trace-server-url-provider-impl.ts index 605bbb5f9..017e56750 100644 --- a/theia-extensions/viewer-prototype/src/browser/trace-server-url-provider-frontend-impl.ts +++ b/theia-extensions/viewer-prototype/src/node/trace-server-url-provider-impl.ts @@ -1,13 +1,17 @@ -import { Emitter, Event, MessageService } from '@theia/core'; -import { FrontendApplicationContribution } from '@theia/core/lib/browser'; -import { EnvVariablesServer } from '@theia/core/lib/common/env-variables/env-variables-protocol'; -import { inject, injectable } from '@theia/core/shared/inversify'; +import { inject, injectable } from 'inversify'; import { TraceServerConfigService } from '../common/trace-server-config'; -import { TraceServerUrlProvider, TRACE_SERVER_DEFAULT_URL } from '../common/trace-server-url-provider'; -import { TracePreferences, TRACE_PORT } from './trace-server-preference'; +import { + TraceServerUrlProvider, + TRACE_SERVER_DEFAULT_URL, + PortPreferenceProxy +} from '../common/trace-server-url-provider'; +import { Event, Emitter } from '@theia/core'; +import { BackendApplicationContribution } from '@theia/core/lib/node'; @injectable() -export class TraceServerUrlProviderImpl implements TraceServerUrlProvider, FrontendApplicationContribution { +export class TraceServerUrlProviderImpl + implements TraceServerUrlProvider, BackendApplicationContribution, PortPreferenceProxy +{ /** * The Trace Server URL resolved from a URL template and a port number. * Updated each time the port is changed from the preferences. @@ -45,12 +49,7 @@ export class TraceServerUrlProviderImpl implements TraceServerUrlProvider, Front return this._onDidChangeTraceServerUrlEmitter.event; } - constructor( - @inject(EnvVariablesServer) protected environment: EnvVariablesServer, - @inject(TracePreferences) protected tracePreferences: TracePreferences, - @inject(TraceServerConfigService) protected traceServerConfigService: TraceServerConfigService, - @inject(MessageService) protected messageService: MessageService - ) { + constructor(@inject(TraceServerConfigService) protected traceServerConfigService: TraceServerConfigService) { this._traceServerUrlPromise = new Promise(resolve => { const self = this.onDidChangeTraceServerUrl(url => { self.dispose(); @@ -58,28 +57,28 @@ export class TraceServerUrlProviderImpl implements TraceServerUrlProvider, Front }); }); // Get the URL template from the remote environment. - this.environment.getValue('TRACE_SERVER_URL').then(variable => { - const url = variable?.value; - this._traceServerUrlTemplate = url ? this.normalizeUrl(url) : TRACE_SERVER_DEFAULT_URL; - this.updateTraceServerUrl(); - }); - // Get the configurable port from Theia's preferences. - this.tracePreferences.ready.then(() => { - this._traceServerPort = this.tracePreferences[TRACE_PORT]; - this.updateTraceServerUrl(); - this.tracePreferences.onPreferenceChanged(async event => { - if (event.preferenceName === TRACE_PORT) { - this._traceServerPort = event.newValue; - this.updateTraceServerUrl(); - try { - await this.traceServerConfigService.stopTraceServer(); - this.messageService.info(`Trace server disconnected on port: ${event.oldValue}.`); - } catch (_) { - // Do not show the error incase the user tries to modify the port before starting a server - } + const variable = process.env['TRACE_SERVER_URL']; + this._traceServerUrlTemplate = variable ? this.normalizeUrl(variable) : TRACE_SERVER_DEFAULT_URL; + this.updateTraceServerUrl(); + } + + async onPortPreferenceChanged( + newPort: number | undefined, + oldValue?: number, + preferenceChanged = false + ): Promise { + this._traceServerPort = newPort; + this.updateTraceServerUrl(); + if (preferenceChanged) { + try { + await this.traceServerConfigService.stopTraceServer(); + if (oldValue) { + console.info(`Trace server disconnected on port: ${oldValue}.`); } - }); - }); + } catch (_) { + // Do not show the error incase the user tries to modify the port before starting a server + } + } } async initialize(): Promise { diff --git a/theia-extensions/viewer-prototype/src/node/viewer-prototype-backend-module.ts b/theia-extensions/viewer-prototype/src/node/viewer-prototype-backend-module.ts index 814ad246d..c6fe75c69 100644 --- a/theia-extensions/viewer-prototype/src/node/viewer-prototype-backend-module.ts +++ b/theia-extensions/viewer-prototype/src/node/viewer-prototype-backend-module.ts @@ -1,12 +1,27 @@ import { ContainerModule } from '@theia/core/shared/inversify'; import { ConnectionHandler, JsonRpcConnectionHandler } from '@theia/core/lib/common'; -import { traceServerPath } from '../common/trace-server-config'; +import { TRACE_SERVER_CLIENT, traceServerPath } from '../common/trace-server-config'; import { TraceServerConfigService } from '../common/trace-server-config'; import { BackendFileService, backendFileServicePath } from '../common/backend-file-service'; import { BackendFileServiceImpl } from './backend-file-service-impl'; +import { PortPreferenceProxy, TRACE_SERVER_PORT, TraceServerUrlProvider } from '../common/trace-server-url-provider'; +import { TraceServerUrlProviderImpl } from './trace-server-url-provider-impl'; +import { BackendApplicationContribution } from '@theia/core/lib/node'; +import { LazyTspClientFactory } from 'traceviewer-base/lib/lazy-tsp-client'; +import { TraceServerConnectionStatusBackendImpl } from './trace-server-connection-status-backend-impl'; +import { + TRACE_SERVER_CONNECTION_STATUS, + TraceServerConnectionStatusBackend +} from '../common/trace-server-connection-status'; export default new ContainerModule(bind => { + bind(LazyTspClientFactory).toFunction(LazyTspClientFactory); bind(BackendFileService).to(BackendFileServiceImpl).inSingletonScope(); + bind(TraceServerUrlProviderImpl).toSelf().inSingletonScope(); + bind(TraceServerUrlProvider).to(TraceServerUrlProviderImpl).inSingletonScope(); + bind(BackendApplicationContribution).toService(TraceServerUrlProvider); + bind(PortPreferenceProxy).toService(TraceServerUrlProvider); + bind(TraceServerConnectionStatusBackend).to(TraceServerConnectionStatusBackendImpl).inSingletonScope(); bind(ConnectionHandler) .toDynamicValue( ctx => @@ -23,4 +38,31 @@ export default new ContainerModule(bind => { ) ) .inSingletonScope(); + bind(ConnectionHandler) + .toDynamicValue( + ctx => + new JsonRpcConnectionHandler(TRACE_SERVER_CLIENT, () => { + const provider = ctx.container.get(TraceServerUrlProvider); + const lazyTspClientFactory = ctx.container.get(LazyTspClientFactory); + const traceServerUrlPromise = provider.getTraceServerUrlPromise(); + return lazyTspClientFactory(traceServerUrlPromise); + }) + ) + .inSingletonScope(); + bind(ConnectionHandler) + .toDynamicValue( + ctx => + new JsonRpcConnectionHandler(TRACE_SERVER_PORT, () => + ctx.container.get(PortPreferenceProxy) + ) + ) + .inSingletonScope(); + bind(ConnectionHandler) + .toDynamicValue( + ctx => + new JsonRpcConnectionHandler(TRACE_SERVER_CONNECTION_STATUS, () => + ctx.container.get(TraceServerConnectionStatusBackend) + ) + ) + .inSingletonScope(); });