@finos/legend-application-data-cube 0.1.21 → 0.2.1

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 (54) hide show
  1. package/lib/__lib__/LegendDataCubeNavigation.d.ts +1 -1
  2. package/lib/__lib__/LegendDataCubeNavigation.d.ts.map +1 -1
  3. package/lib/components/LegendDataCubeWebApplication.d.ts.map +1 -1
  4. package/lib/components/LegendDataCubeWebApplication.js +1 -14
  5. package/lib/components/LegendDataCubeWebApplication.js.map +1 -1
  6. package/lib/components/query-builder/LegendDataCubeQueryBuilder.d.ts.map +1 -1
  7. package/lib/components/query-builder/LegendDataCubeQueryBuilder.js +24 -23
  8. package/lib/components/query-builder/LegendDataCubeQueryBuilder.js.map +1 -1
  9. package/lib/components/query-builder/source-builder/LegendQueryDataCubeSourceBuilder.d.ts.map +1 -1
  10. package/lib/components/query-builder/source-builder/LegendQueryDataCubeSourceBuilder.js +8 -1
  11. package/lib/components/query-builder/source-builder/LegendQueryDataCubeSourceBuilder.js.map +1 -1
  12. package/lib/index.css +1 -1
  13. package/lib/package.json +3 -2
  14. package/lib/stores/LegendDataCubeCacheManager.d.ts +34 -0
  15. package/lib/stores/LegendDataCubeCacheManager.d.ts.map +1 -0
  16. package/lib/stores/LegendDataCubeCacheManager.js +134 -0
  17. package/lib/stores/LegendDataCubeCacheManager.js.map +1 -0
  18. package/lib/stores/LegendDataCubeDataCubeEngine.d.ts +11 -3
  19. package/lib/stores/LegendDataCubeDataCubeEngine.d.ts.map +1 -1
  20. package/lib/stores/LegendDataCubeDataCubeEngine.js +152 -19
  21. package/lib/stores/LegendDataCubeDataCubeEngine.js.map +1 -1
  22. package/lib/stores/query-builder/LegendDataCubeNewQueryState.d.ts.map +1 -1
  23. package/lib/stores/query-builder/LegendDataCubeNewQueryState.js +10 -10
  24. package/lib/stores/query-builder/LegendDataCubeNewQueryState.js.map +1 -1
  25. package/lib/stores/query-builder/LegendDataCubeQueryBuilderStore.d.ts +0 -1
  26. package/lib/stores/query-builder/LegendDataCubeQueryBuilderStore.d.ts.map +1 -1
  27. package/lib/stores/query-builder/LegendDataCubeQueryBuilderStore.js +18 -9
  28. package/lib/stores/query-builder/LegendDataCubeQueryBuilderStore.js.map +1 -1
  29. package/lib/stores/query-builder/source-builder/AdhocQueryDataCubeSourceBuilderState.d.ts +1 -1
  30. package/lib/stores/query-builder/source-builder/AdhocQueryDataCubeSourceBuilderState.d.ts.map +1 -1
  31. package/lib/stores/query-builder/source-builder/AdhocQueryDataCubeSourceBuilderState.js +1 -1
  32. package/lib/stores/query-builder/source-builder/AdhocQueryDataCubeSourceBuilderState.js.map +1 -1
  33. package/lib/stores/query-builder/source-builder/LegendDataCubeSourceBuilderState.d.ts +4 -3
  34. package/lib/stores/query-builder/source-builder/LegendDataCubeSourceBuilderState.d.ts.map +1 -1
  35. package/lib/stores/query-builder/source-builder/LegendDataCubeSourceBuilderState.js +5 -2
  36. package/lib/stores/query-builder/source-builder/LegendDataCubeSourceBuilderState.js.map +1 -1
  37. package/lib/stores/query-builder/source-builder/LegendQueryDataCubeSourceBuilderState.d.ts +3 -2
  38. package/lib/stores/query-builder/source-builder/LegendQueryDataCubeSourceBuilderState.d.ts.map +1 -1
  39. package/lib/stores/query-builder/source-builder/LegendQueryDataCubeSourceBuilderState.js +6 -1
  40. package/lib/stores/query-builder/source-builder/LegendQueryDataCubeSourceBuilderState.js.map +1 -1
  41. package/package.json +13 -12
  42. package/src/__lib__/LegendDataCubeNavigation.ts +1 -1
  43. package/src/components/LegendDataCubeWebApplication.tsx +1 -25
  44. package/src/components/query-builder/LegendDataCubeQueryBuilder.tsx +27 -27
  45. package/src/components/query-builder/source-builder/LegendQueryDataCubeSourceBuilder.tsx +17 -1
  46. package/src/stores/DuckDBWASM.d.ts +22 -0
  47. package/src/stores/LegendDataCubeCacheManager.ts +170 -0
  48. package/src/stores/LegendDataCubeDataCubeEngine.ts +247 -26
  49. package/src/stores/query-builder/LegendDataCubeNewQueryState.tsx +12 -18
  50. package/src/stores/query-builder/LegendDataCubeQueryBuilderStore.tsx +26 -10
  51. package/src/stores/query-builder/source-builder/AdhocQueryDataCubeSourceBuilderState.ts +1 -1
  52. package/src/stores/query-builder/source-builder/LegendDataCubeSourceBuilderState.ts +8 -4
  53. package/src/stores/query-builder/source-builder/LegendQueryDataCubeSourceBuilderState.ts +11 -2
  54. package/tsconfig.json +2 -0
@@ -15,7 +15,7 @@
15
15
  */
16
16
 
17
17
  import {
18
- type V1_Lambda,
18
+ V1_Lambda,
19
19
  type V1_ValueSpecification,
20
20
  type V1_EngineServerClient,
21
21
  V1_PureGraphManager,
@@ -44,6 +44,34 @@ import {
44
44
  V1_PackageableType,
45
45
  V1_deserializeRawValueSpecificationType,
46
46
  V1_Protocol,
47
+ type V1_ExecutionPlan,
48
+ V1_deserializeExecutionPlan,
49
+ V1_SQLExecutionNode,
50
+ V1_SimpleExecutionPlan,
51
+ V1_Binary,
52
+ V1_ClassInstance,
53
+ V1_ClassInstanceType,
54
+ V1_Column,
55
+ V1_Database,
56
+ V1_Date,
57
+ V1_Double,
58
+ V1_DuckDBDatasourceSpecification,
59
+ V1_EngineRuntime,
60
+ V1_IdentifiedConnection,
61
+ V1_Integer,
62
+ V1_PackageableElementPointer,
63
+ V1_PackageableRuntime,
64
+ V1_PureModelContextData,
65
+ V1_RelationStoreAccessor,
66
+ type V1_RelationalDataType,
67
+ V1_RelationalDatabaseConnection,
68
+ V1_Schema,
69
+ V1_StoreConnections,
70
+ V1_Table,
71
+ V1_TestAuthenticationStrategy,
72
+ V1_VarChar,
73
+ PackageableElementPointerType,
74
+ DatabaseType,
47
75
  } from '@finos/legend-graph';
48
76
  import {
49
77
  _elementPtr,
@@ -52,15 +80,13 @@ import {
52
80
  type CompletionItem,
53
81
  _function,
54
82
  DataCubeFunction,
55
- _deserializeLambda,
56
83
  AdhocQueryDataCubeSource,
57
84
  ADHOC_QUERY_DATA_CUBE_SOURCE_TYPE,
58
85
  RawAdhocQueryDataCubeSource,
59
86
  _lambda,
60
- _serializeValueSpecification,
61
- _deserializeValueSpecification,
62
87
  _defaultPrimitiveTypeValue,
63
88
  type DataCubeExecutionOptions,
89
+ CachedDataCubeSource,
64
90
  } from '@finos/legend-data-cube';
65
91
  import {
66
92
  isNonNullable,
@@ -72,8 +98,12 @@ import {
72
98
  HttpStatus,
73
99
  at,
74
100
  assertType,
101
+ guaranteeType,
102
+ guaranteeNonNullable,
103
+ filterByType,
75
104
  } from '@finos/legend-shared';
76
105
  import type { LegendDataCubeApplicationStore } from './LegendDataCubeBaseStore.js';
106
+ import { LegendDataCubeDataCubeCacheManager } from './LegendDataCubeCacheManager.js';
77
107
  import { APPLICATION_EVENT } from '@finos/legend-application';
78
108
  import {
79
109
  LEGEND_QUERY_DATA_CUBE_SOURCE_TYPE,
@@ -91,6 +121,7 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
91
121
  private readonly _depotServerClient: DepotServerClient;
92
122
  private readonly _engineServerClient: V1_EngineServerClient;
93
123
  private readonly _graphManager: V1_PureGraphManager;
124
+ private readonly _cacheManager: LegendDataCubeDataCubeCacheManager;
94
125
 
95
126
  constructor(
96
127
  application: LegendDataCubeApplicationStore,
@@ -104,6 +135,15 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
104
135
  this._depotServerClient = depotServerClient;
105
136
  this._engineServerClient = engineServerClient;
106
137
  this._graphManager = graphManager;
138
+ this._cacheManager = new LegendDataCubeDataCubeCacheManager();
139
+ }
140
+
141
+ async initializeCacheManager() {
142
+ await this._cacheManager.initialize();
143
+ }
144
+
145
+ async disposeCacheManager() {
146
+ await this._cacheManager.dispose();
107
147
  }
108
148
 
109
149
  // ---------------------------------- IMPLEMENTATION ----------------------------------
@@ -125,7 +165,7 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
125
165
  try {
126
166
  source.columns = (
127
167
  await this._getLambdaRelationType(
128
- _serializeValueSpecification(_lambda([], [source.query])),
168
+ this.serializeValueSpecification(_lambda([], [source.query])),
129
169
  source.model,
130
170
  )
131
171
  ).columns;
@@ -155,14 +195,18 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
155
195
  );
156
196
  const source = new LegendQueryDataCubeSource();
157
197
  source.info = queryInfo;
158
- source.lambda = _deserializeLambda(
159
- await this._engineServerClient.grammarToJSON_lambda(
160
- queryInfo.content,
161
- '',
162
- undefined,
163
- undefined,
164
- false,
198
+
199
+ source.lambda = guaranteeType(
200
+ this.deserializeValueSpecification(
201
+ await this._engineServerClient.grammarToJSON_lambda(
202
+ queryInfo.content,
203
+ '',
204
+ undefined,
205
+ undefined,
206
+ false,
207
+ ),
165
208
  ),
209
+ V1_Lambda,
166
210
  );
167
211
  source.mapping = executionContext.mapping;
168
212
  source.runtime = executionContext.runtime;
@@ -181,18 +225,22 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
181
225
  ),
182
226
  );
183
227
  source.query = at(source.lambda.body, 0);
228
+ // use the default parameter values from the query
229
+ //
230
+ // TODO?: we should probably allow configuring the parameters?
231
+ // this would mean we need to create first-class support for parameters in DataCube component
184
232
  const parameterValues = await Promise.all(
185
233
  source.lambda.parameters.map(async (parameter) => {
186
234
  if (parameter.genericType?.rawType instanceof V1_PackageableType) {
187
235
  const paramValue = new V1_ParameterValue();
188
236
  paramValue.name = parameter.name;
189
237
  const type = parameter.genericType.rawType.fullPath;
190
- const defauleValue = queryInfo.defaultParameterValues?.find(
238
+ const defaultValue = queryInfo.defaultParameterValues?.find(
191
239
  (val) => val.name === parameter.name,
192
240
  )?.content;
193
241
  paramValue.value =
194
- defauleValue !== undefined
195
- ? await this.parseValueSpecification(defauleValue)
242
+ defaultValue !== undefined
243
+ ? await this.parseValueSpecification(defaultValue)
196
244
  : {
197
245
  _type: V1_deserializeRawValueSpecificationType(type),
198
246
  value: _defaultPrimitiveTypeValue(type),
@@ -206,7 +254,7 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
206
254
  try {
207
255
  source.columns = (
208
256
  await this._getLambdaRelationType(
209
- _serializeValueSpecification(source.lambda),
257
+ this.serializeValueSpecification(source.lambda),
210
258
  source.model,
211
259
  )
212
260
  ).columns;
@@ -230,7 +278,7 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
230
278
  returnSourceInformation?: boolean | undefined,
231
279
  ) {
232
280
  try {
233
- return _deserializeValueSpecification(
281
+ return this.deserializeValueSpecification(
234
282
  await this._engineServerClient.grammarToJSON_valueSpecification(
235
283
  code,
236
284
  '',
@@ -260,7 +308,7 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
260
308
  pretty?: boolean | undefined,
261
309
  ) {
262
310
  return this._graphManager.valueSpecificationToPureCode(
263
- _serializeValueSpecification(value),
311
+ this.serializeValueSpecification(value),
264
312
  pretty,
265
313
  );
266
314
  }
@@ -303,7 +351,7 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
303
351
  ) {
304
352
  try {
305
353
  return await this._getQueryRelationType(
306
- _serializeValueSpecification(query),
354
+ this.serializeValueSpecification(query),
307
355
  source,
308
356
  );
309
357
  } catch (error) {
@@ -369,6 +417,7 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
369
417
  ) {
370
418
  const queryCodePromise = this.getValueSpecificationCode(query);
371
419
  let result: ExecutionResult;
420
+ const startTime = performance.now();
372
421
  if (source instanceof AdhocQueryDataCubeSource) {
373
422
  result = await this._runQuery(query, source.model, undefined, options);
374
423
  } else if (source instanceof LegendQueryDataCubeSource) {
@@ -379,6 +428,29 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
379
428
  source.parameterValues,
380
429
  options,
381
430
  );
431
+ } else if (source instanceof CachedDataCubeSource) {
432
+ // get the execute plan to extract the generated SQL to run against cached DB
433
+ const executionPlan = await this.generateExecutionPlan(
434
+ query,
435
+ source.model,
436
+ [],
437
+ options,
438
+ );
439
+ const sql = guaranteeNonNullable(
440
+ executionPlan instanceof V1_SimpleExecutionPlan
441
+ ? executionPlan.rootExecutionNode.executionNodes
442
+ .filter(filterByType(V1_SQLExecutionNode))
443
+ .at(-1)?.sqlQuery
444
+ : undefined,
445
+ `Can't process execution plan: failed to extract generated SQL`,
446
+ );
447
+ const endTime = performance.now();
448
+ return {
449
+ executedQuery: await queryCodePromise,
450
+ executedSQL: sql,
451
+ result: await this._cacheManager.runSQLQuery(sql),
452
+ executionTime: endTime - startTime,
453
+ };
382
454
  } else {
383
455
  throw new UnsupportedOperationError(
384
456
  `Can't execute query with unsupported source`,
@@ -387,20 +459,21 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
387
459
  assertType(
388
460
  result,
389
461
  TDSExecutionResult,
390
- `Can't extract execution result: expected tabular data set format`,
462
+ `Can't process execution result: expected tabular data set format`,
391
463
  );
464
+ const endTime = performance.now();
392
465
  const queryCode = await queryCodePromise;
393
- const sql =
466
+ const sql = guaranteeNonNullable(
394
467
  result.activities?.[0] instanceof RelationalExecutionActivities
395
468
  ? result.activities[0].sql
396
- : undefined;
397
- if (!sql) {
398
- throw new Error(`Can't generate SQL for query`);
399
- }
469
+ : undefined,
470
+ `Can't process execution result: failed to extract generated SQL`,
471
+ );
400
472
  return {
401
473
  result: result,
402
474
  executedQuery: queryCode,
403
475
  executedSQL: sql,
476
+ executionTime: endTime - startTime,
404
477
  };
405
478
  }
406
479
 
@@ -418,6 +491,11 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
418
491
  _elementPtr(source.runtime),
419
492
  ].filter(isNonNullable),
420
493
  );
494
+ } else if (source instanceof CachedDataCubeSource) {
495
+ return _function(
496
+ DataCubeFunction.FROM,
497
+ [_elementPtr(source.runtime)].filter(isNonNullable),
498
+ );
421
499
  }
422
500
  return undefined;
423
501
  }
@@ -432,6 +510,8 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
432
510
  return this._getLambdaRelationType(query, source.model);
433
511
  } else if (source instanceof LegendQueryDataCubeSource) {
434
512
  return this._getLambdaRelationType(query, source.model);
513
+ } else if (source instanceof CachedDataCubeSource) {
514
+ return this._getLambdaRelationType(query, serialize(source.model));
435
515
  }
436
516
  throw new UnsupportedOperationError(
437
517
  `Can't get relation type for lambda with unsupported source`,
@@ -472,7 +552,7 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
472
552
  (process.env.NODE_ENV === 'development'
473
553
  ? PureClientVersion.VX_X_X
474
554
  : undefined),
475
- function: _serializeValueSpecification(query),
555
+ function: this.serializeValueSpecification(query),
476
556
  model,
477
557
  context: serialize(
478
558
  V1_rawBaseExecutionContextModelSchema,
@@ -486,6 +566,147 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
486
566
  );
487
567
  }
488
568
 
569
+ private async generateExecutionPlan(
570
+ query: V1_Lambda,
571
+ model: V1_PureModelContext,
572
+ parameterValues?: V1_ParameterValue[] | undefined,
573
+ options?: DataCubeExecutionOptions | undefined,
574
+ ): Promise<V1_ExecutionPlan> {
575
+ return V1_deserializeExecutionPlan(
576
+ await this._engineServerClient.generatePlan({
577
+ clientVersion:
578
+ options?.clientVersion ??
579
+ // eslint-disable-next-line no-process-env
580
+ (process.env.NODE_ENV === 'development'
581
+ ? PureClientVersion.VX_X_X
582
+ : undefined),
583
+ function: this.serializeValueSpecification(query),
584
+ model: serialize(model),
585
+ context: serialize(
586
+ V1_rawBaseExecutionContextModelSchema,
587
+ new V1_RawBaseExecutionContext(),
588
+ ),
589
+ parameterValues: (parameterValues ?? []).map((parameterValue) =>
590
+ serialize(V1_parameterValueModelSchema, parameterValue),
591
+ ),
592
+ }),
593
+ );
594
+ }
595
+
596
+ // ---------------------------------- CACHING --------------------------------------
597
+
598
+ override async initializeCache(
599
+ source: DataCubeSource,
600
+ ): Promise<CachedDataCubeSource | undefined> {
601
+ const cacheQuery = guaranteeNonNullable(
602
+ this.buildExecutionContext(source),
603
+ `Can't initialize cache: failed to build cache query`,
604
+ );
605
+ cacheQuery.parameters.unshift(source.query);
606
+ const result = await this.executeQuery(
607
+ _lambda([], [cacheQuery]),
608
+ source,
609
+ undefined,
610
+ );
611
+ const {
612
+ schema: schemaName,
613
+ table: tableName,
614
+ rowCount,
615
+ } = await this._cacheManager.cache(result.result);
616
+
617
+ // model
618
+ const pacakgePath = 'local';
619
+
620
+ const table = new V1_Table();
621
+ table.name = tableName;
622
+ table.columns = result.result.builder.columns.map((col) => {
623
+ const column = new V1_Column();
624
+ column.name = col.name;
625
+ column.type = this._getColumnType(col.type);
626
+ return column;
627
+ });
628
+
629
+ const schema = new V1_Schema();
630
+ schema.name = schemaName;
631
+ schema.tables = [table];
632
+ const database = new V1_Database();
633
+ database.name = 'db';
634
+ database.package = pacakgePath;
635
+ database.schemas = [schema];
636
+
637
+ const connection = new V1_RelationalDatabaseConnection();
638
+ connection.databaseType = DatabaseType.DuckDB;
639
+ connection.type = DatabaseType.DuckDB;
640
+ const dataSourceSpec = new V1_DuckDBDatasourceSpecification();
641
+ dataSourceSpec.path = '/tmpFile';
642
+ connection.store = database.path;
643
+ connection.datasourceSpecification = dataSourceSpec;
644
+ connection.authenticationStrategy = new V1_TestAuthenticationStrategy();
645
+
646
+ const runtime = new V1_EngineRuntime();
647
+ const storeConnections = new V1_StoreConnections();
648
+ storeConnections.store = new V1_PackageableElementPointer(
649
+ PackageableElementPointerType.STORE,
650
+ database.path,
651
+ );
652
+ const identifiedConnection = new V1_IdentifiedConnection();
653
+ identifiedConnection.connection = connection;
654
+ identifiedConnection.id = 'c0';
655
+ storeConnections.storeConnections = [identifiedConnection];
656
+ runtime.connections = [storeConnections];
657
+
658
+ const packageableRuntime = new V1_PackageableRuntime();
659
+ packageableRuntime.runtimeValue = runtime;
660
+ packageableRuntime.package = pacakgePath;
661
+ packageableRuntime.name = 'rt';
662
+
663
+ const model = new V1_PureModelContextData();
664
+ model.elements = [database, packageableRuntime];
665
+
666
+ // query
667
+ const query = new V1_ClassInstance();
668
+ query.type = V1_ClassInstanceType.RELATION_STORE_ACCESSOR;
669
+ const storeAccessor = new V1_RelationStoreAccessor();
670
+ storeAccessor.path = [database.path, schema.name, table.name];
671
+ query.value = storeAccessor;
672
+
673
+ const cachedSource = new CachedDataCubeSource();
674
+ cachedSource.columns = source.columns;
675
+ cachedSource.query = query;
676
+ cachedSource.model = model;
677
+ cachedSource.runtime = packageableRuntime.path;
678
+ cachedSource.db = database.path;
679
+ cachedSource.schema = schema.name;
680
+ cachedSource.table = table.name;
681
+ cachedSource.count = rowCount;
682
+ return cachedSource;
683
+ }
684
+
685
+ override async disposeCache(source: CachedDataCubeSource) {
686
+ await this._cacheManager.disposeCache(source);
687
+ }
688
+
689
+ // TODO: need a better way to infer datatype from tds builder
690
+ private _getColumnType(type: string | undefined): V1_RelationalDataType {
691
+ if (type === undefined) {
692
+ throw Error('Unsupported data type');
693
+ }
694
+ switch (type) {
695
+ case 'string':
696
+ return new V1_VarChar();
697
+ case 'integer':
698
+ return new V1_Integer();
699
+ case 'date':
700
+ return new V1_Date();
701
+ case 'boolean':
702
+ return new V1_Binary();
703
+ case 'number':
704
+ return new V1_Double();
705
+ default:
706
+ return new V1_VarChar();
707
+ }
708
+ }
709
+
489
710
  // ---------------------------------- APPLICATION ----------------------------------
490
711
 
491
712
  override logDebug(message: string, ...data: unknown[]) {
@@ -29,9 +29,7 @@ import {
29
29
  type LegendDataCubeSourceBuilderState,
30
30
  } from './source-builder/LegendDataCubeSourceBuilderState.js';
31
31
  import {
32
- _selectFunction,
33
32
  type DataCubeAlertService,
34
- DataCubeQuery,
35
33
  DEFAULT_TOOL_PANEL_WINDOW_CONFIG,
36
34
  type DisplayState,
37
35
  } from '@finos/legend-data-cube';
@@ -44,6 +42,8 @@ import {
44
42
  } from './LegendDataCubeQueryBuilderStore.js';
45
43
  import { generateQueryBuilderRoute } from '../../__lib__/LegendDataCubeNavigation.js';
46
44
 
45
+ const DEFAULT_SOURCE_TYPE = LegendDataCubeSourceBuilderType.LEGEND_QUERY;
46
+
47
47
  export class LegendDataCubeNewQueryState {
48
48
  private readonly _application: LegendDataCubeApplicationStore;
49
49
  private readonly _store: LegendDataCubeQueryBuilderStore;
@@ -76,9 +76,7 @@ export class LegendDataCubeNewQueryState {
76
76
  },
77
77
  );
78
78
 
79
- this.sourceBuilder = this.createSourceBuilder(
80
- LegendDataCubeSourceBuilderType.LEGEND_QUERY,
81
- );
79
+ this.sourceBuilder = this.createSourceBuilder(DEFAULT_SOURCE_TYPE);
82
80
  }
83
81
 
84
82
  changeSourceBuilder(
@@ -115,31 +113,27 @@ export class LegendDataCubeNewQueryState {
115
113
  }
116
114
 
117
115
  async finalize(sourceData?: PlainObject) {
118
- if (!this.sourceBuilder.isValid && !sourceData) {
116
+ if (!sourceData && !this.sourceBuilder.isValid) {
119
117
  throw new IllegalStateError(`Can't generate query: source is not valid`);
120
118
  }
121
119
 
122
120
  this.finalizeState.inProgress();
123
121
  try {
124
- const source = sourceData ?? (await this.sourceBuilder.build());
125
- const query = new DataCubeQuery();
126
- const processedSource = await this._engine.processQuerySource(source);
127
- query.source = source;
128
- query.query = await this._engine.getValueSpecificationCode(
129
- _selectFunction(processedSource.columns),
122
+ const query = await this._engine.generateBaseQuery(
123
+ sourceData ?? (await this.sourceBuilder.generateSourceData()),
130
124
  );
125
+ if (query.configuration) {
126
+ this.sourceBuilder.finalizeConfiguration(query.configuration);
127
+ }
128
+
129
+ // reset
131
130
  this._store.setBuilder(new LegendDataCubeQueryBuilderState(query));
132
131
  // only update the route instead of reloading in case we are creating
133
132
  // a new query when we are editing another query
134
133
  this._application.navigationService.navigator.updateCurrentLocation(
135
134
  generateQueryBuilderRoute(null),
136
135
  );
137
-
138
- // reset
139
- this.changeSourceBuilder(
140
- LegendDataCubeSourceBuilderType.LEGEND_QUERY,
141
- true,
142
- );
136
+ this.changeSourceBuilder(DEFAULT_SOURCE_TYPE, true);
143
137
  this.display.close();
144
138
  this.finalizeState.pass();
145
139
  } catch (error) {
@@ -43,7 +43,10 @@ import {
43
43
  } from '@finos/legend-shared';
44
44
  import type { LegendDataCubeDataCubeEngine } from '../LegendDataCubeDataCubeEngine.js';
45
45
  import { LegendDataCubeQuerySaver } from '../../components/query-builder/LegendDataCubeQuerySaver.js';
46
- import { generateQueryBuilderRoute } from '../../__lib__/LegendDataCubeNavigation.js';
46
+ import {
47
+ generateQueryBuilderRoute,
48
+ LEGEND_DATA_CUBE_ROUTE_PATTERN_TOKEN,
49
+ } from '../../__lib__/LegendDataCubeNavigation.js';
47
50
  import { LegendDataCubeQueryLoaderState } from './LegendDataCubeQueryLoaderState.js';
48
51
  import {
49
52
  LegendDataCubeUserDataKey,
@@ -68,7 +71,6 @@ export class LegendDataCubeQueryBuilderState {
68
71
 
69
72
  query: observable,
70
73
  persistentQuery: observable,
71
- setPersistentQuery: action,
72
74
  });
73
75
 
74
76
  this.query = query;
@@ -78,12 +80,6 @@ export class LegendDataCubeQueryBuilderState {
78
80
  setDataCube(val: DataCubeAPI | undefined) {
79
81
  this.dataCube = val;
80
82
  }
81
-
82
- setPersistentQuery(val: PersistentDataCubeQuery) {
83
- this.persistentQuery = val;
84
- this.query = DataCubeQuery.serialization.fromJson(val.content);
85
- this.startTime = Date.now();
86
- }
87
83
  }
88
84
 
89
85
  export class LegendDataCubeQueryBuilderStore {
@@ -154,9 +150,31 @@ export class LegendDataCubeQueryBuilderStore {
154
150
  }
155
151
 
156
152
  async loadQuery(queryId: string | undefined) {
153
+ // internalize the parameters and clean them from the URL
154
+ const sourceData =
155
+ this.application.navigationService.navigator.getCurrentLocationParameterValue(
156
+ LEGEND_DATA_CUBE_ROUTE_PATTERN_TOKEN.SOURCE_DATA,
157
+ );
158
+ if (sourceData && !queryId) {
159
+ this.application.navigationService.navigator.updateCurrentLocation(
160
+ generateQueryBuilderRoute(null),
161
+ );
162
+ // populate the new query state if source data is specified
163
+ try {
164
+ await this.newQueryState.finalize(JSON.parse(atob(sourceData)));
165
+ } catch (error) {
166
+ assertErrorThrown(error);
167
+ this.alertService.alertError(error, {
168
+ message: `Query Creation Failure: Can't materialize query source from source data. Error: ${error.message}`,
169
+ });
170
+ this.setBuilder(undefined);
171
+ }
172
+ }
173
+
157
174
  if (queryId !== this.builder?.persistentQuery?.id) {
158
175
  if (!queryId) {
159
176
  this.setBuilder(undefined);
177
+ this.loader.display.open();
160
178
  return;
161
179
  }
162
180
 
@@ -244,7 +262,6 @@ export class LegendDataCubeQueryBuilderStore {
244
262
  this.application.navigationService.navigator.updateCurrentLocation(
245
263
  generateQueryBuilderRoute(newQuery.id),
246
264
  );
247
- this.builder.setPersistentQuery(persistentQuery);
248
265
  this.updateWindowTitle(persistentQuery);
249
266
 
250
267
  this.saverDisplay.close();
@@ -291,7 +308,6 @@ export class LegendDataCubeQueryBuilderStore {
291
308
  } else {
292
309
  await this.baseStore.graphManager.updateDataCubeQuery(persistentQuery);
293
310
  }
294
- this.builder.setPersistentQuery(persistentQuery);
295
311
  this.updateWindowTitle(persistentQuery);
296
312
 
297
313
  this.saverDisplay.close();
@@ -29,7 +29,7 @@ export class AdhocQueryDataCubeSourceBuilderState extends LegendDataCubeSourceBu
29
29
  return false;
30
30
  }
31
31
 
32
- override build(): Promise<PlainObject> {
32
+ override generateSourceData(): Promise<PlainObject> {
33
33
  throw new Error('Method not implemented.');
34
34
  }
35
35
  }
@@ -14,9 +14,10 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import { ActionState, type PlainObject } from '@finos/legend-shared';
17
+ import { type PlainObject } from '@finos/legend-shared';
18
18
  import type { LegendDataCubeApplicationStore } from '../../LegendDataCubeBaseStore.js';
19
19
  import type { LegendDataCubeDataCubeEngine } from '../../LegendDataCubeDataCubeEngine.js';
20
+ import type { DataCubeConfiguration } from '@finos/legend-data-cube';
20
21
 
21
22
  export enum LegendDataCubeSourceBuilderType {
22
23
  LEGEND_QUERY = 'Legend Query',
@@ -27,8 +28,6 @@ export abstract class LegendDataCubeSourceBuilderState {
27
28
  protected readonly _application: LegendDataCubeApplicationStore;
28
29
  protected readonly _engine: LegendDataCubeDataCubeEngine;
29
30
 
30
- readonly buildState = ActionState.create();
31
-
32
31
  constructor(
33
32
  application: LegendDataCubeApplicationStore,
34
33
  engine: LegendDataCubeDataCubeEngine,
@@ -39,5 +38,10 @@ export abstract class LegendDataCubeSourceBuilderState {
39
38
 
40
39
  abstract get label(): LegendDataCubeSourceBuilderType;
41
40
  abstract get isValid(): boolean;
42
- abstract build(): Promise<PlainObject>;
41
+ abstract generateSourceData(): Promise<PlainObject>;
42
+
43
+ /* Modifies the configuration of the finalized DataCube query based on the source builder */
44
+ finalizeConfiguration(configuration: DataCubeConfiguration) {
45
+ // do nothing
46
+ }
43
47
  }
@@ -39,7 +39,10 @@ import { RawLegendQueryDataCubeSource } from '../../model/LegendQueryDataCubeSou
39
39
  import { APPLICATION_EVENT } from '@finos/legend-application';
40
40
  import type { LegendDataCubeDataCubeEngine } from '../../LegendDataCubeDataCubeEngine.js';
41
41
  import type { LegendDataCubeApplicationStore } from '../../LegendDataCubeBaseStore.js';
42
- import type { DataCubeAlertService } from '@finos/legend-data-cube';
42
+ import type {
43
+ DataCubeAlertService,
44
+ DataCubeConfiguration,
45
+ } from '@finos/legend-data-cube';
43
46
 
44
47
  export class LegendQueryDataCubeSourceBuilderState extends LegendDataCubeSourceBuilderState {
45
48
  private readonly _engineServerClient: V1_EngineServerClient;
@@ -132,7 +135,7 @@ export class LegendQueryDataCubeSourceBuilderState extends LegendDataCubeSourceB
132
135
  return Boolean(this.query);
133
136
  }
134
137
 
135
- override async build() {
138
+ override async generateSourceData() {
136
139
  if (!this.query) {
137
140
  throw new IllegalStateError('Query is missing');
138
141
  }
@@ -140,4 +143,10 @@ export class LegendQueryDataCubeSourceBuilderState extends LegendDataCubeSourceB
140
143
  source.queryId = this.query.id;
141
144
  return RawLegendQueryDataCubeSource.serialization.toJson(source);
142
145
  }
146
+
147
+ override finalizeConfiguration(configuration: DataCubeConfiguration) {
148
+ if (this.query) {
149
+ configuration.name = this.query.name;
150
+ }
151
+ }
143
152
  }
package/tsconfig.json CHANGED
@@ -60,7 +60,9 @@
60
60
  "./src/application/LegendDataCubeApplicationConfig.ts",
61
61
  "./src/application/LegendDataCubeApplicationPlugin.ts",
62
62
  "./src/application/LegendDataCubePluginManager.ts",
63
+ "./src/stores/DuckDBWASM.d.ts",
63
64
  "./src/stores/LegendDataCubeBaseStore.ts",
65
+ "./src/stores/LegendDataCubeCacheManager.ts",
64
66
  "./src/stores/LegendDataCubeDataCubeEngine.ts",
65
67
  "./src/stores/model/LegendQueryDataCubeSource.ts",
66
68
  "./src/stores/query-builder/source-builder/AdhocQueryDataCubeSourceBuilderState.ts",