From f8cd2bfd806879795589483cf60952e4336f9768 Mon Sep 17 00:00:00 2001 From: An Phi Date: Mon, 10 Feb 2025 22:31:39 -0500 Subject: [PATCH] datacube: rename DataCubeQuery to DataCubeSpecification and UX enhancements (#3882) * datacube: improve disclaimer when turning on caching * datacube: rename DataCubeQuery to DataCubeSpecification * datacube: rename DataCubeQuerySnapshot to DataCubeSnapshot * datacube: support deleting persistent data cubes * datacube: show about dialog * datacube: allow syncing name while saving * datacube: allow auto-enable caching * datacube: add update DataCube info dropdown menu item * datacube: support undo/redo * datacube: support view source --- .changeset/cool-frogs-sin.md | 11 + .changeset/great-plums-tell.md | 10 + .changeset/itchy-monkeys-count.md | 10 + .../scripts/setup.js | 3 + .../legend-application-data-cube/README.md | 2 +- .../src/__lib__/LegendDataCubeNavigation.ts | 22 +- .../src/__lib__/LegendDataCubeUserData.ts | 4 +- .../LegendDataCubeApplicationConfig.ts | 12 + .../LegendDataCubeBlockingWindow.tsx | 120 ++++ .../LegendDataCubeWebApplication.tsx | 6 +- .../builder/LegendDataCubeBuilder.tsx | 331 +++++++++++ .../LegendDataCubeBuilderStoreProvider.tsx} | 35 +- .../LegendDataCubeCreator.tsx} | 18 +- .../LegendDataCubeDeleteConfirmation.tsx | 87 +++ .../LegendDataCubeLoader.tsx} | 181 ++++-- .../builder/LegendDataCubeSaver.tsx | 184 +++++++ .../builder/LegendDataCubeSourceViewer.tsx | 108 ++++ .../AdhocQueryDataCubeSourceBuilder.tsx | 2 +- .../LegendQueryDataCubeSourceBuilder.tsx | 31 +- .../LegendDataCubeQueryBuilder.tsx | 164 ------ .../LegendDataCubeQuerySaver.tsx | 116 ---- .../stores/LegendDataCubeDataCubeEngine.ts | 4 +- .../builder/LegendDataCubeBuilderStore.tsx | 516 ++++++++++++++++++ .../LegendDataCubeCreatorState.tsx} | 44 +- .../builder/LegendDataCubeLoaderState.tsx | 248 +++++++++ .../AdhocQueryDataCubeSourceBuilderState.ts | 0 .../LegendDataCubeSourceBuilderState.ts | 2 +- .../LegendQueryDataCubeSourceBuilderState.ts | 0 .../LegendDataCubeQueryBuilderStore.tsx | 323 ----------- .../LegendDataCubeQueryLoaderState.tsx | 260 --------- .../src/__lib__/LegendQueryNavigation.ts | 2 +- .../Core_LegendQueryApplicationPlugin.tsx | 4 +- .../components/__tests__/LegendQuery.test.tsx | 12 +- .../data-cube/ExistingQueryDataCubeViewer.tsx | 4 +- .../data-cube/ExistingQueryDataCubeViewer.ts | 9 +- .../components/LegendREPLDataCubeHeader.tsx | 2 +- ...er.tsx => LegendREPLFrameworkProvider.tsx} | 2 +- .../LegendREPLPublishDataCubeAlert.tsx | 22 +- .../components/LegendREPLWebApplication.tsx | 143 ++--- .../src/stores/LegendREPLBaseStore.tsx | 32 +- .../src/stores/LegendREPLDataCubeEngine.ts | 2 +- .../src/stores/LegendREPLServerClient.ts | 10 +- .../data-cube/LegendStudioDataCubeHelper.ts | 10 +- .../src/__lib__/DataCubeSetting.ts | 2 + .../src/components/DataCube.tsx | 26 +- .../src/components/DataCubePlaceholder.tsx | 7 +- .../src/components/DataCubeTitleBar.tsx | 68 ++- packages/legend-data-cube/src/index.tsx | 5 +- .../src/stores/DataCubeAPI.ts | 31 +- .../src/stores/DataCubeOptions.ts | 24 +- .../src/stores/DataCubeState.tsx | 8 +- .../DataCubeSnapshotService.data-cube-test.ts | 286 ++++++++++ .../{core => }/__tests__/DataCubeTestUtils.ts | 6 +- .../core/DataCubeConfigurationBuilder.ts | 6 +- .../src/stores/core/DataCubeEngine.tsx | 16 +- .../src/stores/core/DataCubeQueryBuilder.ts | 8 +- .../stores/core/DataCubeQueryBuilderUtils.ts | 16 +- ...beQuerySnapshot.ts => DataCubeSnapshot.ts} | 65 ++- ...tBuilder.ts => DataCubeSnapshotBuilder.ts} | 44 +- ...ils.ts => DataCubeSnapshotBuilderUtils.ts} | 64 ++- .../DataCubeQueryRoundtrip.data-cube-test.ts | 41 +- .../DataCubeQueryAggregateOperation.ts | 6 +- ...taCubeQueryAggregateOperation__Average.tsx | 2 +- ...DataCubeQueryAggregateOperation__Count.tsx | 2 +- ...DataCubeQueryAggregateOperation__First.tsx | 2 +- ...beQueryAggregateOperation__JoinStrings.tsx | 2 +- .../DataCubeQueryAggregateOperation__Last.tsx | 2 +- .../DataCubeQueryAggregateOperation__Max.tsx | 2 +- .../DataCubeQueryAggregateOperation__Min.tsx | 2 +- ...ryAggregateOperation__StdDevPopulation.tsx | 2 +- ...eQueryAggregateOperation__StdDevSample.tsx | 2 +- .../DataCubeQueryAggregateOperation__Sum.tsx | 2 +- ...beQueryAggregateOperation__UniqueValue.tsx | 2 +- ...AggregateOperation__VariancePopulation.tsx | 2 +- ...ueryAggregateOperation__VarianceSample.tsx | 2 +- .../filter/DataCubeQueryFilterEditorState.ts | 16 +- .../filter/DataCubeQueryFilterOperation.ts | 8 +- .../DataCubeQueryFilterOperation__Contain.tsx | 6 +- ...ilterOperation__ContainCaseInsensitive.tsx | 6 +- .../DataCubeQueryFilterOperation__EndWith.tsx | 6 +- ...ilterOperation__EndWithCaseInsensitive.tsx | 6 +- .../DataCubeQueryFilterOperation__Equal.tsx | 6 +- ...yFilterOperation__EqualCaseInsensitive.tsx | 6 +- ...rOperation__EqualCaseInsensitiveColumn.tsx | 6 +- ...aCubeQueryFilterOperation__EqualColumn.tsx | 6 +- ...aCubeQueryFilterOperation__GreaterThan.tsx | 6 +- ...ueryFilterOperation__GreaterThanColumn.tsx | 6 +- ...eryFilterOperation__GreaterThanOrEqual.tsx | 6 +- ...terOperation__GreaterThanOrEqualColumn.tsx | 6 +- ...ataCubeQueryFilterOperation__IsNotNull.tsx | 6 +- .../DataCubeQueryFilterOperation__IsNull.tsx | 6 +- ...DataCubeQueryFilterOperation__LessThan.tsx | 6 +- ...beQueryFilterOperation__LessThanColumn.tsx | 6 +- ...eQueryFilterOperation__LessThanOrEqual.tsx | 6 +- ...FilterOperation__LessThanOrEqualColumn.tsx | 6 +- ...taCubeQueryFilterOperation__NotContain.tsx | 6 +- ...taCubeQueryFilterOperation__NotEndWith.tsx | 6 +- ...DataCubeQueryFilterOperation__NotEqual.tsx | 6 +- ...lterOperation__NotEqualCaseInsensitive.tsx | 6 +- ...eration__NotEqualCaseInsensitiveColumn.tsx | 6 +- ...beQueryFilterOperation__NotEqualColumn.tsx | 6 +- ...CubeQueryFilterOperation__NotStartWith.tsx | 6 +- ...ataCubeQueryFilterOperation__StartWith.tsx | 6 +- ...terOperation__StartWithCaseInsensitive.tsx | 6 +- .../core/model/DataCubeConfiguration.ts | 2 +- ...aCubeQuery.ts => DataCubeSpecification.ts} | 20 +- .../services/DataCubeSettingService.tsx | 23 + ...tService.ts => DataCubeSnapshotService.ts} | 181 +++--- .../src/stores/view/DataCubeInfoState.ts | 10 +- .../src/stores/view/DataCubeViewState.ts | 166 +++++- ...ataCubeEditorColumnPropertiesPanelState.ts | 9 +- .../editor/DataCubeEditorColumnsPanelState.ts | 9 +- ...taCubeEditorGeneralPropertiesPanelState.ts | 9 +- ...ataCubeEditorHorizontalPivotsPanelState.ts | 9 +- .../DataCubeEditorMutableConfiguration.ts | 4 +- .../view/editor/DataCubeEditorPanelState.ts | 8 +- .../DataCubeEditorPivotLayoutPanelState.ts | 11 +- .../editor/DataCubeEditorSortsPanelState.ts | 9 +- .../view/editor/DataCubeEditorState.tsx | 18 +- .../DataCubeEditorVerticalPivotsPanelState.ts | 9 +- .../view/extend/DataCubeColumnEditorState.tsx | 4 +- .../extend/DataCubeExtendManagerState.tsx | 22 +- .../view/filter/DataCubeFilterEditorState.tsx | 14 +- .../view/grid/DataCubeGridClientEngine.ts | 12 +- .../grid/DataCubeGridConfigurationBuilder.tsx | 24 +- .../view/grid/DataCubeGridControllerState.ts | 24 +- .../view/grid/DataCubeGridQueryBuilder.ts | 8 +- ...lder.ts => DataCubeGridSnapshotBuilder.ts} | 12 +- .../src/stores/view/grid/DataCubeGridState.ts | 24 +- .../graph-manager/AbstractPureGraphManager.ts | 32 +- ...DataCubeQuery.ts => PersistentDataCube.ts} | 12 +- .../protocol/pure/v1/V1_PureGraphManager.ts | 46 +- .../pure/v1/engine/V1_EngineServerClient.ts | 70 ++- .../pure/v1/engine/V1_GraphManagerEngine.ts | 33 +- .../pure/v1/engine/V1_RemoteEngine.ts | 60 +- packages/legend-graph/src/index.ts | 6 +- .../data-cube/QueryBuilderDataCube.tsx | 5 +- .../data-cube/QueryBuilderDataCubeEngine.ts | 17 +- .../data-cube/QueryBuilderDataCubeHelper.ts | 2 +- .../QueryBuilderDataCubeViewerState.ts | 13 +- .../src/index.ts | 6 +- 141 files changed, 3198 insertions(+), 1794 deletions(-) create mode 100644 .changeset/cool-frogs-sin.md create mode 100644 .changeset/great-plums-tell.md create mode 100644 .changeset/itchy-monkeys-count.md create mode 100644 packages/legend-application-data-cube/src/components/LegendDataCubeBlockingWindow.tsx create mode 100644 packages/legend-application-data-cube/src/components/builder/LegendDataCubeBuilder.tsx rename packages/legend-application-data-cube/src/components/{query-builder/LegendDataCubeQueryBuilderStoreProvider.tsx => builder/LegendDataCubeBuilderStoreProvider.tsx} (53%) rename packages/legend-application-data-cube/src/components/{query-builder/LegendDataCubeNewQueryBuilder.tsx => builder/LegendDataCubeCreator.tsx} (83%) create mode 100644 packages/legend-application-data-cube/src/components/builder/LegendDataCubeDeleteConfirmation.tsx rename packages/legend-application-data-cube/src/components/{query-builder/LegendDataCubeQueryLoader.tsx => builder/LegendDataCubeLoader.tsx} (60%) create mode 100644 packages/legend-application-data-cube/src/components/builder/LegendDataCubeSaver.tsx create mode 100644 packages/legend-application-data-cube/src/components/builder/LegendDataCubeSourceViewer.tsx rename packages/legend-application-data-cube/src/components/{query-builder/source-builder => builder/source}/AdhocQueryDataCubeSourceBuilder.tsx (93%) rename packages/legend-application-data-cube/src/components/{query-builder/source-builder => builder/source}/LegendQueryDataCubeSourceBuilder.tsx (94%) delete mode 100644 packages/legend-application-data-cube/src/components/query-builder/LegendDataCubeQueryBuilder.tsx delete mode 100644 packages/legend-application-data-cube/src/components/query-builder/LegendDataCubeQuerySaver.tsx create mode 100644 packages/legend-application-data-cube/src/stores/builder/LegendDataCubeBuilderStore.tsx rename packages/legend-application-data-cube/src/stores/{query-builder/LegendDataCubeNewQueryState.tsx => builder/LegendDataCubeCreatorState.tsx} (74%) create mode 100644 packages/legend-application-data-cube/src/stores/builder/LegendDataCubeLoaderState.tsx rename packages/legend-application-data-cube/src/stores/{query-builder/source-builder => builder/source}/AdhocQueryDataCubeSourceBuilderState.ts (100%) rename packages/legend-application-data-cube/src/stores/{query-builder/source-builder => builder/source}/LegendDataCubeSourceBuilderState.ts (94%) rename packages/legend-application-data-cube/src/stores/{query-builder/source-builder => builder/source}/LegendQueryDataCubeSourceBuilderState.ts (100%) delete mode 100644 packages/legend-application-data-cube/src/stores/query-builder/LegendDataCubeQueryBuilderStore.tsx delete mode 100644 packages/legend-application-data-cube/src/stores/query-builder/LegendDataCubeQueryLoaderState.tsx rename packages/legend-application-repl/src/components/{LegendREPLFramworkProvider.tsx => LegendREPLFrameworkProvider.tsx} (97%) create mode 100644 packages/legend-data-cube/src/stores/__tests__/DataCubeSnapshotService.data-cube-test.ts rename packages/legend-data-cube/src/stores/{core => }/__tests__/DataCubeTestUtils.ts (96%) rename packages/legend-data-cube/src/stores/core/{DataCubeQuerySnapshot.ts => DataCubeSnapshot.ts} (70%) rename packages/legend-data-cube/src/stores/core/{DataCubeQuerySnapshotBuilder.ts => DataCubeSnapshotBuilder.ts} (96%) rename packages/legend-data-cube/src/stores/core/{DataCubeQuerySnapshotBuilderUtils.ts => DataCubeSnapshotBuilderUtils.ts} (93%) rename packages/legend-data-cube/src/stores/core/model/{DataCubeQuery.ts => DataCubeSpecification.ts} (65%) rename packages/legend-data-cube/src/stores/services/{DataCubeQuerySnapshotService.ts => DataCubeSnapshotService.ts} (52%) rename packages/legend-data-cube/src/stores/view/grid/{DataCubeGridQuerySnapshotBuilder.ts => DataCubeGridSnapshotBuilder.ts} (93%) rename packages/legend-graph/src/graph-manager/action/query/{PersistentDataCubeQuery.ts => PersistentDataCube.ts} (86%) diff --git a/.changeset/cool-frogs-sin.md b/.changeset/cool-frogs-sin.md new file mode 100644 index 0000000000..e2bf338ccb --- /dev/null +++ b/.changeset/cool-frogs-sin.md @@ -0,0 +1,11 @@ +--- +'@finos/legend-application-data-cube-bootstrap': patch +'@finos/legend-vscode-extension-dependencies': patch +'@finos/legend-application-data-cube': patch +'@finos/legend-application-studio': patch +'@finos/legend-application-query': patch +'@finos/legend-application-repl': patch +'@finos/legend-query-builder': patch +'@finos/legend-data-cube': patch +'@finos/legend-graph': patch +--- diff --git a/.changeset/great-plums-tell.md b/.changeset/great-plums-tell.md new file mode 100644 index 0000000000..dd93eb1125 --- /dev/null +++ b/.changeset/great-plums-tell.md @@ -0,0 +1,10 @@ +--- +'@finos/legend-vscode-extension-dependencies': patch +'@finos/legend-application-data-cube': patch +'@finos/legend-application-studio': patch +'@finos/legend-application-query': patch +'@finos/legend-application-repl': patch +'@finos/legend-query-builder': patch +'@finos/legend-data-cube': patch +'@finos/legend-graph': patch +--- diff --git a/.changeset/itchy-monkeys-count.md b/.changeset/itchy-monkeys-count.md new file mode 100644 index 0000000000..dd93eb1125 --- /dev/null +++ b/.changeset/itchy-monkeys-count.md @@ -0,0 +1,10 @@ +--- +'@finos/legend-vscode-extension-dependencies': patch +'@finos/legend-application-data-cube': patch +'@finos/legend-application-studio': patch +'@finos/legend-application-query': patch +'@finos/legend-application-repl': patch +'@finos/legend-query-builder': patch +'@finos/legend-data-cube': patch +'@finos/legend-graph': patch +--- diff --git a/packages/legend-application-data-cube-bootstrap/scripts/setup.js b/packages/legend-application-data-cube-bootstrap/scripts/setup.js index be866500fd..8e25313067 100644 --- a/packages/legend-application-data-cube-bootstrap/scripts/setup.js +++ b/packages/legend-application-data-cube-bootstrap/scripts/setup.js @@ -48,6 +48,9 @@ export const setup = (outputDir, dataCubeDocsDir) => { depot: { url: 'http://localhost:6200/depot/api', }, + query: { + url: 'http://localhost:9001/query', + }, documentation: { url: 'https://legend.finos.org', registry: [], diff --git a/packages/legend-application-data-cube/README.md b/packages/legend-application-data-cube/README.md index 237c75c3c6..81f2d114af 100644 --- a/packages/legend-application-data-cube/README.md +++ b/packages/legend-application-data-cube/README.md @@ -1,3 +1,3 @@ # @finos/legend-application-data-cube -Legend Query core +Legend DataCube core diff --git a/packages/legend-application-data-cube/src/__lib__/LegendDataCubeNavigation.ts b/packages/legend-application-data-cube/src/__lib__/LegendDataCubeNavigation.ts index 0ea68dbfbb..56b9a53db8 100644 --- a/packages/legend-application-data-cube/src/__lib__/LegendDataCubeNavigation.ts +++ b/packages/legend-application-data-cube/src/__lib__/LegendDataCubeNavigation.ts @@ -17,20 +17,28 @@ import { generatePath } from '@finos/legend-application/browser'; export enum LEGEND_DATA_CUBE_ROUTE_PATTERN_TOKEN { - QUERY_ID = 'queryId', + DATA_CUBE_ID = 'dataCubeId', SOURCE_DATA = 'sourceData', } export const LEGEND_DATA_CUBE_ROUTE_PATTERN = Object.freeze({ - QUERY_BUILDER: `/:${LEGEND_DATA_CUBE_ROUTE_PATTERN_TOKEN.QUERY_ID}?`, + BUILDER: `/:${LEGEND_DATA_CUBE_ROUTE_PATTERN_TOKEN.DATA_CUBE_ID}?`, }); -export type LegendDataCubeQueryBuilderPathParams = { - [LEGEND_DATA_CUBE_ROUTE_PATTERN_TOKEN.QUERY_ID]: string; +export type LegendDataCubeBuilderPathParams = { + [LEGEND_DATA_CUBE_ROUTE_PATTERN_TOKEN.DATA_CUBE_ID]: string; }; -export const generateQueryBuilderRoute = (queryId: string | null): string => { - return generatePath(LEGEND_DATA_CUBE_ROUTE_PATTERN.QUERY_BUILDER, { - [LEGEND_DATA_CUBE_ROUTE_PATTERN_TOKEN.QUERY_ID]: queryId, +export const generateBuilderRoute = (dataCubeId: string | null): string => { + return generatePath(LEGEND_DATA_CUBE_ROUTE_PATTERN.BUILDER, { + [LEGEND_DATA_CUBE_ROUTE_PATTERN_TOKEN.DATA_CUBE_ID]: dataCubeId, }); }; + +/** + * @external_application_navigation This depends on Legend Query routing and is hardcoded so it's potentially brittle + */ +export const EXTERNAL_APPLICATION_NAVIGATION__generateQueryViewUrl = ( + queryApplicationUrl: string, + queryId: string, +) => `${queryApplicationUrl}/edit/${queryId}`; diff --git a/packages/legend-application-data-cube/src/__lib__/LegendDataCubeUserData.ts b/packages/legend-application-data-cube/src/__lib__/LegendDataCubeUserData.ts index bfef69c8fe..213a1449bd 100644 --- a/packages/legend-application-data-cube/src/__lib__/LegendDataCubeUserData.ts +++ b/packages/legend-application-data-cube/src/__lib__/LegendDataCubeUserData.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -export const RECENTLY_VIEWED_QUERIES_LIMIT = 10; +export const RECENTLY_VIEWED_DATA_CUBES_LIMIT = 10; export enum LegendDataCubeUserDataKey { - RECENTLY_VIEWED_QUERIES = 'recentlyViewedQueries', + RECENTLY_VIEWED_DATA_CUBES = 'recentlyViewedDataCubes', } diff --git a/packages/legend-application-data-cube/src/application/LegendDataCubeApplicationConfig.ts b/packages/legend-application-data-cube/src/application/LegendDataCubeApplicationConfig.ts index 39016dbd1f..3c00220483 100644 --- a/packages/legend-application-data-cube/src/application/LegendDataCubeApplicationConfig.ts +++ b/packages/legend-application-data-cube/src/application/LegendDataCubeApplicationConfig.ts @@ -30,12 +30,16 @@ export interface LegendDataCubeApplicationConfigurationData url: string; }; engine: { url: string; queryUrl: string }; + query?: { + url: string; + }; } export class LegendDataCubeApplicationConfig extends LegendApplicationConfig { readonly engineServerUrl: string; readonly depotServerUrl: string; readonly engineQueryServerUrl?: string | undefined; + readonly queryApplicationUrl?: string | undefined; constructor( input: LegendApplicationConfigurationInput, @@ -58,6 +62,7 @@ export class LegendDataCubeApplicationConfig extends LegendApplicationConfig { input.configData.engine.queryUrl, ) : undefined; + // depot assertNonNullable( input.configData.depot, @@ -69,6 +74,13 @@ export class LegendDataCubeApplicationConfig extends LegendApplicationConfig { `Can't configure application: 'depot.url' field is missing or empty`, ), ); + + // query + if (input.configData.query?.url) { + this.queryApplicationUrl = LegendApplicationConfig.resolveAbsoluteUrl( + input.configData.query.url, + ); + } } getDefaultApplicationStorageKey(): string { return 'legend-data-cube'; diff --git a/packages/legend-application-data-cube/src/components/LegendDataCubeBlockingWindow.tsx b/packages/legend-application-data-cube/src/components/LegendDataCubeBlockingWindow.tsx new file mode 100644 index 0000000000..b395791cb4 --- /dev/null +++ b/packages/legend-application-data-cube/src/components/LegendDataCubeBlockingWindow.tsx @@ -0,0 +1,120 @@ +/** + * 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 { DataCubeIcon, Dialog } from '@finos/legend-art'; +import { + DEFAULT_TOOL_PANEL_WINDOW_CONFIG, + LayoutConfiguration, + type WindowConfiguration, +} from '@finos/legend-data-cube'; +import { action, makeObservable, observable } from 'mobx'; +import { observer } from 'mobx-react-lite'; +import { useRef } from 'react'; + +export const LegendDataCubeBlockingWindow = observer( + (props: { windowState: LegendDataCubeBlockingWindowState }) => { + const { windowState } = props; + const ref = useRef(null); + + // set the width and height of the dialog to make sure content overflow works properly + const handleEnter = () => { + if (ref.current?.parentElement) { + const { width, height } = + ref.current.parentElement.getBoundingClientRect(); + ref.current.style.width = `${windowState.configuration.window.width ?? width}px`; + ref.current.style.height = `${windowState.configuration.window.height ?? height}px`; + } + }; + + if (!windowState.isOpen) { + return null; + } + return ( + windowState.close()} + slotProps={{ + transition: { + onEnter: handleEnter, + }, + paper: { + elevation: 0, + }, + backdrop: { + classes: { + root: 'bg-black !opacity-25', + }, + }, + }} + classes={{ + root: 'data-cube h-full w-full flex items-center justify-center', + paper: 'min-h-10 min-w-40 rounded-none shadow-md', + }} + > +
+
+
{windowState.configuration.title}
+ +
+
+ {windowState.configuration.contentRenderer( + windowState.configuration, + )} +
+
+
+ ); + }, +); + +export class LegendDataCubeBlockingWindowState { + isOpen = false; + readonly configuration: LayoutConfiguration; + + constructor( + title: string, + contentRenderer: (config: LayoutConfiguration) => React.ReactNode, + windowConfiguration?: WindowConfiguration | undefined, + ) { + makeObservable(this, { + isOpen: observable, + open: action, + close: action, + }); + + this.configuration = new LayoutConfiguration(title, contentRenderer); + this.configuration.window = DEFAULT_TOOL_PANEL_WINDOW_CONFIG; + if (windowConfiguration) { + this.configuration.window = windowConfiguration; + } + } + + open() { + this.isOpen = true; + } + + close() { + this.isOpen = false; + } +} diff --git a/packages/legend-application-data-cube/src/components/LegendDataCubeWebApplication.tsx b/packages/legend-application-data-cube/src/components/LegendDataCubeWebApplication.tsx index 00559d055a..7d6ab29717 100644 --- a/packages/legend-application-data-cube/src/components/LegendDataCubeWebApplication.tsx +++ b/packages/legend-application-data-cube/src/components/LegendDataCubeWebApplication.tsx @@ -21,7 +21,7 @@ import { useLegendDataCubeBaseStore, } from './LegendDataCubeFrameworkProvider.js'; import { observer } from 'mobx-react-lite'; -import { LegendDataCubeQueryBuilder } from './query-builder/LegendDataCubeQueryBuilder.js'; +import { LegendDataCubeBuilder } from './builder/LegendDataCubeBuilder.js'; import { LEGEND_DATA_CUBE_ROUTE_PATTERN } from '../__lib__/LegendDataCubeNavigation.js'; import { useEffect } from 'react'; @@ -39,8 +39,8 @@ const LegendDataCubeWebApplicationRouter = observer(() => { {store.initializeState.hasSucceeded && ( } + path={LEGEND_DATA_CUBE_ROUTE_PATTERN.BUILDER} + element={} /> )} diff --git a/packages/legend-application-data-cube/src/components/builder/LegendDataCubeBuilder.tsx b/packages/legend-application-data-cube/src/components/builder/LegendDataCubeBuilder.tsx new file mode 100644 index 0000000000..0e35369527 --- /dev/null +++ b/packages/legend-application-data-cube/src/components/builder/LegendDataCubeBuilder.tsx @@ -0,0 +1,331 @@ +/** + * 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 { observer } from 'mobx-react-lite'; +import { + DataCube, + FormButton, + type DataCubeSettingValues, + DataCubePlaceholder, + DataCubeNativeMenuItem, + DataCubePlaceholderErrorDisplay, + type DataCubeMenuItem, + DataCubeSpecification, +} from '@finos/legend-data-cube'; +import {} from '@finos/legend-art'; +import { + useLegendDataCubeBuilderStore, + withLegendDataCubeBuilderStore, +} from './LegendDataCubeBuilderStoreProvider.js'; +import { useParams } from '@finos/legend-application/browser'; +import { + LEGEND_DATA_CUBE_ROUTE_PATTERN_TOKEN, + type LegendDataCubeBuilderPathParams, +} from '../../__lib__/LegendDataCubeNavigation.js'; +import { useEffect } from 'react'; +import { LegendDataCubeSettingStorageKey } from '../../__lib__/LegendDataCubeSetting.js'; +import type { LegendDataCubeBuilderStore } from '../../stores/builder/LegendDataCubeBuilderStore.js'; + +const LegendDataCubeBuilderHeader = observer(() => { + const store = useLegendDataCubeBuilderStore(); + + return ( +
+ store.loader.display.open()}> + Load DataCube + + store.creator.display.open()} + > + New DataCube + + store.saverDisplay.open()} + > + Save DataCube + +
+ ); +}); + +export const LegendDataCubeAbout = observer(() => { + const store = useLegendDataCubeBuilderStore(); + const config = store.application.config; + + return ( +
+
+
Environment:
+
{config.env}
+
+
+
Version:
+
{config.appVersion}
+
+
+
Revision:
+
{config.appVersionCommitId}
+
+
+
Build Time:
+
{config.appVersionBuildTime}
+
+
+
+
Engine Server:
+ +
+
+
Depot Server:
+ +
+ {config.engineQueryServerUrl !== undefined && ( +
+
DataCube Server:
+ +
+ )} +
+
+ ); +}); + +function generateMenuItems(store: LegendDataCubeBuilderStore) { + const application = store.application; + const builder = store.builder; + const persistentDataCube = builder?.persistentDataCube; + + const menuItems: (DataCubeMenuItem | DataCubeNativeMenuItem)[] = builder + ? [ + ...(builder.source + ? [ + { + label: 'View Source', + action: () => { + store.sourceViewerDisplay.open(); + }, + }, + ] + : []), + ...(persistentDataCube + ? [ + { + label: 'Reset to Latest Save', + action: () => { + const latestSpecification = + DataCubeSpecification.serialization.fromJson( + persistentDataCube.content, + ); + builder.dataCube + ?.applySpecification(latestSpecification) + .catch((error) => + store.alertService.alertUnhandledError(error), + ); + }, + }, + { + label: 'Update Info...', + action: () => { + // effectively, we open the save window to let user update the DataCube info, such as name, auto-enable caching, etc. + store.saverDisplay.open(); + }, + disabled: + !store.canCurrentUserManageDataCube(persistentDataCube), + }, + { + label: 'Delete DataCube...', + action: () => { + store.setDataCubeToDelete(builder.persistentDataCube); + store.deleteConfirmationDisplay.open(); + }, + disabled: + !store.canCurrentUserManageDataCube(persistentDataCube), + }, + ] + : []), + ] + : []; + return [ + ...(menuItems.length + ? [...menuItems, DataCubeNativeMenuItem.SEPARATOR] + : []), + { + label: 'See Documentation', + action: () => { + const url = application.documentationService.url; + if (url) { + application.navigationService.navigator.visitAddress( + application.documentationService.url, + ); + } + }, + disabled: !application.documentationService.url, + }, + { + label: 'About', + action: () => { + store.aboutDisplay.open(); + }, + }, + ]; +} + +export const LegendDataCubeBuilder = withLegendDataCubeBuilderStore( + observer(() => { + const store = useLegendDataCubeBuilderStore(); + const builder = store.builder; + const application = store.application; + const params = useParams(); + const dataCubeId = + params[LEGEND_DATA_CUBE_ROUTE_PATTERN_TOKEN.DATA_CUBE_ID]; + + useEffect(() => { + application.navigationService.navigator.blockNavigation( + // Only block navigation in production, in development, we should have + // the flexibility to reload the page quickly + // eslint-disable-next-line no-process-env + [() => process.env.NODE_ENV === 'production'], + ); + return (): void => { + application.navigationService.navigator.unblockNavigation(); + }; + }, [application]); + + useEffect(() => { + store + .initialize() + .catch((error) => store.alertService.alertUnhandledError(error)); + return () => { + store + .cleanUp() + .catch((error) => store.alertService.alertUnhandledError(error)); + }; + }, [store]); + + useEffect(() => { + store + .loadDataCube(dataCubeId) + .catch((error) => store.alertService.alertUnhandledError(error)); + }, [store, dataCubeId]); + + if (!store.initializeState.hasSucceeded) { + return ( + } + menuItems={generateMenuItems(store)} + > + {store.initializeState.isInProgress && ( +
+
Initializing...
+
+ )} + {store.initializeState.hasFailed && ( + + )} +
+ ); + } + if (!builder) { + return ( + } + menuItems={generateMenuItems(store)} + > +
+
Create a new DataCube to start
+ store.creator.display.open()} + > + New DataCube + +
+
+ ); + } + return ( + , + getHeaderMenuItems: () => generateMenuItems(store), + settingsData: { + configurations: store.baseStore.settings, + values: application.settingService.getObjectValue( + LegendDataCubeSettingStorageKey.DATA_CUBE, + ) as DataCubeSettingValues | undefined, + }, + onSettingsChanged(event) { + application.settingService.persistValue( + LegendDataCubeSettingStorageKey.DATA_CUBE, + event.values, + ); + }, + enableCache: true, + }} + /> + ); + }), +); diff --git a/packages/legend-application-data-cube/src/components/query-builder/LegendDataCubeQueryBuilderStoreProvider.tsx b/packages/legend-application-data-cube/src/components/builder/LegendDataCubeBuilderStoreProvider.tsx similarity index 53% rename from packages/legend-application-data-cube/src/components/query-builder/LegendDataCubeQueryBuilderStoreProvider.tsx rename to packages/legend-application-data-cube/src/components/builder/LegendDataCubeBuilderStoreProvider.tsx index b412d03d01..93b91f1af0 100644 --- a/packages/legend-application-data-cube/src/components/query-builder/LegendDataCubeQueryBuilderStoreProvider.tsx +++ b/packages/legend-application-data-cube/src/components/builder/LegendDataCubeBuilderStoreProvider.tsx @@ -17,40 +17,43 @@ import { guaranteeNonNullable } from '@finos/legend-shared'; import { useLocalObservable } from 'mobx-react-lite'; import { createContext, useContext } from 'react'; -import { LegendDataCubeQueryBuilderStore } from '../../stores/query-builder/LegendDataCubeQueryBuilderStore.js'; +import { LegendDataCubeBuilderStore } from '../../stores/builder/LegendDataCubeBuilderStore.js'; import { useLegendDataCubeBaseStore } from '../LegendDataCubeFrameworkProvider.js'; +import { LegendDataCubeBlockingWindow } from '../LegendDataCubeBlockingWindow.js'; -const LegendDataCubeQueryBuilderStoreContext = createContext< - LegendDataCubeQueryBuilderStore | undefined +const LegendDataCubeBuilderStoreContext = createContext< + LegendDataCubeBuilderStore | undefined >(undefined); -const LegendDataCubeQueryBuilderStoreProvider = (props: { +const LegendDataCubeBuilderStoreProvider = (props: { children: React.ReactNode; }) => { const { children } = props; const baseStore = useLegendDataCubeBaseStore(); const store = useLocalObservable( - () => new LegendDataCubeQueryBuilderStore(baseStore), + () => new LegendDataCubeBuilderStore(baseStore), ); return ( - + {children} - + + + ); }; -export const useLegendDataCubeQueryBuilderStore = () => +export const useLegendDataCubeBuilderStore = () => guaranteeNonNullable( - useContext(LegendDataCubeQueryBuilderStoreContext), - `Can't find query builder store in context`, + useContext(LegendDataCubeBuilderStoreContext), + `Can't find builder store in context`, ); -export const withLegendDataCubeQueryBuilderStore = ( - WrappedComponent: React.FC, -) => - function WithLegendDataCubeQueryBuilderStore() { +export const withLegendDataCubeBuilderStore = (WrappedComponent: React.FC) => + function WithLegendDataCubeBuilderStore() { return ( - + - + ); }; diff --git a/packages/legend-application-data-cube/src/components/query-builder/LegendDataCubeNewQueryBuilder.tsx b/packages/legend-application-data-cube/src/components/builder/LegendDataCubeCreator.tsx similarity index 83% rename from packages/legend-application-data-cube/src/components/query-builder/LegendDataCubeNewQueryBuilder.tsx rename to packages/legend-application-data-cube/src/components/builder/LegendDataCubeCreator.tsx index 0b8901a041..82f089e8ef 100644 --- a/packages/legend-application-data-cube/src/components/query-builder/LegendDataCubeNewQueryBuilder.tsx +++ b/packages/legend-application-data-cube/src/components/builder/LegendDataCubeCreator.tsx @@ -15,7 +15,7 @@ */ import { observer } from 'mobx-react-lite'; -import { LegendDataCubeSourceBuilderType } from '../../stores/query-builder/source-builder/LegendDataCubeSourceBuilderState.js'; +import { LegendDataCubeSourceBuilderType } from '../../stores/builder/source/LegendDataCubeSourceBuilderState.js'; import { useDropdownMenu } from '@finos/legend-art'; import { FormButton, @@ -23,15 +23,15 @@ import { FormDropdownMenuItem, FormDropdownMenuTrigger, } from '@finos/legend-data-cube'; -import { LegendQueryDataCubeSourceBuilderState } from '../../stores/query-builder/source-builder/LegendQueryDataCubeSourceBuilderState.js'; -import { LegendQueryDataCubeSourceBuilder } from './source-builder/LegendQueryDataCubeSourceBuilder.js'; -import { AdhocQueryDataCubeSourceBuilder } from './source-builder/AdhocQueryDataCubeSourceBuilder.js'; -import { AdhocQueryDataCubeSourceBuilderState } from '../../stores/query-builder/source-builder/AdhocQueryDataCubeSourceBuilderState.js'; -import { useLegendDataCubeQueryBuilderStore } from './LegendDataCubeQueryBuilderStoreProvider.js'; +import { LegendQueryDataCubeSourceBuilderState } from '../../stores/builder/source/LegendQueryDataCubeSourceBuilderState.js'; +import { LegendQueryDataCubeSourceBuilder } from './source/LegendQueryDataCubeSourceBuilder.js'; +import { AdhocQueryDataCubeSourceBuilder } from './source/AdhocQueryDataCubeSourceBuilder.js'; +import { AdhocQueryDataCubeSourceBuilderState } from '../../stores/builder/source/AdhocQueryDataCubeSourceBuilderState.js'; +import { useLegendDataCubeBuilderStore } from './LegendDataCubeBuilderStoreProvider.js'; -export const LegendDataCubeNewQueryBuilder = observer(() => { - const store = useLegendDataCubeQueryBuilderStore(); - const state = store.newQueryState; +export const LegendDataCubeCreator = observer(() => { + const store = useLegendDataCubeBuilderStore(); + const state = store.creator; const sourceBuilder = state.sourceBuilder; const selectedSourceType = sourceBuilder.label; const [ diff --git a/packages/legend-application-data-cube/src/components/builder/LegendDataCubeDeleteConfirmation.tsx b/packages/legend-application-data-cube/src/components/builder/LegendDataCubeDeleteConfirmation.tsx new file mode 100644 index 0000000000..1a0a63907b --- /dev/null +++ b/packages/legend-application-data-cube/src/components/builder/LegendDataCubeDeleteConfirmation.tsx @@ -0,0 +1,87 @@ +/** + * 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 { FormButton, FormTextInput } from '@finos/legend-data-cube'; +import { observer } from 'mobx-react-lite'; +import { useEffect, useState } from 'react'; +import { useLegendDataCubeBuilderStore } from './LegendDataCubeBuilderStoreProvider.js'; +import { formatDate } from '@finos/legend-shared'; +import { useApplicationStore } from '@finos/legend-application'; + +export const LegendDataCubeDeleteConfirmation = observer(() => { + const [text, setText] = useState(''); + const store = useLegendDataCubeBuilderStore(); + const application = useApplicationStore(); + const persistentDataCube = store.dataCubeToDelete; + const confirmationText = `${application.identityService.isAnonymous ? '' : application.identityService.currentUser}_${formatDate(Date.now(), 'yyyyMMdd')}`; + + useEffect(() => { + setText(''); + }, [persistentDataCube]); + + if (!persistentDataCube) { + return null; + } + return ( + <> +
+
+
+
+
+ You are about to delete this DataCube. Once deleted, it{' '} + cannot be recovered +
+ Please type the following to confirm: + + {confirmationText} + +
+
+ {text} +
+
+
+ { + setText(event.target.value); + }} + /> +
+
+
+
+
+ store.deleteConfirmationDisplay.close()}> + Cancel + + { + store + .deleteDataCube() + .catch((error) => store.alertService.alertUnhandledError(error)); + }} + > + Delete + +
+ + ); +}); diff --git a/packages/legend-application-data-cube/src/components/query-builder/LegendDataCubeQueryLoader.tsx b/packages/legend-application-data-cube/src/components/builder/LegendDataCubeLoader.tsx similarity index 60% rename from packages/legend-application-data-cube/src/components/query-builder/LegendDataCubeQueryLoader.tsx rename to packages/legend-application-data-cube/src/components/builder/LegendDataCubeLoader.tsx index 293bc5fd54..e697f930ac 100644 --- a/packages/legend-application-data-cube/src/components/query-builder/LegendDataCubeQueryLoader.tsx +++ b/packages/legend-application-data-cube/src/components/builder/LegendDataCubeLoader.tsx @@ -15,7 +15,13 @@ */ import { observer } from 'mobx-react-lite'; -import { cn, DataCubeIcon, useDropdownMenu } from '@finos/legend-art'; +import { + cn, + DataCubeIcon, + DropdownMenu, + DropdownMenuItem, + useDropdownMenu, +} from '@finos/legend-art'; import { debounce, formatDistanceToNow, @@ -30,29 +36,30 @@ import { FormDropdownMenuTrigger, FormTextInput, } from '@finos/legend-data-cube'; -import { useLegendDataCubeQueryBuilderStore } from './LegendDataCubeQueryBuilderStoreProvider.js'; +import { useLegendDataCubeBuilderStore } from './LegendDataCubeBuilderStoreProvider.js'; import { - DATA_CUBE_QUERY_LOADER_TYPEAHEAD_SEARCH_LIMIT, - DataCubeQuerySortByType, -} from '../../stores/query-builder/LegendDataCubeQueryLoaderState.js'; + DATA_CUBE_LOADER_TYPEAHEAD_SEARCH_LIMIT, + DataCubeSortByType, +} from '../../stores/builder/LegendDataCubeLoaderState.js'; +import { useApplicationStore } from '@finos/legend-application'; -const LegendDataCubeQuerySearcher = observer(() => { - const store = useLegendDataCubeQueryBuilderStore(); +const LegendDataCubeSearcher = observer(() => { + const store = useLegendDataCubeBuilderStore(); const state = store.loader; const searchInputRef = useRef(null); - const searchResults = state.queries; + const searchResults = state.searchResults; useEffect(() => { searchInputRef.current?.focus(); }, [state]); // search text - const debouncedLoadQueries = useMemo( + const debouncedLoader = useMemo( () => debounce((input: string) => { state - .searchQueries(input) + .searchDataCubes(input) .catch((error) => store.alertService.alertUnhandledError(error)); }, 500), [store, state], @@ -62,21 +69,21 @@ const LegendDataCubeQuerySearcher = observer(() => { ) => { if (event.target.value !== state.searchText) { state.setSearchText(event.target.value); - debouncedLoadQueries.cancel(); - debouncedLoadQueries(event.target.value); + debouncedLoader.cancel(); + debouncedLoader(event.target.value); } }; const clearSearches = () => { state.setSearchText(''); - debouncedLoadQueries.cancel(); - debouncedLoadQueries(''); + debouncedLoader.cancel(); + debouncedLoader(''); }; // filter and sort - const toggleShowCurrentUserQueriesOnly = () => { - state.setShowCurrentUserQueriesOnly(!state.showCurrentUserQueriesOnly); - debouncedLoadQueries.cancel(); - debouncedLoadQueries(state.searchText); + const toggleShowCurrentUserResultsOnly = () => { + state.setShowCurrentUserResultsOnly(!state.showCurrentUserResultsOnly); + debouncedLoader.cancel(); + debouncedLoader(state.searchText); }; const [ @@ -85,15 +92,15 @@ const LegendDataCubeQuerySearcher = observer(() => { sortDropdownProps, sortDropdownPropsOpen, ] = useDropdownMenu(); - const applySort = (value: DataCubeQuerySortByType) => { + const applySort = (value: DataCubeSortByType) => { state.setSortBy(value); - debouncedLoadQueries.cancel(); - debouncedLoadQueries(state.searchText); + debouncedLoader.cancel(); + debouncedLoader(state.searchText); }; useEffect(() => { state - .searchQueries('') + .searchDataCubes('') .catch((error) => store.alertService.alertUnhandledError(error)); }, [store, state]); @@ -108,7 +115,7 @@ const LegendDataCubeQuerySearcher = observer(() => { })} onChange={onSearchTextChange} value={state.searchText} - placeholder="Search for queries by name or ID" + placeholder="Search for DataCube(s) by name or ID" />
@@ -132,8 +139,8 @@ const LegendDataCubeQuerySearcher = observer(() => {
@@ -148,7 +155,7 @@ const LegendDataCubeQuerySearcher = observer(() => { Sort by: {state.sortBy} - {Object.values(DataCubeQuerySortByType).map((option) => ( + {Object.values(DataCubeSortByType).map((option) => ( { @@ -170,15 +177,15 @@ const LegendDataCubeQuerySearcher = observer(() => { {state.searchState.hasCompleted && ( <>
- {state.showingDefaultQueries ? ( + {state.showingDefaultResults ? ( `Refine your search to get better matches` ) : searchResults.length >= - DATA_CUBE_QUERY_LOADER_TYPEAHEAD_SEARCH_LIMIT ? ( + DATA_CUBE_LOADER_TYPEAHEAD_SEARCH_LIMIT ? ( <> - {`Found ${DATA_CUBE_QUERY_LOADER_TYPEAHEAD_SEARCH_LIMIT}+ matches`}{' '} + {`Found ${DATA_CUBE_LOADER_TYPEAHEAD_SEARCH_LIMIT}+ matches`}{' '} ) : ( @@ -186,25 +193,25 @@ const LegendDataCubeQuerySearcher = observer(() => { )}
{searchResults - .slice(0, DATA_CUBE_QUERY_LOADER_TYPEAHEAD_SEARCH_LIMIT) - .map((query, idx) => ( + .slice(0, DATA_CUBE_LOADER_TYPEAHEAD_SEARCH_LIMIT) + .map((result, idx) => (
state.setSelectedQuery(query)} + key={result.id} + title="Click to choose DataCube" + onClick={() => state.setSelectedResult(result)} >
- {query.name} + {result.name}
- {query.lastUpdatedAt + {result.lastUpdatedAt ? formatDistanceToNow( - new Date(query.lastUpdatedAt), + new Date(result.lastUpdatedAt), { includeSeconds: true, addSuffix: true, @@ -216,7 +223,7 @@ const LegendDataCubeQuerySearcher = observer(() => {
- {query.owner} + {result.owner}
@@ -240,52 +247,108 @@ const LegendDataCubeQuerySearcher = observer(() => { ); }); -export const LegendDataCubeQueryLoader = observer(() => { - const store = useLegendDataCubeQueryBuilderStore(); +export const LegendDataCubeLoader = observer(() => { + const store = useLegendDataCubeBuilderStore(); + const application = useApplicationStore(); const state = store.loader; - const query = state.selectedQuery; + const selectedResult = state.selectedResult; + const [openManageDropdown, closeManageDropdown, manageDropdownProps] = + useDropdownMenu(); return ( <>
- {!query ? ( - + {!selectedResult ? ( + ) : (
-
+
- {query.name} + {selectedResult.name}
+
- {query.lastUpdatedAt - ? formatDistanceToNow(new Date(query.lastUpdatedAt), { - includeSeconds: true, - addSuffix: true, - }) + {selectedResult.lastUpdatedAt + ? formatDistanceToNow( + new Date(selectedResult.lastUpdatedAt), + { + includeSeconds: true, + addSuffix: true, + }, + ) : '(unknown)'}
- {query.owner} + {selectedResult.owner}
- state.setSelectedQuery(undefined)} - > - Select Another Query - +
+ state.setSelectedResult(undefined)} + > + + Go Back + + + {store.canCurrentUserManageDataCube(selectedResult) && ( + <> + +
Manage DataCube
+
+ +
+
+ + { + store.setDataCubeToDelete(selectedResult); + store.deleteConfirmationDisplay.open(); + closeManageDropdown(); + }} + > + Delete... + + + + )} +
)}
@@ -294,7 +357,7 @@ export const LegendDataCubeQueryLoader = observer(() => { state.display.close()}>Cancel { state .finalize() diff --git a/packages/legend-application-data-cube/src/components/builder/LegendDataCubeSaver.tsx b/packages/legend-application-data-cube/src/components/builder/LegendDataCubeSaver.tsx new file mode 100644 index 0000000000..dd09e326da --- /dev/null +++ b/packages/legend-application-data-cube/src/components/builder/LegendDataCubeSaver.tsx @@ -0,0 +1,184 @@ +/** + * 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 { + DataCubeSpecification, + DEFAULT_REPORT_NAME, + FormBadge_Advanced, + FormButton, + FormCheckbox, + FormTextInput, +} from '@finos/legend-data-cube'; +import { observer } from 'mobx-react-lite'; +import { useEffect, useState } from 'react'; +import { useLegendDataCubeBuilderStore } from './LegendDataCubeBuilderStoreProvider.js'; +import { guaranteeNonNullable, returnUndefOnError } from '@finos/legend-shared'; + +export const LegendDataCubeSaver = observer(() => { + const [name, setName] = useState(DEFAULT_REPORT_NAME); + const [syncName, setSyncName] = useState(false); + const [autoEnableCache, setAutoEnableCache] = useState(false); + const [showAdvancedSettings, setShowAdvancedSettings] = useState(false); + const store = useLegendDataCubeBuilderStore(); + const builder = guaranteeNonNullable(store.builder); + + useEffect(() => { + const persistentDataCube = builder.persistentDataCube; + const latestSpecification = persistentDataCube + ? returnUndefOnError(() => + DataCubeSpecification.serialization.fromJson( + persistentDataCube.content, + ), + ) + : undefined; + + setName( + persistentDataCube?.name ?? + builder.initialSpecification.configuration?.name ?? + DEFAULT_REPORT_NAME, + ); + setSyncName(false); + setAutoEnableCache(latestSpecification?.options?.autoEnableCache ?? false); + }, [builder]); + + return ( + <> +
+
+
+
+
+ Name: +
+ { + setName(event.target.value); + }} + autoFocus={true} + /> +
+
+
+ setSyncName(!syncName)} + /> +
+ {showAdvancedSettings && ( + <> +
+
+
+ Caching: +
+ setAutoEnableCache(!autoEnableCache)} + /> + +
+ + )} +
+
+
+
+
+ setShowAdvancedSettings(!showAdvancedSettings)} + /> + +
+
+ store.saverDisplay.close()}> + Cancel + + {builder.persistentDataCube ? ( + // updating existing DataCube + <> + { + store + .saveDataCube(name, { + syncName, + autoEnableCache, + saveAsNew: false, + }) + .catch((error) => + store.alertService.alertUnhandledError(error), + ); + }} + > + Save + + { + store + .saveDataCube(name, { + syncName, + autoEnableCache, + saveAsNew: true, + }) + .catch((error) => + store.alertService.alertUnhandledError(error), + ); + }} + > + Save As + + + ) : ( + // creating new DataCube + <> + { + store + .createNewDataCube(name, { + syncName, + autoEnableCache, + }) + .catch((error) => + store.alertService.alertUnhandledError(error), + ); + }} + > + Save + + + )} +
+
+ + ); +}); diff --git a/packages/legend-application-data-cube/src/components/builder/LegendDataCubeSourceViewer.tsx b/packages/legend-application-data-cube/src/components/builder/LegendDataCubeSourceViewer.tsx new file mode 100644 index 0000000000..a6ea07248f --- /dev/null +++ b/packages/legend-application-data-cube/src/components/builder/LegendDataCubeSourceViewer.tsx @@ -0,0 +1,108 @@ +/** + * 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 { observer } from 'mobx-react-lite'; +import { useLegendDataCubeBuilderStore } from './LegendDataCubeBuilderStoreProvider.js'; +import { LegendQueryDataCubeSource } from '../../stores/model/LegendQueryDataCubeSource.js'; +import { useLegendDataCubeApplicationStore } from '../LegendDataCubeFrameworkProvider.js'; +import { EXTERNAL_APPLICATION_NAVIGATION__generateQueryViewUrl } from '../../__lib__/LegendDataCubeNavigation.js'; +import { DataCubeIcon } from '@finos/legend-art'; + +export const LegendDataCubeSourceViewer = observer(() => { + const store = useLegendDataCubeBuilderStore(); + const source = store.builder?.source; + const application = useLegendDataCubeApplicationStore(); + + if (!source) { + return null; + } + if (source instanceof LegendQueryDataCubeSource) { + const link = application.config.queryApplicationUrl + ? EXTERNAL_APPLICATION_NAVIGATION__generateQueryViewUrl( + application.config.queryApplicationUrl, + source.info.id, + ) + : undefined; + return ( +
+
+
+
+
+ +
+
+ Legend Query +
+
+ {link && ( +
+ + +
+ )} + {!link && ( +
+
+
+ {source.info.id} +
+
+ +
+ )} +
+
+
+ ); + } + return ( +
{`Can't display source`}
+ ); +}); diff --git a/packages/legend-application-data-cube/src/components/query-builder/source-builder/AdhocQueryDataCubeSourceBuilder.tsx b/packages/legend-application-data-cube/src/components/builder/source/AdhocQueryDataCubeSourceBuilder.tsx similarity index 93% rename from packages/legend-application-data-cube/src/components/query-builder/source-builder/AdhocQueryDataCubeSourceBuilder.tsx rename to packages/legend-application-data-cube/src/components/builder/source/AdhocQueryDataCubeSourceBuilder.tsx index 16955836e4..dcbcbcb260 100644 --- a/packages/legend-application-data-cube/src/components/query-builder/source-builder/AdhocQueryDataCubeSourceBuilder.tsx +++ b/packages/legend-application-data-cube/src/components/builder/source/AdhocQueryDataCubeSourceBuilder.tsx @@ -15,7 +15,7 @@ */ import { observer } from 'mobx-react-lite'; -import type { AdhocQueryDataCubeSourceBuilderState } from '../../../stores/query-builder/source-builder/AdhocQueryDataCubeSourceBuilderState.js'; +import type { AdhocQueryDataCubeSourceBuilderState } from '../../../stores/builder/source/AdhocQueryDataCubeSourceBuilderState.js'; import { FormBadge_WIP } from '@finos/legend-data-cube'; export const AdhocQueryDataCubeSourceBuilder = observer( diff --git a/packages/legend-application-data-cube/src/components/query-builder/source-builder/LegendQueryDataCubeSourceBuilder.tsx b/packages/legend-application-data-cube/src/components/builder/source/LegendQueryDataCubeSourceBuilder.tsx similarity index 94% rename from packages/legend-application-data-cube/src/components/query-builder/source-builder/LegendQueryDataCubeSourceBuilder.tsx rename to packages/legend-application-data-cube/src/components/builder/source/LegendQueryDataCubeSourceBuilder.tsx index fec78a7635..d2bf64e9b8 100644 --- a/packages/legend-application-data-cube/src/components/query-builder/source-builder/LegendQueryDataCubeSourceBuilder.tsx +++ b/packages/legend-application-data-cube/src/components/builder/source/LegendQueryDataCubeSourceBuilder.tsx @@ -20,7 +20,7 @@ import { SORT_BY_OPTIONS, type QueryLoaderState, } from '@finos/legend-query-builder'; -import type { LegendQueryDataCubeSourceBuilderState } from '../../../stores/query-builder/source-builder/LegendQueryDataCubeSourceBuilderState.js'; +import type { LegendQueryDataCubeSourceBuilderState } from '../../../stores/builder/source/LegendQueryDataCubeSourceBuilderState.js'; import { generateGAVCoordinates } from '@finos/legend-storage'; import { cn, DataCubeIcon, useDropdownMenu } from '@finos/legend-art'; import { @@ -40,12 +40,12 @@ import { FormTextInput, } from '@finos/legend-data-cube'; import { CODE_EDITOR_LANGUAGE } from '@finos/legend-code-editor'; -import { useLegendDataCubeQueryBuilderStore } from '../LegendDataCubeQueryBuilderStoreProvider.js'; +import { useLegendDataCubeBuilderStore } from '../LegendDataCubeBuilderStoreProvider.js'; import { useApplicationStore } from '@finos/legend-application'; const LegendQuerySearcher = observer((props: { state: QueryLoaderState }) => { const { state } = props; - const store = useLegendDataCubeQueryBuilderStore(); + const store = useLegendDataCubeBuilderStore(); const searchInputRef = useRef(null); const searchResults = state.queries; @@ -54,7 +54,7 @@ const LegendQuerySearcher = observer((props: { state: QueryLoaderState }) => { }, [state]); // search text - const debouncedLoadQueries = useMemo( + const debouncedLoader = useMemo( () => debounce((input: string) => { flowResult(state.searchQueries(input)).catch((error) => @@ -68,14 +68,14 @@ const LegendQuerySearcher = observer((props: { state: QueryLoaderState }) => { ) => { if (event.target.value !== state.searchText) { state.setSearchText(event.target.value); - debouncedLoadQueries.cancel(); - debouncedLoadQueries(event.target.value); + debouncedLoader.cancel(); + debouncedLoader(event.target.value); } }; const clearSearches = () => { state.setSearchText(''); - debouncedLoadQueries.cancel(); - debouncedLoadQueries(''); + debouncedLoader.cancel(); + debouncedLoader(''); }; // filter and sort @@ -83,8 +83,8 @@ const LegendQuerySearcher = observer((props: { state: QueryLoaderState }) => { const toggleShowCurrentUserQueriesOnly = () => { state.setShowCurrentUserQueriesOnly(!state.showCurrentUserQueriesOnly); setIsMineOnly(!isMineOnly); - debouncedLoadQueries.cancel(); - debouncedLoadQueries(state.searchText); + debouncedLoader.cancel(); + debouncedLoader(state.searchText); }; const [ @@ -95,8 +95,8 @@ const LegendQuerySearcher = observer((props: { state: QueryLoaderState }) => { ] = useDropdownMenu(); const applySort = (value: SORT_BY_OPTIONS) => { state.setSortBy(value); - debouncedLoadQueries.cancel(); - debouncedLoadQueries(state.searchText); + debouncedLoader.cancel(); + debouncedLoader(state.searchText); }; useEffect(() => { @@ -254,7 +254,7 @@ export const LegendQueryDataCubeSourceBuilder = observer( (props: { sourceBuilder: LegendQueryDataCubeSourceBuilderState }) => { const { sourceBuilder } = props; const application = useApplicationStore(); - const store = useLegendDataCubeQueryBuilderStore(); + const store = useLegendDataCubeBuilderStore(); const query = sourceBuilder.query; if (!query) { @@ -320,10 +320,11 @@ export const LegendQueryDataCubeSourceBuilder = observer(
)} sourceBuilder.unsetQuery()} > - Select Another Query + + Go Back
); diff --git a/packages/legend-application-data-cube/src/components/query-builder/LegendDataCubeQueryBuilder.tsx b/packages/legend-application-data-cube/src/components/query-builder/LegendDataCubeQueryBuilder.tsx deleted file mode 100644 index ac9494cb33..0000000000 --- a/packages/legend-application-data-cube/src/components/query-builder/LegendDataCubeQueryBuilder.tsx +++ /dev/null @@ -1,164 +0,0 @@ -/** - * 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 { observer } from 'mobx-react-lite'; -import { - DataCube, - FormButton, - type DataCubeSettingValues, - DataCubePlaceholder, -} from '@finos/legend-data-cube'; -import {} from '@finos/legend-art'; -import { - useLegendDataCubeQueryBuilderStore, - withLegendDataCubeQueryBuilderStore, -} from './LegendDataCubeQueryBuilderStoreProvider.js'; -import { useParams } from '@finos/legend-application/browser'; -import { - LEGEND_DATA_CUBE_ROUTE_PATTERN_TOKEN, - type LegendDataCubeQueryBuilderPathParams, -} from '../../__lib__/LegendDataCubeNavigation.js'; -import { useEffect } from 'react'; -import { LegendDataCubeSettingStorageKey } from '../../__lib__/LegendDataCubeSetting.js'; - -const LegendDataCubeQueryBuilderHeader = observer(() => { - const store = useLegendDataCubeQueryBuilderStore(); - - return ( -
- store.loader.display.open()}> - Load Query - - store.newQueryState.display.open()} - > - New Query - - store.saverDisplay.open()} - > - Save Query - -
- ); -}); - -export const LegendDataCubeQueryBuilder = withLegendDataCubeQueryBuilderStore( - observer(() => { - const store = useLegendDataCubeQueryBuilderStore(); - const builder = store.builder; - const application = store.application; - const params = useParams(); - const queryId = params[LEGEND_DATA_CUBE_ROUTE_PATTERN_TOKEN.QUERY_ID]; - - useEffect(() => { - application.navigationService.navigator.blockNavigation( - // Only block navigation in production, in development, we should have - // the flexibility to reload the page quickly - // eslint-disable-next-line no-process-env - [() => process.env.NODE_ENV === 'production'], - ); - return (): void => { - application.navigationService.navigator.unblockNavigation(); - }; - }, [application]); - - useEffect(() => { - store - .loadQuery(queryId) - .catch((error) => store.alertService.alertUnhandledError(error)); - }, [store, queryId]); - - useEffect(() => { - store.engine - .initializeCacheManager() - .catch((error) => store.alertService.alertUnhandledError(error)); - return () => { - store.engine - .disposeCacheManager() - .catch((error) => store.alertService.alertUnhandledError(error)); - }; - }, [store]); - - if (!builder) { - return ( - } - menuItems={[ - { - label: 'See Documentation', - action: () => { - const url = application.documentationService.url; - if (url) { - application.navigationService.navigator.visitAddress(url); - } - }, - disabled: true, // TODO: enable when we set up the documentation websit - }, - ]} - > -
-
Create a new query to start
- store.newQueryState.display.open()} - > - New Query - -
-
- ); - } - return ( - , - settingsData: { - configurations: store.baseStore.settings, - values: application.settingService.getObjectValue( - LegendDataCubeSettingStorageKey.DATA_CUBE, - ) as DataCubeSettingValues | undefined, - }, - onSettingsChanged(event) { - application.settingService.persistValue( - LegendDataCubeSettingStorageKey.DATA_CUBE, - event.values, - ); - }, - documentationUrl: application.documentationService.url, - enableCache: true, - }} - /> - ); - }), -); diff --git a/packages/legend-application-data-cube/src/components/query-builder/LegendDataCubeQuerySaver.tsx b/packages/legend-application-data-cube/src/components/query-builder/LegendDataCubeQuerySaver.tsx deleted file mode 100644 index 72d64fc582..0000000000 --- a/packages/legend-application-data-cube/src/components/query-builder/LegendDataCubeQuerySaver.tsx +++ /dev/null @@ -1,116 +0,0 @@ -/** - * 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 { - DEFAULT_REPORT_NAME, - FormButton, - FormTextInput, -} from '@finos/legend-data-cube'; -import { observer } from 'mobx-react-lite'; -import { useEffect, useState } from 'react'; -import { useLegendDataCubeQueryBuilderStore } from './LegendDataCubeQueryBuilderStoreProvider.js'; -import { guaranteeNonNullable } from '@finos/legend-shared'; - -export const LegendDataCubeQuerySaver = observer(() => { - const [name, setName] = useState(DEFAULT_REPORT_NAME); - const store = useLegendDataCubeQueryBuilderStore(); - const builder = guaranteeNonNullable(store.builder); - - useEffect(() => { - setName( - builder.persistentQuery?.name ?? - builder.query.configuration?.name ?? - DEFAULT_REPORT_NAME, - ); - }, [builder]); - - return ( - <> -
-
-
-
-
- Name: -
- { - setName(event.target.value); - }} - autoFocus={true} - /> -
-
-
-
-
- store.saverDisplay.close()}> - Cancel - - {builder.persistentQuery ? ( - // updating existing query - <> - { - store - .saveQuery(name, false) - .catch((error) => - store.alertService.alertUnhandledError(error), - ); - }} - > - Save - - { - store - .saveQuery(name, true) - .catch((error) => - store.alertService.alertUnhandledError(error), - ); - }} - > - Save As - - - ) : ( - // creating new query - <> - { - store - .createQuery(name) - .catch((error) => - store.alertService.alertUnhandledError(error), - ); - }} - > - Save - - - )} -
- - ); -}); diff --git a/packages/legend-application-data-cube/src/stores/LegendDataCubeDataCubeEngine.ts b/packages/legend-application-data-cube/src/stores/LegendDataCubeDataCubeEngine.ts index f090299b88..b21b544367 100644 --- a/packages/legend-application-data-cube/src/stores/LegendDataCubeDataCubeEngine.ts +++ b/packages/legend-application-data-cube/src/stores/LegendDataCubeDataCubeEngine.ts @@ -149,9 +149,7 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine { // ---------------------------------- IMPLEMENTATION ---------------------------------- - override async processQuerySource( - value: PlainObject, - ): Promise { + override async processSource(value: PlainObject): Promise { switch (value._type) { case ADHOC_QUERY_DATA_CUBE_SOURCE_TYPE: { const rawSource = diff --git a/packages/legend-application-data-cube/src/stores/builder/LegendDataCubeBuilderStore.tsx b/packages/legend-application-data-cube/src/stores/builder/LegendDataCubeBuilderStore.tsx new file mode 100644 index 0000000000..54ef7cd287 --- /dev/null +++ b/packages/legend-application-data-cube/src/stores/builder/LegendDataCubeBuilderStore.tsx @@ -0,0 +1,516 @@ +/** + * 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 { action, makeObservable, observable } from 'mobx'; +import type { + LegendDataCubeApplicationStore, + LegendDataCubeBaseStore, +} from '../LegendDataCubeBaseStore.js'; +import { + type DataCubeAlertService, + type DataCubeAPI, + type DataCubeLayoutService, + type DataCubeTaskService, + DataCubeSpecification, + DEFAULT_ALERT_WINDOW_CONFIG, + type DisplayState, + DataCubeSpecificationOptions, + type DataCubeSource, +} from '@finos/legend-data-cube'; +import { LegendDataCubeCreatorState } from './LegendDataCubeCreatorState.js'; +import { + PersistentDataCube, + type LightPersistentDataCube, + type V1_EngineServerClient, + type V1_PureGraphManager, +} from '@finos/legend-graph'; +import { + ActionState, + assertErrorThrown, + formatDate, + isString, + uuid, +} from '@finos/legend-shared'; +import type { LegendDataCubeDataCubeEngine } from '../LegendDataCubeDataCubeEngine.js'; +import { LegendDataCubeSaver } from '../../components/builder/LegendDataCubeSaver.js'; +import { + generateBuilderRoute, + LEGEND_DATA_CUBE_ROUTE_PATTERN_TOKEN, +} from '../../__lib__/LegendDataCubeNavigation.js'; +import { LegendDataCubeLoaderState } from './LegendDataCubeLoaderState.js'; +import { + LegendDataCubeUserDataKey, + RECENTLY_VIEWED_DATA_CUBES_LIMIT, +} from '../../__lib__/LegendDataCubeUserData.js'; +import type { DepotServerClient } from '@finos/legend-server-depot'; +import { LegendDataCubeBlockingWindowState } from '../../components/LegendDataCubeBlockingWindow.js'; +import { LegendDataCubeDeleteConfirmation } from '../../components/builder/LegendDataCubeDeleteConfirmation.js'; +import { LegendDataCubeAbout } from '../../components/builder/LegendDataCubeBuilder.js'; +import { LegendDataCubeSourceViewer } from '../../components/builder/LegendDataCubeSourceViewer.js'; + +export class LegendDataCubeBuilderState { + readonly uuid = uuid(); + readonly startTime = Date.now(); + + readonly initialSpecification!: DataCubeSpecification; + persistentDataCube?: PersistentDataCube | undefined; + + dataCube?: DataCubeAPI | undefined; + source?: DataCubeSource | undefined; + + constructor( + specification: DataCubeSpecification, + persistentDataCube?: PersistentDataCube | undefined, + ) { + makeObservable(this, { + persistentDataCube: observable, + setPersistentDataCube: action, + + dataCube: observable, + setDataCube: action, + + source: observable, + setSource: action, + }); + + this.initialSpecification = specification; + this.persistentDataCube = persistentDataCube; + } + + setPersistentDataCube(val: PersistentDataCube | undefined) { + this.persistentDataCube = val; + } + + setDataCube(val: DataCubeAPI | undefined) { + this.dataCube = val; + } + + setSource(val: DataCubeSource | undefined) { + this.source = val; + } +} + +export type LegendDataCubeSaveOptions = { + syncName?: boolean | undefined; + autoEnableCache?: boolean | undefined; +}; + +export class LegendDataCubeBuilderStore { + readonly application: LegendDataCubeApplicationStore; + readonly baseStore: LegendDataCubeBaseStore; + readonly engine: LegendDataCubeDataCubeEngine; + readonly depotServerClient: DepotServerClient; + readonly engineServerClient: V1_EngineServerClient; + readonly graphManager: V1_PureGraphManager; + readonly taskService: DataCubeTaskService; + readonly layoutService: DataCubeLayoutService; + readonly alertService: DataCubeAlertService; + + readonly initializeState = ActionState.create(); + readonly aboutDisplay: DisplayState; + + readonly creator: LegendDataCubeCreatorState; + + readonly saveState = ActionState.create(); + readonly saverDisplay: LegendDataCubeBlockingWindowState; + + readonly deleteState = ActionState.create(); + dataCubeToDelete?: LightPersistentDataCube | PersistentDataCube | undefined; + readonly deleteConfirmationDisplay: LegendDataCubeBlockingWindowState; + + readonly loadState = ActionState.create(); + readonly loader: LegendDataCubeLoaderState; + builder?: LegendDataCubeBuilderState | undefined; + readonly sourceViewerDisplay: DisplayState; + + private passedFirstLoad = false; + + constructor(baseStore: LegendDataCubeBaseStore) { + makeObservable(this, { + builder: observable, + setBuilder: action, + + dataCubeToDelete: observable, + setDataCubeToDelete: action, + }); + + this.application = baseStore.application; + this.baseStore = baseStore; + this.engine = baseStore.engine; + this.depotServerClient = baseStore.depotServerClient; + this.engineServerClient = baseStore.engineServerClient; + this.graphManager = baseStore.graphManager; + this.taskService = baseStore.taskService; + this.alertService = baseStore.alertService; + this.layoutService = baseStore.layoutService; + + this.aboutDisplay = this.layoutService.newDisplay( + 'About', + () => , + { + ...DEFAULT_ALERT_WINDOW_CONFIG, + height: 220, + x: -50, + y: 50, + center: false, + }, + ); + + this.creator = new LegendDataCubeCreatorState(this); + this.loader = new LegendDataCubeLoaderState(this); + this.saverDisplay = new LegendDataCubeBlockingWindowState( + 'Save DataCube', + () => , + { + ...DEFAULT_ALERT_WINDOW_CONFIG, + height: 200, + }, + ); + this.deleteConfirmationDisplay = new LegendDataCubeBlockingWindowState( + 'Delete DataCube', + () => , + { + ...DEFAULT_ALERT_WINDOW_CONFIG, + height: 180, + }, + ); + this.sourceViewerDisplay = this.layoutService.newDisplay( + 'DataCube Source', + () => , + { + ...DEFAULT_ALERT_WINDOW_CONFIG, + height: 200, + }, + ); + } + + setBuilder(val: LegendDataCubeBuilderState | undefined) { + this.builder = val; + } + + private updateWindowTitle(persistentDataCube: PersistentDataCube) { + this.application.layoutService.setWindowTitle( + `\u229E ${persistentDataCube.name}${this.builder ? ` - ${formatDate(new Date(this.builder.startTime), 'HH:mm:ss EEE MMM dd yyyy')}` : ''}`, + ); + } + + getRecentlyViewedDataCubes() { + const data = this.application.userDataService.getObjectValue( + LegendDataCubeUserDataKey.RECENTLY_VIEWED_DATA_CUBES, + ); + return data && Array.isArray(data) && data.every((id) => isString(id)) + ? data + : []; + } + + canCurrentUserManageDataCube( + persistentDataCube: PersistentDataCube | LightPersistentDataCube, + ) { + return ( + persistentDataCube.owner === this.application.identityService.currentUser + ); + } + + async initialize() { + if (this.initializeState.isInProgress) { + return; + } + + this.initializeState.inProgress(); + try { + await this.engine.initializeCacheManager(); + this.initializeState.pass(); + } catch (error) { + assertErrorThrown(error); + this.alertService.alertError(error, { + message: `Infrastructure Failure: ${error.message}`, + }); + this.initializeState.fail(); + } + } + + async cleanUp() { + try { + await this.engine.disposeCacheManager(); + } catch (error) { + assertErrorThrown(error); + this.alertService.alertError(error, { + message: `Infrastructure Failure: ${error.message}`, + }); + } + } + + async loadDataCube(dataCubeId: string | undefined) { + // internalize the parameters and clean them from the URL + const sourceData = + this.application.navigationService.navigator.getCurrentLocationParameterValue( + LEGEND_DATA_CUBE_ROUTE_PATTERN_TOKEN.SOURCE_DATA, + ); + if (sourceData && !dataCubeId) { + this.application.navigationService.navigator.updateCurrentLocation( + generateBuilderRoute(null), + ); + // populate the creator if source data is specified + try { + await this.creator.finalize(JSON.parse(atob(sourceData))); + } catch (error) { + assertErrorThrown(error); + this.alertService.alertError(error, { + message: `DataCube Creation Failure: Can't materialize source from source data. Error: ${error.message}`, + }); + this.setBuilder(undefined); + } + } + + // When user just starts the application with no DataCube ID, and source data + if (!dataCubeId && !sourceData && !this.builder && !this.passedFirstLoad) { + this.loader.display.open(); + } + this.passedFirstLoad = true; + + if (dataCubeId !== this.builder?.persistentDataCube?.id) { + if (!dataCubeId) { + this.setBuilder(undefined); + return; + } + + this.loadState.inProgress(); + + try { + const persistentDataCube = + await this.baseStore.graphManager.getDataCube(dataCubeId); + const specification = DataCubeSpecification.serialization.fromJson( + persistentDataCube.content, + ); + this.setBuilder( + new LegendDataCubeBuilderState(specification, persistentDataCube), + ); + this.updateWindowTitle(persistentDataCube); + + // update the list of stack of recently viewed DataCubes + const recentlyViewedDataCubes = this.getRecentlyViewedDataCubes(); + const idx = recentlyViewedDataCubes.findIndex( + (data) => data === dataCubeId, + ); + if (idx === -1) { + if ( + recentlyViewedDataCubes.length >= RECENTLY_VIEWED_DATA_CUBES_LIMIT + ) { + recentlyViewedDataCubes.pop(); + } + recentlyViewedDataCubes.unshift(dataCubeId); + } else { + recentlyViewedDataCubes.splice(idx, 1); + recentlyViewedDataCubes.unshift(dataCubeId); + } + this.application.userDataService.persistValue( + LegendDataCubeUserDataKey.RECENTLY_VIEWED_DATA_CUBES, + recentlyViewedDataCubes, + ); + + this.loadState.pass(); + } catch (error) { + assertErrorThrown(error); + this.alertService.alertError(error, { + message: `DataCube Load Failure: ${error.message}`, + }); + this.application.navigationService.navigator.updateCurrentLocation( + generateBuilderRoute(null), + ); + + this.loadState.fail(); + } + } + } + + private async generatePersistentDataCube( + api: DataCubeAPI, + name: string, + existingPersistentDataCube?: PersistentDataCube | undefined, + options?: LegendDataCubeSaveOptions, + ) { + const specification = await api.generateSpecification(); + let persistentDataCube: PersistentDataCube; + if (existingPersistentDataCube) { + persistentDataCube = existingPersistentDataCube.clone(); + } else { + persistentDataCube = new PersistentDataCube(); + persistentDataCube.id = uuid(); + } + + if (options !== undefined) { + specification.options = + specification.options ?? new DataCubeSpecificationOptions(); + specification.options.autoEnableCache = options.autoEnableCache; + + if (options.syncName && specification.configuration) { + specification.configuration.name = name; + } + } + + persistentDataCube.name = name; + persistentDataCube.content = + DataCubeSpecification.serialization.toJson(specification); + return persistentDataCube; + } + + async createNewDataCube(name: string, options?: LegendDataCubeSaveOptions) { + if (!this.builder?.dataCube || this.saveState.isInProgress) { + return; + } + + this.saveState.inProgress(); + try { + const persistentDataCube = await this.generatePersistentDataCube( + this.builder.dataCube, + name, + undefined, + options, + ); + + const newPersistentDataCube = + await this.baseStore.graphManager.createDataCube(persistentDataCube); + // NOTE: reload is the cleanest, least bug-prone handling here + // but we can opt for just updating the URL to reflect the new DataCube + // as an optimization. Also, it helps preserve the edition history + // on the existing data-cube. + // + // Another way to avoid reloading the whole app it to force update + // the component using the key prop that ties to an ID + // of the builder. + this.application.navigationService.navigator.updateCurrentLocation( + generateBuilderRoute(newPersistentDataCube.id), + ); + this.updateWindowTitle(persistentDataCube); + if (options?.syncName) { + this.builder.dataCube.updateName(name); + } + + this.saverDisplay.close(); + this.saveState.pass(); + } catch (error) { + assertErrorThrown(error); + this.alertService.alertError(error, { + message: `DataCube Creation Failure: ${error.message}`, + }); + this.saveState.fail(); + } + } + + async saveDataCube( + name: string, + options?: LegendDataCubeSaveOptions & { saveAsNew?: boolean | undefined }, + ) { + if (!this.builder?.dataCube || this.saveState.isInProgress) { + return; + } + + this.saveState.inProgress(); + try { + const persistentDataCube = await this.generatePersistentDataCube( + this.builder.dataCube, + name, + this.builder.persistentDataCube, + options, + ); + + if (options?.saveAsNew) { + persistentDataCube.id = uuid(); + const newPersistentDataCube = + await this.baseStore.graphManager.createDataCube(persistentDataCube); + // NOTE: reload is the cleanest, least bug-prone handling here + // but we can opt for just updating the URL to reflect the new DataCube + // as an optimization. Also, it helps preserve the edition history + // on the existing data-cube. + // + // Another way to avoid reloading the whole app it to force update + // the component using the key prop that ties to an ID + // of the builder. + this.application.navigationService.navigator.updateCurrentLocation( + generateBuilderRoute(newPersistentDataCube.id), + ); + } else { + const updatedPersistentDataCube = + await this.baseStore.graphManager.updateDataCube(persistentDataCube); + this.builder.setPersistentDataCube(updatedPersistentDataCube); + } + this.updateWindowTitle(persistentDataCube); + if (options?.syncName) { + this.builder.dataCube.updateName(name); + } + + this.saverDisplay.close(); + this.saveState.pass(); + } catch (error) { + assertErrorThrown(error); + this.alertService.alertError(error, { + message: `DataCube Update Failure: ${error.message}`, + }); + this.saveState.fail(); + } + } + + setDataCubeToDelete( + val: LightPersistentDataCube | PersistentDataCube | undefined, + ) { + this.dataCubeToDelete = val; + } + + async deleteDataCube() { + if (this.deleteState.isInProgress || !this.dataCubeToDelete) { + return; + } + const dataCubeId = this.dataCubeToDelete.id; + + this.deleteState.inProgress(); + try { + await this.baseStore.graphManager.deleteDataCube(dataCubeId); + + // update the list of stack of recently viewed DataCubes + const recentlyViewedDataCubes = this.getRecentlyViewedDataCubes(); + const idx = recentlyViewedDataCubes.findIndex( + (data) => data === dataCubeId, + ); + if (idx !== -1) { + recentlyViewedDataCubes.splice(idx, 1); + this.application.userDataService.persistValue( + LegendDataCubeUserDataKey.RECENTLY_VIEWED_DATA_CUBES, + recentlyViewedDataCubes, + ); + } + + if (this.builder?.persistentDataCube?.id === dataCubeId) { + this.application.navigationService.navigator.updateCurrentLocation( + generateBuilderRoute(null), + ); + } + + if (this.loader.selectedResult?.id === dataCubeId) { + this.loader.setSelectedResult(undefined); + } + + this.setDataCubeToDelete(undefined); + this.deleteConfirmationDisplay.close(); + this.deleteState.pass(); + } catch (error) { + assertErrorThrown(error); + this.alertService.alertError(error, { + message: `DataCube Delete Failure: ${error.message}`, + }); + this.deleteState.fail(); + } + } +} diff --git a/packages/legend-application-data-cube/src/stores/query-builder/LegendDataCubeNewQueryState.tsx b/packages/legend-application-data-cube/src/stores/builder/LegendDataCubeCreatorState.tsx similarity index 74% rename from packages/legend-application-data-cube/src/stores/query-builder/LegendDataCubeNewQueryState.tsx rename to packages/legend-application-data-cube/src/stores/builder/LegendDataCubeCreatorState.tsx index 4bc537634f..3ff0f18fc8 100644 --- a/packages/legend-application-data-cube/src/stores/query-builder/LegendDataCubeNewQueryState.tsx +++ b/packages/legend-application-data-cube/src/stores/builder/LegendDataCubeCreatorState.tsx @@ -22,31 +22,31 @@ import { UnsupportedOperationError, type PlainObject, } from '@finos/legend-shared'; -import { LegendQueryDataCubeSourceBuilderState } from './source-builder/LegendQueryDataCubeSourceBuilderState.js'; +import { LegendQueryDataCubeSourceBuilderState } from './source/LegendQueryDataCubeSourceBuilderState.js'; import type { LegendDataCubeApplicationStore } from '../LegendDataCubeBaseStore.js'; import { LegendDataCubeSourceBuilderType, type LegendDataCubeSourceBuilderState, -} from './source-builder/LegendDataCubeSourceBuilderState.js'; +} from './source/LegendDataCubeSourceBuilderState.js'; import { type DataCubeAlertService, DEFAULT_TOOL_PANEL_WINDOW_CONFIG, type DisplayState, } from '@finos/legend-data-cube'; import type { LegendDataCubeDataCubeEngine } from '../LegendDataCubeDataCubeEngine.js'; -import { LegendDataCubeNewQueryBuilder } from '../../components/query-builder/LegendDataCubeNewQueryBuilder.js'; -import { AdhocQueryDataCubeSourceBuilderState } from './source-builder/AdhocQueryDataCubeSourceBuilderState.js'; +import { LegendDataCubeCreator } from '../../components/builder/LegendDataCubeCreator.js'; +import { AdhocQueryDataCubeSourceBuilderState } from './source/AdhocQueryDataCubeSourceBuilderState.js'; import { - LegendDataCubeQueryBuilderState, - type LegendDataCubeQueryBuilderStore, -} from './LegendDataCubeQueryBuilderStore.js'; -import { generateQueryBuilderRoute } from '../../__lib__/LegendDataCubeNavigation.js'; + LegendDataCubeBuilderState, + type LegendDataCubeBuilderStore, +} from './LegendDataCubeBuilderStore.js'; +import { generateBuilderRoute } from '../../__lib__/LegendDataCubeNavigation.js'; const DEFAULT_SOURCE_TYPE = LegendDataCubeSourceBuilderType.LEGEND_QUERY; -export class LegendDataCubeNewQueryState { +export class LegendDataCubeCreatorState { private readonly _application: LegendDataCubeApplicationStore; - private readonly _store: LegendDataCubeQueryBuilderStore; + private readonly _store: LegendDataCubeBuilderStore; private readonly _engine: LegendDataCubeDataCubeEngine; private readonly _alertService: DataCubeAlertService; @@ -55,7 +55,7 @@ export class LegendDataCubeNewQueryState { sourceBuilder: LegendDataCubeSourceBuilderState; - constructor(store: LegendDataCubeQueryBuilderStore) { + constructor(store: LegendDataCubeBuilderStore) { makeObservable(this, { sourceBuilder: observable, changeSourceBuilder: action, @@ -67,8 +67,8 @@ export class LegendDataCubeNewQueryState { this._alertService = store.alertService; this.display = store.layoutService.newDisplay( - 'New Query', - () => , + 'New DataCube', + () => , { ...DEFAULT_TOOL_PANEL_WINDOW_CONFIG, width: 500, @@ -114,24 +114,26 @@ export class LegendDataCubeNewQueryState { async finalize(sourceData?: PlainObject) { if (!sourceData && !this.sourceBuilder.isValid) { - throw new IllegalStateError(`Can't generate query: source is not valid`); + throw new IllegalStateError( + `Can't generate DataCube: source is not valid`, + ); } this.finalizeState.inProgress(); try { - const query = await this._engine.generateBaseQuery( + const specification = await this._engine.generateBaseSpecification( sourceData ?? (await this.sourceBuilder.generateSourceData()), ); - if (query.configuration) { - this.sourceBuilder.finalizeConfiguration(query.configuration); + if (specification.configuration) { + this.sourceBuilder.finalizeConfiguration(specification.configuration); } // reset - this._store.setBuilder(new LegendDataCubeQueryBuilderState(query)); + this._store.setBuilder(new LegendDataCubeBuilderState(specification)); // only update the route instead of reloading in case we are creating - // a new query when we are editing another query + // a new DataCube when we are editing another DataCube this._application.navigationService.navigator.updateCurrentLocation( - generateQueryBuilderRoute(null), + generateBuilderRoute(null), ); this.changeSourceBuilder(DEFAULT_SOURCE_TYPE, true); this.display.close(); @@ -139,7 +141,7 @@ export class LegendDataCubeNewQueryState { } catch (error) { assertErrorThrown(error); this._alertService.alertError(error, { - message: `Query Creation Failure: ${error.message}`, + message: `DataCube Creation Failure: ${error.message}`, }); this.finalizeState.fail(); } diff --git a/packages/legend-application-data-cube/src/stores/builder/LegendDataCubeLoaderState.tsx b/packages/legend-application-data-cube/src/stores/builder/LegendDataCubeLoaderState.tsx new file mode 100644 index 0000000000..06066e1be8 --- /dev/null +++ b/packages/legend-application-data-cube/src/stores/builder/LegendDataCubeLoaderState.tsx @@ -0,0 +1,248 @@ +/** + * 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 { + APPLICATION_EVENT, + DEFAULT_TYPEAHEAD_SEARCH_MINIMUM_SEARCH_LENGTH, + type GenericLegendApplicationStore, +} from '@finos/legend-application'; +import { + type LightPersistentDataCube, + QuerySearchSpecification, + QuerySearchSortBy, + type V1_PureGraphManager, +} from '@finos/legend-graph'; +import { ActionState, assertErrorThrown, LogEvent } from '@finos/legend-shared'; +import { makeObservable, observable, action } from 'mobx'; +import type { LegendDataCubeBuilderStore } from './LegendDataCubeBuilderStore.js'; +import { LegendDataCubeUserDataKey } from '../../__lib__/LegendDataCubeUserData.js'; +import { + type DataCubeAlertService, + DEFAULT_TOOL_PANEL_WINDOW_CONFIG, + type DisplayState, +} from '@finos/legend-data-cube'; +import { LegendDataCubeLoader } from '../../components/builder/LegendDataCubeLoader.js'; +import { generateBuilderRoute } from '../../__lib__/LegendDataCubeNavigation.js'; + +export const DATA_CUBE_LOADER_TYPEAHEAD_SEARCH_LIMIT = 50; +export const DATA_CUBE_LOADER_DEFAULT_SEARCH_LIMIT = 10; + +export enum DataCubeSortByType { + LAST_CREATED = 'Last Created', + LAST_VIEWED = 'Last Viewed', + LAST_UPDATED = 'Last Updated', +} + +export class LegendDataCubeLoaderState { + private readonly _application: GenericLegendApplicationStore; + private readonly _store: LegendDataCubeBuilderStore; + private readonly _graphManager: V1_PureGraphManager; + private readonly _alertService: DataCubeAlertService; + + readonly display: DisplayState; + readonly searchState = ActionState.create(); + readonly finalizeState = ActionState.create(); + + searchResults: LightPersistentDataCube[] = []; + selectedResult?: LightPersistentDataCube | undefined; + + searchText = ''; + showCurrentUserResultsOnly = false; + showingDefaultResults = true; + sortBy = DataCubeSortByType.LAST_VIEWED; + + constructor(store: LegendDataCubeBuilderStore) { + makeObservable(this, { + showingDefaultResults: observable, + setShowingDefaultResults: action, + + searchText: observable, + setSearchText: action, + + searchResults: observable, + setSearchResults: action, + + selectedResult: observable, + setSelectedResult: action, + + showCurrentUserResultsOnly: observable, + setShowCurrentUserResultsOnly: action, + + sortBy: observable, + setSortBy: action, + }); + + this._application = store.application; + this._store = store; + this._graphManager = store.graphManager; + this._alertService = store.alertService; + + this.display = store.layoutService.newDisplay( + 'Load DataCube', + () => , + { + ...DEFAULT_TOOL_PANEL_WINDOW_CONFIG, + width: 500, + minWidth: 500, + }, + ); + } + + setSearchText(val: string) { + this.searchText = val; + } + + setSearchResults(results: LightPersistentDataCube[]) { + this.searchResults = results; + } + + setSelectedResult(result: LightPersistentDataCube | undefined) { + this.selectedResult = result; + } + + setShowingDefaultResults(val: boolean) { + this.showingDefaultResults = val; + } + + setShowCurrentUserResultsOnly(val: boolean): void { + this.showCurrentUserResultsOnly = val; + } + + setSortBy(val: DataCubeSortByType) { + this.sortBy = val; + } + + private getSearchSortBy(sortByValue: string) { + switch (sortByValue) { + case DataCubeSortByType.LAST_CREATED: + return QuerySearchSortBy.SORT_BY_CREATE; + case DataCubeSortByType.LAST_UPDATED: + return QuerySearchSortBy.SORT_BY_UPDATE; + case DataCubeSortByType.LAST_VIEWED: + return QuerySearchSortBy.SORT_BY_VIEW; + default: + return undefined; + } + } + + canPerformAdvancedSearch(searchText: string) { + return !( + searchText.length < DEFAULT_TYPEAHEAD_SEARCH_MINIMUM_SEARCH_LENGTH && + !this.showCurrentUserResultsOnly + ); + } + + async searchDataCubes(searchText: string) { + // for the initial search, i.e. no search config is specified, fetch the default entries if possible + if (!this.canPerformAdvancedSearch(searchText)) { + if (!searchText) { + this.setShowingDefaultResults(true); + this.searchState.inProgress(); + this.setSearchResults([]); + let defaultResults: LightPersistentDataCube[] = []; + try { + // first, try to fetch recently viewed entries + try { + const recentlyViewedDataCubeIDs = + this._store.getRecentlyViewedDataCubes(); + if (recentlyViewedDataCubeIDs.length) { + defaultResults = await this._graphManager.getDataCubes( + recentlyViewedDataCubeIDs, + ); + } + } catch (error) { + assertErrorThrown(error); + // if there's an error fetching recently viewed entries, most likely because + // some entries have been removed, just remove them all from the cached user data + this._application.userDataService.persistValue( + LegendDataCubeUserDataKey.RECENTLY_VIEWED_DATA_CUBES, + undefined, + ); + } + // if there's no recently viewed entries, just fetch entries of the current user + if (!defaultResults.length) { + const searchSpecification = new QuerySearchSpecification(); + searchSpecification.limit = DATA_CUBE_LOADER_DEFAULT_SEARCH_LIMIT; + searchSpecification.showCurrentUserQueriesOnly = true; + defaultResults = + await this._graphManager.searchDataCubes(searchSpecification); + } + this.setSearchResults(defaultResults); + this.searchState.pass(); + } catch (error) { + assertErrorThrown(error); + this._application.logService.error( + LogEvent.create(APPLICATION_EVENT.GENERIC_FAILURE), + error, + ); + this.searchState.fail(); + } + } + return; + } + + this.setShowingDefaultResults(false); + this.searchState.inProgress(); + + try { + const searchSpecification = + QuerySearchSpecification.createDefault(searchText); + searchSpecification.limit = DATA_CUBE_LOADER_TYPEAHEAD_SEARCH_LIMIT + 1; + searchSpecification.showCurrentUserQueriesOnly = + this.showCurrentUserResultsOnly; + const searchSortBy = this.getSearchSortBy(this.sortBy); + if (searchSortBy) { + searchSpecification.sortByOption = searchSortBy; + } + this.setSearchResults( + await this._graphManager.searchDataCubes(searchSpecification), + ); + + // if sorting is not configured, sort by name + if (!searchSortBy) { + this.setSearchResults( + this.searchResults.toSorted((a, b) => a.name.localeCompare(b.name)), + ); + } + + this.searchState.pass(); + } catch (error) { + assertErrorThrown(error); + this._alertService.alertError(error, { + message: `DataCube Search Failure: ${error.message}`, + }); + this.searchState.fail(); + } + } + + async finalize() { + if (!this.selectedResult) { + return; + } + + this.finalizeState.inProgress(); + // just simply change the route here and the new DataCube ID will get picked up + // and handled by the builder to load the new DataCube. + this._application.navigationService.navigator.updateCurrentLocation( + generateBuilderRoute(this.selectedResult.id), + ); + + // reset + this.setSelectedResult(undefined); + this.display.close(); + this.finalizeState.pass(); + } +} diff --git a/packages/legend-application-data-cube/src/stores/query-builder/source-builder/AdhocQueryDataCubeSourceBuilderState.ts b/packages/legend-application-data-cube/src/stores/builder/source/AdhocQueryDataCubeSourceBuilderState.ts similarity index 100% rename from packages/legend-application-data-cube/src/stores/query-builder/source-builder/AdhocQueryDataCubeSourceBuilderState.ts rename to packages/legend-application-data-cube/src/stores/builder/source/AdhocQueryDataCubeSourceBuilderState.ts diff --git a/packages/legend-application-data-cube/src/stores/query-builder/source-builder/LegendDataCubeSourceBuilderState.ts b/packages/legend-application-data-cube/src/stores/builder/source/LegendDataCubeSourceBuilderState.ts similarity index 94% rename from packages/legend-application-data-cube/src/stores/query-builder/source-builder/LegendDataCubeSourceBuilderState.ts rename to packages/legend-application-data-cube/src/stores/builder/source/LegendDataCubeSourceBuilderState.ts index f1f65e17ea..0d0800312b 100644 --- a/packages/legend-application-data-cube/src/stores/query-builder/source-builder/LegendDataCubeSourceBuilderState.ts +++ b/packages/legend-application-data-cube/src/stores/builder/source/LegendDataCubeSourceBuilderState.ts @@ -40,7 +40,7 @@ export abstract class LegendDataCubeSourceBuilderState { abstract get isValid(): boolean; abstract generateSourceData(): Promise; - /* Modifies the configuration of the finalized DataCube query based on the source builder */ + /* Modifies the configuration of the finalized DataCube based on the source builder */ finalizeConfiguration(configuration: DataCubeConfiguration) { // do nothing } diff --git a/packages/legend-application-data-cube/src/stores/query-builder/source-builder/LegendQueryDataCubeSourceBuilderState.ts b/packages/legend-application-data-cube/src/stores/builder/source/LegendQueryDataCubeSourceBuilderState.ts similarity index 100% rename from packages/legend-application-data-cube/src/stores/query-builder/source-builder/LegendQueryDataCubeSourceBuilderState.ts rename to packages/legend-application-data-cube/src/stores/builder/source/LegendQueryDataCubeSourceBuilderState.ts diff --git a/packages/legend-application-data-cube/src/stores/query-builder/LegendDataCubeQueryBuilderStore.tsx b/packages/legend-application-data-cube/src/stores/query-builder/LegendDataCubeQueryBuilderStore.tsx deleted file mode 100644 index 0d623e56a5..0000000000 --- a/packages/legend-application-data-cube/src/stores/query-builder/LegendDataCubeQueryBuilderStore.tsx +++ /dev/null @@ -1,323 +0,0 @@ -/** - * 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 { action, makeObservable, observable } from 'mobx'; -import type { - LegendDataCubeApplicationStore, - LegendDataCubeBaseStore, -} from '../LegendDataCubeBaseStore.js'; -import { - type DataCubeAlertService, - type DataCubeAPI, - type DataCubeLayoutService, - type DataCubeTaskService, - DataCubeQuery, - DEFAULT_ALERT_WINDOW_CONFIG, - type DisplayState, -} from '@finos/legend-data-cube'; -import { LegendDataCubeNewQueryState } from './LegendDataCubeNewQueryState.js'; -import { - PersistentDataCubeQuery, - type V1_EngineServerClient, - type V1_PureGraphManager, -} from '@finos/legend-graph'; -import { - ActionState, - assertErrorThrown, - formatDate, - isString, - uuid, -} from '@finos/legend-shared'; -import type { LegendDataCubeDataCubeEngine } from '../LegendDataCubeDataCubeEngine.js'; -import { LegendDataCubeQuerySaver } from '../../components/query-builder/LegendDataCubeQuerySaver.js'; -import { - generateQueryBuilderRoute, - LEGEND_DATA_CUBE_ROUTE_PATTERN_TOKEN, -} from '../../__lib__/LegendDataCubeNavigation.js'; -import { LegendDataCubeQueryLoaderState } from './LegendDataCubeQueryLoaderState.js'; -import { - LegendDataCubeUserDataKey, - RECENTLY_VIEWED_QUERIES_LIMIT, -} from '../../__lib__/LegendDataCubeUserData.js'; -import type { DepotServerClient } from '@finos/legend-server-depot'; - -export class LegendDataCubeQueryBuilderState { - uuid = uuid(); - startTime = Date.now(); - query!: DataCubeQuery; - persistentQuery?: PersistentDataCubeQuery | undefined; - dataCube?: DataCubeAPI | undefined; - - constructor( - query: DataCubeQuery, - persistentQuery?: PersistentDataCubeQuery | undefined, - ) { - makeObservable(this, { - dataCube: observable, - setDataCube: action, - - query: observable, - persistentQuery: observable, - }); - - this.query = query; - this.persistentQuery = persistentQuery; - } - - setDataCube(val: DataCubeAPI | undefined) { - this.dataCube = val; - } -} - -export class LegendDataCubeQueryBuilderStore { - readonly application: LegendDataCubeApplicationStore; - readonly baseStore: LegendDataCubeBaseStore; - readonly engine: LegendDataCubeDataCubeEngine; - readonly depotServerClient: DepotServerClient; - readonly engineServerClient: V1_EngineServerClient; - readonly graphManager: V1_PureGraphManager; - readonly taskService: DataCubeTaskService; - readonly layoutService: DataCubeLayoutService; - readonly alertService: DataCubeAlertService; - - readonly newQueryState: LegendDataCubeNewQueryState; - - readonly saveQueryState = ActionState.create(); - readonly saverDisplay: DisplayState; - - readonly loadQueryState = ActionState.create(); - loader: LegendDataCubeQueryLoaderState; - builder?: LegendDataCubeQueryBuilderState | undefined; - - constructor(baseStore: LegendDataCubeBaseStore) { - makeObservable(this, { - builder: observable, - setBuilder: action, - }); - - this.application = baseStore.application; - this.baseStore = baseStore; - this.engine = baseStore.engine; - this.depotServerClient = baseStore.depotServerClient; - this.engineServerClient = baseStore.engineServerClient; - this.graphManager = baseStore.graphManager; - this.taskService = baseStore.taskService; - this.alertService = baseStore.alertService; - this.layoutService = baseStore.layoutService; - - this.newQueryState = new LegendDataCubeNewQueryState(this); - this.loader = new LegendDataCubeQueryLoaderState(this); - this.saverDisplay = this.layoutService.newDisplay( - 'Save Query', - () => , - { - ...DEFAULT_ALERT_WINDOW_CONFIG, - height: 140, - }, - ); - } - - setBuilder(val: LegendDataCubeQueryBuilderState | undefined) { - this.builder = val; - } - - private updateWindowTitle(persistentQuery: PersistentDataCubeQuery) { - this.application.layoutService.setWindowTitle( - `\u229E ${persistentQuery.name}${this.builder ? ` - ${formatDate(new Date(this.builder.startTime), 'HH:mm:ss EEE MMM dd yyyy')}` : ''}`, - ); - } - - getRecentlyViewedQueries() { - const data = this.application.userDataService.getObjectValue( - LegendDataCubeUserDataKey.RECENTLY_VIEWED_QUERIES, - ); - return data && Array.isArray(data) && data.every((id) => isString(id)) - ? data - : []; - } - - async loadQuery(queryId: string | undefined) { - // internalize the parameters and clean them from the URL - const sourceData = - this.application.navigationService.navigator.getCurrentLocationParameterValue( - LEGEND_DATA_CUBE_ROUTE_PATTERN_TOKEN.SOURCE_DATA, - ); - if (sourceData && !queryId) { - this.application.navigationService.navigator.updateCurrentLocation( - generateQueryBuilderRoute(null), - ); - // populate the new query state if source data is specified - try { - await this.newQueryState.finalize(JSON.parse(atob(sourceData))); - } catch (error) { - assertErrorThrown(error); - this.alertService.alertError(error, { - message: `Query Creation Failure: Can't materialize query source from source data. Error: ${error.message}`, - }); - this.setBuilder(undefined); - } - } - - if (queryId !== this.builder?.persistentQuery?.id) { - if (!queryId) { - this.setBuilder(undefined); - this.loader.display.open(); - return; - } - - this.loadQueryState.inProgress(); - - try { - const persistentQuery = - await this.baseStore.graphManager.getDataCubeQuery(queryId); - const query = DataCubeQuery.serialization.fromJson( - persistentQuery.content, - ); - this.setBuilder( - new LegendDataCubeQueryBuilderState(query, persistentQuery), - ); - this.updateWindowTitle(persistentQuery); - - // update the list of stack of recently viewed queries - const recentlyViewedQueries = this.getRecentlyViewedQueries(); - const idx = recentlyViewedQueries.findIndex((data) => data === queryId); - if (idx === -1) { - if (recentlyViewedQueries.length >= RECENTLY_VIEWED_QUERIES_LIMIT) { - recentlyViewedQueries.pop(); - } - recentlyViewedQueries.unshift(queryId); - } else { - recentlyViewedQueries.splice(idx, 1); - recentlyViewedQueries.unshift(queryId); - } - this.application.userDataService.persistValue( - LegendDataCubeUserDataKey.RECENTLY_VIEWED_QUERIES, - recentlyViewedQueries, - ); - - this.loadQueryState.pass(); - } catch (error) { - assertErrorThrown(error); - this.alertService.alertError(error, { - message: `Query Load Failure: ${error.message}`, - }); - this.loadQueryState.fail(); - } - } - } - - private async generatePersistentQuery( - api: DataCubeAPI, - name: string, - existingPersistentQuery?: PersistentDataCubeQuery | undefined, - ) { - const query = await api.generateDataCubeQuery(); - let persistentQuery: PersistentDataCubeQuery; - if (existingPersistentQuery) { - persistentQuery = existingPersistentQuery.clone(); - } else { - persistentQuery = new PersistentDataCubeQuery(); - persistentQuery.id = uuid(); - } - persistentQuery.name = name; - persistentQuery.content = DataCubeQuery.serialization.toJson(query); - return persistentQuery; - } - - async createQuery(name: string) { - if (!this.builder?.dataCube || this.saveQueryState.isInProgress) { - return; - } - - this.saveQueryState.inProgress(); - try { - const persistentQuery = await this.generatePersistentQuery( - this.builder.dataCube, - name, - ); - - const newQuery = - await this.baseStore.graphManager.createDataCubeQuery(persistentQuery); - // NOTE: reload is the cleanest, least bug-prone handling here - // but we can opt for just updating the URL to reflect the new query - // as an optimization. Also, it helps preserve the edition history - // on the existing data-cube. - // - // Another way to avoid reloading the whole app it to force update - // the component using the key prop that ties to an ID - // of the builder. - this.application.navigationService.navigator.updateCurrentLocation( - generateQueryBuilderRoute(newQuery.id), - ); - this.updateWindowTitle(persistentQuery); - - this.saverDisplay.close(); - this.saveQueryState.pass(); - } catch (error) { - assertErrorThrown(error); - this.alertService.alertError(error, { - message: `Query Creation Failure: ${error.message}`, - }); - this.saveQueryState.fail(); - } - } - - async saveQuery(name: string, saveAsNewQuery: boolean) { - if (!this.builder?.dataCube || this.saveQueryState.isInProgress) { - return; - } - - this.saveQueryState.inProgress(); - try { - const persistentQuery = await this.generatePersistentQuery( - this.builder.dataCube, - name, - this.builder.persistentQuery, - ); - - if (saveAsNewQuery) { - persistentQuery.id = uuid(); - const newQuery = - await this.baseStore.graphManager.createDataCubeQuery( - persistentQuery, - ); - // NOTE: reload is the cleanest, least bug-prone handling here - // but we can opt for just updating the URL to reflect the new query - // as an optimization. Also, it helps preserve the edition history - // on the existing data-cube. - // - // Another way to avoid reloading the whole app it to force update - // the component using the key prop that ties to an ID - // of the builder. - this.application.navigationService.navigator.updateCurrentLocation( - generateQueryBuilderRoute(newQuery.id), - ); - } else { - await this.baseStore.graphManager.updateDataCubeQuery(persistentQuery); - } - this.updateWindowTitle(persistentQuery); - - this.saverDisplay.close(); - this.saveQueryState.pass(); - } catch (error) { - assertErrorThrown(error); - this.alertService.alertError(error, { - message: `Query Update Failure: ${error.message}`, - }); - this.saveQueryState.fail(); - } - } -} diff --git a/packages/legend-application-data-cube/src/stores/query-builder/LegendDataCubeQueryLoaderState.tsx b/packages/legend-application-data-cube/src/stores/query-builder/LegendDataCubeQueryLoaderState.tsx deleted file mode 100644 index 1c93238699..0000000000 --- a/packages/legend-application-data-cube/src/stores/query-builder/LegendDataCubeQueryLoaderState.tsx +++ /dev/null @@ -1,260 +0,0 @@ -/** - * 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 { - APPLICATION_EVENT, - DEFAULT_TYPEAHEAD_SEARCH_MINIMUM_SEARCH_LENGTH, - type GenericLegendApplicationStore, -} from '@finos/legend-application'; -import { - type LightPersistentDataCubeQuery, - QuerySearchSpecification, - QuerySearchSortBy, - type V1_PureGraphManager, -} from '@finos/legend-graph'; -import { ActionState, assertErrorThrown, LogEvent } from '@finos/legend-shared'; -import { makeObservable, observable, action } from 'mobx'; -import type { LegendDataCubeQueryBuilderStore } from './LegendDataCubeQueryBuilderStore.js'; -import { LegendDataCubeUserDataKey } from '../../__lib__/LegendDataCubeUserData.js'; -import { - type DataCubeAlertService, - DEFAULT_TOOL_PANEL_WINDOW_CONFIG, - type DisplayState, -} from '@finos/legend-data-cube'; -import { LegendDataCubeQueryLoader } from '../../components/query-builder/LegendDataCubeQueryLoader.js'; -import { generateQueryBuilderRoute } from '../../__lib__/LegendDataCubeNavigation.js'; - -export const DATA_CUBE_QUERY_LOADER_TYPEAHEAD_SEARCH_LIMIT = 50; -export const DATA_CUBE_QUERY_LOADER_DEFAULT_QUERY_SEARCH_LIMIT = 10; - -export enum DataCubeQuerySortByType { - LAST_CREATED = 'Last Created', - LAST_VIEWED = 'Last Viewed', - LAST_UPDATED = 'Last Updated', -} - -export class LegendDataCubeQueryLoaderState { - private readonly _application: GenericLegendApplicationStore; - private readonly _store: LegendDataCubeQueryBuilderStore; - private readonly _graphManager: V1_PureGraphManager; - private readonly _alertService: DataCubeAlertService; - - readonly display: DisplayState; - readonly searchState = ActionState.create(); - readonly finalizeState = ActionState.create(); - - queries: LightPersistentDataCubeQuery[] = []; - selectedQuery?: LightPersistentDataCubeQuery | undefined; - - searchText = ''; - showCurrentUserQueriesOnly = false; - showingDefaultQueries = true; - sortBy = DataCubeQuerySortByType.LAST_VIEWED; - - constructor(store: LegendDataCubeQueryBuilderStore) { - makeObservable(this, { - showingDefaultQueries: observable, - setShowingDefaultQueries: action, - - searchText: observable, - setSearchText: action, - - queries: observable, - setQueries: action, - - showCurrentUserQueriesOnly: observable, - setShowCurrentUserQueriesOnly: action, - - sortBy: observable, - setSortBy: action, - - selectedQuery: observable, - setSelectedQuery: action, - }); - - this._application = store.application; - this._store = store; - this._graphManager = store.graphManager; - this._alertService = store.alertService; - - this.display = store.layoutService.newDisplay( - 'Load Query', - () => , - { - ...DEFAULT_TOOL_PANEL_WINDOW_CONFIG, - width: 500, - minWidth: 500, - }, - ); - } - - setSearchText(val: string) { - this.searchText = val; - } - - setQueries(val: LightPersistentDataCubeQuery[]) { - this.queries = val; - } - - setShowingDefaultQueries(val: boolean) { - this.showingDefaultQueries = val; - } - - setShowCurrentUserQueriesOnly(val: boolean): void { - this.showCurrentUserQueriesOnly = val; - } - - setSortBy(val: DataCubeQuerySortByType) { - this.sortBy = val; - } - - private getQuerySearchSortBy(sortByValue: string) { - switch (sortByValue) { - case DataCubeQuerySortByType.LAST_CREATED: - return QuerySearchSortBy.SORT_BY_CREATE; - case DataCubeQuerySortByType.LAST_UPDATED: - return QuerySearchSortBy.SORT_BY_UPDATE; - case DataCubeQuerySortByType.LAST_VIEWED: - return QuerySearchSortBy.SORT_BY_VIEW; - default: - return undefined; - } - } - - setSelectedQuery(query: LightPersistentDataCubeQuery | undefined) { - this.selectedQuery = query; - } - - canPerformAdvancedSearch(searchText: string) { - return !( - searchText.length < DEFAULT_TYPEAHEAD_SEARCH_MINIMUM_SEARCH_LENGTH && - !this.showCurrentUserQueriesOnly - ); - } - - async searchQueries(searchText: string) { - // for the initial search, i.e. no search config is specified, fetch the default queries if possible - if (!this.canPerformAdvancedSearch(searchText)) { - if (!searchText) { - this.setShowingDefaultQueries(true); - this.searchState.inProgress(); - this.setQueries([]); - let defaultQueries: LightPersistentDataCubeQuery[] = []; - try { - // first, try to fetch recently viewed queries - try { - const recentlyViewedQueryIDs = - this._store.getRecentlyViewedQueries(); - if (recentlyViewedQueryIDs.length) { - defaultQueries = await this._graphManager.getDataCubeQueries( - recentlyViewedQueryIDs, - ); - } - } catch (error) { - assertErrorThrown(error); - // if there's an error fetching recently viewed queries, most likely because - // some queries have been removed, just remove them all from the cached user data - this._application.userDataService.persistValue( - LegendDataCubeUserDataKey.RECENTLY_VIEWED_QUERIES, - undefined, - ); - } - // if there's no recently viewed queries, just fetch queries of the current user - if (!defaultQueries.length) { - const searchSpecification = new QuerySearchSpecification(); - searchSpecification.limit = - DATA_CUBE_QUERY_LOADER_DEFAULT_QUERY_SEARCH_LIMIT; - searchSpecification.showCurrentUserQueriesOnly = true; - defaultQueries = - await this._graphManager.searchDataCubeQueries( - searchSpecification, - ); - } - this.setQueries(defaultQueries); - this.searchState.pass(); - } catch (error) { - assertErrorThrown(error); - this._application.logService.error( - LogEvent.create(APPLICATION_EVENT.GENERIC_FAILURE), - error, - ); - this.searchState.fail(); - } - } - return; - } - - this.setShowingDefaultQueries(false); - this.searchState.inProgress(); - - try { - const searchSpecification = - QuerySearchSpecification.createDefault(searchText); - searchSpecification.limit = - DATA_CUBE_QUERY_LOADER_TYPEAHEAD_SEARCH_LIMIT + 1; - searchSpecification.showCurrentUserQueriesOnly = - this.showCurrentUserQueriesOnly; - const querySearchSortBy = this.getQuerySearchSortBy(this.sortBy); - if (querySearchSortBy) { - searchSpecification.sortByOption = querySearchSortBy; - } - this.setQueries( - await this._graphManager.searchDataCubeQueries(searchSpecification), - ); - - // if sorting is not configured, sort by name - if (!querySearchSortBy) { - this.setQueries( - this.queries.toSorted((a, b) => a.name.localeCompare(b.name)), - ); - } - - this.searchState.pass(); - } catch (error) { - assertErrorThrown(error); - this._alertService.alertError(error, { - message: `Query Search Failure: ${error.message}`, - }); - this.searchState.fail(); - } - } - - async finalize() { - if (!this.selectedQuery) { - return; - } - - this.finalizeState.inProgress(); - try { - // just simply change the route here and the new query ID will get picked up - // and handled by the query builder to load the new query. - this._application.navigationService.navigator.updateCurrentLocation( - generateQueryBuilderRoute(this.selectedQuery.id), - ); - - // reset - this.setSelectedQuery(undefined); - this.display.close(); - this.finalizeState.pass(); - } catch (error) { - assertErrorThrown(error); - this._alertService.alertError(error, { - message: `Query Load Failure: ${error.message}`, - }); - this.finalizeState.fail(); - } - } -} diff --git a/packages/legend-application-query/src/__lib__/LegendQueryNavigation.ts b/packages/legend-application-query/src/__lib__/LegendQueryNavigation.ts index 639151e3f5..55ec9e8398 100644 --- a/packages/legend-application-query/src/__lib__/LegendQueryNavigation.ts +++ b/packages/legend-application-query/src/__lib__/LegendQueryNavigation.ts @@ -257,7 +257,7 @@ export const EXTERNAL_APPLICATION_NAVIGATION__generateTaxonomyDataspaceViewUrl = /** * @external_application_navigation This depends on Legend DataCube routing and is hardcoded so it's potentially brittle */ -export const EXTERNAL_APPLICATION_NAVIGATION__generateDataCubeNewQueryUrl = ( +export const EXTERNAL_APPLICATION_NAVIGATION__generateNewDataCubeUrl = ( dataCubeApplicationUrl: string, sourceData: object, ): string => diff --git a/packages/legend-application-query/src/components/Core_LegendQueryApplicationPlugin.tsx b/packages/legend-application-query/src/components/Core_LegendQueryApplicationPlugin.tsx index 5405fc195b..1cdaf8c8bb 100644 --- a/packages/legend-application-query/src/components/Core_LegendQueryApplicationPlugin.tsx +++ b/packages/legend-application-query/src/components/Core_LegendQueryApplicationPlugin.tsx @@ -43,7 +43,7 @@ import { MoreVerticalIcon, } from '@finos/legend-art'; import { - EXTERNAL_APPLICATION_NAVIGATION__generateDataCubeNewQueryUrl, + EXTERNAL_APPLICATION_NAVIGATION__generateNewDataCubeUrl, generateCloneServiceQuerySetupRoute, generateCreateMappingQuerySetupRoute, generateEditExistingQuerySetupRoute, @@ -164,7 +164,7 @@ export const QueryDataCubeUsage = observer(() => { queryEditorStore.query && applicationStore.config.dataCubeApplicationUrl ) { - return EXTERNAL_APPLICATION_NAVIGATION__generateDataCubeNewQueryUrl( + return EXTERNAL_APPLICATION_NAVIGATION__generateNewDataCubeUrl( applicationStore.config.dataCubeApplicationUrl, { _type: QUERY_DATACUBE_SOURCE_TYPE, diff --git a/packages/legend-application-query/src/components/__tests__/LegendQuery.test.tsx b/packages/legend-application-query/src/components/__tests__/LegendQuery.test.tsx index b6a60b9e56..4ad4dd07d1 100644 --- a/packages/legend-application-query/src/components/__tests__/LegendQuery.test.tsx +++ b/packages/legend-application-query/src/components/__tests__/LegendQuery.test.tsx @@ -106,9 +106,9 @@ test( TEST__getTestLegendQueryApplicationConfig(), pluginManager, ); - const MOCk_lastOpenedVersion = createMock(); - appStore.releaseNotesService.getViewedVersion = MOCk_lastOpenedVersion; - MOCk_lastOpenedVersion.mockReturnValue('1.0.0'); + const MOCK__lastOpenedVersion = createMock(); + appStore.releaseNotesService.getViewedVersion = MOCK__lastOpenedVersion; + MOCK__lastOpenedVersion.mockReturnValue('1.0.0'); appStore.releaseNotesService.configure(releaseLog as VersionReleaseNotes[]); const mockedQueryEditorStore = TEST__provideMockedQueryEditorStore({ applicationStore: appStore, @@ -165,9 +165,9 @@ test( TEST__getTestLegendQueryApplicationConfig(), pluginManager, ); - const MOCk_lastOpenedVersion = createMock(); - appStore.releaseNotesService.getViewedVersion = MOCk_lastOpenedVersion; - MOCk_lastOpenedVersion.mockReturnValue('2.0.0'); + const MOCK__lastOpenedVersion = createMock(); + appStore.releaseNotesService.getViewedVersion = MOCK__lastOpenedVersion; + MOCK__lastOpenedVersion.mockReturnValue('2.0.0'); appStore.releaseNotesService.configure(releaseLog as VersionReleaseNotes[]); const mockedQueryEditorStore = TEST__provideMockedQueryEditorStore({ applicationStore: appStore, diff --git a/packages/legend-application-query/src/components/data-cube/ExistingQueryDataCubeViewer.tsx b/packages/legend-application-query/src/components/data-cube/ExistingQueryDataCubeViewer.tsx index 545f3e312b..98346124a4 100644 --- a/packages/legend-application-query/src/components/data-cube/ExistingQueryDataCubeViewer.tsx +++ b/packages/legend-application-query/src/components/data-cube/ExistingQueryDataCubeViewer.tsx @@ -38,10 +38,10 @@ export const DataCubeWrapper = observer(() => { flowResult(store.initialize()).catch(applicationStore.alertUnhandledError); }, [applicationStore, store]); - if (!store.engine || !store.query) { + if (!store.engine || !store.specification) { return null; } - return ; + return ; }); export const ExistingQueryDataCubeViewer = observer(() => { diff --git a/packages/legend-application-query/src/stores/data-cube/ExistingQueryDataCubeViewer.ts b/packages/legend-application-query/src/stores/data-cube/ExistingQueryDataCubeViewer.ts index fb5413bb6a..bf60e0b41b 100644 --- a/packages/legend-application-query/src/stores/data-cube/ExistingQueryDataCubeViewer.ts +++ b/packages/legend-application-query/src/stores/data-cube/ExistingQueryDataCubeViewer.ts @@ -29,7 +29,7 @@ import { DEFAULT_TAB_SIZE } from '@finos/legend-application'; import { assertErrorThrown, type GeneratorFn } from '@finos/legend-shared'; import { QueryBuilderDataCubeEngine } from '@finos/legend-query-builder'; import { flow, makeObservable, observable } from 'mobx'; -import { type DataCubeQuery } from '@finos/legend-data-cube'; +import { type DataCubeSpecification } from '@finos/legend-data-cube'; export class ExistingQueryDataCubeEditorStore { readonly applicationStore: LegendQueryApplicationStore; @@ -37,7 +37,7 @@ export class ExistingQueryDataCubeEditorStore { readonly graphManagerState: GraphManagerState; readonly queryId: string; - query?: DataCubeQuery | undefined; + specification?: DataCubeSpecification | undefined; engine?: QueryBuilderDataCubeEngine | undefined; constructor( @@ -46,7 +46,7 @@ export class ExistingQueryDataCubeEditorStore { queryId: string, ) { makeObservable(this, { - query: observable, + specification: observable, engine: observable, initialize: flow, @@ -113,7 +113,8 @@ export class ExistingQueryDataCubeEditorStore { this.graphManagerState, ); this.engine = engine; - this.query = (yield engine.generateInitialQuery()) as DataCubeQuery; + this.specification = + (yield engine.generateInitialSpecification()) as DataCubeSpecification; } catch (error) { assertErrorThrown(error); this.applicationStore.notificationService.notifyError( diff --git a/packages/legend-application-repl/src/components/LegendREPLDataCubeHeader.tsx b/packages/legend-application-repl/src/components/LegendREPLDataCubeHeader.tsx index d097042f53..41ab0ccac2 100644 --- a/packages/legend-application-repl/src/components/LegendREPLDataCubeHeader.tsx +++ b/packages/legend-application-repl/src/components/LegendREPLDataCubeHeader.tsx @@ -20,7 +20,7 @@ import { } from '@finos/legend-data-cube'; import { observer } from 'mobx-react-lite'; import { LegendREPLDataCubeSource } from '../stores/LegendREPLDataCubeSource.js'; -import { useLegendREPLBaseStore } from './LegendREPLFramworkProvider.js'; +import { useLegendREPLBaseStore } from './LegendREPLFrameworkProvider.js'; export const LegendREPLDataCubeHeader = observer( (props: DataCubeInnerHeaderComponentParams) => { diff --git a/packages/legend-application-repl/src/components/LegendREPLFramworkProvider.tsx b/packages/legend-application-repl/src/components/LegendREPLFrameworkProvider.tsx similarity index 97% rename from packages/legend-application-repl/src/components/LegendREPLFramworkProvider.tsx rename to packages/legend-application-repl/src/components/LegendREPLFrameworkProvider.tsx index 5babc949cc..275eb3e154 100644 --- a/packages/legend-application-repl/src/components/LegendREPLFramworkProvider.tsx +++ b/packages/legend-application-repl/src/components/LegendREPLFrameworkProvider.tsx @@ -48,7 +48,7 @@ const LegendREPLBaseStoreProvider: React.FC<{ export const useLegendREPLBaseStore = (): LegendREPLBaseStore => guaranteeNonNullable( useContext(LegendREPLBaseStoreContext), - `Can't find Legend Query base store in context`, + `Can't find Legend REPL base store in context`, ); export const LegendREPLFrameworkProvider: React.FC<{ diff --git a/packages/legend-application-repl/src/components/LegendREPLPublishDataCubeAlert.tsx b/packages/legend-application-repl/src/components/LegendREPLPublishDataCubeAlert.tsx index c6ef0d74ea..5a5df5192a 100644 --- a/packages/legend-application-repl/src/components/LegendREPLPublishDataCubeAlert.tsx +++ b/packages/legend-application-repl/src/components/LegendREPLPublishDataCubeAlert.tsx @@ -14,20 +14,20 @@ * limitations under the License. */ -import { useLegendREPLBaseStore } from './LegendREPLFramworkProvider.js'; -import type { PersistentDataCubeQuery } from '@finos/legend-graph'; +import { useLegendREPLBaseStore } from './LegendREPLFrameworkProvider.js'; +import type { PersistentDataCube } from '@finos/legend-graph'; import { EXTERNAL_APPLICATION_NAVIGATION__generateDataCubeViewUrl } from '../application/LegendREPLNavigation.js'; import { DataCubeIcon } from '@finos/legend-art'; export const LegendREPLPublishDataCubeAlert = (props: { - query: PersistentDataCubeQuery; + persistentDataCube: PersistentDataCube; }) => { - const { query } = props; + const { persistentDataCube } = props; const store = useLegendREPLBaseStore(); const link = store.hostedApplicationBaseUrl ? EXTERNAL_APPLICATION_NAVIGATION__generateDataCubeViewUrl( store.hostedApplicationBaseUrl, - query.id, + persistentDataCube.id, ) : undefined; @@ -39,23 +39,23 @@ export const LegendREPLPublishDataCubeAlert = (props: {
- Published query successfully! + Published DataCube successfully!
{link && (
- To view or share the published query, use the link below. + To view or share the published DataCube, use the link below.
)} {!link && (
- See the published query ID below. + See the published DataCube ID below.
)}
{link && (
-
+
- {query.id} + {persistentDataCube.id}
diff --git a/packages/legend-data-cube/src/index.tsx b/packages/legend-data-cube/src/index.tsx index 1fef78849e..80471aa63f 100644 --- a/packages/legend-data-cube/src/index.tsx +++ b/packages/legend-data-cube/src/index.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -export * from './stores/core/model/DataCubeQuery.js'; +export * from './stores/core/model/DataCubeSpecification.js'; export * from './stores/core/model/DataCubeSource.js'; export { type DataCubeColumn } from './stores/core/model/DataCubeColumn.js'; export * from './stores/core/model/DataCubeConfiguration.js'; @@ -23,7 +23,7 @@ export { CachedDataCubeSource } from './stores/core/model/CachedDataCubeSource.j export * from './stores/core/DataCubeEngine.js'; export * from './stores/core/DataCubeQueryEngine.js'; -export { DataCubeQuerySnapshot } from './stores/core/DataCubeQuerySnapshot.js'; +export { DataCubeSnapshot } from './stores/core/DataCubeSnapshot.js'; export { type DataCubeSetting, @@ -34,6 +34,7 @@ export { export { DataCubeLayoutService, WindowState, + type WindowConfiguration, DisplayState, LayoutConfiguration, DEFAULT_TOOL_PANEL_WINDOW_CONFIG, diff --git a/packages/legend-data-cube/src/stores/DataCubeAPI.ts b/packages/legend-data-cube/src/stores/DataCubeAPI.ts index 8f9cf5fcc3..10cb378add 100644 --- a/packages/legend-data-cube/src/stores/DataCubeAPI.ts +++ b/packages/legend-data-cube/src/stores/DataCubeAPI.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { DataCubeQuery } from './core/model/DataCubeQuery.js'; +import type { DataCubeSpecification } from './core/model/DataCubeSpecification.js'; import type { DataCubeSource } from './core/model/DataCubeSource.js'; import type { DataCubeState } from './DataCubeState.js'; import type { DataCubeViewState } from './view/DataCubeViewState.js'; @@ -29,10 +29,21 @@ export interface DataCubeAPI { */ getProcessedSource(): DataCubeSource | undefined; /** - * Generates the data cube query (including the query, configuration, and source) + * Generates the specification (including the query, configuration, and source) * from the latest state of the DataCube. */ - generateDataCubeQuery(): Promise; + generateSpecification(): Promise; + /** + * Updates the name of the DataCube main view. + */ + updateName(name: string): void; + /** + * Applies the specification to the DataCube, i.e. update the query, configuration. + * + * Note that source cannot be updated via this method, providing a different source + * than the one the DataCube has will result in an error. + */ + applySpecification(specification: DataCubeSpecification): Promise; /** * Retries all failed data fetches and rerender the grid. */ @@ -67,11 +78,19 @@ export class INTERNAL__DataCubeAPI implements DataCubeAPI { // ----------------------------- API ----------------------------- getProcessedSource() { - return this._dataCube.view.getOriginalSource(); + return this._dataCube.view.getInitialSource(); + } + + generateSpecification() { + return this._dataCube.view.generateSpecification(); + } + + updateName(name: string) { + this._dataCube.view.updateName(name); } - generateDataCubeQuery() { - return this._dataCube.view.generateDataCubeQuery(); + async applySpecification(specification: DataCubeSpecification) { + await this._dataCube.view.applySpecification(specification); } retryFailedDataFetches() { diff --git a/packages/legend-data-cube/src/stores/DataCubeOptions.ts b/packages/legend-data-cube/src/stores/DataCubeOptions.ts index edd764118c..22e6443fb0 100644 --- a/packages/legend-data-cube/src/stores/DataCubeOptions.ts +++ b/packages/legend-data-cube/src/stores/DataCubeOptions.ts @@ -48,6 +48,9 @@ export type DataCubeMenuItem = { disabled?: boolean | undefined; action: () => void; }; +export enum DataCubeNativeMenuItem { + SEPARATOR = 'separator', +} type DataCubeOptionsBaseParams = { api: DataCubeAPI; @@ -75,6 +78,10 @@ export type DataCubeOptions = { onViewInitialized?: | ((event: DataCubeViewInitializedEvent) => void) | undefined; + /** + * Enables caching to suppport local query execution. + */ + enableCache?: boolean | undefined; // ------------------------------ GRID ------------------------------ @@ -103,6 +110,12 @@ export type DataCubeOptions = { innerHeaderRenderer?: | ((params: DataCubeInnerHeaderComponentParams) => React.ReactNode) | undefined; + /** + * Provides a list of items to be displayed in the header dropdown menu. + */ + getHeaderMenuItems?: + | (() => (DataCubeMenuItem | DataCubeNativeMenuItem)[]) + | undefined; // ------------------------------ SETTINGS ------------------------------ @@ -119,15 +132,4 @@ export type DataCubeOptions = { onSettingsChanged?: | ((event: DataCubeSettingsChangedEvent) => void) | undefined; - - // ------------------------------ DOCUMENTATION ------------------------------ - - /** - * URL to the documentation website. - */ - documentationUrl?: string | undefined; - /** - * Enables caching to suppport local query execution. - */ - enableCache?: boolean | undefined; }; diff --git a/packages/legend-data-cube/src/stores/DataCubeState.tsx b/packages/legend-data-cube/src/stores/DataCubeState.tsx index d98fbf083c..2f5f7b3385 100644 --- a/packages/legend-data-cube/src/stores/DataCubeState.tsx +++ b/packages/legend-data-cube/src/stores/DataCubeState.tsx @@ -21,7 +21,7 @@ import { action, makeObservable, observable } from 'mobx'; import { DataCubeSettingService } from './services/DataCubeSettingService.js'; import { INTERNAL__DataCubeAPI } from './DataCubeAPI.js'; import type { DataCubeOptions } from './DataCubeOptions.js'; -import type { DataCubeQuery } from './core/model/DataCubeQuery.js'; +import type { DataCubeSpecification } from './core/model/DataCubeSpecification.js'; import { LicenseManager } from 'ag-grid-enterprise'; import { configureCodeEditor, @@ -47,7 +47,7 @@ export class DataCubeState { readonly navigationService: DataCubeNavigationService; readonly telemetryService: DataCubeTelemetryService; - readonly query: DataCubeQuery; + readonly specification: DataCubeSpecification; readonly options?: DataCubeOptions | undefined; readonly initializeState = ActionState.create(); @@ -59,7 +59,7 @@ export class DataCubeState { view: DataCubeViewState; constructor( - query: DataCubeQuery, + specification: DataCubeSpecification, engine: DataCubeEngine, options?: DataCubeOptions | undefined, ) { @@ -90,7 +90,7 @@ export class DataCubeState { this.telemetryService = new DataCubeTelemetryService(this.engine); this.api = new INTERNAL__DataCubeAPI(this); - this.query = query; + this.specification = specification; this.options = options; this.view = new DataCubeViewState(this); diff --git a/packages/legend-data-cube/src/stores/__tests__/DataCubeSnapshotService.data-cube-test.ts b/packages/legend-data-cube/src/stores/__tests__/DataCubeSnapshotService.data-cube-test.ts new file mode 100644 index 0000000000..dbd384f58a --- /dev/null +++ b/packages/legend-data-cube/src/stores/__tests__/DataCubeSnapshotService.data-cube-test.ts @@ -0,0 +1,286 @@ +/** + * 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 { createSpy, unitTest } from '@finos/legend-shared/test'; +import { expect, test } from '@jest/globals'; +import { TEST__DataCubeEngine } from './DataCubeTestUtils.js'; +import { DataCubeSettingService } from '../services/DataCubeSettingService.js'; +import { DataCubeLogService } from '../services/DataCubeLogService.js'; +import { DataCubeLayoutService } from '../services/DataCubeLayoutService.js'; +import { DataCubeSnapshotService } from '../services/DataCubeSnapshotService.js'; +import { DataCubeSnapshot } from '../core/DataCubeSnapshot.js'; +import type { Writable } from '@finos/legend-shared'; + +test(unitTest(`Snapshot Service: undo/redo works properly`), () => { + const service = _newService(); + const s1 = _newSnapshot('s1'); + const s2 = _newSnapshot('s2'); + const s3 = _newSnapshot('s3'); + const s4 = _newSnapshot('s4'); + const s5 = _newSnapshot('s5'); + const s6 = _newSnapshot('s6'); + const s7 = _newSnapshot('s7'); + const s8 = _newSnapshot('s8'); + const s9 = _newSnapshot('s9'); + const s10 = _newSnapshot('s10'); + const s11 = _newSnapshot('s11'); + const s12 = _newSnapshot('s12'); + const s13 = _newSnapshot('s13'); + const s14 = _newSnapshot('s14'); + + expect(service.canUndo).toBe(false); + expect(service.canRedo).toBe(false); + + // cannot undo when there is no more than 1 snapshot + // this snapshot is the initial snapshot and always kept around + service.broadcastSnapshot(s1); + _matchCurrentSnapshot(service, s1); + expect(service.canUndo).toBe(false); + expect(service.canRedo).toBe(false); + + service.broadcastSnapshot(s2); + _matchCurrentSnapshot(service, s2); + expect(service.canUndo).toBe(true); + expect(service.canRedo).toBe(false); + + service.broadcastSnapshot(s3); + _matchCurrentSnapshot(service, s3); + expect(service.canUndo).toBe(true); + expect(service.canRedo).toBe(false); + + service.broadcastSnapshot(s4); + _matchCurrentSnapshot(service, s4); + expect(service.canUndo).toBe(true); + expect(service.canRedo).toBe(false); + + // check when redo is possible + service.undo(); + _matchCurrentSnapshot(service, s3); + expect(service.canUndo).toBe(true); + expect(service.canRedo).toBe(true); + + service.undo(); + _matchCurrentSnapshot(service, s2); + expect(service.canUndo).toBe(true); + expect(service.canRedo).toBe(true); + + service.undo(); + _matchCurrentSnapshot(service, s1); + expect(service.canUndo).toBe(false); + expect(service.canRedo).toBe(true); + + // undoing the first snapshot does nothing + service.undo(); + _matchCurrentSnapshot(service, s1); + expect(service.canUndo).toBe(false); + expect(service.canRedo).toBe(true); + + service.redo(); + _matchCurrentSnapshot(service, s2); + expect(service.canUndo).toBe(true); + expect(service.canRedo).toBe(true); + _matchHistory(service, [s1, s2]); + _matchFullHistory(service, [s1, s2, s3, s4]); + + // when new snapshot is pushed, the redo history is cleared + service.broadcastSnapshot(s5); + _matchCurrentSnapshot(service, s5); + expect(service.canUndo).toBe(true); + expect(service.canRedo).toBe(false); + _matchHistory(service, [s1, s2, s5]); + _matchFullHistory(service, [s1, s2, s5]); + + // when more snapshots are recorded than history size limit, the oldest snapshots are removed + service.broadcastSnapshot(s6); + service.broadcastSnapshot(s7); + service.broadcastSnapshot(s8); + service.broadcastSnapshot(s9); + service.broadcastSnapshot(s10); + service.broadcastSnapshot(s11); + service.broadcastSnapshot(s12); + expect(service.canUndo).toBe(true); + expect(service.canRedo).toBe(false); + _matchHistory(service, [s1, s2, s5, s6, s7, s8, s9, s10, s11, s12]); + + service.broadcastSnapshot(s13); + _matchCurrentSnapshot(service, s13); + expect(service.canUndo).toBe(true); + expect(service.canRedo).toBe(false); + _matchHistory(service, [s2, s5, s6, s7, s8, s9, s10, s11, s12, s13]); + + service.broadcastSnapshot(s14); + _matchCurrentSnapshot(service, s14); + expect(service.canUndo).toBe(true); + expect(service.canRedo).toBe(false); + _matchHistory(service, [s5, s6, s7, s8, s9, s10, s11, s12, s13, s14]); +}); + +test( + unitTest(`Snapshot Service: history size adjustment works properly`), + () => { + const service = _newService(); + const s1 = _newSnapshot('s1'); + const s2 = _newSnapshot('s2'); + const s3 = _newSnapshot('s3'); + const s4 = _newSnapshot('s4'); + const s5 = _newSnapshot('s5'); + const s6 = _newSnapshot('s6'); + const s7 = _newSnapshot('s7'); + const s8 = _newSnapshot('s8'); + const s9 = _newSnapshot('s9'); + const s10 = _newSnapshot('s10'); + const s11 = _newSnapshot('s11'); + const s12 = _newSnapshot('s12'); + const s13 = _newSnapshot('s13'); + const s14 = _newSnapshot('s14'); + + service.broadcastSnapshot(s1); + service.broadcastSnapshot(s2); + service.broadcastSnapshot(s3); + service.broadcastSnapshot(s4); + service.broadcastSnapshot(s5); + service.broadcastSnapshot(s6); + service.broadcastSnapshot(s7); + service.broadcastSnapshot(s8); + service.broadcastSnapshot(s9); + service.broadcastSnapshot(s10); + _matchCurrentSnapshot(service, s10); + _matchFullHistory(service, [s1, s2, s3, s4, s5, s6, s7, s8, s9, s10]); + + // cannot adjust history size to less than minimum size (10) + service.adjustHistorySize(5); + _matchCurrentSnapshot(service, s10); + _matchFullHistory(service, [s1, s2, s3, s4, s5, s6, s7, s8, s9, s10]); + + // adjust history size to greater than current size changes nothing + service.adjustHistorySize(12); + _matchCurrentSnapshot(service, s10); + _matchFullHistory(service, [s1, s2, s3, s4, s5, s6, s7, s8, s9, s10]); + + // reducing history size works properly when current snapshot is the latest + service.broadcastSnapshot(s11); + service.broadcastSnapshot(s12); + _matchCurrentSnapshot(service, s12); + _matchFullHistory(service, [ + s1, + s2, + s3, + s4, + s5, + s6, + s7, + s8, + s9, + s10, + s11, + s12, + ]); + + service.adjustHistorySize(10); + _matchCurrentSnapshot(service, s12); + _matchFullHistory(service, [s3, s4, s5, s6, s7, s8, s9, s10, s11, s12]); + + // reducing history size works properly when current snapshot is not the latest + service.adjustHistorySize(12); + service.broadcastSnapshot(s13); + service.broadcastSnapshot(s14); + _matchCurrentSnapshot(service, s14); + _matchFullHistory(service, [ + s3, + s4, + s5, + s6, + s7, + s8, + s9, + s10, + s11, + s12, + s13, + s14, + ]); + + service.undo(); + service.undo(); + service.undo(); + service.undo(); + _matchCurrentSnapshot(service, s10); + _matchFullHistory(service, [ + s3, + s4, + s5, + s6, + s7, + s8, + s9, + s10, + s11, + s12, + s13, + s14, + ]); + + service.adjustHistorySize(10); + _matchCurrentSnapshot(service, s10); + // redo snapshots are properly pruned + _matchFullHistory(service, [s3, s4, s5, s6, s7, s8, s9, s10]); + }, +); + +function _newService() { + const engine = new TEST__DataCubeEngine(); + const logService = new DataCubeLogService(engine); + const layoutService = new DataCubeLayoutService(); + const settingService = new DataCubeSettingService( + engine, + logService, + layoutService, + ); + createSpy(settingService, 'getNumericValue').mockImplementationOnce(() => 10); + return new DataCubeSnapshotService(logService, settingService); +} + +function _newSnapshot(id: string) { + const snapshot = DataCubeSnapshot.create({}); + (snapshot as Writable).uuid = id; + snapshot.finalize(); + return snapshot; +} + +function _matchCurrentSnapshot( + service: DataCubeSnapshotService, + snapshot: DataCubeSnapshot, +) { + expect(service.getCurrentSnapshot().uuid).toBe(snapshot.uuid); +} + +function _matchHistory( + service: DataCubeSnapshotService, + history: DataCubeSnapshot[], +) { + expect(service.getHistory().map((s) => s.uuid)).toEqual( + history.map((s) => s.uuid), + ); +} + +function _matchFullHistory( + service: DataCubeSnapshotService, + history: DataCubeSnapshot[], +) { + expect(service.getHistory({ full: true }).map((s) => s.uuid)).toEqual( + history.map((s) => s.uuid), + ); +} diff --git a/packages/legend-data-cube/src/stores/core/__tests__/DataCubeTestUtils.ts b/packages/legend-data-cube/src/stores/__tests__/DataCubeTestUtils.ts similarity index 96% rename from packages/legend-data-cube/src/stores/core/__tests__/DataCubeTestUtils.ts rename to packages/legend-data-cube/src/stores/__tests__/DataCubeTestUtils.ts index c78b70e9c7..c17039606c 100644 --- a/packages/legend-data-cube/src/stores/core/__tests__/DataCubeTestUtils.ts +++ b/packages/legend-data-cube/src/stores/__tests__/DataCubeTestUtils.ts @@ -35,8 +35,8 @@ import { type DataCubeRelationType, type DataCubeExecutionOptions, type DataCubeExecutionResult, -} from '../DataCubeEngine.js'; -import type { DataCubeSource } from '../model/DataCubeSource.js'; +} from '../core/DataCubeEngine.js'; +import type { DataCubeSource } from '../core/model/DataCubeSource.js'; import { ENGINE_TEST_SUPPORT__getLambdaRelationType, ENGINE_TEST_SUPPORT__grammarToJSON_valueSpecification, @@ -46,7 +46,7 @@ import { import { deserialize } from 'serializr'; export class TEST__DataCubeEngine extends DataCubeEngine { - override async processQuerySource( + override async processSource( sourceData: PlainObject, ): Promise { throw new Error('Method not implemented.'); diff --git a/packages/legend-data-cube/src/stores/core/DataCubeConfigurationBuilder.ts b/packages/legend-data-cube/src/stores/core/DataCubeConfigurationBuilder.ts index cb85eb1671..b2de9c7620 100644 --- a/packages/legend-data-cube/src/stores/core/DataCubeConfigurationBuilder.ts +++ b/packages/legend-data-cube/src/stores/core/DataCubeConfigurationBuilder.ts @@ -25,12 +25,12 @@ import { DataCubeFontTextAlignment, } from './DataCubeQueryEngine.js'; import { _findCol, type DataCubeColumn } from './model/DataCubeColumn.js'; -import type { DataCubeQuerySnapshotProcessingContext } from './DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotProcessingContext } from './DataCubeSnapshot.js'; import { at } from '@finos/legend-shared'; export function newColumnConfiguration( column: DataCubeColumn, - context?: DataCubeQuerySnapshotProcessingContext | undefined, + context?: DataCubeSnapshotProcessingContext | undefined, ): DataCubeColumnConfiguration { const { name, type } = column; const config = new DataCubeColumnConfiguration(name, type); @@ -108,7 +108,7 @@ export function newColumnConfiguration( } export function newConfiguration( - context: DataCubeQuerySnapshotProcessingContext, + context: DataCubeSnapshotProcessingContext, ): DataCubeConfiguration { const { snapshot, groupBySortColumns } = context; const data = snapshot.data; diff --git a/packages/legend-data-cube/src/stores/core/DataCubeEngine.tsx b/packages/legend-data-cube/src/stores/core/DataCubeEngine.tsx index db056f7283..14c1e8b2f5 100644 --- a/packages/legend-data-cube/src/stores/core/DataCubeEngine.tsx +++ b/packages/legend-data-cube/src/stores/core/DataCubeEngine.tsx @@ -67,7 +67,7 @@ import { DataCubeQueryFilterOperation__EndWithCaseInsensitive } from './filter/D import { DataCubeQueryFilterOperation__NotEndWith } from './filter/DataCubeQueryFilterOperation__NotEndWith.js'; import { DataCubeQueryFilterOperation__IsNull } from './filter/DataCubeQueryFilterOperation__IsNull.js'; import { DataCubeQueryFilterOperation__IsNotNull } from './filter/DataCubeQueryFilterOperation__IsNotNull.js'; -import { DataCubeQuerySnapshot } from './DataCubeQuerySnapshot.js'; +import { DataCubeSnapshot } from './DataCubeSnapshot.js'; import { buildExecutableQuery } from './DataCubeQueryBuilder.js'; import { _toCol, type DataCubeColumn } from './model/DataCubeColumn.js'; import { @@ -84,7 +84,7 @@ import { type PlainObject, } from '@finos/legend-shared'; import type { CachedDataCubeSource } from './model/CachedDataCubeSource.js'; -import { DataCubeQuery } from './model/DataCubeQuery.js'; +import { DataCubeSpecification } from './model/DataCubeSpecification.js'; import { newConfiguration } from './DataCubeConfigurationBuilder.js'; export type CompletionItem = { @@ -197,7 +197,7 @@ export abstract class DataCubeEngine { * Then remove this dummy value from the final code. */ async getPartialQueryCode( - snapshot: DataCubeQuerySnapshot, + snapshot: DataCubeSnapshot, pretty?: boolean | undefined, ) { const source = new INTERNAL__DataCubeSource(); @@ -212,14 +212,14 @@ export abstract class DataCubeEngine { ).substring(`''->`.length); } - async generateBaseQuery(sourceData: PlainObject) { - const source = await this.processQuerySource(sourceData); - const query = new DataCubeQuery(); + async generateBaseSpecification(sourceData: PlainObject) { + const source = await this.processSource(sourceData); + const query = new DataCubeSpecification(); query.source = sourceData; query.query = await this.getValueSpecificationCode( _selectFunction(source.columns), ); - const snapshot = DataCubeQuerySnapshot.create({}); + const snapshot = DataCubeSnapshot.create({}); snapshot.data.sourceColumns = source.columns.map(_toCol); snapshot.data.selectColumns = source.columns.map(_toCol); const configuration = newConfiguration({ @@ -231,7 +231,7 @@ export abstract class DataCubeEngine { // ---------------------------------- PROCESSOR ---------------------------------- - abstract processQuerySource(sourceData: PlainObject): Promise; + abstract processSource(sourceData: PlainObject): Promise; abstract parseValueSpecification( code: string, diff --git a/packages/legend-data-cube/src/stores/core/DataCubeQueryBuilder.ts b/packages/legend-data-cube/src/stores/core/DataCubeQueryBuilder.ts index e9a3ffe88e..d338b6704e 100644 --- a/packages/legend-data-cube/src/stores/core/DataCubeQueryBuilder.ts +++ b/packages/legend-data-cube/src/stores/core/DataCubeQueryBuilder.ts @@ -18,7 +18,7 @@ * [CORE] * * This and its corresponding utilitites are used to build the executable query from - * the query snapshot. The executable query is then used to fetch data. + * the snapshot. The executable query is then used to fetch data. ***************************************************************************************/ import { @@ -26,7 +26,7 @@ import { V1_Lambda, type V1_AppliedFunction, } from '@finos/legend-graph'; -import { type DataCubeQuerySnapshot } from './DataCubeQuerySnapshot.js'; +import { type DataCubeSnapshot } from './DataCubeSnapshot.js'; import { at, guaranteeType } from '@finos/legend-shared'; import { DataCubeFunction, @@ -55,12 +55,12 @@ import { _findCol } from './model/DataCubeColumn.js'; import type { DataCubeEngine } from './DataCubeEngine.js'; export function buildExecutableQuery( - snapshot: DataCubeQuerySnapshot, + snapshot: DataCubeSnapshot, source: DataCubeSource, engine: DataCubeEngine, options?: { postProcessor?: ( - snapshot: DataCubeQuerySnapshot, + snapshot: DataCubeSnapshot, sequence: V1_AppliedFunction[], funcMap: DataCubeQueryFunctionMap, configuration: DataCubeConfiguration, diff --git a/packages/legend-data-cube/src/stores/core/DataCubeQueryBuilderUtils.ts b/packages/legend-data-cube/src/stores/core/DataCubeQueryBuilderUtils.ts index c0fcd9cd7f..d9544007b3 100644 --- a/packages/legend-data-cube/src/stores/core/DataCubeQueryBuilderUtils.ts +++ b/packages/legend-data-cube/src/stores/core/DataCubeQueryBuilderUtils.ts @@ -17,7 +17,7 @@ /*************************************************************************************** * [CORE] * - * These are utilities used to build the executable query from the query snapshot. + * These are utilities used to build the executable query from the snapshot. * The executable query is then used to fetch data. ***************************************************************************************/ @@ -54,10 +54,10 @@ import { V1_createRelationTypeColumn, } from '@finos/legend-graph'; import { - type DataCubeQuerySnapshotFilterCondition, - type DataCubeQuerySnapshotFilter, - type DataCubeQuerySnapshot, -} from './DataCubeQuerySnapshot.js'; + type DataCubeSnapshotFilterCondition, + type DataCubeSnapshotFilter, + type DataCubeSnapshot, +} from './DataCubeSnapshot.js'; import { _findCol, type DataCubeColumn } from './model/DataCubeColumn.js'; import { guaranteeNonNullable, @@ -402,7 +402,7 @@ export function _aggCol_base( export function _pivotAggCols( pivotColumns: DataCubeColumn[], - snapshot: DataCubeQuerySnapshot, + snapshot: DataCubeSnapshot, configuration: DataCubeConfiguration, aggregateOperations: DataCubeQueryAggregateOperation[], ) { @@ -449,7 +449,7 @@ export function _castCols(columns: DataCubeColumn[]) { export function _groupByAggCols( groupByColumns: DataCubeColumn[], - snapshot: DataCubeQuerySnapshot, + snapshot: DataCubeSnapshot, configuration: DataCubeConfiguration, aggregateOperations: DataCubeQueryAggregateOperation[], ) { @@ -544,7 +544,7 @@ export function _groupByAggCols( } export function _filter( - filter: DataCubeQuerySnapshotFilter | DataCubeQuerySnapshotFilterCondition, + filter: DataCubeSnapshotFilter | DataCubeSnapshotFilterCondition, filterOperations: DataCubeQueryFilterOperation[], ) { if ('groupOperator' in filter) { diff --git a/packages/legend-data-cube/src/stores/core/DataCubeQuerySnapshot.ts b/packages/legend-data-cube/src/stores/core/DataCubeSnapshot.ts similarity index 70% rename from packages/legend-data-cube/src/stores/core/DataCubeQuerySnapshot.ts rename to packages/legend-data-cube/src/stores/core/DataCubeSnapshot.ts index ff4c22b883..fa54e5f24f 100644 --- a/packages/legend-data-cube/src/stores/core/DataCubeQuerySnapshot.ts +++ b/packages/legend-data-cube/src/stores/core/DataCubeSnapshot.ts @@ -30,69 +30,66 @@ import type { } from './DataCubeQueryEngine.js'; import type { DataCubeColumn } from './model/DataCubeColumn.js'; -export type DataCubeQuerySnapshotFilterCondition = DataCubeColumn & { +export type DataCubeSnapshotFilterCondition = DataCubeColumn & { value: DataCubeOperationValue; operator: string; not?: boolean | undefined; }; -export type DataCubeQuerySnapshotFilter = { +export type DataCubeSnapshotFilter = { groupOperator: string; - conditions: ( - | DataCubeQuerySnapshotFilterCondition - | DataCubeQuerySnapshotFilter - )[]; + conditions: (DataCubeSnapshotFilterCondition | DataCubeSnapshotFilter)[]; not?: boolean | undefined; }; -export type DataCubeQuerySnapshotExtendedColumn = DataCubeColumn & { +export type DataCubeSnapshotExtendedColumn = DataCubeColumn & { windowFn?: PlainObject | undefined; mapFn: PlainObject; reduceFn?: PlainObject | undefined; }; -export type DataCubeQuerySnapshotAggregateColumn = DataCubeColumn & { +export type DataCubeSnapshotAggregateColumn = DataCubeColumn & { parameterValues: DataCubeOperationValue[]; operator: string; }; -export type DataCubeQuerySnapshotSortColumn = DataCubeColumn & { +export type DataCubeSnapshotSortColumn = DataCubeColumn & { direction: DataCubeQuerySortDirection; }; -export type DataCubeQuerySnapshotGroupBy = { +export type DataCubeSnapshotGroupBy = { columns: DataCubeColumn[]; }; -export type DataCubeQuerySnapshotPivot = { +export type DataCubeSnapshotPivot = { columns: DataCubeColumn[]; castColumns: DataCubeColumn[]; }; -export type DataCubeQuerySnapshotProcessingContext = { - snapshot: DataCubeQuerySnapshot; - pivotAggColumns?: DataCubeQuerySnapshotAggregateColumn[] | undefined; - pivotSortColumns?: DataCubeQuerySnapshotSortColumn[] | undefined; - groupByAggColumns?: DataCubeQuerySnapshotAggregateColumn[] | undefined; - groupBySortColumns?: DataCubeQuerySnapshotSortColumn[] | undefined; +export type DataCubeSnapshotProcessingContext = { + snapshot: DataCubeSnapshot; + pivotAggColumns?: DataCubeSnapshotAggregateColumn[] | undefined; + pivotSortColumns?: DataCubeSnapshotSortColumn[] | undefined; + groupByAggColumns?: DataCubeSnapshotAggregateColumn[] | undefined; + groupBySortColumns?: DataCubeSnapshotSortColumn[] | undefined; }; -export type DataCubeQuerySnapshotData = { +export type DataCubeSnapshotData = { configuration: PlainObject; sourceColumns: DataCubeColumn[]; - leafExtendedColumns: DataCubeQuerySnapshotExtendedColumn[]; - filter?: DataCubeQuerySnapshotFilter | undefined; + leafExtendedColumns: DataCubeSnapshotExtendedColumn[]; + filter?: DataCubeSnapshotFilter | undefined; selectColumns: DataCubeColumn[]; - groupBy?: DataCubeQuerySnapshotGroupBy | undefined; - pivot?: DataCubeQuerySnapshotPivot | undefined; - groupExtendedColumns: DataCubeQuerySnapshotExtendedColumn[]; - sortColumns: DataCubeQuerySnapshotSortColumn[]; + groupBy?: DataCubeSnapshotGroupBy | undefined; + pivot?: DataCubeSnapshotPivot | undefined; + groupExtendedColumns: DataCubeSnapshotExtendedColumn[]; + sortColumns: DataCubeSnapshotSortColumn[]; limit: number | undefined; }; -export class DataCubeQuerySnapshot { +export class DataCubeSnapshot { readonly uuid = uuid(); - readonly data: DataCubeQuerySnapshotData; + readonly data: DataCubeSnapshotData; private _isPatchChange = false; private _finalized = false; @@ -114,7 +111,7 @@ export class DataCubeQuerySnapshot { } static create(configuration: PlainObject) { - return new DataCubeQuerySnapshot(configuration); + return new DataCubeSnapshot(configuration); } /** @@ -163,10 +160,10 @@ export class DataCubeQuerySnapshot { } clone() { - const clone = new DataCubeQuerySnapshot({}); - (clone.data as Writable) = JSON.parse( + const clone = new DataCubeSnapshot({}); + (clone.data as Writable) = JSON.parse( JSON.stringify(this.data), - ) as DataCubeQuerySnapshotData; + ) as DataCubeSnapshotData; return clone; } @@ -175,11 +172,11 @@ export class DataCubeQuerySnapshot { * This should rarely be used, and ideally by core engine only. */ INTERNAL__fullClone() { - const clone = new DataCubeQuerySnapshot({}); - (clone as Writable).uuid = this.uuid; - (clone as Writable).data = JSON.parse( + const clone = new DataCubeSnapshot({}); + (clone as Writable).uuid = this.uuid; + (clone as Writable).data = JSON.parse( JSON.stringify(this.data), - ) as DataCubeQuerySnapshotData; + ) as DataCubeSnapshotData; clone._isPatchChange = this._isPatchChange; clone._finalized = this._finalized; clone._hashCode = this._hashCode; diff --git a/packages/legend-data-cube/src/stores/core/DataCubeQuerySnapshotBuilder.ts b/packages/legend-data-cube/src/stores/core/DataCubeSnapshotBuilder.ts similarity index 96% rename from packages/legend-data-cube/src/stores/core/DataCubeQuerySnapshotBuilder.ts rename to packages/legend-data-cube/src/stores/core/DataCubeSnapshotBuilder.ts index 0734e02ca9..cbfd4495b8 100644 --- a/packages/legend-data-cube/src/stores/core/DataCubeQuerySnapshotBuilder.ts +++ b/packages/legend-data-cube/src/stores/core/DataCubeSnapshotBuilder.ts @@ -17,9 +17,9 @@ /*************************************************************************************** * [CORE] * - * This and its corresponding utilitites are used to build the query snapshot from the - * executable query. This is needed when we initialize the engine by loading a - * persisted query. + * This and its corresponding utilitites are used to build the snapshot from the + * executable query and configuration. This is needed when we initialize the engine by + * loading a specification. ***************************************************************************************/ import { @@ -30,13 +30,13 @@ import { type V1_ValueSpecification, V1_Lambda, } from '@finos/legend-graph'; -import type { DataCubeQuery } from './model/DataCubeQuery.js'; +import type { DataCubeSpecification } from './model/DataCubeSpecification.js'; import { - DataCubeQuerySnapshot, - type DataCubeQuerySnapshotAggregateColumn, - type DataCubeQuerySnapshotProcessingContext, - type DataCubeQuerySnapshotSortColumn, -} from './DataCubeQuerySnapshot.js'; + DataCubeSnapshot, + type DataCubeSnapshotAggregateColumn, + type DataCubeSnapshotProcessingContext, + type DataCubeSnapshotSortColumn, +} from './DataCubeSnapshot.js'; import { _findCol, _toCol, @@ -70,7 +70,7 @@ import { _validatePivot, _checkDuplicateColumns, _validateGroupBy, -} from './DataCubeQuerySnapshotBuilderUtils.js'; +} from './DataCubeSnapshotBuilderUtils.js'; import type { DataCubeSource } from './model/DataCubeSource.js'; import type { DataCubeEngine } from './DataCubeEngine.js'; @@ -345,15 +345,15 @@ function extractFunctionMap( // --------------------------------- MAIN --------------------------------- /** - * Analyze the partial query to build a query snapshot. + * Analyze the partial query to build a snapshot. * * Implementation-wise, this extracts the function call sequence, then walk the * sequence in order to fill in the information for the snapshot. */ -export async function validateAndBuildQuerySnapshot( +export async function validateAndBuildSnapshot( partialQuery: V1_ValueSpecification, source: DataCubeSource, - baseQuery: DataCubeQuery, + specification: DataCubeSpecification, engine: DataCubeEngine, ) { // --------------------------------- BASE --------------------------------- @@ -365,7 +365,7 @@ export async function validateAndBuildQuerySnapshot( engine.serializeValueSpecification(partialQuery), ); const funcMap = extractFunctionMap(query); - const snapshot = DataCubeQuerySnapshot.create({}); + const snapshot = DataCubeSnapshot.create({}); const data = snapshot.data; const registeredColumns = new Map(); /** @@ -476,8 +476,8 @@ export async function validateAndBuildQuerySnapshot( // --------------------------------- PIVOT --------------------------------- - let pivotAggColumns: DataCubeQuerySnapshotAggregateColumn[] = []; - let pivotSortColumns: DataCubeQuerySnapshotSortColumn[] = []; + let pivotAggColumns: DataCubeSnapshotAggregateColumn[] = []; + let pivotSortColumns: DataCubeSnapshotSortColumn[] = []; if (funcMap.pivot && funcMap.pivotCast && funcMap.pivotSort) { const pivotColumns = _colSpecArrayParam(funcMap.pivot, 0).colSpecs.map( (colSpec) => _getCol(colSpec.name), @@ -510,8 +510,8 @@ export async function validateAndBuildQuerySnapshot( // --------------------------------- GROUP BY --------------------------------- - let groupByAggColumns: DataCubeQuerySnapshotAggregateColumn[] = []; - let groupBySortColumns: DataCubeQuerySnapshotSortColumn[] = []; + let groupByAggColumns: DataCubeSnapshotAggregateColumn[] = []; + let groupBySortColumns: DataCubeSnapshotSortColumn[] = []; if (funcMap.groupBy && funcMap.groupBySort) { const groupByColumns = _colSpecArrayParam(funcMap.groupBy, 0).colSpecs.map( (colSpec) => _getCol(colSpec.name), @@ -605,7 +605,7 @@ export async function validateAndBuildQuerySnapshot( pivotAggColumns, pivotSortColumns, }, - baseQuery, + specification, engine, ); data.configuration = configuration.serialize(); @@ -642,12 +642,12 @@ export async function validateAndBuildQuerySnapshot( * configuration is not specified. */ function validateAndBuildConfiguration( - context: DataCubeQuerySnapshotProcessingContext, - baseQuery: DataCubeQuery, + context: DataCubeSnapshotProcessingContext, + specification: DataCubeSpecification, engine: DataCubeEngine, ) { const data = context.snapshot.data; - const config = baseQuery.configuration; + const config = specification.configuration; // generate a default configuration anyway to be used to compare with the // provided configuration for validation purpose const _config = newConfiguration(context); diff --git a/packages/legend-data-cube/src/stores/core/DataCubeQuerySnapshotBuilderUtils.ts b/packages/legend-data-cube/src/stores/core/DataCubeSnapshotBuilderUtils.ts similarity index 93% rename from packages/legend-data-cube/src/stores/core/DataCubeQuerySnapshotBuilderUtils.ts rename to packages/legend-data-cube/src/stores/core/DataCubeSnapshotBuilderUtils.ts index 8ae92e33c2..4d3810284a 100644 --- a/packages/legend-data-cube/src/stores/core/DataCubeQuerySnapshotBuilderUtils.ts +++ b/packages/legend-data-cube/src/stores/core/DataCubeSnapshotBuilderUtils.ts @@ -66,12 +66,12 @@ import { type DataCubeOperationValue, } from './DataCubeQueryEngine.js'; import type { - DataCubeQuerySnapshotAggregateColumn, - DataCubeQuerySnapshotFilter, - DataCubeQuerySnapshotFilterCondition, - DataCubeQuerySnapshotGroupBy, - DataCubeQuerySnapshotPivot, -} from './DataCubeQuerySnapshot.js'; + DataCubeSnapshotAggregateColumn, + DataCubeSnapshotFilter, + DataCubeSnapshotFilterCondition, + DataCubeSnapshotGroupBy, + DataCubeSnapshotPivot, +} from './DataCubeSnapshot.js'; import type { DataCubeQueryFilterOperation } from './filter/DataCubeQueryFilterOperation.js'; import type { DataCubeEngine } from './DataCubeEngine.js'; import { @@ -375,14 +375,14 @@ export function _filter( value: V1_ValueSpecification, columnGetter: (name: string) => DataCubeColumn, filterOperations: DataCubeQueryFilterOperation[], -): DataCubeQuerySnapshotFilter { +): DataCubeSnapshotFilter { if (!(value instanceof V1_AppliedFunction)) { throw new Error( `Can't process filter() expression: expected a function expression`, ); } - const group: DataCubeQuerySnapshotFilter = { + const group: DataCubeSnapshotFilter = { // default to AND group for case where there is only one condition groupOperator: DataCubeQueryFilterGroupOperator.AND, conditions: [], @@ -426,7 +426,7 @@ function _filterCondition( value: V1_ValueSpecification, columnGetter: (name: string) => DataCubeColumn, filterOperations: DataCubeQueryFilterOperation[], -): DataCubeQuerySnapshotFilterCondition | DataCubeQuerySnapshotFilter { +): DataCubeSnapshotFilterCondition | DataCubeSnapshotFilter { if (!(value instanceof V1_AppliedFunction)) { throw new UnsupportedOperationError( `Can't process filter condition expression: expected a function expression`, @@ -707,8 +707,8 @@ export function _pivotSort( } export function _validatePivot( - pivot: DataCubeQuerySnapshotPivot, - pivotAggColumns: DataCubeQuerySnapshotAggregateColumn[], + pivot: DataCubeSnapshotPivot, + pivotAggColumns: DataCubeSnapshotAggregateColumn[], availableColumns: DataCubeColumn[], ) { // check for duplicate columns @@ -834,10 +834,10 @@ export function _groupBySort( } export function _validateGroupBy( - groupBy: DataCubeQuerySnapshotGroupBy, - groupByAggColumns: DataCubeQuerySnapshotAggregateColumn[], - pivot: DataCubeQuerySnapshotPivot | undefined, - pivotAggColumns: DataCubeQuerySnapshotAggregateColumn[], + groupBy: DataCubeSnapshotGroupBy, + groupByAggColumns: DataCubeSnapshotAggregateColumn[], + pivot: DataCubeSnapshotPivot | undefined, + pivotAggColumns: DataCubeSnapshotAggregateColumn[], availableColumns: DataCubeColumn[], ) { // check for duplicate columns @@ -862,7 +862,7 @@ export function _validateGroupBy( } }); - // check all available columns are either grouped on or aggregatd on + // check all available columns are either grouped on or aggregated on availableColumns.forEach((col) => { if ( !( @@ -878,10 +878,12 @@ export function _validateGroupBy( // check against pivot if present if (pivot) { - const aggCols = new Map(); + const aggCols = new Map(); - // check if aggregation specification is consistent (i.e. same type, operator, parameterValues) + // check if aggregation specification is consistent (i.e. same name, operator, parameterValues) // between groupBy aggregate columns + // NOTE: we should not check type here as it can change dynamically due to aggregation, e.g. + // an average aggregation on an integer-value column will result in a float-value column groupByAggColumns .filter((col) => isPivotResultColumnName(col.name)) .forEach((col) => { @@ -895,25 +897,37 @@ export function _validateGroupBy( if (!existingAggCol) { aggCols.set(aggColName, aggCol); - } else if (!deepEqual(existingAggCol, aggCol)) { + } else if ( + // type should not be compared here as it can change dynamically due to aggregation + !deepEqual( + { ...existingAggCol, type: undefined }, + { ...aggCol, type: undefined }, + ) + ) { throw new Error( `Can't process groupBy() expression: found conflicting aggregation specification for column '${aggColName}'`, ); } }); - // check if pivot() aggregate columns are consistent with groupBy() aggregate columns - pivotAggColumns.forEach((col) => { - const existingAggCol = aggCols.get(col.name); + // check if groupBy() aggregate columns are consistent with pivot() aggregate columns + pivotAggColumns.forEach((pivotAggCol) => { + const existingAggCol = aggCols.get(pivotAggCol.name); if (!existingAggCol) { throw new Error( - `Can't process groupBy() expression: column '${col.name}' is aggregated in pivot() expression but not in groupBy() expression`, + `Can't process groupBy() expression: column '${pivotAggCol.name}' is aggregated in pivot() expression but not in groupBy() expression`, ); } - if (!deepEqual(existingAggCol, col)) { + if ( + // type should not be compared here as it can change dynamically due to aggregation + !deepEqual( + { ...existingAggCol, type: undefined }, + { ...pivotAggCol, type: undefined }, + ) + ) { throw new Error( - `Can't process groupBy() expression: found conflicting aggregation specification for column '${col.name}'`, + `Can't process groupBy() expression: found conflicting aggregation specification for column '${pivotAggCol.name}'`, ); } }); diff --git a/packages/legend-data-cube/src/stores/core/__tests__/DataCubeQueryRoundtrip.data-cube-test.ts b/packages/legend-data-cube/src/stores/core/__tests__/DataCubeQueryRoundtrip.data-cube-test.ts index e2b4ddd3be..c256e19873 100644 --- a/packages/legend-data-cube/src/stores/core/__tests__/DataCubeQueryRoundtrip.data-cube-test.ts +++ b/packages/legend-data-cube/src/stores/core/__tests__/DataCubeQueryRoundtrip.data-cube-test.ts @@ -16,24 +16,24 @@ import { integrationTest } from '@finos/legend-shared/test'; import { describe, expect, test } from '@jest/globals'; -import { validateAndBuildQuerySnapshot } from '../DataCubeQuerySnapshotBuilder.js'; +import { validateAndBuildSnapshot } from '../DataCubeSnapshotBuilder.js'; import { assertErrorThrown, at, guaranteeNonNullable, isString, } from '@finos/legend-shared'; -import { DataCubeQuery } from '../model/DataCubeQuery.js'; +import { DataCubeSpecification } from '../model/DataCubeSpecification.js'; import { INTERNAL__DataCubeSource } from '../model/DataCubeSource.js'; import { DataCubeColumnConfiguration, DataCubeConfiguration, } from '../model/DataCubeConfiguration.js'; -import { TEST__DataCubeEngine } from './DataCubeTestUtils.js'; +import { TEST__DataCubeEngine } from '../../__tests__/DataCubeTestUtils.js'; import type { - DataCubeQuerySnapshot, - DataCubeQuerySnapshotFilterCondition, -} from '../DataCubeQuerySnapshot.js'; + DataCubeSnapshot, + DataCubeSnapshotFilterCondition, +} from '../DataCubeSnapshot.js'; import { DataCubeColumnKind, DataCubeOperationAdvancedValueType, @@ -1092,6 +1092,13 @@ const cases: TestCase[] = [ query: `select(~[a, b, c, d])->sort([~c->ascending()])->pivot(~[c], ~[b:x|$x.b:x|$x->sum()])->cast(@meta::pure::metamodel::relation::Relation<(d:String, a:Integer, 'val1__|__b':Integer)>)->groupBy(~[d], ~['val1__|__b':x|$x.'val1__|__b':x|$x->sum(), a:x|$x.a:x|$x->sum()])->sort([~d->ascending()])`, columns: ['a:Integer', 'c:String', 'b:Integer', 'd:String'], }), + _case( + `GroupBy: following pivot() expression where aggregation changes column type`, + { + query: `select(~[a, b, c, d])->sort([~c->ascending()])->pivot(~[c], ~[b:x|$x.b:x|$x->sum()])->cast(@meta::pure::metamodel::relation::Relation<(d:String, a:Integer, 'val1__|__b':Float)>)->groupBy(~[d], ~['val1__|__b':x|$x.'val1__|__b':x|$x->sum(), a:x|$x.a:x|$x->sum()])->sort([~d->ascending()])`, + columns: ['a:Integer', 'c:String', 'b:Integer', 'd:String'], + }, + ), _case(`GroupBy: ERROR - group column not found`, { query: `select(~[b])->groupBy(~[a], ~[b:x|$x.b:x|$x->sum()])->sort([~a->ascending()])`, columns: ['a:String', 'b:Integer'], @@ -1949,16 +1956,16 @@ describe(integrationTest('Roundtrip query processing'), () => { ) => { const engine = new TEST__DataCubeEngine(); const query = await engine.parseValueSpecification(code); - const baseQuery = new DataCubeQuery(); + const baseQuery = new DataCubeSpecification(); const source = new INTERNAL__DataCubeSource(); source.columns = columns; baseQuery.configuration = configurationBuilder ? await configurationBuilder(query, columns) : undefined; - let snapshot: DataCubeQuerySnapshot | undefined; + let snapshot: DataCubeSnapshot | undefined; try { - snapshot = await validateAndBuildQuerySnapshot( + snapshot = await validateAndBuildSnapshot( query, source, baseQuery, @@ -1992,7 +1999,7 @@ type TestCase = [ | undefined ), // configuration builder string | undefined, // error - ((snapshot: DataCubeQuerySnapshot) => void) | undefined, // extra checks on snapshot + ((snapshot: DataCubeSnapshot) => void) | undefined, // extra checks on snapshot ]; function _case( @@ -2007,7 +2014,7 @@ function _case( ) => Promise) | undefined; error?: string | undefined; - validator?: ((snapshot: DataCubeQuerySnapshot) => void) | undefined; + validator?: ((snapshot: DataCubeSnapshot) => void) | undefined; }, ): TestCase { return [ @@ -2027,12 +2034,10 @@ function _case( } function _checkFilterOperator(operator: DataCubeQueryFilterOperator) { - return (snapshot: DataCubeQuerySnapshot) => { + return (snapshot: DataCubeSnapshot) => { expect( - ( - snapshot.data.filter - ?.conditions[0] as DataCubeQuerySnapshotFilterCondition - ).operator, + (snapshot.data.filter?.conditions[0] as DataCubeSnapshotFilterCondition) + .operator, ).toBe(operator); }; } @@ -2050,13 +2055,13 @@ async function _generateDefaultConfiguration( columns: DataCubeColumn[], ) { const engine = new TEST__DataCubeEngine(); - const baseQuery = new DataCubeQuery(); + const baseQuery = new DataCubeSpecification(); const source = new INTERNAL__DataCubeSource(); source.columns = columns; baseQuery.configuration = undefined; try { - const snapshot = await validateAndBuildQuerySnapshot( + const snapshot = await validateAndBuildSnapshot( query, source, baseQuery, diff --git a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation.ts b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation.ts index bf98b3c12c..84cb113be5 100644 --- a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation.ts +++ b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation.ts @@ -18,7 +18,7 @@ import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { type V1_ColSpec } from '@finos/legend-graph'; import type { DataCubeColumnConfiguration } from '../model/DataCubeConfiguration.js'; import type { DataCubeOperationValue } from '../DataCubeQueryEngine.js'; -import type { DataCubeQuerySnapshotAggregateColumn } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotAggregateColumn } from '../DataCubeSnapshot.js'; export abstract class DataCubeQueryAggregateOperation { abstract get label(): React.ReactNode; @@ -37,7 +37,7 @@ export abstract class DataCubeQueryAggregateOperation { abstract buildAggregateColumnSnapshot( colSpec: V1_ColSpec, columnGetter: (name: string) => DataCubeColumn, - ): DataCubeQuerySnapshotAggregateColumn | undefined; + ): DataCubeSnapshotAggregateColumn | undefined; protected _finalizeAggregateColumnSnapshot( data: @@ -46,7 +46,7 @@ export abstract class DataCubeQueryAggregateOperation { paramterValues: DataCubeOperationValue[]; } | undefined, - ): DataCubeQuerySnapshotAggregateColumn | undefined { + ): DataCubeSnapshotAggregateColumn | undefined { if (!data) { return undefined; } diff --git a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__Average.tsx b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__Average.tsx index bb76e7442b..875a7e2ea2 100644 --- a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__Average.tsx +++ b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__Average.tsx @@ -26,7 +26,7 @@ import { import { _aggCol_base } from '../DataCubeQueryBuilderUtils.js'; import type { DataCubeColumnConfiguration } from '../model/DataCubeConfiguration.js'; import type { V1_ColSpec } from '@finos/legend-graph'; -import { _agg_base } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _agg_base } from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryAggregateOperation__Average extends DataCubeQueryAggregateOperation { override get label() { diff --git a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__Count.tsx b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__Count.tsx index d0a6e98193..d073bec731 100644 --- a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__Count.tsx +++ b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__Count.tsx @@ -26,7 +26,7 @@ import { import { _aggCol_base } from '../DataCubeQueryBuilderUtils.js'; import type { DataCubeColumnConfiguration } from '../model/DataCubeConfiguration.js'; import type { V1_ColSpec } from '@finos/legend-graph'; -import { _agg_base } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _agg_base } from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryAggregateOperation__Count extends DataCubeQueryAggregateOperation { override get label() { diff --git a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__First.tsx b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__First.tsx index 73d53b0a26..d22ec028fe 100644 --- a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__First.tsx +++ b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__First.tsx @@ -26,7 +26,7 @@ import { import { _aggCol_base } from '../DataCubeQueryBuilderUtils.js'; import type { DataCubeColumnConfiguration } from '../model/DataCubeConfiguration.js'; import type { V1_ColSpec } from '@finos/legend-graph'; -import { _agg_base } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _agg_base } from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryAggregateOperation__First extends DataCubeQueryAggregateOperation { override get label() { diff --git a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__JoinStrings.tsx b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__JoinStrings.tsx index f349e6ba7d..873fe85324 100644 --- a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__JoinStrings.tsx +++ b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__JoinStrings.tsx @@ -26,7 +26,7 @@ import { } from '../DataCubeQueryEngine.js'; import type { DataCubeColumnConfiguration } from '../model/DataCubeConfiguration.js'; import { _aggCol_base } from '../DataCubeQueryBuilderUtils.js'; -import { _agg_base } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _agg_base } from '../DataCubeSnapshotBuilderUtils.js'; import { isString } from '@finos/legend-shared'; export class DataCubeQueryAggregateOperation__JoinStrings extends DataCubeQueryAggregateOperation { diff --git a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__Last.tsx b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__Last.tsx index ebc80858e5..2d636b79f7 100644 --- a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__Last.tsx +++ b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__Last.tsx @@ -26,7 +26,7 @@ import { import { _aggCol_base } from '../DataCubeQueryBuilderUtils.js'; import type { DataCubeColumnConfiguration } from '../model/DataCubeConfiguration.js'; import type { V1_ColSpec } from '@finos/legend-graph'; -import { _agg_base } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _agg_base } from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryAggregateOperation__Last extends DataCubeQueryAggregateOperation { override get label() { diff --git a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__Max.tsx b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__Max.tsx index 941cf83b71..5a3bfc8563 100644 --- a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__Max.tsx +++ b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__Max.tsx @@ -26,7 +26,7 @@ import { import { _aggCol_base } from '../DataCubeQueryBuilderUtils.js'; import type { DataCubeColumnConfiguration } from '../model/DataCubeConfiguration.js'; import type { V1_ColSpec } from '@finos/legend-graph'; -import { _agg_base } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _agg_base } from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryAggregateOperation__Max extends DataCubeQueryAggregateOperation { override get label() { diff --git a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__Min.tsx b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__Min.tsx index 450773ac77..9e4dcbb41f 100644 --- a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__Min.tsx +++ b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__Min.tsx @@ -26,7 +26,7 @@ import { import { _aggCol_base } from '../DataCubeQueryBuilderUtils.js'; import type { DataCubeColumnConfiguration } from '../model/DataCubeConfiguration.js'; import type { V1_ColSpec } from '@finos/legend-graph'; -import { _agg_base } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _agg_base } from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryAggregateOperation__Min extends DataCubeQueryAggregateOperation { override get label() { diff --git a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__StdDevPopulation.tsx b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__StdDevPopulation.tsx index 83dcbb6938..0613e50808 100644 --- a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__StdDevPopulation.tsx +++ b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__StdDevPopulation.tsx @@ -26,7 +26,7 @@ import { import { _aggCol_base } from '../DataCubeQueryBuilderUtils.js'; import type { DataCubeColumnConfiguration } from '../model/DataCubeConfiguration.js'; import type { V1_ColSpec } from '@finos/legend-graph'; -import { _agg_base } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _agg_base } from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryAggregateOperation__StdDevPopulation extends DataCubeQueryAggregateOperation { override get label() { diff --git a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__StdDevSample.tsx b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__StdDevSample.tsx index 62ce35c05d..549209439f 100644 --- a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__StdDevSample.tsx +++ b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__StdDevSample.tsx @@ -26,7 +26,7 @@ import { import { _aggCol_base } from '../DataCubeQueryBuilderUtils.js'; import type { DataCubeColumnConfiguration } from '../model/DataCubeConfiguration.js'; import type { V1_ColSpec } from '@finos/legend-graph'; -import { _agg_base } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _agg_base } from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryAggregateOperation__StdDevSample extends DataCubeQueryAggregateOperation { override get label() { diff --git a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__Sum.tsx b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__Sum.tsx index f39d3659d3..aa5b712c07 100644 --- a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__Sum.tsx +++ b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__Sum.tsx @@ -26,7 +26,7 @@ import { import { _aggCol_base } from '../DataCubeQueryBuilderUtils.js'; import type { DataCubeColumnConfiguration } from '../model/DataCubeConfiguration.js'; import { type V1_ColSpec } from '@finos/legend-graph'; -import { _agg_base } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _agg_base } from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryAggregateOperation__Sum extends DataCubeQueryAggregateOperation { override get label() { diff --git a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__UniqueValue.tsx b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__UniqueValue.tsx index bdb9ddb683..63da30d58e 100644 --- a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__UniqueValue.tsx +++ b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__UniqueValue.tsx @@ -26,7 +26,7 @@ import { import { _aggCol_base } from '../DataCubeQueryBuilderUtils.js'; import type { DataCubeColumnConfiguration } from '../model/DataCubeConfiguration.js'; import type { V1_ColSpec } from '@finos/legend-graph'; -import { _agg_base } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _agg_base } from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryAggregateOperation__UniqueValue extends DataCubeQueryAggregateOperation { override get label() { diff --git a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__VariancePopulation.tsx b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__VariancePopulation.tsx index 7ac2851e3c..4caee5ff0d 100644 --- a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__VariancePopulation.tsx +++ b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__VariancePopulation.tsx @@ -26,7 +26,7 @@ import { import { _aggCol_base } from '../DataCubeQueryBuilderUtils.js'; import type { DataCubeColumnConfiguration } from '../model/DataCubeConfiguration.js'; import type { V1_ColSpec } from '@finos/legend-graph'; -import { _agg_base } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _agg_base } from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryAggregateOperation__VariancePopulation extends DataCubeQueryAggregateOperation { override get label() { diff --git a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__VarianceSample.tsx b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__VarianceSample.tsx index 8bc598cb3d..118b6aaea3 100644 --- a/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__VarianceSample.tsx +++ b/packages/legend-data-cube/src/stores/core/aggregation/DataCubeQueryAggregateOperation__VarianceSample.tsx @@ -26,7 +26,7 @@ import { import { _aggCol_base } from '../DataCubeQueryBuilderUtils.js'; import type { DataCubeColumnConfiguration } from '../model/DataCubeConfiguration.js'; import type { V1_ColSpec } from '@finos/legend-graph'; -import { _agg_base } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _agg_base } from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryAggregateOperation__VarianceSample extends DataCubeQueryAggregateOperation { override get label() { diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterEditorState.ts b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterEditorState.ts index d11faa2315..6f47cc8e12 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterEditorState.ts +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterEditorState.ts @@ -25,9 +25,9 @@ import { DataCubeQueryFilterGroupOperator, } from '../DataCubeQueryEngine.js'; import type { - DataCubeQuerySnapshotFilter, - DataCubeQuerySnapshotFilterCondition, -} from '../DataCubeQuerySnapshot.js'; + DataCubeSnapshotFilter, + DataCubeSnapshotFilterCondition, +} from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import type { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; @@ -158,9 +158,9 @@ export class DataCubeFilterEditorConditionGroupTreeNode extends DataCubeFilterEd } } -export function buildFilterQuerySnapshot( +export function buildFilterSnapshot( node: DataCubeFilterEditorConditionGroupTreeNode, -): DataCubeQuerySnapshotFilter { +): DataCubeSnapshotFilter { return { groupOperator: node.operation, not: node.not, @@ -172,11 +172,11 @@ export function buildFilterQuerySnapshot( operator: childNode.operation.operator, value: deepClone(childNode.value), not: childNode.not, - } satisfies DataCubeQuerySnapshotFilterCondition; + } satisfies DataCubeSnapshotFilterCondition; } else if ( childNode instanceof DataCubeFilterEditorConditionGroupTreeNode ) { - return buildFilterQuerySnapshot(childNode); + return buildFilterSnapshot(childNode); } throw new IllegalStateError('Unknown filter node'); }), @@ -184,7 +184,7 @@ export function buildFilterQuerySnapshot( } export function buildFilterEditorTree( - _node: DataCubeQuerySnapshotFilter, + _node: DataCubeSnapshotFilter, parent: DataCubeFilterEditorConditionGroupTreeNode | undefined, nodes: Map, operationGetter: (operation: string) => DataCubeQueryFilterOperation, diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation.ts b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation.ts index 94a25b35c3..9e8a62ba1d 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation.ts +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation.ts @@ -15,7 +15,7 @@ */ import type { DataCubeOperationValue } from '../DataCubeQueryEngine.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { type V1_AppliedFunction } from '@finos/legend-graph'; @@ -32,7 +32,7 @@ export abstract class DataCubeQueryFilterOperation { abstract buildConditionSnapshot( expression: V1_AppliedFunction, columnGetter: (name: string) => DataCubeColumn, - ): DataCubeQuerySnapshotFilterCondition | undefined; + ): DataCubeSnapshotFilterCondition | undefined; protected _finalizeConditionSnapshot( data: @@ -41,7 +41,7 @@ export abstract class DataCubeQueryFilterOperation { value: DataCubeOperationValue; } | undefined, - ): DataCubeQuerySnapshotFilterCondition | undefined { + ): DataCubeSnapshotFilterCondition | undefined { if (!data) { return undefined; } @@ -60,6 +60,6 @@ export abstract class DataCubeQueryFilterOperation { } abstract buildConditionExpression( - condition: DataCubeQuerySnapshotFilterCondition, + condition: DataCubeSnapshotFilterCondition, ): V1_AppliedFunction | undefined; } diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__Contain.tsx b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__Contain.tsx index ab5086385f..7a265ca567 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__Contain.tsx +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__Contain.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { DataCubeColumnDataType, @@ -32,7 +32,7 @@ import { _value, } from '../DataCubeQueryBuilderUtils.js'; import { type V1_AppliedFunction } from '@finos/legend-graph'; -import { _filterCondition_base } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _filterCondition_base } from '../DataCubeSnapshotBuilderUtils.js'; import { isString } from '@finos/legend-shared'; export class DataCubeQueryFilterOperation__Contain extends DataCubeQueryFilterOperation { @@ -86,7 +86,7 @@ export class DataCubeQueryFilterOperation__Contain extends DataCubeQueryFilterOp ); } - buildConditionExpression(condition: DataCubeQuerySnapshotFilterCondition) { + buildConditionExpression(condition: DataCubeSnapshotFilterCondition) { return _function(_functionName(DataCubeFunction.CONTAINS), [ _property(condition.name), _value(condition.value), diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__ContainCaseInsensitive.tsx b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__ContainCaseInsensitive.tsx index 0e54b794e1..4730e68ba2 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__ContainCaseInsensitive.tsx +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__ContainCaseInsensitive.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { DataCubeColumnDataType, @@ -32,7 +32,7 @@ import { _value, } from '../DataCubeQueryBuilderUtils.js'; import { type V1_AppliedFunction } from '@finos/legend-graph'; -import { _filterCondition_caseSensitive } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _filterCondition_caseSensitive } from '../DataCubeSnapshotBuilderUtils.js'; import { isString } from '@finos/legend-shared'; export class DataCubeQueryFilterOperation__ContainCaseInsensitive extends DataCubeQueryFilterOperation { @@ -86,7 +86,7 @@ export class DataCubeQueryFilterOperation__ContainCaseInsensitive extends DataCu ); } - buildConditionExpression(condition: DataCubeQuerySnapshotFilterCondition) { + buildConditionExpression(condition: DataCubeSnapshotFilterCondition) { return _function(_functionName(DataCubeFunction.CONTAINS), [ _function(_functionName(DataCubeFunction.TO_LOWERCASE), [ _property(condition.name), diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__EndWith.tsx b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__EndWith.tsx index e045034e0b..f1b9f741b2 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__EndWith.tsx +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__EndWith.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { DataCubeColumnDataType, @@ -32,7 +32,7 @@ import { _value, } from '../DataCubeQueryBuilderUtils.js'; import { type V1_AppliedFunction } from '@finos/legend-graph'; -import { _filterCondition_base } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _filterCondition_base } from '../DataCubeSnapshotBuilderUtils.js'; import { isString } from '@finos/legend-shared'; export class DataCubeQueryFilterOperation__EndWith extends DataCubeQueryFilterOperation { @@ -86,7 +86,7 @@ export class DataCubeQueryFilterOperation__EndWith extends DataCubeQueryFilterOp ); } - buildConditionExpression(condition: DataCubeQuerySnapshotFilterCondition) { + buildConditionExpression(condition: DataCubeSnapshotFilterCondition) { return _function(_functionName(DataCubeFunction.ENDS_WITH), [ _property(condition.name), _value(condition.value), diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__EndWithCaseInsensitive.tsx b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__EndWithCaseInsensitive.tsx index 1ca94eb2de..fc34976e26 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__EndWithCaseInsensitive.tsx +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__EndWithCaseInsensitive.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { DataCubeColumnDataType, @@ -32,7 +32,7 @@ import { _value, } from '../DataCubeQueryBuilderUtils.js'; import { type V1_AppliedFunction } from '@finos/legend-graph'; -import { _filterCondition_caseSensitive } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _filterCondition_caseSensitive } from '../DataCubeSnapshotBuilderUtils.js'; import { isString } from '@finos/legend-shared'; export class DataCubeQueryFilterOperation__EndWithCaseInsensitive extends DataCubeQueryFilterOperation { @@ -86,7 +86,7 @@ export class DataCubeQueryFilterOperation__EndWithCaseInsensitive extends DataCu ); } - buildConditionExpression(condition: DataCubeQuerySnapshotFilterCondition) { + buildConditionExpression(condition: DataCubeSnapshotFilterCondition) { return _function(_functionName(DataCubeFunction.ENDS_WITH), [ _function(_functionName(DataCubeFunction.TO_LOWERCASE), [ _property(condition.name), diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__Equal.tsx b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__Equal.tsx index 599b3c85d5..5d61b62028 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__Equal.tsx +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__Equal.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { DataCubeColumnDataType, @@ -32,7 +32,7 @@ import { _value, } from '../DataCubeQueryBuilderUtils.js'; import { type V1_AppliedFunction } from '@finos/legend-graph'; -import { _filterCondition_base } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _filterCondition_base } from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryFilterOperation__Equal extends DataCubeQueryFilterOperation { override get label() { @@ -90,7 +90,7 @@ export class DataCubeQueryFilterOperation__Equal extends DataCubeQueryFilterOper ); } - buildConditionExpression(condition: DataCubeQuerySnapshotFilterCondition) { + buildConditionExpression(condition: DataCubeSnapshotFilterCondition) { return _function(_functionName(DataCubeFunction.EQUAL), [ _property(condition.name), _value(condition.value), diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__EqualCaseInsensitive.tsx b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__EqualCaseInsensitive.tsx index 609a7bd7dc..43aec76cf6 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__EqualCaseInsensitive.tsx +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__EqualCaseInsensitive.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { DataCubeColumnDataType, @@ -32,7 +32,7 @@ import { _value, } from '../DataCubeQueryBuilderUtils.js'; import { type V1_AppliedFunction } from '@finos/legend-graph'; -import { _filterCondition_caseSensitive } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _filterCondition_caseSensitive } from '../DataCubeSnapshotBuilderUtils.js'; import { isString } from '@finos/legend-shared'; export class DataCubeQueryFilterOperation__EqualCaseInsensitive extends DataCubeQueryFilterOperation { @@ -86,7 +86,7 @@ export class DataCubeQueryFilterOperation__EqualCaseInsensitive extends DataCube ); } - buildConditionExpression(condition: DataCubeQuerySnapshotFilterCondition) { + buildConditionExpression(condition: DataCubeSnapshotFilterCondition) { return _function(_functionName(DataCubeFunction.EQUAL), [ _function(_functionName(DataCubeFunction.TO_LOWERCASE), [ _property(condition.name), diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__EqualCaseInsensitiveColumn.tsx b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__EqualCaseInsensitiveColumn.tsx index dd2ac0d78a..592cf88407 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__EqualCaseInsensitiveColumn.tsx +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__EqualCaseInsensitiveColumn.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { DataCubeColumnDataType, @@ -32,7 +32,7 @@ import { } from '../DataCubeQueryBuilderUtils.js'; import { isString } from '@finos/legend-shared'; import { type V1_AppliedFunction } from '@finos/legend-graph'; -import { _filterCondition_caseSensitive } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _filterCondition_caseSensitive } from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryFilterOperation__EqualCaseInsensitiveColumn extends DataCubeQueryFilterOperation { override get label() { @@ -83,7 +83,7 @@ export class DataCubeQueryFilterOperation__EqualCaseInsensitiveColumn extends Da ); } - buildConditionExpression(condition: DataCubeQuerySnapshotFilterCondition) { + buildConditionExpression(condition: DataCubeSnapshotFilterCondition) { return _function(_functionName(DataCubeFunction.EQUAL), [ _function(_functionName(DataCubeFunction.TO_LOWERCASE), [ _property(condition.name), diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__EqualColumn.tsx b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__EqualColumn.tsx index fe008b663e..c42f16c346 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__EqualColumn.tsx +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__EqualColumn.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { DataCubeColumnDataType, @@ -32,7 +32,7 @@ import { } from '../DataCubeQueryBuilderUtils.js'; import { isString } from '@finos/legend-shared'; import { type V1_AppliedFunction } from '@finos/legend-graph'; -import { _filterCondition_base } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _filterCondition_base } from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryFilterOperation__EqualColumn extends DataCubeQueryFilterOperation { override get label() { @@ -84,7 +84,7 @@ export class DataCubeQueryFilterOperation__EqualColumn extends DataCubeQueryFilt ); } - buildConditionExpression(condition: DataCubeQuerySnapshotFilterCondition) { + buildConditionExpression(condition: DataCubeSnapshotFilterCondition) { return _function(_functionName(DataCubeFunction.EQUAL), [ _property(condition.name), _value(condition.value), diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__GreaterThan.tsx b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__GreaterThan.tsx index e65dae6bfc..daab8cc3bd 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__GreaterThan.tsx +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__GreaterThan.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { DataCubeColumnDataType, @@ -32,7 +32,7 @@ import { _value, } from '../DataCubeQueryBuilderUtils.js'; import { type V1_AppliedFunction } from '@finos/legend-graph'; -import { _filterCondition_base } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _filterCondition_base } from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryFilterOperation__GreaterThan extends DataCubeQueryFilterOperation { override get label() { @@ -92,7 +92,7 @@ export class DataCubeQueryFilterOperation__GreaterThan extends DataCubeQueryFilt ); } - buildConditionExpression(condition: DataCubeQuerySnapshotFilterCondition) { + buildConditionExpression(condition: DataCubeSnapshotFilterCondition) { return _function(_functionName(DataCubeFunction.GREATER_THAN), [ _property(condition.name), _value(condition.value), diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__GreaterThanColumn.tsx b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__GreaterThanColumn.tsx index 995202f0e7..b4909ab6fd 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__GreaterThanColumn.tsx +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__GreaterThanColumn.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { DataCubeColumnDataType, @@ -32,7 +32,7 @@ import { } from '../DataCubeQueryBuilderUtils.js'; import { isString } from '@finos/legend-shared'; import { type V1_AppliedFunction } from '@finos/legend-graph'; -import { _filterCondition_base } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _filterCondition_base } from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryFilterOperation__GreaterThanColumn extends DataCubeQueryFilterOperation { override get label() { @@ -87,7 +87,7 @@ export class DataCubeQueryFilterOperation__GreaterThanColumn extends DataCubeQue ); } - buildConditionExpression(condition: DataCubeQuerySnapshotFilterCondition) { + buildConditionExpression(condition: DataCubeSnapshotFilterCondition) { return _function(_functionName(DataCubeFunction.GREATER_THAN), [ _property(condition.name), _value(condition.value), diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__GreaterThanOrEqual.tsx b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__GreaterThanOrEqual.tsx index 459ca415c1..8ec5864a05 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__GreaterThanOrEqual.tsx +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__GreaterThanOrEqual.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { DataCubeColumnDataType, @@ -32,7 +32,7 @@ import { _value, } from '../DataCubeQueryBuilderUtils.js'; import { type V1_AppliedFunction } from '@finos/legend-graph'; -import { _filterCondition_base } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _filterCondition_base } from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryFilterOperation__GreaterThanOrEqual extends DataCubeQueryFilterOperation { override get label() { @@ -92,7 +92,7 @@ export class DataCubeQueryFilterOperation__GreaterThanOrEqual extends DataCubeQu ); } - buildConditionExpression(condition: DataCubeQuerySnapshotFilterCondition) { + buildConditionExpression(condition: DataCubeSnapshotFilterCondition) { return _function(_functionName(DataCubeFunction.GREATER_THAN_OR_EQUAL), [ _property(condition.name), _value(condition.value), diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__GreaterThanOrEqualColumn.tsx b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__GreaterThanOrEqualColumn.tsx index f5fbeffcfe..8279abc276 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__GreaterThanOrEqualColumn.tsx +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__GreaterThanOrEqualColumn.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { DataCubeColumnDataType, @@ -32,7 +32,7 @@ import { } from '../DataCubeQueryBuilderUtils.js'; import { isString } from '@finos/legend-shared'; import { type V1_AppliedFunction } from '@finos/legend-graph'; -import { _filterCondition_base } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _filterCondition_base } from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryFilterOperation__GreaterThanOrEqualColumn extends DataCubeQueryFilterOperation { override get label() { @@ -87,7 +87,7 @@ export class DataCubeQueryFilterOperation__GreaterThanOrEqualColumn extends Data ); } - buildConditionExpression(condition: DataCubeQuerySnapshotFilterCondition) { + buildConditionExpression(condition: DataCubeSnapshotFilterCondition) { return _function(_functionName(DataCubeFunction.GREATER_THAN_OR_EQUAL), [ _property(condition.name), _value(condition.value), diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__IsNotNull.tsx b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__IsNotNull.tsx index f9757e2059..2b491182bb 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__IsNotNull.tsx +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__IsNotNull.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { DataCubeColumnDataType, @@ -34,7 +34,7 @@ import { type V1_AppliedFunction } from '@finos/legend-graph'; import { _unwrapNotFilterCondition, _filterCondition_base, -} from '../DataCubeQuerySnapshotBuilderUtils.js'; +} from '../DataCubeSnapshotBuilderUtils.js'; import { returnUndefOnError } from '@finos/legend-shared'; export class DataCubeQueryFilterOperation__IsNotNull extends DataCubeQueryFilterOperation { @@ -89,7 +89,7 @@ export class DataCubeQueryFilterOperation__IsNotNull extends DataCubeQueryFilter ); } - buildConditionExpression(condition: DataCubeQuerySnapshotFilterCondition) { + buildConditionExpression(condition: DataCubeSnapshotFilterCondition) { return _not( _function(_functionName(DataCubeFunction.IS_EMPTY), [ _property(condition.name), diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__IsNull.tsx b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__IsNull.tsx index bbf6efed26..92b5335205 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__IsNull.tsx +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__IsNull.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { DataCubeColumnDataType, @@ -30,7 +30,7 @@ import { _property, } from '../DataCubeQueryBuilderUtils.js'; import { type V1_AppliedFunction } from '@finos/legend-graph'; -import { _filterCondition_base } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _filterCondition_base } from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryFilterOperation__IsNull extends DataCubeQueryFilterOperation { override get label() { @@ -84,7 +84,7 @@ export class DataCubeQueryFilterOperation__IsNull extends DataCubeQueryFilterOpe ); } - buildConditionExpression(condition: DataCubeQuerySnapshotFilterCondition) { + buildConditionExpression(condition: DataCubeSnapshotFilterCondition) { return _function(_functionName(DataCubeFunction.IS_EMPTY), [ _property(condition.name), ]); diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__LessThan.tsx b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__LessThan.tsx index 7d856c1de5..5fca783da4 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__LessThan.tsx +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__LessThan.tsx @@ -15,7 +15,7 @@ */ import { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { DataCubeColumnDataType, @@ -33,7 +33,7 @@ import { _value, } from '../DataCubeQueryBuilderUtils.js'; import { type V1_AppliedFunction } from '@finos/legend-graph'; -import { _filterCondition_base } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _filterCondition_base } from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryFilterOperation__LessThan extends DataCubeQueryFilterOperation { override get label() { @@ -93,7 +93,7 @@ export class DataCubeQueryFilterOperation__LessThan extends DataCubeQueryFilterO ); } - buildConditionExpression(condition: DataCubeQuerySnapshotFilterCondition) { + buildConditionExpression(condition: DataCubeSnapshotFilterCondition) { return _function(_functionName(DataCubeFunction.LESS_THAN), [ _property(condition.name), _value(condition.value), diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__LessThanColumn.tsx b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__LessThanColumn.tsx index 4ef95c6487..f1c9f0067b 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__LessThanColumn.tsx +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__LessThanColumn.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { DataCubeColumnDataType, @@ -32,7 +32,7 @@ import { } from '../DataCubeQueryBuilderUtils.js'; import { isString } from '@finos/legend-shared'; import { type V1_AppliedFunction } from '@finos/legend-graph'; -import { _filterCondition_base } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _filterCondition_base } from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryFilterOperation__LessThanColumn extends DataCubeQueryFilterOperation { override get label() { @@ -87,7 +87,7 @@ export class DataCubeQueryFilterOperation__LessThanColumn extends DataCubeQueryF ); } - buildConditionExpression(condition: DataCubeQuerySnapshotFilterCondition) { + buildConditionExpression(condition: DataCubeSnapshotFilterCondition) { return _function(_functionName(DataCubeFunction.LESS_THAN), [ _property(condition.name), _value(condition.value), diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__LessThanOrEqual.tsx b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__LessThanOrEqual.tsx index a6a1a4d7f1..b72ff02a93 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__LessThanOrEqual.tsx +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__LessThanOrEqual.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { DataCubeColumnDataType, @@ -32,7 +32,7 @@ import { _value, } from '../DataCubeQueryBuilderUtils.js'; import { type V1_AppliedFunction } from '@finos/legend-graph'; -import { _filterCondition_base } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _filterCondition_base } from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryFilterOperation__LessThanOrEqual extends DataCubeQueryFilterOperation { override get label() { @@ -92,7 +92,7 @@ export class DataCubeQueryFilterOperation__LessThanOrEqual extends DataCubeQuery ); } - buildConditionExpression(condition: DataCubeQuerySnapshotFilterCondition) { + buildConditionExpression(condition: DataCubeSnapshotFilterCondition) { return _function(_functionName(DataCubeFunction.LESS_THAN_OR_EQUAL), [ _property(condition.name), _value(condition.value), diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__LessThanOrEqualColumn.tsx b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__LessThanOrEqualColumn.tsx index 6d4d0204fb..a928fb6716 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__LessThanOrEqualColumn.tsx +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__LessThanOrEqualColumn.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { DataCubeColumnDataType, @@ -32,7 +32,7 @@ import { } from '../DataCubeQueryBuilderUtils.js'; import { isString } from '@finos/legend-shared'; import { type V1_AppliedFunction } from '@finos/legend-graph'; -import { _filterCondition_base } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _filterCondition_base } from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryFilterOperation__LessThanOrEqualColumn extends DataCubeQueryFilterOperation { override get label() { @@ -87,7 +87,7 @@ export class DataCubeQueryFilterOperation__LessThanOrEqualColumn extends DataCub ); } - buildConditionExpression(condition: DataCubeQuerySnapshotFilterCondition) { + buildConditionExpression(condition: DataCubeSnapshotFilterCondition) { return _function(_functionName(DataCubeFunction.LESS_THAN_OR_EQUAL), [ _property(condition.name), _value(condition.value), diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotContain.tsx b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotContain.tsx index cafdb340aa..52dc2c6a65 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotContain.tsx +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotContain.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { DataCubeColumnDataType, @@ -37,7 +37,7 @@ import { type V1_AppliedFunction } from '@finos/legend-graph'; import { _unwrapNotFilterCondition, _filterCondition_base, -} from '../DataCubeQuerySnapshotBuilderUtils.js'; +} from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryFilterOperation__NotContain extends DataCubeQueryFilterOperation { override get label() { @@ -90,7 +90,7 @@ export class DataCubeQueryFilterOperation__NotContain extends DataCubeQueryFilte ); } - buildConditionExpression(condition: DataCubeQuerySnapshotFilterCondition) { + buildConditionExpression(condition: DataCubeSnapshotFilterCondition) { return _not( _function(_functionName(DataCubeFunction.CONTAINS), [ _property(condition.name), diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotEndWith.tsx b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotEndWith.tsx index 61e7ea04ee..5d1210d1be 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotEndWith.tsx +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotEndWith.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { DataCubeColumnDataType, @@ -37,7 +37,7 @@ import { type V1_AppliedFunction } from '@finos/legend-graph'; import { _unwrapNotFilterCondition, _filterCondition_base, -} from '../DataCubeQuerySnapshotBuilderUtils.js'; +} from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryFilterOperation__NotEndWith extends DataCubeQueryFilterOperation { override get label() { @@ -90,7 +90,7 @@ export class DataCubeQueryFilterOperation__NotEndWith extends DataCubeQueryFilte ); } - buildConditionExpression(condition: DataCubeQuerySnapshotFilterCondition) { + buildConditionExpression(condition: DataCubeSnapshotFilterCondition) { return _not( _function(_functionName(DataCubeFunction.ENDS_WITH), [ _property(condition.name), diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotEqual.tsx b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotEqual.tsx index 011cf0caf4..bfa84d8c86 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotEqual.tsx +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotEqual.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { DataCubeColumnDataType, @@ -37,7 +37,7 @@ import { type V1_AppliedFunction } from '@finos/legend-graph'; import { _unwrapNotFilterCondition, _filterCondition_base, -} from '../DataCubeQuerySnapshotBuilderUtils.js'; +} from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryFilterOperation__NotEqual extends DataCubeQueryFilterOperation { override get label() { @@ -99,7 +99,7 @@ export class DataCubeQueryFilterOperation__NotEqual extends DataCubeQueryFilterO ); } - buildConditionExpression(condition: DataCubeQuerySnapshotFilterCondition) { + buildConditionExpression(condition: DataCubeSnapshotFilterCondition) { return _not( _function(_functionName(DataCubeFunction.EQUAL), [ _property(condition.name), diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotEqualCaseInsensitive.tsx b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotEqualCaseInsensitive.tsx index e755105186..0c21d82a92 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotEqualCaseInsensitive.tsx +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotEqualCaseInsensitive.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { DataCubeColumnDataType, @@ -37,7 +37,7 @@ import { type V1_AppliedFunction } from '@finos/legend-graph'; import { _filterCondition_caseSensitive, _unwrapNotFilterCondition, -} from '../DataCubeQuerySnapshotBuilderUtils.js'; +} from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryFilterOperation__NotEqualCaseInsensitive extends DataCubeQueryFilterOperation { override get label() { @@ -90,7 +90,7 @@ export class DataCubeQueryFilterOperation__NotEqualCaseInsensitive extends DataC ); } - buildConditionExpression(condition: DataCubeQuerySnapshotFilterCondition) { + buildConditionExpression(condition: DataCubeSnapshotFilterCondition) { return _not( _function(_functionName(DataCubeFunction.EQUAL), [ _function(_functionName(DataCubeFunction.TO_LOWERCASE), [ diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotEqualCaseInsensitiveColumn.tsx b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotEqualCaseInsensitiveColumn.tsx index 39bd3dc8b5..97943719ca 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotEqualCaseInsensitiveColumn.tsx +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotEqualCaseInsensitiveColumn.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { DataCubeColumnDataType, @@ -36,7 +36,7 @@ import { type V1_AppliedFunction } from '@finos/legend-graph'; import { _filterCondition_caseSensitive, _unwrapNotFilterCondition, -} from '../DataCubeQuerySnapshotBuilderUtils.js'; +} from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryFilterOperation__NotEqualCaseInsensitiveColumn extends DataCubeQueryFilterOperation { override get label() { @@ -87,7 +87,7 @@ export class DataCubeQueryFilterOperation__NotEqualCaseInsensitiveColumn extends ); } - buildConditionExpression(condition: DataCubeQuerySnapshotFilterCondition) { + buildConditionExpression(condition: DataCubeSnapshotFilterCondition) { return _not( _function(_functionName(DataCubeFunction.EQUAL), [ _function(_functionName(DataCubeFunction.TO_LOWERCASE), [ diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotEqualColumn.tsx b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotEqualColumn.tsx index 36634e37c0..effe495732 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotEqualColumn.tsx +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotEqualColumn.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { DataCubeColumnDataType, @@ -36,7 +36,7 @@ import { type V1_AppliedFunction } from '@finos/legend-graph'; import { _filterCondition_base, _unwrapNotFilterCondition, -} from '../DataCubeQuerySnapshotBuilderUtils.js'; +} from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryFilterOperation__NotEqualColumn extends DataCubeQueryFilterOperation { override get label() { @@ -92,7 +92,7 @@ export class DataCubeQueryFilterOperation__NotEqualColumn extends DataCubeQueryF ); } - buildConditionExpression(condition: DataCubeQuerySnapshotFilterCondition) { + buildConditionExpression(condition: DataCubeSnapshotFilterCondition) { return _not( _function(_functionName(DataCubeFunction.EQUAL), [ _property(condition.name), diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotStartWith.tsx b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotStartWith.tsx index c8feb03bb7..3c4c357102 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotStartWith.tsx +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__NotStartWith.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { DataCubeColumnDataType, @@ -37,7 +37,7 @@ import { type V1_AppliedFunction } from '@finos/legend-graph'; import { _unwrapNotFilterCondition, _filterCondition_base, -} from '../DataCubeQuerySnapshotBuilderUtils.js'; +} from '../DataCubeSnapshotBuilderUtils.js'; export class DataCubeQueryFilterOperation__NotStartWith extends DataCubeQueryFilterOperation { override get label() { @@ -90,7 +90,7 @@ export class DataCubeQueryFilterOperation__NotStartWith extends DataCubeQueryFil ); } - buildConditionExpression(condition: DataCubeQuerySnapshotFilterCondition) { + buildConditionExpression(condition: DataCubeSnapshotFilterCondition) { return _not( _function(_functionName(DataCubeFunction.STARTS_WITH), [ _property(condition.name), diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__StartWith.tsx b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__StartWith.tsx index 0dd87586d4..515aa098d8 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__StartWith.tsx +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__StartWith.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { DataCubeColumnDataType, @@ -32,7 +32,7 @@ import { _value, } from '../DataCubeQueryBuilderUtils.js'; import { type V1_AppliedFunction } from '@finos/legend-graph'; -import { _filterCondition_base } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _filterCondition_base } from '../DataCubeSnapshotBuilderUtils.js'; import { isString } from '@finos/legend-shared'; export class DataCubeQueryFilterOperation__StartWith extends DataCubeQueryFilterOperation { @@ -86,7 +86,7 @@ export class DataCubeQueryFilterOperation__StartWith extends DataCubeQueryFilter ); } - buildConditionExpression(condition: DataCubeQuerySnapshotFilterCondition) { + buildConditionExpression(condition: DataCubeSnapshotFilterCondition) { return _function(_functionName(DataCubeFunction.STARTS_WITH), [ _property(condition.name), _value(condition.value), diff --git a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__StartWithCaseInsensitive.tsx b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__StartWithCaseInsensitive.tsx index a3f52c013b..40af75dfd5 100644 --- a/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__StartWithCaseInsensitive.tsx +++ b/packages/legend-data-cube/src/stores/core/filter/DataCubeQueryFilterOperation__StartWithCaseInsensitive.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { DataCubeQueryFilterOperation } from './DataCubeQueryFilterOperation.js'; -import type { DataCubeQuerySnapshotFilterCondition } from '../DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotFilterCondition } from '../DataCubeSnapshot.js'; import type { DataCubeColumn } from '../model/DataCubeColumn.js'; import { DataCubeColumnDataType, @@ -32,7 +32,7 @@ import { _value, } from '../DataCubeQueryBuilderUtils.js'; import { type V1_AppliedFunction } from '@finos/legend-graph'; -import { _filterCondition_caseSensitive } from '../DataCubeQuerySnapshotBuilderUtils.js'; +import { _filterCondition_caseSensitive } from '../DataCubeSnapshotBuilderUtils.js'; import { isString } from '@finos/legend-shared'; export class DataCubeQueryFilterOperation__StartWithCaseInsensitive extends DataCubeQueryFilterOperation { @@ -86,7 +86,7 @@ export class DataCubeQueryFilterOperation__StartWithCaseInsensitive extends Data ); } - buildConditionExpression(condition: DataCubeQuerySnapshotFilterCondition) { + buildConditionExpression(condition: DataCubeSnapshotFilterCondition) { return _function(_functionName(DataCubeFunction.STARTS_WITH), [ _function(_functionName(DataCubeFunction.TO_LOWERCASE), [ _property(condition.name), diff --git a/packages/legend-data-cube/src/stores/core/model/DataCubeConfiguration.ts b/packages/legend-data-cube/src/stores/core/model/DataCubeConfiguration.ts index ff76e24de4..582251e61b 100644 --- a/packages/legend-data-cube/src/stores/core/model/DataCubeConfiguration.ts +++ b/packages/legend-data-cube/src/stores/core/model/DataCubeConfiguration.ts @@ -98,7 +98,7 @@ export class DataCubeColumnConfiguration { /** * Unlike `isSelected`, this is used to indicate if the column is to be displayed * in the grid or not, this would not influence data-fetching, i.e. the column - * is still fetched and used in various part of the queries, but the column associated + * is still fetched and used in various part of the query, but the column associated * will not be displayed in the result grid. */ hideFromView = false; diff --git a/packages/legend-data-cube/src/stores/core/model/DataCubeQuery.ts b/packages/legend-data-cube/src/stores/core/model/DataCubeSpecification.ts similarity index 65% rename from packages/legend-data-cube/src/stores/core/model/DataCubeQuery.ts rename to packages/legend-data-cube/src/stores/core/model/DataCubeSpecification.ts index a86cba43ef..f0da6e2c03 100644 --- a/packages/legend-data-cube/src/stores/core/model/DataCubeQuery.ts +++ b/packages/legend-data-cube/src/stores/core/model/DataCubeSpecification.ts @@ -19,19 +19,33 @@ import { usingModelSchema, type PlainObject, } from '@finos/legend-shared'; -import { createModelSchema, primitive, raw } from 'serializr'; +import { createModelSchema, optional, primitive, raw } from 'serializr'; import { DataCubeConfiguration } from './DataCubeConfiguration.js'; -export class DataCubeQuery { +export class DataCubeSpecificationOptions { + autoEnableCache?: boolean | undefined; + + static readonly serialization = new SerializationFactory( + createModelSchema(DataCubeSpecificationOptions, { + autoEnableCache: optional(primitive()), + }), + ); +} + +export class DataCubeSpecification { query!: string; configuration?: DataCubeConfiguration | undefined; source!: PlainObject; + options?: DataCubeSpecificationOptions | undefined; static readonly serialization = new SerializationFactory( - createModelSchema(DataCubeQuery, { + createModelSchema(DataCubeSpecification, { configuration: usingModelSchema( DataCubeConfiguration.serialization.schema, ), + options: optional( + usingModelSchema(DataCubeSpecificationOptions.serialization.schema), + ), query: primitive(), source: raw(), }), diff --git a/packages/legend-data-cube/src/stores/services/DataCubeSettingService.tsx b/packages/legend-data-cube/src/stores/services/DataCubeSettingService.tsx index 4e4751fb5d..7f5cd3893f 100644 --- a/packages/legend-data-cube/src/stores/services/DataCubeSettingService.tsx +++ b/packages/legend-data-cube/src/stores/services/DataCubeSettingService.tsx @@ -55,6 +55,7 @@ export type DataCubeSettingValues = { export enum DataCubeSettingGroup { DEBUG = 'Debug', + EDITOR = 'Editor', GRID = 'Grid', } @@ -169,6 +170,28 @@ export class DataCubeSettingService { type: DataCubeSettingType.BOOLEAN, defaultValue: true, } satisfies DataCubeSetting, + { + key: DataCubeSettingKey.GRID_CLIENT__SHOW_AUTO_ENABLE_CACHE_PERFORMANCE_WARNING, + title: `Caching (Auto-Enabled) Performance Warning: Enabled`, + description: `Alerts user that caching is auto-enabled for the DataCube and of potential performance and resource-related issues that caching can cause when enabled.`, + group: DataCubeSettingGroup.GRID, + type: DataCubeSettingType.BOOLEAN, + defaultValue: true, + } satisfies DataCubeSetting, + { + key: DataCubeSettingKey.EDITOR__MAX_HISTORY_STACK_SIZE, + title: `Max History Stack Size`, + description: `Sets the number of maximum snapshots to store in edit history.`, + group: DataCubeSettingGroup.EDITOR, + type: DataCubeSettingType.NUMERIC, + defaultValue: 100, + numericValueMin: 10, + numericValueStep: 10, + action: (api, newValue) => + (api as INTERNAL__DataCubeAPI)._runTaskForEachView((view) => { + view.snapshotService.adjustHistorySize(newValue); + }), + } satisfies DataCubeSetting, { key: DataCubeSettingKey.GRID_CLIENT__SUPPRESS_LARGE_DATASET_WARNING, title: `Large Dataset Warning: Disabled`, diff --git a/packages/legend-data-cube/src/stores/services/DataCubeQuerySnapshotService.ts b/packages/legend-data-cube/src/stores/services/DataCubeSnapshotService.ts similarity index 52% rename from packages/legend-data-cube/src/stores/services/DataCubeQuerySnapshotService.ts rename to packages/legend-data-cube/src/stores/services/DataCubeSnapshotService.ts index 6861da8bba..18a90461cd 100644 --- a/packages/legend-data-cube/src/stores/services/DataCubeQuerySnapshotService.ts +++ b/packages/legend-data-cube/src/stores/services/DataCubeSnapshotService.ts @@ -14,42 +14,38 @@ * limitations under the License. */ -import type { DataCubeQuerySnapshot } from '../core/DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshot } from '../core/DataCubeSnapshot.js'; import { IllegalStateError, assertErrorThrown, at, deepDiff, - guaranteeNonNullable, } from '@finos/legend-shared'; -import type { DataCubeQuery } from '../core/model/DataCubeQuery.js'; import { DataCubeSettingKey } from '../../__lib__/DataCubeSetting.js'; import type { DataCubeEngine } from '../core/DataCubeEngine.js'; import type { DataCubeSettingService } from './DataCubeSettingService.js'; import type { DataCubeLogService } from './DataCubeLogService.js'; +import { action, computed, makeObservable, observable } from 'mobx'; -// TODO: set a stack depth when we implement undo/redo -// const DATA_CUBE_MAX_SNAPSHOT_COUNT = 100; - -interface DataCubeQuerySnapshotSubscriber { +interface DataCubeSnapshotSubscriber { getSnapshotSubscriberName(): string; - getLatestSnapshot(): DataCubeQuerySnapshot | undefined; - receiveSnapshot(snapshot: DataCubeQuerySnapshot): Promise; + getLatestSnapshot(): DataCubeSnapshot | undefined; + receiveSnapshot(snapshot: DataCubeSnapshot): Promise; } -export abstract class DataCubeQuerySnapshotController - implements DataCubeQuerySnapshotSubscriber +export abstract class DataCubeSnapshotController + implements DataCubeSnapshotSubscriber { protected readonly _engine: DataCubeEngine; protected readonly _settingService: DataCubeSettingService; - protected readonly _snapshotService: DataCubeQuerySnapshotService; + protected readonly _snapshotService: DataCubeSnapshotService; - private _latestSnapshot: DataCubeQuerySnapshot | undefined; + private _latestSnapshot: DataCubeSnapshot | undefined; constructor( engine: DataCubeEngine, settingService: DataCubeSettingService, - snapshotService: DataCubeQuerySnapshotService, + snapshotService: DataCubeSnapshotService, ) { this._engine = engine; this._settingService = settingService; @@ -62,7 +58,7 @@ export abstract class DataCubeQuerySnapshotController return this._latestSnapshot; } - async receiveSnapshot(snapshot: DataCubeQuerySnapshot) { + async receiveSnapshot(snapshot: DataCubeSnapshot) { const previousSnapshot = this._latestSnapshot; this._latestSnapshot = snapshot; @@ -72,11 +68,11 @@ export abstract class DataCubeQuerySnapshotController } abstract applySnapshot( - snapshot: DataCubeQuerySnapshot, - previousSnapshot: DataCubeQuerySnapshot | undefined, + snapshot: DataCubeSnapshot, + previousSnapshot: DataCubeSnapshot | undefined, ): Promise; - publishSnapshot(snapshot: DataCubeQuerySnapshot) { + publishSnapshot(snapshot: DataCubeSnapshot) { const previousSnapshot = this._latestSnapshot; this._latestSnapshot = snapshot; @@ -98,25 +94,44 @@ export abstract class DataCubeQuerySnapshotController } } -export class DataCubeQuerySnapshotService { - private readonly _engine: DataCubeEngine; +const MINIMUM_HISTORY_SIZE = 10; + +export class DataCubeSnapshotService { private readonly _logService: DataCubeLogService; + private readonly _settingService: DataCubeSettingService; - private readonly _subscribers: DataCubeQuerySnapshotSubscriber[] = []; - private readonly _snapshots: DataCubeQuerySnapshot[] = []; // stack - private _initialSnapshot: DataCubeQuerySnapshot | undefined; - private _initialQuery: DataCubeQuery | undefined; + private readonly _subscribers: DataCubeSnapshotSubscriber[] = []; - constructor(engine: DataCubeEngine, logService: DataCubeLogService) { - this._engine = engine; + private _snapshots: DataCubeSnapshot[] = []; // stack + private _pointer = -1; + private _historySize; + + constructor( + logService: DataCubeLogService, + settingService: DataCubeSettingService, + ) { + makeObservable(this, { + _snapshots: observable.struct, + _pointer: observable, + canUndo: computed, + canRedo: computed, + undo: action, + redo: action, + adjustHistorySize: action, + broadcastSnapshot: action, + }); this._logService = logService; - } + this._settingService = settingService; - get currentSnapshot() { - return at(this._snapshots, this._snapshots.length - 1); + this._historySize = Math.max( + this._settingService.getNumericValue( + DataCubeSettingKey.EDITOR__MAX_HISTORY_STACK_SIZE, + ), + MINIMUM_HISTORY_SIZE, + ); } - registerSubscriber(subscriber: DataCubeQuerySnapshotSubscriber) { + registerSubscriber(subscriber: DataCubeSnapshotSubscriber) { const existingSubscriber = this._subscribers.find( (sub) => sub.getSnapshotSubscriberName() === @@ -137,41 +152,7 @@ export class DataCubeQuerySnapshotService { this._subscribers.push(subscriber); } - initialize( - initialSnapshot: DataCubeQuerySnapshot, - initialQuery: DataCubeQuery, - ) { - if (this._initialSnapshot || this._initialQuery) { - throw new IllegalStateError( - `Snapshot manager has already been initialized`, - ); - } - this._initialSnapshot = initialSnapshot; - this._initialQuery = initialQuery; - } - - get initialSnapshot() { - return guaranteeNonNullable( - this._initialSnapshot, - `Snapshot manager has not been initialized`, - ); - } - - get initialQuery() { - return guaranteeNonNullable( - this._initialQuery, - `Snapshot manager has not been initialized`, - ); - } - - broadcastSnapshot(snapshot: DataCubeQuerySnapshot) { - if (!snapshot.isFinalized()) { - this._logService.logIllegalStateError( - `Snapshot must be finalized before broadcasting`, - ); - return; - } - this._snapshots.push(snapshot); + private propagateSnapshot(snapshot: DataCubeSnapshot): void { this._subscribers.forEach((subscriber) => { const currentSnapshot = subscriber.getLatestSnapshot(); if (currentSnapshot?.uuid !== snapshot.uuid) { @@ -185,4 +166,74 @@ export class DataCubeQuerySnapshotService { } }); } + + getCurrentSnapshot() { + return at(this._snapshots, this._pointer); + } + + getHistory(options?: { full?: boolean }) { + return options?.full + ? [...this._snapshots] + : this._snapshots.slice(0, this._pointer + 1); + } + + get canUndo() { + return this._pointer > 0; + } + + get canRedo() { + return this._pointer < this._snapshots.length - 1; + } + + undo() { + // Always leave one snapshot in the stack + this._pointer = Math.max(this._pointer - 1, 0); + this.propagateSnapshot(this.getCurrentSnapshot()); + } + + redo() { + this._pointer = Math.min(this._pointer + 1, this._snapshots.length - 1); + this.propagateSnapshot(this.getCurrentSnapshot()); + } + + adjustHistorySize(size: number): void { + let newSize = size; + if (size <= MINIMUM_HISTORY_SIZE) { + this._logService.logIllegalStateError( + `Can't adjust history size to ${size}: value must be greator than ${MINIMUM_HISTORY_SIZE}`, + ); + newSize = Math.max(size, MINIMUM_HISTORY_SIZE); + } + const snapshots = this.getHistory(); + if (snapshots.length > newSize) { + this._snapshots = snapshots.slice(-newSize); + this._pointer = Math.min( + Math.max(this._pointer - (snapshots.length - newSize), 0), + snapshots.length - 1, + ); + } else { + this._snapshots = snapshots; + this._pointer = snapshots.length - 1; + } + this._historySize = newSize; + } + + broadcastSnapshot(snapshot: DataCubeSnapshot) { + if (!snapshot.isFinalized()) { + this._logService.logIllegalStateError( + `Snapshot must be finalized before broadcasting`, + ); + return; + } + + this._snapshots = [ + ...this._snapshots.slice(0, this._pointer + 1), + snapshot, + ]; + this._pointer += 1; + // always adjust to history size after adding a new snapshot + this.adjustHistorySize(this._historySize); + + this.propagateSnapshot(snapshot); + } } diff --git a/packages/legend-data-cube/src/stores/view/DataCubeInfoState.ts b/packages/legend-data-cube/src/stores/view/DataCubeInfoState.ts index 7cb5d2f6e2..e259ee591b 100644 --- a/packages/legend-data-cube/src/stores/view/DataCubeInfoState.ts +++ b/packages/legend-data-cube/src/stores/view/DataCubeInfoState.ts @@ -16,8 +16,8 @@ import { action, makeObservable, observable } from 'mobx'; import type { DataCubeViewState } from './DataCubeViewState.js'; -import { DataCubeQuerySnapshotController } from '../services/DataCubeQuerySnapshotService.js'; -import type { DataCubeQuerySnapshot } from '../core/DataCubeQuerySnapshot.js'; +import { DataCubeSnapshotController } from '../services/DataCubeSnapshotService.js'; +import type { DataCubeSnapshot } from '../core/DataCubeSnapshot.js'; import { DataCubeConfiguration } from '../core/model/DataCubeConfiguration.js'; /** @@ -25,7 +25,7 @@ import { DataCubeConfiguration } from '../core/model/DataCubeConfiguration.js'; * modification to the query, it simplies subscribe to extract information * from the latest snapshot to help display latest static info about the query. */ -export class DataCubeInfoState extends DataCubeQuerySnapshotController { +export class DataCubeInfoState extends DataCubeSnapshotController { private readonly _view: DataCubeViewState; name = ''; @@ -48,8 +48,8 @@ export class DataCubeInfoState extends DataCubeQuerySnapshotController { } override async applySnapshot( - snapshot: DataCubeQuerySnapshot, - previousSnapshot: DataCubeQuerySnapshot | undefined, + snapshot: DataCubeSnapshot, + previousSnapshot: DataCubeSnapshot | undefined, ) { const data = snapshot.data; const configuration = DataCubeConfiguration.serialization.fromJson( diff --git a/packages/legend-data-cube/src/stores/view/DataCubeViewState.ts b/packages/legend-data-cube/src/stores/view/DataCubeViewState.ts index 30ceb95287..6fc526e77b 100644 --- a/packages/legend-data-cube/src/stores/view/DataCubeViewState.ts +++ b/packages/legend-data-cube/src/stores/view/DataCubeViewState.ts @@ -19,22 +19,29 @@ import { DataCubeEditorState } from './editor/DataCubeEditorState.js'; import { ActionState, assertErrorThrown, + deepEqual, IllegalStateError, } from '@finos/legend-shared'; -import { DataCubeQuerySnapshotService } from '../services/DataCubeQuerySnapshotService.js'; +import { DataCubeSnapshotService } from '../services/DataCubeSnapshotService.js'; import { DataCubeInfoState } from './DataCubeInfoState.js'; -import { validateAndBuildQuerySnapshot } from '../core/DataCubeQuerySnapshotBuilder.js'; +import { validateAndBuildSnapshot } from '../core/DataCubeSnapshotBuilder.js'; import { DataCubeFilterEditorState } from './filter/DataCubeFilterEditorState.js'; import { DataCubeExtendManagerState } from './extend/DataCubeExtendManagerState.js'; import type { DataCubeState } from '../DataCubeState.js'; import { type DataCubeEngine } from '../core/DataCubeEngine.js'; import type { DataCubeSource } from '../core/model/DataCubeSource.js'; -import { DataCubeQuery } from '../core/model/DataCubeQuery.js'; +import { DataCubeSpecification } from '../core/model/DataCubeSpecification.js'; import { DataCubeTaskService } from '../services/DataCubeTaskService.js'; import type { DataCubeLogService } from '../services/DataCubeLogService.js'; import { DataCubeConfiguration } from '../core/model/DataCubeConfiguration.js'; -import type { DataCubeLayoutService } from '../services/DataCubeLayoutService.js'; -import type { DataCubeAlertService } from '../services/DataCubeAlertService.js'; +import { + DEFAULT_ALERT_WINDOW_CONFIG, + type DataCubeLayoutService, +} from '../services/DataCubeLayoutService.js'; +import { + AlertType, + type DataCubeAlertService, +} from '../services/DataCubeAlertService.js'; import type { DataCubeSettingService } from '../services/DataCubeSettingService.js'; import { CachedDataCubeSource } from '../core/model/CachedDataCubeSource.js'; import { DataCubeSettingKey } from '../../__lib__/DataCubeSetting.js'; @@ -48,7 +55,7 @@ export class DataCubeViewState { readonly layoutService: DataCubeLayoutService; readonly alertService: DataCubeAlertService; readonly settingService: DataCubeSettingService; - readonly snapshotService: DataCubeQuerySnapshotService; + readonly snapshotService: DataCubeSnapshotService; readonly info: DataCubeInfoState; readonly editor: DataCubeEditorState; @@ -60,7 +67,8 @@ export class DataCubeViewState { readonly processCacheState = ActionState.create(); private _source?: DataCubeSource | undefined; - private _originalSource?: DataCubeSource | undefined; + private _initialSource?: DataCubeSource | undefined; + private _initialSpecification?: DataCubeSpecification | undefined; constructor(dataCube: DataCubeState) { this.dataCube = dataCube; @@ -71,9 +79,9 @@ export class DataCubeViewState { this.alertService = dataCube.alertService; this.settingService = dataCube.settingService; // NOTE: snapshot manager must be instantiated before subscribers - this.snapshotService = new DataCubeQuerySnapshotService( - this.engine, + this.snapshotService = new DataCubeSnapshotService( this.logService, + this.settingService, ); this.info = new DataCubeInfoState(this); @@ -83,14 +91,14 @@ export class DataCubeViewState { this.extend = new DataCubeExtendManagerState(this); } - getOriginalSource() { - return this._originalSource; + getInitialSource() { + return this._initialSource; } - async generateDataCubeQuery() { - const snapshot = this.snapshotService.currentSnapshot; - const query = new DataCubeQuery(); - query.source = this.dataCube.query.source; + async generateSpecification() { + const snapshot = this.snapshotService.getCurrentSnapshot(); + const query = new DataCubeSpecification(); + query.source = this.dataCube.specification.source; query.configuration = DataCubeConfiguration.serialization.fromJson( snapshot.data.configuration, ); @@ -105,6 +113,61 @@ export class DataCubeViewState { return this._source; } + updateName(name: string) { + const baseSnapshot = this.snapshotService.getCurrentSnapshot(); + const snapshot = baseSnapshot.clone(); + + const configuration = DataCubeConfiguration.serialization.fromJson( + baseSnapshot.data.configuration, + ); + if (configuration.name === name) { + return; + } + configuration.name = name; + snapshot.data.configuration = configuration.serialize(); + + snapshot.finalize(); + if (snapshot.hashCode !== baseSnapshot.hashCode) { + this.snapshotService.broadcastSnapshot(snapshot); + } + } + + async applySpecification(specification: DataCubeSpecification) { + const task = this.taskService.newTask('Applying specification...'); + + try { + if (!this._initialSource || !this._initialSpecification) { + throw new Error(`DataCube is not initialized`); + } + if (!deepEqual(specification.source, this._initialSpecification.source)) { + throw new Error(`Can't apply specification with different source`); + } + const partialQuery = await this.engine.parseValueSpecification( + specification.query, + ); + const snapshot = await validateAndBuildSnapshot( + partialQuery, + this._initialSource, + specification, + this.engine, + ); + snapshot.finalize(); + if ( + snapshot.hashCode !== this.snapshotService.getCurrentSnapshot().hashCode + ) { + this.snapshotService.broadcastSnapshot(snapshot); + } + } catch (error) { + assertErrorThrown(error); + this.alertService.alertError(error, { + message: `Specification Application Failure: ${error.message}`, + }); + this.initializeState.fail(); + } finally { + this.taskService.endTask(task); + } + } + async initializeCache() { this.processCacheState.inProgress(); const task = this.taskService.newTask('Initializing cache...'); @@ -129,7 +192,7 @@ export class DataCubeViewState { this.alertService.alertError(error, { message: `Cache Processing Failure: ${error.message}`, }); - this._source = this._originalSource; + this._source = this._initialSource; this.processCacheState.fail(); } finally { this.taskService.endTask(task); @@ -141,7 +204,7 @@ export class DataCubeViewState { return; } const cachedSource = this._source; - this._source = this._originalSource; + this._source = this._initialSource; this.processCacheState.inProgress(); const task = this.taskService.newTask('Disposing cache...'); @@ -159,7 +222,7 @@ export class DataCubeViewState { } } - async initialize(query: DataCubeQuery) { + async initialize(specification: DataCubeSpecification) { this.initializeState.inProgress(); const task = this.taskService.newTask('Initializing...'); @@ -176,18 +239,75 @@ export class DataCubeViewState { this.snapshotService.registerSubscriber(state); }), ); - const source = await this.engine.processQuerySource(query.source); + const source = await this.engine.processSource(specification.source); this._source = source; - this._originalSource = source; + this._initialSource = source; + this._initialSpecification = specification; const partialQuery = await this.engine.parseValueSpecification( - query.query, + specification.query, ); - const initialSnapshot = await validateAndBuildQuerySnapshot( + const initialSnapshot = await validateAndBuildSnapshot( partialQuery, source, - query, + specification, this.engine, ); + + // auto-enable cache if specified before broadcasting the first snapshot + // so first data-fetches will be against cache + if (specification.options?.autoEnableCache) { + await this.grid.setCachingEnabled(true, { + suppressWarning: true, + }); + + if ( + this.settingService.getBooleanValue( + DataCubeSettingKey.GRID_CLIENT__SHOW_AUTO_ENABLE_CACHE_PERFORMANCE_WARNING, + ) + ) { + this.alertService.alert({ + message: `Caching is auto-enabled for this DataCube`, + text: `When enabled, the source dataset will be cached locally in order to boost query performance. But depending on computational resource available to your environment, sometimes, caching can negatively impact the overall performance, and can even lead to crashes.\n\nOverall, caching is still an experimental feature where we only support queries with simple execution plans, certain queries might not work.\n\nYou can disable caching if needed, otherwise, please proceed with caution.`, + type: AlertType.WARNING, + actions: [ + { + label: 'OK', + handler: () => {}, + }, + { + label: 'Disable Caching', + handler: () => { + this.grid + .setCachingEnabled(false, { + suppressWarning: true, + }) + .catch((error) => + this.alertService.alertUnhandledError(error), + ); + }, + }, + { + label: 'Dismiss Warning', + handler: () => { + this.settingService.updateValue( + this.dataCube.api, + DataCubeSettingKey.GRID_CLIENT__SHOW_AUTO_ENABLE_CACHE_PERFORMANCE_WARNING, + false, + ); + }, + }, + ], + windowConfig: { + ...DEFAULT_ALERT_WINDOW_CONFIG, + width: 600, + height: 300, + minWidth: 300, + minHeight: 150, + }, + }); + } + } + this.snapshotService.broadcastSnapshot(initialSnapshot); this.dataCube.options?.onViewInitialized?.({ api: this.dataCube.api, diff --git a/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorColumnPropertiesPanelState.ts b/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorColumnPropertiesPanelState.ts index 25cb4f64f3..0b9e087e0f 100644 --- a/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorColumnPropertiesPanelState.ts +++ b/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorColumnPropertiesPanelState.ts @@ -16,7 +16,7 @@ import { action, computed, makeObservable, observable } from 'mobx'; import type { DataCubeViewState } from '../DataCubeViewState.js'; -import { type DataCubeQuerySnapshot } from '../../core/DataCubeQuerySnapshot.js'; +import { type DataCubeSnapshot } from '../../core/DataCubeSnapshot.js'; import type { DataCubeQueryEditorPanelState } from './DataCubeEditorPanelState.js'; import type { DataCubeEditorState } from './DataCubeEditorState.js'; import { DataCubeEditorMutableColumnConfiguration } from './DataCubeEditorMutableConfiguration.js'; @@ -79,7 +79,7 @@ export class DataCubeEditorColumnPropertiesPanelState } applySnaphot( - snapshot: DataCubeQuerySnapshot, + snapshot: DataCubeSnapshot, configuration: DataCubeConfiguration, ) { this.setColumns( @@ -98,10 +98,7 @@ export class DataCubeEditorColumnPropertiesPanelState } } - buildSnapshot( - newSnapshot: DataCubeQuerySnapshot, - baseSnapshot: DataCubeQuerySnapshot, - ) { + buildSnapshot(newSnapshot: DataCubeSnapshot, baseSnapshot: DataCubeSnapshot) { newSnapshot.data.configuration = { ...newSnapshot.data.configuration, columns: this.columns.map((column) => column.serialize()), diff --git a/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorColumnsPanelState.ts b/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorColumnsPanelState.ts index 46d3604fb6..44c78a1768 100644 --- a/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorColumnsPanelState.ts +++ b/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorColumnsPanelState.ts @@ -15,7 +15,7 @@ */ import { action, makeObservable, observable, override } from 'mobx'; -import { type DataCubeQuerySnapshot } from '../../core/DataCubeQuerySnapshot.js'; +import { type DataCubeSnapshot } from '../../core/DataCubeSnapshot.js'; import { _findCol, _toCol } from '../../core/model/DataCubeColumn.js'; import type { DataCubeQueryEditorPanelState } from './DataCubeEditorPanelState.js'; import { @@ -149,7 +149,7 @@ export class DataCubeEditorColumnsPanelState } applySnaphot( - snapshot: DataCubeQuerySnapshot, + snapshot: DataCubeSnapshot, configuration: DataCubeConfiguration, ) { this.selector.setSelectedColumns( @@ -167,10 +167,7 @@ export class DataCubeEditorColumnsPanelState ); } - buildSnapshot( - newSnapshot: DataCubeQuerySnapshot, - baseSnapshot: DataCubeQuerySnapshot, - ) { + buildSnapshot(newSnapshot: DataCubeSnapshot, baseSnapshot: DataCubeSnapshot) { this.propagateChanges(); // NOTE: these columns make up just part of the set of columns we want to fetch with `select()`. // Subsequently, we need to include columns used for horizontal pivots and vertical pivots. diff --git a/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorGeneralPropertiesPanelState.ts b/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorGeneralPropertiesPanelState.ts index a2038707fb..2320a672b0 100644 --- a/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorGeneralPropertiesPanelState.ts +++ b/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorGeneralPropertiesPanelState.ts @@ -15,7 +15,7 @@ */ import { action, makeObservable, observable } from 'mobx'; -import { type DataCubeQuerySnapshot } from '../../core/DataCubeQuerySnapshot.js'; +import { type DataCubeSnapshot } from '../../core/DataCubeSnapshot.js'; import type { DataCubeQueryEditorPanelState } from './DataCubeEditorPanelState.js'; import type { DataCubeEditorState } from './DataCubeEditorState.js'; import { DataCubeEditorMutableConfiguration } from './DataCubeEditorMutableConfiguration.js'; @@ -43,7 +43,7 @@ export class DataCubeEditorGeneralPropertiesPanelState } applySnaphot( - snapshot: DataCubeQuerySnapshot, + snapshot: DataCubeSnapshot, configuration: DataCubeConfiguration, ) { this.setLimit( @@ -56,10 +56,7 @@ export class DataCubeEditorGeneralPropertiesPanelState ); } - buildSnapshot( - newSnapshot: DataCubeQuerySnapshot, - baseSnapshot: DataCubeQuerySnapshot, - ) { + buildSnapshot(newSnapshot: DataCubeSnapshot, baseSnapshot: DataCubeSnapshot) { const data = newSnapshot.data; data.limit = this.limit === undefined || this.limit < 0 ? undefined : this.limit; diff --git a/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorHorizontalPivotsPanelState.ts b/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorHorizontalPivotsPanelState.ts index 0910897f62..0ac7969fff 100644 --- a/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorHorizontalPivotsPanelState.ts +++ b/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorHorizontalPivotsPanelState.ts @@ -21,7 +21,7 @@ import { DataCubeQuerySortDirection, isPivotResultColumnName, } from '../../core/DataCubeQueryEngine.js'; -import { type DataCubeQuerySnapshot } from '../../core/DataCubeQuerySnapshot.js'; +import { type DataCubeSnapshot } from '../../core/DataCubeSnapshot.js'; import { _findCol, _toCol, @@ -117,7 +117,7 @@ export class DataCubeEditorHorizontalPivotsPanelState } applySnaphot( - snapshot: DataCubeQuerySnapshot, + snapshot: DataCubeSnapshot, configuration: DataCubeConfiguration, ) { this.selector.setSelectedColumns( @@ -135,10 +135,7 @@ export class DataCubeEditorHorizontalPivotsPanelState this.setCastColumns(snapshot.data.pivot?.castColumns ?? []); } - buildSnapshot( - newSnapshot: DataCubeQuerySnapshot, - baseSnapshot: DataCubeQuerySnapshot, - ) { + buildSnapshot(newSnapshot: DataCubeSnapshot, baseSnapshot: DataCubeSnapshot) { newSnapshot.data.pivot = this.selector.selectedColumns.length ? { columns: this.selector.selectedColumns.map(_toCol), diff --git a/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorMutableConfiguration.ts b/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorMutableConfiguration.ts index 99137c20d8..1de7acca0f 100644 --- a/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorMutableConfiguration.ts +++ b/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorMutableConfiguration.ts @@ -47,7 +47,7 @@ import { DataCubeConfiguration, DataCubePivotLayoutConfiguration, } from '../../core/model/DataCubeConfiguration.js'; -import { type DataCubeQuerySnapshot } from '../../core/DataCubeQuerySnapshot.js'; +import { type DataCubeSnapshot } from '../../core/DataCubeSnapshot.js'; import { type DataCubeQueryAggregateOperation } from '../../core/aggregation/DataCubeQueryAggregateOperation.js'; export class DataCubeEditorMutableColumnConfiguration extends DataCubeColumnConfiguration { @@ -56,7 +56,7 @@ export class DataCubeEditorMutableColumnConfiguration extends DataCubeColumnConf static create( json: PlainObject, - snapshot: DataCubeQuerySnapshot | undefined, + snapshot: DataCubeSnapshot | undefined, aggregateOperations: DataCubeQueryAggregateOperation[], ): DataCubeEditorMutableColumnConfiguration { const configuration = Object.assign( diff --git a/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorPanelState.ts b/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorPanelState.ts index 99071875e8..540cef38aa 100644 --- a/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorPanelState.ts +++ b/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorPanelState.ts @@ -15,14 +15,14 @@ */ import type { DataCubeConfiguration } from '../../core/model/DataCubeConfiguration.js'; -import type { DataCubeQuerySnapshot } from '../../core/DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshot } from '../../core/DataCubeSnapshot.js'; export interface DataCubeQueryEditorPanelState { /** * Update the editor state based on the snapshot */ applySnaphot( - snapshot: DataCubeQuerySnapshot, + snapshot: DataCubeSnapshot, configuration: DataCubeConfiguration, ): void; @@ -31,7 +31,7 @@ export interface DataCubeQueryEditorPanelState { * @returns whether the snapshot should be updated or not */ buildSnapshot( - newSnapshot: DataCubeQuerySnapshot, - baseSnapshot: DataCubeQuerySnapshot, + newSnapshot: DataCubeSnapshot, + baseSnapshot: DataCubeSnapshot, ): void; } diff --git a/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorPivotLayoutPanelState.ts b/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorPivotLayoutPanelState.ts index a548fbeacd..c47994f684 100644 --- a/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorPivotLayoutPanelState.ts +++ b/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorPivotLayoutPanelState.ts @@ -15,13 +15,13 @@ */ import { action, makeObservable, observable } from 'mobx'; -import { type DataCubeQuerySnapshot } from '../../core/DataCubeQuerySnapshot.js'; +import { type DataCubeSnapshot } from '../../core/DataCubeSnapshot.js'; import type { DataCubeQueryEditorPanelState } from './DataCubeEditorPanelState.js'; import type { DataCubeEditorState } from './DataCubeEditorState.js'; import { DataCubeEditorMutablePivotLayoutConfiguration } from './DataCubeEditorMutableConfiguration.js'; import type { DataCubeConfiguration } from '../../core/model/DataCubeConfiguration.js'; import type { PlainObject } from '@finos/legend-shared'; -import { _pruneExpandedPaths } from '../../core/DataCubeQuerySnapshotBuilderUtils.js'; +import { _pruneExpandedPaths } from '../../core/DataCubeSnapshotBuilderUtils.js'; export class DataCubeEditorPivotLayoutPanelState implements DataCubeQueryEditorPanelState @@ -41,7 +41,7 @@ export class DataCubeEditorPivotLayoutPanelState } applySnaphot( - snapshot: DataCubeQuerySnapshot, + snapshot: DataCubeSnapshot, configuration: DataCubeConfiguration, ) { this.pivotLayout = DataCubeEditorMutablePivotLayoutConfiguration.create( @@ -49,10 +49,7 @@ export class DataCubeEditorPivotLayoutPanelState ); } - buildSnapshot( - newSnapshot: DataCubeQuerySnapshot, - baseSnapshot: DataCubeQuerySnapshot, - ) { + buildSnapshot(newSnapshot: DataCubeSnapshot, baseSnapshot: DataCubeSnapshot) { this.pivotLayout.setExpandedPaths( _pruneExpandedPaths( baseSnapshot.data.groupBy?.columns ?? [], diff --git a/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorSortsPanelState.ts b/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorSortsPanelState.ts index f8350ff1c3..621bfac8fb 100644 --- a/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorSortsPanelState.ts +++ b/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorSortsPanelState.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { type DataCubeQuerySnapshot } from '../../core/DataCubeQuerySnapshot.js'; +import { type DataCubeSnapshot } from '../../core/DataCubeSnapshot.js'; import { _findCol, _toCol } from '../../core/model/DataCubeColumn.js'; import { DataCubeColumnKind, @@ -96,7 +96,7 @@ export class DataCubeEditorSortsPanelState } applySnaphot( - snapshot: DataCubeQuerySnapshot, + snapshot: DataCubeSnapshot, configuration: DataCubeConfiguration, ) { this.selector.setSelectedColumns( @@ -111,10 +111,7 @@ export class DataCubeEditorSortsPanelState ); } - buildSnapshot( - newSnapshot: DataCubeQuerySnapshot, - baseSnapshot: DataCubeQuerySnapshot, - ) { + buildSnapshot(newSnapshot: DataCubeSnapshot, baseSnapshot: DataCubeSnapshot) { newSnapshot.data.sortColumns = this.selector.selectedColumns.map((col) => ({ ..._toCol(col), direction: col.direction, diff --git a/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorState.tsx b/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorState.tsx index 281a55f2bb..b87304739c 100644 --- a/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorState.tsx +++ b/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorState.tsx @@ -17,11 +17,11 @@ import { action, makeObservable, observable } from 'mobx'; import type { DataCubeViewState } from '../DataCubeViewState.js'; import { DataCubeEditorSortsPanelState } from './DataCubeEditorSortsPanelState.js'; -import { DataCubeQuerySnapshotController } from '../../services/DataCubeQuerySnapshotService.js'; +import { DataCubeSnapshotController } from '../../services/DataCubeSnapshotService.js'; import { - type DataCubeQuerySnapshot, - type DataCubeQuerySnapshotExtendedColumn, -} from '../../core/DataCubeQuerySnapshot.js'; + type DataCubeSnapshot, + type DataCubeSnapshotExtendedColumn, +} from '../../core/DataCubeSnapshot.js'; import { _findCol, _toCol, @@ -62,7 +62,7 @@ export enum DataCubeEditorTab { * It could also host other form editor states like filter editors, but due to ergonomic * reasons, those have been separated out into their own respective query editor states. */ -export class DataCubeEditorState extends DataCubeQuerySnapshotController { +export class DataCubeEditorState extends DataCubeSnapshotController { readonly view: DataCubeViewState; readonly display: DisplayState; readonly finalizationState = ActionState.create(); @@ -79,8 +79,8 @@ export class DataCubeEditorState extends DataCubeQuerySnapshotController { currentTab = DataCubeEditorTab.GENERAL_PROPERTIES; sourceColumns: DataCubeColumn[] = []; - leafExtendColumns: DataCubeQuerySnapshotExtendedColumn[] = []; - groupExtendColumns: DataCubeQuerySnapshotExtendedColumn[] = []; + leafExtendColumns: DataCubeSnapshotExtendedColumn[] = []; + groupExtendColumns: DataCubeSnapshotExtendedColumn[] = []; constructor(view: DataCubeViewState) { super(view.engine, view.settingService, view.snapshotService); @@ -118,8 +118,8 @@ export class DataCubeEditorState extends DataCubeQuerySnapshotController { } override async applySnapshot( - snapshot: DataCubeQuerySnapshot, - previousSnapshot: DataCubeQuerySnapshot | undefined, + snapshot: DataCubeSnapshot, + previousSnapshot: DataCubeSnapshot | undefined, ) { this.sourceColumns = snapshot.data.sourceColumns; this.leafExtendColumns = snapshot.data.leafExtendedColumns; diff --git a/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorVerticalPivotsPanelState.ts b/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorVerticalPivotsPanelState.ts index 223f91e266..4455c88add 100644 --- a/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorVerticalPivotsPanelState.ts +++ b/packages/legend-data-cube/src/stores/view/editor/DataCubeEditorVerticalPivotsPanelState.ts @@ -17,7 +17,7 @@ import { uniqBy } from '@finos/legend-shared'; import type { DataCubeConfiguration } from '../../core/model/DataCubeConfiguration.js'; import { DataCubeColumnKind } from '../../core/DataCubeQueryEngine.js'; -import { type DataCubeQuerySnapshot } from '../../core/DataCubeQuerySnapshot.js'; +import { type DataCubeSnapshot } from '../../core/DataCubeSnapshot.js'; import { _findCol, _toCol } from '../../core/model/DataCubeColumn.js'; import { DataCubeEditorColumnSelectorColumnState, @@ -74,7 +74,7 @@ export class DataCubeEditorVerticalPivotsPanelState } applySnaphot( - snapshot: DataCubeQuerySnapshot, + snapshot: DataCubeSnapshot, configuration: DataCubeConfiguration, ) { this.selector.setSelectedColumns( @@ -85,10 +85,7 @@ export class DataCubeEditorVerticalPivotsPanelState ); } - buildSnapshot( - newSnapshot: DataCubeQuerySnapshot, - baseSnapshot: DataCubeQuerySnapshot, - ) { + buildSnapshot(newSnapshot: DataCubeSnapshot, baseSnapshot: DataCubeSnapshot) { newSnapshot.data.groupBy = this.selector.selectedColumns.length ? { columns: this.selector.selectedColumns.map(_toCol), diff --git a/packages/legend-data-cube/src/stores/view/extend/DataCubeColumnEditorState.tsx b/packages/legend-data-cube/src/stores/view/extend/DataCubeColumnEditorState.tsx index 4d5d3bd85c..568701ac63 100644 --- a/packages/legend-data-cube/src/stores/view/extend/DataCubeColumnEditorState.tsx +++ b/packages/legend-data-cube/src/stores/view/extend/DataCubeColumnEditorState.tsx @@ -46,7 +46,7 @@ import { type V1_ValueSpecification, } from '@finos/legend-graph'; import type { DataCubeColumnConfiguration } from '../../core/model/DataCubeConfiguration.js'; -import type { DataCubeQuerySnapshotExtendedColumn } from '../../core/DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshotExtendedColumn } from '../../core/DataCubeSnapshot.js'; import { _lambda } from '../../core/DataCubeQueryBuilderUtils.js'; import { _findCol } from '../../core/model/DataCubeColumn.js'; @@ -404,7 +404,7 @@ export class DataCubeExistingColumnEditorState extends DataCubeColumnBaseEditorS constructor( manager: DataCubeExtendManagerState, - column: DataCubeQuerySnapshotExtendedColumn, + column: DataCubeSnapshotExtendedColumn, kind: DataCubeColumnKind, isGroupLevel: boolean, ) { diff --git a/packages/legend-data-cube/src/stores/view/extend/DataCubeExtendManagerState.tsx b/packages/legend-data-cube/src/stores/view/extend/DataCubeExtendManagerState.tsx index 449f9574a4..899c6dc960 100644 --- a/packages/legend-data-cube/src/stores/view/extend/DataCubeExtendManagerState.tsx +++ b/packages/legend-data-cube/src/stores/view/extend/DataCubeExtendManagerState.tsx @@ -16,9 +16,9 @@ import { action, computed, makeObservable, observable } from 'mobx'; import { - type DataCubeQuerySnapshot, - type DataCubeQuerySnapshotExtendedColumn, -} from '../../core/DataCubeQuerySnapshot.js'; + type DataCubeSnapshot, + type DataCubeSnapshotExtendedColumn, +} from '../../core/DataCubeSnapshot.js'; import { _findCol, _toCol, @@ -32,7 +32,7 @@ import { uniqBy, } from '@finos/legend-shared'; import type { DataCubeViewState } from '../DataCubeViewState.js'; -import { DataCubeQuerySnapshotController } from '../../services/DataCubeQuerySnapshotService.js'; +import { DataCubeSnapshotController } from '../../services/DataCubeSnapshotService.js'; import { DataCubeConfiguration, type DataCubeColumnConfiguration, @@ -53,9 +53,9 @@ import { EngineError } from '@finos/legend-graph'; class DataCubeQueryExtendedColumnState { readonly name: string; readonly type: string; - readonly data: DataCubeQuerySnapshotExtendedColumn; + readonly data: DataCubeSnapshotExtendedColumn; - constructor(data: DataCubeQuerySnapshotExtendedColumn) { + constructor(data: DataCubeSnapshotExtendedColumn) { this.name = data.name; this.type = data.type; this.data = data; @@ -65,7 +65,7 @@ class DataCubeQueryExtendedColumnState { /** * This query editor state backs the form editor for extend columns, i.e. creating new columns. */ -export class DataCubeExtendManagerState extends DataCubeQuerySnapshotController { +export class DataCubeExtendManagerState extends DataCubeSnapshotController { readonly view: DataCubeViewState; columnConfigurations: DataCubeColumnConfiguration[] = []; @@ -165,7 +165,7 @@ export class DataCubeExtendManagerState extends DataCubeQuerySnapshotController } addNewColumn( - column: DataCubeQuerySnapshotExtendedColumn, + column: DataCubeSnapshotExtendedColumn, isGroupLevel: boolean, columnKind: DataCubeColumnKind | undefined, editor: DataCubeNewColumnState, @@ -193,7 +193,7 @@ export class DataCubeExtendManagerState extends DataCubeQuerySnapshotController async updateColumn( columnName: string, - updatedColumn: DataCubeQuerySnapshotExtendedColumn, + updatedColumn: DataCubeSnapshotExtendedColumn, isGroupLevel: boolean, columnKind: DataCubeColumnKind | undefined, ) { @@ -416,8 +416,8 @@ export class DataCubeExtendManagerState extends DataCubeQuerySnapshotController } override async applySnapshot( - snapshot: DataCubeQuerySnapshot, - previousSnapshot: DataCubeQuerySnapshot | undefined, + snapshot: DataCubeSnapshot, + previousSnapshot: DataCubeSnapshot | undefined, ): Promise { const configuration = DataCubeConfiguration.serialization.fromJson( snapshot.data.configuration, diff --git a/packages/legend-data-cube/src/stores/view/filter/DataCubeFilterEditorState.tsx b/packages/legend-data-cube/src/stores/view/filter/DataCubeFilterEditorState.tsx index 38f9dcc39a..4c45f3649a 100644 --- a/packages/legend-data-cube/src/stores/view/filter/DataCubeFilterEditorState.tsx +++ b/packages/legend-data-cube/src/stores/view/filter/DataCubeFilterEditorState.tsx @@ -19,7 +19,7 @@ import { DataCubeQueryFilterGroupOperator, DataCubeQueryFilterOperator, } from '../../core/DataCubeQueryEngine.js'; -import type { DataCubeQuerySnapshot } from '../../core/DataCubeQuerySnapshot.js'; +import type { DataCubeSnapshot } from '../../core/DataCubeSnapshot.js'; import { deepClone, at, guaranteeNonNullable } from '@finos/legend-shared'; import type { DataCubeViewState } from '../DataCubeViewState.js'; import { @@ -28,9 +28,9 @@ import { DataCubeFilterEditorConditionGroupTreeNode, DataCubeFilterEditorConditionTreeNode, buildFilterEditorTree, - buildFilterQuerySnapshot, + buildFilterSnapshot, } from '../../core/filter/DataCubeQueryFilterEditorState.js'; -import { DataCubeQuerySnapshotController } from '../../services/DataCubeQuerySnapshotService.js'; +import { DataCubeSnapshotController } from '../../services/DataCubeSnapshotService.js'; import { DataCubeConfiguration, type DataCubeColumnConfiguration, @@ -43,7 +43,7 @@ import { _findCol } from '../../core/model/DataCubeColumn.js'; * This query editor state backs the form editor for filter. It batches changes made * to the filter in the form editor. */ -export class DataCubeFilterEditorState extends DataCubeQuerySnapshotController { +export class DataCubeFilterEditorState extends DataCubeSnapshotController { private readonly _view: DataCubeViewState; readonly display: DisplayState; @@ -288,8 +288,8 @@ export class DataCubeFilterEditorState extends DataCubeQuerySnapshotController { } override async applySnapshot( - snapshot: DataCubeQuerySnapshot, - previousSnapshot: DataCubeQuerySnapshot | undefined, + snapshot: DataCubeSnapshot, + previousSnapshot: DataCubeSnapshot | undefined, ): Promise { const configuration = DataCubeConfiguration.serialization.fromJson( snapshot.data.configuration, @@ -317,7 +317,7 @@ export class DataCubeFilterEditorState extends DataCubeQuerySnapshotController { const newSnapshot = baseSnapshot.clone(); newSnapshot.data.filter = this.tree.root - ? buildFilterQuerySnapshot(this.tree.root) + ? buildFilterSnapshot(this.tree.root) : undefined; newSnapshot.finalize(); diff --git a/packages/legend-data-cube/src/stores/view/grid/DataCubeGridClientEngine.ts b/packages/legend-data-cube/src/stores/view/grid/DataCubeGridClientEngine.ts index 56d579bb9e..5aa869f318 100644 --- a/packages/legend-data-cube/src/stores/view/grid/DataCubeGridClientEngine.ts +++ b/packages/legend-data-cube/src/stores/view/grid/DataCubeGridClientEngine.ts @@ -41,10 +41,10 @@ import type { DataCubeConfiguration, DataCubeConfigurationColorKey, } from '../../core/model/DataCubeConfiguration.js'; -import { type DataCubeQuerySnapshot } from '../../core/DataCubeQuerySnapshot.js'; +import { type DataCubeSnapshot } from '../../core/DataCubeSnapshot.js'; import { _findCol, _sortByColName } from '../../core/model/DataCubeColumn.js'; import { isPivotResultColumnName } from '../../core/DataCubeQueryEngine.js'; -import { buildQuerySnapshot } from './DataCubeGridQuerySnapshotBuilder.js'; +import { buildSnapshotFromGridState } from './DataCubeGridSnapshotBuilder.js'; import { AlertType } from '../../services/DataCubeAlertService.js'; import type { DataCubeViewState } from '../DataCubeViewState.js'; import { buildGridDataFetchExecutableQuery } from './DataCubeGridQueryBuilder.js'; @@ -178,7 +178,7 @@ export function getDataForAllFilteredNodes(client: GridApi): T[] { * This is used to manually trigger server-side row model data source getRows() method. */ export function computeHashCodeForDataFetchManualTrigger( - snapshot: DataCubeQuerySnapshot, + snapshot: DataCubeSnapshot, configuration: DataCubeConfiguration, ) { return hashObject( @@ -214,7 +214,7 @@ export function computeHashCodeForDataFetchManualTrigger( function buildRowData( result: TabularDataSet, - snapshot: DataCubeQuerySnapshot, + snapshot: DataCubeSnapshot, ): DataCubeGridClientRowData[] { return result.rows.map((_row, rowIdx) => { const row: DataCubeGridClientRowData = {}; @@ -248,7 +248,7 @@ function buildRowData( } async function getCastColumns( - currentSnapshot: DataCubeQuerySnapshot, + currentSnapshot: DataCubeSnapshot, view: DataCubeViewState, ) { if (!currentSnapshot.data.pivot) { @@ -344,7 +344,7 @@ export class DataCubeGridClientServerSideDataSource // only recompute the snapshot if this is not a drilldown request if (isTopLevelRequest) { - newSnapshot = buildQuerySnapshot(request, currentSnapshot); + newSnapshot = buildSnapshotFromGridState(request, currentSnapshot); // NOTE: if h-pivot is enabled, update the cast columns // and panels which might be affected by this (e.g. sorts) diff --git a/packages/legend-data-cube/src/stores/view/grid/DataCubeGridConfigurationBuilder.tsx b/packages/legend-data-cube/src/stores/view/grid/DataCubeGridConfigurationBuilder.tsx index 6d6c11fd92..25852c0348 100644 --- a/packages/legend-data-cube/src/stores/view/grid/DataCubeGridConfigurationBuilder.tsx +++ b/packages/legend-data-cube/src/stores/view/grid/DataCubeGridConfigurationBuilder.tsx @@ -18,13 +18,13 @@ * [GRID] * * These are utilities used to build the configuration for the grid client, - * AG Grid, from the query snapshot. + * AG Grid, from the snapshot. ***************************************************************************************/ import { - type DataCubeQuerySnapshot, - type DataCubeQuerySnapshotPivot, -} from '../../core/DataCubeQuerySnapshot.js'; + type DataCubeSnapshot, + type DataCubeSnapshotPivot, +} from '../../core/DataCubeSnapshot.js'; import { _findCol, _toCol, @@ -147,7 +147,7 @@ function DataCubeGridLoadingCellRenderer( type ColumnData = { name: string; - snapshot: DataCubeQuerySnapshot; + snapshot: DataCubeSnapshot; column: DataCubeColumnConfiguration; configuration: DataCubeConfiguration; }; @@ -346,7 +346,7 @@ function _displaySpec(columnData: ColumnData) { } function _groupDisplaySpec( - snapshot: DataCubeQuerySnapshot, + snapshot: DataCubeSnapshot, configuration: DataCubeConfiguration, ) { // TODO?: we can technically alternate the styling based on the column at various drilldown level @@ -633,7 +633,7 @@ export function generateBaseGridOptions(view: DataCubeViewState): GridOptions { function generatePivotResultColumnHeaderTooltip( id: string, - snapshot: DataCubeQuerySnapshot, + snapshot: DataCubeSnapshot, configuration: DataCubeConfiguration, ) { const values = id.split(PIVOT_COLUMN_NAME_VALUE_SEPARATOR); @@ -665,7 +665,7 @@ function generatePivotResultColumnHeaderTooltip( function generateDefinitionForPivotResultColumns( pivotResultColumns: DataCubeColumn[], - snapshot: DataCubeQuerySnapshot, + snapshot: DataCubeSnapshot, configuration: DataCubeConfiguration, ) { const columns = pivotResultColumns @@ -793,7 +793,7 @@ function generateDefinitionForPivotResultColumns( */ function rearrangePivotResultColumns( pivotResultColumns: DataCubeColumn[], - pivotData: DataCubeQuerySnapshotPivot, + pivotData: DataCubeSnapshotPivot, configuration: DataCubeConfiguration, ) { try { @@ -832,7 +832,7 @@ function rearrangePivotResultColumns( } export function generateColumnDefs( - snapshot: DataCubeQuerySnapshot, + snapshot: DataCubeSnapshot, configuration: DataCubeConfiguration, ) { // NOTE: only show columns which are fetched in select() as we @@ -923,7 +923,7 @@ export function generateColumnDefs( } export function generateGridOptionsFromSnapshot( - snapshot: DataCubeQuerySnapshot, + snapshot: DataCubeSnapshot, configuration: DataCubeConfiguration, view: DataCubeViewState, ): GridOptions { @@ -989,7 +989,7 @@ export function generateGridOptionsFromSnapshot( // -------------------------------------- EVENT HANDLERS -------------------------------------- // NOTE: make sure the event source must not be 'api' since these handlers are meant for direct // user interaction with the grid. Actions through context menu (i.e. grid controller) or programatic - // update of the grid options due to change in query snapshot should not trigger these handlers. + // update of the grid options due to change in snapshot should not trigger these handlers. onColumnPinned: (event) => { if (event.source !== 'api' && event.column) { diff --git a/packages/legend-data-cube/src/stores/view/grid/DataCubeGridControllerState.ts b/packages/legend-data-cube/src/stores/view/grid/DataCubeGridControllerState.ts index d765297d70..b0cb4b2445 100644 --- a/packages/legend-data-cube/src/stores/view/grid/DataCubeGridControllerState.ts +++ b/packages/legend-data-cube/src/stores/view/grid/DataCubeGridControllerState.ts @@ -22,15 +22,15 @@ import { } from '@finos/legend-shared'; import { DataCubeConfiguration } from '../../core/model/DataCubeConfiguration.js'; import { - type DataCubeQuerySnapshot, - type DataCubeQuerySnapshotSortColumn, -} from '../../core/DataCubeQuerySnapshot.js'; + type DataCubeSnapshot, + type DataCubeSnapshotSortColumn, +} from '../../core/DataCubeSnapshot.js'; import { _findCol, _toCol, type DataCubeColumn, } from '../../core/model/DataCubeColumn.js'; -import { DataCubeQuerySnapshotController } from '../../services/DataCubeQuerySnapshotService.js'; +import { DataCubeSnapshotController } from '../../services/DataCubeSnapshotService.js'; import { type DataCubeColumnPinPlacement, DataCubeColumnKind, @@ -49,13 +49,13 @@ import type { DataCubeViewState } from '../DataCubeViewState.js'; import { generateMenuBuilder } from './DataCubeGridMenuBuilder.js'; import { buildFilterEditorTree, - buildFilterQuerySnapshot, + buildFilterSnapshot, DataCubeFilterEditorConditionGroupTreeNode, type DataCubeFilterEditorConditionTreeNode, type DataCubeFilterEditorTree, type DataCubeFilterEditorTreeNode, } from '../../core/filter/DataCubeQueryFilterEditorState.js'; -import { _pruneExpandedPaths } from '../../core/DataCubeQuerySnapshotBuilderUtils.js'; +import { _pruneExpandedPaths } from '../../core/DataCubeSnapshotBuilderUtils.js'; /** * This query editor state is responsible for capturing updates to the data cube query @@ -72,7 +72,7 @@ import { _pruneExpandedPaths } from '../../core/DataCubeQuerySnapshotBuilderUtil * we MUST NEVER use the editor here, as it could potentially create illegal state * while the editor is still in the middle of a modification that has not been applied. */ -export class DataCubeGridControllerState extends DataCubeQuerySnapshotController { +export class DataCubeGridControllerState extends DataCubeSnapshotController { readonly view: DataCubeViewState; constructor(view: DataCubeViewState) { @@ -342,7 +342,7 @@ export class DataCubeGridControllerState extends DataCubeQuerySnapshotController // --------------------------------- SORT --------------------------------- - sortColumns: DataCubeQuerySnapshotSortColumn[] = []; + sortColumns: DataCubeSnapshotSortColumn[] = []; getSortableColumn(colName: string | undefined) { if (!colName) { @@ -399,8 +399,8 @@ export class DataCubeGridControllerState extends DataCubeQuerySnapshotController } override async applySnapshot( - snapshot: DataCubeQuerySnapshot, - previousSnapshot: DataCubeQuerySnapshot | undefined, + snapshot: DataCubeSnapshot, + previousSnapshot: DataCubeSnapshot | undefined, ) { this.configuration = DataCubeConfiguration.serialization.fromJson( snapshot.data.configuration, @@ -430,7 +430,7 @@ export class DataCubeGridControllerState extends DataCubeQuerySnapshotController this.menuBuilder = generateMenuBuilder(this); } - private propagateChanges(baseSnapshot: DataCubeQuerySnapshot) { + private propagateChanges(baseSnapshot: DataCubeSnapshot) { this.verticalPivotColumns = this.verticalPivotColumns.filter( (col) => !_findCol(this.horizontalPivotColumns, col.name), ); @@ -496,7 +496,7 @@ export class DataCubeGridControllerState extends DataCubeQuerySnapshotController snapshot.data.configuration = this.configuration.serialize(); snapshot.data.filter = this.filterTree.root - ? buildFilterQuerySnapshot(this.filterTree.root) + ? buildFilterSnapshot(this.filterTree.root) : undefined; snapshot.data.selectColumns = this.selectColumns; snapshot.data.pivot = this.horizontalPivotColumns.length diff --git a/packages/legend-data-cube/src/stores/view/grid/DataCubeGridQueryBuilder.ts b/packages/legend-data-cube/src/stores/view/grid/DataCubeGridQueryBuilder.ts index 16f356ba41..6f2a6427e4 100644 --- a/packages/legend-data-cube/src/stores/view/grid/DataCubeGridQueryBuilder.ts +++ b/packages/legend-data-cube/src/stores/view/grid/DataCubeGridQueryBuilder.ts @@ -24,7 +24,7 @@ import { V1_getGenericTypeFullPath, V1_createRelationTypeColumn, } from '@finos/legend-graph'; -import { type DataCubeQuerySnapshot } from '../../core/DataCubeQuerySnapshot.js'; +import { type DataCubeSnapshot } from '../../core/DataCubeSnapshot.js'; import { DataCubeQueryFilterGroupOperator, DataCubeQueryFilterOperator, @@ -61,7 +61,7 @@ import { type DataCubeGridClientDataFetchRequest, } from './DataCubeGridClientEngine.js'; import { DataCubeConfiguration } from '../../core/model/DataCubeConfiguration.js'; -import { _colSpecArrayParam } from '../../core/DataCubeQuerySnapshotBuilderUtils.js'; +import { _colSpecArrayParam } from '../../core/DataCubeSnapshotBuilderUtils.js'; import { buildExecutableQuery } from '../../core/DataCubeQueryBuilder.js'; import type { DataCubeSource } from '../../core/model/DataCubeSource.js'; import type { DataCubeEngine } from '../../core/DataCubeEngine.js'; @@ -151,7 +151,7 @@ function _addCountAggColumnToPivot( export function buildGridDataFetchExecutableQuery( request: DataCubeGridClientDataFetchRequest, - snapshot: DataCubeQuerySnapshot, + snapshot: DataCubeSnapshot, source: DataCubeSource, engine: DataCubeEngine, enablePagination: boolean, @@ -192,7 +192,7 @@ function generateGridDataFetchExecutableQueryPostProcessor( request: DataCubeGridClientDataFetchRequest, ) { return ( - snapshot: DataCubeQuerySnapshot, + snapshot: DataCubeSnapshot, sequence: V1_AppliedFunction[], funcMap: DataCubeQueryFunctionMap, configuration: DataCubeConfiguration, diff --git a/packages/legend-data-cube/src/stores/view/grid/DataCubeGridQuerySnapshotBuilder.ts b/packages/legend-data-cube/src/stores/view/grid/DataCubeGridSnapshotBuilder.ts similarity index 93% rename from packages/legend-data-cube/src/stores/view/grid/DataCubeGridQuerySnapshotBuilder.ts rename to packages/legend-data-cube/src/stores/view/grid/DataCubeGridSnapshotBuilder.ts index 54d4b77a58..51e0b16aec 100644 --- a/packages/legend-data-cube/src/stores/view/grid/DataCubeGridQuerySnapshotBuilder.ts +++ b/packages/legend-data-cube/src/stores/view/grid/DataCubeGridSnapshotBuilder.ts @@ -17,11 +17,11 @@ /*************************************************************************************** * [GRID] * - * These are utilities used to build the query snapshot from the internal state - * of the grid client, AG Grid. + * These are utilities used to build the snapshot from the internal state of the grid + * client, AG Grid. ***************************************************************************************/ -import { type DataCubeQuerySnapshot } from '../../core/DataCubeQuerySnapshot.js'; +import { type DataCubeSnapshot } from '../../core/DataCubeSnapshot.js'; import { _toCol } from '../../core/model/DataCubeColumn.js'; import { DataCubeGridClientSortDirection, @@ -36,7 +36,7 @@ import { } from '../../core/DataCubeQueryEngine.js'; import { DataCubeConfiguration } from '../../core/model/DataCubeConfiguration.js'; import { guaranteeNonNullable, uniqBy } from '@finos/legend-shared'; -import { _pruneExpandedPaths } from '../../core/DataCubeQuerySnapshotBuilderUtils.js'; +import { _pruneExpandedPaths } from '../../core/DataCubeSnapshotBuilderUtils.js'; export function getColumnConfiguration( colName: string, @@ -50,9 +50,9 @@ export function getColumnConfiguration( // --------------------------------- MAIN --------------------------------- -export function buildQuerySnapshot( +export function buildSnapshotFromGridState( request: DataCubeGridClientDataFetchRequest, - baseSnapshot: DataCubeQuerySnapshot, + baseSnapshot: DataCubeSnapshot, ) { const snapshot = baseSnapshot.clone(); const configuration = DataCubeConfiguration.serialization.fromJson( diff --git a/packages/legend-data-cube/src/stores/view/grid/DataCubeGridState.ts b/packages/legend-data-cube/src/stores/view/grid/DataCubeGridState.ts index dfe144f76b..97290e3bc2 100644 --- a/packages/legend-data-cube/src/stores/view/grid/DataCubeGridState.ts +++ b/packages/legend-data-cube/src/stores/view/grid/DataCubeGridState.ts @@ -31,8 +31,8 @@ import { computeHashCodeForDataFetchManualTrigger, INTERNAL__GRID_CLIENT_DEFAULT_ENABLE_CACHING, } from './DataCubeGridClientEngine.js'; -import { DataCubeQuerySnapshotController } from '../../services/DataCubeQuerySnapshotService.js'; -import type { DataCubeQuerySnapshot } from '../../core/DataCubeQuerySnapshot.js'; +import { DataCubeSnapshotController } from '../../services/DataCubeSnapshotService.js'; +import type { DataCubeSnapshot } from '../../core/DataCubeSnapshot.js'; import { generateGridOptionsFromSnapshot } from './DataCubeGridConfigurationBuilder.js'; import { DataCubeConfiguration } from '../../core/model/DataCubeConfiguration.js'; import { DataCubeGridControllerState } from './DataCubeGridControllerState.js'; @@ -56,7 +56,7 @@ import { AlertType } from '../../services/DataCubeAlertService.js'; * row model datasource, so without the companion grid controller, these changes will not * trigger publishing a new snapshot, hence not propagated. */ -export class DataCubeGridState extends DataCubeQuerySnapshotController { +export class DataCubeGridState extends DataCubeSnapshotController { private readonly _view: DataCubeViewState; readonly controller: DataCubeGridControllerState; @@ -126,7 +126,12 @@ export class DataCubeGridState extends DataCubeQuerySnapshotController { }); } - async setCachingEnabled(val: boolean) { + async setCachingEnabled( + val: boolean, + options?: { + suppressWarning?: boolean | undefined; + }, + ) { if (val === this.isCachingEnabled) { return; } @@ -172,11 +177,12 @@ export class DataCubeGridState extends DataCubeQuerySnapshotController { if ( this._settingService.getBooleanValue( DataCubeSettingKey.GRID_CLIENT__SHOW_CACHE_PERFORMANCE_WARNING, - ) + ) && + !options?.suppressWarning ) { this._view.alertService.alert({ message: `Confirm you want to proceed with caching`, - text: `When enabled, the source dataset will be cached locally in order to boost query performance. But depending on computational resource available to your environment, sometimes, caching can negatively impact the overall performance, and can even lead to crashes.\nDo you still want to proceed?`, + text: `When enabled, the source dataset will be cached locally in order to boost query performance. But depending on computational resource available to your environment, sometimes, caching can negatively impact the overall performance, and can even lead to crashes.\n\nOverall, caching is still an experimental feature where we only support queries with simple execution plans, certain queries might not work, in which case, you can abort by turning off caching.\n\nDo you still want to proceed?`, type: AlertType.WARNING, actions: [ { @@ -208,7 +214,7 @@ export class DataCubeGridState extends DataCubeQuerySnapshotController { windowConfig: { ...DEFAULT_ALERT_WINDOW_CONFIG, width: 600, - height: 210, + height: 300, minWidth: 300, minHeight: 150, }, @@ -243,8 +249,8 @@ export class DataCubeGridState extends DataCubeQuerySnapshotController { } override async applySnapshot( - snapshot: DataCubeQuerySnapshot, - previousSnapshot: DataCubeQuerySnapshot | undefined, + snapshot: DataCubeSnapshot, + previousSnapshot: DataCubeSnapshot | undefined, ) { const configuration = DataCubeConfiguration.serialization.fromJson( snapshot.data.configuration, diff --git a/packages/legend-graph/src/graph-manager/AbstractPureGraphManager.ts b/packages/legend-graph/src/graph-manager/AbstractPureGraphManager.ts index 7c907f5292..654d6bdc5f 100644 --- a/packages/legend-graph/src/graph-manager/AbstractPureGraphManager.ts +++ b/packages/legend-graph/src/graph-manager/AbstractPureGraphManager.ts @@ -115,9 +115,9 @@ import type { RelationTypeMetadata } from './action/relation/RelationTypeMetadat import type { CodeCompletionResult } from './action/compilation/Completion.js'; import type { DeploymentResult } from './action/DeploymentResult.js'; import type { - LightPersistentDataCubeQuery, - PersistentDataCubeQuery, -} from './action/query/PersistentDataCubeQuery.js'; + LightPersistentDataCube, + PersistentDataCube, +} from './action/query/PersistentDataCube.js'; export interface TEMPORARY__EngineSetupConfig { env: string; @@ -638,22 +638,20 @@ export abstract class AbstractPureGraphManager { graphLoader: () => Promise[]>, ): Promise<{ mapping: string | undefined; runtime: string }>; - // ------------------------------------------- DataCube Query ------------------------------------------- + // ------------------------------------------- DataCube ------------------------------------------- - abstract searchDataCubeQueries( + abstract searchDataCubes( searchSpecification: QuerySearchSpecification, - ): Promise; - abstract getDataCubeQueries( - queryIds: string[], - ): Promise; - abstract getDataCubeQuery(queryId: string): Promise; - abstract createDataCubeQuery( - query: PersistentDataCubeQuery, - ): Promise; - abstract updateDataCubeQuery( - query: PersistentDataCubeQuery, - ): Promise; - abstract deleteDataCubeQuery(queryId: string): Promise; + ): Promise; + abstract getDataCubes(ids: string[]): Promise; + abstract getDataCube(id: string): Promise; + abstract createDataCube( + dataCube: PersistentDataCube, + ): Promise; + abstract updateDataCube( + dataCube: PersistentDataCube, + ): Promise; + abstract deleteDataCube(id: string): Promise; // -------------------------------------- Analysis -------------------------------------- diff --git a/packages/legend-graph/src/graph-manager/action/query/PersistentDataCubeQuery.ts b/packages/legend-graph/src/graph-manager/action/query/PersistentDataCube.ts similarity index 86% rename from packages/legend-graph/src/graph-manager/action/query/PersistentDataCubeQuery.ts rename to packages/legend-graph/src/graph-manager/action/query/PersistentDataCube.ts index eb751fa7fc..7d3d81db2b 100644 --- a/packages/legend-graph/src/graph-manager/action/query/PersistentDataCubeQuery.ts +++ b/packages/legend-graph/src/graph-manager/action/query/PersistentDataCube.ts @@ -17,7 +17,7 @@ import { SerializationFactory, type PlainObject } from '@finos/legend-shared'; import { createModelSchema, optional, primitive, raw } from 'serializr'; -export class PersistentDataCubeQuery { +export class PersistentDataCube { id!: string; name!: string; description: string | undefined; @@ -29,7 +29,7 @@ export class PersistentDataCubeQuery { lastOpenAt?: number | undefined; static readonly serialization = new SerializationFactory( - createModelSchema(PersistentDataCubeQuery, { + createModelSchema(PersistentDataCube, { id: primitive(), lastUpdatedAt: optional(primitive()), createdAt: optional(primitive()), @@ -42,13 +42,13 @@ export class PersistentDataCubeQuery { ); clone() { - return PersistentDataCubeQuery.serialization.fromJson( - PersistentDataCubeQuery.serialization.toJson(this), + return PersistentDataCube.serialization.fromJson( + PersistentDataCube.serialization.toJson(this), ); } } -export class LightPersistentDataCubeQuery { +export class LightPersistentDataCube { id!: string; name!: string; description: string | undefined; @@ -60,7 +60,7 @@ export class LightPersistentDataCubeQuery { lastOpenAt?: number | undefined; static readonly serialization = new SerializationFactory( - createModelSchema(LightPersistentDataCubeQuery, { + createModelSchema(LightPersistentDataCube, { id: primitive(), lastUpdatedAt: optional(primitive()), createdAt: optional(primitive()), diff --git a/packages/legend-graph/src/graph-manager/protocol/pure/v1/V1_PureGraphManager.ts b/packages/legend-graph/src/graph-manager/protocol/pure/v1/V1_PureGraphManager.ts index f66b50f96f..bff8210ca0 100644 --- a/packages/legend-graph/src/graph-manager/protocol/pure/v1/V1_PureGraphManager.ts +++ b/packages/legend-graph/src/graph-manager/protocol/pure/v1/V1_PureGraphManager.ts @@ -343,9 +343,9 @@ import type { CodeCompletionResult } from '../../../action/compilation/Completio import { V1_CompleteCodeInput } from './engine/compilation/V1_CompleteCodeInput.js'; import type { DeploymentResult } from '../../../action/DeploymentResult.js'; import type { - LightPersistentDataCubeQuery, - PersistentDataCubeQuery, -} from '../../../action/query/PersistentDataCubeQuery.js'; + LightPersistentDataCube, + PersistentDataCube, +} from '../../../action/query/PersistentDataCube.js'; import { V1_QueryParameterValue } from './engine/query/V1_Query.js'; class V1_PureModelContextDataIndex { @@ -3266,43 +3266,39 @@ export class V1_PureGraphManager extends AbstractPureGraphManager { } } - // --------------------------------------------- DataCube Query --------------------------------------------- + // --------------------------------------------- DataCube --------------------------------------------- - override searchDataCubeQueries( + override searchDataCubes( searchSpecification: QuerySearchSpecification, - ): Promise { - return this.engine.searchDataCubeQueries( + ): Promise { + return this.engine.searchDataCubes( V1_transformQuerySearchSpecification(searchSpecification), ); } - override getDataCubeQueries( - queryIds: string[], - ): Promise { - return this.engine.getDataCubeQueries(queryIds); + override getDataCubes(ids: string[]): Promise { + return this.engine.getDataCubes(ids); } - override async getDataCubeQuery( - queryId: string, - ): Promise { - const query = await this.engine.getDataCubeQuery(queryId); + override async getDataCube(id: string): Promise { + const query = await this.engine.getDataCube(id); return query; } - override async createDataCubeQuery( - query: PersistentDataCubeQuery, - ): Promise { - return this.engine.createDataCubeQuery(query); + override async createDataCube( + dataCube: PersistentDataCube, + ): Promise { + return this.engine.createDataCube(dataCube); } - override updateDataCubeQuery( - query: PersistentDataCubeQuery, - ): Promise { - return this.engine.updateDataCubeQuery(query); + override updateDataCube( + dataCube: PersistentDataCube, + ): Promise { + return this.engine.updateDataCube(dataCube); } - override async deleteDataCubeQuery(queryId: string): Promise { - await this.engine.deleteDataCubeQuery(queryId); + override async deleteDataCube(id: string): Promise { + await this.engine.deleteDataCube(id); } // --------------------------------------------- Analysis --------------------------------------------- diff --git a/packages/legend-graph/src/graph-manager/protocol/pure/v1/engine/V1_EngineServerClient.ts b/packages/legend-graph/src/graph-manager/protocol/pure/v1/engine/V1_EngineServerClient.ts index f90fed3a08..0923d3afa3 100644 --- a/packages/legend-graph/src/graph-manager/protocol/pure/v1/engine/V1_EngineServerClient.ts +++ b/packages/legend-graph/src/graph-manager/protocol/pure/v1/engine/V1_EngineServerClient.ts @@ -88,7 +88,7 @@ import type { V1_RelationType } from '../model/packageableElements/type/V1_Relat import type { CodeCompletionResult } from '../../../../action/compilation/Completion.js'; import type { V1_CompleteCodeInput } from './compilation/V1_CompleteCodeInput.js'; import type { DeploymentResult } from '../../../../action/DeploymentResult.js'; -import type { PersistentDataCubeQuery } from '../../../../action/query/PersistentDataCubeQuery.js'; +import type { PersistentDataCube } from '../../../../action/query/PersistentDataCube.js'; enum CORE_ENGINE_ACTIVITY_TRACE { GRAMMAR_TO_JSON = 'transform Pure code to protocol', @@ -124,6 +124,10 @@ enum CORE_ENGINE_ACTIVITY_TRACE { PATCH_QUERY = 'patch query', DELETE_QUERY = 'delete query', + CREATE_DATA_CUBE = 'create DataCube', + UPDATE_DATA_CUBE = 'update DataCube', + DELETE_DATA_CUBE = 'delete DataCube', + CANCEL_USER_EXECUTIONS = 'cancel user executions', MAPPING_MODEL_COVERAGE_ANALYTICS = 'mapping model coverage analytics', @@ -853,53 +857,43 @@ export class V1_EngineServerClient extends AbstractServerClient { this._query(queryId), ); - // ------------------------------------------- DataCube Query ------------------------------------------- + // ------------------------------------------- DataCube ------------------------------------------- - _dataCubeQuery = (queryId?: string): string => + _dataCube = (id?: string): string => `${this.queryBaseUrl ?? this.baseUrl}/pure/v1/query/dataCube${ - queryId ? `/${encodeURIComponent(queryId)}` : '' + id ? `/${encodeURIComponent(id)}` : '' }`; - searchDataCubeQueries = ( + searchDataCubes = ( searchSpecification: PlainObject, - ): Promise[]> => - this.post( - `${this._dataCubeQuery()}/search`, - searchSpecification, - undefined, - ); - getDataCubeQueries = ( - queryIds: string[], - ): Promise[]> => - this.get(`${this._dataCubeQuery()}/batch`, {}, undefined, { - queryIds, + ): Promise[]> => + this.post(`${this._dataCube()}/search`, searchSpecification, undefined); + getDataCubes = (ids: string[]): Promise[]> => + this.get(`${this._dataCube()}/batch`, {}, undefined, { + queryIds: ids, }); - getDataCubeQuery = ( - queryId: string, - ): Promise> => - this.get(this._dataCubeQuery(queryId)); - createDataCubeQuery = ( - query: PlainObject, - ): Promise> => + getDataCube = (id: string): Promise> => + this.get(this._dataCube(id)); + createDataCube = ( + dataCube: PlainObject, + ): Promise> => this.postWithTracing( - this.getTraceData(CORE_ENGINE_ACTIVITY_TRACE.CREATE_QUERY), - this._dataCubeQuery(), - query, + this.getTraceData(CORE_ENGINE_ACTIVITY_TRACE.CREATE_DATA_CUBE), + this._dataCube(), + dataCube, ); - updateDataCubeQuery = ( - queryId: string, - query: PlainObject, - ): Promise> => + updateDataCube = ( + id: string, + dataCube: PlainObject, + ): Promise> => this.putWithTracing( - this.getTraceData(CORE_ENGINE_ACTIVITY_TRACE.UPDATE_QUERY), - this._dataCubeQuery(queryId), - query, + this.getTraceData(CORE_ENGINE_ACTIVITY_TRACE.UPDATE_DATA_CUBE), + this._dataCube(id), + dataCube, ); - deleteDataCubeQuery = ( - queryId: string, - ): Promise> => + deleteDataCube = (id: string): Promise> => this.deleteWithTracing( - this.getTraceData(CORE_ENGINE_ACTIVITY_TRACE.DELETE_QUERY), - this._dataCubeQuery(queryId), + this.getTraceData(CORE_ENGINE_ACTIVITY_TRACE.DELETE_DATA_CUBE), + this._dataCube(id), ); // --------------------------------------- Analysis --------------------------------------- diff --git a/packages/legend-graph/src/graph-manager/protocol/pure/v1/engine/V1_GraphManagerEngine.ts b/packages/legend-graph/src/graph-manager/protocol/pure/v1/engine/V1_GraphManagerEngine.ts index cd96114dbf..3e0d7abe31 100644 --- a/packages/legend-graph/src/graph-manager/protocol/pure/v1/engine/V1_GraphManagerEngine.ts +++ b/packages/legend-graph/src/graph-manager/protocol/pure/v1/engine/V1_GraphManagerEngine.ts @@ -92,9 +92,9 @@ import type { V1_CompleteCodeInput } from './compilation/V1_CompleteCodeInput.js import type { CodeCompletionResult } from '../../../../action/compilation/Completion.js'; import type { DeploymentResult } from '../../../../action/DeploymentResult.js'; import type { - LightPersistentDataCubeQuery, - PersistentDataCubeQuery, -} from '../../../../action/query/PersistentDataCubeQuery.js'; + LightPersistentDataCube, + PersistentDataCube, +} from '../../../../action/query/PersistentDataCube.js'; export interface V1_GraphManagerEngine { config: TEMPORARY__AbstractEngineConfig; @@ -341,27 +341,16 @@ export interface V1_GraphManagerEngine { getCurrentUserId: () => string | undefined; - // ------------------------------------------- DataCube Query ------------------------------------------- + // ------------------------------------------- DataCube ------------------------------------------- - searchDataCubeQueries: ( + searchDataCubes: ( searchSpecification: V1_QuerySearchSpecification, - ) => Promise; - - getDataCubeQueries: ( - queryIds: string[], - ) => Promise; - - getDataCubeQuery: (id: string) => Promise; - - createDataCubeQuery: ( - query: PersistentDataCubeQuery, - ) => Promise; - - updateDataCubeQuery: ( - query: PersistentDataCubeQuery, - ) => Promise; - - deleteDataCubeQuery: (id: string) => Promise; + ) => Promise; + getDataCubes: (ids: string[]) => Promise; + getDataCube: (id: string) => Promise; + createDataCube: (dataCube: PersistentDataCube) => Promise; + updateDataCube: (dataCube: PersistentDataCube) => Promise; + deleteDataCube: (id: string) => Promise; // ------------------------------------------ Analysis ------------------------------------------ diff --git a/packages/legend-graph/src/graph-manager/protocol/pure/v1/engine/V1_RemoteEngine.ts b/packages/legend-graph/src/graph-manager/protocol/pure/v1/engine/V1_RemoteEngine.ts index 188415290f..7216b843d2 100644 --- a/packages/legend-graph/src/graph-manager/protocol/pure/v1/engine/V1_RemoteEngine.ts +++ b/packages/legend-graph/src/graph-manager/protocol/pure/v1/engine/V1_RemoteEngine.ts @@ -163,9 +163,9 @@ import { V1_CompleteCodeInput } from './compilation/V1_CompleteCodeInput.js'; import { CodeCompletionResult } from '../../../../action/compilation/Completion.js'; import { DeploymentResult } from '../../../../action/DeploymentResult.js'; import { - LightPersistentDataCubeQuery, - PersistentDataCubeQuery, -} from '../../../../action/query/PersistentDataCubeQuery.js'; + LightPersistentDataCube, + PersistentDataCube, +} from '../../../../action/query/PersistentDataCube.js'; import { V1_getGenericTypeFullPath } from '../helpers/V1_DomainHelper.js'; import { V1_relationTypeModelSchema } from '../transformation/pureProtocol/serializationHelpers/V1_TypeSerializationHelper.js'; @@ -1174,55 +1174,51 @@ export class V1_RemoteEngine implements V1_GraphManagerEngine { } // ------------------------------------------ QueryData Cube ------------------------------------------ - async searchDataCubeQueries( + async searchDataCubes( searchSpecification: V1_QuerySearchSpecification, - ): Promise { + ): Promise { return ( - await this.engineServerClient.searchDataCubeQueries( + await this.engineServerClient.searchDataCubes( V1_QuerySearchSpecification.serialization.toJson(searchSpecification), ) - ).map((query) => - LightPersistentDataCubeQuery.serialization.fromJson(query), - ); + ).map((query) => LightPersistentDataCube.serialization.fromJson(query)); } - async getDataCubeQueries( - queryIds: string[], - ): Promise { - return (await this.engineServerClient.getDataCubeQueries(queryIds)).map( - (query) => LightPersistentDataCubeQuery.serialization.fromJson(query), + async getDataCubes(ids: string[]): Promise { + return (await this.engineServerClient.getDataCubes(ids)).map((query) => + LightPersistentDataCube.serialization.fromJson(query), ); } - async getDataCubeQuery(id: string): Promise { - return PersistentDataCubeQuery.serialization.fromJson( - await this.engineServerClient.getDataCubeQuery(id), + async getDataCube(id: string): Promise { + return PersistentDataCube.serialization.fromJson( + await this.engineServerClient.getDataCube(id), ); } - async createDataCubeQuery( - query: PersistentDataCubeQuery, - ): Promise { - return PersistentDataCubeQuery.serialization.fromJson( - await this.engineServerClient.createDataCubeQuery( - PersistentDataCubeQuery.serialization.toJson(query), + async createDataCube( + dataCube: PersistentDataCube, + ): Promise { + return PersistentDataCube.serialization.fromJson( + await this.engineServerClient.createDataCube( + PersistentDataCube.serialization.toJson(dataCube), ), ); } - async updateDataCubeQuery( - query: PersistentDataCubeQuery, - ): Promise { - return PersistentDataCubeQuery.serialization.fromJson( - await this.engineServerClient.updateDataCubeQuery( - query.id, - PersistentDataCubeQuery.serialization.toJson(query), + async updateDataCube( + dataCube: PersistentDataCube, + ): Promise { + return PersistentDataCube.serialization.fromJson( + await this.engineServerClient.updateDataCube( + dataCube.id, + PersistentDataCube.serialization.toJson(dataCube), ), ); } - async deleteDataCubeQuery(queryId: string): Promise { - await this.engineServerClient.deleteDataCubeQuery(queryId); + async deleteDataCube(queryId: string): Promise { + await this.engineServerClient.deleteDataCube(queryId); } // ------------------------------------------ Analysis ------------------------------------------ diff --git a/packages/legend-graph/src/index.ts b/packages/legend-graph/src/index.ts index a4199cd35b..ceaa0216e5 100644 --- a/packages/legend-graph/src/index.ts +++ b/packages/legend-graph/src/index.ts @@ -232,9 +232,9 @@ export { INTERNAL__UnknownExecutionResult } from './graph-manager/action/executi export { INTERNAL__UnknownExecutionNode } from './graph/metamodel/pure/executionPlan/nodes/INTERNAL__UnknownExecutionNode.js'; export { INTERNAL__UnknownResultType } from './graph/metamodel/pure/executionPlan/result/INTERNAL__UnknownResultType.js'; export { - PersistentDataCubeQuery, - LightPersistentDataCubeQuery, -} from './graph-manager/action/query/PersistentDataCubeQuery.js'; + PersistentDataCube, + LightPersistentDataCube, +} from './graph-manager/action/query/PersistentDataCube.js'; export * from './graph/metamodel/pure/executionPlan/ExecutionPlan.js'; export { ExecutionNode } from './graph/metamodel/pure/executionPlan/nodes/ExecutionNode.js'; diff --git a/packages/legend-query-builder/src/components/data-cube/QueryBuilderDataCube.tsx b/packages/legend-query-builder/src/components/data-cube/QueryBuilderDataCube.tsx index 64af35c9e4..e4d0952a0d 100644 --- a/packages/legend-query-builder/src/components/data-cube/QueryBuilderDataCube.tsx +++ b/packages/legend-query-builder/src/components/data-cube/QueryBuilderDataCube.tsx @@ -72,7 +72,10 @@ export const QueryDataCubeViewer = observer( options?.fullScreen, })} > - +
{!options?.fullScreen && ( diff --git a/packages/legend-query-builder/src/stores/data-cube/QueryBuilderDataCubeEngine.ts b/packages/legend-query-builder/src/stores/data-cube/QueryBuilderDataCubeEngine.ts index ec2ccfce21..b2c8475ba1 100644 --- a/packages/legend-query-builder/src/stores/data-cube/QueryBuilderDataCubeEngine.ts +++ b/packages/legend-query-builder/src/stores/data-cube/QueryBuilderDataCubeEngine.ts @@ -35,7 +35,7 @@ import { _function, DataCubeFunction, type CompletionItem, - DataCubeQuery, + DataCubeSpecification, } from '@finos/legend-data-cube'; import { guaranteeType, @@ -75,7 +75,7 @@ export class QueryBuilderDataCubeEngine extends DataCubeEngine { this.parameters = selectQuery.parameters; } - override async processQuerySource(sourceData: PlainObject) { + override async processSource(sourceData: PlainObject) { // TODO: this is an abnormal usage of this method, this is the place // where we can enforce which source this engine supports, instead // of hardcoding the logic like this. @@ -239,19 +239,18 @@ export class QueryBuilderDataCubeEngine extends DataCubeEngine { return srcFuncExp; } - async generateInitialQuery() { + async generateInitialSpecification() { const columns = (await this.getRelationType(this.selectInitialQuery)) .columns; - const query = new DataCubeQuery(); - query.query = `~[${columns.map((e) => `'${e.name}'`)}]->select()`; - - return query; + const specification = new DataCubeSpecification(); + specification.query = `~[${columns.map((e) => `'${e.name}'`)}]->select()`; + return specification; } - async getRelationType(query: RawLambda) { + async getRelationType(lambda: RawLambda) { const relationType = await this.graphState.graphManager.getLambdaRelationType( - query, + lambda, this.graphState.graph, ); return relationType; diff --git a/packages/legend-query-builder/src/stores/data-cube/QueryBuilderDataCubeHelper.ts b/packages/legend-query-builder/src/stores/data-cube/QueryBuilderDataCubeHelper.ts index 67c52f873b..e87e058d6a 100644 --- a/packages/legend-query-builder/src/stores/data-cube/QueryBuilderDataCubeHelper.ts +++ b/packages/legend-query-builder/src/stores/data-cube/QueryBuilderDataCubeHelper.ts @@ -54,6 +54,6 @@ export const createDataCubeViewerStateFromQueryBuilder = async ( queryBuilderState.graphManagerState, ); queryBuilderState.setLambdaWriteMode(currentLambdaWriterMode); - const query = await engine.generateInitialQuery(); + const query = await engine.generateInitialSpecification(); return new QueryBuilderDataCubeViewerState(query, engine); }; diff --git a/packages/legend-query-builder/src/stores/data-cube/QueryBuilderDataCubeViewerState.ts b/packages/legend-query-builder/src/stores/data-cube/QueryBuilderDataCubeViewerState.ts index 83077cbd80..215a26006b 100644 --- a/packages/legend-query-builder/src/stores/data-cube/QueryBuilderDataCubeViewerState.ts +++ b/packages/legend-query-builder/src/stores/data-cube/QueryBuilderDataCubeViewerState.ts @@ -14,15 +14,18 @@ * limitations under the License. */ -import type { DataCubeQuery } from '@finos/legend-data-cube'; +import type { DataCubeSpecification } from '@finos/legend-data-cube'; import type { QueryBuilderDataCubeEngine } from './QueryBuilderDataCubeEngine.js'; export class QueryBuilderDataCubeViewerState { - query: DataCubeQuery; - engine: QueryBuilderDataCubeEngine; + readonly specification: DataCubeSpecification; + readonly engine: QueryBuilderDataCubeEngine; - constructor(query: DataCubeQuery, engine: QueryBuilderDataCubeEngine) { - this.query = query; + constructor( + specification: DataCubeSpecification, + engine: QueryBuilderDataCubeEngine, + ) { + this.specification = specification; this.engine = engine; } } diff --git a/packages/legend-vscode-extension-dependencies/src/index.ts b/packages/legend-vscode-extension-dependencies/src/index.ts index 38e745e0ae..e96fd8f570 100644 --- a/packages/legend-vscode-extension-dependencies/src/index.ts +++ b/packages/legend-vscode-extension-dependencies/src/index.ts @@ -100,9 +100,9 @@ export { getFunctionNameWithPath, getFunctionSignature, GraphManagerState, - LightPersistentDataCubeQuery, + LightPersistentDataCube, PackageableElementExplicitReference, - PersistentDataCubeQuery, + PersistentDataCube, PostValidationAssertionResult, PureExecution, RawLambda, @@ -318,6 +318,6 @@ export { DataCube, DataCubeEngine, DataCubeFunction, - DataCubeQuery, + DataCubeSpecification, DataCubeSource, } from '@finos/legend-data-cube';