@finos/legend-cube-builder 0.0.2 → 0.0.3
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/README.md +60 -1
- package/docs/adding-an-operation.md +92 -0
- package/docs/hosting.md +79 -0
- package/docs/testing.md +83 -0
- package/lib/__lib__/LegendCubeHelpText.d.ts.map +1 -1
- package/lib/__lib__/LegendCubeHelpText.js +11 -3
- package/lib/__lib__/LegendCubeHelpText.js.map +1 -1
- package/lib/__lib__/LegendCubeLabels.d.ts +19 -0
- package/lib/__lib__/LegendCubeLabels.d.ts.map +1 -1
- package/lib/__lib__/LegendCubeLabels.js +19 -0
- package/lib/__lib__/LegendCubeLabels.js.map +1 -1
- package/lib/__test-utils__/CubeEngineTestSupport.d.ts +6 -0
- package/lib/__test-utils__/CubeEngineTestSupport.d.ts.map +1 -1
- package/lib/__test-utils__/CubeEngineTestSupport.js +30 -0
- package/lib/__test-utils__/CubeEngineTestSupport.js.map +1 -1
- package/lib/__test-utils__/CubeOperationsTestUtils.d.ts +42 -0
- package/lib/__test-utils__/CubeOperationsTestUtils.d.ts.map +1 -0
- package/lib/__test-utils__/CubeOperationsTestUtils.js +92 -0
- package/lib/__test-utils__/CubeOperationsTestUtils.js.map +1 -0
- package/lib/__test-utils__/FakeCubeEngine.d.ts.map +1 -1
- package/lib/__test-utils__/FakeCubeEngine.js +7 -1
- package/lib/__test-utils__/FakeCubeEngine.js.map +1 -1
- package/lib/components/CubeNodeIcon.d.ts.map +1 -1
- package/lib/components/CubeNodeIcon.js +12 -2
- package/lib/components/CubeNodeIcon.js.map +1 -1
- package/lib/components/canvas/CubeCanvas.d.ts.map +1 -1
- package/lib/components/canvas/CubeCanvas.js +5 -3
- package/lib/components/canvas/CubeCanvas.js.map +1 -1
- package/lib/components/canvas/CubeCanvasElements.d.ts +2 -0
- package/lib/components/canvas/CubeCanvasElements.d.ts.map +1 -1
- package/lib/components/canvas/CubeCanvasElements.js +2 -1
- package/lib/components/canvas/CubeCanvasElements.js.map +1 -1
- package/lib/components/canvas/CubeCanvasNode.d.ts.map +1 -1
- package/lib/components/canvas/CubeCanvasNode.js +5 -2
- package/lib/components/canvas/CubeCanvasNode.js.map +1 -1
- package/lib/components/editors/CubeColumnPicker.d.ts +9 -2
- package/lib/components/editors/CubeColumnPicker.d.ts.map +1 -1
- package/lib/components/editors/CubeColumnPicker.js +9 -4
- package/lib/components/editors/CubeColumnPicker.js.map +1 -1
- package/lib/components/editors/CubeDistinctEditor.d.ts +23 -0
- package/lib/components/editors/CubeDistinctEditor.d.ts.map +1 -0
- package/lib/components/editors/CubeDistinctEditor.js +24 -0
- package/lib/components/editors/CubeDistinctEditor.js.map +1 -0
- package/lib/components/editors/CubeIntegerField.d.ts +29 -0
- package/lib/components/editors/CubeIntegerField.d.ts.map +1 -0
- package/lib/components/editors/CubeIntegerField.js +30 -0
- package/lib/components/editors/CubeIntegerField.js.map +1 -0
- package/lib/components/editors/CubeJoinEditor.d.ts.map +1 -1
- package/lib/components/editors/CubeJoinEditor.js +11 -3
- package/lib/components/editors/CubeJoinEditor.js.map +1 -1
- package/lib/components/editors/CubeNodeEditorPanel.d.ts.map +1 -1
- package/lib/components/editors/CubeNodeEditorPanel.js +3 -2
- package/lib/components/editors/CubeNodeEditorPanel.js.map +1 -1
- package/lib/components/editors/CubeNodeEditorRegistry.d.ts.map +1 -1
- package/lib/components/editors/CubeNodeEditorRegistry.js +14 -1
- package/lib/components/editors/CubeNodeEditorRegistry.js.map +1 -1
- package/lib/components/editors/CubeRenameEditor.d.ts +26 -0
- package/lib/components/editors/CubeRenameEditor.d.ts.map +1 -0
- package/lib/components/editors/CubeRenameEditor.js +58 -0
- package/lib/components/editors/CubeRenameEditor.js.map +1 -0
- package/lib/components/editors/CubeRestrictEditor.d.ts +26 -0
- package/lib/components/editors/CubeRestrictEditor.d.ts.map +1 -0
- package/lib/components/editors/CubeRestrictEditor.js +23 -0
- package/lib/components/editors/CubeRestrictEditor.js.map +1 -0
- package/lib/components/editors/CubeRowCountEditor.d.ts +25 -0
- package/lib/components/editors/CubeRowCountEditor.d.ts.map +1 -0
- package/lib/components/editors/CubeRowCountEditor.js +38 -0
- package/lib/components/editors/CubeRowCountEditor.js.map +1 -0
- package/lib/components/editors/CubeSliceEditor.d.ts +25 -0
- package/lib/components/editors/CubeSliceEditor.d.ts.map +1 -0
- package/lib/components/editors/CubeSliceEditor.js +40 -0
- package/lib/components/editors/CubeSliceEditor.js.map +1 -0
- package/lib/components/editors/CubeSortEditor.d.ts +27 -0
- package/lib/components/editors/CubeSortEditor.d.ts.map +1 -0
- package/lib/components/editors/CubeSortEditor.js +72 -0
- package/lib/components/editors/CubeSortEditor.js.map +1 -0
- package/lib/components/editors/CubeSourceEditor.d.ts.map +1 -1
- package/lib/components/editors/CubeSourceEditor.js +1 -2
- package/lib/components/editors/CubeSourceEditor.js.map +1 -1
- package/lib/components/editors/CubeValueEditor.d.ts.map +1 -1
- package/lib/components/editors/CubeValueEditor.js +2 -1
- package/lib/components/editors/CubeValueEditor.js.map +1 -1
- package/lib/components/grid/CubeGridRegion.d.ts.map +1 -1
- package/lib/components/grid/CubeGridRegion.js +1 -1
- package/lib/components/grid/CubeGridRegion.js.map +1 -1
- package/lib/components/grid/CubeResultGrid.d.ts +14 -1
- package/lib/components/grid/CubeResultGrid.d.ts.map +1 -1
- package/lib/components/grid/CubeResultGrid.js +41 -5
- package/lib/components/grid/CubeResultGrid.js.map +1 -1
- package/lib/graph-manager/CubeEngine.d.ts +13 -0
- package/lib/graph-manager/CubeEngine.d.ts.map +1 -1
- package/lib/graph-manager/CubeEngine.js.map +1 -1
- package/lib/graph-manager/CubeModelOutlineHelper.d.ts +8 -0
- package/lib/graph-manager/CubeModelOutlineHelper.d.ts.map +1 -1
- package/lib/graph-manager/CubeModelOutlineHelper.js +21 -0
- package/lib/graph-manager/CubeModelOutlineHelper.js.map +1 -1
- package/lib/graph-manager/protocol/pure/v1/V1_CubeModelOutlineBuilder.d.ts.map +1 -1
- package/lib/graph-manager/protocol/pure/v1/V1_CubeModelOutlineBuilder.js +56 -3
- package/lib/graph-manager/protocol/pure/v1/V1_CubeModelOutlineBuilder.js.map +1 -1
- package/lib/graph-manager/protocol/pure/v1/__test-utils__/V1_CubeEngineTestUtils.d.ts.map +1 -1
- package/lib/graph-manager/protocol/pure/v1/__test-utils__/V1_CubeEngineTestUtils.js +3 -3
- package/lib/graph-manager/protocol/pure/v1/__test-utils__/V1_CubeEngineTestUtils.js.map +1 -1
- package/lib/index.css +1 -1
- package/lib/stores/CubeEditorState.d.ts +34 -4
- package/lib/stores/CubeEditorState.d.ts.map +1 -1
- package/lib/stores/CubeEditorState.js +87 -6
- package/lib/stores/CubeEditorState.js.map +1 -1
- package/lib/stores/CubeExecutionState.d.ts.map +1 -1
- package/lib/stores/CubeExecutionState.js +19 -3
- package/lib/stores/CubeExecutionState.js.map +1 -1
- package/lib/stores/CubeGridQuickActions.d.ts +40 -0
- package/lib/stores/CubeGridQuickActions.d.ts.map +1 -0
- package/lib/stores/CubeGridQuickActions.js +86 -0
- package/lib/stores/CubeGridQuickActions.js.map +1 -0
- package/lib/stores/CubeNodeEditorState.d.ts +19 -1
- package/lib/stores/CubeNodeEditorState.d.ts.map +1 -1
- package/lib/stores/CubeNodeEditorState.js +55 -0
- package/lib/stores/CubeNodeEditorState.js.map +1 -1
- package/lib/stores/CubeShowPureState.d.ts +4 -1
- package/lib/stores/CubeShowPureState.d.ts.map +1 -1
- package/lib/stores/CubeShowPureState.js +22 -5
- package/lib/stores/CubeShowPureState.js.map +1 -1
- package/lib/stores/editors/CubeIntegerText.d.ts +24 -0
- package/lib/stores/editors/CubeIntegerText.d.ts.map +1 -0
- package/lib/stores/editors/CubeIntegerText.js +33 -0
- package/lib/stores/editors/CubeIntegerText.js.map +1 -0
- package/lib/stores/editors/CubeJoinDraft.d.ts +13 -5
- package/lib/stores/editors/CubeJoinDraft.d.ts.map +1 -1
- package/lib/stores/editors/CubeJoinDraft.js +21 -12
- package/lib/stores/editors/CubeJoinDraft.js.map +1 -1
- package/lib/stores/editors/CubeNodeDraftRegistry.d.ts +6 -0
- package/lib/stores/editors/CubeNodeDraftRegistry.d.ts.map +1 -1
- package/lib/stores/editors/CubeNodeDraftRegistry.js +23 -1
- package/lib/stores/editors/CubeNodeDraftRegistry.js.map +1 -1
- package/lib/stores/editors/CubeRenameDraft.d.ts +44 -0
- package/lib/stores/editors/CubeRenameDraft.d.ts.map +1 -0
- package/lib/stores/editors/CubeRenameDraft.js +79 -0
- package/lib/stores/editors/CubeRenameDraft.js.map +1 -0
- package/lib/stores/editors/CubeRestrictDraft.d.ts +37 -0
- package/lib/stores/editors/CubeRestrictDraft.d.ts.map +1 -0
- package/lib/stores/editors/CubeRestrictDraft.js +73 -0
- package/lib/stores/editors/CubeRestrictDraft.js.map +1 -0
- package/lib/stores/editors/CubeRowCountDraft.d.ts +42 -0
- package/lib/stores/editors/CubeRowCountDraft.d.ts.map +1 -0
- package/lib/stores/editors/CubeRowCountDraft.js +55 -0
- package/lib/stores/editors/CubeRowCountDraft.js.map +1 -0
- package/lib/stores/editors/CubeSliceDraft.d.ts +42 -0
- package/lib/stores/editors/CubeSliceDraft.d.ts.map +1 -0
- package/lib/stores/editors/CubeSliceDraft.js +72 -0
- package/lib/stores/editors/CubeSliceDraft.js.map +1 -0
- package/lib/stores/editors/CubeSortDraft.d.ts +49 -0
- package/lib/stores/editors/CubeSortDraft.d.ts.map +1 -0
- package/lib/stores/editors/CubeSortDraft.js +99 -0
- package/lib/stores/editors/CubeSortDraft.js.map +1 -0
- package/package.json +8 -8
- package/src/__lib__/LegendCubeHelpText.ts +22 -2
- package/src/__lib__/LegendCubeLabels.ts +30 -0
- package/src/__test-utils__/CubeEngineTestSupport.ts +35 -0
- package/src/__test-utils__/CubeOperationsTestUtils.ts +177 -0
- package/src/__test-utils__/FakeCubeEngine.ts +7 -1
- package/src/components/CubeNodeIcon.tsx +18 -1
- package/src/components/canvas/CubeCanvas.tsx +5 -3
- package/src/components/canvas/CubeCanvasElements.ts +4 -1
- package/src/components/canvas/CubeCanvasNode.tsx +9 -1
- package/src/components/editors/CubeColumnPicker.tsx +36 -12
- package/src/components/editors/CubeDistinctEditor.tsx +28 -0
- package/src/components/editors/CubeIntegerField.tsx +51 -0
- package/src/components/editors/CubeJoinEditor.tsx +26 -3
- package/src/components/editors/CubeNodeEditorPanel.tsx +12 -1
- package/src/components/editors/CubeNodeEditorRegistry.ts +20 -0
- package/src/components/editors/CubeRenameEditor.tsx +143 -0
- package/src/components/editors/CubeRestrictEditor.tsx +103 -0
- package/src/components/editors/CubeRowCountEditor.tsx +53 -0
- package/src/components/editors/CubeSliceEditor.tsx +69 -0
- package/src/components/editors/CubeSortEditor.tsx +206 -0
- package/src/components/editors/CubeSourceEditor.tsx +0 -10
- package/src/components/editors/CubeValueEditor.tsx +2 -1
- package/src/components/grid/CubeGridRegion.tsx +1 -0
- package/src/components/grid/CubeResultGrid.tsx +59 -5
- package/src/graph-manager/CubeEngine.ts +14 -0
- package/src/graph-manager/CubeModelOutlineHelper.ts +26 -0
- package/src/graph-manager/protocol/pure/v1/V1_CubeModelOutlineBuilder.ts +89 -3
- package/src/graph-manager/protocol/pure/v1/__test-utils__/V1_CubeEngineTestUtils.ts +6 -3
- package/src/stores/CubeEditorState.ts +110 -5
- package/src/stores/CubeExecutionState.ts +36 -3
- package/src/stores/CubeGridQuickActions.ts +129 -0
- package/src/stores/CubeNodeEditorState.ts +70 -1
- package/src/stores/CubeShowPureState.ts +27 -5
- package/src/stores/editors/CubeIntegerText.ts +34 -0
- package/src/stores/editors/CubeJoinDraft.ts +43 -16
- package/src/stores/editors/CubeNodeDraftRegistry.ts +35 -1
- package/src/stores/editors/CubeRenameDraft.ts +110 -0
- package/src/stores/editors/CubeRestrictDraft.ts +83 -0
- package/src/stores/editors/CubeRowCountDraft.ts +69 -0
- package/src/stores/editors/CubeSliceDraft.ts +87 -0
- package/src/stores/editors/CubeSortDraft.ts +135 -0
- package/tsconfig.json +15 -0
|
@@ -135,3 +135,38 @@ export const CUBE_ENGINE_TEST__execute = async (
|
|
|
135
135
|
text: async () => response.data,
|
|
136
136
|
};
|
|
137
137
|
};
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Generates the execution plan of a query, never running it, and gives the
|
|
141
|
+
* SQL of its nodes, outermost first; a body given as text (with lossless
|
|
142
|
+
* numbers) is sent as it is
|
|
143
|
+
*/
|
|
144
|
+
export const CUBE_ENGINE_TEST__generatePlanSql = async (
|
|
145
|
+
input: object | string,
|
|
146
|
+
): Promise<string[]> => {
|
|
147
|
+
const { data } = await axios.post<unknown, AxiosResponse<string>>(
|
|
148
|
+
`${PURE_API}/execution/generatePlan`,
|
|
149
|
+
input,
|
|
150
|
+
{
|
|
151
|
+
headers: JSON_HEADERS,
|
|
152
|
+
responseType: 'text',
|
|
153
|
+
transformResponse: (text: string) => text,
|
|
154
|
+
},
|
|
155
|
+
);
|
|
156
|
+
const sql: string[] = [];
|
|
157
|
+
const visit = (node: unknown): void => {
|
|
158
|
+
if (Array.isArray(node)) {
|
|
159
|
+
node.forEach(visit);
|
|
160
|
+
} else if (node && typeof node === 'object') {
|
|
161
|
+
Object.entries(node).forEach(([key, value]) => {
|
|
162
|
+
if (key === 'sqlQuery' && typeof value === 'string') {
|
|
163
|
+
sql.push(value);
|
|
164
|
+
} else {
|
|
165
|
+
visit(value);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
visit(parseLosslessJSON(data));
|
|
171
|
+
return sql;
|
|
172
|
+
};
|
|
@@ -0,0 +1,177 @@
|
|
|
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 { expect } from '@jest/globals';
|
|
18
|
+
import {
|
|
19
|
+
buildSchemasAndValidity,
|
|
20
|
+
Connection,
|
|
21
|
+
createNodeRegistry,
|
|
22
|
+
Query,
|
|
23
|
+
type QueryNode,
|
|
24
|
+
QueryEmitter,
|
|
25
|
+
RelationalTableSource,
|
|
26
|
+
Schema,
|
|
27
|
+
} from '@finos/legend-cube';
|
|
28
|
+
import type {
|
|
29
|
+
CubeEngine,
|
|
30
|
+
CubeResult,
|
|
31
|
+
CubeResultValue,
|
|
32
|
+
} from '../graph-manager/CubeEngine.js';
|
|
33
|
+
import {
|
|
34
|
+
CUBE_NORTHWIND_DATABASE,
|
|
35
|
+
CUBE_NORTHWIND_MODEL,
|
|
36
|
+
CUBE_NORTHWIND_RUNTIME,
|
|
37
|
+
} from '../stores/fixtures/CubeNorthwindModel.js';
|
|
38
|
+
|
|
39
|
+
// Helpers for engine tests of operations on the Cube Northwind fixture,
|
|
40
|
+
// through the engine port only (no V1 classes), so any CubeEngine works
|
|
41
|
+
|
|
42
|
+
/** A table of the Cube Northwind model, not resolved yet */
|
|
43
|
+
export const TEST__northwindTable = (
|
|
44
|
+
id: string,
|
|
45
|
+
table: string,
|
|
46
|
+
schema = 'NORTHWIND',
|
|
47
|
+
): RelationalTableSource =>
|
|
48
|
+
new RelationalTableSource(id, {
|
|
49
|
+
database: CUBE_NORTHWIND_DATABASE,
|
|
50
|
+
schema,
|
|
51
|
+
table,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
/** The sources, resolved through the engine as the editor resolves them */
|
|
55
|
+
export const TEST__resolveSources = async (
|
|
56
|
+
engine: CubeEngine,
|
|
57
|
+
sources: readonly RelationalTableSource[],
|
|
58
|
+
): Promise<RelationalTableSource[]> => {
|
|
59
|
+
const typed = await engine.resolveSchemas(
|
|
60
|
+
CUBE_NORTHWIND_MODEL,
|
|
61
|
+
new Map(
|
|
62
|
+
sources.map((source) => [
|
|
63
|
+
source.id,
|
|
64
|
+
[source.database, source.schema, source.table] as const,
|
|
65
|
+
]),
|
|
66
|
+
),
|
|
67
|
+
);
|
|
68
|
+
return sources.map((source) => {
|
|
69
|
+
const schema = typed.get(source.id);
|
|
70
|
+
if (!(schema instanceof Schema)) {
|
|
71
|
+
throw schema ?? new Error(`No schema for ${source.id}`);
|
|
72
|
+
}
|
|
73
|
+
return source.withResolution({ kind: 'resolved', schema });
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/** A chain of nodes, each feeding the next on its first port, captured at the last */
|
|
78
|
+
export const TEST__chainOf = (nodes: readonly QueryNode[]): Query =>
|
|
79
|
+
new Query(
|
|
80
|
+
nodes,
|
|
81
|
+
nodes
|
|
82
|
+
.slice(1)
|
|
83
|
+
.map(
|
|
84
|
+
(node, index) =>
|
|
85
|
+
new Connection(
|
|
86
|
+
(nodes[index] as QueryNode).id,
|
|
87
|
+
node.id,
|
|
88
|
+
node.ports[0] as string,
|
|
89
|
+
),
|
|
90
|
+
),
|
|
91
|
+
nodes.at(-1)?.id,
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
/** Every node of the query is valid */
|
|
95
|
+
export const TEST__expectValidQuery = (query: Query): void => {
|
|
96
|
+
const { validity } = buildSchemasAndValidity(
|
|
97
|
+
query,
|
|
98
|
+
createNodeRegistry().queryRules,
|
|
99
|
+
);
|
|
100
|
+
expect([...validity].filter(([, errors]) => errors.length)).toEqual([]);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Runs the query up to its capture node, with this row limit; with a database
|
|
105
|
+
* type, written as for that database (on the fixture's H2 all the same, so a
|
|
106
|
+
* workaround's rows can be checked)
|
|
107
|
+
*/
|
|
108
|
+
export const TEST__runQuery = async (
|
|
109
|
+
engine: CubeEngine,
|
|
110
|
+
query: Query,
|
|
111
|
+
rowLimit: number,
|
|
112
|
+
databaseType?: string,
|
|
113
|
+
): Promise<CubeResult> => {
|
|
114
|
+
TEST__expectValidQuery(query);
|
|
115
|
+
return engine.execute(
|
|
116
|
+
CUBE_NORTHWIND_MODEL,
|
|
117
|
+
new QueryEmitter(query).emitExecutionLambda({
|
|
118
|
+
rowLimit,
|
|
119
|
+
runtime: CUBE_NORTHWIND_RUNTIME,
|
|
120
|
+
databaseType,
|
|
121
|
+
}),
|
|
122
|
+
);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/** The values of one column of a result */
|
|
126
|
+
export const TEST__columnValues = (
|
|
127
|
+
result: CubeResult,
|
|
128
|
+
column: string,
|
|
129
|
+
): CubeResultValue[] => {
|
|
130
|
+
const index = result.columns.indexOf(column);
|
|
131
|
+
expect(index).toBeGreaterThanOrEqual(0);
|
|
132
|
+
return result.rows.map((row) => row[index] ?? null);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/** The schema Cube infers for a node of the query, its capture node by default */
|
|
136
|
+
export const TEST__inferredSchema = (
|
|
137
|
+
query: Query,
|
|
138
|
+
nodeId = query.selected ?? '',
|
|
139
|
+
): Schema => {
|
|
140
|
+
const schema = buildSchemasAndValidity(
|
|
141
|
+
query,
|
|
142
|
+
createNodeRegistry().queryRules,
|
|
143
|
+
).schemas.get(nodeId);
|
|
144
|
+
expect(schema).toBeDefined();
|
|
145
|
+
return schema as Schema;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* The engine types the node as Cube infers it: the same names, in the same
|
|
150
|
+
* order, of the same types and parameters. Cube's nullability may only be
|
|
151
|
+
* wider, since the engine misreports it after outer joins (PLAN §4.7).
|
|
152
|
+
*/
|
|
153
|
+
export const TEST__expectEngineTyping = async (
|
|
154
|
+
engine: CubeEngine,
|
|
155
|
+
query: Query,
|
|
156
|
+
nodeId = query.selected ?? '',
|
|
157
|
+
): Promise<void> => {
|
|
158
|
+
const cube = TEST__inferredSchema(query, nodeId);
|
|
159
|
+
const typed = (
|
|
160
|
+
await engine.typeLambdas(
|
|
161
|
+
CUBE_NORTHWIND_MODEL,
|
|
162
|
+
new Map([[nodeId, new QueryEmitter(query).emitTypingLambda(nodeId)]]),
|
|
163
|
+
)
|
|
164
|
+
).get(nodeId);
|
|
165
|
+
expect(typed).toBeInstanceOf(Schema);
|
|
166
|
+
const engineSchema = typed as Schema;
|
|
167
|
+
expect(
|
|
168
|
+
cube.columns.map((column) => [column.name, column.type.fullName]),
|
|
169
|
+
).toEqual(
|
|
170
|
+
engineSchema.columns.map((column) => [column.name, column.type.fullName]),
|
|
171
|
+
);
|
|
172
|
+
cube.columns.forEach((column, index) => {
|
|
173
|
+
if (engineSchema.columns[index]?.nullable) {
|
|
174
|
+
expect([column.name, column.nullable]).toEqual([column.name, true]);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
};
|
|
@@ -62,7 +62,13 @@ export const FAKE_NORTHWIND_OUTLINE: CubeModelOutline = {
|
|
|
62
62
|
],
|
|
63
63
|
},
|
|
64
64
|
],
|
|
65
|
-
runtimes: [
|
|
65
|
+
runtimes: [
|
|
66
|
+
{
|
|
67
|
+
path: NORTHWIND_RUNTIME,
|
|
68
|
+
storePaths: [NORTHWIND_DATABASE],
|
|
69
|
+
connections: [{ storePath: NORTHWIND_DATABASE, databaseType: 'H2' }],
|
|
70
|
+
},
|
|
71
|
+
],
|
|
66
72
|
};
|
|
67
73
|
|
|
68
74
|
/** The schemas the fake gives, keyed by `<schema>.<table>` as the outline names them */
|
|
@@ -15,10 +15,17 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import {
|
|
18
|
+
AlignBottomIcon,
|
|
19
|
+
AlignMiddleIcon,
|
|
20
|
+
AlignTopIcon,
|
|
18
21
|
ArrowsJoinIcon,
|
|
22
|
+
CompressIcon,
|
|
23
|
+
DataCubeIcon,
|
|
19
24
|
clsx,
|
|
20
25
|
FilterIcon,
|
|
26
|
+
PencilIcon,
|
|
21
27
|
QuestionSquareIcon,
|
|
28
|
+
SortIcon,
|
|
22
29
|
TableIcon,
|
|
23
30
|
} from '@finos/legend-art';
|
|
24
31
|
|
|
@@ -29,6 +36,13 @@ const NODE_ICONS: Readonly<
|
|
|
29
36
|
table: TableIcon,
|
|
30
37
|
filter: FilterIcon,
|
|
31
38
|
join: ArrowsJoinIcon,
|
|
39
|
+
limit: AlignTopIcon,
|
|
40
|
+
drop: AlignBottomIcon,
|
|
41
|
+
slice: AlignMiddleIcon,
|
|
42
|
+
distinct: CompressIcon,
|
|
43
|
+
restrict: DataCubeIcon.TableColumns,
|
|
44
|
+
rename: PencilIcon,
|
|
45
|
+
sort: SortIcon,
|
|
32
46
|
};
|
|
33
47
|
|
|
34
48
|
/** Whether a node definition's icon name maps to an icon, rather than the question mark */
|
|
@@ -41,7 +55,10 @@ export const CubeNodeIcon: React.FC<{
|
|
|
41
55
|
className?: string;
|
|
42
56
|
}> = (props) => {
|
|
43
57
|
const { icon, className } = props;
|
|
58
|
+
// an own key only: a name such as `constructor` must not reach Object's
|
|
44
59
|
const Icon =
|
|
45
|
-
|
|
60
|
+
icon !== undefined && hasCubeNodeIcon(icon)
|
|
61
|
+
? (NODE_ICONS[icon] ?? QuestionSquareIcon)
|
|
62
|
+
: QuestionSquareIcon;
|
|
46
63
|
return <Icon className={clsx(className)} />;
|
|
47
64
|
};
|
|
@@ -93,14 +93,16 @@ const CubeCanvasFlow = observer((props: { editorState: CubeEditorState }) => {
|
|
|
93
93
|
);
|
|
94
94
|
const edges = useMemo(() => buildCubeCanvasEdges(query), [query]);
|
|
95
95
|
// the view fits the graph again whenever the layout moves a node, or the
|
|
96
|
-
// canvas
|
|
97
|
-
// once React Flow has measured every node:
|
|
96
|
+
// canvas changes size (e.g. the node editor opens beside it, or the
|
|
97
|
+
// splitter above the grid moves), once React Flow has measured every node:
|
|
98
|
+
// it fits only measured ones
|
|
98
99
|
const layoutSignature = useMemo(
|
|
99
100
|
() => JSON.stringify([...positions]),
|
|
100
101
|
[positions],
|
|
101
102
|
);
|
|
102
103
|
const nodesInitialized = useNodesInitialized();
|
|
103
104
|
const canvasWidth = useStore((state) => state.width);
|
|
105
|
+
const canvasHeight = useStore((state) => state.height);
|
|
104
106
|
useEffect(() => {
|
|
105
107
|
if (!nodesInitialized) {
|
|
106
108
|
return undefined;
|
|
@@ -109,7 +111,7 @@ const CubeCanvasFlow = observer((props: { editorState: CubeEditorState }) => {
|
|
|
109
111
|
fitView(FIT_VIEW_OPTIONS).catch(noop());
|
|
110
112
|
}, 0);
|
|
111
113
|
return () => window.clearTimeout(timer);
|
|
112
|
-
}, [layoutSignature, canvasWidth, nodesInitialized, fitView]);
|
|
114
|
+
}, [layoutSignature, canvasWidth, canvasHeight, nodesInitialized, fitView]);
|
|
113
115
|
|
|
114
116
|
return (
|
|
115
117
|
<ReactFlow<CubeCanvasFlowNode, CubeCanvasFlowEdge>
|
|
@@ -117,6 +117,8 @@ export interface CubeCanvasNodeStatus {
|
|
|
117
117
|
readonly isResolving: boolean;
|
|
118
118
|
/** The last run failed on it */
|
|
119
119
|
readonly hasEngineError: boolean;
|
|
120
|
+
/** It has a warning, stored (a table that changed) or derived (a lost sort order) */
|
|
121
|
+
readonly hasWarnings: boolean;
|
|
120
122
|
/** Its errors, then its warnings, its description and its id, one a line */
|
|
121
123
|
readonly tooltip: string;
|
|
122
124
|
}
|
|
@@ -128,7 +130,7 @@ export const getCubeCanvasNodeStatus = (
|
|
|
128
130
|
const ownErrors = editorState.analysis.validity.get(node.id);
|
|
129
131
|
// the tooltip adds the engine's error; the states keep it apart
|
|
130
132
|
const errors = editorState.getNodeErrors(node.id);
|
|
131
|
-
const warnings = editorState.
|
|
133
|
+
const warnings = editorState.getNodeWarnings(node);
|
|
132
134
|
return {
|
|
133
135
|
isCapture: editorState.document.query.selected === node.id,
|
|
134
136
|
isInvalid: (ownErrors ?? []).some(
|
|
@@ -139,6 +141,7 @@ export const getCubeCanvasNodeStatus = (
|
|
|
139
141
|
),
|
|
140
142
|
isResolving: editorState.isPendingSource(node),
|
|
141
143
|
hasEngineError: editorState.hostIssues.has(node.id),
|
|
144
|
+
hasWarnings: warnings.length > 0,
|
|
142
145
|
tooltip: [
|
|
143
146
|
...(ownErrors ? [] : [MISSING_VALIDITY_TOOLTIP]),
|
|
144
147
|
...errors,
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import { clsx, ContextMenu } from '@finos/legend-art';
|
|
17
|
+
import { clsx, ContextMenu, WarningIcon } from '@finos/legend-art';
|
|
18
18
|
import { Handle, type NodeProps, Position } from '@xyflow/react';
|
|
19
19
|
import { observer } from 'mobx-react-lite';
|
|
20
20
|
import { useRef } from 'react';
|
|
@@ -113,6 +113,7 @@ export const CubeCanvasNode = observer(
|
|
|
113
113
|
'legend-cube__node--resolving animate-pulse': status.isResolving,
|
|
114
114
|
'legend-cube__node--engine-error bg-[var(--color-status-error-bg)]':
|
|
115
115
|
status.hasEngineError,
|
|
116
|
+
'legend-cube__node--warning': status.hasWarnings,
|
|
116
117
|
'legend-cube__node--drop-target outline-dashed outline-2 outline-[var(--color-accent)]':
|
|
117
118
|
isDropTarget,
|
|
118
119
|
},
|
|
@@ -128,6 +129,13 @@ export const CubeCanvasNode = observer(
|
|
|
128
129
|
<span className="line-clamp-2 min-w-0 break-words leading-tight">
|
|
129
130
|
{node.describe()}
|
|
130
131
|
</span>
|
|
132
|
+
{status.hasWarnings && (
|
|
133
|
+
// the warning's text is in the node's tooltip
|
|
134
|
+
<WarningIcon
|
|
135
|
+
className="ml-auto shrink-0 text-[var(--color-status-warn)]"
|
|
136
|
+
aria-hidden={true}
|
|
137
|
+
/>
|
|
138
|
+
)}
|
|
131
139
|
</div>
|
|
132
140
|
<Handle
|
|
133
141
|
id={CUBE_OUTPUT_HANDLE_ID}
|
|
@@ -23,7 +23,12 @@ import {
|
|
|
23
23
|
StringTypeIcon,
|
|
24
24
|
ToggleIcon,
|
|
25
25
|
} from '@finos/legend-art';
|
|
26
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
type CubeType,
|
|
28
|
+
type Schema,
|
|
29
|
+
type SchemaColumn,
|
|
30
|
+
TypeFamily,
|
|
31
|
+
} from '@finos/legend-cube';
|
|
27
32
|
import { getColumnTypeLabel } from '../../__lib__/LegendCubeLabels.js';
|
|
28
33
|
|
|
29
34
|
const FAMILY_ICONS: Readonly<
|
|
@@ -55,7 +60,9 @@ export const CubeColumnTypeIcon: React.FC<{
|
|
|
55
60
|
* columns of the actual input). Each option names its type, e.g.
|
|
56
61
|
* `CUSTOMER_ID: Varchar(5)?`; the picked column's type shows beside, with
|
|
57
62
|
* its family's icon and its full type in the tooltip. A column the input
|
|
58
|
-
* doesn't have stays shown, so the problem can be seen and fixed.
|
|
63
|
+
* doesn't have stays shown, so the problem can be seen and fixed. A column
|
|
64
|
+
* the editor can't take here is shown, with the reason `isColumnDisabled`
|
|
65
|
+
* gives after its type, but can't be picked.
|
|
59
66
|
*/
|
|
60
67
|
export const CubeColumnPicker: React.FC<{
|
|
61
68
|
/** What the column is for, e.g. `Left join column 1`: the picker's accessible name */
|
|
@@ -67,8 +74,21 @@ export const CubeColumnPicker: React.FC<{
|
|
|
67
74
|
disabled?: boolean | undefined;
|
|
68
75
|
/** Marks the pick as wrong, e.g. a type that doesn't match */
|
|
69
76
|
invalid?: boolean | undefined;
|
|
77
|
+
/**
|
|
78
|
+
* Why a column can't be picked, shown after its type, e.g. a Sort's Variant
|
|
79
|
+
* column, or one another row has; `undefined` when it can be
|
|
80
|
+
*/
|
|
81
|
+
isColumnDisabled?: ((column: SchemaColumn) => string | undefined) | undefined;
|
|
70
82
|
}> = (props) => {
|
|
71
|
-
const {
|
|
83
|
+
const {
|
|
84
|
+
label,
|
|
85
|
+
schema,
|
|
86
|
+
value,
|
|
87
|
+
onChange,
|
|
88
|
+
disabled,
|
|
89
|
+
invalid,
|
|
90
|
+
isColumnDisabled,
|
|
91
|
+
} = props;
|
|
72
92
|
const picked = value ? schema.lookup(value) : undefined;
|
|
73
93
|
return (
|
|
74
94
|
<div className="flex min-w-0 items-center gap-1">
|
|
@@ -90,15 +110,19 @@ export const CubeColumnPicker: React.FC<{
|
|
|
90
110
|
{value ? `${value} (not in the input)` : 'Pick a column'}
|
|
91
111
|
</option>
|
|
92
112
|
)}
|
|
93
|
-
{schema.columns.map((column) =>
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
113
|
+
{schema.columns.map((column) => {
|
|
114
|
+
const reason = isColumnDisabled?.(column);
|
|
115
|
+
return (
|
|
116
|
+
<option
|
|
117
|
+
key={column.name}
|
|
118
|
+
value={column.name}
|
|
119
|
+
title={column.type.fullName}
|
|
120
|
+
disabled={reason !== undefined}
|
|
121
|
+
>
|
|
122
|
+
{`${column.name}: ${getColumnTypeLabel(column)}${reason === undefined ? '' : ` (${reason})`}`}
|
|
123
|
+
</option>
|
|
124
|
+
);
|
|
125
|
+
})}
|
|
102
126
|
</select>
|
|
103
127
|
{picked && (
|
|
104
128
|
<span
|
|
@@ -0,0 +1,28 @@
|
|
|
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 { observer } from 'mobx-react-lite';
|
|
18
|
+
import { DISTINCT_EDITOR_TEXT } from '../../__lib__/LegendCubeLabels.js';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The Distinct editor (spec §17.6: "nothing — description only"). Distinct
|
|
22
|
+
* has no settings, so it has no draft, and the panel shows no Apply or Cancel.
|
|
23
|
+
*/
|
|
24
|
+
export const CubeDistinctEditor = observer(() => (
|
|
25
|
+
<div className="text-base text-[var(--color-text-secondary)]">
|
|
26
|
+
{DISTINCT_EDITOR_TEXT}
|
|
27
|
+
</div>
|
|
28
|
+
));
|
|
@@ -0,0 +1,51 @@
|
|
|
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 { clsx } from '@finos/legend-art';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A field for a whole number, such as a size or a row index. It shows the
|
|
21
|
+
* text as typed and reports every keystroke, so the panel checks the edited
|
|
22
|
+
* node as the user types. Enter and Escape do nothing special: Apply is the
|
|
23
|
+
* panel's, and Ctrl+Z is the field's own undo.
|
|
24
|
+
*/
|
|
25
|
+
export const CubeIntegerField: React.FC<{
|
|
26
|
+
label: string;
|
|
27
|
+
text: string;
|
|
28
|
+
invalid: boolean;
|
|
29
|
+
disabled: boolean;
|
|
30
|
+
onChange: (text: string) => void;
|
|
31
|
+
}> = (props) => {
|
|
32
|
+
const { label, text, invalid, disabled, onChange } = props;
|
|
33
|
+
return (
|
|
34
|
+
<input
|
|
35
|
+
aria-label={label}
|
|
36
|
+
aria-invalid={invalid}
|
|
37
|
+
className={clsx(
|
|
38
|
+
'h-6 w-24 rounded-sm border bg-[var(--color-bg-input)] px-1 text-base',
|
|
39
|
+
invalid
|
|
40
|
+
? 'border-[var(--color-status-error)] text-[var(--color-status-error)]'
|
|
41
|
+
: 'border-[var(--color-border-default)]',
|
|
42
|
+
)}
|
|
43
|
+
type="text"
|
|
44
|
+
inputMode="numeric"
|
|
45
|
+
spellCheck={false}
|
|
46
|
+
value={text}
|
|
47
|
+
disabled={disabled}
|
|
48
|
+
onChange={(event) => onChange(event.target.value)}
|
|
49
|
+
/>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
@@ -21,13 +21,17 @@ import {
|
|
|
21
21
|
isJoinType,
|
|
22
22
|
JOIN_TYPE_LABELS,
|
|
23
23
|
JOIN_TYPES,
|
|
24
|
+
planJoinDuplicateFix,
|
|
24
25
|
type Schema,
|
|
25
26
|
} from '@finos/legend-cube';
|
|
26
27
|
import { guaranteeType, noop } from '@finos/legend-shared';
|
|
27
28
|
import { flowResult } from 'mobx';
|
|
28
29
|
import { observer } from 'mobx-react-lite';
|
|
29
30
|
import { useEffect } from 'react';
|
|
30
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
CUBE_TABLE_FLAG_LABELS,
|
|
33
|
+
READ_ONLY_CUBE_TITLE,
|
|
34
|
+
} from '../../__lib__/LegendCubeLabels.js';
|
|
31
35
|
import { CubeTableFlag } from '../../graph-manager/CubeEngine.js';
|
|
32
36
|
import {
|
|
33
37
|
type CubeJoinKeyPair,
|
|
@@ -137,6 +141,10 @@ export const CubeJoinEditor = observer((props: CubeNodeEditorProps) => {
|
|
|
137
141
|
edited.leftColumns,
|
|
138
142
|
edited.rightColumns,
|
|
139
143
|
);
|
|
144
|
+
// the names the autofix would give, to show beside each shared column
|
|
145
|
+
const fix = duplicates.length
|
|
146
|
+
? planJoinDuplicateFix(edited, leftSchema, rightSchema)
|
|
147
|
+
: undefined;
|
|
140
148
|
const canAddPair =
|
|
141
149
|
!readOnly &&
|
|
142
150
|
draft.pairs.length <
|
|
@@ -211,12 +219,27 @@ export const CubeJoinEditor = observer((props: CubeNodeEditorProps) => {
|
|
|
211
219
|
both:
|
|
212
220
|
</div>
|
|
213
221
|
<ul aria-label="Columns in both inputs">
|
|
214
|
-
{duplicates.map((name) => (
|
|
222
|
+
{duplicates.map((name, index) => (
|
|
215
223
|
<li key={name} className="font-mono">
|
|
216
|
-
{
|
|
224
|
+
{fix
|
|
225
|
+
? `${name} → ${fix.left[index]?.to} (Left), ${fix.right[index]?.to} (Right)`
|
|
226
|
+
: name}
|
|
217
227
|
</li>
|
|
218
228
|
))}
|
|
219
229
|
</ul>
|
|
230
|
+
<div className="mt-1">
|
|
231
|
+
<CubeButton
|
|
232
|
+
title={
|
|
233
|
+
readOnly
|
|
234
|
+
? READ_ONLY_CUBE_TITLE
|
|
235
|
+
: 'Add a Rename before each input that gives these columns new names. Applies your changes too, as one step to undo.'
|
|
236
|
+
}
|
|
237
|
+
disabled={!editorState.nodeEditor.canRenameDuplicateColumns}
|
|
238
|
+
onClick={() => editorState.nodeEditor.renameDuplicateColumns()}
|
|
239
|
+
>
|
|
240
|
+
Rename them
|
|
241
|
+
</CubeButton>
|
|
242
|
+
</div>
|
|
220
243
|
</div>
|
|
221
244
|
)}
|
|
222
245
|
</div>
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import { QuestionCircleIcon, TimesIcon } from '@finos/legend-art';
|
|
17
|
+
import { QuestionCircleIcon, TimesIcon, WarningIcon } from '@finos/legend-art';
|
|
18
18
|
import {
|
|
19
19
|
isIncompleteError,
|
|
20
20
|
isSchemasError,
|
|
@@ -80,6 +80,7 @@ export const CubeNodeEditorPanel = observer(
|
|
|
80
80
|
isEditable && !inputProblems.length
|
|
81
81
|
? validateEdited(edited, inputSchemas)
|
|
82
82
|
: [];
|
|
83
|
+
const warnings = editorState.getNodeWarnings(node);
|
|
83
84
|
return (
|
|
84
85
|
<div
|
|
85
86
|
className="flex h-full flex-col border-l border-[var(--color-border-default)] bg-[var(--color-bg-panel)]"
|
|
@@ -143,6 +144,16 @@ export const CubeNodeEditorPanel = observer(
|
|
|
143
144
|
</button>
|
|
144
145
|
</div>
|
|
145
146
|
<div className="min-h-0 flex-1 overflow-auto p-2">
|
|
147
|
+
{warnings.map((warning) => (
|
|
148
|
+
<div
|
|
149
|
+
key={warning}
|
|
150
|
+
className="mb-2 flex gap-1 text-sm text-[var(--color-status-warn)]"
|
|
151
|
+
role="status"
|
|
152
|
+
>
|
|
153
|
+
<WarningIcon className="mt-0.5 shrink-0" aria-hidden={true} />
|
|
154
|
+
<span>{warning}</span>
|
|
155
|
+
</div>
|
|
156
|
+
))}
|
|
146
157
|
{inputProblems.length ? (
|
|
147
158
|
<div
|
|
148
159
|
className="text-base text-[var(--color-status-warn)]"
|
|
@@ -15,15 +15,28 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import {
|
|
18
|
+
Distinct,
|
|
19
|
+
Drop,
|
|
18
20
|
Filter,
|
|
19
21
|
Join,
|
|
22
|
+
Limit,
|
|
20
23
|
RelationalTableSource,
|
|
24
|
+
Rename,
|
|
25
|
+
Restrict,
|
|
21
26
|
type Schema,
|
|
27
|
+
Slice,
|
|
28
|
+
Sort,
|
|
22
29
|
} from '@finos/legend-cube';
|
|
23
30
|
import type { CubeEditorState } from '../../stores/CubeEditorState.js';
|
|
24
31
|
import type { CubeNodeDraft } from '../../stores/editors/CubeNodeDraft.js';
|
|
32
|
+
import { CubeDistinctEditor } from './CubeDistinctEditor.js';
|
|
25
33
|
import { CubeFilterEditor } from './CubeFilterEditor.js';
|
|
26
34
|
import { CubeJoinEditor } from './CubeJoinEditor.js';
|
|
35
|
+
import { CubeRenameEditor } from './CubeRenameEditor.js';
|
|
36
|
+
import { CubeRestrictEditor } from './CubeRestrictEditor.js';
|
|
37
|
+
import { CubeRowCountEditor } from './CubeRowCountEditor.js';
|
|
38
|
+
import { CubeSliceEditor } from './CubeSliceEditor.js';
|
|
39
|
+
import { CubeSortEditor } from './CubeSortEditor.js';
|
|
27
40
|
import { CubeSourceEditor } from './CubeSourceEditor.js';
|
|
28
41
|
|
|
29
42
|
/**
|
|
@@ -56,6 +69,13 @@ export const CUBE_NODE_EDITORS: ReadonlyMap<
|
|
|
56
69
|
React.FC<CubeNodeEditorProps>
|
|
57
70
|
> = new Map<string, React.FC<CubeNodeEditorProps>>([
|
|
58
71
|
[RelationalTableSource.TYPE, CubeSourceEditor],
|
|
72
|
+
[Sort.TYPE, CubeSortEditor],
|
|
59
73
|
[Filter.TYPE, CubeFilterEditor],
|
|
74
|
+
[Restrict.TYPE, CubeRestrictEditor],
|
|
75
|
+
[Rename.TYPE, CubeRenameEditor],
|
|
76
|
+
[Distinct.TYPE, CubeDistinctEditor],
|
|
60
77
|
[Join.TYPE, CubeJoinEditor],
|
|
78
|
+
[Drop.TYPE, CubeRowCountEditor],
|
|
79
|
+
[Limit.TYPE, CubeRowCountEditor],
|
|
80
|
+
[Slice.TYPE, CubeSliceEditor],
|
|
61
81
|
]);
|