@finos/legend-application-studio 28.21.26 → 28.21.28
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/__lib__/LegendStudioDocumentation.d.ts +1 -0
- package/lib/__lib__/LegendStudioDocumentation.d.ts.map +1 -1
- package/lib/__lib__/LegendStudioDocumentation.js +2 -0
- package/lib/__lib__/LegendStudioDocumentation.js.map +1 -1
- package/lib/__lib__/LegendStudioEvent.d.ts +2 -0
- package/lib/__lib__/LegendStudioEvent.d.ts.map +1 -1
- package/lib/__lib__/LegendStudioEvent.js +2 -0
- package/lib/__lib__/LegendStudioEvent.js.map +1 -1
- package/lib/__lib__/LegendStudioTelemetryHelper.d.ts +2 -0
- package/lib/__lib__/LegendStudioTelemetryHelper.d.ts.map +1 -1
- package/lib/__lib__/LegendStudioTelemetryHelper.js +6 -0
- package/lib/__lib__/LegendStudioTelemetryHelper.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 +15 -1
- 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 +100 -6
- package/lib/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.js.map +1 -1
- package/lib/components/editor/editor-group/service-editor/ServiceEditor.d.ts.map +1 -1
- package/lib/components/editor/editor-group/service-editor/ServiceEditor.js +16 -3
- package/lib/components/editor/editor-group/service-editor/ServiceEditor.js.map +1 -1
- package/lib/components/editor/editor-group/testable/LakehouseTestableEditor.d.ts +4 -0
- package/lib/components/editor/editor-group/testable/LakehouseTestableEditor.d.ts.map +1 -1
- package/lib/components/editor/editor-group/testable/LakehouseTestableEditor.js +10 -6
- package/lib/components/editor/editor-group/testable/LakehouseTestableEditor.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/testable/DataProductTestableState.d.ts +6 -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 +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.js.map +1 -1
- package/package.json +16 -16
- package/src/__lib__/LegendStudioDocumentation.ts +3 -0
- package/src/__lib__/LegendStudioEvent.ts +2 -0
- package/src/__lib__/LegendStudioTelemetryHelper.ts +22 -0
- package/src/components/editor/editor-group/dataProduct/DataProductEditor.tsx +24 -0
- package/src/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.tsx +373 -4
- package/src/components/editor/editor-group/service-editor/ServiceEditor.tsx +29 -3
- package/src/components/editor/editor-group/testable/LakehouseTestableEditor.tsx +26 -5
- package/src/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.ts +1 -1
package/src/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.tsx
CHANGED
|
@@ -17,11 +17,13 @@
|
|
|
17
17
|
import { observer } from 'mobx-react-lite';
|
|
18
18
|
import { flowResult } from 'mobx';
|
|
19
19
|
import {
|
|
20
|
+
BlankPanelContent,
|
|
20
21
|
BlankPanelPlaceholder,
|
|
21
22
|
clsx,
|
|
22
23
|
ContextMenu,
|
|
23
24
|
CustomSelectorInput,
|
|
24
25
|
Dialog,
|
|
26
|
+
FilledWindowMaximizeIcon,
|
|
25
27
|
MenuContent,
|
|
26
28
|
MenuContentItem,
|
|
27
29
|
Modal,
|
|
@@ -37,20 +39,42 @@ import {
|
|
|
37
39
|
PanelHeaderActions,
|
|
38
40
|
PanelLoadingIndicator,
|
|
39
41
|
PlusIcon,
|
|
42
|
+
RefreshIcon,
|
|
43
|
+
TimesIcon,
|
|
40
44
|
} from '@finos/legend-art';
|
|
41
|
-
import
|
|
45
|
+
import {
|
|
46
|
+
PrimitiveInstanceValue,
|
|
47
|
+
PrimitiveType,
|
|
48
|
+
type DataProductTestSuite,
|
|
49
|
+
type ValueSpecification,
|
|
50
|
+
} from '@finos/legend-graph';
|
|
42
51
|
import type { DataProductEditorState } from '../../../../../stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.js';
|
|
43
52
|
import {
|
|
53
|
+
DataProductValueSpecificationTestParameterState,
|
|
54
|
+
type DataProductTestParameterState,
|
|
55
|
+
DataProductTestState,
|
|
56
|
+
getAccessPointLambda,
|
|
44
57
|
type DataProductTestableState,
|
|
45
58
|
type DataProductTestSuiteState,
|
|
46
59
|
} from '../../../../../stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.js';
|
|
47
|
-
import { forwardRef, useRef, useState } from 'react';
|
|
48
|
-
import {
|
|
60
|
+
import { forwardRef, useEffect, useRef, useState } from 'react';
|
|
61
|
+
import {
|
|
62
|
+
getContentTypeWithParamFromQuery,
|
|
63
|
+
type TestParamContentType,
|
|
64
|
+
validateTestableId,
|
|
65
|
+
} from '../../../../../stores/editor/utils/TestableUtils.js';
|
|
49
66
|
import { useEditorStore } from '../../../EditorStoreProvider.js';
|
|
50
67
|
import { guaranteeNonNullable } from '@finos/legend-shared';
|
|
51
|
-
import {
|
|
68
|
+
import {
|
|
69
|
+
ExternalFormatParameterEditorModal,
|
|
70
|
+
RenameModal,
|
|
71
|
+
} from '../../testable/TestableSharedComponents.js';
|
|
52
72
|
import { testSuite_setId } from '../../../../../stores/graph-modifier/Testable_GraphModifierHelper.js';
|
|
53
73
|
import { LakehouseTestSuiteEditor } from '../../testable/LakehouseTestableEditor.js';
|
|
74
|
+
import {
|
|
75
|
+
BasicValueSpecificationEditor,
|
|
76
|
+
instanceValue_setValue,
|
|
77
|
+
} from '@finos/legend-query-builder';
|
|
54
78
|
|
|
55
79
|
// ─── Create Suite Modal ───────────────────────────────────────────────────────
|
|
56
80
|
|
|
@@ -291,6 +315,343 @@ const CreateTestModal = observer(
|
|
|
291
315
|
},
|
|
292
316
|
);
|
|
293
317
|
|
|
318
|
+
const DataProductTestParameterEditor = observer(
|
|
319
|
+
(props: {
|
|
320
|
+
testState: DataProductTestState;
|
|
321
|
+
paramState: DataProductTestParameterState;
|
|
322
|
+
isReadOnly: boolean;
|
|
323
|
+
contentTypeParamPair: TestParamContentType | undefined;
|
|
324
|
+
}) => {
|
|
325
|
+
const { testState, paramState, isReadOnly, contentTypeParamPair } = props;
|
|
326
|
+
const [showPopUp, setShowPopup] = useState(false);
|
|
327
|
+
const valueSpecParamState =
|
|
328
|
+
paramState instanceof DataProductValueSpecificationTestParameterState
|
|
329
|
+
? paramState
|
|
330
|
+
: undefined;
|
|
331
|
+
const valueSpec = valueSpecParamState?.valueSpec;
|
|
332
|
+
|
|
333
|
+
const paramIsRequired = Boolean(
|
|
334
|
+
testState.queryVariableExpressions.find(
|
|
335
|
+
(v) =>
|
|
336
|
+
v.name === paramState.parameterValue.name &&
|
|
337
|
+
v.multiplicity.lowerBound > 0,
|
|
338
|
+
),
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
const showCodeEditor =
|
|
342
|
+
Boolean(contentTypeParamPair) &&
|
|
343
|
+
valueSpecParamState?.varExpression.genericType?.value.rawType ===
|
|
344
|
+
PrimitiveType.STRING &&
|
|
345
|
+
valueSpec instanceof PrimitiveInstanceValue;
|
|
346
|
+
|
|
347
|
+
const type = contentTypeParamPair
|
|
348
|
+
? contentTypeParamPair.contentType
|
|
349
|
+
: (valueSpecParamState?.varExpression.genericType?.value.rawType.name ??
|
|
350
|
+
'unknown');
|
|
351
|
+
|
|
352
|
+
const openInPopUp = (): void => setShowPopup(!showPopUp);
|
|
353
|
+
const closePopUp = (): void => setShowPopup(false);
|
|
354
|
+
|
|
355
|
+
const updateParamValue = (val: string): void => {
|
|
356
|
+
if (
|
|
357
|
+
!(valueSpec instanceof PrimitiveInstanceValue) ||
|
|
358
|
+
!valueSpecParamState
|
|
359
|
+
) {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const nextValue =
|
|
363
|
+
valueSpecParamState.varExpression.genericType?.value.rawType ===
|
|
364
|
+
PrimitiveType.BYTE
|
|
365
|
+
? btoa(val)
|
|
366
|
+
: val;
|
|
367
|
+
instanceValue_setValue(
|
|
368
|
+
valueSpec,
|
|
369
|
+
nextValue,
|
|
370
|
+
0,
|
|
371
|
+
testState.editorStore.changeDetectionState.observerContext,
|
|
372
|
+
);
|
|
373
|
+
valueSpecParamState.updateValueSpecification(valueSpec);
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
return (
|
|
377
|
+
<div
|
|
378
|
+
key={paramState.parameterValue.name}
|
|
379
|
+
className="panel__content__form__section"
|
|
380
|
+
>
|
|
381
|
+
<div className="panel__content__form__section__header__label">
|
|
382
|
+
{paramState.parameterValue.name}
|
|
383
|
+
<button
|
|
384
|
+
className={clsx('type-tree__node__type__label', {})}
|
|
385
|
+
tabIndex={-1}
|
|
386
|
+
title={type}
|
|
387
|
+
>
|
|
388
|
+
{type}
|
|
389
|
+
</button>
|
|
390
|
+
</div>
|
|
391
|
+
|
|
392
|
+
{!valueSpecParamState && (
|
|
393
|
+
<BlankPanelContent>Unsupported parameter value</BlankPanelContent>
|
|
394
|
+
)}
|
|
395
|
+
|
|
396
|
+
{valueSpecParamState &&
|
|
397
|
+
showCodeEditor &&
|
|
398
|
+
valueSpec instanceof PrimitiveInstanceValue && (
|
|
399
|
+
<div className="data-product-test-editor__parameter__code-editor">
|
|
400
|
+
<textarea
|
|
401
|
+
className="panel__content__form__section__textarea value-spec-editor__input"
|
|
402
|
+
spellCheck={false}
|
|
403
|
+
value={
|
|
404
|
+
valueSpecParamState.varExpression.genericType?.value
|
|
405
|
+
.rawType === PrimitiveType.BYTE
|
|
406
|
+
? atob(valueSpec.values[0] as string)
|
|
407
|
+
: (valueSpec.values[0] as string)
|
|
408
|
+
}
|
|
409
|
+
placeholder={
|
|
410
|
+
(valueSpec.values[0] as string) === '' ? '(empty)' : undefined
|
|
411
|
+
}
|
|
412
|
+
onChange={(event) => updateParamValue(event.target.value)}
|
|
413
|
+
/>
|
|
414
|
+
{showPopUp && (
|
|
415
|
+
<ExternalFormatParameterEditorModal
|
|
416
|
+
valueSpec={valueSpecParamState.valueSpec}
|
|
417
|
+
varExpression={valueSpecParamState.varExpression}
|
|
418
|
+
onClose={closePopUp}
|
|
419
|
+
isReadOnly={isReadOnly}
|
|
420
|
+
updateParamValue={updateParamValue}
|
|
421
|
+
contentTypeParamPair={guaranteeNonNullable(
|
|
422
|
+
contentTypeParamPair,
|
|
423
|
+
)}
|
|
424
|
+
/>
|
|
425
|
+
)}
|
|
426
|
+
<div className="data-product-test-editor__parameter__value__actions">
|
|
427
|
+
<button
|
|
428
|
+
className="data-product-test-editor__parameter__code-editor__expand-btn"
|
|
429
|
+
onClick={openInPopUp}
|
|
430
|
+
tabIndex={-1}
|
|
431
|
+
title="Open in a popup..."
|
|
432
|
+
>
|
|
433
|
+
<FilledWindowMaximizeIcon />
|
|
434
|
+
</button>
|
|
435
|
+
<button
|
|
436
|
+
className="btn--icon btn--dark btn--sm data-product-test-editor__parameter__code-editor__expand-btn"
|
|
437
|
+
disabled={isReadOnly || paramIsRequired}
|
|
438
|
+
onClick={(): void =>
|
|
439
|
+
testState.removeParamValueState(paramState)
|
|
440
|
+
}
|
|
441
|
+
tabIndex={-1}
|
|
442
|
+
title={
|
|
443
|
+
paramIsRequired ? 'Parameter Required' : 'Remove Parameter'
|
|
444
|
+
}
|
|
445
|
+
>
|
|
446
|
+
<TimesIcon />
|
|
447
|
+
</button>
|
|
448
|
+
</div>
|
|
449
|
+
</div>
|
|
450
|
+
)}
|
|
451
|
+
|
|
452
|
+
{valueSpecParamState && !showCodeEditor && (
|
|
453
|
+
<div className="data-product-test-editor__parameter__value">
|
|
454
|
+
<BasicValueSpecificationEditor
|
|
455
|
+
valueSpecification={valueSpecParamState.valueSpec}
|
|
456
|
+
setValueSpecification={(val: ValueSpecification): void => {
|
|
457
|
+
valueSpecParamState.updateValueSpecification(val);
|
|
458
|
+
}}
|
|
459
|
+
graph={testState.editorStore.graphManagerState.graph}
|
|
460
|
+
observerContext={
|
|
461
|
+
testState.editorStore.changeDetectionState.observerContext
|
|
462
|
+
}
|
|
463
|
+
typeCheckOption={{
|
|
464
|
+
expectedType:
|
|
465
|
+
valueSpecParamState.varExpression.genericType?.value
|
|
466
|
+
.rawType ?? PrimitiveType.STRING,
|
|
467
|
+
}}
|
|
468
|
+
className="query-builder__parameters__value__editor"
|
|
469
|
+
resetValue={(): void => {
|
|
470
|
+
valueSpecParamState.resetValueSpec();
|
|
471
|
+
}}
|
|
472
|
+
/>
|
|
473
|
+
<div className="data-product-test-editor__parameter__value__actions">
|
|
474
|
+
<button
|
|
475
|
+
className="btn--icon btn--dark btn--sm"
|
|
476
|
+
disabled={isReadOnly || paramIsRequired}
|
|
477
|
+
onClick={(): void =>
|
|
478
|
+
testState.removeParamValueState(paramState)
|
|
479
|
+
}
|
|
480
|
+
tabIndex={-1}
|
|
481
|
+
title={
|
|
482
|
+
paramIsRequired ? 'Parameter Required' : 'Remove Parameter'
|
|
483
|
+
}
|
|
484
|
+
>
|
|
485
|
+
<TimesIcon />
|
|
486
|
+
</button>
|
|
487
|
+
</div>
|
|
488
|
+
</div>
|
|
489
|
+
)}
|
|
490
|
+
</div>
|
|
491
|
+
);
|
|
492
|
+
},
|
|
493
|
+
);
|
|
494
|
+
|
|
495
|
+
const NewDataProductParameterModal = observer(
|
|
496
|
+
(props: { testState: DataProductTestState; isReadOnly: boolean }) => {
|
|
497
|
+
const { testState, isReadOnly } = props;
|
|
498
|
+
const applicationStore = testState.editorStore.applicationStore;
|
|
499
|
+
const currentOption = {
|
|
500
|
+
value: testState.newParameterValueName,
|
|
501
|
+
label: testState.newParameterValueName,
|
|
502
|
+
};
|
|
503
|
+
const options = testState.newParamOptions;
|
|
504
|
+
const closeModal = (): void => testState.setShowNewParameterModal(false);
|
|
505
|
+
const onChange = (val: { label: string; value: string } | null): void => {
|
|
506
|
+
if (val === null) {
|
|
507
|
+
testState.setNewParameterValueName('');
|
|
508
|
+
} else if (val.value !== testState.newParameterValueName) {
|
|
509
|
+
testState.setNewParameterValueName(val.value);
|
|
510
|
+
}
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
return (
|
|
514
|
+
<Dialog
|
|
515
|
+
open={testState.showNewParameterModal}
|
|
516
|
+
onClose={closeModal}
|
|
517
|
+
classes={{ container: 'search-modal__container' }}
|
|
518
|
+
slotProps={{
|
|
519
|
+
paper: {
|
|
520
|
+
classes: { root: 'search-modal__inner-container' },
|
|
521
|
+
},
|
|
522
|
+
}}
|
|
523
|
+
>
|
|
524
|
+
<form
|
|
525
|
+
onSubmit={(event) => {
|
|
526
|
+
event.preventDefault();
|
|
527
|
+
testState.addParameterValue();
|
|
528
|
+
}}
|
|
529
|
+
className="modal modal--dark search-modal"
|
|
530
|
+
>
|
|
531
|
+
<div className="modal__title">New Test Parameter Value</div>
|
|
532
|
+
<CustomSelectorInput
|
|
533
|
+
className="panel__content__form__section__dropdown"
|
|
534
|
+
options={options}
|
|
535
|
+
onChange={onChange}
|
|
536
|
+
value={currentOption}
|
|
537
|
+
escapeClearsValue={true}
|
|
538
|
+
darkMode={
|
|
539
|
+
!applicationStore.layoutService
|
|
540
|
+
.TEMPORARY__isLightColorThemeEnabled
|
|
541
|
+
}
|
|
542
|
+
disabled={isReadOnly}
|
|
543
|
+
/>
|
|
544
|
+
<div className="search-modal__actions">
|
|
545
|
+
<button className="btn btn--dark" disabled={isReadOnly}>
|
|
546
|
+
Add
|
|
547
|
+
</button>
|
|
548
|
+
</div>
|
|
549
|
+
</form>
|
|
550
|
+
</Dialog>
|
|
551
|
+
);
|
|
552
|
+
},
|
|
553
|
+
);
|
|
554
|
+
|
|
555
|
+
const DataProductTestEditorExtension = observer(
|
|
556
|
+
(props: { testState: DataProductTestState; isReadOnly: boolean }) => {
|
|
557
|
+
const { testState, isReadOnly } = props;
|
|
558
|
+
|
|
559
|
+
useEffect(() => {
|
|
560
|
+
testState.syncWithQuery();
|
|
561
|
+
}, [testState]);
|
|
562
|
+
|
|
563
|
+
const hasQueryParameters = Boolean(
|
|
564
|
+
testState.queryVariableExpressions.length,
|
|
565
|
+
);
|
|
566
|
+
if (!hasQueryParameters) {
|
|
567
|
+
return null;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
const accessPoint = testState.suiteState.testableState.ownAccessPoints.find(
|
|
571
|
+
(ap) => ap.id === testState.test.accessPointId,
|
|
572
|
+
);
|
|
573
|
+
const query = accessPoint ? getAccessPointLambda(accessPoint) : undefined;
|
|
574
|
+
const contentTypeParamPairs = getContentTypeWithParamFromQuery(
|
|
575
|
+
query,
|
|
576
|
+
testState.editorStore,
|
|
577
|
+
);
|
|
578
|
+
|
|
579
|
+
const addParameter = (): void => {
|
|
580
|
+
testState.openNewParamModal();
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
const generateParameterValues = (): void => {
|
|
584
|
+
testState.generateTestParameterValues();
|
|
585
|
+
};
|
|
586
|
+
|
|
587
|
+
return (
|
|
588
|
+
<div className="data-product-test-editor__parameters-panel-container">
|
|
589
|
+
<div className="panel data-product-test-editor__parameters-panel">
|
|
590
|
+
<div className="data-product-test-editor__parameters-header">
|
|
591
|
+
<div className="data-product-test-editor__parameters-header__title">
|
|
592
|
+
<div className="data-product-test-editor__parameters-header__title__label">
|
|
593
|
+
Parameters
|
|
594
|
+
</div>
|
|
595
|
+
</div>
|
|
596
|
+
<div className="data-product-test-editor__parameters-header__actions">
|
|
597
|
+
<button
|
|
598
|
+
className="panel__header__action data-product-test-editor__generate-btn"
|
|
599
|
+
onClick={generateParameterValues}
|
|
600
|
+
disabled={!testState.newParamOptions.length}
|
|
601
|
+
title="Generate test parameter values"
|
|
602
|
+
tabIndex={-1}
|
|
603
|
+
>
|
|
604
|
+
<div className="data-product-test-editor__generate-btn__label">
|
|
605
|
+
<RefreshIcon className="data-product-test-editor__generate-btn__label__icon" />
|
|
606
|
+
<div className="data-product-test-editor__generate-btn__label__title">
|
|
607
|
+
Generate
|
|
608
|
+
</div>
|
|
609
|
+
</div>
|
|
610
|
+
</button>
|
|
611
|
+
<button
|
|
612
|
+
className="panel__header__action"
|
|
613
|
+
tabIndex={-1}
|
|
614
|
+
disabled={!testState.newParamOptions.length}
|
|
615
|
+
onClick={addParameter}
|
|
616
|
+
title="Add Parameter Value"
|
|
617
|
+
>
|
|
618
|
+
<PlusIcon />
|
|
619
|
+
</button>
|
|
620
|
+
</div>
|
|
621
|
+
</div>
|
|
622
|
+
|
|
623
|
+
<div className="data-product-test-editor__parameters">
|
|
624
|
+
{testState.parameterValueStates.map((paramState) => (
|
|
625
|
+
<DataProductTestParameterEditor
|
|
626
|
+
key={paramState.uuid}
|
|
627
|
+
isReadOnly={isReadOnly}
|
|
628
|
+
paramState={paramState}
|
|
629
|
+
testState={testState}
|
|
630
|
+
contentTypeParamPair={contentTypeParamPairs.find(
|
|
631
|
+
(pair) => pair.param === paramState.parameterValue.name,
|
|
632
|
+
)}
|
|
633
|
+
/>
|
|
634
|
+
))}
|
|
635
|
+
{testState.parameterValueStates.length === 0 && (
|
|
636
|
+
<BlankPanelPlaceholder
|
|
637
|
+
text="No parameter values"
|
|
638
|
+
tooltipText="Generate or add a parameter value"
|
|
639
|
+
/>
|
|
640
|
+
)}
|
|
641
|
+
</div>
|
|
642
|
+
</div>
|
|
643
|
+
|
|
644
|
+
{testState.showNewParameterModal && (
|
|
645
|
+
<NewDataProductParameterModal
|
|
646
|
+
testState={testState}
|
|
647
|
+
isReadOnly={isReadOnly}
|
|
648
|
+
/>
|
|
649
|
+
)}
|
|
650
|
+
</div>
|
|
651
|
+
);
|
|
652
|
+
},
|
|
653
|
+
);
|
|
654
|
+
|
|
294
655
|
// ─── Suite Tab Context Menu ───────────────────────────────────────────────────
|
|
295
656
|
|
|
296
657
|
const SuiteHeaderTabContextMenu = observer(
|
|
@@ -407,6 +768,14 @@ export const DataProductTestableEditor = observer(
|
|
|
407
768
|
suiteState={selectedSuiteState}
|
|
408
769
|
testableState={testableState}
|
|
409
770
|
isReadOnly={isReadOnly}
|
|
771
|
+
renderTestStateExtension={(testState) =>
|
|
772
|
+
testState instanceof DataProductTestState ? (
|
|
773
|
+
<DataProductTestEditorExtension
|
|
774
|
+
testState={testState}
|
|
775
|
+
isReadOnly={isReadOnly}
|
|
776
|
+
/>
|
|
777
|
+
) : null
|
|
778
|
+
}
|
|
410
779
|
/>
|
|
411
780
|
)}
|
|
412
781
|
{!dp.tests.length && (
|
|
@@ -37,7 +37,13 @@ import {
|
|
|
37
37
|
PanelContentLists,
|
|
38
38
|
SparkleIcon,
|
|
39
39
|
} from '@finos/legend-art';
|
|
40
|
-
import {
|
|
40
|
+
import {
|
|
41
|
+
assertErrorThrown,
|
|
42
|
+
debounce,
|
|
43
|
+
HttpStatus,
|
|
44
|
+
NetworkClientError,
|
|
45
|
+
prettyCONSTName,
|
|
46
|
+
} from '@finos/legend-shared';
|
|
41
47
|
import { ServiceExecutionEditor } from './ServiceExecutionEditor.js';
|
|
42
48
|
import { LEGEND_STUDIO_TEST_ID } from '../../../../__lib__/LegendStudioTesting.js';
|
|
43
49
|
import { ServiceRegistrationEditor } from './ServiceRegistrationEditor.js';
|
|
@@ -70,11 +76,11 @@ import {
|
|
|
70
76
|
import { LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY } from '../../../../__lib__/LegendStudioApplicationNavigationContext.js';
|
|
71
77
|
import { ServiceTestableEditor } from './testable/ServiceTestableEditor.js';
|
|
72
78
|
import { flowResult } from 'mobx';
|
|
73
|
-
import { LEGEND_STUDIO_DOCUMENTATION_KEY } from '../../../../__lib__/LegendStudioDocumentation.js';
|
|
74
|
-
import { DocumentationLink } from '@finos/legend-lego/application';
|
|
75
79
|
import { ServicePostValidationsEditor } from './ServicePostValidationEditor.js';
|
|
76
80
|
import type { DSL_Service_LegendStudioApplicationPlugin_Extension } from '../../../../stores/extensions/DSL_Service_LegendStudioApplicationPlugin_Extension.js';
|
|
77
81
|
import { LegendStudioTelemetryHelper } from '../../../../__lib__/LegendStudioTelemetryHelper.js';
|
|
82
|
+
import { LEGEND_STUDIO_DOCUMENTATION_KEY } from '../../../../__lib__/LegendStudioDocumentation.js';
|
|
83
|
+
import { DocumentationLink } from '@finos/legend-lego/application';
|
|
78
84
|
|
|
79
85
|
type UserOption = { label: string; value: string };
|
|
80
86
|
|
|
@@ -278,6 +284,26 @@ const ServiceGeneralEditor = observer(() => {
|
|
|
278
284
|
]);
|
|
279
285
|
const suggestion = await aiDocSuggester(serviceGrammar, legendAIUrl);
|
|
280
286
|
setAIDocSuggestion(suggestion);
|
|
287
|
+
} catch (error) {
|
|
288
|
+
assertErrorThrown(error);
|
|
289
|
+
LegendStudioTelemetryHelper.logEvent_ServiceLegendAISuggestFailure(
|
|
290
|
+
applicationStore.telemetryService,
|
|
291
|
+
service.path,
|
|
292
|
+
error.message,
|
|
293
|
+
);
|
|
294
|
+
if (
|
|
295
|
+
error instanceof NetworkClientError &&
|
|
296
|
+
(error.response.status === HttpStatus.UNAUTHORIZED ||
|
|
297
|
+
error.response.status === HttpStatus.FORBIDDEN)
|
|
298
|
+
) {
|
|
299
|
+
const docEntry = applicationStore.documentationService.getDocEntry(
|
|
300
|
+
LEGEND_STUDIO_DOCUMENTATION_KEY.LEGENDAI_HOW_TO_GET_ENTITLEMENTS,
|
|
301
|
+
);
|
|
302
|
+
if (docEntry?.url) {
|
|
303
|
+
error.message = `${error.message}. Please check how to get entitlements: ${docEntry.url}`;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
throw error;
|
|
281
307
|
} finally {
|
|
282
308
|
setIsSuggestingWithAI(false);
|
|
283
309
|
}
|
|
@@ -55,7 +55,7 @@ import {
|
|
|
55
55
|
} from '@finos/legend-art';
|
|
56
56
|
import type { AtomicTest, TestSuite } from '@finos/legend-graph';
|
|
57
57
|
import type { ActionState, GeneratorFn } from '@finos/legend-shared';
|
|
58
|
-
import { useState } from 'react';
|
|
58
|
+
import { useState, type ReactNode } from 'react';
|
|
59
59
|
import type { TestableTestEditorState } from '../../../../stores/editor/editor-state/element-editor-state/testable/TestableEditorState.js';
|
|
60
60
|
import {
|
|
61
61
|
TESTABLE_RESULT,
|
|
@@ -341,8 +341,14 @@ export const LakehouseTestDataEditor = observer(
|
|
|
341
341
|
// ─── Test editor: assertion viewer ────────────────────────────────────────────
|
|
342
342
|
|
|
343
343
|
export const LakehouseTestEditor = observer(
|
|
344
|
-
(props: {
|
|
345
|
-
|
|
344
|
+
(props: {
|
|
345
|
+
testState: TestableTestEditorState;
|
|
346
|
+
isReadOnly: boolean;
|
|
347
|
+
renderTestStateExtension?: (
|
|
348
|
+
testState: TestableTestEditorState,
|
|
349
|
+
) => ReactNode;
|
|
350
|
+
}) => {
|
|
351
|
+
const { testState, renderTestStateExtension } = props;
|
|
346
352
|
const selectedAssertion = testState.selectedAsertionState;
|
|
347
353
|
|
|
348
354
|
return (
|
|
@@ -353,6 +359,7 @@ export const LakehouseTestEditor = observer(
|
|
|
353
359
|
</div>
|
|
354
360
|
</div>
|
|
355
361
|
<div className="panel">
|
|
362
|
+
{renderTestStateExtension?.(testState)}
|
|
356
363
|
{selectedAssertion && (
|
|
357
364
|
<TestAssertionEditor testAssertionState={selectedAssertion} />
|
|
358
365
|
)}
|
|
@@ -449,8 +456,12 @@ export const LakehouseTestsEditor = observer(
|
|
|
449
456
|
suiteState: LakehouseSuiteStateForEditor;
|
|
450
457
|
testableState: LakehouseTestableStateForEditor;
|
|
451
458
|
isReadOnly: boolean;
|
|
459
|
+
renderTestStateExtension?: (
|
|
460
|
+
testState: TestableTestEditorState,
|
|
461
|
+
) => ReactNode;
|
|
452
462
|
}) => {
|
|
453
|
-
const { suiteState, testableState, isReadOnly } =
|
|
463
|
+
const { suiteState, testableState, isReadOnly, renderTestStateExtension } =
|
|
464
|
+
props;
|
|
454
465
|
const selectedTest = suiteState.selectTestState;
|
|
455
466
|
|
|
456
467
|
return (
|
|
@@ -513,6 +524,9 @@ export const LakehouseTestsEditor = observer(
|
|
|
513
524
|
<LakehouseTestEditor
|
|
514
525
|
testState={selectedTest}
|
|
515
526
|
isReadOnly={isReadOnly}
|
|
527
|
+
{...(renderTestStateExtension
|
|
528
|
+
? { renderTestStateExtension }
|
|
529
|
+
: {})}
|
|
516
530
|
/>
|
|
517
531
|
) : (
|
|
518
532
|
<BlankPanelPlaceholder
|
|
@@ -535,8 +549,12 @@ export const LakehouseTestSuiteEditor = observer(
|
|
|
535
549
|
suiteState: LakehouseSuiteStateForEditor;
|
|
536
550
|
testableState: LakehouseTestableStateForEditor;
|
|
537
551
|
isReadOnly: boolean;
|
|
552
|
+
renderTestStateExtension?: (
|
|
553
|
+
testState: TestableTestEditorState,
|
|
554
|
+
) => ReactNode;
|
|
538
555
|
}) => {
|
|
539
|
-
const { suiteState, testableState, isReadOnly } =
|
|
556
|
+
const { suiteState, testableState, isReadOnly, renderTestStateExtension } =
|
|
557
|
+
props;
|
|
540
558
|
|
|
541
559
|
return (
|
|
542
560
|
<div className="service-test-suite-editor">
|
|
@@ -555,6 +573,9 @@ export const LakehouseTestSuiteEditor = observer(
|
|
|
555
573
|
suiteState={suiteState}
|
|
556
574
|
testableState={testableState}
|
|
557
575
|
isReadOnly={isReadOnly}
|
|
576
|
+
{...(renderTestStateExtension
|
|
577
|
+
? { renderTestStateExtension }
|
|
578
|
+
: {})}
|
|
558
579
|
/>
|
|
559
580
|
</ResizablePanel>
|
|
560
581
|
</ResizablePanelGroup>
|
|
@@ -93,7 +93,7 @@ export type { LakehouseElementTestDataState as DataProductElementTestDataState }
|
|
|
93
93
|
* Returns the lambda for an access point (handles both LakehouseAccessPoint
|
|
94
94
|
* and FunctionAccessPoint).
|
|
95
95
|
*/
|
|
96
|
-
const getAccessPointLambda = (
|
|
96
|
+
export const getAccessPointLambda = (
|
|
97
97
|
accessPoint: AccessPoint,
|
|
98
98
|
): RawLambda | undefined =>
|
|
99
99
|
accessPoint instanceof LakehouseAccessPoint
|