@finos/legend-application-studio 28.21.4 → 28.21.5

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/components/editor/editor-group/data-editor/EmbeddedDataEditor.d.ts +1 -1
  2. package/lib/components/editor/editor-group/data-editor/EmbeddedDataEditor.d.ts.map +1 -1
  3. package/lib/components/editor/editor-group/data-editor/EmbeddedDataEditor.js +3 -3
  4. package/lib/components/editor/editor-group/data-editor/EmbeddedDataEditor.js.map +1 -1
  5. package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.d.ts +3 -0
  6. package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.d.ts.map +1 -1
  7. package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.js +12 -35
  8. package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.js.map +1 -1
  9. package/lib/components/editor/editor-group/dataProduct/DataProductEditor.d.ts.map +1 -1
  10. package/lib/components/editor/editor-group/dataProduct/DataProductEditor.js +19 -6
  11. package/lib/components/editor/editor-group/dataProduct/DataProductEditor.js.map +1 -1
  12. package/lib/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.d.ts.map +1 -1
  13. package/lib/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.js +59 -22
  14. package/lib/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.js.map +1 -1
  15. package/lib/components/editor/editor-group/function-activator/testable/FunctionTestableEditor.d.ts.map +1 -1
  16. package/lib/components/editor/editor-group/function-activator/testable/FunctionTestableEditor.js +113 -75
  17. package/lib/components/editor/editor-group/function-activator/testable/FunctionTestableEditor.js.map +1 -1
  18. package/lib/components/editor/editor-group/testable/TestableSharedComponents.d.ts.map +1 -1
  19. package/lib/components/editor/editor-group/testable/TestableSharedComponents.js +1 -1
  20. package/lib/components/editor/editor-group/testable/TestableSharedComponents.js.map +1 -1
  21. package/lib/components/editor/side-bar/DevMetadataPanel.d.ts.map +1 -1
  22. package/lib/components/editor/side-bar/DevMetadataPanel.js +37 -6
  23. package/lib/components/editor/side-bar/DevMetadataPanel.js.map +1 -1
  24. package/lib/index.css +2 -2
  25. package/lib/index.css.map +1 -1
  26. package/lib/package.json +1 -1
  27. package/lib/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.d.ts +1 -1
  28. package/lib/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.d.ts.map +1 -1
  29. package/lib/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.js +20 -48
  30. package/lib/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.js.map +1 -1
  31. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.d.ts +9 -14
  32. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.d.ts.map +1 -1
  33. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.js +125 -78
  34. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.js.map +1 -1
  35. package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.d.ts +18 -4
  36. package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.d.ts.map +1 -1
  37. package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.js +214 -53
  38. package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.js.map +1 -1
  39. package/lib/stores/editor/sidebar-state/dev-metadata/DevMetadataState.d.ts +9 -0
  40. package/lib/stores/editor/sidebar-state/dev-metadata/DevMetadataState.d.ts.map +1 -1
  41. package/lib/stores/editor/sidebar-state/dev-metadata/DevMetadataState.js +55 -0
  42. package/lib/stores/editor/sidebar-state/dev-metadata/DevMetadataState.js.map +1 -1
  43. package/package.json +16 -16
  44. package/src/components/editor/editor-group/data-editor/EmbeddedDataEditor.tsx +3 -0
  45. package/src/components/editor/editor-group/data-editor/RelationElementsDataEditor.tsx +200 -186
  46. package/src/components/editor/editor-group/dataProduct/DataProductEditor.tsx +25 -7
  47. package/src/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.tsx +149 -86
  48. package/src/components/editor/editor-group/function-activator/testable/FunctionTestableEditor.tsx +425 -308
  49. package/src/components/editor/editor-group/testable/TestableSharedComponents.tsx +2 -11
  50. package/src/components/editor/side-bar/DevMetadataPanel.tsx +194 -10
  51. package/src/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.ts +28 -50
  52. package/src/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.ts +164 -100
  53. package/src/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.ts +303 -72
  54. package/src/stores/editor/sidebar-state/dev-metadata/DevMetadataState.ts +76 -0
@@ -26,6 +26,7 @@ import {
26
26
  LakehouseAccessPoint,
27
27
  DataProductTestSuite,
28
28
  BaseDataResolver,
29
+ ReferenceDataResolver,
29
30
  DataProductAccessPointTest,
30
31
  RelationElementsData,
31
32
  RelationElement,
@@ -34,11 +35,14 @@ import {
34
35
  TestError,
35
36
  TestExecutionStatus,
36
37
  EqualToRelation,
38
+ RelationRowTestData,
37
39
  observe_RelationElement,
40
+ observe_RelationRowTestData,
38
41
  observe_RelationElementsData,
39
42
  observe_DataProductTestSuite,
40
43
  IngestDefinition,
41
44
  getAccessorItemLabelForElement,
45
+ type AbstractPureGraphManager,
42
46
  } from '@finos/legend-graph';
43
47
  import {
44
48
  type GeneratorFn,
@@ -59,7 +63,10 @@ import {
59
63
  } from 'mobx';
60
64
  import type { EditorStore } from '../../../../EditorStore.js';
61
65
  import type { DataProductEditorState } from '../DataProductEditorState.js';
62
- import { RelationElementState } from '../../data/EmbeddedDataState.js';
66
+ import {
67
+ RelationElementsDataState,
68
+ RelationElementState,
69
+ } from '../../data/EmbeddedDataState.js';
63
70
  import { TESTABLE_RESULT } from '../../../../sidebar-state/testable/GlobalTestRunnerState.js';
64
71
  import { testSuite_addTest } from '../../../../../graph-modifier/Testable_GraphModifierHelper.js';
65
72
  import {
@@ -71,10 +78,12 @@ const createEmptyRelationElement = (
71
78
  itemId: string,
72
79
  columns: string[] = [],
73
80
  ): RelationElement => {
81
+ const row = observe_RelationRowTestData(new RelationRowTestData());
82
+ row.values = columns.map(() => '');
74
83
  const relationElement = new RelationElement();
75
84
  relationElement.paths = [itemId];
76
85
  relationElement.columns = columns;
77
- relationElement.rows = [];
86
+ relationElement.rows = [row];
78
87
  return observe_RelationElement(relationElement);
79
88
  };
80
89
 
@@ -123,6 +132,7 @@ interface ElementDataItem {
123
132
 
124
133
  const getElementDataItems = (
125
134
  element: PackageableElement,
135
+ graphManager: AbstractPureGraphManager,
126
136
  ): ElementDataItem[] => {
127
137
  if (element instanceof DataProduct) {
128
138
  return element.accessPointGroups
@@ -133,10 +143,12 @@ const getElementDataItems = (
133
143
  }));
134
144
  }
135
145
  if (element instanceof IngestDefinition) {
136
- return (element.TEMPORARY_MATVIEW_FUNCTION_DATA_SETS ?? []).map((ds) => ({
137
- id: ds.name,
138
- label: ds.name,
139
- }));
146
+ return graphManager
147
+ .getIngestDefinitionDatasetNames(element)
148
+ .map((name) => ({
149
+ id: name,
150
+ label: name,
151
+ }));
140
152
  }
141
153
  return [];
142
154
  };
@@ -252,31 +264,21 @@ export class DataProductElementTestDataState {
252
264
  readonly testDataState: DataProductTestDataState;
253
265
  readonly testData: BaseDataResolver;
254
266
  readonly editorStore: EditorStore;
255
-
256
- /** Currently selected dataset/item within this element's RelationElementsData */
257
- selectedItemId: string | undefined;
258
- /** RelationElementState for the currently selected item */
259
- relationElementState: RelationElementState | undefined;
267
+ readonly relationElementsDataState: RelationElementsDataState | undefined;
260
268
 
261
269
  constructor(
262
270
  testDataState: DataProductTestDataState,
263
271
  testData: BaseDataResolver,
264
272
  ) {
265
- makeObservable(this, {
266
- selectedItemId: observable,
267
- relationElementState: observable,
268
- setSelectedItem: action,
269
- });
270
273
  this.testDataState = testDataState;
271
274
  this.testData = testData;
272
275
  this.editorStore = testDataState.editorStore;
273
- // Select the first item that has data
274
276
  if (testData.data instanceof RelationElementsData) {
275
- const firstRel = testData.data.relationElements[0];
276
- if (firstRel) {
277
- this.selectedItemId = firstRel.paths[0];
278
- this.relationElementState = new RelationElementState(firstRel);
279
- }
277
+ this.relationElementsDataState = new RelationElementsDataState(
278
+ this.editorStore,
279
+ testData.data,
280
+ );
281
+ this.initAccessorOptions();
280
282
  }
281
283
  }
282
284
 
@@ -292,35 +294,81 @@ export class DataProductElementTestDataState {
292
294
  return getAccessorItemLabelForElement(this.element as AccessorOwner);
293
295
  }
294
296
 
295
- get configuredItemIds(): string[] {
296
- if (this.testData.data instanceof RelationElementsData) {
297
- return this.testData.data.relationElements.map((re) => re.paths[0] ?? '');
297
+ private initAccessorOptions(): void {
298
+ const dataState = this.relationElementsDataState;
299
+ if (!dataState) {
300
+ return;
298
301
  }
299
- return [];
300
- }
301
-
302
- get configuredItems(): ElementDataItem[] {
303
- const availableItemsById = new Map(
304
- getElementDataItems(this.element).map((item) => [item.id, item.label]),
302
+ this.refreshAccessorOptions(dataState).catch(noop);
303
+ dataState.setRefreshAccessorOptions(() =>
304
+ this.refreshAccessorOptions(dataState),
305
305
  );
306
- return this.configuredItemIds.map((id) => ({
307
- id,
308
- label: availableItemsById.get(id) ?? id,
309
- }));
310
306
  }
311
307
 
312
- setSelectedItem(itemId: string | undefined): void {
313
- this.selectedItemId = itemId;
314
- if (itemId && this.testData.data instanceof RelationElementsData) {
315
- const relEl = this.testData.data.relationElements.find(
316
- (re) => re.paths[0] === itemId,
317
- );
318
- this.relationElementState = relEl
319
- ? new RelationElementState(relEl)
320
- : undefined;
321
- } else {
322
- this.relationElementState = undefined;
308
+ private async refreshAccessorOptions(
309
+ dataState: RelationElementsDataState,
310
+ ): Promise<void> {
311
+ const element = this.element;
312
+ const graphManager = this.editorStore.graphManagerState.graphManager;
313
+ const graph = this.editorStore.graphManagerState.graph;
314
+ const items = getElementDataItems(element, graphManager);
315
+ if (items.length === 0) {
316
+ dataState.setAccessorOptions(undefined, undefined);
317
+ return;
323
318
  }
319
+ const typeLabel = this.itemLabel;
320
+ const options = await Promise.all(
321
+ items.map(async (item) => {
322
+ let columns: string[] = [];
323
+ try {
324
+ if (element instanceof IngestDefinition) {
325
+ const accessor = graphManager.createAccessorFromPackageableElement(
326
+ element,
327
+ graph,
328
+ { schemaName: undefined, tableName: item.id },
329
+ );
330
+ if (accessor) {
331
+ columns = accessor.relationType.columns.map((c) => c.name);
332
+ }
333
+ } else if (element instanceof DataProduct) {
334
+ const accessor = await graphManager.buildDataProductAccessor(
335
+ element,
336
+ graph,
337
+ { tableName: item.id },
338
+ );
339
+ if (accessor) {
340
+ columns = accessor.relationType.columns.map((c) => c.name);
341
+ }
342
+ }
343
+ } catch {
344
+ // best-effort column resolution
345
+ }
346
+ return {
347
+ label: item.label,
348
+ value: item.id,
349
+ columns,
350
+ };
351
+ }),
352
+ );
353
+ runInAction(() => {
354
+ dataState.setAccessorOptions(options, typeLabel);
355
+ // Back-fill columns on existing relation elements that have none
356
+ const columnsByItem = new Map(
357
+ options
358
+ .filter((o) => o.columns.length > 0)
359
+ .map((o) => [o.value, o.columns]),
360
+ );
361
+ for (const relState of dataState.relationElementStates) {
362
+ const rel = relState.relationElement;
363
+ if (rel.columns.length === 0) {
364
+ const key = rel.paths[rel.paths.length - 1];
365
+ const cols = key ? columnsByItem.get(key) : undefined;
366
+ if (cols) {
367
+ rel.columns = cols;
368
+ }
369
+ }
370
+ }
371
+ });
324
372
  }
325
373
  }
326
374
 
@@ -332,6 +380,7 @@ export class DataProductTestDataState {
332
380
 
333
381
  elementTestDataStates: DataProductElementTestDataState[] = [];
334
382
  selectedElementTestDataState: DataProductElementTestDataState | undefined;
383
+ showAddElementModal = false;
335
384
 
336
385
  constructor(
337
386
  suiteState: DataProductTestSuiteState,
@@ -343,7 +392,11 @@ export class DataProductTestDataState {
343
392
  makeObservable(this, {
344
393
  elementTestDataStates: observable,
345
394
  selectedElementTestDataState: observable,
395
+ showAddElementModal: observable,
346
396
  setSelectedElementTestDataState: action,
397
+ setShowAddElementModal: action,
398
+ addElement: action,
399
+ deleteElement: action,
347
400
  refreshElementTestDataStates: action,
348
401
  });
349
402
  this.editorStore = suiteState.editorStore;
@@ -351,16 +404,64 @@ export class DataProductTestDataState {
351
404
  this.refreshElementTestDataStates(options);
352
405
  }
353
406
 
407
+ get availableElementsToAdd(): PackageableElement[] {
408
+ const suite = this.suiteState.suite;
409
+ const existingPaths = new Set(
410
+ (suite.testData ?? [])
411
+ .filter(
412
+ (td): td is BaseDataResolver | ReferenceDataResolver =>
413
+ td instanceof BaseDataResolver ||
414
+ td instanceof ReferenceDataResolver,
415
+ )
416
+ .map((td) => td.element.value.path),
417
+ );
418
+ const graph = this.editorStore.graphManagerState.graph;
419
+ const currentDpPath = this.suiteState.testableState.dataProduct.path;
420
+ const candidates: PackageableElement[] = [
421
+ ...graph.ingests,
422
+ ...graph.allElements.filter(
423
+ (e) => e instanceof DataProduct && e.path !== currentDpPath,
424
+ ),
425
+ ];
426
+ return candidates.filter((e) => !existingPaths.has(e.path));
427
+ }
428
+
429
+ setShowAddElementModal(val: boolean): void {
430
+ this.showAddElementModal = val;
431
+ }
432
+
433
+ addElement(path: string): void {
434
+ const element =
435
+ this.editorStore.graphManagerState.graph.getNullableElement(path);
436
+ if (!element) {
437
+ return;
438
+ }
439
+ const resolver = new BaseDataResolver();
440
+ resolver.element = PackageableElementExplicitReference.create(element);
441
+ const relData = new RelationElementsData();
442
+ relData.relationElements = [];
443
+ observe_RelationElementsData(relData);
444
+ resolver.data = relData;
445
+ const suite = this.suiteState.suite;
446
+ suite.testData = [...(suite.testData ?? []), resolver];
447
+ this.refreshElementTestDataStates({ selectedElementPath: path });
448
+ }
449
+
450
+ deleteElement(elementState: DataProductElementTestDataState): void {
451
+ const suite = this.suiteState.suite;
452
+ if (suite.testData) {
453
+ const idx = suite.testData.indexOf(elementState.testData);
454
+ suite.testData.splice(idx, 1);
455
+ }
456
+ this.refreshElementTestDataStates();
457
+ }
458
+
354
459
  refreshElementTestDataStates(options?: {
355
460
  selectedElementPath?: string | undefined;
356
- selectedItemId?: string | undefined;
357
461
  }): void {
358
462
  const previouslySelectedElementPath =
359
463
  options?.selectedElementPath ??
360
464
  this.selectedElementTestDataState?.element.path;
361
- const previouslySelectedItemId =
362
- options?.selectedItemId ??
363
- this.selectedElementTestDataState?.selectedItemId;
364
465
  const suite = this.suiteState.suite;
365
466
  this.elementTestDataStates = (suite.testData ?? [])
366
467
  .filter((td): td is BaseDataResolver => td instanceof BaseDataResolver)
@@ -370,14 +471,6 @@ export class DataProductTestDataState {
370
471
  this.elementTestDataStates.find(
371
472
  (state) => state.element.path === previouslySelectedElementPath,
372
473
  ) ?? this.elementTestDataStates[0];
373
-
374
- if (this.selectedElementTestDataState && previouslySelectedItemId) {
375
- const nextSelectedItemId =
376
- this.selectedElementTestDataState.configuredItemIds.find(
377
- (itemId) => itemId === previouslySelectedItemId,
378
- ) ?? this.selectedElementTestDataState.configuredItemIds[0];
379
- this.selectedElementTestDataState.setSelectedItem(nextSelectedItemId);
380
- }
381
474
  }
382
475
 
383
476
  setSelectedElementTestDataState(
@@ -427,8 +520,6 @@ export class DataProductTestSuiteState extends TestableTestSuiteEditorState {
427
520
  this.testDataState = new DataProductTestDataState(this, {
428
521
  selectedElementPath:
429
522
  this.testDataState.selectedElementTestDataState?.element.path,
430
- selectedItemId:
431
- this.testDataState.selectedElementTestDataState?.selectedItemId,
432
523
  });
433
524
  }
434
525
 
@@ -540,42 +631,19 @@ export class DataProductTestSuiteState extends TestableTestSuiteEditorState {
540
631
  }
541
632
  }
542
633
  } else {
543
- // Fallback: single resolver on current DP
544
- let relationData = this.suite.testData?.find(
545
- (td): td is BaseDataResolver =>
546
- td instanceof BaseDataResolver &&
547
- td.element.value === this.testableState.dataProduct &&
548
- td.data instanceof RelationElementsData,
549
- )?.data as RelationElementsData | undefined;
550
-
551
- if (!relationData) {
552
- const testData = new BaseDataResolver();
553
- testData.element = PackageableElementExplicitReference.create(
554
- this.testableState.dataProduct,
555
- );
556
- relationData = new RelationElementsData();
557
- relationData.relationElements = [];
558
- observe_RelationElementsData(relationData);
559
- testData.data = relationData;
560
- this.suite.testData = [...(this.suite.testData ?? []), testData];
561
- }
562
- if (
563
- !relationData.relationElements.find(
564
- (re) => re.paths[0] === accessPointId,
565
- )
566
- ) {
567
- relationData.relationElements.push(
568
- createEmptyRelationElement(accessPointId, inferredColumns),
569
- );
570
- }
634
+ this.editorStore.applicationStore.notificationService.notifyWarning(
635
+ 'Access Point accessors cannot be resolved',
636
+ );
571
637
  }
572
638
 
573
639
  const assertion = new EqualToRelation();
574
640
  assertion.id = 'assert_1';
575
641
  const expectedRelElement = new RelationElement();
642
+ const expectedRow = observe_RelationRowTestData(new RelationRowTestData());
643
+ expectedRow.values = inferredColumns.map(() => '');
576
644
  expectedRelElement.paths = [accessPointId];
577
645
  expectedRelElement.columns = inferredColumns;
578
- expectedRelElement.rows = [];
646
+ expectedRelElement.rows = [expectedRow];
579
647
  observe_RelationElement(expectedRelElement);
580
648
  assertion.expected = expectedRelElement;
581
649
  test.assertions = [assertion];
@@ -792,17 +860,11 @@ export class DataProductTestableState {
792
860
  suite.testData.push(resolver);
793
861
  }
794
862
  }
795
- // Fallback: no external sources resolved seed a single resolver on current DP
863
+ // If no external sources were resolved, notify the user and leave test data empty
796
864
  if (suite.testData.length === 0) {
797
- const testData = new BaseDataResolver();
798
- testData.element = PackageableElementExplicitReference.create(dp);
799
- const relData = new RelationElementsData();
800
- relData.relationElements = [
801
- createEmptyRelationElement(accessPointId, inferredColumns),
802
- ];
803
- observe_RelationElementsData(relData);
804
- testData.data = relData;
805
- suite.testData = [testData];
865
+ this.editorStore.applicationStore.notificationService.notifyWarning(
866
+ 'Access Point accessors cannot be resolved',
867
+ );
806
868
  }
807
869
 
808
870
  // Create one initial test with EqualToRelation assertion
@@ -814,9 +876,11 @@ export class DataProductTestableState {
814
876
  const assertion = new EqualToRelation();
815
877
  assertion.id = 'assert_1';
816
878
  const expectedRelElement = new RelationElement();
879
+ const expectedRow = observe_RelationRowTestData(new RelationRowTestData());
880
+ expectedRow.values = inferredColumns.map(() => '');
817
881
  expectedRelElement.paths = [accessPointId];
818
882
  expectedRelElement.columns = inferredColumns;
819
- expectedRelElement.rows = [];
883
+ expectedRelElement.rows = [expectedRow];
820
884
  observe_RelationElement(expectedRelElement);
821
885
  assertion.expected = expectedRelElement;
822
886
  test.assertions = [assertion];