@finos/legend-application-data-cube 0.3.14 → 0.3.16

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.
Files changed (97) hide show
  1. package/lib/application/Core_LegendDataCube_LegendApplicationPlugin.d.ts +7 -1
  2. package/lib/application/Core_LegendDataCube_LegendApplicationPlugin.d.ts.map +1 -1
  3. package/lib/application/Core_LegendDataCube_LegendApplicationPlugin.js +10 -1
  4. package/lib/application/Core_LegendDataCube_LegendApplicationPlugin.js.map +1 -1
  5. package/lib/application/LegendDataCube.d.ts.map +1 -1
  6. package/lib/application/LegendDataCube.js +2 -0
  7. package/lib/application/LegendDataCube.js.map +1 -1
  8. package/lib/application/LegendDataCubeApplicationPlugin.d.ts +9 -0
  9. package/lib/application/LegendDataCubeApplicationPlugin.d.ts.map +1 -1
  10. package/lib/application/LegendDataCubeApplicationPlugin.js.map +1 -1
  11. package/lib/components/__test-utils__/LegendDataCubeStoreTestUtils.d.ts.map +1 -1
  12. package/lib/components/__test-utils__/LegendDataCubeStoreTestUtils.js +2 -0
  13. package/lib/components/__test-utils__/LegendDataCubeStoreTestUtils.js.map +1 -1
  14. package/lib/components/builder/LegendDataCubeBuilder.d.ts.map +1 -1
  15. package/lib/components/builder/LegendDataCubeBuilder.js +10 -2
  16. package/lib/components/builder/LegendDataCubeBuilder.js.map +1 -1
  17. package/lib/components/builder/LegendDataCubeCreator.js +4 -4
  18. package/lib/components/builder/LegendDataCubeCreator.js.map +1 -1
  19. package/lib/components/builder/LegendDataCubeSourceViewer.d.ts.map +1 -1
  20. package/lib/components/builder/LegendDataCubeSourceViewer.js +134 -18
  21. package/lib/components/builder/LegendDataCubeSourceViewer.js.map +1 -1
  22. package/lib/components/builder/source/{AdhocQueryDataCubeSourceBuilder.d.ts → FreeformTDSExpressionDataCubeSourceBuilder.d.ts} +4 -4
  23. package/lib/components/builder/source/FreeformTDSExpressionDataCubeSourceBuilder.d.ts.map +1 -0
  24. package/lib/components/builder/source/{AdhocQueryDataCubeSourceBuilder.js → FreeformTDSExpressionDataCubeSourceBuilder.js} +2 -2
  25. package/lib/components/builder/source/FreeformTDSExpressionDataCubeSourceBuilder.js.map +1 -0
  26. package/lib/components/builder/source/LegendQueryDataCubeSourceBuilder.d.ts.map +1 -1
  27. package/lib/components/builder/source/LegendQueryDataCubeSourceBuilder.js +30 -4
  28. package/lib/components/builder/source/LegendQueryDataCubeSourceBuilder.js.map +1 -1
  29. package/lib/components/builder/source/LegendQueryDataCubeSourceBuilder_DataCubeApplicationPlugin.d.ts +24 -0
  30. package/lib/components/builder/source/LegendQueryDataCubeSourceBuilder_DataCubeApplicationPlugin.d.ts.map +1 -0
  31. package/lib/components/builder/source/LegendQueryDataCubeSourceBuilder_DataCubeApplicationPlugin.js +67 -0
  32. package/lib/components/builder/source/LegendQueryDataCubeSourceBuilder_DataCubeApplicationPlugin.js.map +1 -0
  33. package/lib/index.css +2 -2
  34. package/lib/index.css.map +1 -1
  35. package/lib/package.json +3 -2
  36. package/lib/stores/LegendDataCubeDataCubeEngine.d.ts +6 -1
  37. package/lib/stores/LegendDataCubeDataCubeEngine.d.ts.map +1 -1
  38. package/lib/stores/LegendDataCubeDataCubeEngine.js +81 -50
  39. package/lib/stores/LegendDataCubeDataCubeEngine.js.map +1 -1
  40. package/lib/stores/builder/LegendDataCubeBuilderStore.d.ts +2 -1
  41. package/lib/stores/builder/LegendDataCubeBuilderStore.d.ts.map +1 -1
  42. package/lib/stores/builder/LegendDataCubeBuilderStore.js +4 -2
  43. package/lib/stores/builder/LegendDataCubeBuilderStore.js.map +1 -1
  44. package/lib/stores/builder/LegendDataCubeCreatorState.d.ts.map +1 -1
  45. package/lib/stores/builder/LegendDataCubeCreatorState.js +5 -5
  46. package/lib/stores/builder/LegendDataCubeCreatorState.js.map +1 -1
  47. package/lib/stores/builder/source/{AdHocCodeEditorState.d.ts → FreeformExpressionCodeEditorState.d.ts} +2 -2
  48. package/lib/stores/builder/source/FreeformExpressionCodeEditorState.d.ts.map +1 -0
  49. package/lib/stores/builder/source/{AdHocCodeEditorState.js → FreeformExpressionCodeEditorState.js} +2 -2
  50. package/lib/stores/builder/source/FreeformExpressionCodeEditorState.js.map +1 -0
  51. package/lib/stores/builder/source/{AdhocQueryDataCubeSourceBuilderState.d.ts → FreeformTDSExpressionDataCubeSourceBuilderState.d.ts} +4 -4
  52. package/lib/stores/builder/source/FreeformTDSExpressionDataCubeSourceBuilderState.d.ts.map +1 -0
  53. package/lib/stores/builder/source/{AdhocQueryDataCubeSourceBuilderState.js → FreeformTDSExpressionDataCubeSourceBuilderState.js} +8 -8
  54. package/lib/stores/builder/source/FreeformTDSExpressionDataCubeSourceBuilderState.js.map +1 -0
  55. package/lib/stores/builder/source/LegendDataCubeSourceBuilderState.d.ts +1 -1
  56. package/lib/stores/builder/source/LegendDataCubeSourceBuilderState.d.ts.map +1 -1
  57. package/lib/stores/builder/source/LegendDataCubeSourceBuilderState.js +1 -1
  58. package/lib/stores/builder/source/LegendDataCubeSourceBuilderState.js.map +1 -1
  59. package/lib/stores/builder/source/LegendQueryDataCubeSourceBuilderState.d.ts +21 -3
  60. package/lib/stores/builder/source/LegendQueryDataCubeSourceBuilderState.d.ts.map +1 -1
  61. package/lib/stores/builder/source/LegendQueryDataCubeSourceBuilderState.js +73 -7
  62. package/lib/stores/builder/source/LegendQueryDataCubeSourceBuilderState.js.map +1 -1
  63. package/lib/stores/builder/source/LegendQueryDataCubeSourceBuilderStateHelper.d.ts +20 -0
  64. package/lib/stores/builder/source/LegendQueryDataCubeSourceBuilderStateHelper.d.ts.map +1 -0
  65. package/lib/stores/builder/source/LegendQueryDataCubeSourceBuilderStateHelper.js +61 -0
  66. package/lib/stores/builder/source/LegendQueryDataCubeSourceBuilderStateHelper.js.map +1 -0
  67. package/lib/stores/model/LegendQueryDataCubeSource.d.ts +6 -3
  68. package/lib/stores/model/LegendQueryDataCubeSource.d.ts.map +1 -1
  69. package/lib/stores/model/LegendQueryDataCubeSource.js +3 -2
  70. package/lib/stores/model/LegendQueryDataCubeSource.js.map +1 -1
  71. package/package.json +12 -11
  72. package/src/application/Core_LegendDataCube_LegendApplicationPlugin.ts +21 -2
  73. package/src/application/LegendDataCube.tsx +2 -0
  74. package/src/application/LegendDataCubeApplicationPlugin.ts +15 -0
  75. package/src/components/__test-utils__/LegendDataCubeStoreTestUtils.tsx +2 -0
  76. package/src/components/builder/LegendDataCubeBuilder.tsx +34 -20
  77. package/src/components/builder/LegendDataCubeCreator.tsx +5 -5
  78. package/src/components/builder/LegendDataCubeSourceViewer.tsx +426 -78
  79. package/src/components/builder/source/{AdhocQueryDataCubeSourceBuilder.tsx → FreeformTDSExpressionDataCubeSourceBuilder.tsx} +3 -3
  80. package/src/components/builder/source/LegendQueryDataCubeSourceBuilder.tsx +92 -1
  81. package/src/components/builder/source/LegendQueryDataCubeSourceBuilder_DataCubeApplicationPlugin.tsx +128 -0
  82. package/src/stores/LegendDataCubeDataCubeEngine.ts +138 -75
  83. package/src/stores/builder/LegendDataCubeBuilderStore.tsx +4 -1
  84. package/src/stores/builder/LegendDataCubeCreatorState.tsx +7 -4
  85. package/src/stores/builder/source/{AdHocCodeEditorState.tsx → FreeformExpressionCodeEditorState.tsx} +1 -1
  86. package/src/stores/builder/source/{AdhocQueryDataCubeSourceBuilderState.ts → FreeformTDSExpressionDataCubeSourceBuilderState.ts} +8 -8
  87. package/src/stores/builder/source/LegendDataCubeSourceBuilderState.ts +1 -1
  88. package/src/stores/builder/source/LegendQueryDataCubeSourceBuilderState.ts +161 -8
  89. package/src/stores/builder/source/LegendQueryDataCubeSourceBuilderStateHelper.ts +121 -0
  90. package/src/stores/model/LegendQueryDataCubeSource.ts +8 -4
  91. package/tsconfig.json +5 -3
  92. package/lib/components/builder/source/AdhocQueryDataCubeSourceBuilder.d.ts.map +0 -1
  93. package/lib/components/builder/source/AdhocQueryDataCubeSourceBuilder.js.map +0 -1
  94. package/lib/stores/builder/source/AdHocCodeEditorState.d.ts.map +0 -1
  95. package/lib/stores/builder/source/AdHocCodeEditorState.js.map +0 -1
  96. package/lib/stores/builder/source/AdhocQueryDataCubeSourceBuilderState.d.ts.map +0 -1
  97. package/lib/stores/builder/source/AdhocQueryDataCubeSourceBuilderState.js.map +0 -1
@@ -16,7 +16,10 @@
16
16
 
17
17
  import { observer } from 'mobx-react-lite';
18
18
  import { useLegendDataCubeBuilderStore } from './LegendDataCubeBuilderStoreProvider.js';
19
- import { LegendQueryDataCubeSource } from '../../stores/model/LegendQueryDataCubeSource.js';
19
+ import {
20
+ LegendQueryDataCubeSource,
21
+ RawLegendQueryDataCubeSource,
22
+ } from '../../stores/model/LegendQueryDataCubeSource.js';
20
23
  import { useLegendDataCubeApplicationStore } from '../LegendDataCubeFrameworkProvider.js';
21
24
  import {
22
25
  EXTERNAL_APPLICATION_NAVIGATION__generateQueryViewUrl,
@@ -24,20 +27,437 @@ import {
24
27
  EXTERNAL_APPLICATION_NAVIGATION__generateStudioViewUrl,
25
28
  } from '../../__lib__/LegendDataCubeNavigation.js';
26
29
  import { DataCubeIcon } from '@finos/legend-art';
27
- import { UserDefinedFunctionDataCubeSource } from '@finos/legend-data-cube';
28
- import { useCallback, useEffect, useState } from 'react';
30
+ import {
31
+ _defaultPrimitiveTypeValue,
32
+ _elementPtr,
33
+ _primitiveValue,
34
+ _property,
35
+ AlertType,
36
+ FormAlert,
37
+ FormButton,
38
+ isPrimitiveType,
39
+ UserDefinedFunctionDataCubeSource,
40
+ } from '@finos/legend-data-cube';
41
+ import { useCallback, useEffect, useMemo, useState } from 'react';
29
42
  import {
30
43
  type DepotServerClient,
31
44
  DepotScope,
32
45
  StoreProjectData,
33
46
  } from '@finos/legend-server-depot';
34
- import { returnUndefOnError } from '@finos/legend-shared';
35
47
  import {
48
+ deepClone,
49
+ guaranteeIsString,
50
+ guaranteeNonNullable,
51
+ guaranteeType,
52
+ isNonNullable,
53
+ returnUndefOnError,
54
+ } from '@finos/legend-shared';
55
+ import {
56
+ type LightQuery,
57
+ type PureProtocolProcessorPlugin,
58
+ type V1_Enumeration,
59
+ type V1_PureModelContext,
60
+ type V1_PureModelContextData,
61
+ type V1_Variable,
62
+ PRIMITIVE_TYPE,
63
+ V1_CORE_SYSTEM_MODELS,
36
64
  V1_deserializePureModelContext,
65
+ V1_deserializePureModelContextData,
37
66
  V1_LegendSDLC,
67
+ V1_observe_ValueSpecification,
68
+ V1_PackageableType,
38
69
  V1_PureModelContextPointer,
39
- type V1_PureModelContext,
70
+ V1_serializeValueSpecification,
71
+ V1_ValueSpecification,
40
72
  } from '@finos/legend-graph';
73
+ import {
74
+ isValidV1_ValueSpecification,
75
+ V1_BasicValueSpecificationEditor,
76
+ } from '@finos/legend-query-builder';
77
+ import { LegendDataCubeBuilderState } from '../../stores/builder/LegendDataCubeBuilderStore.js';
78
+ import {
79
+ fetchV1Enumeration,
80
+ isVariableEnumerationType,
81
+ } from '../../stores/builder/source/LegendQueryDataCubeSourceBuilderStateHelper.js';
82
+
83
+ const handleFetchEnumerations = async (
84
+ enumerationVariables: V1_Variable[],
85
+ query: LightQuery,
86
+ systemModel: V1_PureModelContextData,
87
+ depotServerClient: DepotServerClient,
88
+ plugins: PureProtocolProcessorPlugin[],
89
+ updateCallback: (val: { [name: string]: V1_Enumeration }) => void,
90
+ ) => {
91
+ await Promise.all(
92
+ enumerationVariables.map(async (variable) => {
93
+ const packageableType = guaranteeType(
94
+ variable.genericType?.rawType,
95
+ V1_PackageableType,
96
+ 'Can only edit parameters with packageable type',
97
+ );
98
+ const enumeration = await fetchV1Enumeration(
99
+ packageableType.fullPath,
100
+ query,
101
+ systemModel,
102
+ depotServerClient,
103
+ plugins,
104
+ );
105
+ return { variable, enumeration };
106
+ }),
107
+ ).then((response) => {
108
+ const result = response.reduce(
109
+ (acc, { variable, enumeration }) => {
110
+ acc[variable.name] = enumeration;
111
+ return acc;
112
+ },
113
+ {} as { [name: string]: V1_Enumeration },
114
+ );
115
+ updateCallback(result);
116
+ });
117
+ };
118
+
119
+ const LegendQuerySourceViewer = observer(
120
+ (props: { source: LegendQueryDataCubeSource }) => {
121
+ const { source } = props;
122
+ const store = useLegendDataCubeBuilderStore();
123
+ const application = useLegendDataCubeApplicationStore();
124
+
125
+ const systemModel = useMemo(
126
+ () => V1_deserializePureModelContextData(V1_CORE_SYSTEM_MODELS),
127
+ [],
128
+ );
129
+ const [params, setParams] = useState<
130
+ { variable: V1_Variable; valueSpec: V1_ValueSpecification }[]
131
+ >(source.parameterValues.map(deepClone));
132
+ const [enumerations, setEnumerations] = useState<{
133
+ [name: string]: V1_Enumeration;
134
+ }>({});
135
+ const [isUpdatingParameters, setIsUpdatingParameters] = useState(false);
136
+
137
+ // If caching is enabled on the grid, we disable editing the query parameters.
138
+ // User will need to disable caching to be able to edit parameters.
139
+ const isCachingEnabled =
140
+ store.builder?.dataCube?.isCachingEnabled() ?? false;
141
+
142
+ const _handleFetchEnumerations = useCallback(async () => {
143
+ const enumerationVariables = source.parameterValues
144
+ .map((parameter) =>
145
+ source.lambda.parameters.find(
146
+ (_param) => parameter.variable.name === _param.name,
147
+ ),
148
+ )
149
+ .filter(isNonNullable)
150
+ .filter(isVariableEnumerationType);
151
+ await handleFetchEnumerations(
152
+ enumerationVariables,
153
+ source.info,
154
+ systemModel,
155
+ store.depotServerClient,
156
+ application.pluginManager.getPureProtocolProcessorPlugins(),
157
+ setEnumerations,
158
+ );
159
+ }, [
160
+ application.pluginManager,
161
+ source,
162
+ store.depotServerClient,
163
+ systemModel,
164
+ ]);
165
+
166
+ useEffect(() => {
167
+ // eslint-disable-next-line no-void
168
+ void void _handleFetchEnumerations();
169
+ }, [_handleFetchEnumerations]);
170
+
171
+ const updateParameterValue = (
172
+ name: string,
173
+ valueSpec: V1_ValueSpecification,
174
+ ): void => {
175
+ setParams(
176
+ params.map((param) => {
177
+ if (param.variable.name === name) {
178
+ param.valueSpec = valueSpec;
179
+ }
180
+ return param;
181
+ }),
182
+ );
183
+ };
184
+
185
+ const updateBuilderWithNewSpecification = async () => {
186
+ // Verify that all params are valid
187
+ if (
188
+ params.some(
189
+ (param) =>
190
+ !isValidV1_ValueSpecification(
191
+ param.valueSpec,
192
+ param.variable.multiplicity,
193
+ ),
194
+ )
195
+ ) {
196
+ return;
197
+ }
198
+
199
+ setIsUpdatingParameters(true);
200
+
201
+ try {
202
+ // Create the new raw source with new parameter values
203
+ const newRawSource = new RawLegendQueryDataCubeSource();
204
+ newRawSource.queryId = source.info.id;
205
+ newRawSource.parameterValues = params.map((param) => {
206
+ const parameterVariable = source.lambda.parameters.find(
207
+ (_param) => _param.name === param.variable.name,
208
+ );
209
+ return [
210
+ JSON.stringify(
211
+ V1_serializeValueSpecification(
212
+ guaranteeNonNullable(parameterVariable),
213
+ application.pluginManager.getPureProtocolProcessorPlugins(),
214
+ ),
215
+ ),
216
+ JSON.stringify(
217
+ V1_serializeValueSpecification(
218
+ guaranteeType(param.valueSpec, V1_ValueSpecification),
219
+ application.pluginManager.getPureProtocolProcessorPlugins(),
220
+ ),
221
+ ),
222
+ ];
223
+ });
224
+ const newRawSourceData =
225
+ RawLegendQueryDataCubeSource.serialization.toJson(newRawSource);
226
+
227
+ // Process the new raw source and create the new specification
228
+ const newSource = await store.engine.processSource(newRawSourceData);
229
+ const newSpecification = await store.engine.generateBaseSpecification(
230
+ RawLegendQueryDataCubeSource.serialization.toJson(newRawSource),
231
+ newSource,
232
+ );
233
+ newSpecification.configuration =
234
+ store.builder?.initialSpecification.configuration;
235
+
236
+ // Update the builder with a new state containing the new specification
237
+ // We pass in the existing persistent data cube so we don't lose the
238
+ // saved state of the data cube, if it exists.
239
+ store.setBuilder(
240
+ new LegendDataCubeBuilderState(
241
+ store,
242
+ newSpecification,
243
+ store.builder?.persistentDataCube,
244
+ ),
245
+ );
246
+
247
+ store.sourceViewerDisplay.close();
248
+ } finally {
249
+ setIsUpdatingParameters(false);
250
+ }
251
+ };
252
+
253
+ const link = application.config.queryApplicationUrl
254
+ ? EXTERNAL_APPLICATION_NAVIGATION__generateQueryViewUrl(
255
+ application.config.queryApplicationUrl,
256
+ source.info.id,
257
+ )
258
+ : undefined;
259
+
260
+ const queryHasParameters = params.length > 0;
261
+
262
+ return (
263
+ <div className="h-full w-full px-2 pt-2">
264
+ <div
265
+ className={`h-[calc(100%_-_${queryHasParameters ? 40 : 8}px)] w-full border border-neutral-300 bg-white`}
266
+ >
267
+ <div className="h-full w-full select-none overflow-auto p-2">
268
+ <div className="flex h-6">
269
+ <div className="flex h-6 items-center text-xl font-medium">
270
+ <DataCubeIcon.Table />
271
+ </div>
272
+ <div className="ml-1 flex h-6 items-center text-xl font-medium">
273
+ Legend Query
274
+ </div>
275
+ </div>
276
+ {link && (
277
+ <div className="mt-2 flex h-6 w-full">
278
+ <div className="flex h-full w-[calc(100%_-_20px)] items-center border border-r-0 border-neutral-400 px-1.5 font-bold text-sky-500 underline">
279
+ <a
280
+ href={link}
281
+ target="_blank"
282
+ rel="noopener noreferrer"
283
+ className="overflow-hidden overflow-ellipsis whitespace-nowrap"
284
+ >
285
+ {link}
286
+ </a>
287
+ </div>
288
+ <button
289
+ className="flex aspect-square h-full w-6 items-center justify-center border border-neutral-400 bg-neutral-300 hover:brightness-95"
290
+ onClick={() => {
291
+ store.application.clipboardService
292
+ .copyTextToClipboard(link)
293
+ .catch((error) =>
294
+ store.alertService.alertUnhandledError(error),
295
+ );
296
+ }}
297
+ title="Copy Link"
298
+ >
299
+ <DataCubeIcon.Clipboard />
300
+ </button>
301
+ </div>
302
+ )}
303
+ {!link && (
304
+ <div className="mt-2 flex h-6 w-full">
305
+ <div className="flex h-full w-[calc(100%_-_20px)] items-center border border-r-0 border-neutral-400 bg-neutral-200 px-1.5">
306
+ <div className="overflow-hidden overflow-ellipsis whitespace-nowrap">
307
+ {source.info.id}
308
+ </div>
309
+ </div>
310
+ <button
311
+ className="flex aspect-square h-full w-6 items-center justify-center border border-neutral-400 bg-neutral-300 hover:brightness-95"
312
+ onClick={() => {
313
+ application.clipboardService
314
+ .copyTextToClipboard(source.info.id)
315
+ .catch((error) =>
316
+ store.alertService.alertUnhandledError(error),
317
+ );
318
+ }}
319
+ title="Copy ID"
320
+ >
321
+ <DataCubeIcon.Clipboard />
322
+ </button>
323
+ </div>
324
+ )}
325
+ {queryHasParameters && (
326
+ <div className="h-50 mt-2 w-full overflow-auto">
327
+ <div>Parameters:</div>
328
+ {params.map(
329
+ (parameter: {
330
+ variable: V1_Variable;
331
+ valueSpec: V1_ValueSpecification;
332
+ }) => {
333
+ const parameterVariable = guaranteeNonNullable(
334
+ source.lambda.parameters.find(
335
+ (param) => param.name === parameter.variable.name,
336
+ ),
337
+ `Unable to find parameter with name ${parameter.variable.name} in source lambda`,
338
+ );
339
+ const packageableType = guaranteeType(
340
+ parameterVariable.genericType?.rawType,
341
+ V1_PackageableType,
342
+ 'Can only edit parameters with packageable type',
343
+ );
344
+
345
+ const resetValue = (): void => {
346
+ if (isPrimitiveType(packageableType.fullPath)) {
347
+ updateParameterValue(
348
+ parameter.variable.name,
349
+ V1_observe_ValueSpecification(
350
+ _primitiveValue(
351
+ packageableType.fullPath,
352
+ _defaultPrimitiveTypeValue(
353
+ packageableType.fullPath,
354
+ ),
355
+ ),
356
+ ),
357
+ );
358
+ } else {
359
+ // If not a primitive, assume it is an enum
360
+ const typeParam = _elementPtr(
361
+ guaranteeIsString(packageableType.fullPath),
362
+ );
363
+ const valueSpec = _property('', [typeParam]);
364
+ updateParameterValue(
365
+ parameter.variable.name,
366
+ V1_observe_ValueSpecification(valueSpec),
367
+ );
368
+ }
369
+ };
370
+
371
+ return (
372
+ <div
373
+ key={parameter.variable.name}
374
+ className="mt-1 flex h-fit min-h-5 w-full"
375
+ >
376
+ <div className="my-auto">
377
+ {parameter.variable.name}
378
+ {': '}
379
+ </div>
380
+ <V1_BasicValueSpecificationEditor
381
+ valueSpecification={guaranteeType(
382
+ parameter.valueSpec,
383
+ V1_ValueSpecification,
384
+ )}
385
+ multiplicity={parameterVariable.multiplicity}
386
+ typeCheckOption={{
387
+ expectedType: packageableType,
388
+ match:
389
+ packageableType.fullPath ===
390
+ PRIMITIVE_TYPE.DATETIME,
391
+ }}
392
+ setValueSpecification={(
393
+ val: V1_ValueSpecification,
394
+ ) => {
395
+ updateParameterValue(
396
+ parameter.variable.name,
397
+ V1_observe_ValueSpecification(val),
398
+ );
399
+ }}
400
+ resetValue={resetValue}
401
+ className="ml-2 flex flex-auto"
402
+ enumeration={enumerations[parameter.variable.name]}
403
+ selectorConfig={{
404
+ optionCustomization: { rowHeight: 20 },
405
+ }}
406
+ lightMode={true}
407
+ readOnly={isCachingEnabled}
408
+ />
409
+ </div>
410
+ );
411
+ },
412
+ )}
413
+ {isCachingEnabled && (
414
+ <FormAlert
415
+ message="Parameter editing disabled"
416
+ type={AlertType.WARNING}
417
+ text="Parameter editing is disabled while caching is enabled. To update query parameter values, disable caching first."
418
+ className="mt-3"
419
+ />
420
+ )}
421
+ </div>
422
+ )}
423
+ </div>
424
+ </div>
425
+ {queryHasParameters && (
426
+ <div className="flex h-10 items-center justify-end px-2">
427
+ <FormButton
428
+ disabled={isUpdatingParameters}
429
+ onClick={() => store.sourceViewerDisplay.close()}
430
+ >
431
+ Cancel
432
+ </FormButton>
433
+ <FormButton
434
+ className="ml-2"
435
+ disabled={
436
+ params.some(
437
+ (param) =>
438
+ !isValidV1_ValueSpecification(
439
+ param.valueSpec,
440
+ param.variable.multiplicity,
441
+ ),
442
+ ) ||
443
+ isCachingEnabled ||
444
+ isUpdatingParameters
445
+ }
446
+ onClick={() => {
447
+ // eslint-disable-next-line no-void
448
+ void (async () => {
449
+ await updateBuilderWithNewSpecification();
450
+ })();
451
+ }}
452
+ >
453
+ Update Query Parameters
454
+ </FormButton>
455
+ </div>
456
+ )}
457
+ </div>
458
+ );
459
+ },
460
+ );
41
461
 
42
462
  const handleFetchProject = (
43
463
  depotServerClient: DepotServerClient,
@@ -191,84 +611,12 @@ const UserDefinedFunctionSourceViewer = observer(
191
611
  export const LegendDataCubeSourceViewer = observer(() => {
192
612
  const store = useLegendDataCubeBuilderStore();
193
613
  const source = store.builder?.source;
194
- const application = useLegendDataCubeApplicationStore();
195
614
 
196
615
  if (!source) {
197
616
  return null;
198
617
  }
199
618
  if (source instanceof LegendQueryDataCubeSource) {
200
- const link = application.config.queryApplicationUrl
201
- ? EXTERNAL_APPLICATION_NAVIGATION__generateQueryViewUrl(
202
- application.config.queryApplicationUrl,
203
- source.info.id,
204
- )
205
- : undefined;
206
-
207
- return (
208
- <div className="h-full w-full px-2 pt-2">
209
- <div className="h-[calc(100%_-_8px)] w-full border border-neutral-300 bg-white">
210
- <div className="h-full w-full select-none p-2">
211
- <div className="flex h-6">
212
- <div className="flex h-6 items-center text-xl font-medium">
213
- <DataCubeIcon.Table />
214
- </div>
215
- <div className="ml-1 flex h-6 items-center text-xl font-medium">
216
- Legend Query
217
- </div>
218
- </div>
219
- {link && (
220
- <div className="mt-2 flex h-6 w-full">
221
- <div className="flex h-full w-[calc(100%_-_20px)] items-center border border-r-0 border-neutral-400 px-1.5 font-bold text-sky-500 underline">
222
- <a
223
- href={link}
224
- target="_blank"
225
- rel="noopener noreferrer"
226
- className="overflow-hidden overflow-ellipsis whitespace-nowrap"
227
- >
228
- {link}
229
- </a>
230
- </div>
231
- <button
232
- className="flex aspect-square h-full w-6 items-center justify-center border border-neutral-400 bg-neutral-300 hover:brightness-95"
233
- onClick={() => {
234
- store.application.clipboardService
235
- .copyTextToClipboard(link)
236
- .catch((error) =>
237
- store.alertService.alertUnhandledError(error),
238
- );
239
- }}
240
- title="Copy Link"
241
- >
242
- <DataCubeIcon.Clipboard />
243
- </button>
244
- </div>
245
- )}
246
- {!link && (
247
- <div className="mt-2 flex h-6 w-full">
248
- <div className="flex h-full w-[calc(100%_-_20px)] items-center border border-r-0 border-neutral-400 bg-neutral-200 px-1.5">
249
- <div className="overflow-hidden overflow-ellipsis whitespace-nowrap">
250
- {source.info.id}
251
- </div>
252
- </div>
253
- <button
254
- className="flex aspect-square h-full w-6 items-center justify-center border border-neutral-400 bg-neutral-300 hover:brightness-95"
255
- onClick={() => {
256
- application.clipboardService
257
- .copyTextToClipboard(source.info.id)
258
- .catch((error) =>
259
- store.alertService.alertUnhandledError(error),
260
- );
261
- }}
262
- title="Copy ID"
263
- >
264
- <DataCubeIcon.Clipboard />
265
- </button>
266
- </div>
267
- )}
268
- </div>
269
- </div>
270
- </div>
271
- );
619
+ return <LegendQuerySourceViewer source={source} />;
272
620
  } else if (source instanceof UserDefinedFunctionDataCubeSource) {
273
621
  return <UserDefinedFunctionSourceViewer source={source} />;
274
622
  }
@@ -33,7 +33,7 @@ import {
33
33
  V1_deserializePackageableElement,
34
34
  } from '@finos/legend-graph';
35
35
  import type { Entity } from '@finos/legend-storage';
36
- import type { AdhocQueryDataCubeSourceBuilderState } from '../../../stores/builder/source/AdhocQueryDataCubeSourceBuilderState.js';
36
+ import type { FreeformTDSExpressionDataCubeSourceBuilderState } from '../../../stores/builder/source/FreeformTDSExpressionDataCubeSourceBuilderState.js';
37
37
  import {
38
38
  buildProjectOption,
39
39
  buildRuntimeOption,
@@ -53,9 +53,9 @@ export const buildMappingOption = (mapping: V1_Mapping): MappingOption => ({
53
53
  value: mapping,
54
54
  });
55
55
 
56
- export const AdhocQueryDataCubeSourceBuilder = observer(
56
+ export const FreeformTDSExpressionDataCubeSourceBuilder = observer(
57
57
  (props: {
58
- sourceBuilder: AdhocQueryDataCubeSourceBuilderState;
58
+ sourceBuilder: FreeformTDSExpressionDataCubeSourceBuilderState;
59
59
  store: LegendDataCubeBuilderStore;
60
60
  }) => {
61
61
  const { sourceBuilder, store } = props;
@@ -16,9 +16,10 @@
16
16
 
17
17
  import { observer } from 'mobx-react-lite';
18
18
  import {
19
+ type QueryLoaderState,
19
20
  QUERY_LOADER_TYPEAHEAD_SEARCH_LIMIT,
20
21
  SORT_BY_OPTIONS,
21
- type QueryLoaderState,
22
+ V1_BasicValueSpecificationEditor,
22
23
  } from '@finos/legend-query-builder';
23
24
  import type { LegendQueryDataCubeSourceBuilderState } from '../../../stores/builder/source/LegendQueryDataCubeSourceBuilderState.js';
24
25
  import { generateGAVCoordinates } from '@finos/legend-storage';
@@ -26,11 +27,17 @@ import { cn, DataCubeIcon, useDropdownMenu } from '@finos/legend-art';
26
27
  import {
27
28
  debounce,
28
29
  formatDistanceToNow,
30
+ guaranteeIsString,
31
+ guaranteeType,
29
32
  quantifyList,
30
33
  } from '@finos/legend-shared';
31
34
  import { flowResult } from 'mobx';
32
35
  import { useRef, useState, useMemo, useEffect } from 'react';
33
36
  import {
37
+ _defaultPrimitiveTypeValue,
38
+ _elementPtr,
39
+ _primitiveValue,
40
+ _property,
34
41
  FormButton,
35
42
  FormCheckbox,
36
43
  FormCodeEditor,
@@ -38,10 +45,17 @@ import {
38
45
  FormDropdownMenuItem,
39
46
  FormDropdownMenuTrigger,
40
47
  FormTextInput,
48
+ isPrimitiveType,
41
49
  } from '@finos/legend-data-cube';
42
50
  import { CODE_EDITOR_LANGUAGE } from '@finos/legend-code-editor';
43
51
  import { useLegendDataCubeBuilderStore } from '../LegendDataCubeBuilderStoreProvider.js';
44
52
  import { useApplicationStore } from '@finos/legend-application';
53
+ import {
54
+ type V1_ValueSpecification,
55
+ PRIMITIVE_TYPE,
56
+ V1_observe_ValueSpecification,
57
+ V1_PackageableType,
58
+ } from '@finos/legend-graph';
45
59
 
46
60
  const LegendQuerySearcher = observer((props: { state: QueryLoaderState }) => {
47
61
  const { state } = props;
@@ -319,6 +333,83 @@ export const LegendQueryDataCubeSourceBuilder = observer(
319
333
  />
320
334
  </div>
321
335
  )}
336
+ {sourceBuilder.queryParameters &&
337
+ sourceBuilder.queryParameters.length > 0 && (
338
+ <div className="h-50 mt-2 w-full overflow-auto">
339
+ {sourceBuilder.queryParameterValues &&
340
+ Object.entries(sourceBuilder.queryParameterValues).map(
341
+ ([name, { variable, valueSpec }]) => {
342
+ const packageableType = guaranteeType(
343
+ variable.genericType?.rawType,
344
+ V1_PackageableType,
345
+ 'Can only edit parameters with packageable type',
346
+ );
347
+ const enumeration = sourceBuilder.queryEnumerations?.[name];
348
+ const resetValue = (): void => {
349
+ if (isPrimitiveType(packageableType.fullPath)) {
350
+ sourceBuilder.setQueryParameterValue(
351
+ name,
352
+ V1_observe_ValueSpecification(
353
+ _primitiveValue(
354
+ packageableType.fullPath,
355
+ _defaultPrimitiveTypeValue(
356
+ packageableType.fullPath,
357
+ ),
358
+ ),
359
+ ),
360
+ );
361
+ } else {
362
+ // If not a primitive, assume it is an enum
363
+ const typeParam = _elementPtr(
364
+ guaranteeIsString(packageableType.fullPath),
365
+ );
366
+ const enumValueSpec = _property('', [typeParam]);
367
+ sourceBuilder.setQueryParameterValue(
368
+ name,
369
+ V1_observe_ValueSpecification(enumValueSpec),
370
+ );
371
+ }
372
+ };
373
+ return (
374
+ <div
375
+ key={name}
376
+ className="mt-1 flex h-fit min-h-5 w-full"
377
+ >
378
+ <div className="my-auto">
379
+ {name}
380
+ {': '}
381
+ </div>
382
+ <V1_BasicValueSpecificationEditor
383
+ valueSpecification={valueSpec}
384
+ multiplicity={variable.multiplicity}
385
+ typeCheckOption={{
386
+ expectedType: packageableType,
387
+ match:
388
+ packageableType.fullPath ===
389
+ PRIMITIVE_TYPE.DATETIME,
390
+ }}
391
+ setValueSpecification={(
392
+ val: V1_ValueSpecification,
393
+ ) => {
394
+ sourceBuilder.setQueryParameterValue(
395
+ name,
396
+ V1_observe_ValueSpecification(val),
397
+ );
398
+ }}
399
+ resetValue={resetValue}
400
+ className="ml-2 flex flex-auto"
401
+ enumeration={enumeration}
402
+ selectorConfig={{
403
+ optionCustomization: { rowHeight: 20 },
404
+ }}
405
+ lightMode={true}
406
+ />
407
+ </div>
408
+ );
409
+ },
410
+ )}
411
+ </div>
412
+ )}
322
413
  <FormButton
323
414
  className="mt-2 flex items-center pl-1"
324
415
  onClick={() => sourceBuilder.unsetQuery()}