@finos/legend-application-studio 28.19.59 → 28.19.60

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 (22) hide show
  1. package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.d.ts +7 -1
  2. package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.d.ts.map +1 -1
  3. package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.js +7 -7
  4. package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.js.map +1 -1
  5. package/lib/components/editor/editor-group/dataProduct/DataProductEditor.d.ts.map +1 -1
  6. package/lib/components/editor/editor-group/dataProduct/DataProductEditor.js +150 -6
  7. package/lib/components/editor/editor-group/dataProduct/DataProductEditor.js.map +1 -1
  8. package/lib/index.css +1 -1
  9. package/lib/package.json +1 -1
  10. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.d.ts +20 -1
  11. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.d.ts.map +1 -1
  12. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.js +186 -2
  13. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.js.map +1 -1
  14. package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.d.ts +2 -1
  15. package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.d.ts.map +1 -1
  16. package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.js +9 -0
  17. package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.js.map +1 -1
  18. package/package.json +8 -8
  19. package/src/components/editor/editor-group/data-editor/RelationElementsDataEditor.tsx +7 -7
  20. package/src/components/editor/editor-group/dataProduct/DataProductEditor.tsx +310 -4
  21. package/src/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.ts +277 -0
  22. package/src/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.ts +19 -0
@@ -52,13 +52,27 @@ import {
52
52
  type V1_DataProductArtifact,
53
53
  type V1_AccessPointGroupInfo,
54
54
  type V1_AccessPointImplementation,
55
+ RelationElementsData,
56
+ RelationElement,
57
+ observe_RelationElement,
58
+ observe_RelationElementsData,
59
+ V1_PureGraphManager,
60
+ V1_LambdaReturnTypeInput,
61
+ V1_transformRawLambda,
62
+ V1_GraphTransformerContextBuilder,
63
+ V1_relationTypeModelSchema,
64
+ V1_RemoteEngine,
65
+ DataElementReference,
66
+ DataElement,
55
67
  } from '@finos/legend-graph';
56
68
  import type { EditorStore } from '../../../EditorStore.js';
57
69
  import { ElementEditorState } from '../ElementEditorState.js';
70
+ import { RelationElementState } from '../data/EmbeddedDataState.js';
58
71
  import {
59
72
  action,
60
73
  computed,
61
74
  flow,
75
+ flowResult,
62
76
  makeObservable,
63
77
  observable,
64
78
  runInAction,
@@ -105,6 +119,7 @@ import type {
105
119
  AdhocDataProductDeployResponse,
106
120
  LakehouseIngestionManager,
107
121
  } from '@finos/legend-server-lakehouse';
122
+ import { deserialize } from 'serializr';
108
123
 
109
124
  export enum DATA_PRODUCT_TAB {
110
125
  HOME = 'Home',
@@ -145,6 +160,8 @@ export class AccessPointState {
145
160
  export class AccessPointLambdaEditorState extends LambdaEditorState {
146
161
  readonly editorStore: EditorStore;
147
162
  readonly val: LakehouseAccessPointState;
163
+ lambdaRelationColumns: string[] | undefined;
164
+ isUpdatingRelationColumns = false;
148
165
 
149
166
  constructor(val: LakehouseAccessPointState) {
150
167
  super('', LAMBDA_PIPE, {
@@ -152,6 +169,80 @@ export class AccessPointLambdaEditorState extends LambdaEditorState {
152
169
  });
153
170
  this.val = val;
154
171
  this.editorStore = val.state.state.editorStore;
172
+
173
+ makeObservable(this, {
174
+ lambdaRelationColumns: observable,
175
+ isUpdatingRelationColumns: observable,
176
+ setLambdaRelationColumns: action,
177
+ updateLambdaRelationColumns: flow,
178
+ });
179
+
180
+ flowResult(this.updateLambdaRelationColumns()).catch(
181
+ this.editorStore.applicationStore.alertUnhandledError,
182
+ );
183
+ }
184
+
185
+ setLambdaRelationColumns(columns: string[] | undefined): void {
186
+ this.lambdaRelationColumns = columns;
187
+ }
188
+
189
+ setIsUpdatingRelationColumns(value: boolean): void {
190
+ this.isUpdatingRelationColumns = value;
191
+ }
192
+
193
+ *updateLambdaRelationColumns(): GeneratorFn<void> {
194
+ // Prevent concurrent calls
195
+ if (this.isUpdatingRelationColumns) {
196
+ return;
197
+ }
198
+
199
+ this.setIsUpdatingRelationColumns(true);
200
+ const relationElement = this.val.relationElementState?.relationElement;
201
+ if (relationElement) {
202
+ this.val.deleteRelationElement();
203
+ }
204
+ try {
205
+ const model = guaranteeType(
206
+ this.editorStore.graphManagerState.graphManager,
207
+ V1_PureGraphManager,
208
+ ).getFullGraphModelData(this.editorStore.graphManagerState.graph);
209
+
210
+ const relationTypeInput = new V1_LambdaReturnTypeInput(
211
+ model,
212
+ V1_transformRawLambda(
213
+ this.val.accessPoint.func,
214
+ new V1_GraphTransformerContextBuilder(
215
+ this.editorStore.pluginManager.getPureProtocolProcessorPlugins(),
216
+ ).build(),
217
+ ),
218
+ );
219
+
220
+ const relationType = deserialize(
221
+ V1_relationTypeModelSchema,
222
+ (yield guaranteeType(
223
+ guaranteeType(
224
+ this.editorStore.graphManagerState.graphManager,
225
+ V1_PureGraphManager,
226
+ ).engine,
227
+ V1_RemoteEngine,
228
+ )
229
+ .getEngineServerClient()
230
+ .lambdaRelationType(
231
+ V1_LambdaReturnTypeInput.serialization.toJson(relationTypeInput),
232
+ )) as object,
233
+ );
234
+
235
+ this.setLambdaRelationColumns(
236
+ relationType.columns.map((column) => column.name),
237
+ );
238
+ } catch {
239
+ this.setLambdaRelationColumns(undefined);
240
+ } finally {
241
+ if (relationElement) {
242
+ this.val.addRelationElement(relationElement, false);
243
+ }
244
+ this.setIsUpdatingRelationColumns(false);
245
+ }
155
246
  }
156
247
 
157
248
  override get lambdaId(): string {
@@ -253,6 +344,10 @@ export class LakehouseAccessPointState extends AccessPointState {
253
344
  lambdaState: AccessPointLambdaEditorState;
254
345
  artifactGenerationContent: string | undefined;
255
346
  generatingArtifactState = ActionState.create();
347
+ relationElementState: RelationElementState | undefined;
348
+ showSampleValuesEditor = true;
349
+ showSampleValuesModal = false;
350
+
256
351
  // Add lineage state and isGeneratingLineage
257
352
  lineageState: LineageState;
258
353
  generatingLineageAction = ActionState.create();
@@ -265,16 +360,195 @@ export class LakehouseAccessPointState extends AccessPointState {
265
360
  generatingArtifactState: observable,
266
361
  generateArtifact: flow,
267
362
  isRunningProcess: computed,
363
+ relationElementState: observable,
364
+ showSampleValuesEditor: observable,
365
+ setShowSampleValuesEditor: action,
366
+ showSampleValuesModal: observable,
367
+ setShowSampleValuesModal: action,
368
+ addRelationElement: action,
369
+ deleteRelationElement: action,
370
+ // Add observables for lineage
268
371
  lineageState: observable,
269
372
  setArtifactContent: action,
270
373
  generatingLineageAction: observable,
271
374
  generateLineage: flow,
375
+ hasRelationElementMismatch: computed,
272
376
  });
273
377
  this.accessPoint = val;
274
378
  this.lambdaState = new AccessPointLambdaEditorState(this);
275
379
  this.lineageState = new LineageState(
276
380
  this.state.state.editorStore.applicationStore,
277
381
  );
382
+ this.relationElementState = this.getRelationElementState();
383
+ }
384
+
385
+ get hasRelationElementMismatch(): boolean {
386
+ if (!this.relationElementState) {
387
+ return false;
388
+ }
389
+
390
+ const relationElement = this.relationElementState.relationElement;
391
+ const lambdaColumns = this.lambdaState.lambdaRelationColumns;
392
+
393
+ if (lambdaColumns === undefined) {
394
+ return true;
395
+ }
396
+
397
+ const relationColumnSet = new Set(relationElement.columns);
398
+ const lambdaColumnSet = new Set(lambdaColumns);
399
+
400
+ if (relationColumnSet.size !== lambdaColumnSet.size) {
401
+ return true;
402
+ }
403
+
404
+ return !Array.from(lambdaColumnSet).every((col) =>
405
+ relationColumnSet.has(col),
406
+ );
407
+ }
408
+
409
+ getRelationElementMismatchMessage(): string | undefined {
410
+ if (!this.hasRelationElementMismatch) {
411
+ return undefined;
412
+ }
413
+
414
+ const lambdaColumns = this.lambdaState.lambdaRelationColumns;
415
+
416
+ if (lambdaColumns === undefined) {
417
+ return `Fix compiler errors and make sure AccessPoint ${this.accessPoint.id} returns a Relation type`;
418
+ }
419
+
420
+ return `Sample values columns must match: [${lambdaColumns.join(', ')}]`;
421
+ }
422
+
423
+ setShowSampleValuesEditor(value: boolean): void {
424
+ this.showSampleValuesEditor = value;
425
+ }
426
+
427
+ setShowSampleValuesModal(value: boolean): void {
428
+ this.showSampleValuesModal = value;
429
+ }
430
+
431
+ getRelationElementState(): RelationElementState | undefined {
432
+ const relationElement = this.getRelationElement();
433
+ return relationElement
434
+ ? new RelationElementState(relationElement)
435
+ : undefined;
436
+ }
437
+
438
+ getRelationElement(): RelationElement | undefined {
439
+ const product = this.state.state.product;
440
+ if (!product.sampleValues) {
441
+ return undefined;
442
+ }
443
+ for (const embeddedData of product.sampleValues) {
444
+ if (embeddedData instanceof RelationElementsData) {
445
+ const relationElement = embeddedData.relationElements.find(
446
+ (res) => res.paths[0] === this.accessPoint.id,
447
+ );
448
+ if (relationElement) {
449
+ return relationElement;
450
+ }
451
+ }
452
+ }
453
+ return undefined;
454
+ }
455
+
456
+ relationElementExistsinDataElementReference(): string | undefined {
457
+ const product = this.state.state.product;
458
+ if (!product.sampleValues) {
459
+ return undefined;
460
+ }
461
+ for (const embeddedData of product.sampleValues) {
462
+ if (embeddedData instanceof DataElementReference) {
463
+ const dataElement = embeddedData.dataElement.value;
464
+ if (
465
+ dataElement instanceof DataElement &&
466
+ dataElement.data instanceof RelationElementsData
467
+ ) {
468
+ const relationElement = dataElement.data.relationElements.find(
469
+ (res) => res.paths[0] === this.accessPoint.id,
470
+ );
471
+ if (relationElement) {
472
+ return dataElement.path;
473
+ }
474
+ }
475
+ }
476
+ }
477
+ return undefined;
478
+ }
479
+
480
+ createAndaddRelationElement(): void {
481
+ const newElement = new RelationElement();
482
+ newElement.paths = [this.accessPoint.id];
483
+
484
+ if (this.lambdaState.lambdaRelationColumns) {
485
+ newElement.columns = [...this.lambdaState.lambdaRelationColumns];
486
+ } else {
487
+ this.state.state.editorStore.applicationStore.notificationService.notifyError(
488
+ `Can't get AccessPoint Relation columns. Fix compiler errors and make sure AccessPoint ${this.accessPoint.id} returns a Relation`,
489
+ );
490
+ return;
491
+ }
492
+
493
+ newElement.rows = [];
494
+ this.addRelationElement(observe_RelationElement(newElement), true);
495
+ }
496
+
497
+ addRelationElement(newElement: RelationElement, openModal: boolean): void {
498
+ const product = this.state.state.product;
499
+
500
+ let relationElementsData = product.sampleValues?.find(
501
+ (embeddedData) => embeddedData instanceof RelationElementsData,
502
+ );
503
+
504
+ if (!relationElementsData) {
505
+ if (!product.sampleValues) {
506
+ product.sampleValues = [];
507
+ }
508
+ relationElementsData = observe_RelationElementsData(
509
+ new RelationElementsData(),
510
+ );
511
+ relationElementsData.relationElements = [];
512
+ product.sampleValues.push(relationElementsData);
513
+ }
514
+ addUniqueEntry(relationElementsData.relationElements, newElement);
515
+
516
+ this.relationElementState = new RelationElementState(newElement);
517
+
518
+ if (openModal) {
519
+ this.setShowSampleValuesModal(true);
520
+ }
521
+ }
522
+
523
+ deleteRelationElement(): void {
524
+ const product = this.state.state.product;
525
+ if (!product.sampleValues) {
526
+ return;
527
+ }
528
+
529
+ product.sampleValues.forEach((embeddedData) => {
530
+ if (embeddedData instanceof RelationElementsData) {
531
+ const elementToRemove = embeddedData.relationElements.find(
532
+ (re) => re.paths[0] === this.accessPoint.id,
533
+ );
534
+ if (elementToRemove) {
535
+ deleteEntry(embeddedData.relationElements, elementToRemove);
536
+ }
537
+ }
538
+ });
539
+
540
+ product.sampleValues = product.sampleValues.filter(
541
+ (ed) =>
542
+ !(ed instanceof RelationElementsData) || ed.relationElements.length > 0,
543
+ );
544
+
545
+ if (product.sampleValues.length === 0) {
546
+ product.sampleValues = undefined;
547
+ }
548
+
549
+ if (this.relationElementState) {
550
+ this.relationElementState = undefined;
551
+ }
278
552
  }
279
553
 
280
554
  get editorStore(): EditorStore {
@@ -451,6 +725,9 @@ export class AccessPointGroupState {
451
725
 
452
726
  deleteAccessPoint(val: AccessPointState): void {
453
727
  const state = this.accessPointStates.find((a) => a === val);
728
+ if (state instanceof LakehouseAccessPointState) {
729
+ state.deleteRelationElement();
730
+ }
454
731
  deleteEntry(this.accessPointStates, state);
455
732
  dataProduct_deleteAccessPoint(this.value, val.accessPoint);
456
733
  }
@@ -21,6 +21,7 @@ import {
21
21
  type DataProductIcon,
22
22
  type Email,
23
23
  type LakehouseAccessPoint,
24
+ type RelationElementsData,
24
25
  DataProductLink,
25
26
  observe_AccessPoint,
26
27
  observe_Email,
@@ -219,6 +220,24 @@ export const dataProduct_deleteExpertise = action(
219
220
  },
220
221
  );
221
222
 
223
+ export const dataProduct_deleteRelationElement = action(
224
+ (
225
+ product: DataProduct,
226
+ relationData: RelationElementsData,
227
+ accessPointId: string,
228
+ ) => {
229
+ const elementToDelete = relationData.relationElements.find(
230
+ (re) => re.paths[0] === accessPointId,
231
+ );
232
+ if (elementToDelete) {
233
+ deleteEntry(relationData.relationElements, elementToDelete);
234
+ if (relationData.relationElements.length === 0) {
235
+ product.sampleValues = undefined;
236
+ }
237
+ }
238
+ },
239
+ );
240
+
222
241
  export const expertise_setDescription = action(
223
242
  (expertise: Expertise, desc: string) => {
224
243
  expertise.description = desc;