@finos/legend-application-studio 28.21.13 → 28.21.15
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/application/LegendStudioApplicationConfig.d.ts +17 -0
- package/lib/application/LegendStudioApplicationConfig.d.ts.map +1 -1
- package/lib/application/LegendStudioApplicationConfig.js +2 -0
- package/lib/application/LegendStudioApplicationConfig.js.map +1 -1
- package/lib/components/editor/StatusBar.d.ts.map +1 -1
- package/lib/components/editor/StatusBar.js +8 -3
- package/lib/components/editor/StatusBar.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 +109 -15
- package/lib/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.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/LegendStudioBaseStore.d.ts +118 -0
- package/lib/stores/LegendStudioBaseStore.d.ts.map +1 -1
- package/lib/stores/LegendStudioBaseStore.js +380 -32
- package/lib/stores/LegendStudioBaseStore.js.map +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.d.ts +34 -1
- 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 +203 -2
- package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.js.map +1 -1
- package/package.json +16 -16
- package/src/application/LegendStudioApplicationConfig.ts +25 -1
- package/src/components/editor/StatusBar.tsx +21 -1
- package/src/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.tsx +370 -22
- package/src/stores/LegendStudioBaseStore.ts +432 -41
- package/src/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.ts +317 -0
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
type TestSuite,
|
|
22
22
|
type RawLambda,
|
|
23
23
|
type AccessorOwner,
|
|
24
|
+
type ValueSpecification,
|
|
24
25
|
DataProduct,
|
|
25
26
|
FunctionAccessPoint,
|
|
26
27
|
LakehouseAccessPoint,
|
|
@@ -35,21 +36,30 @@ import {
|
|
|
35
36
|
TestError,
|
|
36
37
|
TestExecutionStatus,
|
|
37
38
|
EqualToRelation,
|
|
39
|
+
FunctionParameterValue,
|
|
38
40
|
RelationRowTestData,
|
|
41
|
+
VariableExpression,
|
|
42
|
+
observe_FunctionParameterValue,
|
|
39
43
|
observe_RelationElement,
|
|
40
44
|
observe_RelationRowTestData,
|
|
41
45
|
observe_RelationElementsData,
|
|
42
46
|
observe_DataProductTestSuite,
|
|
47
|
+
observe_ValueSpecification,
|
|
48
|
+
buildLambdaVariableExpressions,
|
|
43
49
|
IngestDefinition,
|
|
44
50
|
getAccessorItemLabelForElement,
|
|
45
51
|
type AbstractPureGraphManager,
|
|
46
52
|
} from '@finos/legend-graph';
|
|
47
53
|
import {
|
|
48
54
|
type GeneratorFn,
|
|
55
|
+
type PlainObject,
|
|
49
56
|
ActionState,
|
|
50
57
|
assertErrorThrown,
|
|
58
|
+
filterByType,
|
|
59
|
+
guaranteeNonNullable,
|
|
51
60
|
deleteEntry,
|
|
52
61
|
addUniqueEntry,
|
|
62
|
+
returnUndefOnError,
|
|
53
63
|
uuid,
|
|
54
64
|
noop,
|
|
55
65
|
} from '@finos/legend-shared';
|
|
@@ -73,6 +83,7 @@ import {
|
|
|
73
83
|
TestableTestEditorState,
|
|
74
84
|
TestableTestSuiteEditorState,
|
|
75
85
|
} from '../../testable/TestableEditorState.js';
|
|
86
|
+
import { generateVariableExpressionMockValue } from '@finos/legend-query-builder';
|
|
76
87
|
|
|
77
88
|
const createEmptyRelationElement = (
|
|
78
89
|
itemId: string,
|
|
@@ -178,6 +189,73 @@ const inferDataProductItemColumns = async (
|
|
|
178
189
|
|
|
179
190
|
// ─── Per-test state ──────────────────────────────────────────────────────────
|
|
180
191
|
|
|
192
|
+
export class DataProductTestParameterState {
|
|
193
|
+
readonly uuid = uuid();
|
|
194
|
+
readonly editorStore: EditorStore;
|
|
195
|
+
readonly testState: DataProductTestState;
|
|
196
|
+
parameterValue: FunctionParameterValue;
|
|
197
|
+
|
|
198
|
+
constructor(
|
|
199
|
+
parameterValue: FunctionParameterValue,
|
|
200
|
+
editorStore: EditorStore,
|
|
201
|
+
testState: DataProductTestState,
|
|
202
|
+
) {
|
|
203
|
+
this.editorStore = editorStore;
|
|
204
|
+
this.testState = testState;
|
|
205
|
+
this.parameterValue = parameterValue;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export class DataProductValueSpecificationTestParameterState extends DataProductTestParameterState {
|
|
210
|
+
valueSpec: ValueSpecification;
|
|
211
|
+
varExpression: VariableExpression;
|
|
212
|
+
|
|
213
|
+
constructor(
|
|
214
|
+
parameterValue: FunctionParameterValue,
|
|
215
|
+
editorStore: EditorStore,
|
|
216
|
+
testState: DataProductTestState,
|
|
217
|
+
valueSpec: ValueSpecification,
|
|
218
|
+
varExpression: VariableExpression,
|
|
219
|
+
) {
|
|
220
|
+
super(parameterValue, editorStore, testState);
|
|
221
|
+
makeObservable(this, {
|
|
222
|
+
valueSpec: observable,
|
|
223
|
+
updateValueSpecification: action,
|
|
224
|
+
updateParameterValue: action,
|
|
225
|
+
resetValueSpec: action,
|
|
226
|
+
});
|
|
227
|
+
this.valueSpec = valueSpec;
|
|
228
|
+
this.varExpression = varExpression;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
updateValueSpecification(val: ValueSpecification): void {
|
|
232
|
+
this.valueSpec = observe_ValueSpecification(
|
|
233
|
+
val,
|
|
234
|
+
this.editorStore.changeDetectionState.observerContext,
|
|
235
|
+
);
|
|
236
|
+
this.updateParameterValue();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
updateParameterValue(): void {
|
|
240
|
+
const updatedValueSpec =
|
|
241
|
+
this.editorStore.graphManagerState.graphManager.serializeValueSpecification(
|
|
242
|
+
this.valueSpec,
|
|
243
|
+
);
|
|
244
|
+
this.parameterValue.value = updatedValueSpec;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
resetValueSpec(): void {
|
|
248
|
+
const mockValue = generateVariableExpressionMockValue(
|
|
249
|
+
this.varExpression,
|
|
250
|
+
this.editorStore.graphManagerState.graph,
|
|
251
|
+
this.editorStore.changeDetectionState.observerContext,
|
|
252
|
+
);
|
|
253
|
+
if (mockValue) {
|
|
254
|
+
this.updateValueSpecification(mockValue);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
181
259
|
export class DataProductTestState extends TestableTestEditorState {
|
|
182
260
|
readonly suiteState: DataProductTestSuiteState;
|
|
183
261
|
override test: DataProductAccessPointTest;
|
|
@@ -185,6 +263,9 @@ export class DataProductTestState extends TestableTestEditorState {
|
|
|
185
263
|
|
|
186
264
|
/** Wraps assertion.expected — drives both column definitions and test data rows. */
|
|
187
265
|
testDataRelationState: RelationElementState | undefined;
|
|
266
|
+
parameterValueStates: DataProductTestParameterState[] = [];
|
|
267
|
+
newParameterValueName = '';
|
|
268
|
+
showNewParameterModal = false;
|
|
188
269
|
|
|
189
270
|
constructor(
|
|
190
271
|
suiteState: DataProductTestSuiteState,
|
|
@@ -206,6 +287,9 @@ export class DataProductTestState extends TestableTestEditorState {
|
|
|
206
287
|
runningTestAction: observable,
|
|
207
288
|
// own observable
|
|
208
289
|
testDataRelationState: observable,
|
|
290
|
+
parameterValueStates: observable,
|
|
291
|
+
newParameterValueName: observable,
|
|
292
|
+
showNewParameterModal: observable,
|
|
209
293
|
// actions from base class
|
|
210
294
|
setSelectedTab: action,
|
|
211
295
|
setAssertionToRename: action,
|
|
@@ -214,14 +298,247 @@ export class DataProductTestState extends TestableTestEditorState {
|
|
|
214
298
|
openAssertion: action,
|
|
215
299
|
resetResult: action,
|
|
216
300
|
handleTestResult: action,
|
|
301
|
+
setNewParameterValueName: action,
|
|
302
|
+
setShowNewParameterModal: action,
|
|
303
|
+
openNewParamModal: action,
|
|
304
|
+
addParameterValue: action,
|
|
305
|
+
addExpressionParameterValue: action,
|
|
306
|
+
syncWithQuery: action,
|
|
307
|
+
generateTestParameterValues: action,
|
|
308
|
+
removeParamValueState: action,
|
|
217
309
|
// flow from base class
|
|
218
310
|
runTest: flow,
|
|
219
311
|
});
|
|
220
312
|
this.suiteState = suiteState;
|
|
221
313
|
this.test = test;
|
|
314
|
+
this.normalizePositionalParameters();
|
|
315
|
+
this.parameterValueStates = this.buildParameterStates();
|
|
222
316
|
this.buildTestDataRelationState().catch(noop());
|
|
223
317
|
}
|
|
224
318
|
|
|
319
|
+
private normalizePositionalParameters(): void {
|
|
320
|
+
const params = this.test.parameters ?? [];
|
|
321
|
+
if (!params.length) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const expressions = this.queryVariableExpressions;
|
|
326
|
+
if (!expressions.length) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const unnamedParams = params.filter((p) => !p.name);
|
|
331
|
+
if (!unnamedParams.length) {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const takenNames = new Set(
|
|
336
|
+
params.map((p) => p.name).filter((name) => Boolean(name)),
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
expressions.forEach((expr) => {
|
|
340
|
+
if (!takenNames.has(expr.name)) {
|
|
341
|
+
const nextUnnamed = unnamedParams.shift();
|
|
342
|
+
if (nextUnnamed) {
|
|
343
|
+
nextUnnamed.name = expr.name;
|
|
344
|
+
takenNames.add(expr.name);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
get queryVariableExpressions(): VariableExpression[] {
|
|
351
|
+
const accessPoint = this.suiteState.testableState.ownAccessPoints.find(
|
|
352
|
+
(ap) => ap.id === this.test.accessPointId,
|
|
353
|
+
);
|
|
354
|
+
const query = accessPoint ? getAccessPointLambda(accessPoint) : undefined;
|
|
355
|
+
if (!query) {
|
|
356
|
+
return [];
|
|
357
|
+
}
|
|
358
|
+
return buildLambdaVariableExpressions(
|
|
359
|
+
query,
|
|
360
|
+
this.editorStore.graphManagerState,
|
|
361
|
+
).filter(filterByType(VariableExpression));
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
get newParamOptions(): { value: string; label: string }[] {
|
|
365
|
+
const queryVarExpressions = this.queryVariableExpressions;
|
|
366
|
+
const currentParams = this.test.parameters ?? [];
|
|
367
|
+
return queryVarExpressions
|
|
368
|
+
.filter((v) => !currentParams.find((i) => i.name === v.name))
|
|
369
|
+
.map((e) => ({ value: e.name, label: e.name }));
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
setNewParameterValueName(val: string): void {
|
|
373
|
+
this.newParameterValueName = val;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
setShowNewParameterModal(val: boolean): void {
|
|
377
|
+
this.showNewParameterModal = val;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
openNewParamModal(): void {
|
|
381
|
+
this.setShowNewParameterModal(true);
|
|
382
|
+
const option = this.newParamOptions[0];
|
|
383
|
+
if (option) {
|
|
384
|
+
this.newParameterValueName = option.value;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
addParameterValue(): void {
|
|
389
|
+
try {
|
|
390
|
+
const expressions = this.queryVariableExpressions;
|
|
391
|
+
const expression = guaranteeNonNullable(
|
|
392
|
+
expressions.find((v) => v.name === this.newParameterValueName),
|
|
393
|
+
);
|
|
394
|
+
this.addExpressionParameterValue(expression);
|
|
395
|
+
} catch (error) {
|
|
396
|
+
assertErrorThrown(error);
|
|
397
|
+
this.editorStore.applicationStore.notificationService.notifyError(error);
|
|
398
|
+
} finally {
|
|
399
|
+
this.setShowNewParameterModal(false);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
syncWithQuery(): void {
|
|
404
|
+
this.normalizePositionalParameters();
|
|
405
|
+
|
|
406
|
+
this.parameterValueStates.forEach((paramState) => {
|
|
407
|
+
const expression = this.queryVariableExpressions.find(
|
|
408
|
+
(v) => v.name === paramState.parameterValue.name,
|
|
409
|
+
);
|
|
410
|
+
if (!expression) {
|
|
411
|
+
deleteEntry(this.parameterValueStates, paramState);
|
|
412
|
+
deleteEntry(this.test.parameters ?? [], paramState.parameterValue);
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
this.queryVariableExpressions.forEach((v) => {
|
|
417
|
+
const multiplicity = v.multiplicity;
|
|
418
|
+
const isRequired = multiplicity.lowerBound > 0;
|
|
419
|
+
const paramState = this.parameterValueStates.find(
|
|
420
|
+
(p) => p.parameterValue.name === v.name,
|
|
421
|
+
);
|
|
422
|
+
if (!paramState && isRequired) {
|
|
423
|
+
this.addExpressionParameterValue(v);
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
addExpressionParameterValue(expression: VariableExpression): void {
|
|
429
|
+
try {
|
|
430
|
+
const mockValue = guaranteeNonNullable(
|
|
431
|
+
generateVariableExpressionMockValue(
|
|
432
|
+
expression,
|
|
433
|
+
this.editorStore.graphManagerState.graph,
|
|
434
|
+
this.editorStore.changeDetectionState.observerContext,
|
|
435
|
+
),
|
|
436
|
+
);
|
|
437
|
+
const paramValue = observe_FunctionParameterValue(
|
|
438
|
+
new FunctionParameterValue(),
|
|
439
|
+
);
|
|
440
|
+
paramValue.name = expression.name;
|
|
441
|
+
paramValue.value =
|
|
442
|
+
this.editorStore.graphManagerState.graphManager.serializeValueSpecification(
|
|
443
|
+
mockValue,
|
|
444
|
+
);
|
|
445
|
+
if (this.test.parameters) {
|
|
446
|
+
this.test.parameters.push(paramValue);
|
|
447
|
+
} else {
|
|
448
|
+
this.test.parameters = [paramValue];
|
|
449
|
+
}
|
|
450
|
+
const paramValueState =
|
|
451
|
+
new DataProductValueSpecificationTestParameterState(
|
|
452
|
+
paramValue,
|
|
453
|
+
this.editorStore,
|
|
454
|
+
this,
|
|
455
|
+
observe_ValueSpecification(
|
|
456
|
+
mockValue,
|
|
457
|
+
this.editorStore.changeDetectionState.observerContext,
|
|
458
|
+
),
|
|
459
|
+
expression,
|
|
460
|
+
);
|
|
461
|
+
this.parameterValueStates.push(paramValueState);
|
|
462
|
+
} catch (error) {
|
|
463
|
+
assertErrorThrown(error);
|
|
464
|
+
this.editorStore.applicationStore.notificationService.notifyError(error);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
generateTestParameterValues(): void {
|
|
469
|
+
try {
|
|
470
|
+
const varExpressions = this.queryVariableExpressions;
|
|
471
|
+
const parameterValueStates = varExpressions
|
|
472
|
+
.map((varExpression) => {
|
|
473
|
+
const mockValue = generateVariableExpressionMockValue(
|
|
474
|
+
varExpression,
|
|
475
|
+
this.editorStore.graphManagerState.graph,
|
|
476
|
+
this.editorStore.changeDetectionState.observerContext,
|
|
477
|
+
);
|
|
478
|
+
if (mockValue) {
|
|
479
|
+
const paramValue = observe_FunctionParameterValue(
|
|
480
|
+
new FunctionParameterValue(),
|
|
481
|
+
);
|
|
482
|
+
paramValue.name = varExpression.name;
|
|
483
|
+
paramValue.value =
|
|
484
|
+
this.editorStore.graphManagerState.graphManager.serializeValueSpecification(
|
|
485
|
+
mockValue,
|
|
486
|
+
);
|
|
487
|
+
return new DataProductValueSpecificationTestParameterState(
|
|
488
|
+
paramValue,
|
|
489
|
+
this.editorStore,
|
|
490
|
+
this,
|
|
491
|
+
mockValue,
|
|
492
|
+
varExpression,
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
return undefined;
|
|
496
|
+
})
|
|
497
|
+
.filter(
|
|
498
|
+
(value): value is DataProductValueSpecificationTestParameterState =>
|
|
499
|
+
value !== undefined,
|
|
500
|
+
);
|
|
501
|
+
this.test.parameters = parameterValueStates.map((s) => s.parameterValue);
|
|
502
|
+
this.parameterValueStates = parameterValueStates;
|
|
503
|
+
} catch (error) {
|
|
504
|
+
assertErrorThrown(error);
|
|
505
|
+
this.editorStore.applicationStore.notificationService.notifyError(
|
|
506
|
+
`Unable to generate parameter values: ${error.message}`,
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
buildParameterStates(): DataProductTestParameterState[] {
|
|
512
|
+
const varExpressions = this.queryVariableExpressions;
|
|
513
|
+
const paramValues = this.test.parameters ?? [];
|
|
514
|
+
return paramValues.map((paramValue) => {
|
|
515
|
+
const spec = returnUndefOnError(() =>
|
|
516
|
+
this.editorStore.graphManagerState.graphManager.buildValueSpecification(
|
|
517
|
+
paramValue.value as PlainObject,
|
|
518
|
+
this.editorStore.graphManagerState.graph,
|
|
519
|
+
),
|
|
520
|
+
);
|
|
521
|
+
const expression = varExpressions.find((e) => e.name === paramValue.name);
|
|
522
|
+
return spec && expression
|
|
523
|
+
? new DataProductValueSpecificationTestParameterState(
|
|
524
|
+
paramValue,
|
|
525
|
+
this.editorStore,
|
|
526
|
+
this,
|
|
527
|
+
observe_ValueSpecification(
|
|
528
|
+
spec,
|
|
529
|
+
this.editorStore.changeDetectionState.observerContext,
|
|
530
|
+
),
|
|
531
|
+
expression,
|
|
532
|
+
)
|
|
533
|
+
: new DataProductTestParameterState(paramValue, this.editorStore, this);
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
removeParamValueState(paramState: DataProductTestParameterState): void {
|
|
538
|
+
deleteEntry(this.parameterValueStates, paramState);
|
|
539
|
+
deleteEntry(this.test.parameters ?? [], paramState.parameterValue);
|
|
540
|
+
}
|
|
541
|
+
|
|
225
542
|
private async buildTestDataRelationState(): Promise<void> {
|
|
226
543
|
const assertion = this.test.assertions.find(
|
|
227
544
|
(a): a is EqualToRelation => a instanceof EqualToRelation,
|