@finos/legend-cube-builder 0.0.0-stage → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +5 -2
- package/lib/__lib__/LegendCubeCommand.d.ts +26 -0
- package/lib/__lib__/LegendCubeCommand.d.ts.map +1 -0
- package/lib/__lib__/LegendCubeCommand.js +40 -0
- package/lib/__lib__/LegendCubeCommand.js.map +1 -0
- package/lib/__lib__/LegendCubeHelpText.d.ts +19 -0
- package/lib/__lib__/LegendCubeHelpText.d.ts.map +1 -0
- package/lib/__lib__/LegendCubeHelpText.js +27 -0
- package/lib/__lib__/LegendCubeHelpText.js.map +1 -0
- package/lib/__lib__/LegendCubeLabels.d.ts +68 -0
- package/lib/__lib__/LegendCubeLabels.d.ts.map +1 -0
- package/lib/__lib__/LegendCubeLabels.js +109 -0
- package/lib/__lib__/LegendCubeLabels.js.map +1 -0
- package/lib/__lib__/LegendCubeTesting.d.ts +33 -0
- package/lib/__lib__/LegendCubeTesting.d.ts.map +1 -0
- package/lib/__lib__/LegendCubeTesting.js +34 -0
- package/lib/__lib__/LegendCubeTesting.js.map +1 -0
- package/lib/__test-utils__/CubeCanvasTestUtils.d.ts +24 -0
- package/lib/__test-utils__/CubeCanvasTestUtils.d.ts.map +1 -0
- package/lib/__test-utils__/CubeCanvasTestUtils.js +37 -0
- package/lib/__test-utils__/CubeCanvasTestUtils.js.map +1 -0
- package/lib/__test-utils__/CubeEngineTestSupport.d.ts +41 -0
- package/lib/__test-utils__/CubeEngineTestSupport.d.ts.map +1 -0
- package/lib/__test-utils__/CubeEngineTestSupport.js +76 -0
- package/lib/__test-utils__/CubeEngineTestSupport.js.map +1 -0
- package/lib/__test-utils__/CubeNorthwindTestQueries.d.ts +31 -0
- package/lib/__test-utils__/CubeNorthwindTestQueries.d.ts.map +1 -0
- package/lib/__test-utils__/CubeNorthwindTestQueries.js +102 -0
- package/lib/__test-utils__/CubeNorthwindTestQueries.js.map +1 -0
- package/lib/__test-utils__/CubePageTestUtils.d.ts +33 -0
- package/lib/__test-utils__/CubePageTestUtils.d.ts.map +1 -0
- package/lib/__test-utils__/CubePageTestUtils.js +68 -0
- package/lib/__test-utils__/CubePageTestUtils.js.map +1 -0
- package/lib/__test-utils__/CubeTestApplication.d.ts +30 -0
- package/lib/__test-utils__/CubeTestApplication.d.ts.map +1 -0
- package/lib/__test-utils__/CubeTestApplication.js +57 -0
- package/lib/__test-utils__/CubeTestApplication.js.map +1 -0
- package/lib/__test-utils__/FakeCubeEngine.d.ts +41 -0
- package/lib/__test-utils__/FakeCubeEngine.d.ts.map +1 -0
- package/lib/__test-utils__/FakeCubeEngine.js +85 -0
- package/lib/__test-utils__/FakeCubeEngine.js.map +1 -0
- package/lib/components/CubeButton.d.ts +20 -0
- package/lib/components/CubeButton.d.ts.map +1 -0
- package/lib/components/CubeButton.js +24 -0
- package/lib/components/CubeButton.js.map +1 -0
- package/lib/components/CubeEditor.d.ts +31 -0
- package/lib/components/CubeEditor.d.ts.map +1 -0
- package/lib/components/CubeEditor.js +90 -0
- package/lib/components/CubeEditor.js.map +1 -0
- package/lib/components/CubeNodeIcon.d.ts +23 -0
- package/lib/components/CubeNodeIcon.d.ts.map +1 -0
- package/lib/components/CubeNodeIcon.js +32 -0
- package/lib/components/CubeNodeIcon.js.map +1 -0
- package/lib/components/canvas/CubeCanvas.d.ts +37 -0
- package/lib/components/canvas/CubeCanvas.d.ts.map +1 -0
- package/lib/components/canvas/CubeCanvas.js +135 -0
- package/lib/components/canvas/CubeCanvas.js.map +1 -0
- package/lib/components/canvas/CubeCanvasContext.d.ts +19 -0
- package/lib/components/canvas/CubeCanvasContext.d.ts.map +1 -0
- package/lib/components/canvas/CubeCanvasContext.js +22 -0
- package/lib/components/canvas/CubeCanvasContext.js.map +1 -0
- package/lib/components/canvas/CubeCanvasContextMenu.d.ts +28 -0
- package/lib/components/canvas/CubeCanvasContextMenu.d.ts.map +1 -0
- package/lib/components/canvas/CubeCanvasContextMenu.js +35 -0
- package/lib/components/canvas/CubeCanvasContextMenu.js.map +1 -0
- package/lib/components/canvas/CubeCanvasDnd.d.ts +43 -0
- package/lib/components/canvas/CubeCanvasDnd.d.ts.map +1 -0
- package/lib/components/canvas/CubeCanvasDnd.js +51 -0
- package/lib/components/canvas/CubeCanvasDnd.js.map +1 -0
- package/lib/components/canvas/CubeCanvasEdge.d.ts +24 -0
- package/lib/components/canvas/CubeCanvasEdge.d.ts.map +1 -0
- package/lib/components/canvas/CubeCanvasEdge.js +33 -0
- package/lib/components/canvas/CubeCanvasEdge.js.map +1 -0
- package/lib/components/canvas/CubeCanvasElements.d.ts +64 -0
- package/lib/components/canvas/CubeCanvasElements.d.ts.map +1 -0
- package/lib/components/canvas/CubeCanvasElements.js +96 -0
- package/lib/components/canvas/CubeCanvasElements.js.map +1 -0
- package/lib/components/canvas/CubeCanvasLayout.d.ts +32 -0
- package/lib/components/canvas/CubeCanvasLayout.d.ts.map +1 -0
- package/lib/components/canvas/CubeCanvasLayout.js +63 -0
- package/lib/components/canvas/CubeCanvasLayout.js.map +1 -0
- package/lib/components/canvas/CubeCanvasNode.d.ts +29 -0
- package/lib/components/canvas/CubeCanvasNode.d.ts.map +1 -0
- package/lib/components/canvas/CubeCanvasNode.js +72 -0
- package/lib/components/canvas/CubeCanvasNode.js.map +1 -0
- package/lib/components/editors/CubeColumnPicker.d.ts +40 -0
- package/lib/components/editors/CubeColumnPicker.d.ts.map +1 -0
- package/lib/components/editors/CubeColumnPicker.js +51 -0
- package/lib/components/editors/CubeColumnPicker.js.map +1 -0
- package/lib/components/editors/CubeFilterEditor.d.ts +26 -0
- package/lib/components/editors/CubeFilterEditor.d.ts.map +1 -0
- package/lib/components/editors/CubeFilterEditor.js +90 -0
- package/lib/components/editors/CubeFilterEditor.js.map +1 -0
- package/lib/components/editors/CubeJoinEditor.d.ts +26 -0
- package/lib/components/editors/CubeJoinEditor.d.ts.map +1 -0
- package/lib/components/editors/CubeJoinEditor.js +76 -0
- package/lib/components/editors/CubeJoinEditor.js.map +1 -0
- package/lib/components/editors/CubeNodeEditorPanel.d.ts +29 -0
- package/lib/components/editors/CubeNodeEditorPanel.d.ts.map +1 -0
- package/lib/components/editors/CubeNodeEditorPanel.js +71 -0
- package/lib/components/editors/CubeNodeEditorPanel.js.map +1 -0
- package/lib/components/editors/CubeNodeEditorRegistry.d.ts +44 -0
- package/lib/components/editors/CubeNodeEditorRegistry.d.ts.map +1 -0
- package/lib/components/editors/CubeNodeEditorRegistry.js +30 -0
- package/lib/components/editors/CubeNodeEditorRegistry.js.map +1 -0
- package/lib/components/editors/CubeSourceEditor.d.ts +25 -0
- package/lib/components/editors/CubeSourceEditor.d.ts.map +1 -0
- package/lib/components/editors/CubeSourceEditor.js +42 -0
- package/lib/components/editors/CubeSourceEditor.js.map +1 -0
- package/lib/components/editors/CubeValueEditor.d.ts +40 -0
- package/lib/components/editors/CubeValueEditor.d.ts.map +1 -0
- package/lib/components/editors/CubeValueEditor.js +177 -0
- package/lib/components/editors/CubeValueEditor.js.map +1 -0
- package/lib/components/grid/CubeGridRegion.d.ts +23 -0
- package/lib/components/grid/CubeGridRegion.d.ts.map +1 -0
- package/lib/components/grid/CubeGridRegion.js +94 -0
- package/lib/components/grid/CubeGridRegion.js.map +1 -0
- package/lib/components/grid/CubeGridValues.d.ts +25 -0
- package/lib/components/grid/CubeGridValues.d.ts.map +1 -0
- package/lib/components/grid/CubeGridValues.js +95 -0
- package/lib/components/grid/CubeGridValues.js.map +1 -0
- package/lib/components/grid/CubeResultGrid.d.ts +26 -0
- package/lib/components/grid/CubeResultGrid.d.ts.map +1 -0
- package/lib/components/grid/CubeResultGrid.js +50 -0
- package/lib/components/grid/CubeResultGrid.js.map +1 -0
- package/lib/components/palette/CubePalette.d.ts +29 -0
- package/lib/components/palette/CubePalette.d.ts.map +1 -0
- package/lib/components/palette/CubePalette.js +73 -0
- package/lib/components/palette/CubePalette.js.map +1 -0
- package/lib/components/show-pure/CubeShowPureDialog.d.ts +23 -0
- package/lib/components/show-pure/CubeShowPureDialog.d.ts.map +1 -0
- package/lib/components/show-pure/CubeShowPureDialog.js +37 -0
- package/lib/components/show-pure/CubeShowPureDialog.js.map +1 -0
- package/lib/components/source-picker/CubeSourcePicker.d.ts +22 -0
- package/lib/components/source-picker/CubeSourcePicker.d.ts.map +1 -0
- package/lib/components/source-picker/CubeSourcePicker.js +89 -0
- package/lib/components/source-picker/CubeSourcePicker.js.map +1 -0
- package/lib/components/spec-transfer/CubeSpecTransferDialog.d.ts +26 -0
- package/lib/components/spec-transfer/CubeSpecTransferDialog.d.ts.map +1 -0
- package/lib/components/spec-transfer/CubeSpecTransferDialog.js +45 -0
- package/lib/components/spec-transfer/CubeSpecTransferDialog.js.map +1 -0
- package/lib/graph-manager/CubeEngine.d.ts +115 -0
- package/lib/graph-manager/CubeEngine.d.ts.map +1 -0
- package/lib/graph-manager/CubeEngine.js +61 -0
- package/lib/graph-manager/CubeEngine.js.map +1 -0
- package/lib/graph-manager/CubeModelOutlineHelper.d.ts +25 -0
- package/lib/graph-manager/CubeModelOutlineHelper.d.ts.map +1 -0
- package/lib/graph-manager/CubeModelOutlineHelper.js +24 -0
- package/lib/graph-manager/CubeModelOutlineHelper.js.map +1 -0
- package/lib/graph-manager/protocol/pure/CubeEngineBuilder.d.ts +26 -0
- package/lib/graph-manager/protocol/pure/CubeEngineBuilder.d.ts.map +1 -0
- package/lib/graph-manager/protocol/pure/CubeEngineBuilder.js +22 -0
- package/lib/graph-manager/protocol/pure/CubeEngineBuilder.js.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeEngineErrors.d.ts +27 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeEngineErrors.d.ts.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeEngineErrors.js +76 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeEngineErrors.js.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeExecutionResultReader.d.ts +27 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeExecutionResultReader.d.ts.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeExecutionResultReader.js +82 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeExecutionResultReader.js.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeLambdaSerializer.d.ts +32 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeLambdaSerializer.d.ts.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeLambdaSerializer.js +195 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeLambdaSerializer.js.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeModelOutlineBuilder.d.ts +20 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeModelOutlineBuilder.d.ts.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeModelOutlineBuilder.js +121 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeModelOutlineBuilder.js.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeRelationTypeAdapter.d.ts +24 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeRelationTypeAdapter.d.ts.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeRelationTypeAdapter.js +43 -0
- package/lib/graph-manager/protocol/pure/v1/V1_CubeRelationTypeAdapter.js.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/V1_LegendCubeEngine.d.ts +45 -0
- package/lib/graph-manager/protocol/pure/v1/V1_LegendCubeEngine.d.ts.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/V1_LegendCubeEngine.js +171 -0
- package/lib/graph-manager/protocol/pure/v1/V1_LegendCubeEngine.js.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/__test-utils__/V1_CubeEngineTestUtils.d.ts +22 -0
- package/lib/graph-manager/protocol/pure/v1/__test-utils__/V1_CubeEngineTestUtils.d.ts.map +1 -0
- package/lib/graph-manager/protocol/pure/v1/__test-utils__/V1_CubeEngineTestUtils.js +79 -0
- package/lib/graph-manager/protocol/pure/v1/__test-utils__/V1_CubeEngineTestUtils.js.map +1 -0
- package/lib/index.css +17 -0
- package/lib/index.css.map +1 -0
- package/lib/index.d.ts +25 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +24 -0
- package/lib/index.js.map +1 -0
- package/lib/stores/CubeEditorState.d.ts +206 -0
- package/lib/stores/CubeEditorState.d.ts.map +1 -0
- package/lib/stores/CubeEditorState.js +551 -0
- package/lib/stores/CubeEditorState.js.map +1 -0
- package/lib/stores/CubeExecutionState.d.ts +67 -0
- package/lib/stores/CubeExecutionState.d.ts.map +1 -0
- package/lib/stores/CubeExecutionState.js +196 -0
- package/lib/stores/CubeExecutionState.js.map +1 -0
- package/lib/stores/CubeHost.d.ts +31 -0
- package/lib/stores/CubeHost.d.ts.map +1 -0
- package/lib/stores/CubeHost.js +17 -0
- package/lib/stores/CubeHost.js.map +1 -0
- package/lib/stores/CubeNodeEditorState.d.ts +85 -0
- package/lib/stores/CubeNodeEditorState.d.ts.map +1 -0
- package/lib/stores/CubeNodeEditorState.js +207 -0
- package/lib/stores/CubeNodeEditorState.js.map +1 -0
- package/lib/stores/CubeShowPureState.d.ts +38 -0
- package/lib/stores/CubeShowPureState.d.ts.map +1 -0
- package/lib/stores/CubeShowPureState.js +83 -0
- package/lib/stores/CubeShowPureState.js.map +1 -0
- package/lib/stores/CubeSourcePickerState.d.ts +98 -0
- package/lib/stores/CubeSourcePickerState.d.ts.map +1 -0
- package/lib/stores/CubeSourcePickerState.js +334 -0
- package/lib/stores/CubeSourcePickerState.js.map +1 -0
- package/lib/stores/CubeSpecTransferState.d.ts +60 -0
- package/lib/stores/CubeSpecTransferState.d.ts.map +1 -0
- package/lib/stores/CubeSpecTransferState.js +167 -0
- package/lib/stores/CubeSpecTransferState.js.map +1 -0
- package/lib/stores/LocalModelCatalog.d.ts +37 -0
- package/lib/stores/LocalModelCatalog.d.ts.map +1 -0
- package/lib/stores/LocalModelCatalog.js +49 -0
- package/lib/stores/LocalModelCatalog.js.map +1 -0
- package/lib/stores/editors/CubeFilterDraft.d.ts +61 -0
- package/lib/stores/editors/CubeFilterDraft.d.ts.map +1 -0
- package/lib/stores/editors/CubeFilterDraft.js +186 -0
- package/lib/stores/editors/CubeFilterDraft.js.map +1 -0
- package/lib/stores/editors/CubeJoinDraft.d.ts +63 -0
- package/lib/stores/editors/CubeJoinDraft.d.ts.map +1 -0
- package/lib/stores/editors/CubeJoinDraft.js +136 -0
- package/lib/stores/editors/CubeJoinDraft.js.map +1 -0
- package/lib/stores/editors/CubeNodeDraft.d.ts +38 -0
- package/lib/stores/editors/CubeNodeDraft.d.ts.map +1 -0
- package/lib/stores/editors/CubeNodeDraft.js +35 -0
- package/lib/stores/editors/CubeNodeDraft.js.map +1 -0
- package/lib/stores/editors/CubeNodeDraftRegistry.d.ts +29 -0
- package/lib/stores/editors/CubeNodeDraftRegistry.d.ts.map +1 -0
- package/lib/stores/editors/CubeNodeDraftRegistry.js +33 -0
- package/lib/stores/editors/CubeNodeDraftRegistry.js.map +1 -0
- package/lib/stores/fixtures/CubeNorthwindModel.d.ts +23 -0
- package/lib/stores/fixtures/CubeNorthwindModel.d.ts.map +1 -0
- package/lib/stores/fixtures/CubeNorthwindModel.js +338 -0
- package/lib/stores/fixtures/CubeNorthwindModel.js.map +1 -0
- package/package.json +81 -3
- package/src/__lib__/LegendCubeCommand.ts +44 -0
- package/src/__lib__/LegendCubeHelpText.ts +38 -0
- package/src/__lib__/LegendCubeLabels.ts +141 -0
- package/src/__lib__/LegendCubeTesting.ts +33 -0
- package/src/__test-utils__/CubeCanvasTestUtils.ts +53 -0
- package/src/__test-utils__/CubeEngineTestSupport.ts +137 -0
- package/src/__test-utils__/CubeNorthwindTestQueries.ts +165 -0
- package/src/__test-utils__/CubePageTestUtils.tsx +96 -0
- package/src/__test-utils__/CubeTestApplication.ts +82 -0
- package/src/__test-utils__/FakeCubeEngine.ts +139 -0
- package/src/components/CubeButton.tsx +35 -0
- package/src/components/CubeEditor.tsx +239 -0
- package/src/components/CubeNodeIcon.tsx +47 -0
- package/src/components/canvas/CubeCanvas.tsx +260 -0
- package/src/components/canvas/CubeCanvasContext.ts +31 -0
- package/src/components/canvas/CubeCanvasContextMenu.tsx +89 -0
- package/src/components/canvas/CubeCanvasDnd.ts +82 -0
- package/src/components/canvas/CubeCanvasEdge.tsx +57 -0
- package/src/components/canvas/CubeCanvasElements.ts +150 -0
- package/src/components/canvas/CubeCanvasLayout.ts +88 -0
- package/src/components/canvas/CubeCanvasNode.tsx +141 -0
- package/src/components/editors/CubeColumnPicker.tsx +114 -0
- package/src/components/editors/CubeFilterEditor.tsx +309 -0
- package/src/components/editors/CubeJoinEditor.tsx +224 -0
- package/src/components/editors/CubeNodeEditorPanel.tsx +204 -0
- package/src/components/editors/CubeNodeEditorRegistry.ts +61 -0
- package/src/components/editors/CubeSourceEditor.tsx +116 -0
- package/src/components/editors/CubeValueEditor.tsx +332 -0
- package/src/components/grid/CubeGridRegion.tsx +274 -0
- package/src/components/grid/CubeGridValues.ts +125 -0
- package/src/components/grid/CubeResultGrid.tsx +84 -0
- package/src/components/palette/CubePalette.tsx +177 -0
- package/src/components/show-pure/CubeShowPureDialog.tsx +101 -0
- package/src/components/source-picker/CubeSourcePicker.tsx +304 -0
- package/src/components/spec-transfer/CubeSpecTransferDialog.tsx +157 -0
- package/src/graph-manager/CubeEngine.ts +155 -0
- package/src/graph-manager/CubeModelOutlineHelper.ts +32 -0
- package/src/graph-manager/protocol/pure/CubeEngineBuilder.ts +34 -0
- package/src/graph-manager/protocol/pure/v1/V1_CubeEngineErrors.ts +109 -0
- package/src/graph-manager/protocol/pure/v1/V1_CubeExecutionResultReader.ts +115 -0
- package/src/graph-manager/protocol/pure/v1/V1_CubeLambdaSerializer.ts +257 -0
- package/src/graph-manager/protocol/pure/v1/V1_CubeModelOutlineBuilder.ts +157 -0
- package/src/graph-manager/protocol/pure/v1/V1_CubeRelationTypeAdapter.ts +58 -0
- package/src/graph-manager/protocol/pure/v1/V1_LegendCubeEngine.ts +278 -0
- package/src/graph-manager/protocol/pure/v1/__test-utils__/V1_CubeEngineTestUtils.ts +124 -0
- package/src/index.ts +41 -0
- package/src/stores/CubeEditorState.ts +702 -0
- package/src/stores/CubeExecutionState.ts +255 -0
- package/src/stores/CubeHost.ts +32 -0
- package/src/stores/CubeNodeEditorState.ts +255 -0
- package/src/stores/CubeShowPureState.ts +96 -0
- package/src/stores/CubeSourcePickerState.ts +426 -0
- package/src/stores/CubeSpecTransferState.ts +198 -0
- package/src/stores/LocalModelCatalog.ts +70 -0
- package/src/stores/editors/CubeFilterDraft.ts +241 -0
- package/src/stores/editors/CubeJoinDraft.ts +197 -0
- package/src/stores/editors/CubeNodeDraft.ts +46 -0
- package/src/stores/editors/CubeNodeDraftRegistry.ts +49 -0
- package/src/stores/fixtures/CubeNorthwindModel.ts +355 -0
- package/tsconfig.json +118 -0
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026-present, Goldman Sachs
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
type CubeContext,
|
|
19
|
+
getRelationalDisplayName,
|
|
20
|
+
type ModelContext,
|
|
21
|
+
RELATIONAL_TABLE_SOURCE_DEFINITION,
|
|
22
|
+
RelationalTableSource,
|
|
23
|
+
} from '@finos/legend-cube';
|
|
24
|
+
import { assertErrorThrown, type GeneratorFn } from '@finos/legend-shared';
|
|
25
|
+
import {
|
|
26
|
+
action,
|
|
27
|
+
computed,
|
|
28
|
+
flow,
|
|
29
|
+
flowResult,
|
|
30
|
+
makeObservable,
|
|
31
|
+
observable,
|
|
32
|
+
} from 'mobx';
|
|
33
|
+
import {
|
|
34
|
+
CubeEngineError,
|
|
35
|
+
CubeEngineErrorKind,
|
|
36
|
+
CubeTableFlag,
|
|
37
|
+
type CubeModelOutline,
|
|
38
|
+
type CubeOutlineDatabase,
|
|
39
|
+
type CubeOutlineRuntime,
|
|
40
|
+
type CubeOutlineSchema,
|
|
41
|
+
type CubeOutlineTable,
|
|
42
|
+
} from '../graph-manager/CubeEngine.js';
|
|
43
|
+
import { getRuntimesForDatabase } from '../graph-manager/CubeModelOutlineHelper.js';
|
|
44
|
+
import type { CubeEditorState } from './CubeEditorState.js';
|
|
45
|
+
import { type BundledModel, createTextModel } from './LocalModelCatalog.js';
|
|
46
|
+
|
|
47
|
+
const toMessage = (error: unknown): string => {
|
|
48
|
+
assertErrorThrown(error);
|
|
49
|
+
return error instanceof CubeEngineError ? error.firstLine : error.message;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/** Whether a table can be picked: a table with a column Cube can't read can't */
|
|
53
|
+
export const isTableSelectable = (table: CubeOutlineTable): boolean =>
|
|
54
|
+
!table.flags.includes(CubeTableFlag.UNAVAILABLE);
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The source picker (PLAN §6.2.7, Settled before M1.8): model, then database,
|
|
58
|
+
* then a runtime keyed by that database, then schema and table. A step with
|
|
59
|
+
* a single choice is picked automatically. The first table fixes the cube's
|
|
60
|
+
* model and runtime; later tables come from the same database and runtime.
|
|
61
|
+
* A table is added only once its schema has resolved.
|
|
62
|
+
*/
|
|
63
|
+
export class CubeSourcePickerState {
|
|
64
|
+
readonly editorState: CubeEditorState;
|
|
65
|
+
|
|
66
|
+
isOpen = false;
|
|
67
|
+
model: ModelContext | undefined;
|
|
68
|
+
/** The model is Pure text the user pastes, not a bundled one */
|
|
69
|
+
isPastingModel = false;
|
|
70
|
+
pastedModelText = '';
|
|
71
|
+
/** The model the paste box last loaded: only it keeps the box shown */
|
|
72
|
+
private pastedModel: ModelContext | undefined;
|
|
73
|
+
outline: CubeModelOutline | undefined;
|
|
74
|
+
isLoadingModel = false;
|
|
75
|
+
databasePath: string | undefined;
|
|
76
|
+
runtimePath: string | undefined;
|
|
77
|
+
schemaName: string | undefined;
|
|
78
|
+
tableName: string | undefined;
|
|
79
|
+
tableSearch = '';
|
|
80
|
+
isResolving = false;
|
|
81
|
+
/** Why the outline or the table couldn't be loaded, shown in the dialog */
|
|
82
|
+
error: string | undefined;
|
|
83
|
+
/** Counts Add presses: closing the dialog moves it on, so the pending Add is dropped */
|
|
84
|
+
private confirmRequest = 0;
|
|
85
|
+
|
|
86
|
+
constructor(editorState: CubeEditorState) {
|
|
87
|
+
makeObservable(this, {
|
|
88
|
+
isOpen: observable,
|
|
89
|
+
model: observable.ref,
|
|
90
|
+
isPastingModel: observable,
|
|
91
|
+
pastedModelText: observable,
|
|
92
|
+
outline: observable.ref,
|
|
93
|
+
isLoadingModel: observable,
|
|
94
|
+
databasePath: observable,
|
|
95
|
+
runtimePath: observable,
|
|
96
|
+
schemaName: observable,
|
|
97
|
+
tableName: observable,
|
|
98
|
+
tableSearch: observable,
|
|
99
|
+
isResolving: observable,
|
|
100
|
+
error: observable,
|
|
101
|
+
fixedContext: computed,
|
|
102
|
+
fixedDatabasePath: computed,
|
|
103
|
+
databases: computed,
|
|
104
|
+
runtimes: computed,
|
|
105
|
+
schemas: computed,
|
|
106
|
+
tables: computed,
|
|
107
|
+
canConfirm: computed,
|
|
108
|
+
open: action,
|
|
109
|
+
close: action,
|
|
110
|
+
selectDatabase: action,
|
|
111
|
+
selectRuntime: action,
|
|
112
|
+
selectSchema: action,
|
|
113
|
+
selectTable: action,
|
|
114
|
+
setTableSearch: action,
|
|
115
|
+
startPastingModel: action,
|
|
116
|
+
setPastedModelText: action,
|
|
117
|
+
selectModel: flow,
|
|
118
|
+
loadPastedModel: flow,
|
|
119
|
+
confirm: flow,
|
|
120
|
+
});
|
|
121
|
+
this.editorState = editorState;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
get models(): readonly BundledModel[] {
|
|
125
|
+
return this.editorState.host.modelCatalog.models;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Once the cube has a model, every table comes from it */
|
|
129
|
+
get fixedContext(): CubeContext | undefined {
|
|
130
|
+
return this.editorState.document.context;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Once the cube has a table, every other one comes from its database */
|
|
134
|
+
get fixedDatabasePath(): string | undefined {
|
|
135
|
+
return this.editorState.document.query.nodes.find(
|
|
136
|
+
(node): node is RelationalTableSource =>
|
|
137
|
+
node instanceof RelationalTableSource,
|
|
138
|
+
)?.database;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
get databases(): readonly CubeOutlineDatabase[] {
|
|
142
|
+
const databases = this.outline?.databases ?? [];
|
|
143
|
+
const fixedRuntime = this.fixedContext?.runtime;
|
|
144
|
+
return databases.filter(
|
|
145
|
+
(database) =>
|
|
146
|
+
(this.fixedDatabasePath === undefined ||
|
|
147
|
+
database.path === this.fixedDatabasePath) &&
|
|
148
|
+
(fixedRuntime === undefined ||
|
|
149
|
+
(this.outline &&
|
|
150
|
+
getRuntimesForDatabase(this.outline, database.path).some(
|
|
151
|
+
(runtime) => runtime.path === fixedRuntime,
|
|
152
|
+
))),
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
get runtimes(): readonly CubeOutlineRuntime[] {
|
|
157
|
+
if (!this.outline || this.databasePath === undefined) {
|
|
158
|
+
return [];
|
|
159
|
+
}
|
|
160
|
+
const fixedRuntime = this.fixedContext?.runtime;
|
|
161
|
+
return getRuntimesForDatabase(this.outline, this.databasePath).filter(
|
|
162
|
+
(runtime) => fixedRuntime === undefined || runtime.path === fixedRuntime,
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
get schemas(): readonly CubeOutlineSchema[] {
|
|
167
|
+
return (
|
|
168
|
+
this.databases.find((database) => database.path === this.databasePath)
|
|
169
|
+
?.schemas ?? []
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** The schema's tables, views left out, filtered by the search text */
|
|
174
|
+
get tables(): readonly CubeOutlineTable[] {
|
|
175
|
+
const search = this.tableSearch.trim().toLowerCase();
|
|
176
|
+
return (
|
|
177
|
+
this.schemas.find((schema) => schema.name === this.schemaName)?.tables ??
|
|
178
|
+
[]
|
|
179
|
+
).filter(
|
|
180
|
+
(table) =>
|
|
181
|
+
!table.isView &&
|
|
182
|
+
getRelationalDisplayName(table.name).toLowerCase().includes(search),
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
get canConfirm(): boolean {
|
|
187
|
+
const table = this.tables.find(
|
|
188
|
+
(candidate) => candidate.name === this.tableName,
|
|
189
|
+
);
|
|
190
|
+
return (
|
|
191
|
+
!this.isLoadingModel &&
|
|
192
|
+
!this.isResolving &&
|
|
193
|
+
this.model !== undefined &&
|
|
194
|
+
this.runtimePath !== undefined &&
|
|
195
|
+
table !== undefined &&
|
|
196
|
+
isTableSelectable(table)
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Opens the dialog on the cube's model, or on the only model offered. A
|
|
202
|
+
* model whose outline failed to load is loaded again. Does nothing while
|
|
203
|
+
* the cube is read-only.
|
|
204
|
+
*/
|
|
205
|
+
open(): void {
|
|
206
|
+
if (this.editorState.readOnly) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
this.isOpen = true;
|
|
210
|
+
this.error = undefined;
|
|
211
|
+
const model =
|
|
212
|
+
this.fixedContext?.model ??
|
|
213
|
+
(this.models.length === 1 && !this.isPastingModel
|
|
214
|
+
? this.models[0]?.model
|
|
215
|
+
: this.model);
|
|
216
|
+
if (!model) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
if (model !== this.model || !this.outline) {
|
|
220
|
+
flowResult(this.selectModel(model)).catch(
|
|
221
|
+
this.editorState.host.applicationStore.alertUnhandledError,
|
|
222
|
+
);
|
|
223
|
+
} else if (
|
|
224
|
+
this.databasePath !== undefined &&
|
|
225
|
+
!this.databases.some((database) => database.path === this.databasePath)
|
|
226
|
+
) {
|
|
227
|
+
// the cube changed since the last pick, e.g. by undo
|
|
228
|
+
const [onlyDatabase, ...otherDatabases] = this.databases;
|
|
229
|
+
this.selectDatabase(
|
|
230
|
+
onlyDatabase && !otherDatabases.length ? onlyDatabase.path : undefined,
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** Closes the dialog; a table still being typed is not added */
|
|
236
|
+
close(): void {
|
|
237
|
+
this.isOpen = false;
|
|
238
|
+
this.confirmRequest++;
|
|
239
|
+
this.isResolving = false;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/** Offers a text box for a Pure model in place of the bundled ones */
|
|
243
|
+
startPastingModel(): void {
|
|
244
|
+
this.isPastingModel = true;
|
|
245
|
+
this.model = undefined;
|
|
246
|
+
this.outline = undefined;
|
|
247
|
+
this.error = undefined;
|
|
248
|
+
this.isLoadingModel = false;
|
|
249
|
+
this.resetFrom('database');
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
setPastedModelText(text: string): void {
|
|
253
|
+
this.pastedModelText = text;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Loads the pasted Pure text as the model: the cube will hold the text
|
|
258
|
+
* itself, so it counts towards the size a saved cube can have
|
|
259
|
+
*/
|
|
260
|
+
*loadPastedModel(): GeneratorFn<void> {
|
|
261
|
+
if (this.pastedModelText.trim()) {
|
|
262
|
+
this.pastedModel = createTextModel(this.pastedModelText);
|
|
263
|
+
yield flowResult(this.selectModel(this.pastedModel));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Loads a model's outline. Any model but the one the paste box loaded ends
|
|
269
|
+
* pasting, e.g. a bundled model, or the model of a cube opened meanwhile.
|
|
270
|
+
*/
|
|
271
|
+
*selectModel(model: ModelContext): GeneratorFn<void> {
|
|
272
|
+
this.isPastingModel = model === this.pastedModel;
|
|
273
|
+
this.model = model;
|
|
274
|
+
this.outline = undefined;
|
|
275
|
+
this.error = undefined;
|
|
276
|
+
this.resetFrom('database');
|
|
277
|
+
this.isLoadingModel = true;
|
|
278
|
+
try {
|
|
279
|
+
const outline = (yield this.editorState.host.modelCatalog.loadOutline(
|
|
280
|
+
model,
|
|
281
|
+
)) as CubeModelOutline;
|
|
282
|
+
if (this.model !== model) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
this.outline = outline;
|
|
286
|
+
const [onlyDatabase, ...otherDatabases] = this.databases;
|
|
287
|
+
if (onlyDatabase && !otherDatabases.length) {
|
|
288
|
+
this.selectDatabase(onlyDatabase.path);
|
|
289
|
+
}
|
|
290
|
+
} catch (error) {
|
|
291
|
+
if (this.model === model) {
|
|
292
|
+
this.error = toMessage(error);
|
|
293
|
+
}
|
|
294
|
+
} finally {
|
|
295
|
+
if (this.model === model) {
|
|
296
|
+
this.isLoadingModel = false;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
selectDatabase(path: string | undefined): void {
|
|
302
|
+
this.databasePath = path;
|
|
303
|
+
this.resetFrom('runtime');
|
|
304
|
+
const [onlyRuntime, ...otherRuntimes] = this.runtimes;
|
|
305
|
+
if (onlyRuntime && !otherRuntimes.length) {
|
|
306
|
+
this.runtimePath = onlyRuntime.path;
|
|
307
|
+
}
|
|
308
|
+
const [onlySchema, ...otherSchemas] = this.schemas;
|
|
309
|
+
if (onlySchema && !otherSchemas.length) {
|
|
310
|
+
this.schemaName = onlySchema.name;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
selectRuntime(path: string | undefined): void {
|
|
315
|
+
this.runtimePath = path;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
selectSchema(name: string | undefined): void {
|
|
319
|
+
this.schemaName = name;
|
|
320
|
+
this.resetFrom('table');
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
selectTable(name: string | undefined): void {
|
|
324
|
+
this.tableName = name;
|
|
325
|
+
this.error = undefined;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
setTableSearch(search: string): void {
|
|
329
|
+
this.tableSearch = search;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Types the picked table with the engine, then adds it; the first table
|
|
334
|
+
* also sets the cube's model and runtime, in the same undo step. The cube
|
|
335
|
+
* may have changed while the engine answered: the table is added only if
|
|
336
|
+
* the cube still has the same model and runtime and can take it.
|
|
337
|
+
*/
|
|
338
|
+
*confirm(): GeneratorFn<void> {
|
|
339
|
+
if (!this.canConfirm) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
const { editorState } = this;
|
|
343
|
+
const model = this.model as ModelContext;
|
|
344
|
+
const runtime = this.runtimePath as string;
|
|
345
|
+
const coordinates = {
|
|
346
|
+
database: this.databasePath as string,
|
|
347
|
+
schema: this.schemaName as string,
|
|
348
|
+
table: this.tableName as string,
|
|
349
|
+
};
|
|
350
|
+
const contextBefore = editorState.document.context;
|
|
351
|
+
// the engine stamps the table's accessor with its node id, so the id comes first
|
|
352
|
+
const id = editorState.document.query.generateId(
|
|
353
|
+
RelationalTableSource.TYPE,
|
|
354
|
+
);
|
|
355
|
+
const request = ++this.confirmRequest;
|
|
356
|
+
this.isResolving = true;
|
|
357
|
+
this.error = undefined;
|
|
358
|
+
try {
|
|
359
|
+
const typed = (yield editorState.host.engine.resolveSchemas(
|
|
360
|
+
model,
|
|
361
|
+
new Map([
|
|
362
|
+
[id, [coordinates.database, coordinates.schema, coordinates.table]],
|
|
363
|
+
]),
|
|
364
|
+
)) as Awaited<ReturnType<typeof editorState.host.engine.resolveSchemas>>;
|
|
365
|
+
if (request !== this.confirmRequest) {
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
const schema = typed.get(id);
|
|
369
|
+
if (schema === undefined || schema instanceof CubeEngineError) {
|
|
370
|
+
throw (
|
|
371
|
+
schema ??
|
|
372
|
+
new CubeEngineError(
|
|
373
|
+
CubeEngineErrorKind.COMPILE,
|
|
374
|
+
`The engine gave no schema for this table`,
|
|
375
|
+
)
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
const node = RELATIONAL_TABLE_SOURCE_DEFINITION.resolve(
|
|
379
|
+
RELATIONAL_TABLE_SOURCE_DEFINITION.fromCoordinates(id, coordinates),
|
|
380
|
+
{ kind: 'resolved', schema },
|
|
381
|
+
);
|
|
382
|
+
const { document } = editorState;
|
|
383
|
+
if (document.context !== contextBefore || !document.query.canAdd(node)) {
|
|
384
|
+
throw new Error(
|
|
385
|
+
`The cube changed while the table was loading; pick the table again.`,
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
const query = document.query.add(node);
|
|
389
|
+
// the first table sets the model and runtime; a cube saved with a model
|
|
390
|
+
// but no runtime takes the picked runtime and keeps the rest
|
|
391
|
+
editorState.applyDocument(
|
|
392
|
+
document.context?.runtime !== undefined
|
|
393
|
+
? document.withQuery(query)
|
|
394
|
+
: document
|
|
395
|
+
.withContext({
|
|
396
|
+
...document.context,
|
|
397
|
+
model: document.context?.model ?? model,
|
|
398
|
+
runtime,
|
|
399
|
+
})
|
|
400
|
+
.withQuery(query),
|
|
401
|
+
);
|
|
402
|
+
this.isOpen = false;
|
|
403
|
+
this.resetFrom('table');
|
|
404
|
+
} catch (error) {
|
|
405
|
+
if (request === this.confirmRequest) {
|
|
406
|
+
this.error = toMessage(error);
|
|
407
|
+
}
|
|
408
|
+
} finally {
|
|
409
|
+
if (request === this.confirmRequest) {
|
|
410
|
+
this.isResolving = false;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
private resetFrom(step: 'database' | 'runtime' | 'table'): void {
|
|
416
|
+
if (step === 'database') {
|
|
417
|
+
this.databasePath = undefined;
|
|
418
|
+
}
|
|
419
|
+
if (step === 'database' || step === 'runtime') {
|
|
420
|
+
this.runtimePath = undefined;
|
|
421
|
+
this.schemaName = undefined;
|
|
422
|
+
}
|
|
423
|
+
this.tableName = undefined;
|
|
424
|
+
this.tableSearch = '';
|
|
425
|
+
}
|
|
426
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026-present, Goldman Sachs
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
CubeSpecDecodeError,
|
|
19
|
+
MAX_SPEC_BYTES,
|
|
20
|
+
parseCubeSpec,
|
|
21
|
+
serializeCubeSpec,
|
|
22
|
+
} from '@finos/legend-cube';
|
|
23
|
+
import {
|
|
24
|
+
ContentType,
|
|
25
|
+
downloadFileUsingDataURI,
|
|
26
|
+
type GeneratorFn,
|
|
27
|
+
readFileAsText,
|
|
28
|
+
} from '@finos/legend-shared';
|
|
29
|
+
import { action, flow, makeObservable, observable } from 'mobx';
|
|
30
|
+
import type { CubeEditorState } from './CubeEditorState.js';
|
|
31
|
+
|
|
32
|
+
export enum CUBE_SPEC_TRANSFER_MODE {
|
|
33
|
+
EXPORT = 'export',
|
|
34
|
+
IMPORT = 'import',
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const errorMessage = (error: unknown): string =>
|
|
38
|
+
error instanceof Error ? error.message : String(error);
|
|
39
|
+
|
|
40
|
+
/** Why a spec can't be imported; a broken document names the path of the problem */
|
|
41
|
+
export const getSpecImportError = (error: unknown): string =>
|
|
42
|
+
error instanceof CubeSpecDecodeError
|
|
43
|
+
? error.path
|
|
44
|
+
? `Can't import the spec: ${error.path} ${error.detail}`
|
|
45
|
+
: `Can't import the spec: it ${error.detail}`
|
|
46
|
+
: `Can't import the spec: ${errorMessage(error)}`;
|
|
47
|
+
|
|
48
|
+
/** A file name for a cube's spec, from its name */
|
|
49
|
+
export const getCubeSpecFileName = (name: string | undefined): string => {
|
|
50
|
+
const base = name?.trim().replace(/[^\w.-]+/gu, '_') ?? '';
|
|
51
|
+
return `${base.length ? base : 'cube'}.cube.json`;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Export and import of the saved spec (PLAN §10.3), the only way to keep a
|
|
56
|
+
* cube until the Cube store (M8). Both are developer tools for now, labelled
|
|
57
|
+
* "(dev)" (Settled before M1.8).
|
|
58
|
+
*/
|
|
59
|
+
export class CubeSpecTransferState {
|
|
60
|
+
readonly editorState: CubeEditorState;
|
|
61
|
+
|
|
62
|
+
mode: CUBE_SPEC_TRANSFER_MODE | undefined;
|
|
63
|
+
/** The spec to export, once written */
|
|
64
|
+
exportText: string | undefined;
|
|
65
|
+
/** The spec text to import, pasted or read from a file */
|
|
66
|
+
importText = '';
|
|
67
|
+
/** Why the export or the import failed */
|
|
68
|
+
error: string | undefined;
|
|
69
|
+
isReadingFile = false;
|
|
70
|
+
/** Counts file reads, so a read that finishes after another starts is dropped */
|
|
71
|
+
private fileRequest = 0;
|
|
72
|
+
|
|
73
|
+
constructor(editorState: CubeEditorState) {
|
|
74
|
+
makeObservable<CubeSpecTransferState, 'fileRequest'>(this, {
|
|
75
|
+
mode: observable,
|
|
76
|
+
exportText: observable,
|
|
77
|
+
importText: observable,
|
|
78
|
+
error: observable,
|
|
79
|
+
isReadingFile: observable,
|
|
80
|
+
fileRequest: observable,
|
|
81
|
+
openExport: action,
|
|
82
|
+
openImport: action,
|
|
83
|
+
close: action,
|
|
84
|
+
setImportText: action,
|
|
85
|
+
importSpec: action,
|
|
86
|
+
readImportFile: flow,
|
|
87
|
+
});
|
|
88
|
+
this.editorState = editorState;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Writes the cube's spec, invalid queries included; a spec over the size cap is refused */
|
|
92
|
+
openExport(): void {
|
|
93
|
+
this.mode = CUBE_SPEC_TRANSFER_MODE.EXPORT;
|
|
94
|
+
this.error = undefined;
|
|
95
|
+
this.exportText = undefined;
|
|
96
|
+
const { document, registry } = this.editorState;
|
|
97
|
+
try {
|
|
98
|
+
this.exportText = serializeCubeSpec(document, registry);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
this.error = errorMessage(error);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
openImport(): void {
|
|
105
|
+
this.mode = CUBE_SPEC_TRANSFER_MODE.IMPORT;
|
|
106
|
+
this.error = undefined;
|
|
107
|
+
this.importText = '';
|
|
108
|
+
this.isReadingFile = false;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
close(): void {
|
|
112
|
+
this.mode = undefined;
|
|
113
|
+
this.exportText = undefined;
|
|
114
|
+
this.importText = '';
|
|
115
|
+
this.error = undefined;
|
|
116
|
+
this.fileRequest++;
|
|
117
|
+
this.isReadingFile = false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
setImportText(text: string): void {
|
|
121
|
+
this.importText = text;
|
|
122
|
+
this.error = undefined;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async copyExport(): Promise<void> {
|
|
126
|
+
if (this.exportText !== undefined) {
|
|
127
|
+
await this.editorState.host.applicationStore.clipboardService.copyTextToClipboard(
|
|
128
|
+
this.exportText,
|
|
129
|
+
{ notifySuccessMessage: 'Cube spec copied to the clipboard' },
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
downloadExport(): void {
|
|
135
|
+
if (this.exportText !== undefined) {
|
|
136
|
+
downloadFileUsingDataURI(
|
|
137
|
+
getCubeSpecFileName(this.editorState.document.name),
|
|
138
|
+
this.exportText,
|
|
139
|
+
ContentType.APPLICATION_JSON,
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Reads a spec file into the import text; a file over the size cap is refused unread */
|
|
145
|
+
*readImportFile(file: File): GeneratorFn<void> {
|
|
146
|
+
const request = ++this.fileRequest;
|
|
147
|
+
this.error = undefined;
|
|
148
|
+
if (file.size > MAX_SPEC_BYTES) {
|
|
149
|
+
// a file being read before this one is dropped
|
|
150
|
+
this.isReadingFile = false;
|
|
151
|
+
this.error = `The file is too large to import: a spec is at most ${MAX_SPEC_BYTES} bytes`;
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
this.isReadingFile = true;
|
|
155
|
+
try {
|
|
156
|
+
const text = (yield readFileAsText(file)) as string;
|
|
157
|
+
if (request === this.fileRequest) {
|
|
158
|
+
this.importText = text;
|
|
159
|
+
}
|
|
160
|
+
} catch (error) {
|
|
161
|
+
if (request === this.fileRequest) {
|
|
162
|
+
// the browser's FileReader rejects with its error event, not an Error
|
|
163
|
+
const reason =
|
|
164
|
+
error instanceof Error
|
|
165
|
+
? error.message
|
|
166
|
+
: error instanceof ProgressEvent &&
|
|
167
|
+
error.target instanceof FileReader
|
|
168
|
+
? (error.target.error?.message ?? 'the browser could not read it')
|
|
169
|
+
: String(error);
|
|
170
|
+
this.error = `Can't read the file: ${reason}`;
|
|
171
|
+
}
|
|
172
|
+
} finally {
|
|
173
|
+
if (request === this.fileRequest) {
|
|
174
|
+
this.isReadingFile = false;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Opens the spec in place of the cube, as one undo step, and never runs it.
|
|
181
|
+
* A spec that can't be read leaves the cube as it was and says why: a
|
|
182
|
+
* broken document names the path of the problem, e.g. `query.nodes[2].database`.
|
|
183
|
+
*/
|
|
184
|
+
importSpec(): boolean {
|
|
185
|
+
let decoded: ReturnType<typeof parseCubeSpec>;
|
|
186
|
+
try {
|
|
187
|
+
decoded = parseCubeSpec(this.importText, {
|
|
188
|
+
registry: this.editorState.registry,
|
|
189
|
+
});
|
|
190
|
+
} catch (error) {
|
|
191
|
+
this.error = getSpecImportError(error);
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
this.editorState.importDocument(decoded.document, decoded.readOnly);
|
|
195
|
+
this.close();
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026-present, Goldman Sachs
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type { ModelContext } from '@finos/legend-cube';
|
|
18
|
+
import type {
|
|
19
|
+
CubeEngine,
|
|
20
|
+
CubeModelOutline,
|
|
21
|
+
} from '../graph-manager/CubeEngine.js';
|
|
22
|
+
import { CUBE_NORTHWIND_MODEL } from './fixtures/CubeNorthwindModel.js';
|
|
23
|
+
|
|
24
|
+
// The models of the slice (PLAN §6.2.3): Pure text bundled with Cube, or
|
|
25
|
+
// pasted by the user. Picking one gives the model context the cube saves, the
|
|
26
|
+
// text itself (§6.2.2), so a saved cube needs no catalog to open.
|
|
27
|
+
|
|
28
|
+
export interface BundledModel {
|
|
29
|
+
readonly id: string;
|
|
30
|
+
readonly label: string;
|
|
31
|
+
readonly model: ModelContext;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const BUNDLED_MODELS: readonly BundledModel[] = Object.freeze([
|
|
35
|
+
{
|
|
36
|
+
id: 'cube-northwind',
|
|
37
|
+
label: 'Northwind (Cube fixture)',
|
|
38
|
+
model: CUBE_NORTHWIND_MODEL,
|
|
39
|
+
},
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
/** The model context of pasted Pure text */
|
|
43
|
+
export const createTextModel = (code: string): ModelContext =>
|
|
44
|
+
Object.freeze({ _type: 'text', code });
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Loads the outlines of models through the engine port, each model's once:
|
|
48
|
+
* the picker asks again whenever it opens
|
|
49
|
+
*/
|
|
50
|
+
export class LocalModelCatalog {
|
|
51
|
+
readonly models = BUNDLED_MODELS;
|
|
52
|
+
private readonly engine: CubeEngine;
|
|
53
|
+
private readonly outlines = new Map<string, Promise<CubeModelOutline>>();
|
|
54
|
+
|
|
55
|
+
constructor(engine: CubeEngine) {
|
|
56
|
+
this.engine = engine;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
loadOutline(model: ModelContext): Promise<CubeModelOutline> {
|
|
60
|
+
const key = JSON.stringify(model);
|
|
61
|
+
let outline = this.outlines.get(key);
|
|
62
|
+
if (!outline) {
|
|
63
|
+
outline = this.engine.loadModel(model);
|
|
64
|
+
// a failed load is tried again next time
|
|
65
|
+
outline.catch(() => this.outlines.delete(key));
|
|
66
|
+
this.outlines.set(key, outline);
|
|
67
|
+
}
|
|
68
|
+
return outline;
|
|
69
|
+
}
|
|
70
|
+
}
|