@finos/legend-application-studio 28.21.12 → 28.21.14
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/ActivityBar.d.ts +13 -0
- package/lib/components/editor/ActivityBar.d.ts.map +1 -1
- package/lib/components/editor/ActivityBar.js +36 -15
- package/lib/components/editor/ActivityBar.js.map +1 -1
- package/lib/components/editor/editor-group/dataProduct/DataProductEditor.d.ts +2 -0
- package/lib/components/editor/editor-group/dataProduct/DataProductEditor.d.ts.map +1 -1
- package/lib/components/editor/editor-group/dataProduct/DataProductEditor.js +81 -19
- 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 +109 -15
- 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 +4 -1
- package/lib/components/editor/editor-group/service-editor/ServiceEditor.js.map +1 -1
- package/lib/components/workspace-setup/WorkspaceSetup.d.ts.map +1 -1
- package/lib/components/workspace-setup/WorkspaceSetup.js +20 -3
- package/lib/components/workspace-setup/WorkspaceSetup.js.map +1 -1
- package/lib/index.css +2 -2
- package/lib/index.css.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/package.json +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/lib/stores/extensions/DSL_DataProduct_LegendStudioApplicationPlugin_Extension.d.ts +61 -0
- package/lib/stores/extensions/DSL_DataProduct_LegendStudioApplicationPlugin_Extension.d.ts.map +1 -0
- package/lib/stores/extensions/DSL_DataProduct_LegendStudioApplicationPlugin_Extension.js +17 -0
- package/lib/stores/extensions/DSL_DataProduct_LegendStudioApplicationPlugin_Extension.js.map +1 -0
- package/lib/stores/extensions/DSL_Service_LegendStudioApplicationPlugin_Extension.d.ts +2 -2
- package/lib/stores/extensions/DSL_Service_LegendStudioApplicationPlugin_Extension.d.ts.map +1 -1
- package/lib/stores/workspace-setup/WorkspaceSetupStore.d.ts.map +1 -1
- package/lib/stores/workspace-setup/WorkspaceSetupStore.js +6 -2
- package/lib/stores/workspace-setup/WorkspaceSetupStore.js.map +1 -1
- package/package.json +13 -13
- package/src/components/editor/ActivityBar.tsx +61 -38
- package/src/components/editor/editor-group/dataProduct/DataProductEditor.tsx +207 -29
- package/src/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.tsx +370 -22
- package/src/components/editor/editor-group/service-editor/ServiceEditor.tsx +5 -1
- package/src/components/workspace-setup/WorkspaceSetup.tsx +27 -2
- package/src/index.ts +1 -0
- package/src/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.ts +317 -0
- package/src/stores/extensions/DSL_DataProduct_LegendStudioApplicationPlugin_Extension.ts +77 -0
- package/src/stores/extensions/DSL_Service_LegendStudioApplicationPlugin_Extension.ts +1 -2
- package/src/stores/workspace-setup/WorkspaceSetupStore.ts +5 -0
- package/tsconfig.json +1 -0
package/src/components/editor/editor-group/dataProduct/testable/DataProductTestableEditor.tsx
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
CustomSelectorInput,
|
|
25
25
|
Dialog,
|
|
26
26
|
ErrorWarnIcon,
|
|
27
|
+
FilledWindowMaximizeIcon,
|
|
27
28
|
MenuContent,
|
|
28
29
|
MenuContentItem,
|
|
29
30
|
Modal,
|
|
@@ -40,6 +41,7 @@ import {
|
|
|
40
41
|
PanelLoadingIndicator,
|
|
41
42
|
PlayIcon,
|
|
42
43
|
PlusIcon,
|
|
44
|
+
RefreshIcon,
|
|
43
45
|
ResizablePanel,
|
|
44
46
|
ResizablePanelGroup,
|
|
45
47
|
ResizablePanelSplitter,
|
|
@@ -47,9 +49,19 @@ import {
|
|
|
47
49
|
RunAllIcon,
|
|
48
50
|
TimesIcon,
|
|
49
51
|
} from '@finos/legend-art';
|
|
50
|
-
import
|
|
52
|
+
import {
|
|
53
|
+
FunctionAccessPoint,
|
|
54
|
+
LakehouseAccessPoint,
|
|
55
|
+
PrimitiveInstanceValue,
|
|
56
|
+
PrimitiveType,
|
|
57
|
+
type RawLambda,
|
|
58
|
+
type DataProductTestSuite,
|
|
59
|
+
type ValueSpecification,
|
|
60
|
+
} from '@finos/legend-graph';
|
|
51
61
|
import type { DataProductEditorState } from '../../../../../stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.js';
|
|
52
62
|
import {
|
|
63
|
+
DataProductValueSpecificationTestParameterState,
|
|
64
|
+
type DataProductTestParameterState,
|
|
53
65
|
type DataProductTestableState,
|
|
54
66
|
type DataProductTestSuiteState,
|
|
55
67
|
type DataProductTestState,
|
|
@@ -63,14 +75,24 @@ import {
|
|
|
63
75
|
getTestableResultFromTestResult,
|
|
64
76
|
} from '../../../../../stores/editor/sidebar-state/testable/GlobalTestRunnerState.js';
|
|
65
77
|
import { RelationElementsDataEditor } from '../../data-editor/RelationElementsDataEditor.js';
|
|
66
|
-
import {
|
|
78
|
+
import {
|
|
79
|
+
getContentTypeWithParamFromQuery,
|
|
80
|
+
type TestParamContentType,
|
|
81
|
+
validateTestableId,
|
|
82
|
+
} from '../../../../../stores/editor/utils/TestableUtils.js';
|
|
67
83
|
import { useEditorStore } from '../../../EditorStoreProvider.js';
|
|
68
84
|
import { guaranteeNonNullable } from '@finos/legend-shared';
|
|
69
85
|
import {
|
|
86
|
+
ExternalFormatParameterEditorModal,
|
|
70
87
|
RenameModal,
|
|
71
88
|
TestAssertionEditor,
|
|
72
89
|
} from '../../testable/TestableSharedComponents.js';
|
|
73
90
|
import { testSuite_setId } from '../../../../../stores/graph-modifier/Testable_GraphModifierHelper.js';
|
|
91
|
+
import { TESTABLE_TEST_TAB } from '../../../../../stores/editor/editor-state/element-editor-state/testable/TestableEditorState.js';
|
|
92
|
+
import {
|
|
93
|
+
BasicValueSpecificationEditor,
|
|
94
|
+
instanceValue_setValue,
|
|
95
|
+
} from '@finos/legend-query-builder';
|
|
74
96
|
|
|
75
97
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
76
98
|
// Create Suite Modal — test name + access point (datasets are auto-inferred)
|
|
@@ -489,11 +511,11 @@ const DataProductTestDataEditor = observer(
|
|
|
489
511
|
|
|
490
512
|
return (
|
|
491
513
|
<div
|
|
492
|
-
className={clsx('
|
|
493
|
-
'
|
|
514
|
+
className={clsx('data-product-test-data-editor panel', {
|
|
515
|
+
'data-product-test-data-editor--no-data': !hasTestData,
|
|
494
516
|
})}
|
|
495
517
|
>
|
|
496
|
-
<div className="
|
|
518
|
+
<div className="data-product-test-data-editor__data">
|
|
497
519
|
<ResizablePanelGroup orientation="vertical">
|
|
498
520
|
{/* Left: elements list — always visible */}
|
|
499
521
|
<ResizablePanel minSize={100} size={180}>
|
|
@@ -515,7 +537,7 @@ const DataProductTestDataEditor = observer(
|
|
|
515
537
|
)}
|
|
516
538
|
</div>
|
|
517
539
|
{!hasTestData ? (
|
|
518
|
-
<div className="
|
|
540
|
+
<div className="data-product-test-data-editor__warning">
|
|
519
541
|
<ErrorWarnIcon />
|
|
520
542
|
<span>Add an element to configure test data</span>
|
|
521
543
|
</div>
|
|
@@ -638,26 +660,352 @@ const TestItem = observer(
|
|
|
638
660
|
// Test Editor — shows SETUP (input data) and ASSERTION (expected + result) tabs
|
|
639
661
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
640
662
|
|
|
663
|
+
const DataProductTestParameterEditor = observer(
|
|
664
|
+
(props: {
|
|
665
|
+
isReadOnly: boolean;
|
|
666
|
+
paramState: DataProductTestParameterState;
|
|
667
|
+
testState: DataProductTestState;
|
|
668
|
+
contentTypeParamPair: TestParamContentType | undefined;
|
|
669
|
+
}) => {
|
|
670
|
+
const { testState, paramState, isReadOnly, contentTypeParamPair } = props;
|
|
671
|
+
const [showPopUp, setShowPopUp] = useState(false);
|
|
672
|
+
const valueSpecificationParamState =
|
|
673
|
+
paramState instanceof DataProductValueSpecificationTestParameterState
|
|
674
|
+
? paramState
|
|
675
|
+
: undefined;
|
|
676
|
+
const paramExpression = valueSpecificationParamState?.varExpression;
|
|
677
|
+
const paramIsRequired = (paramExpression?.multiplicity.lowerBound ?? 0) > 0;
|
|
678
|
+
const type = contentTypeParamPair
|
|
679
|
+
? contentTypeParamPair.contentType
|
|
680
|
+
: (paramExpression?.genericType?.value.rawType.name ?? 'unknown');
|
|
681
|
+
const primitiveValueSpecification =
|
|
682
|
+
valueSpecificationParamState?.valueSpec instanceof PrimitiveInstanceValue
|
|
683
|
+
? valueSpecificationParamState.valueSpec
|
|
684
|
+
: undefined;
|
|
685
|
+
const primitiveValue = primitiveValueSpecification?.values[0] as
|
|
686
|
+
| string
|
|
687
|
+
| undefined;
|
|
688
|
+
const paramValue =
|
|
689
|
+
paramExpression?.genericType?.value.rawType === PrimitiveType.BYTE &&
|
|
690
|
+
primitiveValue !== undefined
|
|
691
|
+
? atob(primitiveValue)
|
|
692
|
+
: (primitiveValue ?? '');
|
|
693
|
+
|
|
694
|
+
const openInPopUp = (): void => setShowPopUp(!showPopUp);
|
|
695
|
+
const closePopUp = (): void => setShowPopUp(false);
|
|
696
|
+
const updateParamValue = (val: string): void => {
|
|
697
|
+
if (
|
|
698
|
+
primitiveValueSpecification &&
|
|
699
|
+
valueSpecificationParamState &&
|
|
700
|
+
paramExpression
|
|
701
|
+
) {
|
|
702
|
+
instanceValue_setValue(
|
|
703
|
+
primitiveValueSpecification,
|
|
704
|
+
paramExpression.genericType?.value.rawType === PrimitiveType.BYTE
|
|
705
|
+
? btoa(val)
|
|
706
|
+
: val,
|
|
707
|
+
0,
|
|
708
|
+
testState.editorStore.changeDetectionState.observerContext,
|
|
709
|
+
);
|
|
710
|
+
valueSpecificationParamState.updateValueSpecification(
|
|
711
|
+
primitiveValueSpecification,
|
|
712
|
+
);
|
|
713
|
+
}
|
|
714
|
+
};
|
|
715
|
+
|
|
716
|
+
return (
|
|
717
|
+
<div
|
|
718
|
+
key={paramState.parameterValue.name}
|
|
719
|
+
className="panel__content__form__section"
|
|
720
|
+
>
|
|
721
|
+
<div className="panel__content__form__section__header__label">
|
|
722
|
+
{paramState.parameterValue.name}
|
|
723
|
+
<button
|
|
724
|
+
className={clsx('type-tree__node__type__label', {})}
|
|
725
|
+
tabIndex={-1}
|
|
726
|
+
title={type}
|
|
727
|
+
>
|
|
728
|
+
{type}
|
|
729
|
+
</button>
|
|
730
|
+
</div>
|
|
731
|
+
{!valueSpecificationParamState || !paramExpression ? (
|
|
732
|
+
<BlankPanelPlaceholder
|
|
733
|
+
text="Unsupported parameter value"
|
|
734
|
+
tooltipText="This parameter was preserved but cannot currently be edited in form mode"
|
|
735
|
+
/>
|
|
736
|
+
) : contentTypeParamPair && primitiveValueSpecification ? (
|
|
737
|
+
<div className="data-product-test-editor__parameter__code-editor">
|
|
738
|
+
<textarea
|
|
739
|
+
className="panel__content__form__section__textarea value-spec-editor__input"
|
|
740
|
+
spellCheck={false}
|
|
741
|
+
value={paramValue}
|
|
742
|
+
placeholder={primitiveValue === '' ? '(empty)' : undefined}
|
|
743
|
+
onChange={(event) => {
|
|
744
|
+
updateParamValue(event.target.value);
|
|
745
|
+
}}
|
|
746
|
+
/>
|
|
747
|
+
{showPopUp && (
|
|
748
|
+
<ExternalFormatParameterEditorModal
|
|
749
|
+
valueSpec={valueSpecificationParamState.valueSpec}
|
|
750
|
+
varExpression={paramExpression}
|
|
751
|
+
isReadOnly={isReadOnly}
|
|
752
|
+
onClose={closePopUp}
|
|
753
|
+
updateParamValue={updateParamValue}
|
|
754
|
+
contentTypeParamPair={contentTypeParamPair}
|
|
755
|
+
/>
|
|
756
|
+
)}
|
|
757
|
+
<div className="data-product-test-editor__parameter__value__actions">
|
|
758
|
+
<button
|
|
759
|
+
className="data-product-test-editor__parameter__code-editor__expand-btn"
|
|
760
|
+
onClick={openInPopUp}
|
|
761
|
+
tabIndex={-1}
|
|
762
|
+
title="Open in a popup..."
|
|
763
|
+
>
|
|
764
|
+
<FilledWindowMaximizeIcon />
|
|
765
|
+
</button>
|
|
766
|
+
<button
|
|
767
|
+
className="btn--icon btn--dark btn--sm data-product-test-editor__parameter__code-editor__expand-btn"
|
|
768
|
+
disabled={isReadOnly || paramIsRequired}
|
|
769
|
+
onClick={(): void =>
|
|
770
|
+
testState.removeParamValueState(paramState)
|
|
771
|
+
}
|
|
772
|
+
tabIndex={-1}
|
|
773
|
+
title={
|
|
774
|
+
paramIsRequired ? 'Parameter Required' : 'Remove Parameter'
|
|
775
|
+
}
|
|
776
|
+
>
|
|
777
|
+
<TimesIcon />
|
|
778
|
+
</button>
|
|
779
|
+
</div>
|
|
780
|
+
</div>
|
|
781
|
+
) : (
|
|
782
|
+
<div className="data-product-test-editor__parameter__value">
|
|
783
|
+
<BasicValueSpecificationEditor
|
|
784
|
+
valueSpecification={valueSpecificationParamState.valueSpec}
|
|
785
|
+
setValueSpecification={(val: ValueSpecification): void => {
|
|
786
|
+
valueSpecificationParamState.updateValueSpecification(val);
|
|
787
|
+
}}
|
|
788
|
+
graph={testState.editorStore.graphManagerState.graph}
|
|
789
|
+
observerContext={
|
|
790
|
+
testState.editorStore.changeDetectionState.observerContext
|
|
791
|
+
}
|
|
792
|
+
typeCheckOption={{
|
|
793
|
+
expectedType:
|
|
794
|
+
paramExpression.genericType?.value.rawType ??
|
|
795
|
+
PrimitiveType.STRING,
|
|
796
|
+
}}
|
|
797
|
+
className="query-builder__parameters__value__editor"
|
|
798
|
+
resetValue={(): void => {
|
|
799
|
+
valueSpecificationParamState.resetValueSpec();
|
|
800
|
+
}}
|
|
801
|
+
/>
|
|
802
|
+
<div className="data-product-test-editor__parameter__value__actions">
|
|
803
|
+
<button
|
|
804
|
+
className="btn--icon btn--dark btn--sm"
|
|
805
|
+
disabled={isReadOnly || paramIsRequired}
|
|
806
|
+
onClick={(): void =>
|
|
807
|
+
testState.removeParamValueState(paramState)
|
|
808
|
+
}
|
|
809
|
+
tabIndex={-1}
|
|
810
|
+
title={
|
|
811
|
+
paramIsRequired ? 'Parameter Required' : 'Remove Parameter'
|
|
812
|
+
}
|
|
813
|
+
>
|
|
814
|
+
<TimesIcon />
|
|
815
|
+
</button>
|
|
816
|
+
</div>
|
|
817
|
+
</div>
|
|
818
|
+
)}
|
|
819
|
+
</div>
|
|
820
|
+
);
|
|
821
|
+
},
|
|
822
|
+
);
|
|
823
|
+
|
|
824
|
+
const NewDataProductParameterModal = observer(
|
|
825
|
+
(props: { testState: DataProductTestState; isReadOnly: boolean }) => {
|
|
826
|
+
const { testState, isReadOnly } = props;
|
|
827
|
+
const applicationStore = testState.editorStore.applicationStore;
|
|
828
|
+
const currentOption = {
|
|
829
|
+
value: testState.newParameterValueName,
|
|
830
|
+
label: testState.newParameterValueName,
|
|
831
|
+
};
|
|
832
|
+
const options = testState.newParamOptions;
|
|
833
|
+
const closeModal = (): void => testState.setShowNewParameterModal(false);
|
|
834
|
+
const onChange = (val: { label: string; value: string } | null): void => {
|
|
835
|
+
if (val === null) {
|
|
836
|
+
testState.setNewParameterValueName('');
|
|
837
|
+
} else if (val.value !== testState.newParameterValueName) {
|
|
838
|
+
testState.setNewParameterValueName(val.value);
|
|
839
|
+
}
|
|
840
|
+
};
|
|
841
|
+
return (
|
|
842
|
+
<Dialog
|
|
843
|
+
open={testState.showNewParameterModal}
|
|
844
|
+
onClose={closeModal}
|
|
845
|
+
classes={{ container: 'search-modal__container' }}
|
|
846
|
+
slotProps={{
|
|
847
|
+
paper: {
|
|
848
|
+
classes: { root: 'search-modal__inner-container' },
|
|
849
|
+
},
|
|
850
|
+
}}
|
|
851
|
+
>
|
|
852
|
+
<form
|
|
853
|
+
onSubmit={(event) => {
|
|
854
|
+
event.preventDefault();
|
|
855
|
+
testState.addParameterValue();
|
|
856
|
+
}}
|
|
857
|
+
className="modal modal--dark search-modal"
|
|
858
|
+
>
|
|
859
|
+
<div className="modal__title">New Test Parameter Value</div>
|
|
860
|
+
<CustomSelectorInput
|
|
861
|
+
className="panel__content__form__section__dropdown"
|
|
862
|
+
options={options}
|
|
863
|
+
onChange={onChange}
|
|
864
|
+
value={currentOption}
|
|
865
|
+
escapeClearsValue={true}
|
|
866
|
+
darkMode={
|
|
867
|
+
!applicationStore.layoutService
|
|
868
|
+
.TEMPORARY__isLightColorThemeEnabled
|
|
869
|
+
}
|
|
870
|
+
disabled={isReadOnly}
|
|
871
|
+
/>
|
|
872
|
+
<div className="search-modal__actions">
|
|
873
|
+
<button className="btn btn--dark" disabled={isReadOnly}>
|
|
874
|
+
Add
|
|
875
|
+
</button>
|
|
876
|
+
</div>
|
|
877
|
+
</form>
|
|
878
|
+
</Dialog>
|
|
879
|
+
);
|
|
880
|
+
},
|
|
881
|
+
);
|
|
882
|
+
|
|
641
883
|
const DataProductTestEditor = observer(
|
|
642
884
|
(props: { testState: DataProductTestState; isReadOnly: boolean }) => {
|
|
643
|
-
const { testState } = props;
|
|
885
|
+
const { testState, isReadOnly } = props;
|
|
886
|
+
const selectedTab = testState.selectedTab;
|
|
644
887
|
const selectedAssertion = testState.selectedAsertionState;
|
|
645
888
|
|
|
889
|
+
useEffect(() => {
|
|
890
|
+
testState.syncWithQuery();
|
|
891
|
+
}, [testState]);
|
|
892
|
+
|
|
893
|
+
const addParameter = (): void => {
|
|
894
|
+
testState.openNewParamModal();
|
|
895
|
+
};
|
|
896
|
+
|
|
897
|
+
const generateParameterValues = (): void => {
|
|
898
|
+
testState.generateTestParameterValues();
|
|
899
|
+
};
|
|
900
|
+
|
|
901
|
+
const accessPoint = testState.suiteState.testableState.ownAccessPoints.find(
|
|
902
|
+
(ap) => ap.id === testState.test.accessPointId,
|
|
903
|
+
);
|
|
904
|
+
let accessPointQuery: RawLambda | undefined;
|
|
905
|
+
if (accessPoint instanceof LakehouseAccessPoint) {
|
|
906
|
+
accessPointQuery = accessPoint.func;
|
|
907
|
+
} else if (accessPoint instanceof FunctionAccessPoint) {
|
|
908
|
+
accessPointQuery = accessPoint.query;
|
|
909
|
+
}
|
|
910
|
+
const hasQueryParameters = Boolean(
|
|
911
|
+
testState.queryVariableExpressions.length,
|
|
912
|
+
);
|
|
913
|
+
|
|
646
914
|
return (
|
|
647
915
|
<div className="function-test-editor panel">
|
|
648
916
|
<div className="panel__header">
|
|
649
|
-
<div className="panel__header
|
|
917
|
+
<div className="panel__header data-product-test-editor__header--with-tabs">
|
|
650
918
|
<div className="panel__header__title__content">Assertion</div>
|
|
651
919
|
</div>
|
|
652
920
|
</div>
|
|
653
921
|
<div className="panel">
|
|
654
|
-
{
|
|
655
|
-
|
|
922
|
+
{selectedTab === TESTABLE_TEST_TAB.ASSERTION && (
|
|
923
|
+
<>
|
|
924
|
+
{selectedAssertion ? (
|
|
925
|
+
<div className="data-product-test-editor__assertion">
|
|
926
|
+
{hasQueryParameters && (
|
|
927
|
+
<div className="data-product-test-editor__parameters-panel-container">
|
|
928
|
+
<div className="panel data-product-test-editor__parameters-panel">
|
|
929
|
+
<div className="data-product-test-editor__parameters-header">
|
|
930
|
+
<div className="data-product-test-editor__parameters-header__title">
|
|
931
|
+
<div className="data-product-test-editor__parameters-header__title__label">
|
|
932
|
+
Parameters
|
|
933
|
+
</div>
|
|
934
|
+
</div>
|
|
935
|
+
<div className="panel__header__actions data-product-test-editor__parameters-header__actions">
|
|
936
|
+
<button
|
|
937
|
+
className="panel__header__action data-product-test-editor__generate-btn"
|
|
938
|
+
onClick={generateParameterValues}
|
|
939
|
+
disabled={!testState.newParamOptions.length}
|
|
940
|
+
title="Generate test parameter values"
|
|
941
|
+
tabIndex={-1}
|
|
942
|
+
>
|
|
943
|
+
<div className="data-product-test-editor__generate-btn__label">
|
|
944
|
+
<RefreshIcon className="data-product-test-editor__generate-btn__label__icon" />
|
|
945
|
+
<div className="data-product-test-editor__generate-btn__label__title">
|
|
946
|
+
Generate
|
|
947
|
+
</div>
|
|
948
|
+
</div>
|
|
949
|
+
</button>
|
|
950
|
+
<button
|
|
951
|
+
className="panel__header__action data-product-test-editor__parameters-header__action"
|
|
952
|
+
tabIndex={-1}
|
|
953
|
+
disabled={!testState.newParamOptions.length}
|
|
954
|
+
onClick={addParameter}
|
|
955
|
+
title="Add Parameter Value"
|
|
956
|
+
>
|
|
957
|
+
<PlusIcon />
|
|
958
|
+
</button>
|
|
959
|
+
</div>
|
|
960
|
+
</div>
|
|
961
|
+
<div className="data-product-test-editor__parameters">
|
|
962
|
+
{testState.parameterValueStates.map((paramState) => (
|
|
963
|
+
<DataProductTestParameterEditor
|
|
964
|
+
key={paramState.uuid}
|
|
965
|
+
isReadOnly={isReadOnly}
|
|
966
|
+
paramState={paramState}
|
|
967
|
+
testState={testState}
|
|
968
|
+
contentTypeParamPair={getContentTypeWithParamFromQuery(
|
|
969
|
+
accessPointQuery,
|
|
970
|
+
testState.editorStore,
|
|
971
|
+
).find(
|
|
972
|
+
(pair) =>
|
|
973
|
+
pair.param === paramState.parameterValue.name,
|
|
974
|
+
)}
|
|
975
|
+
/>
|
|
976
|
+
))}
|
|
977
|
+
</div>
|
|
978
|
+
</div>
|
|
979
|
+
</div>
|
|
980
|
+
)}
|
|
981
|
+
<div
|
|
982
|
+
className={clsx(
|
|
983
|
+
'data-product-test-editor__assertion-result',
|
|
984
|
+
{
|
|
985
|
+
'data-product-test-editor__assertion-result--full':
|
|
986
|
+
!hasQueryParameters,
|
|
987
|
+
},
|
|
988
|
+
)}
|
|
989
|
+
>
|
|
990
|
+
<TestAssertionEditor
|
|
991
|
+
testAssertionState={selectedAssertion}
|
|
992
|
+
/>
|
|
993
|
+
</div>
|
|
994
|
+
</div>
|
|
995
|
+
) : null}
|
|
996
|
+
</>
|
|
656
997
|
)}
|
|
657
|
-
{
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
998
|
+
{selectedTab === TESTABLE_TEST_TAB.ASSERTION &&
|
|
999
|
+
!selectedAssertion && (
|
|
1000
|
+
<BlankPanelPlaceholder
|
|
1001
|
+
text="No assertion"
|
|
1002
|
+
tooltipText="No assertion configured for this test"
|
|
1003
|
+
/>
|
|
1004
|
+
)}
|
|
1005
|
+
{testState.showNewParameterModal && (
|
|
1006
|
+
<NewDataProductParameterModal
|
|
1007
|
+
testState={testState}
|
|
1008
|
+
isReadOnly={isReadOnly}
|
|
661
1009
|
/>
|
|
662
1010
|
)}
|
|
663
1011
|
</div>
|
|
@@ -680,8 +1028,8 @@ const DataProductTestsEditor = observer(
|
|
|
680
1028
|
const selectedTest = suiteState.selectTestState;
|
|
681
1029
|
|
|
682
1030
|
return (
|
|
683
|
-
<div className="panel
|
|
684
|
-
<div className="
|
|
1031
|
+
<div className="panel data-product-test-editor__tests">
|
|
1032
|
+
<div className="data-product-test-editor__tests__content">
|
|
685
1033
|
<ResizablePanelGroup orientation="vertical">
|
|
686
1034
|
<ResizablePanel minSize={100} size={200}>
|
|
687
1035
|
<div className="binding-editor__header">
|
|
@@ -765,7 +1113,7 @@ const DataProductTestSuiteEditor = observer(
|
|
|
765
1113
|
const isReadOnly = testableState.dataProductEditorState.isReadOnly;
|
|
766
1114
|
|
|
767
1115
|
return (
|
|
768
|
-
<div className="
|
|
1116
|
+
<div className="data-product-test-suite-editor">
|
|
769
1117
|
<ResizablePanelGroup orientation="horizontal">
|
|
770
1118
|
<ResizablePanel size={580} minSize={28}>
|
|
771
1119
|
<DataProductTestDataEditor
|
|
@@ -851,7 +1199,7 @@ export const DataProductTestableEditor = observer(
|
|
|
851
1199
|
testSuite_setId(guaranteeNonNullable(testableState.suiteToRename), val);
|
|
852
1200
|
|
|
853
1201
|
return (
|
|
854
|
-
<Panel className="
|
|
1202
|
+
<Panel className="data-product-test-suite-editor">
|
|
855
1203
|
<PanelLoadingIndicator
|
|
856
1204
|
isLoading={testableState.runningAllTestsState.isInProgress}
|
|
857
1205
|
/>
|
|
@@ -872,14 +1220,14 @@ export const DataProductTestableEditor = observer(
|
|
|
872
1220
|
|
|
873
1221
|
<PanelHeader>
|
|
874
1222
|
{dp.tests.length ? (
|
|
875
|
-
<PanelHeader className="
|
|
1223
|
+
<PanelHeader className="data-product-test-suite-editor__header data-product-test-suite-editor__header--with-tabs">
|
|
876
1224
|
<div className="uml-element-editor__tabs">
|
|
877
1225
|
{dp.tests.map((suite) => (
|
|
878
1226
|
<div
|
|
879
1227
|
key={suite.id}
|
|
880
1228
|
onClick={(): void => changeSuite(suite)}
|
|
881
|
-
className={clsx('
|
|
882
|
-
'
|
|
1229
|
+
className={clsx('data-product-test-suite-editor__tab', {
|
|
1230
|
+
'data-product-test-suite-editor__tab--active':
|
|
883
1231
|
selectedSuiteState?.suite === suite,
|
|
884
1232
|
})}
|
|
885
1233
|
>
|
|
@@ -907,7 +1255,7 @@ export const DataProductTestableEditor = observer(
|
|
|
907
1255
|
</PanelHeaderActionItem>
|
|
908
1256
|
</PanelHeaderActions>
|
|
909
1257
|
</PanelHeader>
|
|
910
|
-
<Panel className="
|
|
1258
|
+
<Panel className="data-product-test-suite-editor">
|
|
911
1259
|
{selectedSuiteState && (
|
|
912
1260
|
<DataProductTestSuiteEditor suiteState={selectedSuiteState} />
|
|
913
1261
|
)}
|
|
@@ -267,7 +267,11 @@ const ServiceGeneralEditor = observer(() => {
|
|
|
267
267
|
setIsSuggestingWithAI(true);
|
|
268
268
|
setAIDocSuggestion(undefined);
|
|
269
269
|
try {
|
|
270
|
-
const
|
|
270
|
+
const serviceGrammar =
|
|
271
|
+
await editorStore.graphManagerState.graphManager.elementsToPureCode([
|
|
272
|
+
service,
|
|
273
|
+
]);
|
|
274
|
+
const suggestion = await aiDocSuggester(serviceGrammar, legendAIUrl);
|
|
271
275
|
setAIDocSuggestion(suggestion);
|
|
272
276
|
} finally {
|
|
273
277
|
setIsSuggestingWithAI(false);
|
|
@@ -49,7 +49,7 @@ import { useApplicationNavigationContext } from '@finos/legend-application';
|
|
|
49
49
|
import { useParams } from '@finos/legend-application/browser';
|
|
50
50
|
import { LEGEND_STUDIO_DOCUMENTATION_KEY } from '../../__lib__/LegendStudioDocumentation.js';
|
|
51
51
|
import { CreateProjectModal } from './CreateProjectModal.js';
|
|
52
|
-
import { ActivityBarMenu } from '../editor/ActivityBar.js';
|
|
52
|
+
import { ActivityBarMenu, ColorThemeToggle } from '../editor/ActivityBar.js';
|
|
53
53
|
import { LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY } from '../../__lib__/LegendStudioApplicationNavigationContext.js';
|
|
54
54
|
import { CreateWorkspaceModal } from './CreateWorkspaceModal.js';
|
|
55
55
|
import { RecentWorkspacesPanel } from './RecentWorkspacesPanel.js';
|
|
@@ -369,14 +369,24 @@ export const WorkspaceSetup = withWorkspaceSetupStore(
|
|
|
369
369
|
// Build a unified option list: recent projects (that aren't already in the
|
|
370
370
|
// loaded set) are prepended so users can instantly re-open common work
|
|
371
371
|
// without waiting for the SDLC search to round-trip.
|
|
372
|
+
const sandboxProject =
|
|
373
|
+
setupStore.sandboxProject instanceof Project
|
|
374
|
+
? setupStore.sandboxProject
|
|
375
|
+
: undefined;
|
|
376
|
+
const sandboxProjectId = sandboxProject?.projectId;
|
|
372
377
|
const loadedProjectOptions = setupStore.projects
|
|
378
|
+
.filter((p) => p.projectId !== sandboxProjectId)
|
|
373
379
|
.map(buildProjectOption)
|
|
374
380
|
.sort(compareLabelFn);
|
|
375
381
|
const loadedProjectIds = new Set(
|
|
376
382
|
setupStore.projects.map((p) => p.projectId),
|
|
377
383
|
);
|
|
378
384
|
const recentProjectOptions: ProjectOption[] = setupStore.recentProjects
|
|
379
|
-
.filter(
|
|
385
|
+
.filter(
|
|
386
|
+
(r) =>
|
|
387
|
+
!loadedProjectIds.has(r.projectId) &&
|
|
388
|
+
r.projectId !== sandboxProjectId,
|
|
389
|
+
)
|
|
380
390
|
.map((r) => {
|
|
381
391
|
// Rebuild a real Project from the cached metadata; no synthetic
|
|
382
392
|
// fields needed since we persist everything the schema requires.
|
|
@@ -390,6 +400,17 @@ export const WorkspaceSetup = withWorkspaceSetupStore(
|
|
|
390
400
|
return { label: stub.name, value: stub };
|
|
391
401
|
});
|
|
392
402
|
const projectOptions: ProjectOption[] = [
|
|
403
|
+
// The user's own sandbox project is fetched via a dedicated call
|
|
404
|
+
// (`loadSandboxProject`) and excluded from the main search results, so
|
|
405
|
+
// we surface it here as a labeled option pinned to the top of the list.
|
|
406
|
+
...(sandboxProject
|
|
407
|
+
? [
|
|
408
|
+
{
|
|
409
|
+
label: `${sandboxProject.name} (sandbox)`,
|
|
410
|
+
value: sandboxProject,
|
|
411
|
+
},
|
|
412
|
+
]
|
|
413
|
+
: []),
|
|
393
414
|
...recentProjectOptions,
|
|
394
415
|
...loadedProjectOptions,
|
|
395
416
|
];
|
|
@@ -546,6 +567,10 @@ export const WorkspaceSetup = withWorkspaceSetupStore(
|
|
|
546
567
|
<div className="workspace-setup__body">
|
|
547
568
|
<div className="activity-bar">
|
|
548
569
|
<ActivityBarMenu />
|
|
570
|
+
{/* Spacer so the theme toggle sits at the bottom of the strip,
|
|
571
|
+
mirroring how the editor's activity bar is laid out. */}
|
|
572
|
+
<div className="activity-bar__items" />
|
|
573
|
+
<ColorThemeToggle />
|
|
549
574
|
</div>
|
|
550
575
|
<div
|
|
551
576
|
className="workspace-setup__content"
|
package/src/index.ts
CHANGED
|
@@ -64,6 +64,7 @@ export * from './stores/graph-modifier/DSL_Service_GraphModifierHelper.js';
|
|
|
64
64
|
export * from './stores/graph-modifier/RawValueSpecificationGraphModifierHelper.js';
|
|
65
65
|
export * from './stores/extensions/DSL_Mapping_LegendStudioApplicationPlugin_Extension.js';
|
|
66
66
|
export * from './stores/extensions/DSL_Service_LegendStudioApplicationPlugin_Extension.js';
|
|
67
|
+
export * from './stores/extensions/DSL_DataProduct_LegendStudioApplicationPlugin_Extension.js';
|
|
67
68
|
export * from './stores/extensions/DSL_Data_LegendStudioApplicationPlugin_Extension.js';
|
|
68
69
|
export { DataProductEditorState } from './stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.js';
|
|
69
70
|
|