diff --git a/.changeset/kind-rabbits-draw.md b/.changeset/kind-rabbits-draw.md new file mode 100644 index 0000000000..457d23317c --- /dev/null +++ b/.changeset/kind-rabbits-draw.md @@ -0,0 +1,6 @@ +--- +'@finos/legend-extension-dsl-data-space': minor +'@finos/legend-application-query': minor +--- + +Use PMCD returned by mapping analysis to build minimal graph for query diff --git a/.changeset/ten-pets-grow.md b/.changeset/ten-pets-grow.md new file mode 100644 index 0000000000..2c72b7f42d --- /dev/null +++ b/.changeset/ten-pets-grow.md @@ -0,0 +1,7 @@ +--- +'@finos/legend-extension-dsl-data-space-studio': patch +'@finos/legend-extension-dsl-data-quality': patch +'@finos/legend-application-studio': patch +'@finos/legend-query-builder': patch +'@finos/legend-graph': patch +--- diff --git a/packages/legend-application-query/src/components/Core_LegendQueryApplicationPlugin.tsx b/packages/legend-application-query/src/components/Core_LegendQueryApplicationPlugin.tsx index 5d5144645b..83c51d29ce 100644 --- a/packages/legend-application-query/src/components/Core_LegendQueryApplicationPlugin.tsx +++ b/packages/legend-application-query/src/components/Core_LegendQueryApplicationPlugin.tsx @@ -15,9 +15,10 @@ */ import { + type QueryGraphBuilderGetter, + type QuerySetupActionConfiguration, LegendQueryApplicationPlugin, QuerySetupActionTag, - type QuerySetupActionConfiguration, } from '../stores/LegendQueryApplicationPlugin.js'; import packageJson from '../../package.json' with { type: 'json' }; import type { QuerySetupLandingPageStore } from '../stores/QuerySetupStore.js'; @@ -50,10 +51,10 @@ import { LEGEND_QUERY_ROUTE_PATTERN, } from '../__lib__/LegendQueryNavigation.js'; import { - ActionAlertActionType, - ActionAlertType, type ApplicationPageEntry, type LegendApplicationSetup, + ActionAlertActionType, + ActionAlertType, } from '@finos/legend-application'; import { CloneQueryServiceSetup } from './CloneQueryServiceSetup.js'; import { QueryProductionizerSetup } from './QueryProductionizerSetup.js'; @@ -68,27 +69,60 @@ import { generateDataSpaceQuerySetupRoute, } from '../__lib__/DSL_DataSpace_LegendQueryNavigation.js'; import { - QUERY_BUILDER_SUPPORTED_GET_ALL_FUNCTIONS, + type QueryBuilderState, type QueryBuilderHeaderActionConfiguration, type QueryBuilderMenuActionConfiguration, + type QueryBuilderPropagateExecutionContextChangeHelper, + QUERY_BUILDER_SUPPORTED_GET_ALL_FUNCTIONS, } from '@finos/legend-query-builder'; import { + type QueryEditorStore, ExistingQueryEditorStore, QueryBuilderActionConfig_QueryApplication, } from '../stores/QueryEditorStore.js'; import { DataSpaceQueryBuilderState, + DataSpacesDepotRepository, generateDataSpaceTemplateQueryPromotionRoute, } from '@finos/legend-extension-dsl-data-space/application'; -import { RuntimePointer } from '@finos/legend-graph'; +import { + createGraphBuilderReport, + GRAPH_MANAGER_EVENT, + isValidFullPath, + LegendSDLC, + QUERY_PROFILE_PATH, + resolvePackagePathAndElementName, + RuntimePointer, + V1_EngineRuntime, + V1_Mapping, + V1_PackageableRuntime, + V1_PureGraphManager, +} from '@finos/legend-graph'; import { LegendQueryTelemetryHelper } from '../__lib__/LegendQueryTelemetryHelper.js'; -import { StoreProjectData } from '@finos/legend-server-depot'; -import { buildUrl } from '@finos/legend-shared'; +import { resolveVersion, StoreProjectData } from '@finos/legend-server-depot'; +import { + type GeneratorFn, + ActionState, + assertErrorThrown, + buildUrl, + getNullableFirstEntry, + guaranteeNonNullable, + guaranteeType, + LogEvent, + StopWatch, + uniq, +} from '@finos/legend-shared'; import { parseProjectIdentifier } from '@finos/legend-storage'; import { QueryEditorExistingQueryHeader } from './QueryEditor.js'; import { DataSpaceTemplateQueryCreatorStore } from '../stores/data-space/DataSpaceTemplateQueryCreatorStore.js'; import { createViewSDLCProjectHandler } from '../stores/data-space/DataSpaceQueryBuilderHelper.js'; import { DataSpaceQueryCreatorStore } from '../stores/data-space/DataSpaceQueryCreatorStore.js'; +import { + QUERY_PROFILE_TAG_DATA_SPACE, + resolveUsableDataSpaceClasses, +} from '@finos/legend-extension-dsl-data-space/graph'; +import { flowResult } from 'mobx'; +import { LEGEND_QUERY_APP_EVENT } from '../__lib__/LegendQueryEvent.js'; export class Core_LegendQueryApplicationPlugin extends LegendQueryApplicationPlugin { static NAME = packageJson.extensions.applicationQueryPlugin; @@ -727,4 +761,223 @@ export class Core_LegendQueryApplicationPlugin extends LegendQueryApplicationPlu }, }; } + + override getExtraQueryGraphBuilderGetters(): QueryGraphBuilderGetter[] { + return [ + ( + editorStore: QueryEditorStore, + ): ((editorStore: QueryEditorStore) => GeneratorFn) | undefined => { + function* buildGraph(): GeneratorFn { + // do nothing + } + if (editorStore instanceof ExistingQueryEditorStore) { + const query = editorStore.query; + const dataSpaceTaggedValue = query?.taggedValues?.find( + (taggedValue) => + taggedValue.profile === QUERY_PROFILE_PATH && + taggedValue.tag === QUERY_PROFILE_TAG_DATA_SPACE && + isValidFullPath(taggedValue.value), + ); + if (dataSpaceTaggedValue) { + return buildGraph; + } + } + return undefined; + }, + ]; + } + + getExtraQueryBuilderPropagateExecutionContextChangeHelper?(): QueryBuilderPropagateExecutionContextChangeHelper[] { + return [ + ( + queryBuilderState: QueryBuilderState, + isGraphBuildingNotRequired?: boolean, + ): (() => Promise) | undefined => { + /** + * Propagation after changing the execution context: + * - The mapping will be updated to the mapping of the execution context + * - The runtime will be updated to the default runtime of the execution context + * - If no class is chosen, try to choose a compatible class + * - If the chosen class is compatible with the new selected execution context mapping, do nothing, otherwise, try to choose a compatible class + */ + const propagateExecutionContextChange = async (): Promise => { + const dataSpaceQueryBuilderState = guaranteeType( + queryBuilderState, + DataSpaceQueryBuilderState, + ); + const mapping = + dataSpaceQueryBuilderState.executionContext.mapping.value; + const mappingModelCoverageAnalysisResult = + dataSpaceQueryBuilderState.dataSpaceAnalysisResult?.executionContextsIndex.get( + dataSpaceQueryBuilderState.executionContext.name, + )?.mappingModelCoverageAnalysisResult; + const editorStore = ( + queryBuilderState.workflowState + .actionConfig as QueryBuilderActionConfig_QueryApplication + ).editorStore; + if ( + dataSpaceQueryBuilderState.dataSpaceAnalysisResult && + mappingModelCoverageAnalysisResult + ) { + if (!isGraphBuildingNotRequired) { + try { + const stopWatch = new StopWatch(); + const graph = + dataSpaceQueryBuilderState.graphManagerState.createNewGraph(); + const graph_buildReport = createGraphBuilderReport(); + // Create dummy mappings and runtimes + // TODO?: these stubbed mappings and runtimes are not really useful that useful, so either we should + // simplify the model here or potentially refactor the backend analytics endpoint to return these as model + const mappingModels = uniq( + Array.from( + dataSpaceQueryBuilderState.dataSpaceAnalysisResult.executionContextsIndex.values(), + ).map((context) => context.mapping), + ).map((m) => { + const _mapping = new V1_Mapping(); + const [packagePath, name] = resolvePackagePathAndElementName( + m.path, + ); + _mapping.package = packagePath; + _mapping.name = name; + return guaranteeType( + dataSpaceQueryBuilderState.graphManagerState.graphManager, + V1_PureGraphManager, + ).elementProtocolToEntity(_mapping); + }); + const runtimeModels = uniq( + Array.from( + dataSpaceQueryBuilderState.dataSpaceAnalysisResult.executionContextsIndex.values(), + ) + .map((context) => context.defaultRuntime) + .concat( + Array.from( + dataSpaceQueryBuilderState.dataSpaceAnalysisResult.executionContextsIndex.values(), + ).flatMap((val) => val.compatibleRuntimes), + ), + ).map((r) => { + const runtime = new V1_PackageableRuntime(); + const [packagePath, name] = resolvePackagePathAndElementName( + r.path, + ); + runtime.package = packagePath; + runtime.name = name; + runtime.runtimeValue = new V1_EngineRuntime(); + return guaranteeType( + dataSpaceQueryBuilderState.graphManagerState.graphManager, + V1_PureGraphManager, + ).elementProtocolToEntity(runtime); + }); + const graphEntities = guaranteeNonNullable( + mappingModelCoverageAnalysisResult.entities, + ) + .concat(mappingModels) + .concat(runtimeModels) + // NOTE: if an element could be found in the graph already it means it comes from system + // so we could rid of it + .filter( + (el) => + !graph.getNullableElement(el.path, false) && + !el.path.startsWith('meta::'), + ); + let option; + if ( + dataSpaceQueryBuilderState.dataSpaceRepo instanceof + DataSpacesDepotRepository + ) { + option = new LegendSDLC( + dataSpaceQueryBuilderState.dataSpaceRepo.project.groupId, + dataSpaceQueryBuilderState.dataSpaceRepo.project.artifactId, + resolveVersion( + dataSpaceQueryBuilderState.dataSpaceRepo.project + .versionId, + ), + ); + } + if (option) { + await dataSpaceQueryBuilderState.graphManagerState.graphManager.buildGraphForQuery( + graph, + graphEntities, + ActionState.create(), + ); + } else { + await dataSpaceQueryBuilderState.graphManagerState.graphManager.buildGraphForQuery( + graph, + graphEntities, + ActionState.create(), + option, + graph_buildReport, + ); + } + dataSpaceQueryBuilderState.graphManagerState.graph = graph; + const dependency_buildReport = createGraphBuilderReport(); + // report + stopWatch.record(GRAPH_MANAGER_EVENT.INITIALIZE_GRAPH__SUCCESS); + const graphBuilderReportData = { + timings: + dataSpaceQueryBuilderState.applicationStore.timeService.finalizeTimingsRecord( + stopWatch, + ), + dependencies: dependency_buildReport, + dependenciesCount: + dataSpaceQueryBuilderState.graphManagerState.graph + .dependencyManager.numberOfDependencies, + graph: graph_buildReport, + }; + editorStore.logBuildGraphMetrics(graphBuilderReportData); + dataSpaceQueryBuilderState.applicationStore.logService.info( + LogEvent.create( + GRAPH_MANAGER_EVENT.INITIALIZE_GRAPH__SUCCESS, + ), + graphBuilderReportData, + ); + } catch (error) { + assertErrorThrown(error); + editorStore.applicationStore.logService.error( + LogEvent.create(LEGEND_QUERY_APP_EVENT.GENERIC_FAILURE), + error, + ); + editorStore.graphManagerState.graph = + editorStore.graphManagerState.createNewGraph(); + await flowResult(editorStore.buildFullGraph()); + } + } + dataSpaceQueryBuilderState.explorerState.mappingModelCoverageAnalysisResult = + mappingModelCoverageAnalysisResult; + } + const compatibleClasses = resolveUsableDataSpaceClasses( + dataSpaceQueryBuilderState.dataSpace, + mapping, + dataSpaceQueryBuilderState.graphManagerState, + dataSpaceQueryBuilderState, + ); + dataSpaceQueryBuilderState.changeMapping(mapping); + dataSpaceQueryBuilderState.changeRuntime( + new RuntimePointer( + dataSpaceQueryBuilderState.executionContext.defaultRuntime, + ), + ); + // if there is no chosen class or the chosen one is not compatible + // with the mapping then pick a compatible class if possible + if ( + !dataSpaceQueryBuilderState.class || + !compatibleClasses.includes(dataSpaceQueryBuilderState.class) + ) { + const possibleNewClass = getNullableFirstEntry(compatibleClasses); + if (possibleNewClass) { + dataSpaceQueryBuilderState.changeClass(possibleNewClass); + } + } + dataSpaceQueryBuilderState.explorerState.refreshTreeData(); + }; + if ( + queryBuilderState instanceof DataSpaceQueryBuilderState && + queryBuilderState.workflowState.actionConfig instanceof + QueryBuilderActionConfig_QueryApplication + ) { + return propagateExecutionContextChange; + } + return undefined; + }, + ]; + } } diff --git a/packages/legend-application-query/src/components/QueryEditor.tsx b/packages/legend-application-query/src/components/QueryEditor.tsx index 2feb79c4ef..c9cafbcb14 100644 --- a/packages/legend-application-query/src/components/QueryEditor.tsx +++ b/packages/legend-application-query/src/components/QueryEditor.tsx @@ -470,13 +470,21 @@ const QueryEditorExistingQueryInfoModal = observer(
Mapping
- {executionContext.mapping.value.name} + { + updateState.editorStore.graphManagerState.graph.getMapping( + executionContext.mapping, + ).name + }
Runtime
- {executionContext.runtime.value.name} + { + updateState.editorStore.graphManagerState.graph.getRuntime( + executionContext.runtime, + ).name + }
diff --git a/packages/legend-application-query/src/components/__test-utils__/QueryEditorComponentTestUtils.tsx b/packages/legend-application-query/src/components/__test-utils__/QueryEditorComponentTestUtils.tsx index 8152a55880..ffc95244bf 100644 --- a/packages/legend-application-query/src/components/__test-utils__/QueryEditorComponentTestUtils.tsx +++ b/packages/legend-application-query/src/components/__test-utils__/QueryEditorComponentTestUtils.tsx @@ -22,7 +22,6 @@ import { Query, LightQuery, RawLambda, - PackageableElementExplicitReference, type RawMappingModelCoverageAnalysisResult, QueryExplicitExecutionContext, } from '@finos/legend-graph'; @@ -132,11 +131,11 @@ export const TEST__setUpQueryEditor = async ( query.owner = lightQuery.owner; query.isCurrentUserQuery = lightQuery.isCurrentUserQuery; const _mapping = graphManagerState.graph.getMapping(mappingPath); + query.mapping = mappingPath; + query.runtime = runtimePath; const execContext = new QueryExplicitExecutionContext(); - execContext.mapping = PackageableElementExplicitReference.create(_mapping); - execContext.runtime = PackageableElementExplicitReference.create( - graphManagerState.graph.getRuntime(runtimePath), - ); + execContext.mapping = mappingPath; + execContext.runtime = runtimePath; query.executionContext = execContext; query.content = 'some content'; createSpy( diff --git a/packages/legend-application-query/src/stores/LegendQueryApplicationPlugin.tsx b/packages/legend-application-query/src/stores/LegendQueryApplicationPlugin.tsx index 02c1b5034c..a3959856d6 100644 --- a/packages/legend-application-query/src/stores/LegendQueryApplicationPlugin.tsx +++ b/packages/legend-application-query/src/stores/LegendQueryApplicationPlugin.tsx @@ -15,17 +15,19 @@ */ import { LegendApplicationPlugin } from '@finos/legend-application'; -import type { Query } from '@finos/legend-graph'; import type { - QueryBuilderState, QueryBuilder_LegendApplicationPlugin_Extension, + QueryBuilderState, } from '@finos/legend-query-builder'; +import type { GeneratorFn } from '@finos/legend-shared'; +import type React from 'react'; import type { LegendQueryPluginManager } from '../application/LegendQueryPluginManager.js'; -import { - type ExistingQueryEditorStore, - type QueryEditorStore, +import type { + ExistingQueryEditorStore, + QueryEditorStore, } from './QueryEditorStore.js'; import type { QuerySetupLandingPageStore } from './QuerySetupStore.js'; +import type { Query } from '@finos/legend-graph'; export enum QuerySetupActionTag { PRODUCTIONIZATION = 'Productionization', @@ -63,6 +65,10 @@ export type NewQueryNavigationPath = ( editorStore: ExistingQueryEditorStore, ) => string | undefined; +export type QueryGraphBuilderGetter = ( + editorStore: QueryEditorStore, +) => ((editorStore: QueryEditorStore) => GeneratorFn) | undefined; + export type QueryEditorActionConfiguration = { key: string; renderer: ( @@ -97,4 +103,9 @@ export class LegendQueryApplicationPlugin * Get the list of actions (configurations) for query setup. */ getExtraQuerySetupActionConfigurations?(): QuerySetupActionConfiguration[]; + + /** + * Get the list of query graph builders + */ + getExtraQueryGraphBuilderGetters?(): QueryGraphBuilderGetter[]; } diff --git a/packages/legend-application-query/src/stores/QueryEditorStore.ts b/packages/legend-application-query/src/stores/QueryEditorStore.ts index 72219bf6a3..3428974e13 100644 --- a/packages/legend-application-query/src/stores/QueryEditorStore.ts +++ b/packages/legend-application-query/src/stores/QueryEditorStore.ts @@ -90,6 +90,7 @@ import { StoreProjectData, LATEST_VERSION_ALIAS, VersionedProjectData, + retrieveProjectEntitiesWithDependencies, } from '@finos/legend-server-depot'; import { ActionAlertActionType, @@ -119,9 +120,9 @@ import { type DataSpaceInfo, } from '@finos/legend-extension-dsl-data-space/application'; import { - DSL_DataSpace_getGraphManagerExtension, type DataSpace, type DataSpaceExecutionContext, + DSL_DataSpace_getGraphManagerExtension, getOwnDataSpace, retrieveAnalyticsResultCache, } from '@finos/legend-extension-dsl-data-space/graph'; @@ -289,6 +290,7 @@ export abstract class QueryEditorStore { setShowDataspaceInfo: action, initialize: flow, buildGraph: flow, + buildFullGraph: flow, searchExistingQueryName: flow, }); @@ -409,6 +411,10 @@ export abstract class QueryEditorStore { // do nothing } + requiresGraphBuilding(): boolean { + return true; + } + async buildQueryForPersistence( query: Query, rawLambda: RawLambda, @@ -425,6 +431,13 @@ export abstract class QueryEditorStore { this.queryBuilderState.executionContextState.mapping, 'Query required mapping to update', ); + const runtimeValue = guaranteeType( + this.queryBuilderState.executionContextState.runtimeValue, + RuntimePointer, + 'Query runtime must be of type runtime pointer', + ); + query.mapping = this.queryBuilderState.executionContextState.mapping.path; + query.runtime = runtimeValue.packageableRuntime.value.path; query.executionContext = this.queryBuilderState.getQueryExecutionContext(); query.content = @@ -563,7 +576,7 @@ export abstract class QueryEditorStore { ); } - *buildGraph(): GeneratorFn { + *buildFullGraph(): GeneratorFn { const stopWatch = new StopWatch(); const projectInfo = this.getProjectInfo(); @@ -662,6 +675,20 @@ export abstract class QueryEditorStore { ); } } + + *buildGraph(): GeneratorFn { + const queryGraphBuilderGetters = this.applicationStore.pluginManager + .getApplicationPlugins() + .flatMap((plugin) => plugin.getExtraQueryGraphBuilderGetters?.() ?? []); + for (const getter of queryGraphBuilderGetters) { + const builderFunction = getter(this); + if (builderFunction) { + yield flowResult(builderFunction(this)); + return; + } + } + yield flowResult(this.buildFullGraph()); + } } export class QueryBuilderActionConfig_QueryApplication extends QueryBuilderActionConfig { @@ -1255,8 +1282,15 @@ export class ExistingQueryEditorStore extends QueryEditorStore { } override async setUpEditorState(): Promise { - this.setLightQuery( - await this.graphManagerState.graphManager.getLightQuery(this.queryId), + const query = await this.graphManagerState.graphManager.getQuery( + this.queryId, + this.graphManagerState.graph, + ); + this.setQuery(query); + this.setLightQuery(toLightQuery(query)); + LegendQueryUserDataHelper.addRecentlyViewedQuery( + this.applicationStore.userDataService, + query.id, ); } @@ -1267,35 +1301,93 @@ export class ExistingQueryEditorStore extends QueryEditorStore { exec.dataSpacePath, this.graphManagerState.graph, ); + const mapping = query.mapping + ? this.graphManagerState.graph.getMapping(query.mapping) + : undefined; + const runtime = query.runtime + ? this.graphManagerState.graph.getRuntime(query.runtime) + : undefined; const matchingExecutionContext = resolveExecutionContext( dataSpace, exec.executionKey, - query.mapping?.value, - query.runtime?.value, + mapping, + runtime, ); if (matchingExecutionContext) { let dataSpaceAnalysisResult; + let isLightGraphEnabled = true; try { + this.initState.setMessage('Fetching dataspace analysis result'); const project = StoreProjectData.serialization.fromJson( await this.depotServerClient.getProject( query.groupId, query.artifactId, ), ); + const graph_buildReport = createGraphBuilderReport(); + const stopWatch = new StopWatch(); + // initialize system + stopWatch.record(); + await this.graphManagerState.initializeSystem(); + stopWatch.record( + GRAPH_MANAGER_EVENT.INITIALIZE_GRAPH_SYSTEM__SUCCESS, + ); + const dependency_buildReport = createGraphBuilderReport(); dataSpaceAnalysisResult = await DSL_DataSpace_getGraphManagerExtension( this.graphManagerState.graphManager, - ).retrieveDataSpaceAnalysisFromCache(() => - retrieveAnalyticsResultCache( - project, - query.versionId, - dataSpace.path, - this.depotServerClient, - ), + ).analyzeDataSpaceCoverage( + exec.dataSpacePath, + () => + retrieveProjectEntitiesWithDependencies( + project, + query.versionId, + this.depotServerClient, + ), + () => + retrieveAnalyticsResultCache( + project, + query.versionId, + exec.dataSpacePath, + this.depotServerClient, + ), + undefined, + graph_buildReport, + this.graphManagerState.graph, + undefined, + query.mapping, + this.getProjectInfo(), + this.applicationStore.notificationService, ); - } catch { - // do nothing + // report + stopWatch.record(GRAPH_MANAGER_EVENT.INITIALIZE_GRAPH__SUCCESS); + const graphBuilderReportData = { + timings: + this.applicationStore.timeService.finalizeTimingsRecord( + stopWatch, + ), + dependencies: dependency_buildReport, + dependenciesCount: + this.graphManagerState.graph.dependencyManager + .numberOfDependencies, + graph: graph_buildReport, + }; + this.logBuildGraphMetrics(graphBuilderReportData); + this.applicationStore.logService.info( + LogEvent.create(GRAPH_MANAGER_EVENT.INITIALIZE_GRAPH__SUCCESS), + graphBuilderReportData, + ); + } catch (error) { + this.applicationStore.logService.error( + LogEvent.create(LEGEND_QUERY_APP_EVENT.GENERIC_FAILURE), + error, + ); + isLightGraphEnabled = false; + this.graphManagerState.graph = + this.graphManagerState.createNewGraph(); + await flowResult(this.buildFullGraph()); } + const sourceInfo = { groupId: query.groupId, artifactId: query.artifactId, @@ -1321,7 +1413,7 @@ export class ExistingQueryEditorStore extends QueryEditorStore { (dataSpaceInfo: DataSpaceInfo) => hasDataSpaceInfoBeenVisited(dataSpaceInfo, visitedDataSpaces), ), - (dataSpaceInfo: DataSpaceInfo) => { + async (dataSpaceInfo: DataSpaceInfo) => { if (dataSpaceInfo.defaultExecutionContext) { const proceed = (): void => this.applicationStore.navigationService.navigator.goToLocation( @@ -1384,6 +1476,7 @@ export class ExistingQueryEditorStore extends QueryEditorStore { ); } }, + isLightGraphEnabled, dataSpaceAnalysisResult, undefined, undefined, @@ -1433,12 +1526,18 @@ export class ExistingQueryEditorStore extends QueryEditorStore { new QueryBuilderActionConfig_QueryApplication(this), ); classQueryBuilderState.executionContextState.setMapping( - exec.mapping.value, + exec.mapping + ? this.graphManagerState.graph.getMapping(exec.mapping) + : undefined, ); classQueryBuilderState.executionContextState.setRuntimeValue( - new RuntimePointer( - PackageableElementExplicitReference.create(exec.runtime.value), - ), + exec.runtime + ? new RuntimePointer( + PackageableElementExplicitReference.create( + this.graphManagerState.graph.getRuntime(exec.runtime), + ), + ) + : undefined, ); return classQueryBuilderState; } diff --git a/packages/legend-application-query/src/stores/data-space/DataSpaceQueryCreatorStore.ts b/packages/legend-application-query/src/stores/data-space/DataSpaceQueryCreatorStore.ts index 860ab04873..f7a38e9968 100644 --- a/packages/legend-application-query/src/stores/data-space/DataSpaceQueryCreatorStore.ts +++ b/packages/legend-application-query/src/stores/data-space/DataSpaceQueryCreatorStore.ts @@ -22,14 +22,18 @@ import { RuntimePointer, PackageableElementExplicitReference, QueryProjectCoordinates, + createGraphBuilderReport, + GRAPH_MANAGER_EVENT, } from '@finos/legend-graph'; import { type DepotServerClient, StoreProjectData, LATEST_VERSION_ALIAS, + retrieveProjectEntitiesWithDependencies, } from '@finos/legend-server-depot'; import { LogEvent, + StopWatch, assertErrorThrown, assertTrue, guaranteeNonNullable, @@ -182,6 +186,10 @@ export class DataSpaceQueryCreatorStore extends QueryEditorStore { super.initialize(); } + override requiresGraphBuilding(): boolean { + return false; + } + async initializeQueryBuilderState(): Promise { if (this.queryableDataSpace) { return this.initializeQueryBuilderStateWithQueryableDataSpace( @@ -277,37 +285,91 @@ export class DataSpaceQueryCreatorStore extends QueryEditorStore { async initializeQueryBuilderStateWithQueryableDataSpace( queryableDataSpace: QueryableDataSpace, ): Promise { - const dataSpace = getDataSpace( - queryableDataSpace.dataSpacePath, - this.graphManagerState.graph, - ); - const executionContext = guaranteeNonNullable( - dataSpace.executionContexts.find( - (context) => context.name === queryableDataSpace.executionContext, - ), - `Can't find execution context '${queryableDataSpace.executionContext}'`, - ); let dataSpaceAnalysisResult; + let isLightGraphEnabled = true; try { + const stopWatch = new StopWatch(); const project = StoreProjectData.serialization.fromJson( await this.depotServerClient.getProject( queryableDataSpace.groupId, queryableDataSpace.artifactId, ), ); + this.initState.setMessage('Fetching dataspace analysis result'); + // initialize system + stopWatch.record(); + await this.graphManagerState.initializeSystem(); + stopWatch.record(GRAPH_MANAGER_EVENT.INITIALIZE_GRAPH_SYSTEM__SUCCESS); + + const graph_buildReport = createGraphBuilderReport(); + const dependency_buildReport = createGraphBuilderReport(); dataSpaceAnalysisResult = await DSL_DataSpace_getGraphManagerExtension( this.graphManagerState.graphManager, - ).retrieveDataSpaceAnalysisFromCache(() => - retrieveAnalyticsResultCache( - project, - queryableDataSpace.versionId, - dataSpace.path, - this.depotServerClient, - ), + ).analyzeDataSpaceCoverage( + queryableDataSpace.dataSpacePath, + () => + retrieveProjectEntitiesWithDependencies( + project, + queryableDataSpace.versionId, + this.depotServerClient, + ), + () => + retrieveAnalyticsResultCache( + project, + queryableDataSpace.versionId, + queryableDataSpace.dataSpacePath, + this.depotServerClient, + ), + undefined, + graph_buildReport, + this.graphManagerState.graph, + queryableDataSpace.executionContext, + undefined, + this.getProjectInfo(), + this.applicationStore.notificationService, + ); + const mappingPath = dataSpaceAnalysisResult.executionContextsIndex.get( + queryableDataSpace.executionContext, + )?.mapping; + if (mappingPath) { + // report + stopWatch.record(GRAPH_MANAGER_EVENT.INITIALIZE_GRAPH__SUCCESS); + const graphBuilderReportData = { + timings: + this.applicationStore.timeService.finalizeTimingsRecord(stopWatch), + dependencies: dependency_buildReport, + dependenciesCount: + this.graphManagerState.graph.dependencyManager.numberOfDependencies, + graph: graph_buildReport, + }; + this.applicationStore.logService.info( + LogEvent.create(GRAPH_MANAGER_EVENT.INITIALIZE_GRAPH__SUCCESS), + graphBuilderReportData, + ); + } else { + isLightGraphEnabled = false; + this.graphManagerState.graph = this.graphManagerState.createNewGraph(); + await flowResult(this.buildFullGraph()); + } + } catch (error) { + this.applicationStore.logService.error( + LogEvent.create(LEGEND_QUERY_APP_EVENT.GENERIC_FAILURE), + error, ); - } catch { - // do nothing + this.graphManagerState.graph = this.graphManagerState.createNewGraph(); + isLightGraphEnabled = false; + await flowResult(this.buildFullGraph()); } + const dataSpace = getDataSpace( + queryableDataSpace.dataSpacePath, + this.graphManagerState.graph, + ); + const executionContext = guaranteeNonNullable( + dataSpace.executionContexts.find( + (context) => context.name === queryableDataSpace.executionContext, + ), + `Can't find execution context '${queryableDataSpace.executionContext}'`, + ); const sourceInfo = { groupId: queryableDataSpace.groupId, artifactId: queryableDataSpace.artifactId, @@ -333,11 +395,12 @@ export class DataSpaceQueryCreatorStore extends QueryEditorStore { (dataSpaceInfo: DataSpaceInfo) => hasDataSpaceInfoBeenVisited(dataSpaceInfo, visitedDataSpaces), ), - (dataSpaceInfo: DataSpaceInfo) => { + async (dataSpaceInfo: DataSpaceInfo) => { flowResult(this.changeDataSpace(dataSpaceInfo)).catch( this.applicationStore.alertUnhandledError, ); }, + isLightGraphEnabled, dataSpaceAnalysisResult, (ec: DataSpaceExecutionContext) => { returnUndefOnError(() => @@ -361,7 +424,7 @@ export class DataSpaceQueryCreatorStore extends QueryEditorStore { ); } queryBuilderState.setExecutionContext(executionContext); - queryBuilderState.propagateExecutionContextChange(executionContext); + await queryBuilderState.propagateExecutionContextChange(true); // set runtime if already chosen if (queryableDataSpace.runtimePath) { @@ -417,6 +480,10 @@ export class DataSpaceQueryCreatorStore extends QueryEditorStore { } } + override *buildGraph(): GeneratorFn { + // do nothing + } + addVisitedDataSpace(queryableDataSpace: QueryableDataSpace): void { try { LegendQueryUserDataHelper.addVisitedDatspace( diff --git a/packages/legend-application-query/src/stores/data-space/DataSpaceTemplateQueryCreatorStore.ts b/packages/legend-application-query/src/stores/data-space/DataSpaceTemplateQueryCreatorStore.ts index 0e625799a3..b0e8af2567 100644 --- a/packages/legend-application-query/src/stores/data-space/DataSpaceTemplateQueryCreatorStore.ts +++ b/packages/legend-application-query/src/stores/data-space/DataSpaceTemplateQueryCreatorStore.ts @@ -35,9 +35,9 @@ import { parseGACoordinates, } from '@finos/legend-storage'; import { + type QueryPersistConfiguration, QueryBuilderActionConfig_QueryApplication, QueryEditorStore, - type QueryPersistConfiguration, } from '../QueryEditorStore.js'; import type { LegendQueryApplicationStore } from '../LegendQueryBaseStore.js'; import { @@ -167,11 +167,12 @@ export class DataSpaceTemplateQueryCreatorStore extends QueryEditorStore { this.versionId, undefined, ), - (dataSpaceInfo: DataSpaceInfo) => { + async (dataSpaceInfo: DataSpaceInfo) => { this.applicationStore.notificationService.notifyWarning( `Can't switch data space to visit current template query`, ); }, + false, dataSpaceAnalysisResult, undefined, undefined, @@ -180,7 +181,7 @@ export class DataSpaceTemplateQueryCreatorStore extends QueryEditorStore { sourceInfo, ); queryBuilderState.setExecutionContext(executionContext); - queryBuilderState.propagateExecutionContextChange(executionContext); + await queryBuilderState.propagateExecutionContextChange(); queryBuilderState.initializeWithQuery(query); return queryBuilderState; } diff --git a/packages/legend-application-studio/src/components/editor/editor-group/function-activator/FunctionEditor.tsx b/packages/legend-application-studio/src/components/editor/editor-group/function-activator/FunctionEditor.tsx index 1e077e052f..31ad6d2e06 100644 --- a/packages/legend-application-studio/src/components/editor/editor-group/function-activator/FunctionEditor.tsx +++ b/packages/legend-application-studio/src/components/editor/editor-group/function-activator/FunctionEditor.tsx @@ -1236,7 +1236,7 @@ export const FunctionEditor = observer(() => { ); await flowResult( embeddedQueryBuilderState.setEmbeddedQueryBuilderConfiguration({ - setupQueryBuilderState: (): QueryBuilderState => + setupQueryBuilderState: async (): Promise => functionQueryBuilderState, actionConfigs: [ { diff --git a/packages/legend-application-studio/src/components/editor/editor-group/mapping-editor/MappingExecutionBuilder.tsx b/packages/legend-application-studio/src/components/editor/editor-group/mapping-editor/MappingExecutionBuilder.tsx index ac7dee2c1b..03bf2f6c64 100644 --- a/packages/legend-application-studio/src/components/editor/editor-group/mapping-editor/MappingExecutionBuilder.tsx +++ b/packages/legend-application-studio/src/components/editor/editor-group/mapping-editor/MappingExecutionBuilder.tsx @@ -209,7 +209,7 @@ const MappingExecutionQueryEditor = observer( const embeddedQueryBuilderState = editorStore.embeddedQueryBuilderState; await flowResult( embeddedQueryBuilderState.setEmbeddedQueryBuilderConfiguration({ - setupQueryBuilderState: (): QueryBuilderState => { + setupQueryBuilderState: async (): Promise => { const queryBuilderState = new MappingExecutionQueryBuilderState( embeddedQueryBuilderState.editorStore.applicationStore, embeddedQueryBuilderState.editorStore.graphManagerState, diff --git a/packages/legend-application-studio/src/components/editor/editor-group/mapping-editor/MappingTestableEditor.tsx b/packages/legend-application-studio/src/components/editor/editor-group/mapping-editor/MappingTestableEditor.tsx index bb01c597e4..050ca9d27d 100644 --- a/packages/legend-application-studio/src/components/editor/editor-group/mapping-editor/MappingTestableEditor.tsx +++ b/packages/legend-application-studio/src/components/editor/editor-group/mapping-editor/MappingTestableEditor.tsx @@ -626,7 +626,7 @@ const MappingTestSuiteQueryEditor = observer( const embeddedQueryBuilderState = editorStore.embeddedQueryBuilderState; await flowResult( embeddedQueryBuilderState.setEmbeddedQueryBuilderConfiguration({ - setupQueryBuilderState: (): QueryBuilderState => { + setupQueryBuilderState: async (): Promise => { const queryBuilderState = new MappingExecutionQueryBuilderState( embeddedQueryBuilderState.editorStore.applicationStore, embeddedQueryBuilderState.editorStore.graphManagerState, diff --git a/packages/legend-application-studio/src/components/editor/editor-group/mapping-editor/legacy/DEPRECATED__MappingTestEditor.tsx b/packages/legend-application-studio/src/components/editor/editor-group/mapping-editor/legacy/DEPRECATED__MappingTestEditor.tsx index aaf830bd16..f9c08923a4 100644 --- a/packages/legend-application-studio/src/components/editor/editor-group/mapping-editor/legacy/DEPRECATED__MappingTestEditor.tsx +++ b/packages/legend-application-studio/src/components/editor/editor-group/mapping-editor/legacy/DEPRECATED__MappingTestEditor.tsx @@ -17,7 +17,7 @@ import { useState, useEffect, useCallback } from 'react'; import { observer } from 'mobx-react-lite'; import { - type DEPRECATED__MappingTestState as DEPRECATED__MappingTestState, + type DEPRECATED__MappingTestState, MAPPING_TEST_EDITOR_TAB_TYPE, TEST_RESULT, MappingTestObjectInputDataState, @@ -112,7 +112,7 @@ const MappingTestQueryEditor = observer( const embeddedQueryBuilderState = editorStore.embeddedQueryBuilderState; await flowResult( embeddedQueryBuilderState.setEmbeddedQueryBuilderConfiguration({ - setupQueryBuilderState: (): QueryBuilderState => { + setupQueryBuilderState: async (): Promise => { const queryBuilderState = new MappingExecutionQueryBuilderState( embeddedQueryBuilderState.editorStore.applicationStore, embeddedQueryBuilderState.editorStore.graphManagerState, diff --git a/packages/legend-application-studio/src/components/editor/editor-group/service-editor/ServiceExecutionQueryEditor.tsx b/packages/legend-application-studio/src/components/editor/editor-group/service-editor/ServiceExecutionQueryEditor.tsx index 6ecf0f0532..1d5122af67 100644 --- a/packages/legend-application-studio/src/components/editor/editor-group/service-editor/ServiceExecutionQueryEditor.tsx +++ b/packages/legend-application-studio/src/components/editor/editor-group/service-editor/ServiceExecutionQueryEditor.tsx @@ -137,7 +137,7 @@ export const ServiceExecutionQueryEditor = observer( executionState.selectedExecutionContextState; await flowResult( embeddedQueryBuilderState.setEmbeddedQueryBuilderConfiguration({ - setupQueryBuilderState: (): QueryBuilderState => { + setupQueryBuilderState: async (): Promise => { const sourceInfo = { service: service.path, ...editorStore.editorMode.getSourceInfo(), @@ -488,7 +488,7 @@ export const queryService = async ( }; await flowResult( embeddedQueryBuilderState.setEmbeddedQueryBuilderConfiguration({ - setupQueryBuilderState: (): QueryBuilderState => { + setupQueryBuilderState: async (): Promise => { const queryBuilderState = new ServiceQueryBuilderState( embeddedQueryBuilderState.editorStore.applicationStore, embeddedQueryBuilderState.editorStore.graphManagerState, diff --git a/packages/legend-application-studio/src/components/editor/editor-group/uml-editor/ClassQueryBuilder.tsx b/packages/legend-application-studio/src/components/editor/editor-group/uml-editor/ClassQueryBuilder.tsx index 6e5d8f9f11..dd01bab989 100644 --- a/packages/legend-application-studio/src/components/editor/editor-group/uml-editor/ClassQueryBuilder.tsx +++ b/packages/legend-application-studio/src/components/editor/editor-group/uml-editor/ClassQueryBuilder.tsx @@ -376,7 +376,7 @@ export const queryClass = async ( const embeddedQueryBuilderState = editorStore.embeddedQueryBuilderState; await flowResult( embeddedQueryBuilderState.setEmbeddedQueryBuilderConfiguration({ - setupQueryBuilderState: () => { + setupQueryBuilderState: async () => { const queryBuilderState = new ClassQueryBuilderState( embeddedQueryBuilderState.editorStore.applicationStore, embeddedQueryBuilderState.editorStore.graphManagerState, diff --git a/packages/legend-application-studio/src/stores/editor/EmbeddedQueryBuilderState.ts b/packages/legend-application-studio/src/stores/editor/EmbeddedQueryBuilderState.ts index 03581d7bd7..7b5f5ee2eb 100644 --- a/packages/legend-application-studio/src/stores/editor/EmbeddedQueryBuilderState.ts +++ b/packages/legend-application-studio/src/stores/editor/EmbeddedQueryBuilderState.ts @@ -30,7 +30,7 @@ type EmbeddedQueryBuilderActionConfiguration = { }; type EmbeddedQueryBuilderConfiguration = { - setupQueryBuilderState: () => QueryBuilderState; + setupQueryBuilderState: () => Promise; disableCompile?: boolean | undefined; actionConfigs: EmbeddedQueryBuilderActionConfiguration[]; }; @@ -100,7 +100,8 @@ export class EmbeddedQueryBuilderState { } } if (!this.editorStore.graphState.error) { - this.queryBuilderState = config.setupQueryBuilderState(); + this.queryBuilderState = + (yield config.setupQueryBuilderState()) as QueryBuilderState; this.actionConfigs = config.actionConfigs; this.editorStore.applicationStore.layoutService.setBackdropContainerElementID( QUERY_BUILDER_COMPONENT_ELEMENT_ID.BACKDROP_CONTAINER, diff --git a/packages/legend-application-studio/src/stores/editor/editor-state/element-editor-state/database/QueryDatabaseState.ts b/packages/legend-application-studio/src/stores/editor/editor-state/element-editor-state/database/QueryDatabaseState.ts index 86f5594d7d..e52acad8f3 100644 --- a/packages/legend-application-studio/src/stores/editor/editor-state/element-editor-state/database/QueryDatabaseState.ts +++ b/packages/legend-application-studio/src/stores/editor/editor-state/element-editor-state/database/QueryDatabaseState.ts @@ -226,7 +226,7 @@ export class QueryDatabaseState { ); yield flowResult( embeddedQueryBuilderState.setEmbeddedQueryBuilderConfiguration({ - setupQueryBuilderState: () => queryBuilderState, + setupQueryBuilderState: async () => queryBuilderState, actionConfigs: [], }), ); diff --git a/packages/legend-application-studio/src/stores/editor/editor-state/end-to-end-workflow-state/QueryConnectionEndToEndWorkflowEditorState.ts b/packages/legend-application-studio/src/stores/editor/editor-state/end-to-end-workflow-state/QueryConnectionEndToEndWorkflowEditorState.ts index 24fc11e1bc..e9371d1f5c 100644 --- a/packages/legend-application-studio/src/stores/editor/editor-state/end-to-end-workflow-state/QueryConnectionEndToEndWorkflowEditorState.ts +++ b/packages/legend-application-studio/src/stores/editor/editor-state/end-to-end-workflow-state/QueryConnectionEndToEndWorkflowEditorState.ts @@ -209,7 +209,7 @@ export class QueryConnectionConfirmationAndGrammarEditorStepperState extends Con yield flowResult( this.editorStore.embeddedQueryBuilderState.setEmbeddedQueryBuilderConfiguration( { - setupQueryBuilderState: () => queryBuilderState, + setupQueryBuilderState: async () => queryBuilderState, actionConfigs: [], }, ), diff --git a/packages/legend-extension-dsl-data-space-studio/src/components/DataSpaceQueryAction.tsx b/packages/legend-extension-dsl-data-space-studio/src/components/DataSpaceQueryAction.tsx index aceb3d325b..49fa173b59 100644 --- a/packages/legend-extension-dsl-data-space-studio/src/components/DataSpaceQueryAction.tsx +++ b/packages/legend-extension-dsl-data-space-studio/src/components/DataSpaceQueryAction.tsx @@ -39,7 +39,7 @@ export const queryDataSpace = async ( const embeddedQueryBuilderState = editorStore.embeddedQueryBuilderState; await flowResult( embeddedQueryBuilderState.setEmbeddedQueryBuilderConfiguration({ - setupQueryBuilderState: () => { + setupQueryBuilderState: async () => { const sourceInfo = Object.assign( {}, editorStore.editorMode.getSourceInfo(), @@ -55,7 +55,7 @@ export const queryDataSpace = async ( dataSpace, dataSpace.defaultExecutionContext, undefined, - (dataSpaceInfo: DataSpaceInfo) => { + async (dataSpaceInfo: DataSpaceInfo) => { queryBuilderState.dataSpace = guaranteeType( queryBuilderState.graphManagerState.graph.getElement( dataSpaceInfo.path, @@ -65,10 +65,9 @@ export const queryDataSpace = async ( queryBuilderState.setExecutionContext( queryBuilderState.dataSpace.defaultExecutionContext, ); - queryBuilderState.propagateExecutionContextChange( - queryBuilderState.dataSpace.defaultExecutionContext, - ); + await queryBuilderState.propagateExecutionContextChange(); }, + false, undefined, undefined, undefined, @@ -79,9 +78,7 @@ export const queryDataSpace = async ( queryBuilderState.setExecutionContext( dataSpace.defaultExecutionContext, ); - queryBuilderState.propagateExecutionContextChange( - dataSpace.defaultExecutionContext, - ); + await queryBuilderState.propagateExecutionContextChange(); return queryBuilderState; }, actionConfigs: [], diff --git a/packages/legend-extension-dsl-data-space/src/components/DSL_DataSpace_LegendApplicationPlugin.tsx b/packages/legend-extension-dsl-data-space/src/components/DSL_DataSpace_LegendApplicationPlugin.tsx index 79d0d95f5f..53e1301500 100644 --- a/packages/legend-extension-dsl-data-space/src/components/DSL_DataSpace_LegendApplicationPlugin.tsx +++ b/packages/legend-extension-dsl-data-space/src/components/DSL_DataSpace_LegendApplicationPlugin.tsx @@ -15,11 +15,11 @@ */ import { - LegendApplicationPlugin, type LegendApplicationSetup, type LegendApplicationPluginManager, - collectKeyedCommandConfigEntriesFromConfig, type KeyedCommandConfigEntry, + collectKeyedCommandConfigEntriesFromConfig, + LegendApplicationPlugin, } from '@finos/legend-application'; import packageJson from '../../package.json' with { type: 'json' }; import type { @@ -175,10 +175,10 @@ export class DSL_DataSpace_LegendApplicationPlugin } return []; }, - loadCuratedTemplateQuery: ( + loadCuratedTemplateQuery: async ( templateQuery: CuratedTemplateQuery, queryBuilderState: QueryBuilderState, - ): void => { + ): Promise => { if (queryBuilderState instanceof DataSpaceQueryBuilderState) { if ( queryBuilderState.executionContext.name !== @@ -190,9 +190,7 @@ export class DSL_DataSpace_LegendApplicationPlugin ); if (executionContext) { queryBuilderState.setExecutionContext(executionContext); - queryBuilderState.propagateExecutionContextChange( - executionContext, - ); + await queryBuilderState.propagateExecutionContextChange(); queryBuilderState.initializeWithQuery(templateQuery.query); queryBuilderState.onExecutionContextChange?.(executionContext); } diff --git a/packages/legend-extension-dsl-data-space/src/components/query-builder/DataSpaceQueryBuilder.tsx b/packages/legend-extension-dsl-data-space/src/components/query-builder/DataSpaceQueryBuilder.tsx index 11ddebfd9f..6c05a6c01a 100644 --- a/packages/legend-extension-dsl-data-space/src/components/query-builder/DataSpaceQueryBuilder.tsx +++ b/packages/legend-extension-dsl-data-space/src/components/query-builder/DataSpaceQueryBuilder.tsx @@ -45,6 +45,7 @@ import { } from '@finos/legend-query-builder'; import { type Runtime, + type PackageableRuntime, getMappingCompatibleRuntimes, PackageableElementExplicitReference, RuntimePointer, @@ -89,6 +90,25 @@ export const formatDataSpaceOptionLabel = ( ); +const resolveExecutionContextRuntimes = ( + queryBuilderState: DataSpaceQueryBuilderState, +): PackageableRuntime[] => { + if (queryBuilderState.dataSpaceAnalysisResult) { + const executionContext = Array.from( + queryBuilderState.dataSpaceAnalysisResult.executionContextsIndex.values(), + ).find( + (e) => + e.mapping.path === + queryBuilderState.executionContext.mapping.value.path, + ); + return guaranteeNonNullable(executionContext).compatibleRuntimes; + } + return getMappingCompatibleRuntimes( + queryBuilderState.executionContext.mapping.value, + queryBuilderState.graphManagerState.usableRuntimes, + ); +}; + export type ExecutionContextOption = { label: string; value: DataSpaceExecutionContext; @@ -152,7 +172,9 @@ const DataSpaceQueryBuilderSetupPanelContent = observer( }, }; const onDataSpaceOptionChange = (option: DataSpaceOption): void => { - queryBuilderState.onDataSpaceChange(option.value); + queryBuilderState + .onDataSpaceChange(option.value) + .catch(queryBuilderState.applicationStore.alertUnhandledError); }; const openDataSpaceAdvancedSearch = (): void => { @@ -170,22 +192,20 @@ const DataSpaceQueryBuilderSetupPanelContent = observer( const selectedExecutionContextOption = buildExecutionContextOption( queryBuilderState.executionContext, ); - const onExecutionContextOptionChange = ( + + const onExecutionContextOptionChange = async ( option: ExecutionContextOption, - ): void => { + ): Promise => { if (option.value === queryBuilderState.executionContext) { return; } queryBuilderState.setExecutionContext(option.value); - queryBuilderState.propagateExecutionContextChange(option.value); + await queryBuilderState.propagateExecutionContextChange(); queryBuilderState.onExecutionContextChange?.(option.value); }; // runtime - const runtimeOptions = getMappingCompatibleRuntimes( - queryBuilderState.executionContext.mapping.value, - queryBuilderState.graphManagerState.usableRuntimes, - ) + const runtimeOptions = resolveExecutionContextRuntimes(queryBuilderState) .map( (rt) => new RuntimePointer(PackageableElementExplicitReference.create(rt)), @@ -220,6 +240,7 @@ const DataSpaceQueryBuilderSetupPanelContent = observer( queryBuilderState.dataSpace, queryBuilderState.executionContext.mapping.value, queryBuilderState.graphManagerState, + queryBuilderState, ); useEffect(() => { diff --git a/packages/legend-extension-dsl-data-space/src/components/query-builder/DataSpaceQueryBuilderTemplateQueryPanelContent.tsx b/packages/legend-extension-dsl-data-space/src/components/query-builder/DataSpaceQueryBuilderTemplateQueryPanelContent.tsx index 126ca083cf..2e8dbbd772 100644 --- a/packages/legend-extension-dsl-data-space/src/components/query-builder/DataSpaceQueryBuilderTemplateQueryPanelContent.tsx +++ b/packages/legend-extension-dsl-data-space/src/components/query-builder/DataSpaceQueryBuilderTemplateQueryPanelContent.tsx @@ -36,6 +36,7 @@ import { getExecutionContextFromDataspaceExecutable, getQueryFromDataspaceExecutable, } from '../../graph-manager/DSL_DataSpace_GraphManagerHelper.js'; +import { flowResult } from 'mobx'; const DataSpaceTemplateQueryDialog = observer( (props: { @@ -49,7 +50,9 @@ const DataSpaceTemplateQueryDialog = observer( queryBuilderState.setTemplateQueryDialogOpen(false); }; - const loadTemplateQuery = (template: DataSpaceExecutable): void => { + const loadTemplateQuery = async ( + template: DataSpaceExecutable, + ): Promise => { const executionContext = getExecutionContextFromDataspaceExecutable( queryBuilderState.dataSpace, template, @@ -75,7 +78,7 @@ const DataSpaceTemplateQueryDialog = observer( queryBuilderState.executionContext.hashCode ) { queryBuilderState.setExecutionContext(executionContext); - queryBuilderState.propagateExecutionContextChange(executionContext); + await queryBuilderState.propagateExecutionContextChange(); queryBuilderState.initializeWithQuery(query); queryBuilderState.onExecutionContextChange?.(executionContext); } else if (query) { @@ -84,7 +87,7 @@ const DataSpaceTemplateQueryDialog = observer( handleClose(); }; - const loadQuery = (template: DataSpaceExecutable): void => { + const loadQuery = async (template: DataSpaceExecutable): Promise => { if (queryBuilderState.changeDetectionState.hasChanged) { applicationStore.alertService.setActionAlertInfo({ message: @@ -94,7 +97,11 @@ const DataSpaceTemplateQueryDialog = observer( { label: 'Proceed', type: ActionAlertActionType.PROCEED_WITH_CAUTION, - handler: (): void => loadTemplateQuery(template), + handler: (): void => { + loadTemplateQuery(template).catch( + applicationStore.alertUnhandledError, + ); + }, }, { label: 'Abort', @@ -104,7 +111,7 @@ const DataSpaceTemplateQueryDialog = observer( ], }); } else { - loadTemplateQuery(template); + flowResult(loadTemplateQuery(template)); } }; @@ -157,7 +164,9 @@ const DataSpaceTemplateQueryDialog = observer(