UNPKG

@finos/legend-application-studio

Version:
302 lines (278 loc) 11.2 kB
/** * Copyright (c) 2020-present, Goldman Sachs * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { guaranteeNonNullable, type GeneratorFn, assertErrorThrown, StopWatch, LogEvent, assertNonEmptyString, } from '@finos/legend-shared'; import type { EditorStore } from '../editor/EditorStore.js'; import { flow, flowResult, makeObservable, observable } from 'mobx'; import type { ShowcaseViewerPathParams } from '../../__lib__/LegendStudioNavigation.js'; import { LEGEND_STUDIO_APP_EVENT } from '../../__lib__/LegendStudioEvent.js'; import { ShowcaseRegistryServerClient, type Showcase, } from '@finos/legend-server-showcase'; import { DependencyGraphBuilderError, GRAPH_MANAGER_EVENT, GraphBuilderError, GraphDataDeserializationError, createGraphBuilderReport, } from '@finos/legend-graph'; import { DEFAULT_TAB_SIZE } from '@finos/legend-application'; import { payloadDebugger } from '../editor/panel-group/DevToolPanelState.js'; import { LegendStudioTelemetryHelper } from '../../__lib__/LegendStudioTelemetryHelper.js'; import { GRAPH_EDITOR_MODE } from '../editor/EditorConfig.js'; import type { Entity } from '@finos/legend-storage'; export class ShowcaseViewerStore { readonly editorStore: EditorStore; private readonly showcaseServerClient?: ShowcaseRegistryServerClient; _showcase: Showcase | undefined; constructor(editorStore: EditorStore) { this.editorStore = editorStore; makeObservable(this, { _showcase: observable, initialize: flow, buildGraph: flow, }); if (this.editorStore.applicationStore.config.showcaseServerUrl) { this.showcaseServerClient = new ShowcaseRegistryServerClient({ baseUrl: this.editorStore.applicationStore.config.showcaseServerUrl, }); } } private get client(): ShowcaseRegistryServerClient { return guaranteeNonNullable( this.showcaseServerClient, `Showcase registry server client is not configured`, ); } get showcase(): Showcase { return guaranteeNonNullable(this._showcase); } setShowcase(val: Showcase): void { this._showcase = val; } *initialize(params: ShowcaseViewerPathParams): GeneratorFn<void> { if (!this.editorStore.initState.isInInitialState) { return; } const showcasePath = params.showcasePath; this.editorStore.initState.inProgress(); const onLeave = (hasBuildSucceeded: boolean): void => { this.editorStore.initState.complete(hasBuildSucceeded); }; try { const stopWatch = new StopWatch(); assertNonEmptyString(showcasePath, 'Showcase path expected'); // fetch showcase this.editorStore.initState.setMessage(`Fetching Showcase Data...`); this._showcase = (yield this.client.getShowcase( showcasePath, )) as Showcase; LegendStudioTelemetryHelper.logEvent_ShowcaseManagerShowcaseProjectLaunch( this.editorStore.applicationStore.telemetryService, { showcasePath: showcasePath, }, ); // initialize graph manager yield this.editorStore.graphManagerState.graphManager.initialize( { env: this.editorStore.applicationStore.config.env, tabSize: DEFAULT_TAB_SIZE, clientConfig: { baseUrl: this.editorStore.applicationStore.config.engineServerUrl, queryBaseUrl: this.editorStore.applicationStore.config.engineQueryServerUrl, enableCompression: true, payloadDebugger, }, }, { tracerService: this.editorStore.applicationStore.tracerService, }, ); // convert code to entities stopWatch.record(); const grammar = this.showcase.code; this.editorStore.initState.setMessage( `Converting showcase code to entities...`, ); const entities = (yield this.editorStore.graphManagerState.graphManager.pureCodeToEntities( grammar, // we want to keep section index so we read imports correctly { TEMPORARY__keepSectionIndex: true, }, )) as Entity[]; this.editorStore.initState.setMessage(undefined); stopWatch.record(GRAPH_MANAGER_EVENT.FETCH_GRAPH_ENTITIES__SUCCESS); const graphBuilderResult = (yield flowResult( this.buildGraph(entities), )) as boolean; if (!graphBuilderResult) { onLeave(false); return; } this.editorStore.initState.setMessage(`Generating elements...`); if ( this.editorStore.graphManagerState.graph.ownGenerationSpecifications .length ) { yield flowResult( this.editorStore.graphState.graphGenerationState.globalGenerate(), ); } // build explorer tree this.editorStore.explorerTreeState.buildImmutableModelTrees(); this.editorStore.explorerTreeState.build(); // observe graph: we still capture user changes on forms yield flowResult(this.editorStore.changeDetectionState.observeGraph()); // complete actions this.editorStore.initState.setMessage(undefined); onLeave(true); } catch (error) { assertErrorThrown(error); this.editorStore.applicationStore.logService.error( LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE), error, ); this.editorStore.applicationStore.notificationService.notifyError(error); onLeave(false); } } *buildGraph(entities: Entity[]): GeneratorFn<boolean> { try { const stopWatch = new StopWatch(); yield this.editorStore.graphManagerState.initializeSystem(); // reset this.editorStore.graphManagerState.resetGraph(); // build dependencies stopWatch.record(); const dependencyManager = this.editorStore.graphManagerState.graphManager.createDependencyManager(); this.editorStore.graphManagerState.graph.dependencyManager = dependencyManager; const dependency_buildReport = createGraphBuilderReport(); yield this.editorStore.graphManagerState.graphManager.buildDependencies( this.editorStore.graphManagerState.coreModel, this.editorStore.graphManagerState.systemModel, dependencyManager, new Map(), this.editorStore.graphManagerState.dependenciesBuildState, {}, dependency_buildReport, ); // build graph const graph_buildReport = createGraphBuilderReport(); yield this.editorStore.graphManagerState.graphManager.buildGraph( this.editorStore.graphManagerState.graph, entities, this.editorStore.graphManagerState.graphBuildState, undefined, graph_buildReport, ); // report stopWatch.record(GRAPH_MANAGER_EVENT.INITIALIZE_GRAPH__SUCCESS); const graphBuilderReportData = { timings: this.editorStore.applicationStore.timeService.finalizeTimingsRecord( stopWatch, ), dependencies: dependency_buildReport, dependenciesCount: this.editorStore.graphManagerState.graph.dependencyManager .numberOfDependencies, graph: graph_buildReport, }; LegendStudioTelemetryHelper.logEvent_GraphInitializationSucceeded( this.editorStore.applicationStore.telemetryService, graphBuilderReportData, ); this.editorStore.applicationStore.logService.info( LogEvent.create(GRAPH_MANAGER_EVENT.INITIALIZE_GRAPH__SUCCESS), graphBuilderReportData, ); // fetch available editor configurations yield Promise.all([ this.editorStore.graphState.graphGenerationState.globalFileGenerationState.fetchAvailableFileGenerationDescriptions(), this.editorStore.graphState.graphGenerationState.externalFormatState.fetchExternalFormatDescriptions(), this.editorStore.graphState.fetchAvailableFunctionActivatorConfigurations(), ]); return true; } catch (error) { assertErrorThrown(error); // if graph builder fails, we fall back to text-mode this.editorStore.applicationStore.logService.error( LogEvent.create(GRAPH_MANAGER_EVENT.GRAPH_BUILDER_FAILURE), error, ); if (error instanceof DependencyGraphBuilderError) { // no recovery if dependency models cannot be built, this makes assumption that all dependencies models are compiled successfully // TODO: we might want to handle this more gracefully when we can show people the dependency model element in the future this.editorStore.applicationStore.notificationService.notifyError( `Can't initialize dependency models. Error: ${error.message}`, ); this.editorStore.applicationStore.alertService.setBlockingAlert({ message: `Can't initialize dependencies`, prompt: 'Please use editor to better invesigate the issue', }); } else if (error instanceof GraphDataDeserializationError) { // if something goes wrong with de-serialization, we can't really do anything but to alert this.editorStore.applicationStore.notificationService.notifyError( `Can't deserialize graph. Error: ${error.message}`, ); this.editorStore.applicationStore.alertService.setBlockingAlert({ message: `Can't deserialize graph`, prompt: 'Please use editor to better invesigate the issue', }); } else if (error instanceof GraphBuilderError) { // TODO: we should split this into 2 notifications when we support multiple notifications this.editorStore.applicationStore.notificationService.notifyError( `Can't build graph. Redirected to text mode for debugging. Error: ${error.message}`, ); this.editorStore.applicationStore.logService.error( LogEvent.create(GRAPH_MANAGER_EVENT.GRAPH_BUILDER_FAILURE), error, ); yield flowResult( this.editorStore.switchModes(GRAPH_EDITOR_MODE.GRAMMAR_TEXT, { isGraphBuildFailure: true, }), ); if (this.editorStore.graphEditorMode.mode === GRAPH_EDITOR_MODE.FORM) { // nothing we can do here so we will just block the user this.editorStore.applicationStore.alertService.setBlockingAlert({ message: `Can't compose Pure code from graph models`, prompt: 'Please use editor to better invesigate the issue', }); return false; } } else { this.editorStore.applicationStore.notificationService.notifyError( error, ); } return false; } } }