@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.
- package/lib/components/editor/editor-group/data-editor/EmbeddedDataEditor.d.ts +1 -1
- package/lib/components/editor/editor-group/data-editor/EmbeddedDataEditor.d.ts.map +1 -1
- package/lib/components/editor/editor-group/data-editor/EmbeddedDataEditor.js +3 -3
- package/lib/components/editor/editor-group/data-editor/EmbeddedDataEditor.js.map +1 -1
- package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.d.ts +3 -0
- package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.d.ts.map +1 -1
- package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.js +12 -35
- 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 +19 -6
- package/lib/components/editor/editor-group/dataProduct/DataProductEditor.js.map +1 -1
- package/lib/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.d.ts.map +1 -1
- package/lib/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.js +59 -22
- package/lib/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.js.map +1 -1
- package/lib/components/editor/editor-group/function-activator/testable/FunctionTestableEditor.d.ts.map +1 -1
- package/lib/components/editor/editor-group/function-activator/testable/FunctionTestableEditor.js +113 -75
- package/lib/components/editor/editor-group/function-activator/testable/FunctionTestableEditor.js.map +1 -1
- package/lib/components/editor/editor-group/testable/TestableSharedComponents.d.ts.map +1 -1
- package/lib/components/editor/editor-group/testable/TestableSharedComponents.js +1 -1
- package/lib/components/editor/editor-group/testable/TestableSharedComponents.js.map +1 -1
- package/lib/components/editor/side-bar/DevMetadataPanel.d.ts.map +1 -1
- package/lib/components/editor/side-bar/DevMetadataPanel.js +37 -6
- package/lib/components/editor/side-bar/DevMetadataPanel.js.map +1 -1
- package/lib/index.css +2 -2
- package/lib/index.css.map +1 -1
- package/lib/package.json +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.d.ts +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.d.ts.map +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.js +20 -48
- package/lib/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.js.map +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.d.ts +9 -14
- package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.d.ts.map +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.js +125 -78
- package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.js.map +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.d.ts +18 -4
- package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.d.ts.map +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.js +214 -53
- package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.js.map +1 -1
- package/lib/stores/editor/sidebar-state/dev-metadata/DevMetadataState.d.ts +9 -0
- package/lib/stores/editor/sidebar-state/dev-metadata/DevMetadataState.d.ts.map +1 -1
- package/lib/stores/editor/sidebar-state/dev-metadata/DevMetadataState.js +55 -0
- package/lib/stores/editor/sidebar-state/dev-metadata/DevMetadataState.js.map +1 -1
- package/package.json +16 -16
- package/src/components/editor/editor-group/data-editor/EmbeddedDataEditor.tsx +3 -0
- package/src/components/editor/editor-group/data-editor/RelationElementsDataEditor.tsx +200 -186
- package/src/components/editor/editor-group/dataProduct/DataProductEditor.tsx +25 -7
- package/src/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.tsx +149 -86
- package/src/components/editor/editor-group/function-activator/testable/FunctionTestableEditor.tsx +425 -308
- package/src/components/editor/editor-group/testable/TestableSharedComponents.tsx +2 -11
- package/src/components/editor/side-bar/DevMetadataPanel.tsx +194 -10
- package/src/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.ts +28 -50
- package/src/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.ts +164 -100
- package/src/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.ts +303 -72
- package/src/stores/editor/sidebar-state/dev-metadata/DevMetadataState.ts +76 -0
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import { action, flow, makeObservable, observable } from 'mobx';
|
|
17
|
+
import { action, flow, flowResult, makeObservable, observable } from 'mobx';
|
|
18
18
|
import type { EditorStore } from '../../../../EditorStore.js';
|
|
19
19
|
import type { FunctionEditorState } from '../../FunctionEditorState.js';
|
|
20
20
|
import {
|
|
@@ -31,6 +31,8 @@ import {
|
|
|
31
31
|
filterByType,
|
|
32
32
|
deleteEntry,
|
|
33
33
|
UnsupportedOperationError,
|
|
34
|
+
noop,
|
|
35
|
+
type GeneratorFn,
|
|
34
36
|
} from '@finos/legend-shared';
|
|
35
37
|
import {
|
|
36
38
|
type ConcreteFunctionDefinition,
|
|
@@ -40,6 +42,7 @@ import {
|
|
|
40
42
|
type ObserverContext,
|
|
41
43
|
type ValueSpecification,
|
|
42
44
|
type TestAssertion,
|
|
45
|
+
type AccessorOwner,
|
|
43
46
|
FunctionParameterValue,
|
|
44
47
|
VariableExpression,
|
|
45
48
|
FunctionTest,
|
|
@@ -58,9 +61,17 @@ import {
|
|
|
58
61
|
observe_ValueSpecification,
|
|
59
62
|
buildLambdaVariableExpressions,
|
|
60
63
|
EqualTo,
|
|
64
|
+
EqualToRelation,
|
|
65
|
+
RelationElement,
|
|
66
|
+
observe_RelationElement,
|
|
61
67
|
ModelStore,
|
|
62
68
|
RelationElementsData,
|
|
63
69
|
CORE_PURE_PATH,
|
|
70
|
+
type Accessor,
|
|
71
|
+
DataProductAccessor,
|
|
72
|
+
IngestionAccessor,
|
|
73
|
+
RelationalStoreAccessor,
|
|
74
|
+
V1_buildRelationElementsDataFromAccessors,
|
|
64
75
|
} from '@finos/legend-graph';
|
|
65
76
|
import {
|
|
66
77
|
TestablePackageableElementEditorState,
|
|
@@ -68,6 +79,7 @@ import {
|
|
|
68
79
|
TestableTestSuiteEditorState,
|
|
69
80
|
} from '../../testable/TestableEditorState.js';
|
|
70
81
|
import { EmbeddedDataEditorState } from '../../data/DataEditorState.js';
|
|
82
|
+
import { RelationElementsDataState } from '../../data/EmbeddedDataState.js';
|
|
71
83
|
import {
|
|
72
84
|
functionTestable_deleteDataStore,
|
|
73
85
|
functionTestable_setEmbeddedData,
|
|
@@ -207,6 +219,69 @@ export class FunctionStoreTestDataState {
|
|
|
207
219
|
hideSource: true,
|
|
208
220
|
},
|
|
209
221
|
);
|
|
222
|
+
this.initAccessorOptions();
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
private initAccessorOptions(): void {
|
|
226
|
+
const embeddedDataState = this.embeddedEditorState.embeddedDataState;
|
|
227
|
+
if (!(embeddedDataState instanceof RelationElementsDataState)) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
this.refreshAccessorOptions(embeddedDataState).catch(noop());
|
|
231
|
+
embeddedDataState.setRefreshAccessorOptions(() =>
|
|
232
|
+
this.refreshAccessorOptions(embeddedDataState),
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
private async refreshAccessorOptions(
|
|
237
|
+
embeddedDataState: RelationElementsDataState,
|
|
238
|
+
): Promise<void> {
|
|
239
|
+
const parentElement = this.storeTestData.element.value;
|
|
240
|
+
const accessors =
|
|
241
|
+
this.testDataState.functionTestableState
|
|
242
|
+
.resolvedIngestOrDataProductAccessors;
|
|
243
|
+
const parentAccessors = accessors.filter(
|
|
244
|
+
(a) => a.accessorOwner === parentElement.path,
|
|
245
|
+
);
|
|
246
|
+
if (!parentAccessors.length) {
|
|
247
|
+
embeddedDataState.setAccessorOptions(undefined, undefined);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const firstAccessor = parentAccessors[0];
|
|
251
|
+
if (!firstAccessor) {
|
|
252
|
+
embeddedDataState.setAccessorOptions(undefined, undefined);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const columnOverrides = new Map<string, string[]>();
|
|
257
|
+
for (const accessor of parentAccessors) {
|
|
258
|
+
const cols = accessor.relationType.columns.map((col) => col.name);
|
|
259
|
+
if (cols.length > 0) {
|
|
260
|
+
columnOverrides.set(accessor.accessor, cols);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const typeLabel = firstAccessor.accessorLabel;
|
|
265
|
+
const options = parentAccessors.map((a) => ({
|
|
266
|
+
label: a.accessor,
|
|
267
|
+
value: a.accessor,
|
|
268
|
+
columns: columnOverrides.get(a.accessor) ?? [],
|
|
269
|
+
}));
|
|
270
|
+
|
|
271
|
+
if (columnOverrides.size > 0) {
|
|
272
|
+
for (const relState of embeddedDataState.relationElementStates) {
|
|
273
|
+
const rel = relState.relationElement;
|
|
274
|
+
if (rel.columns.length === 0) {
|
|
275
|
+
const key = rel.paths[rel.paths.length - 1];
|
|
276
|
+
const cols = key ? columnOverrides.get(key) : undefined;
|
|
277
|
+
if (cols) {
|
|
278
|
+
rel.columns = cols;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
embeddedDataState.setAccessorOptions(options, typeLabel);
|
|
210
285
|
}
|
|
211
286
|
|
|
212
287
|
setDataElementModal(val: boolean): void {
|
|
@@ -532,6 +607,7 @@ class FunctionTestDataState {
|
|
|
532
607
|
selectedDataState: FunctionStoreTestDataState | undefined;
|
|
533
608
|
dataHolder: FunctionTestSuite;
|
|
534
609
|
showNewModal = false;
|
|
610
|
+
showAddElementModal = false;
|
|
535
611
|
|
|
536
612
|
constructor(
|
|
537
613
|
editorStore: EditorStore,
|
|
@@ -542,9 +618,12 @@ class FunctionTestDataState {
|
|
|
542
618
|
selectedDataState: observable,
|
|
543
619
|
dataHolder: observable,
|
|
544
620
|
showNewModal: observable,
|
|
621
|
+
showAddElementModal: observable,
|
|
545
622
|
initDefaultStore: action,
|
|
546
623
|
setShowModal: action,
|
|
624
|
+
setShowAddElementModal: action,
|
|
547
625
|
deleteStoreTestData: action,
|
|
626
|
+
addDataElement: action,
|
|
548
627
|
openStoreTestData: action,
|
|
549
628
|
});
|
|
550
629
|
this.editorStore = editorStore;
|
|
@@ -566,6 +645,49 @@ class FunctionTestDataState {
|
|
|
566
645
|
this.showNewModal = val;
|
|
567
646
|
}
|
|
568
647
|
|
|
648
|
+
setShowAddElementModal(val: boolean): void {
|
|
649
|
+
this.showAddElementModal = val;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
get existingElementPaths(): string[] {
|
|
653
|
+
return (this.dataHolder.testData ?? []).map((td) => td.element.value.path);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
get availableElementsToAdd(): { path: string }[] {
|
|
657
|
+
const accessors =
|
|
658
|
+
this.functionTestableState.resolvedIngestOrDataProductAccessors;
|
|
659
|
+
const existingPaths = new Set(this.existingElementPaths);
|
|
660
|
+
const parentPaths = new Set<string>();
|
|
661
|
+
for (const accessor of accessors) {
|
|
662
|
+
const parentPath = accessor.path[0];
|
|
663
|
+
if (parentPath && !existingPaths.has(parentPath)) {
|
|
664
|
+
parentPaths.add(parentPath);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
return Array.from(parentPaths).map((path) => ({ path }));
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
addDataElement(elementPath: string): void {
|
|
671
|
+
const accessors =
|
|
672
|
+
this.functionTestableState.resolvedIngestOrDataProductAccessors;
|
|
673
|
+
const group = accessors.filter((a) => a.path[0] === elementPath);
|
|
674
|
+
if (!group.length) {
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
const element =
|
|
678
|
+
this.editorStore.graphManagerState.graph.getElement(elementPath);
|
|
679
|
+
const data = new FunctionTestData();
|
|
680
|
+
data.element = PackageableElementExplicitReference.create(
|
|
681
|
+
element as AccessorOwner,
|
|
682
|
+
);
|
|
683
|
+
data.data = V1_buildRelationElementsDataFromAccessors(group);
|
|
684
|
+
if (!this.dataHolder.testData) {
|
|
685
|
+
this.dataHolder.testData = [];
|
|
686
|
+
}
|
|
687
|
+
this.dataHolder.testData.push(data);
|
|
688
|
+
this.openStoreTestData(data);
|
|
689
|
+
}
|
|
690
|
+
|
|
569
691
|
deleteStoreTestData(val: FunctionTestData): void {
|
|
570
692
|
functionTestable_deleteDataStore(this.dataHolder, val);
|
|
571
693
|
this.initDefaultStore();
|
|
@@ -580,26 +702,60 @@ class FunctionTestDataState {
|
|
|
580
702
|
}
|
|
581
703
|
}
|
|
582
704
|
|
|
583
|
-
export const createFunctionTest = (
|
|
705
|
+
export const createFunctionTest = async (
|
|
584
706
|
id: string,
|
|
585
707
|
observerContext: ObserverContext,
|
|
586
708
|
containsRuntime: boolean,
|
|
587
709
|
functionDefinition: ConcreteFunctionDefinition,
|
|
588
710
|
editorStore: EditorStore,
|
|
589
711
|
suite?: FunctionTestSuite | undefined,
|
|
590
|
-
): FunctionTest => {
|
|
712
|
+
): Promise<FunctionTest> => {
|
|
591
713
|
const funcionTest = new FunctionTest();
|
|
592
714
|
funcionTest.id = id;
|
|
593
715
|
funcionTest.assertions = [];
|
|
594
716
|
let _assertion: TestAssertion;
|
|
595
|
-
|
|
717
|
+
const type = functionDefinition.returnType.value.rawType;
|
|
718
|
+
if (
|
|
719
|
+
type.path === CORE_PURE_PATH.RELATION ||
|
|
720
|
+
type.path === CORE_PURE_PATH.TABULAR_DATASET
|
|
721
|
+
) {
|
|
722
|
+
const assertion = new EqualToRelation();
|
|
723
|
+
assertion.id = DEFAULT_TEST_ASSERTION_ID;
|
|
724
|
+
const expectedRelElement = new RelationElement();
|
|
725
|
+
expectedRelElement.paths = [];
|
|
726
|
+
expectedRelElement.rows = [];
|
|
727
|
+
let inferredColumns: string[] = [];
|
|
728
|
+
if (type.path === CORE_PURE_PATH.RELATION) {
|
|
729
|
+
try {
|
|
730
|
+
const rawLambda = new RawLambda(
|
|
731
|
+
functionDefinition.parameters.map((_param) =>
|
|
732
|
+
editorStore.graphManagerState.graphManager.serializeRawValueSpecification(
|
|
733
|
+
_param,
|
|
734
|
+
),
|
|
735
|
+
),
|
|
736
|
+
functionDefinition.expressionSequence,
|
|
737
|
+
);
|
|
738
|
+
const relationTypeMetadata =
|
|
739
|
+
await editorStore.graphManagerState.graphManager.getLambdaRelationType(
|
|
740
|
+
rawLambda,
|
|
741
|
+
editorStore.graphManagerState.graph,
|
|
742
|
+
);
|
|
743
|
+
inferredColumns = relationTypeMetadata.columns.map((col) => col.name);
|
|
744
|
+
} catch {
|
|
745
|
+
// best-effort: leave columns empty
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
expectedRelElement.columns = inferredColumns;
|
|
749
|
+
observe_RelationElement(expectedRelElement);
|
|
750
|
+
assertion.expected = expectedRelElement;
|
|
751
|
+
_assertion = assertion;
|
|
752
|
+
} else if (containsRuntime) {
|
|
596
753
|
_assertion = createDefaultEqualToJSONTestAssertion(
|
|
597
754
|
DEFAULT_TEST_ASSERTION_ID,
|
|
598
755
|
);
|
|
599
756
|
} else {
|
|
600
757
|
const equalTo = new EqualTo();
|
|
601
758
|
equalTo.id = DEFAULT_TEST_ASSERTION_ID;
|
|
602
|
-
const type = functionDefinition.returnType.value.rawType;
|
|
603
759
|
const valSpec = buildDefaultInstanceValue(
|
|
604
760
|
editorStore.graphManagerState.graph,
|
|
605
761
|
type,
|
|
@@ -652,7 +808,7 @@ export class FunctionTestSuiteState extends TestableTestSuiteEditorState {
|
|
|
652
808
|
deleteTest: action,
|
|
653
809
|
buildTestStates: action,
|
|
654
810
|
setShowModal: action,
|
|
655
|
-
addNewTest:
|
|
811
|
+
addNewTest: flow,
|
|
656
812
|
});
|
|
657
813
|
this.functionTestableState = functionTestableState;
|
|
658
814
|
this.suite = suite;
|
|
@@ -682,15 +838,15 @@ export class FunctionTestSuiteState extends TestableTestSuiteEditorState {
|
|
|
682
838
|
return undefined;
|
|
683
839
|
}
|
|
684
840
|
|
|
685
|
-
addNewTest(id: string): void {
|
|
686
|
-
const test = createFunctionTest(
|
|
841
|
+
*addNewTest(id: string): GeneratorFn<void> {
|
|
842
|
+
const test = (yield createFunctionTest(
|
|
687
843
|
id,
|
|
688
844
|
this.editorStore.changeDetectionState.observerContext,
|
|
689
845
|
this.functionTestableState.containsRuntime,
|
|
690
846
|
this.functionTestableState.function,
|
|
691
847
|
this.editorStore,
|
|
692
848
|
this.suite,
|
|
693
|
-
);
|
|
849
|
+
)) as FunctionTest;
|
|
694
850
|
testSuite_addTest(
|
|
695
851
|
this.suite,
|
|
696
852
|
test,
|
|
@@ -711,6 +867,7 @@ export class FunctionTestableState extends TestablePackageableElementEditorState
|
|
|
711
867
|
declare runningSuite: FunctionTestSuite | undefined;
|
|
712
868
|
|
|
713
869
|
createSuiteModal = false;
|
|
870
|
+
cachedAccessors: Accessor[] = [];
|
|
714
871
|
|
|
715
872
|
constructor(functionEditorState: FunctionEditorState) {
|
|
716
873
|
super(functionEditorState, functionEditorState.functionElement);
|
|
@@ -722,6 +879,7 @@ export class FunctionTestableState extends TestablePackageableElementEditorState
|
|
|
722
879
|
runningSuite: observable,
|
|
723
880
|
testableComponentToRename: observable,
|
|
724
881
|
createSuiteModal: observable,
|
|
882
|
+
cachedAccessors: observable,
|
|
725
883
|
init: action,
|
|
726
884
|
buildTestSuiteState: action,
|
|
727
885
|
deleteTestSuite: action,
|
|
@@ -730,6 +888,8 @@ export class FunctionTestableState extends TestablePackageableElementEditorState
|
|
|
730
888
|
setRenameComponent: action,
|
|
731
889
|
clearTestResultsForSuite: action,
|
|
732
890
|
setCreateSuite: action,
|
|
891
|
+
resolveAccessors: flow,
|
|
892
|
+
createSuite: flow,
|
|
733
893
|
runTestable: flow,
|
|
734
894
|
runSuite: flow,
|
|
735
895
|
runAllFailingSuites: flow,
|
|
@@ -750,6 +910,36 @@ export class FunctionTestableState extends TestablePackageableElementEditorState
|
|
|
750
910
|
return Boolean(this.associatedRuntimes?.length);
|
|
751
911
|
}
|
|
752
912
|
|
|
913
|
+
get resolvedIngestOrDataProductAccessors(): Accessor[] {
|
|
914
|
+
return this.cachedAccessors.filter(
|
|
915
|
+
(a) => a instanceof DataProductAccessor || a instanceof IngestionAccessor,
|
|
916
|
+
);
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
private buildRawLambdaFromFunction(): RawLambda {
|
|
920
|
+
return new RawLambda(
|
|
921
|
+
this.function.parameters.map((_param) =>
|
|
922
|
+
this.editorStore.graphManagerState.graphManager.serializeRawValueSpecification(
|
|
923
|
+
_param,
|
|
924
|
+
),
|
|
925
|
+
),
|
|
926
|
+
this.function.expressionSequence,
|
|
927
|
+
);
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
*resolveAccessors(): GeneratorFn<void> {
|
|
931
|
+
try {
|
|
932
|
+
const rawLambda = this.buildRawLambdaFromFunction();
|
|
933
|
+
this.cachedAccessors =
|
|
934
|
+
(yield this.editorStore.graphManagerState.graphManager.collectAccessorsInRawLambda(
|
|
935
|
+
rawLambda,
|
|
936
|
+
this.editorStore.graphManagerState.graph,
|
|
937
|
+
)) as Accessor[];
|
|
938
|
+
} catch {
|
|
939
|
+
this.cachedAccessors = [];
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
|
|
753
943
|
override init(): void {
|
|
754
944
|
if (!this.selectedTestSuite) {
|
|
755
945
|
const suite = this.function.tests[0];
|
|
@@ -757,84 +947,125 @@ export class FunctionTestableState extends TestablePackageableElementEditorState
|
|
|
757
947
|
? this.buildTestSuiteState(suite)
|
|
758
948
|
: undefined;
|
|
759
949
|
}
|
|
950
|
+
flowResult(this.resolveAccessors()).catch(noop);
|
|
760
951
|
}
|
|
761
952
|
|
|
762
953
|
setCreateSuite(val: boolean): void {
|
|
763
954
|
this.createSuiteModal = val;
|
|
764
955
|
}
|
|
765
956
|
|
|
766
|
-
createSuite(suiteName: string, testName: string): void {
|
|
957
|
+
*createSuite(suiteName: string, testName: string): GeneratorFn<void> {
|
|
767
958
|
const functionSuite = new FunctionTestSuite();
|
|
768
959
|
functionSuite.id = suiteName;
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
)
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
...engineRuntime.connectionStores
|
|
802
|
-
.map((e) => e.storePointers.map((sPt) => sPt.value))
|
|
803
|
-
.flat(),
|
|
804
|
-
].filter(isNonNullable);
|
|
805
|
-
assertTrue(Boolean(stores.length), 'No runtime store found');
|
|
806
|
-
assertTrue(
|
|
807
|
-
stores.length === 1,
|
|
808
|
-
'Only one store supported in runtime for function tests',
|
|
960
|
+
|
|
961
|
+
yield flowResult(this.resolveAccessors());
|
|
962
|
+
|
|
963
|
+
const ingestOrDataProductAccessors =
|
|
964
|
+
this.resolvedIngestOrDataProductAccessors;
|
|
965
|
+
const databaseAccessors = this.cachedAccessors.filter(
|
|
966
|
+
(a) => a instanceof RelationalStoreAccessor,
|
|
967
|
+
);
|
|
968
|
+
|
|
969
|
+
try {
|
|
970
|
+
if (ingestOrDataProductAccessors.length) {
|
|
971
|
+
// Group by parent element path and create test data per parent
|
|
972
|
+
const parentElementMap = new Map<string, Accessor[]>();
|
|
973
|
+
for (const accessor of ingestOrDataProductAccessors) {
|
|
974
|
+
const key = accessor.accessorOwner;
|
|
975
|
+
if (key) {
|
|
976
|
+
const group = parentElementMap.get(key) ?? [];
|
|
977
|
+
group.push(accessor);
|
|
978
|
+
parentElementMap.set(key, group);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
functionSuite.testData = Array.from(parentElementMap.entries()).map(
|
|
982
|
+
([parentPath, group]) => {
|
|
983
|
+
const data = new FunctionTestData();
|
|
984
|
+
const element =
|
|
985
|
+
this.editorStore.graphManagerState.graph.getElement(parentPath);
|
|
986
|
+
data.element = PackageableElementExplicitReference.create(
|
|
987
|
+
element as AccessorOwner,
|
|
988
|
+
);
|
|
989
|
+
data.data = V1_buildRelationElementsDataFromAccessors(group);
|
|
990
|
+
return data;
|
|
991
|
+
},
|
|
809
992
|
);
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
993
|
+
} else {
|
|
994
|
+
// No ingest/data product accessors found — try runtime-based approach
|
|
995
|
+
const engineRuntimes = this.associatedRuntimes;
|
|
996
|
+
if (engineRuntimes?.length) {
|
|
997
|
+
assertTrue(
|
|
998
|
+
engineRuntimes.length === 1,
|
|
999
|
+
`Function Test Suite Only supports One Runtime at this time. Found ${engineRuntimes.length}`,
|
|
1000
|
+
);
|
|
1001
|
+
const engineRuntime = guaranteeNonNullable(engineRuntimes[0]);
|
|
1002
|
+
assertTrue(
|
|
1003
|
+
!(
|
|
1004
|
+
engineRuntime.connectionStores.length &&
|
|
1005
|
+
engineRuntime.connections.length
|
|
1006
|
+
),
|
|
1007
|
+
`Runtime found has two connection types defined. Please use connection stores only`,
|
|
823
1008
|
);
|
|
1009
|
+
const stores = [
|
|
1010
|
+
...engineRuntime.connections
|
|
1011
|
+
.map((e) =>
|
|
1012
|
+
e.storeConnections.map((s) => s.connection.store?.value).flat(),
|
|
1013
|
+
)
|
|
1014
|
+
.flat(),
|
|
1015
|
+
...engineRuntime.connectionStores
|
|
1016
|
+
.map((e) => e.storePointers.map((sPt) => sPt.value))
|
|
1017
|
+
.flat(),
|
|
1018
|
+
].filter(isNonNullable);
|
|
1019
|
+
assertTrue(Boolean(stores.length), 'No runtime store found');
|
|
1020
|
+
assertTrue(
|
|
1021
|
+
stores.length === 1,
|
|
1022
|
+
'Only one store supported in runtime for function tests',
|
|
1023
|
+
);
|
|
1024
|
+
const store = guaranteeNonNullable(stores[0]);
|
|
1025
|
+
const data = new FunctionTestData();
|
|
1026
|
+
if (store instanceof Database) {
|
|
1027
|
+
const relation = new RelationElementsData();
|
|
1028
|
+
data.element = PackageableElementExplicitReference.create(store);
|
|
1029
|
+
data.data = relation;
|
|
1030
|
+
} else if (store instanceof ModelStore) {
|
|
1031
|
+
const modelStoreData = createBareExternalFormat();
|
|
1032
|
+
data.element = PackageableElementExplicitReference.create(store);
|
|
1033
|
+
data.data = modelStoreData;
|
|
1034
|
+
} else {
|
|
1035
|
+
throw new UnsupportedOperationError(
|
|
1036
|
+
`function test store data does not support store: ${store.path}`,
|
|
1037
|
+
);
|
|
1038
|
+
}
|
|
1039
|
+
functionSuite.testData = [data];
|
|
1040
|
+
} else {
|
|
1041
|
+
const type = this.function.returnType.value.rawType;
|
|
1042
|
+
if (
|
|
1043
|
+
type.path === CORE_PURE_PATH.RELATION ||
|
|
1044
|
+
type.path === CORE_PURE_PATH.TABULAR_DATASET
|
|
1045
|
+
) {
|
|
1046
|
+
this.editorStore.applicationStore.notificationService.notifyError(
|
|
1047
|
+
`Unable to create function test suite: no runtime or accessors found, or they could not be resolved`,
|
|
1048
|
+
);
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
824
1051
|
}
|
|
825
|
-
functionSuite.testData = [data];
|
|
826
|
-
} catch (error) {
|
|
827
|
-
assertErrorThrown(error);
|
|
828
|
-
this.editorStore.applicationStore.notificationService.notifyError(
|
|
829
|
-
`Unable to create function test suite: ${error.message}`,
|
|
830
|
-
);
|
|
831
|
-
return;
|
|
832
1052
|
}
|
|
1053
|
+
} catch (error) {
|
|
1054
|
+
assertErrorThrown(error);
|
|
1055
|
+
this.editorStore.applicationStore.notificationService.notifyError(
|
|
1056
|
+
`Unable to create function test suite: ${error.message}`,
|
|
1057
|
+
);
|
|
1058
|
+
return;
|
|
833
1059
|
}
|
|
834
|
-
|
|
1060
|
+
|
|
1061
|
+
const hasTestData =
|
|
1062
|
+
this.containsRuntime ||
|
|
1063
|
+
ingestOrDataProductAccessors.length > 0 ||
|
|
1064
|
+
databaseAccessors.length > 0;
|
|
1065
|
+
yield createFunctionTest(
|
|
835
1066
|
testName,
|
|
836
1067
|
this.editorStore.changeDetectionState.observerContext,
|
|
837
|
-
|
|
1068
|
+
hasTestData,
|
|
838
1069
|
this.function,
|
|
839
1070
|
this.editorStore,
|
|
840
1071
|
functionSuite,
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
assertNonNullable,
|
|
21
21
|
assertTrue,
|
|
22
22
|
type GeneratorFn,
|
|
23
|
+
type PlainObject,
|
|
23
24
|
} from '@finos/legend-shared';
|
|
24
25
|
import type { EditorStore } from '../../EditorStore.js';
|
|
25
26
|
import { action, flow, makeObservable, observable } from 'mobx';
|
|
@@ -27,14 +28,24 @@ import {
|
|
|
27
28
|
type DeployProjectResponse,
|
|
28
29
|
MetadataRequestOptions,
|
|
29
30
|
} from '@finos/legend-graph';
|
|
31
|
+
import type { Entity } from '@finos/legend-storage';
|
|
30
32
|
import { LegendStudioTelemetryHelper } from '../../../../__lib__/LegendStudioTelemetryHelper.js';
|
|
31
33
|
|
|
34
|
+
export const DEV_SNAPSHOT_VERSION = '1.0.0-SNAPSHOT';
|
|
35
|
+
|
|
32
36
|
export class DevMetadataState {
|
|
33
37
|
readonly editorStore: EditorStore;
|
|
34
38
|
result: DeployProjectResponse | undefined;
|
|
35
39
|
options: MetadataRequestOptions = new MetadataRequestOptions();
|
|
36
40
|
pushState = ActionState.create();
|
|
37
41
|
|
|
42
|
+
// Compare-with-dev state
|
|
43
|
+
compareState = ActionState.create();
|
|
44
|
+
currentWorkspaceCode: string | undefined;
|
|
45
|
+
snapshotCode: string | undefined;
|
|
46
|
+
snapshotNotAvailable = false;
|
|
47
|
+
isCompareModalOpen = false;
|
|
48
|
+
|
|
38
49
|
constructor(editorStore: EditorStore) {
|
|
39
50
|
this.editorStore = editorStore;
|
|
40
51
|
|
|
@@ -43,6 +54,14 @@ export class DevMetadataState {
|
|
|
43
54
|
push: flow,
|
|
44
55
|
options: observable,
|
|
45
56
|
setOptions: action,
|
|
57
|
+
compareState: observable,
|
|
58
|
+
currentWorkspaceCode: observable,
|
|
59
|
+
snapshotCode: observable,
|
|
60
|
+
snapshotNotAvailable: observable,
|
|
61
|
+
isCompareModalOpen: observable,
|
|
62
|
+
openCompareModal: action,
|
|
63
|
+
closeCompareModal: action,
|
|
64
|
+
compareWithSnapshot: flow,
|
|
46
65
|
});
|
|
47
66
|
}
|
|
48
67
|
|
|
@@ -50,6 +69,14 @@ export class DevMetadataState {
|
|
|
50
69
|
this.options = options;
|
|
51
70
|
}
|
|
52
71
|
|
|
72
|
+
openCompareModal(): void {
|
|
73
|
+
this.isCompareModalOpen = true;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
closeCompareModal(): void {
|
|
77
|
+
this.isCompareModalOpen = false;
|
|
78
|
+
}
|
|
79
|
+
|
|
53
80
|
get projectGAV(): { groupId: string; artifactId: string } | undefined {
|
|
54
81
|
const currentProjectConfiguration =
|
|
55
82
|
this.editorStore.projectConfigurationEditorState.projectConfiguration;
|
|
@@ -62,6 +89,55 @@ export class DevMetadataState {
|
|
|
62
89
|
return undefined;
|
|
63
90
|
}
|
|
64
91
|
|
|
92
|
+
*compareWithSnapshot(): GeneratorFn<void> {
|
|
93
|
+
try {
|
|
94
|
+
this.compareState.inProgress();
|
|
95
|
+
this.snapshotNotAvailable = false;
|
|
96
|
+
this.currentWorkspaceCode = undefined;
|
|
97
|
+
this.snapshotCode = undefined;
|
|
98
|
+
const gav = this.projectGAV;
|
|
99
|
+
assertNonNullable(gav, 'Project configuration is required to compare');
|
|
100
|
+
const graphManager = this.editorStore.graphManagerState.graphManager;
|
|
101
|
+
|
|
102
|
+
// 1) Current workspace -> Pure code (same as toggling to text mode)
|
|
103
|
+
const currentCode = (yield graphManager.graphToPureCode(
|
|
104
|
+
this.editorStore.graphManagerState.graph,
|
|
105
|
+
{ pretty: true, excludeUnknown: true },
|
|
106
|
+
)) as string;
|
|
107
|
+
this.currentWorkspaceCode = currentCode;
|
|
108
|
+
|
|
109
|
+
// 2) Snapshot from depot -> Pure code; if the call fails we treat
|
|
110
|
+
// it as "nothing deployed yet"
|
|
111
|
+
let snapshotEntities: Entity[] | undefined;
|
|
112
|
+
try {
|
|
113
|
+
const entitiesJson =
|
|
114
|
+
(yield this.editorStore.depotServerClient.getVersionEntities(
|
|
115
|
+
gav.groupId,
|
|
116
|
+
gav.artifactId,
|
|
117
|
+
DEV_SNAPSHOT_VERSION,
|
|
118
|
+
)) as PlainObject<Entity>[];
|
|
119
|
+
snapshotEntities = entitiesJson as unknown as Entity[];
|
|
120
|
+
} catch {
|
|
121
|
+
this.snapshotNotAvailable = true;
|
|
122
|
+
this.compareState.complete();
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const snapshotCode = (yield graphManager.entitiesToPureCode(
|
|
127
|
+
snapshotEntities,
|
|
128
|
+
{ pretty: true },
|
|
129
|
+
)) as string;
|
|
130
|
+
this.snapshotCode = snapshotCode;
|
|
131
|
+
this.compareState.complete();
|
|
132
|
+
} catch (error) {
|
|
133
|
+
assertErrorThrown(error);
|
|
134
|
+
this.editorStore.applicationStore.notificationService.notifyError(
|
|
135
|
+
`Error comparing with dev snapshot: ${error.message}`,
|
|
136
|
+
);
|
|
137
|
+
this.compareState.fail();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
65
141
|
*push(): GeneratorFn<void> {
|
|
66
142
|
try {
|
|
67
143
|
this.result = undefined;
|