diff --git a/.changeset/warm-drinks-unite.md b/.changeset/warm-drinks-unite.md new file mode 100644 index 00000000000..dd9da3c602c --- /dev/null +++ b/.changeset/warm-drinks-unite.md @@ -0,0 +1,7 @@ +--- +'@finos/legend-extension-dsl-data-space': patch +'@finos/legend-extension-dsl-service': patch +'@finos/legend-application-studio': patch +'@finos/legend-application-query': patch +'@finos/legend-query-builder': patch +--- diff --git a/packages/legend-application-query/src/stores/QueryEditorStore.ts b/packages/legend-application-query/src/stores/QueryEditorStore.ts index 5421a4b91c3..e32e8632531 100644 --- a/packages/legend-application-query/src/stores/QueryEditorStore.ts +++ b/packages/legend-application-query/src/stores/QueryEditorStore.ts @@ -393,16 +393,16 @@ export abstract class QueryEditorStore { 'Query builder state required to build query to edit', ); assertNonNullable( - this.queryBuilderState.mapping, + this.queryBuilderState.executionContextState.mapping, 'Query required mapping to update', ); const runtimeValue = guaranteeType( - this.queryBuilderState.runtimeValue, + this.queryBuilderState.executionContextState.runtimeValue, RuntimePointer, 'Query runtime must be of type runtime pointer', ); query.mapping = PackageableElementExplicitReference.create( - this.queryBuilderState.mapping, + this.queryBuilderState.executionContextState.mapping, ); query.runtime = runtimeValue.packageableRuntime; query.content = @@ -672,8 +672,10 @@ export class MappingQueryCreatorStore extends QueryEditorStore { this.artifactId, this.versionId, val.path, - guaranteeType(queryBuilderState.runtimeValue, RuntimePointer) - .packageableRuntime.value.path, + guaranteeType( + queryBuilderState.executionContextState.runtimeValue, + RuntimePointer, + ).packageableRuntime.value.path, ), ); }, @@ -683,7 +685,10 @@ export class MappingQueryCreatorStore extends QueryEditorStore { this.groupId, this.artifactId, this.versionId, - guaranteeType(queryBuilderState.mapping, Mapping).path, + guaranteeType( + queryBuilderState.executionContextState.mapping, + Mapping, + ).path, guaranteeType(val, RuntimePointer).packageableRuntime.value.path, ), ); @@ -1015,8 +1020,8 @@ export class ExistingQueryEditorStore extends QueryEditorStore { queryBuilderState ?? new ClassQueryBuilderState(this.applicationStore, this.graphManagerState); - queryBuilderState.setMapping(query.mapping.value); - queryBuilderState.setRuntimeValue( + queryBuilderState.executionContextState.setMapping(query.mapping.value); + queryBuilderState.executionContextState.setRuntimeValue( new RuntimePointer( PackageableElementExplicitReference.create(query.runtime.value), ), 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 5ee49756379..4c32185361a 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 @@ -115,91 +115,78 @@ export const ServiceExecutionQueryEditor = observer( applicationStore.guardUnhandledError(async () => { const selectedExecutionState = executionState.selectedExecutionContextState; - if (selectedExecutionState?.executionContext.mapping === undefined) { - applicationStore.notificationService.notifyError( - 'Editing query without runtime and mapping is unsupported via query builder, please leverage the text mode to edit query', - ); - executionState.setOpeningQueryEditor(false); - } else { - const mapping = selectedExecutionState.executionContext.mapping.value; - if (!isStubbed_PackageableElement(mapping)) { - await flowResult( - embeddedQueryBuilderState.setEmbeddedQueryBuilderConfiguration({ - setupQueryBuilderState: (): QueryBuilderState => { - const queryBuilderState = new ServiceQueryBuilderState( - embeddedQueryBuilderState.editorStore.applicationStore, - embeddedQueryBuilderState.editorStore.graphManagerState, - service, - undefined, - selectedExecutionState.executionContext instanceof - KeyedExecutionParameter - ? selectedExecutionState.executionContext.key - : undefined, + + await flowResult( + embeddedQueryBuilderState.setEmbeddedQueryBuilderConfiguration({ + setupQueryBuilderState: (): QueryBuilderState => { + const queryBuilderState = new ServiceQueryBuilderState( + embeddedQueryBuilderState.editorStore.applicationStore, + embeddedQueryBuilderState.editorStore.graphManagerState, + service, + undefined, + selectedExecutionState?.executionContext instanceof + KeyedExecutionParameter + ? selectedExecutionState.executionContext.key + : undefined, + ); + queryBuilderState.initializeWithQuery( + executionState.execution.func, + ); + if (openInTextMode) { + queryBuilderState.textEditorState.openModal( + QueryBuilderTextEditorMode.TEXT, + ); + } + return queryBuilderState; + }, + actionConfigs: [ + { + key: 'save-query-btn', + renderer: ( + queryBuilderState: QueryBuilderState, + ): React.ReactNode => { + const save = applicationStore.guardUnhandledError( + async () => { + try { + const rawLambda = queryBuilderState.buildQuery(); + await flowResult( + executionState.queryState.updateLamba(rawLambda), + ); + applicationStore.notificationService.notifySuccess( + `Service query is updated`, + ); + embeddedQueryBuilderState.setEmbeddedQueryBuilderConfiguration( + undefined, + ); + } catch (error) { + assertErrorThrown(error); + applicationStore.notificationService.notifyError( + `Can't save query: ${error.message}`, + ); + } + }, ); - queryBuilderState.initializeWithQuery( - executionState.execution.func, + + return ( + ); - if (openInTextMode) { - queryBuilderState.textEditorState.openModal( - QueryBuilderTextEditorMode.TEXT, - ); - } - return queryBuilderState; }, - actionConfigs: [ - { - key: 'save-query-btn', - renderer: ( - queryBuilderState: QueryBuilderState, - ): React.ReactNode => { - const save = applicationStore.guardUnhandledError( - async () => { - try { - const rawLambda = queryBuilderState.buildQuery(); - await flowResult( - executionState.queryState.updateLamba(rawLambda), - ); - applicationStore.notificationService.notifySuccess( - `Service query is updated`, - ); - embeddedQueryBuilderState.setEmbeddedQueryBuilderConfiguration( - undefined, - ); - } catch (error) { - assertErrorThrown(error); - applicationStore.notificationService.notifyError( - `Can't save query: ${error.message}`, - ); - } - }, - ); - - return ( - - ); - }, - }, - ], - disableCompile: isStubbed_RawLambda( - executionState.queryState.query, - ), - }), - ); - executionState.setOpeningQueryEditor(false); - return; - } - applicationStore.notificationService.notifyWarning( - 'Please specify a mapping and a runtime for the execution context to edit with query builder', - ); - executionState.setOpeningQueryEditor(false); - } + }, + ], + disableCompile: isStubbed_RawLambda( + executionState.queryState.query, + ), + }), + ); + executionState.setOpeningQueryEditor(false); + return; }); const importQuery = (): void => 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 b3e9e189a3c..59eec78c3f1 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 @@ -68,11 +68,11 @@ const promoteQueryToService = async ( const applicationStore = editorStore.applicationStore; try { const mapping = guaranteeNonNullable( - queryBuilderState.mapping, + queryBuilderState.executionContextState.mapping, 'Mapping is required to create service execution', ); const runtime = guaranteeNonNullable( - queryBuilderState.runtimeValue, + queryBuilderState.executionContextState.runtimeValue, 'Runtime is required to create service execution', ); const query = queryBuilderState.buildQuery(); @@ -269,15 +269,23 @@ const PromoteToServiceQueryBuilderAction = observer( setPromoteQueryType(type); const closeNewServiceModal = (): void => setPromoteQueryType(undefined); const allowPromotion = Boolean( - queryBuilderState.mapping && - queryBuilderState.runtimeValue && + queryBuilderState.executionContextState.mapping && + queryBuilderState.executionContextState.runtimeValue && !queryBuilderState.allValidationIssues.length, ); + console.log( + 'queryBuilderState.executionContextState.mapping', + queryBuilderState.executionContextState.mapping, + ); + console.log( + 'queryBuilderState.executionContextState.runtimeValue', + queryBuilderState.executionContextState.runtimeValue, + ); const renderSaveAsModal = (): React.ReactNode => { if ( promoteQueryModal === PROMOTE_QUERY_TYPE.SERVICE && - queryBuilderState.mapping + queryBuilderState.executionContextState.mapping ) { const promoteToService = async ( packagePath: string, @@ -295,7 +303,7 @@ const PromoteToServiceQueryBuilderAction = observer( return ( { - if (option.value === queryBuilderState.runtimeValue) { + if ( + option.value === queryBuilderState.executionContextState.runtimeValue + ) { return; } queryBuilderState.changeRuntime(option.value); diff --git a/packages/legend-extension-dsl-data-space/src/stores/query/DataSpaceQueryCreatorStore.ts b/packages/legend-extension-dsl-data-space/src/stores/query/DataSpaceQueryCreatorStore.ts index 80f3809056c..a963c38fc14 100644 --- a/packages/legend-extension-dsl-data-space/src/stores/query/DataSpaceQueryCreatorStore.ts +++ b/packages/legend-extension-dsl-data-space/src/stores/query/DataSpaceQueryCreatorStore.ts @@ -156,7 +156,7 @@ export class DataSpaceQueryCreatorStore extends QueryEditorStore { (ec: DataSpaceExecutionContext) => { // runtime should already be set const runtimePointer = guaranteeType( - queryBuilderState.runtimeValue, + queryBuilderState.executionContextState.runtimeValue, RuntimePointer, ); this.applicationStore.navigationService.navigator.updateCurrentLocation( @@ -194,7 +194,7 @@ export class DataSpaceQueryCreatorStore extends QueryEditorStore { (_class: Class) => { // runtime should already be set const runtimePointer = guaranteeType( - queryBuilderState.runtimeValue, + queryBuilderState.executionContextState.runtimeValue, RuntimePointer, ); queryBuilderState.applicationStore.navigationService.navigator.updateCurrentLocation( diff --git a/packages/legend-extension-dsl-service/src/components/query/ServiceRegisterModal.tsx b/packages/legend-extension-dsl-service/src/components/query/ServiceRegisterModal.tsx index b8f079b903d..b6c508840d8 100644 --- a/packages/legend-extension-dsl-service/src/components/query/ServiceRegisterModal.tsx +++ b/packages/legend-extension-dsl-service/src/components/query/ServiceRegisterModal.tsx @@ -148,8 +148,11 @@ const ServiceRegisterModal = observer( !servicePattern || !isServiceUrlPatternValid || !selectedEnvOption || - !queryBuilderState.mapping || - !(queryBuilderState.runtimeValue instanceof RuntimePointer) + !queryBuilderState.executionContextState.mapping || + !( + queryBuilderState.executionContextState.runtimeValue instanceof + RuntimePointer + ) ) { return; } @@ -164,8 +167,9 @@ const ServiceRegisterModal = observer( servicePattern, owners.map((o) => o.value), queryBuilderState.buildQuery(), - queryBuilderState.mapping.path, - queryBuilderState.runtimeValue.packageableRuntime.value.path, + queryBuilderState.executionContextState.mapping.path, + queryBuilderState.executionContextState.runtimeValue + .packageableRuntime.value.path, editorStore.graphManagerState, ); diff --git a/packages/legend-manual-tests/src/__tests__/query-execution/TEMPORARY__QueryBuilderExecution.test.tsx b/packages/legend-manual-tests/src/__tests__/query-execution/TEMPORARY__QueryBuilderExecution.test.tsx index ddf6bf44069..91a88b1bf10 100644 --- a/packages/legend-manual-tests/src/__tests__/query-execution/TEMPORARY__QueryBuilderExecution.test.tsx +++ b/packages/legend-manual-tests/src/__tests__/query-execution/TEMPORARY__QueryBuilderExecution.test.tsx @@ -128,9 +128,9 @@ test(integrationTest('test query execution with parameters'), async () => { queryBuilderState.graphManagerState.graphManager as V1_PureGraphManager ).createExecutionInput( queryBuilderState.graphManagerState.graph, - guaranteeNonNullable(queryBuilderState.mapping), + guaranteeNonNullable(queryBuilderState.executionContextState.mapping), queryBuilderState.resultState.buildExecutionRawLambda(), - guaranteeNonNullable(queryBuilderState.runtimeValue), + guaranteeNonNullable(queryBuilderState.executionContextState.runtimeValue), V1_PureGraphManager.DEV_PROTOCOL_VERSION, parameterValues, ); diff --git a/packages/legend-query-builder/src/components/QueryBuilderSideBar.tsx b/packages/legend-query-builder/src/components/QueryBuilderSideBar.tsx index dfea8c82f01..fee0f538248 100644 --- a/packages/legend-query-builder/src/components/QueryBuilderSideBar.tsx +++ b/packages/legend-query-builder/src/components/QueryBuilderSideBar.tsx @@ -212,6 +212,206 @@ export const QueryBuilderClassSelector = observer( }, ); +export const QueryBuilderMappingSelector = observer( + (props: { + queryBuilderState: QueryBuilderState; + classes: Class[]; + onClassChange?: ((val: Class) => void) | undefined; + noMatchMessage?: string | undefined; + }) => { + const { queryBuilderState, classes, onClassChange, noMatchMessage } = props; + const milestoningState = queryBuilderState.milestoningState; + const applicationStore = useApplicationStore(); + + // class + const elementFilterOption = createFilter({ + ignoreCase: true, + ignoreAccents: false, + stringify: (option: PackageableElementOption): string => + option.value.path, + }); + + const classOptions = classes.map((_class) => ({ + value: _class, + label: generateClassLabel(_class, queryBuilderState), + })); + const selectedClassOption = queryBuilderState.class + ? { + value: queryBuilderState.class, + label: generateClassLabel(queryBuilderState.class, queryBuilderState), + } + : null; + const changeClass = (val: PackageableElementOption): void => { + if (val.value === queryBuilderState.class) { + return; + } + queryBuilderState.changeClass(val.value); + onClassChange?.(val.value); + }; + + // milestoning + const showMilestoningEditor = (): void => + milestoningState.setShowMilestoningEditor(true); + + return ( +
+
+
+
+ +
+ + {queryBuilderState.isQuerySupported && ( + + )} + {milestoningState.isMilestonedQuery && ( + + )} +
+
+
+ ); + }, +); + +export const QueryBuilderFunction = observer( + (props: { + queryBuilderState: QueryBuilderState; + classes: Class[]; + onClassChange?: ((val: Class) => void) | undefined; + noMatchMessage?: string | undefined; + }) => { + const { queryBuilderState, classes, onClassChange, noMatchMessage } = props; + const milestoningState = queryBuilderState.milestoningState; + const applicationStore = useApplicationStore(); + + // class + const elementFilterOption = createFilter({ + ignoreCase: true, + ignoreAccents: false, + stringify: (option: PackageableElementOption): string => + option.value.path, + }); + + const classOptions = classes.map((_class) => ({ + value: _class, + label: generateClassLabel(_class, queryBuilderState), + })); + const selectedClassOption = queryBuilderState.class + ? { + value: queryBuilderState.class, + label: generateClassLabel(queryBuilderState.class, queryBuilderState), + } + : null; + const changeClass = (val: PackageableElementOption): void => { + if (val.value === queryBuilderState.class) { + return; + } + queryBuilderState.changeClass(val.value); + onClassChange?.(val.value); + }; + + // milestoning + const showMilestoningEditor = (): void => + milestoningState.setShowMilestoningEditor(true); + + return ( +
+
+
+
+ +
+ + {queryBuilderState.isQuerySupported && ( + + )} + {milestoningState.isMilestonedQuery && ( + + )} +
+
+
+ ); + }, +); + export const buildRuntimeValueOption = ( runtimeValue: Runtime, ): { label: string; value: Runtime } => ({ @@ -257,13 +457,14 @@ const BasicQueryBuilderSetup = observer( queryBuilderState.graphManagerState.usableMappings.map( buildElementOption, ); - const selectedMappingOption = queryBuilderState.mapping - ? buildElementOption(queryBuilderState.mapping) + const selectedMappingOption = queryBuilderState.executionContextState + .mapping + ? buildElementOption(queryBuilderState.executionContextState.mapping) : null; const changeMapping = (val: PackageableElementOption): void => { if ( !queryBuilderState.class || - val.value === queryBuilderState.mapping || + val.value === queryBuilderState.executionContextState.mapping || queryBuilderState.isMappingReadOnly ) { return; @@ -284,12 +485,15 @@ const BasicQueryBuilderSetup = observer( new RuntimePointer(PackageableElementExplicitReference.create(rt)), ) .map(buildRuntimeValueOption); - const selectedRuntimeOption = queryBuilderState.runtimeValue - ? buildRuntimeValueOption(queryBuilderState.runtimeValue) + const selectedRuntimeOption = queryBuilderState.executionContextState + .runtimeValue + ? buildRuntimeValueOption( + queryBuilderState.executionContextState.runtimeValue, + ) : null; const changeRuntime = (val: { value: Runtime }): void => { if ( - val.value === queryBuilderState.runtimeValue || + val.value === queryBuilderState.executionContextState.runtimeValue || queryBuilderState.isRuntimeReadOnly ) { return; @@ -368,7 +572,7 @@ const BasicQueryBuilderSetup = observer( disabled={ queryBuilderState.isRuntimeReadOnly || !queryBuilderState.class || - !queryBuilderState.mapping + !queryBuilderState.executionContextState.mapping } options={runtimeOptions} onChange={changeRuntime} diff --git a/packages/legend-query-builder/src/components/__test-utils__/QueryBuilderComponentTestUtils.tsx b/packages/legend-query-builder/src/components/__test-utils__/QueryBuilderComponentTestUtils.tsx index f11a5131775..43f629e44fb 100644 --- a/packages/legend-query-builder/src/components/__test-utils__/QueryBuilderComponentTestUtils.tsx +++ b/packages/legend-query-builder/src/components/__test-utils__/QueryBuilderComponentTestUtils.tsx @@ -84,8 +84,8 @@ export const TEST__setUpQueryBuilder = async ( graphManagerState, ); const mapping = graphManagerState.graph.getMapping(mappingPath); - queryBuilderState.setMapping(mapping); - queryBuilderState.setRuntimeValue( + queryBuilderState.executionContextState.setMapping(mapping); + queryBuilderState.executionContextState.setRuntimeValue( new RuntimePointer( PackageableElementExplicitReference.create( graphManagerState.graph.getRuntime(runtimePath), diff --git a/packages/legend-query-builder/src/components/explorer/QueryBuilderExplorerPanel.tsx b/packages/legend-query-builder/src/components/explorer/QueryBuilderExplorerPanel.tsx index 72b2e27cf89..7e59fa5f46e 100644 --- a/packages/legend-query-builder/src/components/explorer/QueryBuilderExplorerPanel.tsx +++ b/packages/legend-query-builder/src/components/explorer/QueryBuilderExplorerPanel.tsx @@ -849,7 +849,11 @@ export const QueryBuilderExplorerPanel = observer( flowResult(explorerState.analyzeMappingModelCoverage()).catch( applicationStore.alertUnhandledError, ); - }, [applicationStore, explorerState, queryBuilderState.mapping]); + }, [ + applicationStore, + explorerState, + queryBuilderState.executionContextState.mapping, + ]); return (
): void => { - if (!queryBuilderState.class || val.value === queryBuilderState.mapping) { + if ( + !queryBuilderState.class || + val.value === queryBuilderState.executionContextState.mapping + ) { return; } queryBuilderState.changeMapping(val.value); @@ -90,9 +94,9 @@ const ClassQueryBuilderSetupPanelContent = observer( // runtime const runtimeOptions = ( - queryBuilderState.mapping + queryBuilderState.executionContextState.mapping ? getMappingCompatibleRuntimes( - queryBuilderState.mapping, + queryBuilderState.executionContextState.mapping, queryBuilderState.graphManagerState.usableRuntimes, ) : [] @@ -102,11 +106,14 @@ const ClassQueryBuilderSetupPanelContent = observer( new RuntimePointer(PackageableElementExplicitReference.create(rt)), ) .map(buildRuntimeValueOption); - const selectedRuntimeOption = queryBuilderState.runtimeValue - ? buildRuntimeValueOption(queryBuilderState.runtimeValue) + const selectedRuntimeOption = queryBuilderState.executionContextState + .runtimeValue + ? buildRuntimeValueOption( + queryBuilderState.executionContextState.runtimeValue, + ) : null; const changeRuntime = (val: { value: Runtime }): void => { - if (val.value === queryBuilderState.runtimeValue) { + if (val.value === queryBuilderState.executionContextState.runtimeValue) { return; } queryBuilderState.changeRuntime(val.value); @@ -177,7 +184,8 @@ const ClassQueryBuilderSetupPanelContent = observer( placeholder="Choose a runtime..." noMatchMessage="No compatible runtime found for specified mapping" disabled={ - !queryBuilderState.class || !queryBuilderState.mapping + !queryBuilderState.class || + !queryBuilderState.executionContextState.mapping } options={runtimeOptions} onChange={changeRuntime} diff --git a/packages/legend-query-builder/src/components/workflows/MappingQueryBuilder.tsx b/packages/legend-query-builder/src/components/workflows/MappingQueryBuilder.tsx index fba9cc8e225..700caf62c5a 100644 --- a/packages/legend-query-builder/src/components/workflows/MappingQueryBuilder.tsx +++ b/packages/legend-query-builder/src/components/workflows/MappingQueryBuilder.tsx @@ -62,11 +62,12 @@ const MappingQueryBuilderSetupPanelContent = observer( queryBuilderState.graphManagerState.usableMappings.map( buildElementOption, ); - const selectedMappingOption = queryBuilderState.mapping - ? buildElementOption(queryBuilderState.mapping) + const selectedMappingOption = queryBuilderState.executionContextState + .mapping + ? buildElementOption(queryBuilderState.executionContextState.mapping) : null; const changeMapping = (val: PackageableElementOption): void => { - if (val.value === queryBuilderState.mapping) { + if (val.value === queryBuilderState.executionContextState.mapping) { return; } queryBuilderState.changeMapping(val.value); @@ -82,9 +83,9 @@ const MappingQueryBuilderSetupPanelContent = observer( // runtime const runtimeOptions = ( - queryBuilderState.mapping + queryBuilderState.executionContextState.mapping ? getMappingCompatibleRuntimes( - queryBuilderState.mapping, + queryBuilderState.executionContextState.mapping, queryBuilderState.graphManagerState.usableRuntimes, ) : [] @@ -94,11 +95,14 @@ const MappingQueryBuilderSetupPanelContent = observer( new RuntimePointer(PackageableElementExplicitReference.create(rt)), ) .map(buildRuntimeValueOption); - const selectedRuntimeOption = queryBuilderState.runtimeValue - ? buildRuntimeValueOption(queryBuilderState.runtimeValue) + const selectedRuntimeOption = queryBuilderState.executionContextState + .runtimeValue + ? buildRuntimeValueOption( + queryBuilderState.executionContextState.runtimeValue, + ) : null; const changeRuntime = (val: { value: Runtime }): void => { - if (val.value === queryBuilderState.runtimeValue) { + if (val.value === queryBuilderState.executionContextState.runtimeValue) { return; } queryBuilderState.changeRuntime(val.value); @@ -113,9 +117,9 @@ const MappingQueryBuilderSetupPanelContent = observer( }); // class - const classes = queryBuilderState.mapping + const classes = queryBuilderState.executionContextState.mapping ? getMappingCompatibleClasses( - queryBuilderState.mapping, + queryBuilderState.executionContextState.mapping, queryBuilderState.graphManagerState.usableClasses, ) : []; @@ -170,7 +174,7 @@ const MappingQueryBuilderSetupPanelContent = observer( className="panel__content__form__section__dropdown query-builder__setup__config-group__item__selector" placeholder="Choose a runtime..." noMatchMessage="No compatible runtime found for specified mapping" - disabled={!queryBuilderState.mapping} + disabled={!queryBuilderState.executionContextState.mapping} options={runtimeOptions} onChange={changeRuntime} value={selectedRuntimeOption} diff --git a/packages/legend-query-builder/src/components/workflows/ServiceQueryBuilder.tsx b/packages/legend-query-builder/src/components/workflows/ServiceQueryBuilder.tsx index c6305c5efe8..04675e244df 100644 --- a/packages/legend-query-builder/src/components/workflows/ServiceQueryBuilder.tsx +++ b/packages/legend-query-builder/src/components/workflows/ServiceQueryBuilder.tsx @@ -87,9 +87,9 @@ const ServiceQueryBuilderSetupPanelContent = observer( }; // class - const classes = queryBuilderState.mapping + const classes = queryBuilderState.executionContextState.mapping ? getMappingCompatibleClasses( - queryBuilderState.mapping, + queryBuilderState.executionContextState.mapping, queryBuilderState.graphManagerState.usableClasses, ) : []; diff --git a/packages/legend-query-builder/src/stores/QueryBuilderExecutionContextState.ts b/packages/legend-query-builder/src/stores/QueryBuilderExecutionContextState.ts new file mode 100644 index 00000000000..14b66f82df0 --- /dev/null +++ b/packages/legend-query-builder/src/stores/QueryBuilderExecutionContextState.ts @@ -0,0 +1,85 @@ +/** + * 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 type { Mapping, Runtime } from '@finos/legend-graph'; +import type { QueryBuilderState } from './QueryBuilderState.js'; +import { action, makeObservable, observable } from 'mobx'; + +export abstract class QueryBuilderExecutionContextState { + readonly queryBuilderState: QueryBuilderState; + mapping: Mapping | undefined; + runtimeValue: Runtime | undefined; + + constructor(queryBuilderState: QueryBuilderState) { + this.queryBuilderState = queryBuilderState; + } + + setMapping(val: Mapping | undefined): void { + this.mapping = val; + } + + setRuntimeValue(val: Runtime | undefined): void { + this.runtimeValue = val; + } + + abstract isMappingReadOnly(): boolean; + abstract isRuntimeReadOny(): boolean; +} + +export class QueryBuilderInlineFromExecutionContextState extends QueryBuilderExecutionContextState { + constructor(queryBuilderState: QueryBuilderState) { + super(queryBuilderState); + makeObservable(this, { + mapping: observable, + runtimeValue: observable, + setMapping: action, + setRuntimeValue: action, + }); + } + isMappingReadOnly(): boolean { + return false; + } + + isRuntimeReadOny(): boolean { + return false; + } +} + +export class QueryBuilderExternalExecutionContextState extends QueryBuilderExecutionContextState { + constructor(queryBuilderState: QueryBuilderState) { + super(queryBuilderState); + makeObservable(this, { + mapping: observable, + runtimeValue: observable, + setMapping: action, + setRuntimeValue: action, + }); + } + + override setMapping(val: Mapping | undefined): void { + this.mapping = val; + } + + override setRuntimeValue(val: Runtime | undefined): void { + this.runtimeValue = val; + } + override isMappingReadOnly(): boolean { + throw new Error('Method not implemented.'); + } + override isRuntimeReadOny(): boolean { + throw new Error('Method not implemented.'); + } +} diff --git a/packages/legend-query-builder/src/stores/QueryBuilderInternalizeState.ts b/packages/legend-query-builder/src/stores/QueryBuilderInternalizeState.ts new file mode 100644 index 00000000000..1469f647a3f --- /dev/null +++ b/packages/legend-query-builder/src/stores/QueryBuilderInternalizeState.ts @@ -0,0 +1,33 @@ +/** + * 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 type { QueryBuilderState } from './QueryBuilderState.js'; +import type { Binding, VariableExpression } from '@finos/legend-graph'; +export class QueryBuilderInternalizeState { + readonly queryBuilderState: QueryBuilderState; + binding: Binding; + inputData: VariableExpression; + + constructor( + binding: Binding, + inputData: VariableExpression, + queryBuilderState: QueryBuilderState, + ) { + this.queryBuilderState = queryBuilderState; + this.binding = binding; + this.inputData = inputData; + } +} diff --git a/packages/legend-query-builder/src/stores/QueryBuilderResultState.ts b/packages/legend-query-builder/src/stores/QueryBuilderResultState.ts index 6d1719450c2..e083d6407f4 100644 --- a/packages/legend-query-builder/src/stores/QueryBuilderResultState.ts +++ b/packages/legend-query-builder/src/stores/QueryBuilderResultState.ts @@ -217,8 +217,8 @@ export class QueryBuilderResultState { const result = (yield this.queryBuilderState.graphManagerState.graphManager.runQuery( query, - this.queryBuilderState.mapping, - this.queryBuilderState.runtimeValue, + this.queryBuilderState.executionContextState.mapping, + this.queryBuilderState.executionContextState.runtimeValue, this.queryBuilderState.graphManagerState.graph, { serializationFormat, @@ -260,11 +260,11 @@ export class QueryBuilderResultState { this.setIsRunningQuery(true); const currentHashCode = this.queryBuilderState.hashCode; const mapping = guaranteeNonNullable( - this.queryBuilderState.mapping, + this.queryBuilderState.executionContextState.mapping, 'Mapping is required to execute query', ); const runtime = guaranteeNonNullable( - this.queryBuilderState.runtimeValue, + this.queryBuilderState.executionContextState.runtimeValue, `Runtime is required to execute query`, ); const query = this.buildExecutionRawLambda(); @@ -350,11 +350,11 @@ export class QueryBuilderResultState { try { this.isGeneratingPlan = true; const mapping = guaranteeNonNullable( - this.queryBuilderState.mapping, + this.queryBuilderState.executionContextState.mapping, 'Mapping is required to execute query', ); const runtime = guaranteeNonNullable( - this.queryBuilderState.runtimeValue, + this.queryBuilderState.executionContextState.runtimeValue, `Runtime is required to execute query`, ); const query = this.queryBuilderState.buildQuery(); diff --git a/packages/legend-query-builder/src/stores/QueryBuilderState.ts b/packages/legend-query-builder/src/stores/QueryBuilderState.ts index 15630b6cb22..4557ac35dcb 100644 --- a/packages/legend-query-builder/src/stores/QueryBuilderState.ts +++ b/packages/legend-query-builder/src/stores/QueryBuilderState.ts @@ -51,6 +51,8 @@ import { type Mapping, type Runtime, type GraphManagerState, + type ValueSpecification, + type Type, GRAPH_MANAGER_EVENT, CompilationError, extractSourceInformationCoordinates, @@ -62,9 +64,7 @@ import { isStubbed_RawLambda, buildLambdaVariableExpressions, buildRawLambdaFromLambdaFunction, - type ValueSpecification, PrimitiveType, - type Type, SimpleFunctionExpression, extractElementNameFromPath, SUPPORTED_FUNCTIONS, @@ -94,6 +94,11 @@ import { QueryBuilderConstantsState } from './QueryBuilderConstantsState.js'; import { QueryBuilderCheckEntitlementsState } from './entitlements/QueryBuilderCheckEntitlementsState.js'; import { QueryBuilderTDSState } from './fetch-structure/tds/QueryBuilderTDSState.js'; import { QUERY_BUILDER_PURE_PATH } from '../graph/QueryBuilderMetaModelConst.js'; +import { QueryBuilderInternalizeState } from './QueryBuilderInternalizeState.js'; +import { + QueryBuilderExternalExecutionContextState, + type QueryBuilderExecutionContextState, +} from './QueryBuilderExecutionContextState.js'; export abstract class QueryBuilderState implements CommandRegistrar { readonly applicationStore: GenericLegendApplicationStore; @@ -124,8 +129,8 @@ export abstract class QueryBuilderState implements CommandRegistrar { isCalendarEnabled = false; class?: Class | undefined; - mapping?: Mapping | undefined; - runtimeValue?: Runtime | undefined; + executionContextState: QueryBuilderExecutionContextState; + internalizeState?: QueryBuilderInternalizeState | undefined; // NOTE: this makes it so that we need to import components in stores code, // we probably want to refactor to an extension mechanism @@ -153,9 +158,8 @@ export abstract class QueryBuilderState implements CommandRegistrar { isCheckingEntitlments: observable, isCalendarEnabled: observable, changeDetectionState: observable, + executionContextState: observable, class: observable, - mapping: observable, - runtimeValue: observable, sideBarClassName: computed, isQuerySupported: computed, @@ -167,13 +171,12 @@ export abstract class QueryBuilderState implements CommandRegistrar { setIsCalendarEnabled: action, setIsCheckingEntitlments: action, setClass: action, - setMapping: action, - setRuntimeValue: action, resetQueryResult: action, resetQueryContent: action, changeClass: action, changeMapping: action, + setExecutionContextState: action, rebuildWithQuery: action, compileQuery: flow, @@ -182,7 +185,9 @@ export abstract class QueryBuilderState implements CommandRegistrar { this.applicationStore = applicationStore; this.graphManagerState = graphManagerState; - + this.executionContextState = new QueryBuilderExternalExecutionContextState( + this, + ); this.milestoningState = new QueryBuilderMilestoningState(this); this.explorerState = new QueryBuilderExplorerState(this); this.parametersState = new QueryBuilderParametersState(this); @@ -243,6 +248,10 @@ export abstract class QueryBuilderState implements CommandRegistrar { return this.allVariables.map((e) => e.name); } + setInternalize(val: QueryBuilderInternalizeState | undefined): void { + this.internalizeState = val; + } + setShowFunctionsExplorerPanel(val: boolean): void { this.showFunctionsExplorerPanel = val; } @@ -267,12 +276,8 @@ export abstract class QueryBuilderState implements CommandRegistrar { this.class = val; } - setMapping(val: Mapping | undefined): void { - this.mapping = val; - } - - setRuntimeValue(val: Runtime | undefined): void { - this.runtimeValue = val; + setExecutionContextState(val: QueryBuilderExecutionContextState): void { + this.executionContextState = val; } get isQuerySupported(): boolean { @@ -366,12 +371,12 @@ export abstract class QueryBuilderState implements CommandRegistrar { this.resetQueryContent(); this.milestoningState.updateMilestoningConfiguration(); } - this.setMapping(val); + this.executionContextState.setMapping(val); } changeRuntime(val: Runtime): void { this.resetQueryResult(); - this.setRuntimeValue(val); + this.executionContextState.setRuntimeValue(val); } getCurrentParameterValues(): Map | undefined { @@ -414,11 +419,11 @@ export abstract class QueryBuilderState implements CommandRegistrar { 'Query must be supported to build from function', ); const mapping = guaranteeNonNullable( - this.mapping, + this.executionContextState.mapping, 'Mapping required to build from() function', ); const runtime = guaranteeNonNullable( - this.runtimeValue, + this.executionContextState.runtimeValue, 'Runtime required to build from query', ); const runtimePointer = guaranteeType( @@ -659,8 +664,10 @@ export abstract class QueryBuilderState implements CommandRegistrar { this.graphManagerState, ); basicState.class = this.class; - basicState.mapping = this.mapping; - basicState.runtimeValue = this.runtimeValue; + basicState.executionContextState.mapping = + this.executionContextState.mapping; + basicState.executionContextState.runtimeValue = + this.executionContextState.runtimeValue; return basicState; } diff --git a/packages/legend-query-builder/src/stores/QueryBuilderStateBuilder.ts b/packages/legend-query-builder/src/stores/QueryBuilderStateBuilder.ts index 5673046fe00..6756dba56ce 100644 --- a/packages/legend-query-builder/src/stores/QueryBuilderStateBuilder.ts +++ b/packages/legend-query-builder/src/stores/QueryBuilderStateBuilder.ts @@ -30,7 +30,7 @@ import { type FunctionExpression, type GraphFetchTreeInstanceValue, type ValueSpecificationVisitor, - type InstanceValue, + InstanceValue, type INTERNAL__UnknownValueSpecification, type LambdaFunction, type KeyExpressionInstanceValue, @@ -47,6 +47,11 @@ import { type ValueSpecification, SUPPORTED_FUNCTIONS, isSuperType, + PackageableElementReference, + Mapping, + PackageableRuntime, + RuntimePointer, + PackageableElementExplicitReference, } from '@finos/legend-graph'; import { processTDSPostFilterExpression } from './fetch-structure/tds/post-filter/QueryBuilderPostFilterStateBuilder.js'; import { processFilterExpression } from './filter/QueryBuilderFilterStateBuilder.js'; @@ -58,6 +63,7 @@ import { processGraphFetchExpression, processGraphFetchExternalizeExpression, processGraphFetchSerializeExpression, + processInternalizeExpression, } from './fetch-structure/graph-fetch/QueryBuilderGraphFetchTreeStateBuilder.js'; import { processTDSDistinctExpression, @@ -78,6 +84,8 @@ import { processWatermarkExpression } from './watermark/QueryBuilderWatermarkSta import { QueryBuilderConstantExpressionState } from './QueryBuilderConstantsState.js'; import { checkIfEquivalent } from './milestoning/QueryBuilderMilestoningHelper.js'; import type { QueryBuilderParameterValue } from './QueryBuilderParametersState.js'; +import { toJS } from 'mobx'; +import { QueryBuilderInlineFromExecutionContextState } from './QueryBuilderExecutionContextState.js'; const processGetAllExpression = ( expression: SimpleFunctionExpression, @@ -146,6 +154,51 @@ const processLetExpression = ( queryBuilderState.constantState.addConstant(constantExpression); }; +const processFromFunction = ( + expression: SimpleFunctionExpression, + queryBuilderState: QueryBuilderState, +): void => { + // mapping + const mappingInstanceExpression = guaranteeType( + expression.parametersValues[1], + InstanceValue, + `Can't process internalize() expression: only support internalize() with 1st parameter as instance value`, + ); + const mapping = guaranteeType( + guaranteeType( + mappingInstanceExpression.values[0], + PackageableElementReference, + `Can't process from() expression: only support from() with 1st parameter as packagableElement value`, + ).value, + Mapping, + `Can't process from() expression: only support from() with 1st parameter as mapping value`, + ); + // runtime + const runtimeInstanceExpression = guaranteeType( + expression.parametersValues[2], + InstanceValue, + `Can't process from() expression: only support from() with 2nd parameter as instance value`, + ); + const runtimeVal = guaranteeType( + guaranteeType( + runtimeInstanceExpression.values[0], + PackageableElementReference, + `Can't process from() expression: only support from() with 2nd parameter as packagableElement value`, + ).value, + PackageableRuntime, + `Can't process from() expression: only support from() with 2nd parameter as runtime value`, + ); + const fromContext = new QueryBuilderInlineFromExecutionContextState( + queryBuilderState, + ); + fromContext.setMapping(mapping); + fromContext.setRuntimeValue( + new RuntimePointer(PackageableElementExplicitReference.create(runtimeVal)), + ); + queryBuilderState.setExecutionContextState(fromContext); + return; +}; + /** * This is the value specification processor (a.k.a state builder) for query builder. * @@ -316,11 +369,24 @@ export class QueryBuilderValueSpecificationProcessor valueSpecification: SimpleFunctionExpression, ): void { const functionName = valueSpecification.functionName; + console.log('visit_SimpleFunctionExpression', functionName); if ( matchFunctionName(functionName, QUERY_BUILDER_SUPPORTED_FUNCTIONS.GET_ALL) ) { processGetAllExpression(valueSpecification, this.queryBuilderState); return; + } else if ( + matchFunctionName( + functionName, + QUERY_BUILDER_SUPPORTED_FUNCTIONS.INTERNALIZE, + ) + ) { + processInternalizeExpression( + valueSpecification, + this.queryBuilderState, + this.parentLambda, + ); + return; } else if ( matchFunctionName(functionName, [ QUERY_BUILDER_SUPPORTED_FUNCTIONS.FILTER, @@ -545,6 +611,20 @@ export class QueryBuilderValueSpecificationProcessor this.parentLambda, ); return; + } else if (matchFunctionName(functionName, [SUPPORTED_FUNCTIONS.FROM])) { + const parameters = valueSpecification.parametersValues; + assertTrue( + parameters.length === 3, + 'From function expects 2 parameters (mapping and runtime)', + ); + processFromFunction(valueSpecification, this.queryBuilderState); + QueryBuilderValueSpecificationProcessor.processChild( + guaranteeNonNullable(parameters[0]), + valueSpecification, + this.parentLambda, + this.queryBuilderState, + ); + return; } else if ( matchFunctionName( functionName, diff --git a/packages/legend-query-builder/src/stores/QueryBuilderValueSpecificationBuilder.ts b/packages/legend-query-builder/src/stores/QueryBuilderValueSpecificationBuilder.ts index 91e3a045a29..6edc9b2e1a0 100644 --- a/packages/legend-query-builder/src/stores/QueryBuilderValueSpecificationBuilder.ts +++ b/packages/legend-query-builder/src/stores/QueryBuilderValueSpecificationBuilder.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { guaranteeNonNullable } from '@finos/legend-shared'; +import { guaranteeNonNullable, guaranteeType } from '@finos/legend-shared'; import { type Class, Multiplicity, @@ -31,6 +31,7 @@ import { PrimitiveInstanceValue, PrimitiveType, SUPPORTED_FUNCTIONS, + RuntimePointer, } from '@finos/legend-graph'; import type { QueryBuilderState } from './QueryBuilderState.js'; import { buildFilterExpression } from './filter/QueryBuilderFilterValueSpecificationBuilder.js'; @@ -40,6 +41,10 @@ import { QUERY_BUILDER_SUPPORTED_FUNCTIONS } from '../graph/QueryBuilderMetaMode import { buildWatermarkExpression } from './watermark/QueryBuilderWatermarkValueSpecificationBuilder.js'; import { buildExecutionQueryFromLambdaFunction } from './shared/LambdaParameterState.js'; import type { QueryBuilderConstantExpressionState } from './QueryBuilderConstantsState.js'; +import { + QueryBuilderInlineFromExecutionContextState, + type QueryBuilderExecutionContextState, +} from './QueryBuilderExecutionContextState.js'; export const buildGetAllFunction = ( _class: Class, @@ -91,6 +96,46 @@ const buildLetExpression = ( return letFunc; }; +const buildExecutionContextState = ( + executionState: QueryBuilderExecutionContextState, + lambdaFunction: LambdaFunction, +): LambdaFunction => { + if (executionState instanceof QueryBuilderInlineFromExecutionContextState) { + const precedingExpression = guaranteeNonNullable( + lambdaFunction.expressionSequence[0], + `Can't build from expression: preceding expression is not defined`, + ); + const fromFunc = new SimpleFunctionExpression( + extractElementNameFromPath(SUPPORTED_FUNCTIONS.FROM), + ); + // 1st param + const mapping = guaranteeNonNullable( + executionState.mapping, + 'Mapping required for building from() expression', + ); + const mappingInstance = new InstanceValue(Multiplicity.ONE, undefined); + mappingInstance.values = [ + PackageableElementExplicitReference.create(mapping), + ]; + // 2nd parameter + const runtime = guaranteeType( + executionState.runtimeValue, + RuntimePointer, + 'Runtime Pointer required for building from() expression', + ); + const runtimeInstance = new InstanceValue(Multiplicity.ONE, undefined); + runtimeInstance.values = [runtime.packageableRuntime]; + // build externalize + fromFunc.parametersValues = [ + precedingExpression, + mappingInstance, + runtimeInstance, + ]; + lambdaFunction.expressionSequence[0] = fromFunc; + } + return lambdaFunction; +}; + const buildFetchStructure = ( fetchStructureState: QueryBuilderFetchStructureState, lambdaFunction: LambdaFunction, @@ -157,6 +202,11 @@ export const buildLambdaFunction = ( lambdaFunction, options, ); + // build execution-state + buildExecutionContextState( + queryBuilderState.executionContextState, + lambdaFunction, + ); // build variable expressions if (queryBuilderState.constantState.constants.length) { @@ -185,5 +235,6 @@ export const buildLambdaFunction = ( ); } } + return lambdaFunction; }; diff --git a/packages/legend-query-builder/src/stores/__test-utils__/QueryBuilderStateTestUtils.ts b/packages/legend-query-builder/src/stores/__test-utils__/QueryBuilderStateTestUtils.ts index e40a56aa829..664f315a226 100644 --- a/packages/legend-query-builder/src/stores/__test-utils__/QueryBuilderStateTestUtils.ts +++ b/packages/legend-query-builder/src/stores/__test-utils__/QueryBuilderStateTestUtils.ts @@ -138,9 +138,11 @@ export const TEST__setUpQueryBuilderState = async ( if (executionContext) { const graph = queryBuilderState.graphManagerState.graph; queryBuilderState.class = graph.getClass(executionContext._class); - queryBuilderState.mapping = graph.getMapping(executionContext.mapping); + queryBuilderState.executionContextState.mapping = graph.getMapping( + executionContext.mapping, + ); if (executionContext.runtime) { - queryBuilderState.runtimeValue = graph.getRuntime( + queryBuilderState.executionContextState.runtimeValue = graph.getRuntime( executionContext.runtime, ); } diff --git a/packages/legend-query-builder/src/stores/entitlements/QueryBuilderCheckEntitlementsState.ts b/packages/legend-query-builder/src/stores/entitlements/QueryBuilderCheckEntitlementsState.ts index fe9a3a12bda..183bdc1d059 100644 --- a/packages/legend-query-builder/src/stores/entitlements/QueryBuilderCheckEntitlementsState.ts +++ b/packages/legend-query-builder/src/stores/entitlements/QueryBuilderCheckEntitlementsState.ts @@ -53,16 +53,18 @@ export class QueryBuilderCheckEntitlementsState implements Hashable { this.dataAccessState = undefined; if ( - this.queryBuilderState.mapping && - this.queryBuilderState.runtimeValue instanceof RuntimePointer + this.queryBuilderState.executionContextState.mapping && + this.queryBuilderState.executionContextState.runtimeValue instanceof + RuntimePointer ) { this.dataAccessState = new DataAccessState( this.queryBuilderState.applicationStore, this.queryBuilderState.graphManagerState, { - mapping: this.queryBuilderState.mapping.path, + mapping: this.queryBuilderState.executionContextState.mapping.path, runtime: - this.queryBuilderState.runtimeValue.packageableRuntime.value.path, + this.queryBuilderState.executionContextState.runtimeValue + .packageableRuntime.value.path, getQuery: async () => this.createExecutableQuery( this.queryBuilderState.buildQuery(), diff --git a/packages/legend-query-builder/src/stores/explorer/QueryBuilderExplorerState.ts b/packages/legend-query-builder/src/stores/explorer/QueryBuilderExplorerState.ts index 0d7aa2e969a..c4262e6c621 100644 --- a/packages/legend-query-builder/src/stores/explorer/QueryBuilderExplorerState.ts +++ b/packages/legend-query-builder/src/stores/explorer/QueryBuilderExplorerState.ts @@ -660,7 +660,7 @@ export class QueryBuilderExplorerState { refreshTreeData(): void { const _class = this.queryBuilderState.class; - const _mapping = this.queryBuilderState.mapping; + const _mapping = this.queryBuilderState.executionContextState.mapping; this.setTreeData( _class && _mapping && this.mappingModelCoverageAnalysisResult ? getQueryBuilderTreeData( @@ -675,8 +675,8 @@ export class QueryBuilderExplorerState { // We will only refetch if the analysis result's mapping has changed. // This makes the assumption that the mapping has not been edited, which is a valid assumption since query is not for editing mappings if ( - this.queryBuilderState.mapping && - this.queryBuilderState.mapping !== + this.queryBuilderState.executionContextState.mapping && + this.queryBuilderState.executionContextState.mapping !== this.mappingModelCoverageAnalysisResult?.mapping ) { this.mappingModelCoverageAnalysisState.inProgress(); @@ -692,7 +692,7 @@ export class QueryBuilderExplorerState { try { this.mappingModelCoverageAnalysisResult = (yield flowResult( this.queryBuilderState.graphManagerState.graphManager.analyzeMappingModelCoverage( - this.queryBuilderState.mapping, + this.queryBuilderState.executionContextState.mapping, this.queryBuilderState.graphManagerState.graph, ), )) as MappingModelCoverageAnalysisResult; @@ -720,7 +720,7 @@ export class QueryBuilderExplorerState { *previewData( node: QueryBuilderExplorerTreePropertyNodeData, ): GeneratorFn { - const runtime = this.queryBuilderState.runtimeValue; + const runtime = this.queryBuilderState.executionContextState.runtimeValue; if (!runtime) { this.queryBuilderState.applicationStore.notificationService.notifyWarning( `Can't preview data for property '${node.property.name}': runtime is not specified`, @@ -730,7 +730,7 @@ export class QueryBuilderExplorerState { if ( !node.mappingData.mapped || !this.queryBuilderState.class || - !this.queryBuilderState.mapping + !this.queryBuilderState.executionContextState.mapping ) { return; } @@ -759,7 +759,7 @@ export class QueryBuilderExplorerState { this.queryBuilderState, propertyExpression, ), - this.queryBuilderState.mapping, + this.queryBuilderState.executionContextState.mapping, runtime, this.queryBuilderState.graphManagerState.graph, )) as ExecutionResult; @@ -796,7 +796,7 @@ export class QueryBuilderExplorerState { this.queryBuilderState, propertyExpression, ), - this.queryBuilderState.mapping, + this.queryBuilderState.executionContextState.mapping, runtime, this.queryBuilderState.graphManagerState.graph, )) as ExecutionResult; diff --git a/packages/legend-query-builder/src/stores/fetch-structure/graph-fetch/QueryBuilderGraphFetchTreeStateBuilder.ts b/packages/legend-query-builder/src/stores/fetch-structure/graph-fetch/QueryBuilderGraphFetchTreeStateBuilder.ts index c3ab66a4016..57efa7eee31 100644 --- a/packages/legend-query-builder/src/stores/fetch-structure/graph-fetch/QueryBuilderGraphFetchTreeStateBuilder.ts +++ b/packages/legend-query-builder/src/stores/fetch-structure/graph-fetch/QueryBuilderGraphFetchTreeStateBuilder.ts @@ -32,11 +32,13 @@ import { CollectionInstanceValue, getClassProperty, PrimitiveType, + VariableExpression, } from '@finos/legend-graph'; import { assertIsBoolean, assertIsString, assertTrue, + assertType, guaranteeIsString, guaranteeNonNullable, guaranteeType, @@ -45,7 +47,7 @@ import { QUERY_BUILDER_PURE_PATH, QUERY_BUILDER_SUPPORTED_FUNCTIONS, } from '../../../graph/QueryBuilderMetaModelConst.js'; -import type { QueryBuilderState } from '../../QueryBuilderState.js'; +import { type QueryBuilderState } from '../../QueryBuilderState.js'; import { QueryBuilderValueSpecificationProcessor } from '../../QueryBuilderStateBuilder.js'; import { FETCH_STRUCTURE_IMPLEMENTATION } from '../QueryBuilderFetchStructureImplementationState.js'; import { @@ -56,6 +58,7 @@ import { } from './QueryBuilderGraphFetchTreeState.js'; import { buildGraphFetchTreeData } from './QueryBuilderGraphFetchTreeUtil.js'; import {} from 'mobx'; +import { QueryBuilderInternalizeState } from '../../QueryBuilderInternalizeState.js'; export const processGraphFetchExpression = ( expression: SimpleFunctionExpression, @@ -105,6 +108,68 @@ export const processGraphFetchExpression = ( } }; +export const processInternalizeExpression = ( + expression: SimpleFunctionExpression, + queryBuilderState: QueryBuilderState, + parentLambda: LambdaFunction, +): void => { + // update fetch-structure + queryBuilderState.fetchStructureState.changeImplementation( + FETCH_STRUCTURE_IMPLEMENTATION.GRAPH_FETCH, + ); + const functionName = expression.functionName; + console.log(expression); + // check parameters + assertTrue( + expression.parametersValues.length === 3, + `Can't process ${functionName}() expression: ${functionName}() expects 2 argument`, + ); + + console.log('expression', expression); + + // first param classs + const classVal = expression.parametersValues[0]; + const _class = classVal?.genericType?.value.rawType; + assertType( + _class, + Class, + `Can't process internalize() expression: internalize() return type is missing`, + ); + + queryBuilderState.setClass(_class); + queryBuilderState.milestoningState.clearMilestoningDates(); + queryBuilderState.explorerState.refreshTreeData(); + // binding + + const instanceExpression = guaranteeType( + expression.parametersValues[1], + InstanceValue, + `Can't process internalize() expression: only support internalize() with 1st parameter as instance value`, + ); + const binding = guaranteeType( + guaranteeType( + instanceExpression.values[0], + PackageableElementReference, + `Can't process internalize() expression: only support internalize() with 1st parameter as packagableElement value`, + ).value, + Binding, + `Can't process internalize() expression: only support internalize() with 1st parameter as binding value`, + ); + + const variableExpression = guaranteeType( + expression.parametersValues[2], + VariableExpression, + ); + + const inernalize = new QueryBuilderInternalizeState( + binding, + variableExpression, + queryBuilderState, + ); + + queryBuilderState.setInternalize(inernalize); +}; + type PropertyValue = object | string | number | boolean; // Dynamically sets key values of config @@ -323,10 +388,12 @@ export const processGraphFetchExternalizeExpression = ( ); // build preceding expression + console.log('x', precedingExpression.functionName); assertTrue( matchFunctionName(precedingExpression.functionName, [ QUERY_BUILDER_SUPPORTED_FUNCTIONS.GRAPH_FETCH, QUERY_BUILDER_SUPPORTED_FUNCTIONS.GRAPH_FETCH_CHECKED, + QUERY_BUILDER_SUPPORTED_FUNCTIONS.INTERNALIZE, ]), `Can't process externalize() expression: only support externalize() in graph-fetch expression`, ); @@ -373,7 +440,6 @@ export const processGraphFetchExternalizeExpression = ( Binding, `Can't process externalize() expression: only support externalize() with 1st parameter as binding value`, ); - const externalizeState = new GraphFetchExternalFormatSerializationState( graphFetchTreeState, binding, diff --git a/packages/legend-query-builder/src/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.ts b/packages/legend-query-builder/src/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.ts index a08d85d935d..e448d255673 100644 --- a/packages/legend-query-builder/src/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.ts +++ b/packages/legend-query-builder/src/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.ts @@ -380,10 +380,12 @@ export class PostFilterConditionState implements Hashable { this.value, ), guaranteeNonNullable( - this.postFilterState.tdsState.queryBuilderState.mapping, + this.postFilterState.tdsState.queryBuilderState + .executionContextState.mapping, ), guaranteeNonNullable( - this.postFilterState.tdsState.queryBuilderState.runtimeValue, + this.postFilterState.tdsState.queryBuilderState + .executionContextState.runtimeValue, ), this.postFilterState.tdsState.queryBuilderState.graphManagerState .graph, diff --git a/packages/legend-query-builder/src/stores/filter/QueryBuilderFilterState.ts b/packages/legend-query-builder/src/stores/filter/QueryBuilderFilterState.ts index 8ec55ede0d1..1e0745d8bc7 100644 --- a/packages/legend-query-builder/src/stores/filter/QueryBuilderFilterState.ts +++ b/packages/legend-query-builder/src/stores/filter/QueryBuilderFilterState.ts @@ -134,9 +134,12 @@ export class FilterConditionState implements Hashable { this.propertyExpressionState.propertyExpression, this.value, ), - guaranteeNonNullable(this.filterState.queryBuilderState.mapping), guaranteeNonNullable( - this.filterState.queryBuilderState.runtimeValue, + this.filterState.queryBuilderState.executionContextState.mapping, + ), + guaranteeNonNullable( + this.filterState.queryBuilderState.executionContextState + .runtimeValue, ), this.filterState.queryBuilderState.graphManagerState.graph, )) as ExecutionResult; diff --git a/packages/legend-query-builder/src/stores/workflows/ClassQueryBuilderState.ts b/packages/legend-query-builder/src/stores/workflows/ClassQueryBuilderState.ts index 18822322124..ce87e8d2162 100644 --- a/packages/legend-query-builder/src/stores/workflows/ClassQueryBuilderState.ts +++ b/packages/legend-query-builder/src/stores/workflows/ClassQueryBuilderState.ts @@ -44,7 +44,8 @@ export class ClassQueryBuilderState extends QueryBuilderState { ); // cascading const isCurrentMappingCompatible = - this.mapping && compatibleMappings.includes(this.mapping); + this.executionContextState.mapping && + compatibleMappings.includes(this.executionContextState.mapping); if (this.isMappingReadOnly || isCurrentMappingCompatible) { return; } diff --git a/packages/legend-query-builder/src/stores/workflows/ServiceQueryBuilderState.ts b/packages/legend-query-builder/src/stores/workflows/ServiceQueryBuilderState.ts index 403a0502a6e..af2fad385f7 100644 --- a/packages/legend-query-builder/src/stores/workflows/ServiceQueryBuilderState.ts +++ b/packages/legend-query-builder/src/stores/workflows/ServiceQueryBuilderState.ts @@ -78,12 +78,8 @@ export class ServiceQueryBuilderState extends QueryBuilderState { this.onExecutionContextChange = onExecutionContextChange; if (service.execution instanceof PureSingleExecution) { - assertTrue( - Boolean(service.execution.mapping && service.execution.runtime), - 'Service queries without runtime/mapping are not supported', - ); - this.mapping = service.execution.mapping?.value; - this.runtimeValue = service.execution.runtime; + this.executionContextState.mapping = service.execution.mapping?.value; + this.executionContextState.runtimeValue = service.execution.runtime; } else if (service.execution instanceof PureMultiExecution) { this.executionContexts = service.execution.executionParameters.map( (ep) => ({ @@ -92,7 +88,6 @@ export class ServiceQueryBuilderState extends QueryBuilderState { runtimeValue: ep.runtime, }), ); - let selectedExecutionContext: ServiceExecutionContext; if (executionContextKey) { const matchingExecutionContext = this.executionContexts.find( @@ -115,8 +110,9 @@ export class ServiceQueryBuilderState extends QueryBuilderState { } this.setSelectedExecutionContext(selectedExecutionContext); - this.mapping = selectedExecutionContext.mapping; - this.runtimeValue = selectedExecutionContext.runtimeValue; + this.executionContextState.mapping = selectedExecutionContext.mapping; + this.executionContextState.runtimeValue = + selectedExecutionContext.runtimeValue; } }