@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.
- package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.d.ts +7 -1
- package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.d.ts.map +1 -1
- package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.js +7 -7
- package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.js.map +1 -1
- package/lib/components/editor/editor-group/dataProduct/DataProductEditor.d.ts.map +1 -1
- package/lib/components/editor/editor-group/dataProduct/DataProductEditor.js +150 -6
- package/lib/components/editor/editor-group/dataProduct/DataProductEditor.js.map +1 -1
- package/lib/index.css +1 -1
- package/lib/package.json +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.d.ts +20 -1
- package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.d.ts.map +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.js +186 -2
- package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.js.map +1 -1
- package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.d.ts +2 -1
- package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.d.ts.map +1 -1
- package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.js +9 -0
- package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.js.map +1 -1
- package/package.json +8 -8
- package/src/components/editor/editor-group/data-editor/RelationElementsDataEditor.tsx +7 -7
- package/src/components/editor/editor-group/dataProduct/DataProductEditor.tsx +310 -4
- package/src/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.ts +277 -0
- package/src/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.ts +19 -0
package/src/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.ts
CHANGED
|
@@ -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;
|