Skip to content

Commit

Permalink
datacube: rename DataCubeQuery to DataCubeSpecification and UX enhanc…
Browse files Browse the repository at this point in the history
…ements (#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
  • Loading branch information
akphi authored Feb 11, 2025
1 parent ae44c67 commit f8cd2bf
Show file tree
Hide file tree
Showing 141 changed files with 3,198 additions and 1,794 deletions.
11 changes: 11 additions & 0 deletions .changeset/cool-frogs-sin.md
Original file line number Diff line number Diff line change
@@ -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
---
10 changes: 10 additions & 0 deletions .changeset/great-plums-tell.md
Original file line number Diff line number Diff line change
@@ -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
---
10 changes: 10 additions & 0 deletions .changeset/itchy-monkeys-count.md
Original file line number Diff line number Diff line change
@@ -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
---
Original file line number Diff line number Diff line change
Expand Up @@ -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: [],
Expand Down
2 changes: 1 addition & 1 deletion packages/legend-application-data-cube/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# @finos/legend-application-data-cube

Legend Query core
Legend DataCube core
Original file line number Diff line number Diff line change
Expand Up @@ -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}`;
Original file line number Diff line number Diff line change
Expand Up @@ -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',
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<LegendDataCubeApplicationConfigurationData>,
Expand All @@ -58,6 +62,7 @@ export class LegendDataCubeApplicationConfig extends LegendApplicationConfig {
input.configData.engine.queryUrl,
)
: undefined;

// depot
assertNonNullable(
input.configData.depot,
Expand All @@ -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';
Expand Down
Original file line number Diff line number Diff line change
@@ -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<HTMLDivElement>(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 (
<Dialog
open={windowState.isOpen}
onClose={() => 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',
}}
>
<div
className="border border-neutral-400 bg-neutral-200 shadow-xl"
ref={ref}
>
<div className="flex h-6 w-full select-none items-center justify-between border-b border-b-neutral-300 bg-white">
<div className="px-2">{windowState.configuration.title}</div>
<button
className="flex h-[23px] w-6 items-center justify-center hover:bg-red-500 hover:text-white"
onClick={() => windowState.close()}
>
<DataCubeIcon.X />
</button>
</div>
<div className="h-[calc(100%_-_24px)] w-full overflow-auto">
{windowState.configuration.contentRenderer(
windowState.configuration,
)}
</div>
</div>
</Dialog>
);
},
);

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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -39,8 +39,8 @@ const LegendDataCubeWebApplicationRouter = observer(() => {
{store.initializeState.hasSucceeded && (
<Routes>
<Route
path={LEGEND_DATA_CUBE_ROUTE_PATTERN.QUERY_BUILDER}
element={<LegendDataCubeQueryBuilder />}
path={LEGEND_DATA_CUBE_ROUTE_PATTERN.BUILDER}
element={<LegendDataCubeBuilder />}
/>
</Routes>
)}
Expand Down
Loading

0 comments on commit f8cd2bf

Please sign in to comment.