@finos/legend-application-studio 28.3.4 → 28.4.0

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 (55) hide show
  1. package/lib/application/LegendStudioApplicationConfig.d.ts +5 -0
  2. package/lib/application/LegendStudioApplicationConfig.d.ts.map +1 -1
  3. package/lib/application/LegendStudioApplicationConfig.js +7 -1
  4. package/lib/application/LegendStudioApplicationConfig.js.map +1 -1
  5. package/lib/components/editor/editor-group/FunctionEditor.d.ts.map +1 -1
  6. package/lib/components/editor/editor-group/FunctionEditor.js +1 -1
  7. package/lib/components/editor/editor-group/FunctionEditor.js.map +1 -1
  8. package/lib/components/editor/editor-group/data-editor/RelationalCSVDataEditor.d.ts +5 -0
  9. package/lib/components/editor/editor-group/data-editor/RelationalCSVDataEditor.d.ts.map +1 -1
  10. package/lib/components/editor/editor-group/mapping-editor/DEPRECATED__MappingTestEditor.d.ts.map +1 -1
  11. package/lib/components/editor/editor-group/mapping-editor/DEPRECATED__MappingTestEditor.js +1 -1
  12. package/lib/components/editor/editor-group/mapping-editor/DEPRECATED__MappingTestEditor.js.map +1 -1
  13. package/lib/components/editor/editor-group/mapping-editor/MappingExecutionBuilder.d.ts.map +1 -1
  14. package/lib/components/editor/editor-group/mapping-editor/MappingExecutionBuilder.js +1 -1
  15. package/lib/components/editor/editor-group/mapping-editor/MappingExecutionBuilder.js.map +1 -1
  16. package/lib/components/editor/editor-group/mapping-editor/MappingTestableEditor.d.ts.map +1 -1
  17. package/lib/components/editor/editor-group/mapping-editor/MappingTestableEditor.js +1 -1
  18. package/lib/components/editor/editor-group/mapping-editor/MappingTestableEditor.js.map +1 -1
  19. package/lib/components/editor/editor-group/service-editor/ServiceExecutionQueryEditor.d.ts +4 -1
  20. package/lib/components/editor/editor-group/service-editor/ServiceExecutionQueryEditor.d.ts.map +1 -1
  21. package/lib/components/editor/editor-group/service-editor/ServiceExecutionQueryEditor.js +52 -4
  22. package/lib/components/editor/editor-group/service-editor/ServiceExecutionQueryEditor.js.map +1 -1
  23. package/lib/components/editor/editor-group/service-editor/testable/ServiceTestDataEditor.d.ts +23 -1
  24. package/lib/components/editor/editor-group/service-editor/testable/ServiceTestDataEditor.d.ts.map +1 -1
  25. package/lib/components/editor/editor-group/service-editor/testable/ServiceTestDataEditor.js +101 -8
  26. package/lib/components/editor/editor-group/service-editor/testable/ServiceTestDataEditor.js.map +1 -1
  27. package/lib/components/editor/editor-group/uml-editor/ClassQueryBuilder.d.ts.map +1 -1
  28. package/lib/components/editor/editor-group/uml-editor/ClassQueryBuilder.js +1 -1
  29. package/lib/components/editor/editor-group/uml-editor/ClassQueryBuilder.js.map +1 -1
  30. package/lib/components/editor/side-bar/Explorer.d.ts.map +1 -1
  31. package/lib/components/editor/side-bar/Explorer.js +8 -2
  32. package/lib/components/editor/side-bar/Explorer.js.map +1 -1
  33. package/lib/index.css +1 -1
  34. package/lib/package.json +8 -8
  35. package/lib/stores/editor/editor-state/element-editor-state/mapping/MappingExecutionQueryBuilderState.d.ts +2 -2
  36. package/lib/stores/editor/editor-state/element-editor-state/mapping/MappingExecutionQueryBuilderState.d.ts.map +1 -1
  37. package/lib/stores/editor/editor-state/element-editor-state/mapping/MappingExecutionQueryBuilderState.js +3 -3
  38. package/lib/stores/editor/editor-state/element-editor-state/mapping/MappingExecutionQueryBuilderState.js.map +1 -1
  39. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestDataState.d.ts +38 -1
  40. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestDataState.d.ts.map +1 -1
  41. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestDataState.js +195 -6
  42. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestDataState.js.map +1 -1
  43. package/package.json +19 -19
  44. package/src/application/LegendStudioApplicationConfig.ts +10 -0
  45. package/src/components/editor/editor-group/FunctionEditor.tsx +1 -0
  46. package/src/components/editor/editor-group/data-editor/RelationalCSVDataEditor.tsx +1 -1
  47. package/src/components/editor/editor-group/mapping-editor/DEPRECATED__MappingTestEditor.tsx +1 -0
  48. package/src/components/editor/editor-group/mapping-editor/MappingExecutionBuilder.tsx +1 -0
  49. package/src/components/editor/editor-group/mapping-editor/MappingTestableEditor.tsx +1 -0
  50. package/src/components/editor/editor-group/service-editor/ServiceExecutionQueryEditor.tsx +107 -7
  51. package/src/components/editor/editor-group/service-editor/testable/ServiceTestDataEditor.tsx +296 -5
  52. package/src/components/editor/editor-group/uml-editor/ClassQueryBuilder.tsx +1 -0
  53. package/src/components/editor/side-bar/Explorer.tsx +17 -0
  54. package/src/stores/editor/editor-state/element-editor-state/mapping/MappingExecutionQueryBuilderState.ts +6 -2
  55. package/src/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestDataState.ts +337 -11
@@ -25,6 +25,13 @@ import {
25
25
  type Mapping,
26
26
  type TestDataGenerationResult,
27
27
  type GraphManagerState,
28
+ type Column,
29
+ type ValueSpecification,
30
+ type Table,
31
+ type PrimitiveInstanceValue,
32
+ type ObserverContext,
33
+ type PureModel,
34
+ type RelationalDataType,
28
35
  ConnectionTestData,
29
36
  PureSingleExecution,
30
37
  PureMultiExecution,
@@ -41,6 +48,10 @@ import {
41
48
  Database,
42
49
  RuntimePointer,
43
50
  buildRawLambdaFromLambdaFunction,
51
+ TableRowIdentifiers,
52
+ RowIdentifier,
53
+ ColumnValuePair,
54
+ TablePtr,
44
55
  } from '@finos/legend-graph';
45
56
  import {
46
57
  type GeneratorFn,
@@ -53,6 +64,7 @@ import {
53
64
  getNullableFirstEntry,
54
65
  assertType,
55
66
  assertTrue,
67
+ uuid,
56
68
  } from '@finos/legend-shared';
57
69
  import { action, flow, flowResult, makeObservable, observable } from 'mobx';
58
70
  import type { EditorStore } from '../../../../EditorStore.js';
@@ -83,7 +95,9 @@ import {
83
95
  buildExecutionParameterValues,
84
96
  buildParametersLetLambdaFunc,
85
97
  getExecutionQueryFromRawLambda,
98
+ createMockPrimitiveValueSpecification,
86
99
  } from '@finos/legend-query-builder';
100
+ import { getPrimitiveTypeFromRelationalType } from '../../../../utils/MockDataUtils.js';
87
101
 
88
102
  export const createConnectionTestData = (
89
103
  val: IdentifiedConnection,
@@ -97,6 +111,24 @@ export const createConnectionTestData = (
97
111
  return connectionTestData;
98
112
  };
99
113
 
114
+ export const createMockPrimitiveValueSpecificationFromRelationalDataType = (
115
+ relationalDataType: RelationalDataType,
116
+ graph: PureModel,
117
+ observerContext: ObserverContext,
118
+ ): ValueSpecification | undefined => {
119
+ const primitiveTypeFromRelational =
120
+ getPrimitiveTypeFromRelationalType(relationalDataType);
121
+
122
+ if (primitiveTypeFromRelational) {
123
+ return createMockPrimitiveValueSpecification(
124
+ primitiveTypeFromRelational,
125
+ graph,
126
+ observerContext,
127
+ );
128
+ }
129
+ return undefined;
130
+ };
131
+
100
132
  export class ServiceTestDataParametersState extends LambdaParametersState {
101
133
  connectionTestDataState: ConnectionTestDataState;
102
134
 
@@ -111,24 +143,38 @@ export class ServiceTestDataParametersState extends LambdaParametersState {
111
143
  this.connectionTestDataState = connectionTestDataState;
112
144
  }
113
145
 
114
- openModal(serviceExecutionParameters: {
115
- query: RawLambda;
116
- mapping: Mapping;
117
- runtime: Runtime;
118
- }): void {
146
+ openModal(
147
+ serviceExecutionParameters: {
148
+ query: RawLambda;
149
+ mapping: Mapping;
150
+ runtime: Runtime;
151
+ },
152
+ option?: {
153
+ generateWithSeedData: boolean;
154
+ },
155
+ ): void {
119
156
  this.parameterStates = this.build(serviceExecutionParameters.query);
120
- this.parameterValuesEditorState.open(
121
- (): Promise<void> =>
122
- flowResult(
157
+ this.parameterValuesEditorState.open((): Promise<void> => {
158
+ if (option?.generateWithSeedData) {
159
+ return flowResult(
160
+ this.connectionTestDataState.generateTestDataWithSeedDataForDatabaseConnection(
161
+ serviceExecutionParameters,
162
+ ),
163
+ ).catch(
164
+ this.connectionTestDataState.editorStore.applicationStore
165
+ .alertUnhandledError,
166
+ );
167
+ } else {
168
+ return flowResult(
123
169
  this.connectionTestDataState.generateTestDataForDatabaseConnection(
124
170
  serviceExecutionParameters,
125
171
  ),
126
172
  ).catch(
127
173
  this.connectionTestDataState.editorStore.applicationStore
128
174
  .alertUnhandledError,
129
- ),
130
- PARAMETER_SUBMIT_ACTION.RUN,
131
- );
175
+ );
176
+ }
177
+ }, PARAMETER_SUBMIT_ACTION.RUN);
132
178
  }
133
179
 
134
180
  build(query: RawLambda): LambdaParameterState[] {
@@ -157,14 +203,107 @@ export class ServiceTestDataParametersState extends LambdaParametersState {
157
203
  }
158
204
  }
159
205
 
206
+ export class RowIdentifierState {
207
+ readonly connectionTestDataState: ConnectionTestDataState;
208
+ readonly _UUID = uuid();
209
+ column: Column;
210
+ value: ValueSpecification;
211
+
212
+ constructor(
213
+ connectionTestDataState: ConnectionTestDataState,
214
+ column: Column,
215
+ value: ValueSpecification,
216
+ ) {
217
+ makeObservable(this, {
218
+ column: observable,
219
+ value: observable,
220
+ updateRowIdentifierColumn: action,
221
+ updateRowIdentifierValue: action,
222
+ });
223
+ this.connectionTestDataState = connectionTestDataState;
224
+ this.column = column;
225
+ this.value = value;
226
+ }
227
+
228
+ updateRowIdentifierColumn(col: Column): void {
229
+ this.column = col;
230
+ }
231
+
232
+ updateRowIdentifierValue(val: ValueSpecification): void {
233
+ this.value = observe_ValueSpecification(
234
+ val,
235
+ this.connectionTestDataState.editorStore.changeDetectionState
236
+ .observerContext,
237
+ );
238
+ }
239
+ }
240
+
241
+ export class TableRowIdentifierState {
242
+ readonly connectionTestDataState: ConnectionTestDataState;
243
+ readonly _UUID = uuid();
244
+ table: Table;
245
+ rowIdentifierStates: RowIdentifierState[] = [];
246
+
247
+ constructor(
248
+ connectionTestDataState: ConnectionTestDataState,
249
+ table: Table,
250
+ rowIdentifierStates: RowIdentifierState[],
251
+ ) {
252
+ makeObservable(this, {
253
+ table: observable,
254
+ rowIdentifierStates: observable,
255
+ updateTable: action,
256
+ setNewRowIdentifierState: action,
257
+ addNewRowIdentifierState: action,
258
+ removeRowIdentifierState: action,
259
+ });
260
+ this.connectionTestDataState = connectionTestDataState;
261
+ this.table = table;
262
+ this.rowIdentifierStates = rowIdentifierStates;
263
+ }
264
+
265
+ updateTable(table: Table): void {
266
+ this.table = table;
267
+ }
268
+
269
+ setNewRowIdentifierState(rowIdentifierStates: RowIdentifierState[]): void {
270
+ this.rowIdentifierStates = rowIdentifierStates;
271
+ }
272
+
273
+ addNewRowIdentifierState(column: Column): void {
274
+ const valueSpec =
275
+ createMockPrimitiveValueSpecificationFromRelationalDataType(
276
+ guaranteeNonNullable(column.type),
277
+ this.connectionTestDataState.editorStore.graphManagerState.graph,
278
+ this.connectionTestDataState.editorStore.changeDetectionState
279
+ .observerContext,
280
+ );
281
+ if (valueSpec) {
282
+ const rowIdentifierState = new RowIdentifierState(
283
+ this.connectionTestDataState,
284
+ column,
285
+ valueSpec,
286
+ );
287
+ this.rowIdentifierStates.push(rowIdentifierState);
288
+ }
289
+ }
290
+
291
+ removeRowIdentifierState(rowIdentifierState: RowIdentifierState): void {
292
+ deleteEntry(this.rowIdentifierStates, rowIdentifierState);
293
+ }
294
+ }
295
+
160
296
  export class ConnectionTestDataState {
161
297
  readonly editorStore: EditorStore;
162
298
  readonly testDataState: ServiceTestDataState;
163
299
  readonly connectionData: ConnectionTestData;
164
300
  readonly parametersState: ServiceTestDataParametersState;
165
301
  readonly generatingTestDataState = ActionState.create();
302
+ readonly generatingTestDataWithSeedDataState = ActionState.create();
166
303
  readonly generateSchemaQueryState = ActionState.create();
167
304
  useSharedModal = false;
305
+ useSeedDataInputModal = false;
306
+ tableRowIdentifierStates: TableRowIdentifierState[] = [];
168
307
 
169
308
  embeddedEditorState: EmbeddedDataEditorState;
170
309
  anonymizeGeneratedData = true;
@@ -174,17 +313,26 @@ export class ConnectionTestDataState {
174
313
  connectionData: ConnectionTestData,
175
314
  ) {
176
315
  makeObservable(this, {
316
+ tableRowIdentifierStates: observable,
177
317
  generatingTestDataState: observable,
318
+ generatingTestDataWithSeedDataState: observable,
178
319
  generateSchemaQueryState: observable,
179
320
  embeddedEditorState: observable,
180
321
  useSharedModal: observable,
322
+ useSeedDataInputModal: observable,
181
323
  anonymizeGeneratedData: observable,
182
324
  setAnonymizeGeneratedData: action,
325
+ setUseSeedDataInputModal: action,
326
+ setNewTableIdentifierState: action,
327
+ addNewTableIdentifierState: action,
328
+ removeTableIdentifierState: action,
183
329
  changeEmbeddedData: action,
184
330
  buildEmbeddedEditorState: action,
185
331
  createExecutableQuery: action,
186
332
  generateTestData: flow,
187
333
  generateTestDataForDatabaseConnection: flow,
334
+ generateTestDataWithSeedData: flow,
335
+ generateTestDataWithSeedDataForDatabaseConnection: flow,
188
336
  generateQuerySchemas: flow,
189
337
  });
190
338
  this.testDataState = testDataState;
@@ -214,10 +362,50 @@ export class ConnectionTestDataState {
214
362
  }
215
363
  }
216
364
 
365
+ getAvailableTables(): Table[] {
366
+ const runtime =
367
+ this.testDataState.testSuiteState.testableState.serviceEditorState
368
+ .executionState.serviceExecutionParameters?.runtime;
369
+ if (runtime instanceof RuntimePointer) {
370
+ const db =
371
+ runtime.packageableRuntime.value.runtimeValue.connections[0]?.store
372
+ .value;
373
+ if (db instanceof Database) {
374
+ return db.schemas.flatMap((schema) => schema.tables);
375
+ }
376
+ }
377
+ return [];
378
+ }
379
+
217
380
  setUseSharedModal(val: boolean): void {
218
381
  this.useSharedModal = val;
219
382
  }
220
383
 
384
+ setUseSeedDataInputModal(val: boolean): void {
385
+ this.useSeedDataInputModal = val;
386
+ }
387
+
388
+ setNewTableIdentifierState(
389
+ tableRowIdentifierStates: TableRowIdentifierState[],
390
+ ): void {
391
+ this.tableRowIdentifierStates = tableRowIdentifierStates;
392
+ }
393
+
394
+ addNewTableIdentifierState(table: Table): void {
395
+ const tableRowIdentifierState = new TableRowIdentifierState(
396
+ this,
397
+ table,
398
+ [],
399
+ );
400
+ this.tableRowIdentifierStates.push(tableRowIdentifierState);
401
+ }
402
+
403
+ removeTableIdentifierState(
404
+ tableRowIdentifierState: TableRowIdentifierState,
405
+ ): void {
406
+ deleteEntry(this.tableRowIdentifierStates, tableRowIdentifierState);
407
+ }
408
+
221
409
  setAnonymizeGeneratedData(val: boolean): void {
222
410
  this.anonymizeGeneratedData = val;
223
411
  }
@@ -337,6 +525,144 @@ export class ConnectionTestDataState {
337
525
  }
338
526
  }
339
527
 
528
+ *generateTestDataWithSeedDataForDatabaseConnection(serviceExecutionParameters: {
529
+ query: RawLambda;
530
+ mapping: Mapping;
531
+ runtime: Runtime;
532
+ }): GeneratorFn<void> {
533
+ try {
534
+ this.generatingTestDataWithSeedDataState.inProgress();
535
+ // NOTE: since we don't have a generic mechanism for test-data generation
536
+ // we will only report metrics around API usage, when we genericize, we will
537
+ // move this out
538
+ LegendStudioTelemetryHelper.logEvent_TestDataGenerationLaunched(
539
+ this.testDataState.editorStore.applicationStore.telemetryService,
540
+ );
541
+ const report = reportGraphAnalytics(
542
+ this.editorStore.graphManagerState.graph,
543
+ );
544
+ const tableRowIdentifiers = this.tableRowIdentifierStates.map(
545
+ (tableRowIdentifierState) => {
546
+ const rowIdentifiersArray =
547
+ tableRowIdentifierState.rowIdentifierStates.map(
548
+ (rowIdentifierState) =>
549
+ new RowIdentifier([
550
+ new ColumnValuePair(
551
+ rowIdentifierState.column.name,
552
+ (rowIdentifierState.value as PrimitiveInstanceValue)
553
+ .values[0] as object, // to do : change once engine code is in place
554
+ ),
555
+ ]),
556
+ );
557
+ const tablePtr = new TablePtr();
558
+ tablePtr.database = tableRowIdentifierState.table.schema._OWNER.path;
559
+ tablePtr.mainTableDb = tablePtr.database;
560
+ tablePtr.schema = tableRowIdentifierState.table.schema.name;
561
+ tablePtr.table = tableRowIdentifierState.table.name;
562
+ return new TableRowIdentifiers(tablePtr, rowIdentifiersArray);
563
+ },
564
+ );
565
+ const value =
566
+ (yield this.editorStore.graphManagerState.graphManager.generateExecuteTestDataWithSeedData(
567
+ getExecutionQueryFromRawLambda(
568
+ serviceExecutionParameters.query,
569
+ this.parametersState.parameterStates,
570
+ this.editorStore.graphManagerState,
571
+ ),
572
+ tableRowIdentifiers,
573
+ serviceExecutionParameters.mapping,
574
+ serviceExecutionParameters.runtime,
575
+ this.editorStore.graphManagerState.graph,
576
+ {
577
+ anonymizeGeneratedData: this.anonymizeGeneratedData,
578
+ parameterValues: buildExecutionParameterValues(
579
+ this.parametersState.parameterStates,
580
+ this.editorStore.graphManagerState,
581
+ ),
582
+ },
583
+ report,
584
+ )) as string;
585
+
586
+ // NOTE: since we don't have a generic mechanism for test-data generation
587
+ // we will only report metrics around API usage, when we genericize, we will
588
+ // move this out
589
+ LegendStudioTelemetryHelper.logEvent_TestDataGenerationSucceeded(
590
+ this.editorStore.applicationStore.telemetryService,
591
+ report,
592
+ );
593
+ service_setConnectionTestDataEmbeddedData(
594
+ this.connectionData,
595
+ TEMPORARY__createRelationalDataFromCSV(value),
596
+ this.editorStore.changeDetectionState.observerContext,
597
+ );
598
+ this.embeddedEditorState = new EmbeddedDataEditorState(
599
+ this.testDataState.editorStore,
600
+ this.connectionData.testData,
601
+ );
602
+ this.generatingTestDataWithSeedDataState.pass();
603
+ } catch (error) {
604
+ assertErrorThrown(error);
605
+ this.editorStore.applicationStore.notificationService.notifyError(
606
+ `Unable to generate test data: ${error.message}`,
607
+ );
608
+ this.generatingTestDataWithSeedDataState.fail();
609
+ } finally {
610
+ this.generatingTestDataWithSeedDataState.complete();
611
+ }
612
+ }
613
+
614
+ *generateTestDataWithSeedData(): GeneratorFn<void> {
615
+ try {
616
+ this.generatingTestDataWithSeedDataState.inProgress();
617
+ const connection = guaranteeNonNullable(
618
+ this.resolveConnectionValue(this.connectionData.connectionId),
619
+ `Unable to resolve connection ID '${this.connectionData.connectionId}`,
620
+ );
621
+ if (connection instanceof DatabaseConnection) {
622
+ const serviceExecutionParameters = guaranteeNonNullable(
623
+ this.testDataState.testSuiteState.testableState.serviceEditorState
624
+ .executionState.serviceExecutionParameters,
625
+ );
626
+ const parameters = (serviceExecutionParameters.query.parameters ??
627
+ []) as object[];
628
+ if (parameters.length > 0) {
629
+ this.parametersState.openModal(serviceExecutionParameters, {
630
+ generateWithSeedData: true,
631
+ });
632
+ return;
633
+ } else {
634
+ yield flowResult(
635
+ this.generateTestDataWithSeedDataForDatabaseConnection(
636
+ serviceExecutionParameters,
637
+ ),
638
+ );
639
+ }
640
+ } else {
641
+ // TODO: delete this once the backend code is in place
642
+ service_setConnectionTestDataEmbeddedData(
643
+ this.connectionData,
644
+ connection.accept_ConnectionVisitor(
645
+ new TEMPORARY__EmbeddedDataConnectionVisitor(this.editorStore),
646
+ ),
647
+ this.editorStore.changeDetectionState.observerContext,
648
+ );
649
+ }
650
+ this.embeddedEditorState = new EmbeddedDataEditorState(
651
+ this.testDataState.editorStore,
652
+ this.connectionData.testData,
653
+ );
654
+ this.generatingTestDataWithSeedDataState.pass();
655
+ } catch (error) {
656
+ assertErrorThrown(error);
657
+ this.editorStore.applicationStore.notificationService.notifyError(
658
+ `Unable to generate test data with seed data: ${error.message}`,
659
+ );
660
+ this.generatingTestDataWithSeedDataState.fail();
661
+ } finally {
662
+ this.generatingTestDataWithSeedDataState.complete();
663
+ }
664
+ }
665
+
340
666
  *generateQuerySchemas(): GeneratorFn<void> {
341
667
  try {
342
668
  this.generateSchemaQueryState.inProgress();