@finos/legend-application-data-cube 0.3.5 → 0.3.7
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/lib/components/builder/LegendDataCubeCreator.d.ts.map +1 -1
- package/lib/components/builder/LegendDataCubeCreator.js +2 -5
- package/lib/components/builder/LegendDataCubeCreator.js.map +1 -1
- package/lib/components/builder/source/AdhocQueryDataCubeSourceBuilder.d.ts +8 -0
- package/lib/components/builder/source/AdhocQueryDataCubeSourceBuilder.d.ts.map +1 -1
- package/lib/components/builder/source/AdhocQueryDataCubeSourceBuilder.js +175 -2
- package/lib/components/builder/source/AdhocQueryDataCubeSourceBuilder.js.map +1 -1
- package/lib/index.css +1 -1
- package/lib/package.json +1 -1
- package/lib/stores/LegendDataCubeDataCubeEngine.d.ts +9 -4
- package/lib/stores/LegendDataCubeDataCubeEngine.d.ts.map +1 -1
- package/lib/stores/LegendDataCubeDataCubeEngine.js +34 -10
- package/lib/stores/LegendDataCubeDataCubeEngine.js.map +1 -1
- package/lib/stores/builder/LegendDataCubeCreatorState.d.ts.map +1 -1
- package/lib/stores/builder/LegendDataCubeCreatorState.js +1 -1
- package/lib/stores/builder/LegendDataCubeCreatorState.js.map +1 -1
- package/lib/stores/builder/source/AdHocCodeEditorState.d.ts +40 -0
- package/lib/stores/builder/source/AdHocCodeEditorState.d.ts.map +1 -0
- package/lib/stores/builder/source/AdHocCodeEditorState.js +147 -0
- package/lib/stores/builder/source/AdHocCodeEditorState.js.map +1 -0
- package/lib/stores/builder/source/AdhocQueryDataCubeSourceBuilderState.d.ts +38 -1
- package/lib/stores/builder/source/AdhocQueryDataCubeSourceBuilderState.d.ts.map +1 -1
- package/lib/stores/builder/source/AdhocQueryDataCubeSourceBuilderState.js +144 -4
- package/lib/stores/builder/source/AdhocQueryDataCubeSourceBuilderState.js.map +1 -1
- package/package.json +7 -7
- package/src/components/builder/LegendDataCubeCreator.tsx +2 -4
- package/src/components/builder/source/AdhocQueryDataCubeSourceBuilder.tsx +348 -4
- package/src/stores/LegendDataCubeDataCubeEngine.ts +38 -12
- package/src/stores/builder/LegendDataCubeCreatorState.tsx +1 -0
- package/src/stores/builder/source/AdHocCodeEditorState.tsx +189 -0
- package/src/stores/builder/source/AdhocQueryDataCubeSourceBuilderState.ts +221 -4
- package/tsconfig.json +1 -0
|
@@ -15,14 +15,358 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { observer } from 'mobx-react-lite';
|
|
18
|
+
import { CustomSelectorInput, WarningIcon } from '@finos/legend-art';
|
|
19
|
+
import { useEffect, useState } from 'react';
|
|
20
|
+
import { flowResult } from 'mobx';
|
|
21
|
+
import { type LegendDataCubeBuilderStore } from '../../../stores/builder/LegendDataCubeBuilderStore.js';
|
|
22
|
+
import { LATEST_VERSION_ALIAS } from '@finos/legend-server-depot';
|
|
23
|
+
import {
|
|
24
|
+
ActionState,
|
|
25
|
+
assertErrorThrown,
|
|
26
|
+
compareSemVerVersions,
|
|
27
|
+
guaranteeNonNullable,
|
|
28
|
+
} from '@finos/legend-shared';
|
|
29
|
+
import {
|
|
30
|
+
CORE_PURE_PATH,
|
|
31
|
+
V1_Mapping,
|
|
32
|
+
V1_PackageableRuntime,
|
|
33
|
+
V1_deserializePackageableElement,
|
|
34
|
+
} from '@finos/legend-graph';
|
|
35
|
+
import type { Entity } from '@finos/legend-storage';
|
|
18
36
|
import type { AdhocQueryDataCubeSourceBuilderState } from '../../../stores/builder/source/AdhocQueryDataCubeSourceBuilderState.js';
|
|
37
|
+
import {
|
|
38
|
+
buildProjectOption,
|
|
39
|
+
buildRuntimeOption,
|
|
40
|
+
buildVersionOption,
|
|
41
|
+
type ProjectOption,
|
|
42
|
+
type RuntimeOption,
|
|
43
|
+
type VersionOption,
|
|
44
|
+
} from './UserDefinedFunctionDataCubeSourceBuilder.js';
|
|
45
|
+
import { DataCubeCodeEditor } from '@finos/legend-data-cube';
|
|
46
|
+
|
|
47
|
+
export type MappingOption = {
|
|
48
|
+
label: string;
|
|
49
|
+
value: V1_Mapping;
|
|
50
|
+
};
|
|
51
|
+
export const buildMappingOption = (mapping: V1_Mapping): MappingOption => ({
|
|
52
|
+
label: mapping.name,
|
|
53
|
+
value: mapping,
|
|
54
|
+
});
|
|
19
55
|
|
|
20
56
|
export const AdhocQueryDataCubeSourceBuilder = observer(
|
|
21
|
-
(props: {
|
|
57
|
+
(props: {
|
|
58
|
+
sourceBuilder: AdhocQueryDataCubeSourceBuilderState;
|
|
59
|
+
store: LegendDataCubeBuilderStore;
|
|
60
|
+
}) => {
|
|
61
|
+
const { sourceBuilder, store } = props;
|
|
62
|
+
const [fetchSelectedProjectVersionsStatus] = useState(ActionState.create());
|
|
63
|
+
const [fetchSelectedVersionRuntimesStatus] = useState(ActionState.create());
|
|
64
|
+
const [fetchSelectedVersionMappingsStatus] = useState(ActionState.create());
|
|
65
|
+
|
|
66
|
+
//projects
|
|
67
|
+
const projectOptions = sourceBuilder.projects.map(buildProjectOption);
|
|
68
|
+
const selectedProjectOption = sourceBuilder.currentProject
|
|
69
|
+
? buildProjectOption(sourceBuilder.currentProject)
|
|
70
|
+
: null;
|
|
71
|
+
const projectSelectorPlaceholder = sourceBuilder.loadProjectsState
|
|
72
|
+
.isInProgress
|
|
73
|
+
? 'Loading projects'
|
|
74
|
+
: sourceBuilder.loadProjectsState.hasFailed
|
|
75
|
+
? 'Error fetching projects'
|
|
76
|
+
: sourceBuilder.projects.length
|
|
77
|
+
? 'Choose a project'
|
|
78
|
+
: 'No Projects available from depot';
|
|
79
|
+
const onProjectOptionChange = async (
|
|
80
|
+
option: ProjectOption | null,
|
|
81
|
+
): Promise<void> => {
|
|
82
|
+
if (option?.value !== sourceBuilder.currentProject) {
|
|
83
|
+
sourceBuilder.setCurrentProject(option?.value);
|
|
84
|
+
|
|
85
|
+
// cascade
|
|
86
|
+
sourceBuilder.setCurrentVersionId(undefined);
|
|
87
|
+
sourceBuilder.setCurrentProjectVersions([]);
|
|
88
|
+
sourceBuilder.setCurrentRuntime(undefined);
|
|
89
|
+
sourceBuilder.setRuntimes([]);
|
|
90
|
+
sourceBuilder.setModelPointer(undefined);
|
|
91
|
+
sourceBuilder.setMappings([]);
|
|
92
|
+
sourceBuilder.setCurrentMapping(undefined);
|
|
93
|
+
sourceBuilder.codeEditorState.clearQuery();
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
fetchSelectedProjectVersionsStatus.inProgress();
|
|
97
|
+
const versions = await store.depotServerClient.getVersions(
|
|
98
|
+
guaranteeNonNullable(option?.value.groupId),
|
|
99
|
+
guaranteeNonNullable(option?.value.artifactId),
|
|
100
|
+
true,
|
|
101
|
+
);
|
|
102
|
+
sourceBuilder.setCurrentProjectVersions(versions);
|
|
103
|
+
} catch (error) {
|
|
104
|
+
assertErrorThrown(error);
|
|
105
|
+
store.application.notificationService.notifyError(error);
|
|
106
|
+
} finally {
|
|
107
|
+
fetchSelectedProjectVersionsStatus.reset();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
//versions
|
|
113
|
+
const versionOptions = [
|
|
114
|
+
LATEST_VERSION_ALIAS,
|
|
115
|
+
...(sourceBuilder.currentProjectVersions ?? []),
|
|
116
|
+
]
|
|
117
|
+
.toSorted((v1, v2) => compareSemVerVersions(v2, v1))
|
|
118
|
+
.map(buildVersionOption);
|
|
119
|
+
const selectedVersionOption = sourceBuilder.currentVersionId
|
|
120
|
+
? buildVersionOption(sourceBuilder.currentVersionId)
|
|
121
|
+
: null;
|
|
122
|
+
const versionSelectorPlaceholder = !sourceBuilder.currentProject
|
|
123
|
+
? 'No project selected'
|
|
124
|
+
: 'Choose a version';
|
|
125
|
+
const onVersionChange = async (
|
|
126
|
+
option: VersionOption | null,
|
|
127
|
+
): Promise<void> => {
|
|
128
|
+
if (option?.value !== sourceBuilder.currentVersionId) {
|
|
129
|
+
sourceBuilder.setCurrentVersionId(option?.value);
|
|
130
|
+
|
|
131
|
+
//cascade
|
|
132
|
+
sourceBuilder.setCurrentRuntime(undefined);
|
|
133
|
+
sourceBuilder.setRuntimes([]);
|
|
134
|
+
sourceBuilder.setCurrentMapping(undefined);
|
|
135
|
+
sourceBuilder.setMappings([]);
|
|
136
|
+
sourceBuilder.codeEditorState.clearQuery();
|
|
137
|
+
|
|
138
|
+
//by this point, we should have all the necessary info to build the model context pointer
|
|
139
|
+
sourceBuilder.setModelPointer(
|
|
140
|
+
sourceBuilder.buildPureModelContextPointer(),
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
const plugins =
|
|
145
|
+
store.application.pluginManager.getPureProtocolProcessorPlugins();
|
|
146
|
+
|
|
147
|
+
//runtimes
|
|
148
|
+
fetchSelectedVersionRuntimesStatus.inProgress();
|
|
149
|
+
const allFetchedRuntimes =
|
|
150
|
+
await store.depotServerClient.getVersionEntities(
|
|
151
|
+
guaranteeNonNullable(sourceBuilder.currentProject?.groupId),
|
|
152
|
+
guaranteeNonNullable(sourceBuilder.currentProject?.artifactId),
|
|
153
|
+
guaranteeNonNullable(option?.value),
|
|
154
|
+
CORE_PURE_PATH.RUNTIME,
|
|
155
|
+
);
|
|
156
|
+
const fetchedRuntimeOptions: V1_PackageableRuntime[] = [];
|
|
157
|
+
allFetchedRuntimes.forEach((runtime) => {
|
|
158
|
+
const runtimeEntity = runtime.entity as Entity;
|
|
159
|
+
const currentRuntimeElement = V1_deserializePackageableElement(
|
|
160
|
+
runtimeEntity.content,
|
|
161
|
+
plugins,
|
|
162
|
+
);
|
|
163
|
+
if (currentRuntimeElement instanceof V1_PackageableRuntime) {
|
|
164
|
+
fetchedRuntimeOptions.push(currentRuntimeElement);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
sourceBuilder.setRuntimes(fetchedRuntimeOptions);
|
|
168
|
+
fetchSelectedVersionRuntimesStatus.reset();
|
|
169
|
+
|
|
170
|
+
//mappings
|
|
171
|
+
fetchSelectedVersionMappingsStatus.inProgress();
|
|
172
|
+
const allFetchedMappings =
|
|
173
|
+
await store.depotServerClient.getVersionEntities(
|
|
174
|
+
guaranteeNonNullable(sourceBuilder.currentProject?.groupId),
|
|
175
|
+
guaranteeNonNullable(sourceBuilder.currentProject?.artifactId),
|
|
176
|
+
guaranteeNonNullable(option?.value),
|
|
177
|
+
CORE_PURE_PATH.MAPPING,
|
|
178
|
+
);
|
|
179
|
+
const fetchedMappingOptions: V1_Mapping[] = [];
|
|
180
|
+
allFetchedMappings.forEach((mapping) => {
|
|
181
|
+
const mappingEntity = mapping.entity as Entity;
|
|
182
|
+
const currentMappingElement = V1_deserializePackageableElement(
|
|
183
|
+
mappingEntity.content,
|
|
184
|
+
plugins,
|
|
185
|
+
);
|
|
186
|
+
if (currentMappingElement instanceof V1_Mapping) {
|
|
187
|
+
fetchedMappingOptions.push(currentMappingElement);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
sourceBuilder.setMappings(fetchedMappingOptions);
|
|
191
|
+
fetchSelectedVersionMappingsStatus.reset();
|
|
192
|
+
} catch (error) {
|
|
193
|
+
assertErrorThrown(error);
|
|
194
|
+
store.application.notificationService.notifyError(error);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
//runtimes
|
|
200
|
+
const runtimeOptions = sourceBuilder.runtimes?.map(buildRuntimeOption);
|
|
201
|
+
const selectedRuntimeOption = sourceBuilder.currentRuntime
|
|
202
|
+
? buildRuntimeOption(sourceBuilder.currentRuntime)
|
|
203
|
+
: null;
|
|
204
|
+
const runtimeSelectorPlaceholder = !sourceBuilder.currentVersionId
|
|
205
|
+
? 'No version selected'
|
|
206
|
+
: 'Choose a runtime';
|
|
207
|
+
const onRuntimeChange = (option: RuntimeOption | null) => {
|
|
208
|
+
if (option === null) {
|
|
209
|
+
sourceBuilder.codeEditorState.clearQuery();
|
|
210
|
+
}
|
|
211
|
+
if (option?.value !== sourceBuilder.currentRuntime) {
|
|
212
|
+
sourceBuilder.setCurrentRuntime(option?.value);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
//mappings
|
|
217
|
+
const mappingOptions = sourceBuilder.mappings?.map(buildMappingOption);
|
|
218
|
+
const selectedMappingOption = sourceBuilder.currentMapping
|
|
219
|
+
? buildMappingOption(sourceBuilder.currentMapping)
|
|
220
|
+
: null;
|
|
221
|
+
const mappingSelectorPlaceholder = !sourceBuilder.currentVersionId
|
|
222
|
+
? 'No version selected'
|
|
223
|
+
: 'Choose a mapping';
|
|
224
|
+
const onMappingChange = (option: MappingOption | null) => {
|
|
225
|
+
if (option?.value !== sourceBuilder.currentMapping) {
|
|
226
|
+
sourceBuilder.setCurrentMapping(option?.value);
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
const allowQueryEditing = (): boolean => {
|
|
231
|
+
return Boolean(
|
|
232
|
+
sourceBuilder.currentProject &&
|
|
233
|
+
sourceBuilder.currentVersionId &&
|
|
234
|
+
sourceBuilder.currentRuntime,
|
|
235
|
+
);
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
useEffect(() => {
|
|
239
|
+
flowResult(sourceBuilder.loadProjects()).catch(
|
|
240
|
+
store.application.alertUnhandledError,
|
|
241
|
+
);
|
|
242
|
+
}, [sourceBuilder, store.application]);
|
|
243
|
+
|
|
22
244
|
return (
|
|
23
|
-
<div className="flex h-full w-full
|
|
24
|
-
<div className="flex
|
|
25
|
-
|
|
245
|
+
<div className="flex h-full w-full">
|
|
246
|
+
<div className="m-3 flex w-full flex-col items-stretch gap-2 text-neutral-500">
|
|
247
|
+
<div className="query-setup__wizard__group">
|
|
248
|
+
<div className="query-setup__wizard__group__title">Project</div>
|
|
249
|
+
<CustomSelectorInput
|
|
250
|
+
className="query-setup__wizard__selector"
|
|
251
|
+
options={projectOptions}
|
|
252
|
+
disabled={
|
|
253
|
+
sourceBuilder.loadProjectsState.isInProgress ||
|
|
254
|
+
!projectOptions.length
|
|
255
|
+
}
|
|
256
|
+
isLoading={sourceBuilder.loadProjectsState.isInProgress}
|
|
257
|
+
onChange={(option: ProjectOption | null) => {
|
|
258
|
+
onProjectOptionChange(option).catch(
|
|
259
|
+
store.application.alertUnhandledError,
|
|
260
|
+
);
|
|
261
|
+
}}
|
|
262
|
+
value={selectedProjectOption}
|
|
263
|
+
placeholder={projectSelectorPlaceholder}
|
|
264
|
+
isClearable={true}
|
|
265
|
+
escapeClearsValue={true}
|
|
266
|
+
/>
|
|
267
|
+
</div>
|
|
268
|
+
|
|
269
|
+
<div className="query-setup__wizard__group">
|
|
270
|
+
<div className="query-setup__wizard__group__title">Version</div>
|
|
271
|
+
<CustomSelectorInput
|
|
272
|
+
className="query-setup__wizard__selector"
|
|
273
|
+
options={versionOptions}
|
|
274
|
+
disabled={
|
|
275
|
+
!sourceBuilder.currentProject ||
|
|
276
|
+
fetchSelectedProjectVersionsStatus.isInProgress ||
|
|
277
|
+
!versionOptions.length
|
|
278
|
+
}
|
|
279
|
+
isLoading={fetchSelectedProjectVersionsStatus.isInProgress}
|
|
280
|
+
onChange={(option: VersionOption | null) => {
|
|
281
|
+
onVersionChange(option).catch(
|
|
282
|
+
store.application.alertUnhandledError,
|
|
283
|
+
);
|
|
284
|
+
}}
|
|
285
|
+
value={selectedVersionOption}
|
|
286
|
+
placeholder={versionSelectorPlaceholder}
|
|
287
|
+
isClearable={true}
|
|
288
|
+
escapeClearsValue={true}
|
|
289
|
+
/>
|
|
290
|
+
</div>
|
|
291
|
+
|
|
292
|
+
<div className="query-setup__wizard__group">
|
|
293
|
+
<div className="query-setup__wizard__group__title">Runtime</div>
|
|
294
|
+
<CustomSelectorInput
|
|
295
|
+
className="query-setup__wizard__selector"
|
|
296
|
+
options={runtimeOptions}
|
|
297
|
+
disabled={
|
|
298
|
+
!sourceBuilder.currentVersionId ||
|
|
299
|
+
fetchSelectedVersionRuntimesStatus.isInProgress ||
|
|
300
|
+
!versionOptions.length
|
|
301
|
+
}
|
|
302
|
+
isLoading={fetchSelectedVersionRuntimesStatus.isInProgress}
|
|
303
|
+
onChange={(option: RuntimeOption | null) => {
|
|
304
|
+
onRuntimeChange(option);
|
|
305
|
+
}}
|
|
306
|
+
value={selectedRuntimeOption}
|
|
307
|
+
placeholder={runtimeSelectorPlaceholder}
|
|
308
|
+
isClearable={true}
|
|
309
|
+
escapeClearsValue={true}
|
|
310
|
+
/>
|
|
311
|
+
</div>
|
|
312
|
+
|
|
313
|
+
<div className="query-setup__wizard__group">
|
|
314
|
+
<div className="query-setup__wizard__group__title">
|
|
315
|
+
Mapping (optional)
|
|
316
|
+
</div>
|
|
317
|
+
<CustomSelectorInput
|
|
318
|
+
className="query-setup__wizard__selector"
|
|
319
|
+
options={mappingOptions}
|
|
320
|
+
disabled={
|
|
321
|
+
!sourceBuilder.currentVersionId ||
|
|
322
|
+
fetchSelectedVersionMappingsStatus.isInProgress ||
|
|
323
|
+
!versionOptions.length
|
|
324
|
+
}
|
|
325
|
+
isLoading={fetchSelectedVersionMappingsStatus.isInProgress}
|
|
326
|
+
onChange={(option: MappingOption | null) => {
|
|
327
|
+
onMappingChange(option);
|
|
328
|
+
}}
|
|
329
|
+
value={selectedMappingOption}
|
|
330
|
+
placeholder={mappingSelectorPlaceholder}
|
|
331
|
+
isClearable={true}
|
|
332
|
+
escapeClearsValue={true}
|
|
333
|
+
/>
|
|
334
|
+
</div>
|
|
335
|
+
|
|
336
|
+
<div className="query-setup__wizard__group">
|
|
337
|
+
<div className="query-setup__wizard__group__title">Query</div>
|
|
338
|
+
|
|
339
|
+
<div
|
|
340
|
+
className="mt-2 h-40 w-full"
|
|
341
|
+
style={{
|
|
342
|
+
border: '2px solid #e5e7eb',
|
|
343
|
+
padding: '5px',
|
|
344
|
+
borderRadius: '5px',
|
|
345
|
+
position: 'relative',
|
|
346
|
+
backgroundColor: allowQueryEditing() ? '#ffffff' : '#f2f2f2',
|
|
347
|
+
}}
|
|
348
|
+
>
|
|
349
|
+
{allowQueryEditing() ? (
|
|
350
|
+
<DataCubeCodeEditor state={sourceBuilder.codeEditorState} />
|
|
351
|
+
) : (
|
|
352
|
+
<div>No runtime selected </div>
|
|
353
|
+
)}
|
|
354
|
+
</div>
|
|
355
|
+
{sourceBuilder.codeEditorState.compilationError ? (
|
|
356
|
+
<div
|
|
357
|
+
className="flex h-full w-full flex-shrink-0 text-sm"
|
|
358
|
+
style={{ color: 'red' }}
|
|
359
|
+
>
|
|
360
|
+
<WarningIcon />
|
|
361
|
+
Compilation Error: Ensure TDS is returned and all class names
|
|
362
|
+
are correct
|
|
363
|
+
</div>
|
|
364
|
+
) : sourceBuilder.queryCompileState.isInProgress ? (
|
|
365
|
+
<div className="flex h-full w-full flex-shrink-0 text-sm">
|
|
366
|
+
Compiling query...
|
|
367
|
+
</div>
|
|
368
|
+
) : null}
|
|
369
|
+
</div>
|
|
26
370
|
</div>
|
|
27
371
|
</div>
|
|
28
372
|
);
|
|
@@ -88,7 +88,6 @@ import {
|
|
|
88
88
|
import {
|
|
89
89
|
_elementPtr,
|
|
90
90
|
DataCubeEngine,
|
|
91
|
-
type DataCubeSource,
|
|
92
91
|
type CompletionItem,
|
|
93
92
|
_function,
|
|
94
93
|
DataCubeFunction,
|
|
@@ -103,6 +102,7 @@ import {
|
|
|
103
102
|
DataCubeExecutionError,
|
|
104
103
|
RawUserDefinedFunctionDataCubeSource,
|
|
105
104
|
ADHOC_FUNCTION_DATA_CUBE_SOURCE_TYPE,
|
|
105
|
+
type DataCubeSource,
|
|
106
106
|
UserDefinedFunctionDataCubeSource,
|
|
107
107
|
DataCubeQueryFilterOperator,
|
|
108
108
|
} from '@finos/legend-data-cube';
|
|
@@ -177,6 +177,9 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
|
|
|
177
177
|
const rawSource =
|
|
178
178
|
RawAdhocQueryDataCubeSource.serialization.fromJson(value);
|
|
179
179
|
const source = new AdhocQueryDataCubeSource();
|
|
180
|
+
if (rawSource.mapping) {
|
|
181
|
+
source.mapping = rawSource.mapping;
|
|
182
|
+
}
|
|
180
183
|
source.runtime = rawSource.runtime;
|
|
181
184
|
source.model = rawSource.model;
|
|
182
185
|
source.query = await this.parseValueSpecification(
|
|
@@ -406,7 +409,10 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
|
|
|
406
409
|
),
|
|
407
410
|
),
|
|
408
411
|
);
|
|
409
|
-
|
|
412
|
+
// If the lambda has multiple expressions, the source query should only be the final
|
|
413
|
+
// expression of the lambda. All previous expressions should be left untouched and will
|
|
414
|
+
// be prepended to the transformed query when it is executed.
|
|
415
|
+
source.query = at(source.lambda.body, source.lambda.body.length - 1);
|
|
410
416
|
try {
|
|
411
417
|
source.columns = (
|
|
412
418
|
await this._getLambdaRelationType(
|
|
@@ -527,35 +533,43 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
|
|
|
527
533
|
override async getQueryTypeahead(
|
|
528
534
|
code: string,
|
|
529
535
|
baseQuery: V1_Lambda,
|
|
530
|
-
|
|
536
|
+
context: DataCubeSource | PlainObject,
|
|
531
537
|
) {
|
|
532
538
|
const baseQueryCode = await this.getValueSpecificationCode(baseQuery);
|
|
533
539
|
let codeBlock = baseQueryCode + code;
|
|
534
540
|
codeBlock = codeBlock.startsWith(LAMBDA_PIPE)
|
|
535
541
|
? codeBlock.substring(LAMBDA_PIPE.length)
|
|
536
542
|
: codeBlock;
|
|
537
|
-
if (
|
|
543
|
+
if (context instanceof AdhocQueryDataCubeSource) {
|
|
538
544
|
return (
|
|
539
545
|
await this._engineServerClient.completeCode({
|
|
540
546
|
codeBlock,
|
|
541
|
-
model:
|
|
547
|
+
model: context.model,
|
|
542
548
|
})
|
|
543
549
|
).completions as CompletionItem[];
|
|
544
|
-
} else if (
|
|
550
|
+
} else if (context instanceof UserDefinedFunctionDataCubeSource) {
|
|
545
551
|
return (
|
|
546
552
|
await this._engineServerClient.completeCode({
|
|
547
553
|
codeBlock,
|
|
548
|
-
model:
|
|
554
|
+
model: context.model,
|
|
549
555
|
})
|
|
550
556
|
).completions as CompletionItem[];
|
|
551
|
-
} else if (
|
|
557
|
+
} else if (context instanceof LegendQueryDataCubeSource) {
|
|
558
|
+
return (
|
|
559
|
+
await this._engineServerClient.completeCode({
|
|
560
|
+
codeBlock,
|
|
561
|
+
model: context.model,
|
|
562
|
+
})
|
|
563
|
+
).completions as CompletionItem[];
|
|
564
|
+
} else if (Object.getPrototypeOf(context) === Object.prototype) {
|
|
552
565
|
return (
|
|
553
566
|
await this._engineServerClient.completeCode({
|
|
554
567
|
codeBlock,
|
|
555
|
-
model:
|
|
568
|
+
model: context,
|
|
556
569
|
})
|
|
557
570
|
).completions as CompletionItem[];
|
|
558
571
|
}
|
|
572
|
+
|
|
559
573
|
throw new UnsupportedOperationError(
|
|
560
574
|
`Can't get code completion for lambda with unsupported source`,
|
|
561
575
|
);
|
|
@@ -642,7 +656,16 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
|
|
|
642
656
|
result = await this._runQuery(query, source.model, undefined, options);
|
|
643
657
|
} else if (source instanceof LegendQueryDataCubeSource) {
|
|
644
658
|
query.parameters = source.lambda.parameters;
|
|
645
|
-
|
|
659
|
+
// If the source lambda has multiple expressions, we should prepend all but the
|
|
660
|
+
// last expression to the transformed query body (which came from the final
|
|
661
|
+
// expression of the source lambda).
|
|
662
|
+
query.body = [
|
|
663
|
+
...source.letParameterValueSpec,
|
|
664
|
+
...(source.lambda.body.length > 1
|
|
665
|
+
? source.lambda.body.slice(0, -1)
|
|
666
|
+
: []),
|
|
667
|
+
...query.body,
|
|
668
|
+
];
|
|
646
669
|
result = await this._runQuery(
|
|
647
670
|
query,
|
|
648
671
|
source.model,
|
|
@@ -741,7 +764,10 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
|
|
|
741
764
|
if (source instanceof AdhocQueryDataCubeSource) {
|
|
742
765
|
return _function(
|
|
743
766
|
DataCubeFunction.FROM,
|
|
744
|
-
[
|
|
767
|
+
[
|
|
768
|
+
source.mapping ? _elementPtr(source.mapping) : undefined,
|
|
769
|
+
_elementPtr(source.runtime),
|
|
770
|
+
].filter(isNonNullable),
|
|
745
771
|
);
|
|
746
772
|
} else if (source instanceof UserDefinedFunctionDataCubeSource) {
|
|
747
773
|
return _function(
|
|
@@ -879,7 +905,7 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
|
|
|
879
905
|
);
|
|
880
906
|
}
|
|
881
907
|
|
|
882
|
-
|
|
908
|
+
async _getLambdaRelationType(
|
|
883
909
|
lambda: PlainObject<V1_Lambda>,
|
|
884
910
|
model: PlainObject<V1_PureModelContext>,
|
|
885
911
|
) {
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2020-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 { clearMarkers, setErrorMarkers } from '@finos/legend-code-editor';
|
|
18
|
+
import {
|
|
19
|
+
DataCubeCodeEditorState,
|
|
20
|
+
type DataCubeEngine,
|
|
21
|
+
} from '@finos/legend-data-cube';
|
|
22
|
+
import { EngineError, type V1_Lambda } from '@finos/legend-graph';
|
|
23
|
+
import {
|
|
24
|
+
uuid,
|
|
25
|
+
ActionState,
|
|
26
|
+
type PlainObject,
|
|
27
|
+
assertErrorThrown,
|
|
28
|
+
} from '@finos/legend-shared';
|
|
29
|
+
import { action, makeObservable, observable, runInAction } from 'mobx';
|
|
30
|
+
|
|
31
|
+
export class AdHocCodeEditorState extends DataCubeCodeEditorState {
|
|
32
|
+
protected override readonly uuid = uuid();
|
|
33
|
+
codeSuffix: string;
|
|
34
|
+
|
|
35
|
+
readonly validationState = ActionState.create();
|
|
36
|
+
compilationError?: Error | undefined;
|
|
37
|
+
override currentlyEditing = false;
|
|
38
|
+
|
|
39
|
+
alertHandler: (error: Error) => void;
|
|
40
|
+
override model: PlainObject | undefined;
|
|
41
|
+
compileQueryCheck: () => Promise<boolean | undefined>;
|
|
42
|
+
queryLambda: () => V1_Lambda;
|
|
43
|
+
|
|
44
|
+
constructor(
|
|
45
|
+
alertHandler: (error: Error) => void,
|
|
46
|
+
model: PlainObject | undefined,
|
|
47
|
+
compileQueryCheck: () => Promise<boolean | undefined>,
|
|
48
|
+
queryLambda: () => V1_Lambda,
|
|
49
|
+
engine: DataCubeEngine,
|
|
50
|
+
) {
|
|
51
|
+
super(engine);
|
|
52
|
+
makeObservable(this, {
|
|
53
|
+
code: observable,
|
|
54
|
+
|
|
55
|
+
editor: observable.ref,
|
|
56
|
+
setEditor: action,
|
|
57
|
+
|
|
58
|
+
codeError: observable.ref,
|
|
59
|
+
compilationError: observable,
|
|
60
|
+
showError: action,
|
|
61
|
+
clearError: action,
|
|
62
|
+
|
|
63
|
+
returnType: observable,
|
|
64
|
+
setReturnType: action,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
this.codePrefix = '';
|
|
68
|
+
this.codeSuffix = '';
|
|
69
|
+
|
|
70
|
+
this.alertHandler = alertHandler;
|
|
71
|
+
this.model = model;
|
|
72
|
+
this.compileQueryCheck = compileQueryCheck;
|
|
73
|
+
this.queryLambda = queryLambda;
|
|
74
|
+
this.engine = engine;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
compileFunction = async (): Promise<boolean | undefined> => {
|
|
78
|
+
if (this.code.length !== 0) {
|
|
79
|
+
try {
|
|
80
|
+
this.currentlyEditing = false;
|
|
81
|
+
return await this.compileQueryCheck();
|
|
82
|
+
} catch (error) {
|
|
83
|
+
assertErrorThrown(error);
|
|
84
|
+
if (error instanceof EngineError) {
|
|
85
|
+
this.validationState.fail();
|
|
86
|
+
// correct the source information since we added prefix to the code
|
|
87
|
+
// and reveal error in the editor
|
|
88
|
+
if (error.sourceInformation) {
|
|
89
|
+
error.sourceInformation.startColumn -=
|
|
90
|
+
error.sourceInformation.startLine === 1
|
|
91
|
+
? this.codePrefix.length
|
|
92
|
+
: 0;
|
|
93
|
+
error.sourceInformation.endColumn -=
|
|
94
|
+
error.sourceInformation.endLine === 1
|
|
95
|
+
? this.codePrefix.length
|
|
96
|
+
: 0;
|
|
97
|
+
const fullRange = this.editorModel.getFullModelRange();
|
|
98
|
+
if (
|
|
99
|
+
error.sourceInformation.startLine < 1 ||
|
|
100
|
+
(error.sourceInformation.startLine === 1 &&
|
|
101
|
+
error.sourceInformation.startColumn < 1) ||
|
|
102
|
+
error.sourceInformation.endLine > fullRange.endLineNumber ||
|
|
103
|
+
(error.sourceInformation.endLine === fullRange.endLineNumber &&
|
|
104
|
+
error.sourceInformation.endColumn > fullRange.endColumn)
|
|
105
|
+
) {
|
|
106
|
+
error.sourceInformation.startColumn = fullRange.startColumn;
|
|
107
|
+
error.sourceInformation.startLine = fullRange.startLineNumber;
|
|
108
|
+
error.sourceInformation.endColumn = fullRange.endColumn;
|
|
109
|
+
error.sourceInformation.endLine = fullRange.endLineNumber;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
this.showError(error);
|
|
113
|
+
return undefined;
|
|
114
|
+
} else if (error instanceof Error) {
|
|
115
|
+
this.setCompilationError(error);
|
|
116
|
+
}
|
|
117
|
+
this.alertHandler(error);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return undefined;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
override get hasErrors(): boolean {
|
|
124
|
+
return Boolean(this.codeError ?? this.compilationError);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
isValid(): boolean {
|
|
128
|
+
return Boolean(
|
|
129
|
+
!this.hasErrors &&
|
|
130
|
+
this.code.length !== 0 &&
|
|
131
|
+
!this.currentlyEditing &&
|
|
132
|
+
!this.validationState.isInProgress,
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
setCompilationError(val: Error | undefined) {
|
|
137
|
+
runInAction(() => {
|
|
138
|
+
this.compilationError = val;
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
setModel(val: PlainObject | undefined) {
|
|
143
|
+
this.model = val;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
clearQuery() {
|
|
147
|
+
this.clearError();
|
|
148
|
+
this.editor?.getModel()?.setValue('');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
override clearError() {
|
|
152
|
+
this.codeError = undefined;
|
|
153
|
+
this.compilationError = undefined;
|
|
154
|
+
clearMarkers(this.uuid);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
showError(error: EngineError) {
|
|
158
|
+
this.codeError = error;
|
|
159
|
+
if (error.sourceInformation) {
|
|
160
|
+
setErrorMarkers(
|
|
161
|
+
this.editorModel,
|
|
162
|
+
[
|
|
163
|
+
{
|
|
164
|
+
message: error.message,
|
|
165
|
+
startLineNumber: error.sourceInformation.startLine,
|
|
166
|
+
startColumn: error.sourceInformation.startColumn,
|
|
167
|
+
endLineNumber: error.sourceInformation.endLine,
|
|
168
|
+
endColumn: error.sourceInformation.endColumn,
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
this.uuid,
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async getReturnType() {
|
|
177
|
+
this.validationState.inProgress();
|
|
178
|
+
|
|
179
|
+
// properly reset the error state before revalidating
|
|
180
|
+
this.clearError();
|
|
181
|
+
this.setReturnType(undefined);
|
|
182
|
+
|
|
183
|
+
await this.compileFunction();
|
|
184
|
+
|
|
185
|
+
this.validationState.complete();
|
|
186
|
+
|
|
187
|
+
return undefined;
|
|
188
|
+
}
|
|
189
|
+
}
|