@finos/legend-application-studio 28.19.50 → 28.19.52
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.map +1 -1
- package/lib/components/editor/editor-group/data-editor/EmbeddedDataEditor.js +5 -1
- package/lib/components/editor/editor-group/data-editor/EmbeddedDataEditor.js.map +1 -1
- package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.d.ts +23 -0
- package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.d.ts.map +1 -0
- package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.js +197 -0
- package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.js.map +1 -0
- package/lib/components/editor/editor-group/dataProduct/DataProductEditor.d.ts.map +1 -1
- package/lib/components/editor/editor-group/dataProduct/DataProductEditor.js +53 -10
- package/lib/components/editor/editor-group/dataProduct/DataProductEditor.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/ExternalFormatState.d.ts +2 -1
- package/lib/stores/editor/editor-state/ExternalFormatState.d.ts.map +1 -1
- package/lib/stores/editor/editor-state/ExternalFormatState.js +1 -0
- package/lib/stores/editor/editor-state/ExternalFormatState.js.map +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.d.ts +30 -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 +195 -1
- 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/DataProductEditorState.d.ts +1 -0
- package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.d.ts.map +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.js +6 -2
- package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.js.map +1 -1
- 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 +13 -5
- package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.js.map +1 -1
- package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.d.ts +6 -1
- package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.d.ts.map +1 -1
- package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.js +31 -1
- package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.js.map +1 -1
- package/package.json +10 -10
- package/src/components/editor/editor-group/data-editor/EmbeddedDataEditor.tsx +9 -0
- package/src/components/editor/editor-group/data-editor/RelationElementsDataEditor.tsx +567 -0
- package/src/components/editor/editor-group/dataProduct/DataProductEditor.tsx +188 -26
- package/src/stores/editor/editor-state/ExternalFormatState.ts +1 -0
- package/src/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.ts +233 -0
- package/src/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.ts +7 -0
- package/src/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.ts +24 -5
- package/src/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.ts +41 -0
- package/tsconfig.json +1 -0
|
@@ -94,27 +94,28 @@ import { CODE_EDITOR_LANGUAGE } from '@finos/legend-code-editor';
|
|
|
94
94
|
import { CodeEditor } from '@finos/legend-lego/code-editor';
|
|
95
95
|
import {
|
|
96
96
|
type DataProduct,
|
|
97
|
+
type DataProductElement,
|
|
98
|
+
type DataProductElementScope,
|
|
99
|
+
type DataProductRuntimeInfo,
|
|
100
|
+
type Expertise,
|
|
97
101
|
type GraphManagerState,
|
|
98
102
|
type LakehouseAccessPoint,
|
|
99
|
-
type V1_DataProductArtifactAccessPointGroup,
|
|
100
|
-
type V1_DataProductArtifactAccessPointImplementation,
|
|
101
|
-
type V1_DataProductArtifactGeneration,
|
|
102
103
|
type Mapping,
|
|
103
|
-
type PackageableRuntime,
|
|
104
|
-
type DataProductRuntimeInfo,
|
|
105
104
|
type PackageableElement,
|
|
106
|
-
type
|
|
107
|
-
type
|
|
105
|
+
type PackageableRuntime,
|
|
106
|
+
type V1_AccessPointGroupInfo,
|
|
107
|
+
type V1_AccessPointImplementation,
|
|
108
|
+
type V1_DataProductArtifact,
|
|
108
109
|
DataProductEmbeddedImageIcon,
|
|
109
110
|
DataProductLibraryIcon,
|
|
110
111
|
Email,
|
|
111
112
|
LakehouseTargetEnv,
|
|
112
113
|
StereotypeExplicitReference,
|
|
114
|
+
V1_DataProduct,
|
|
113
115
|
V1_DataProductIconLibraryId,
|
|
114
|
-
validate_PureExecutionMapping,
|
|
115
116
|
V1_PureGraphManager,
|
|
116
117
|
V1_RemoteEngine,
|
|
117
|
-
|
|
118
|
+
validate_PureExecutionMapping,
|
|
118
119
|
} from '@finos/legend-graph';
|
|
119
120
|
import {
|
|
120
121
|
accessPoint_setClassification,
|
|
@@ -134,6 +135,10 @@ import {
|
|
|
134
135
|
runtimeInfo_setDescription,
|
|
135
136
|
supportInfo_setSupportUrl,
|
|
136
137
|
supportInfo_setWebsite,
|
|
138
|
+
expertise_setDescription,
|
|
139
|
+
expertise_addId,
|
|
140
|
+
expertise_deleteId,
|
|
141
|
+
dataProduct_deleteExpertise,
|
|
137
142
|
} from '../../../../stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.js';
|
|
138
143
|
import { LEGEND_STUDIO_TEST_ID } from '../../../../__lib__/LegendStudioTesting.js';
|
|
139
144
|
import { LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY } from '../../../../__lib__/LegendStudioApplicationNavigationContext.js';
|
|
@@ -156,6 +161,7 @@ import {
|
|
|
156
161
|
ProductViewer,
|
|
157
162
|
} from '@finos/legend-extension-dsl-data-product';
|
|
158
163
|
import type { LegendStudioApplicationStore } from '../../../../stores/LegendStudioBaseStore.js';
|
|
164
|
+
import type { DepotServerClient } from '@finos/legend-server-depot';
|
|
159
165
|
|
|
160
166
|
export enum AP_GROUP_MODAL_ERRORS {
|
|
161
167
|
GROUP_NAME_EMPTY = 'Group Name is empty',
|
|
@@ -489,16 +495,15 @@ export const LakehouseDataProductAccessPointEditor = observer(
|
|
|
489
495
|
if (dataProductContent) {
|
|
490
496
|
const contentJson = JSON.parse(
|
|
491
497
|
dataProductContent,
|
|
492
|
-
) as
|
|
498
|
+
) as V1_DataProductArtifact;
|
|
493
499
|
const apPlanGeneration = contentJson.accessPointGroups
|
|
494
500
|
.find(
|
|
495
|
-
(group:
|
|
501
|
+
(group: V1_AccessPointGroupInfo) =>
|
|
496
502
|
group.id === groupState.value.id,
|
|
497
503
|
)
|
|
498
504
|
?.accessPointImplementations.find(
|
|
499
|
-
(
|
|
500
|
-
apImplementation
|
|
501
|
-
) => apImplementation.id === accessPoint.id,
|
|
505
|
+
(apImplementation: V1_AccessPointImplementation) =>
|
|
506
|
+
apImplementation.id === accessPoint.id,
|
|
502
507
|
);
|
|
503
508
|
|
|
504
509
|
setDebugOutput(JSON.stringify(apPlanGeneration, null, 2));
|
|
@@ -1933,6 +1938,145 @@ const HomeTab = observer(
|
|
|
1933
1938
|
},
|
|
1934
1939
|
);
|
|
1935
1940
|
|
|
1941
|
+
const ExpertiseEditor = observer(
|
|
1942
|
+
(props: { dataProductEditorState: DataProductEditorState }) => {
|
|
1943
|
+
const { dataProductEditorState } = props;
|
|
1944
|
+
const product = dataProductEditorState.product;
|
|
1945
|
+
|
|
1946
|
+
const NewExpertIdComponent = observer(
|
|
1947
|
+
(newElementProps: { expertise: Expertise }) => {
|
|
1948
|
+
const { expertise } = newElementProps;
|
|
1949
|
+
const [title, setTitle] = useState('');
|
|
1950
|
+
|
|
1951
|
+
return (
|
|
1952
|
+
<div className="data-product-editor__support-info__expertise-id-container">
|
|
1953
|
+
<div className="panel__content__form__section__list__new-item__input">
|
|
1954
|
+
<input
|
|
1955
|
+
className="input input-group__input panel__content__form__section__input input--dark"
|
|
1956
|
+
type="title"
|
|
1957
|
+
placeholder="Enter User ID"
|
|
1958
|
+
value={title}
|
|
1959
|
+
onChange={(event) => {
|
|
1960
|
+
setTitle(event.target.value);
|
|
1961
|
+
}}
|
|
1962
|
+
/>
|
|
1963
|
+
</div>
|
|
1964
|
+
<button
|
|
1965
|
+
className="panel__content__form__section__list__new-item__add-btn btn btn--dark"
|
|
1966
|
+
onClick={() => {
|
|
1967
|
+
expertise_addId(expertise, title);
|
|
1968
|
+
setTitle('');
|
|
1969
|
+
}}
|
|
1970
|
+
>
|
|
1971
|
+
Save
|
|
1972
|
+
</button>
|
|
1973
|
+
</div>
|
|
1974
|
+
);
|
|
1975
|
+
},
|
|
1976
|
+
);
|
|
1977
|
+
|
|
1978
|
+
const addNewExpertise = () => {
|
|
1979
|
+
dataProductEditorState.createExpertise();
|
|
1980
|
+
};
|
|
1981
|
+
|
|
1982
|
+
const updateExpertiseDescription = (
|
|
1983
|
+
expertise: Expertise,
|
|
1984
|
+
val: string | undefined,
|
|
1985
|
+
): void => {
|
|
1986
|
+
if (val) {
|
|
1987
|
+
expertise_setDescription(expertise, val);
|
|
1988
|
+
}
|
|
1989
|
+
};
|
|
1990
|
+
|
|
1991
|
+
const handleRemoveId = (expertise: Expertise, id: string) => {
|
|
1992
|
+
expertise_deleteId(expertise, id);
|
|
1993
|
+
};
|
|
1994
|
+
|
|
1995
|
+
const handleRemoveExpertise = (expertise: Expertise) => {
|
|
1996
|
+
dataProduct_deleteExpertise(product, expertise);
|
|
1997
|
+
};
|
|
1998
|
+
|
|
1999
|
+
return (
|
|
2000
|
+
<>
|
|
2001
|
+
<PanelHeader className="panel__header--access-point">
|
|
2002
|
+
<div className="panel__content__form__section__header__label">
|
|
2003
|
+
Expertise
|
|
2004
|
+
</div>
|
|
2005
|
+
<PanelHeaderActions>
|
|
2006
|
+
<PanelHeaderActionItem
|
|
2007
|
+
className="panel__header__action"
|
|
2008
|
+
onClick={addNewExpertise}
|
|
2009
|
+
title="Add new expertise"
|
|
2010
|
+
>
|
|
2011
|
+
<PlusIcon />
|
|
2012
|
+
</PanelHeaderActionItem>
|
|
2013
|
+
</PanelHeaderActions>
|
|
2014
|
+
</PanelHeader>
|
|
2015
|
+
{dataProductEditorState.product.expertise?.map((expertise) => (
|
|
2016
|
+
<>
|
|
2017
|
+
<div className="data-product-editor__expertise">
|
|
2018
|
+
<div className="panel__content__form__section">
|
|
2019
|
+
<div className="panel__content__form__section__header__prompt">
|
|
2020
|
+
Description
|
|
2021
|
+
</div>
|
|
2022
|
+
<textarea
|
|
2023
|
+
className="panel__content__form__section__textarea"
|
|
2024
|
+
spellCheck={false}
|
|
2025
|
+
disabled={dataProductEditorState.isReadOnly}
|
|
2026
|
+
value={expertise.description ?? ''}
|
|
2027
|
+
onChange={(event) =>
|
|
2028
|
+
updateExpertiseDescription(expertise, event.target.value)
|
|
2029
|
+
}
|
|
2030
|
+
style={{
|
|
2031
|
+
height: '100%',
|
|
2032
|
+
}}
|
|
2033
|
+
/>
|
|
2034
|
+
</div>
|
|
2035
|
+
<div className="panel__content__form__section">
|
|
2036
|
+
<div className="panel__content__form__section__header__prompt">
|
|
2037
|
+
User IDs
|
|
2038
|
+
</div>
|
|
2039
|
+
<div className="panel__content__form__section__list__id-list">
|
|
2040
|
+
{expertise.expertIds?.map((id) => (
|
|
2041
|
+
<div
|
|
2042
|
+
className="panel__content__form__section__list__item"
|
|
2043
|
+
key={id}
|
|
2044
|
+
>
|
|
2045
|
+
{id}
|
|
2046
|
+
|
|
2047
|
+
<button
|
|
2048
|
+
className="panel__content__form__section__list__item__remove-btn"
|
|
2049
|
+
disabled={dataProductEditorState.isReadOnly}
|
|
2050
|
+
onClick={() => handleRemoveId(expertise, id)}
|
|
2051
|
+
tabIndex={-1}
|
|
2052
|
+
>
|
|
2053
|
+
<TimesIcon />
|
|
2054
|
+
</button>
|
|
2055
|
+
</div>
|
|
2056
|
+
))}
|
|
2057
|
+
</div>
|
|
2058
|
+
<NewExpertIdComponent expertise={expertise} />
|
|
2059
|
+
</div>
|
|
2060
|
+
<div className="data-product-editor__expertise__actions">
|
|
2061
|
+
<button
|
|
2062
|
+
className="access-point-editor__generic-entry__remove-btn--group"
|
|
2063
|
+
onClick={() => {
|
|
2064
|
+
handleRemoveExpertise(expertise);
|
|
2065
|
+
}}
|
|
2066
|
+
tabIndex={-1}
|
|
2067
|
+
title="Remove Expertise"
|
|
2068
|
+
>
|
|
2069
|
+
<TimesIcon />
|
|
2070
|
+
</button>
|
|
2071
|
+
</div>
|
|
2072
|
+
</div>
|
|
2073
|
+
</>
|
|
2074
|
+
))}
|
|
2075
|
+
</>
|
|
2076
|
+
);
|
|
2077
|
+
},
|
|
2078
|
+
);
|
|
2079
|
+
|
|
1936
2080
|
const SupportTab = observer(
|
|
1937
2081
|
(props: {
|
|
1938
2082
|
dataProductEditorState: DataProductEditorState;
|
|
@@ -2192,6 +2336,7 @@ const SupportTab = observer(
|
|
|
2192
2336
|
isReadOnly={isReadOnly}
|
|
2193
2337
|
emptyMessage="No emails specified"
|
|
2194
2338
|
/>
|
|
2339
|
+
<ExpertiseEditor dataProductEditorState={dataProductEditorState} />
|
|
2195
2340
|
</div>
|
|
2196
2341
|
);
|
|
2197
2342
|
},
|
|
@@ -2201,6 +2346,7 @@ const getDataProductViewerState = (
|
|
|
2201
2346
|
product: DataProduct,
|
|
2202
2347
|
graphManagerState: GraphManagerState,
|
|
2203
2348
|
applicationStore: LegendStudioApplicationStore,
|
|
2349
|
+
depotServerClient: DepotServerClient,
|
|
2204
2350
|
) => {
|
|
2205
2351
|
const graphManager = guaranteeType(
|
|
2206
2352
|
graphManagerState.graphManager,
|
|
@@ -2211,16 +2357,19 @@ const getDataProductViewerState = (
|
|
|
2211
2357
|
V1_DataProduct,
|
|
2212
2358
|
);
|
|
2213
2359
|
const remoteEngine = guaranteeType(graphManager.engine, V1_RemoteEngine);
|
|
2214
|
-
|
|
2360
|
+
const dataProductViewerState = new DataProductViewerState(
|
|
2215
2361
|
v1_dataProduct,
|
|
2216
2362
|
applicationStore,
|
|
2217
2363
|
remoteEngine.getEngineServerClient(),
|
|
2364
|
+
depotServerClient,
|
|
2218
2365
|
graphManagerState,
|
|
2219
2366
|
applicationStore.config.options.dataProductConfig,
|
|
2220
2367
|
undefined,
|
|
2221
2368
|
undefined,
|
|
2222
2369
|
{},
|
|
2223
2370
|
);
|
|
2371
|
+
dataProductViewerState.init();
|
|
2372
|
+
return dataProductViewerState;
|
|
2224
2373
|
};
|
|
2225
2374
|
|
|
2226
2375
|
export const DataProductEditor = observer(() => {
|
|
@@ -2230,14 +2379,9 @@ export const DataProductEditor = observer(() => {
|
|
|
2230
2379
|
const product = dataProductEditorState.product;
|
|
2231
2380
|
const isReadOnly = dataProductEditorState.isReadOnly;
|
|
2232
2381
|
const auth = useAuth();
|
|
2233
|
-
const [showPreview,
|
|
2234
|
-
const [dataProductViewerState, setDataProductViewerState] =
|
|
2235
|
-
|
|
2236
|
-
product,
|
|
2237
|
-
editorStore.graphManagerState,
|
|
2238
|
-
editorStore.applicationStore,
|
|
2239
|
-
),
|
|
2240
|
-
);
|
|
2382
|
+
const [showPreview, setShowPreview] = useState(false);
|
|
2383
|
+
const [dataProductViewerState, setDataProductViewerState] =
|
|
2384
|
+
useState<DataProductViewerState>();
|
|
2241
2385
|
|
|
2242
2386
|
const deployDataProduct = (): void => {
|
|
2243
2387
|
// Trigger OAuth flow if not authenticated
|
|
@@ -2319,6 +2463,7 @@ export const DataProductEditor = observer(() => {
|
|
|
2319
2463
|
product,
|
|
2320
2464
|
editorStore.graphManagerState,
|
|
2321
2465
|
editorStore.applicationStore,
|
|
2466
|
+
editorStore.depotServerClient,
|
|
2322
2467
|
),
|
|
2323
2468
|
);
|
|
2324
2469
|
}
|
|
@@ -2328,6 +2473,7 @@ export const DataProductEditor = observer(() => {
|
|
|
2328
2473
|
[
|
|
2329
2474
|
editorStore.applicationStore,
|
|
2330
2475
|
editorStore.graphManagerState,
|
|
2476
|
+
editorStore.depotServerClient,
|
|
2331
2477
|
product,
|
|
2332
2478
|
showPreview,
|
|
2333
2479
|
],
|
|
@@ -2349,7 +2495,21 @@ export const DataProductEditor = observer(() => {
|
|
|
2349
2495
|
<div className="btn__dropdown-combo btn__dropdown-combo--primary">
|
|
2350
2496
|
<button
|
|
2351
2497
|
className="btn__dropdown-combo__label"
|
|
2352
|
-
onClick={() =>
|
|
2498
|
+
onClick={() => {
|
|
2499
|
+
setShowPreview((prev) => {
|
|
2500
|
+
if (!prev) {
|
|
2501
|
+
setDataProductViewerState(
|
|
2502
|
+
getDataProductViewerState(
|
|
2503
|
+
product,
|
|
2504
|
+
editorStore.graphManagerState,
|
|
2505
|
+
editorStore.applicationStore,
|
|
2506
|
+
editorStore.depotServerClient,
|
|
2507
|
+
),
|
|
2508
|
+
);
|
|
2509
|
+
}
|
|
2510
|
+
return !prev;
|
|
2511
|
+
});
|
|
2512
|
+
}}
|
|
2353
2513
|
title={showPreview ? 'Hide Preview' : 'Preview Description'}
|
|
2354
2514
|
tabIndex={-1}
|
|
2355
2515
|
style={{
|
|
@@ -2396,8 +2556,10 @@ export const DataProductEditor = observer(() => {
|
|
|
2396
2556
|
<DataProductSidebar dataProductEditorState={dataProductEditorState} />
|
|
2397
2557
|
<ResizablePanelGroup orientation="vertical">
|
|
2398
2558
|
<ResizablePanel>{renderActivivtyBarTab()}</ResizablePanel>
|
|
2399
|
-
{showPreview &&
|
|
2400
|
-
|
|
2559
|
+
{showPreview && dataProductViewerState && (
|
|
2560
|
+
<ResizablePanelSplitter />
|
|
2561
|
+
)}
|
|
2562
|
+
{showPreview && dataProductViewerState && (
|
|
2401
2563
|
<ResizablePanel>
|
|
2402
2564
|
<div className="data-product-editor__preview-container theme__hc-light">
|
|
2403
2565
|
<ProductViewer productViewerState={dataProductViewerState} />
|
|
@@ -24,6 +24,11 @@ import {
|
|
|
24
24
|
ExternalFormatData,
|
|
25
25
|
ModelStoreData,
|
|
26
26
|
ModelEmbeddedData,
|
|
27
|
+
type RelationElement,
|
|
28
|
+
RelationElementsData,
|
|
29
|
+
RelationRowTestData,
|
|
30
|
+
observe_RelationRowTestData,
|
|
31
|
+
observe_RelationElement,
|
|
27
32
|
} from '@finos/legend-graph';
|
|
28
33
|
import {
|
|
29
34
|
ContentType,
|
|
@@ -64,6 +69,9 @@ export const createEmbeddedData = (
|
|
|
64
69
|
} else if (type === EmbeddedDataType.RELATIONAL_CSV) {
|
|
65
70
|
const relational = new RelationalCSVData();
|
|
66
71
|
return relational;
|
|
72
|
+
} else if (type === EmbeddedDataType.RELATION_ELEMENTS_DATA) {
|
|
73
|
+
const testData = new RelationElementsData();
|
|
74
|
+
return testData;
|
|
67
75
|
} else if (type === EmbeddedDataType.MODEL_STORE_DATA) {
|
|
68
76
|
const modelStoreData = new ModelStoreData();
|
|
69
77
|
return modelStoreData;
|
|
@@ -201,6 +209,229 @@ export class ModelStoreDataState extends EmbeddedDataState {
|
|
|
201
209
|
}
|
|
202
210
|
}
|
|
203
211
|
|
|
212
|
+
export class RelationElementState {
|
|
213
|
+
relationElement: RelationElement;
|
|
214
|
+
|
|
215
|
+
constructor(relationElement: RelationElement) {
|
|
216
|
+
makeObservable(this, {
|
|
217
|
+
relationElement: observable,
|
|
218
|
+
addColumn: action,
|
|
219
|
+
removeColumn: action,
|
|
220
|
+
updateColumn: action,
|
|
221
|
+
addRow: action,
|
|
222
|
+
removeRow: action,
|
|
223
|
+
updateRow: action,
|
|
224
|
+
clearAllData: action,
|
|
225
|
+
importCSV: action,
|
|
226
|
+
});
|
|
227
|
+
this.relationElement = relationElement;
|
|
228
|
+
this.relationElement = observe_RelationElement(relationElement);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
addColumn(name: string): void {
|
|
232
|
+
this.relationElement.columns.push(name);
|
|
233
|
+
this.relationElement.rows.forEach((row) => {
|
|
234
|
+
row.values.push('');
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
removeColumn(index: number): void {
|
|
239
|
+
const columnToRemove = this.relationElement.columns[index];
|
|
240
|
+
if (columnToRemove) {
|
|
241
|
+
this.relationElement.columns.splice(index, 1);
|
|
242
|
+
this.relationElement.rows.forEach((row) => {
|
|
243
|
+
row.values.splice(index, 1);
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
updateColumn(index: number, name: string): void {
|
|
249
|
+
const oldName = this.relationElement.columns[index];
|
|
250
|
+
if (oldName && oldName !== name) {
|
|
251
|
+
this.relationElement.columns[index] = name;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
addRow(): void {
|
|
256
|
+
const row = new RelationRowTestData();
|
|
257
|
+
row.values = [];
|
|
258
|
+
const newRow = observe_RelationRowTestData(row);
|
|
259
|
+
this.relationElement.columns.forEach((col) => {
|
|
260
|
+
newRow.values.push('');
|
|
261
|
+
});
|
|
262
|
+
this.relationElement.rows.push(newRow);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
removeRow(index: number): void {
|
|
266
|
+
this.relationElement.rows.splice(index, 1);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
updateRow(rowIndex: number, columnIndex: number, value: string): void {
|
|
270
|
+
if (this.relationElement.rows[rowIndex]) {
|
|
271
|
+
this.relationElement.rows[rowIndex].values[columnIndex] = value;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
clearAllData(): void {
|
|
276
|
+
this.relationElement.rows.splice(0);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
exportJSON(): string {
|
|
280
|
+
return JSON.stringify(
|
|
281
|
+
{
|
|
282
|
+
columns: this.relationElement.columns,
|
|
283
|
+
data: this.relationElement.rows,
|
|
284
|
+
},
|
|
285
|
+
null,
|
|
286
|
+
2,
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
exportSQL(): string {
|
|
291
|
+
if (
|
|
292
|
+
this.relationElement.columns.length === 0 ||
|
|
293
|
+
this.relationElement.rows.length === 0
|
|
294
|
+
) {
|
|
295
|
+
return '';
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const tableName = 'test_data';
|
|
299
|
+
const defaultDataType = 'VARCHAR(1000)';
|
|
300
|
+
const columnDefs = this.relationElement.columns
|
|
301
|
+
.map((col) => `${col} ${defaultDataType}`)
|
|
302
|
+
.join(', ');
|
|
303
|
+
const createTable = `CREATE TABLE ${tableName} (${columnDefs});`;
|
|
304
|
+
|
|
305
|
+
const insertStatements = this.relationElement.rows.map((row) => {
|
|
306
|
+
const values = this.relationElement.columns
|
|
307
|
+
.map((col, colIndex) => {
|
|
308
|
+
const value = row.values[colIndex] ?? '';
|
|
309
|
+
if (value !== '') {
|
|
310
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
311
|
+
}
|
|
312
|
+
return 'NULL';
|
|
313
|
+
})
|
|
314
|
+
.join(', ');
|
|
315
|
+
return `INSERT INTO ${tableName} VALUES (${values});`;
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
return [createTable, '', ...insertStatements].join('\n');
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
exportCSV(): string {
|
|
322
|
+
const headers = this.relationElement.columns.map((col) => col);
|
|
323
|
+
const csvLines = [headers.join(',')];
|
|
324
|
+
|
|
325
|
+
this.relationElement.rows.forEach((row) => {
|
|
326
|
+
const values = headers.map((header, headerIndex) => {
|
|
327
|
+
const value = row.values[headerIndex] ?? '';
|
|
328
|
+
if (value.includes(',') || value.includes('"')) {
|
|
329
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
330
|
+
}
|
|
331
|
+
return value;
|
|
332
|
+
});
|
|
333
|
+
csvLines.push(values.join(','));
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
return csvLines.join('\n');
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
private parseCSVLine(line: string): string[] {
|
|
340
|
+
const result: string[] = [];
|
|
341
|
+
let current = '';
|
|
342
|
+
let inQuotes = false;
|
|
343
|
+
|
|
344
|
+
for (let i = 0; i < line.length; i++) {
|
|
345
|
+
const char = line[i];
|
|
346
|
+
if (char === '"') {
|
|
347
|
+
inQuotes = !inQuotes;
|
|
348
|
+
} else if (char === ',' && !inQuotes) {
|
|
349
|
+
result.push(current.trim());
|
|
350
|
+
current = '';
|
|
351
|
+
} else {
|
|
352
|
+
current += char;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
result.push(current.trim());
|
|
356
|
+
return result;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
importCSV(csvContent: string): void {
|
|
360
|
+
const lines = csvContent.trim().split('\n');
|
|
361
|
+
if (lines.length === 0) {
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const firstLine = lines[0];
|
|
366
|
+
if (!firstLine) {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const headers = this.parseCSVLine(firstLine);
|
|
371
|
+
this.relationElement.columns = headers;
|
|
372
|
+
|
|
373
|
+
this.relationElement.rows = lines.slice(1).map((line) => {
|
|
374
|
+
const values = this.parseCSVLine(line);
|
|
375
|
+
const row = new RelationRowTestData();
|
|
376
|
+
row.values = [];
|
|
377
|
+
headers.forEach((header, index) => {
|
|
378
|
+
row.values[index] = values[index] ?? '';
|
|
379
|
+
});
|
|
380
|
+
return observe_RelationRowTestData(row);
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export class RelationElementsDataState extends EmbeddedDataState {
|
|
386
|
+
override embeddedData: RelationElementsData;
|
|
387
|
+
showImportCSVModal = false;
|
|
388
|
+
showNewRelationElementModal = false;
|
|
389
|
+
activeRelationElement: RelationElementState | undefined;
|
|
390
|
+
relationElementStates: RelationElementState[];
|
|
391
|
+
|
|
392
|
+
constructor(editorStore: EditorStore, embeddedData: RelationElementsData) {
|
|
393
|
+
super(editorStore, embeddedData);
|
|
394
|
+
makeObservable(this, {
|
|
395
|
+
embeddedData: observable,
|
|
396
|
+
showImportCSVModal: observable,
|
|
397
|
+
showNewRelationElementModal: observable,
|
|
398
|
+
activeRelationElement: observable,
|
|
399
|
+
setActiveRelationElement: action,
|
|
400
|
+
setShowImportCSVModal: action,
|
|
401
|
+
setShowNewRelationElementModal: action,
|
|
402
|
+
addRelationElement: action,
|
|
403
|
+
});
|
|
404
|
+
this.embeddedData = embeddedData;
|
|
405
|
+
this.relationElementStates = embeddedData.relationElements.map(
|
|
406
|
+
(relationElement) => new RelationElementState(relationElement),
|
|
407
|
+
);
|
|
408
|
+
this.activeRelationElement = this.relationElementStates[0];
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
label(): string {
|
|
412
|
+
return 'Relation Elements Test Data';
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
setActiveRelationElement(val: RelationElementState | undefined): void {
|
|
416
|
+
this.activeRelationElement = val;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
addRelationElement(relationElement: RelationElement): void {
|
|
420
|
+
const newElementState = new RelationElementState(relationElement);
|
|
421
|
+
this.relationElementStates.push(newElementState);
|
|
422
|
+
this.embeddedData.relationElements.push(relationElement);
|
|
423
|
+
this.setActiveRelationElement(newElementState);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
setShowImportCSVModal(show: boolean): void {
|
|
427
|
+
this.showImportCSVModal = show;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
setShowNewRelationElementModal(show: boolean): void {
|
|
431
|
+
this.showNewRelationElementModal = show;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
204
435
|
export class RelationalCSVDataTableState {
|
|
205
436
|
readonly editorStore: EditorStore;
|
|
206
437
|
table: RelationalCSVDataTable;
|
|
@@ -374,6 +605,8 @@ export function buildEmbeddedDataEditorState(
|
|
|
374
605
|
);
|
|
375
606
|
} else if (embeddedData instanceof RelationalCSVData) {
|
|
376
607
|
return new RelationalCSVDataState(editorStore, embeddedData);
|
|
608
|
+
} else if (embeddedData instanceof RelationElementsData) {
|
|
609
|
+
return new RelationElementsDataState(editorStore, embeddedData);
|
|
377
610
|
} else if (embeddedData instanceof DataElementReference) {
|
|
378
611
|
return new DataElementReferenceState(editorStore, embeddedData, options);
|
|
379
612
|
} else {
|
package/src/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.ts
CHANGED
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
DataProductRuntimeInfo,
|
|
44
44
|
type DataProductElement,
|
|
45
45
|
type Mapping,
|
|
46
|
+
Expertise,
|
|
46
47
|
observe_DataProductElementScope,
|
|
47
48
|
DataProductElementScope,
|
|
48
49
|
validate_PureExecutionMapping,
|
|
@@ -78,6 +79,7 @@ import {
|
|
|
78
79
|
accessPointGroup_swapAccessPoints,
|
|
79
80
|
dataProduct_addAccessPoint,
|
|
80
81
|
dataProduct_addAccessPointGroup,
|
|
82
|
+
dataProduct_addExpertise,
|
|
81
83
|
dataProduct_deleteAccessPoint,
|
|
82
84
|
dataProduct_deleteAccessPointGroup,
|
|
83
85
|
dataProduct_swapAccessPointGroups,
|
|
@@ -726,6 +728,11 @@ export class DataProductEditorState extends ElementEditorState {
|
|
|
726
728
|
});
|
|
727
729
|
}
|
|
728
730
|
|
|
731
|
+
createExpertise() {
|
|
732
|
+
const newExpertise = new Expertise();
|
|
733
|
+
dataProduct_addExpertise(this.product, newExpertise);
|
|
734
|
+
}
|
|
735
|
+
|
|
729
736
|
*deploy(token: string | undefined): GeneratorFn<void> {
|
|
730
737
|
try {
|
|
731
738
|
assertTrue(
|
|
@@ -54,12 +54,13 @@ import {
|
|
|
54
54
|
InstanceValue,
|
|
55
55
|
PackageableElementReference,
|
|
56
56
|
Database,
|
|
57
|
-
RelationalCSVData,
|
|
58
57
|
PackageableElementExplicitReference,
|
|
59
58
|
observe_ValueSpecification,
|
|
60
59
|
buildLambdaVariableExpressions,
|
|
61
60
|
EqualTo,
|
|
62
61
|
ModelStore,
|
|
62
|
+
RelationElementsData,
|
|
63
|
+
CORE_PURE_PATH,
|
|
63
64
|
} from '@finos/legend-graph';
|
|
64
65
|
import {
|
|
65
66
|
TestablePackageableElementEditorState,
|
|
@@ -122,7 +123,14 @@ const resolveRuntimesFromQuery = (
|
|
|
122
123
|
): EngineRuntime[] | undefined => {
|
|
123
124
|
try {
|
|
124
125
|
const body = func.expressionSequence;
|
|
125
|
-
const rawLambda = new RawLambda(
|
|
126
|
+
const rawLambda = new RawLambda(
|
|
127
|
+
func.parameters.map((_param) =>
|
|
128
|
+
editorStore.graphManagerState.graphManager.serializeRawValueSpecification(
|
|
129
|
+
_param,
|
|
130
|
+
),
|
|
131
|
+
),
|
|
132
|
+
body,
|
|
133
|
+
);
|
|
126
134
|
const functions = new Map<string, SimpleFunctionExpression[]>();
|
|
127
135
|
const valueSpec =
|
|
128
136
|
editorStore.graphManagerState.graphManager.buildValueSpecification(
|
|
@@ -759,7 +767,18 @@ export class FunctionTestableState extends TestablePackageableElementEditorState
|
|
|
759
767
|
const functionSuite = new FunctionTestSuite();
|
|
760
768
|
functionSuite.id = suiteName;
|
|
761
769
|
const engineRuntimes = this.associatedRuntimes;
|
|
762
|
-
if (engineRuntimes?.length) {
|
|
770
|
+
if (!engineRuntimes?.length) {
|
|
771
|
+
const type = this.function.returnType.value.rawType;
|
|
772
|
+
if (
|
|
773
|
+
type.path === CORE_PURE_PATH.RELATION ||
|
|
774
|
+
type.path === CORE_PURE_PATH.TABULAR_DATASET
|
|
775
|
+
) {
|
|
776
|
+
this.editorStore.applicationStore.notificationService.notifyWarning(
|
|
777
|
+
`Unable to find runtime or function contains accessors incompatible for test suite creation`,
|
|
778
|
+
);
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
} else {
|
|
763
782
|
try {
|
|
764
783
|
assertTrue(
|
|
765
784
|
engineRuntimes.length === 1,
|
|
@@ -791,9 +810,9 @@ export class FunctionTestableState extends TestablePackageableElementEditorState
|
|
|
791
810
|
const store = guaranteeNonNullable(stores[0]);
|
|
792
811
|
const data = new FunctionTestData();
|
|
793
812
|
if (store instanceof Database) {
|
|
794
|
-
const
|
|
813
|
+
const relation = new RelationElementsData();
|
|
795
814
|
data.element = PackageableElementExplicitReference.create(store);
|
|
796
|
-
data.data =
|
|
815
|
+
data.data = relation;
|
|
797
816
|
} else if (store instanceof ModelStore) {
|
|
798
817
|
const modelStoreData = createBareExternalFormat();
|
|
799
818
|
data.element = PackageableElementExplicitReference.create(store);
|