@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.
Files changed (42) hide show
  1. package/lib/components/editor/editor-group/data-editor/EmbeddedDataEditor.d.ts.map +1 -1
  2. package/lib/components/editor/editor-group/data-editor/EmbeddedDataEditor.js +5 -1
  3. package/lib/components/editor/editor-group/data-editor/EmbeddedDataEditor.js.map +1 -1
  4. package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.d.ts +23 -0
  5. package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.d.ts.map +1 -0
  6. package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.js +197 -0
  7. package/lib/components/editor/editor-group/data-editor/RelationElementsDataEditor.js.map +1 -0
  8. package/lib/components/editor/editor-group/dataProduct/DataProductEditor.d.ts.map +1 -1
  9. package/lib/components/editor/editor-group/dataProduct/DataProductEditor.js +53 -10
  10. package/lib/components/editor/editor-group/dataProduct/DataProductEditor.js.map +1 -1
  11. package/lib/index.css +2 -2
  12. package/lib/index.css.map +1 -1
  13. package/lib/package.json +1 -1
  14. package/lib/stores/editor/editor-state/ExternalFormatState.d.ts +2 -1
  15. package/lib/stores/editor/editor-state/ExternalFormatState.d.ts.map +1 -1
  16. package/lib/stores/editor/editor-state/ExternalFormatState.js +1 -0
  17. package/lib/stores/editor/editor-state/ExternalFormatState.js.map +1 -1
  18. package/lib/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.d.ts +30 -1
  19. package/lib/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.d.ts.map +1 -1
  20. package/lib/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.js +195 -1
  21. package/lib/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.js.map +1 -1
  22. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.d.ts +1 -0
  23. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.d.ts.map +1 -1
  24. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.js +6 -2
  25. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.js.map +1 -1
  26. package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.d.ts.map +1 -1
  27. package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.js +13 -5
  28. package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.js.map +1 -1
  29. package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.d.ts +6 -1
  30. package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.d.ts.map +1 -1
  31. package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.js +31 -1
  32. package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.js.map +1 -1
  33. package/package.json +10 -10
  34. package/src/components/editor/editor-group/data-editor/EmbeddedDataEditor.tsx +9 -0
  35. package/src/components/editor/editor-group/data-editor/RelationElementsDataEditor.tsx +567 -0
  36. package/src/components/editor/editor-group/dataProduct/DataProductEditor.tsx +188 -26
  37. package/src/stores/editor/editor-state/ExternalFormatState.ts +1 -0
  38. package/src/stores/editor/editor-state/element-editor-state/data/EmbeddedDataState.ts +233 -0
  39. package/src/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.ts +7 -0
  40. package/src/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.ts +24 -5
  41. package/src/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.ts +41 -0
  42. 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 DataProductElementScope,
107
- type DataProductElement,
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
- V1_DataProduct,
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 V1_DataProductArtifactGeneration;
498
+ ) as V1_DataProductArtifact;
493
499
  const apPlanGeneration = contentJson.accessPointGroups
494
500
  .find(
495
- (group: V1_DataProductArtifactAccessPointGroup) =>
501
+ (group: V1_AccessPointGroupInfo) =>
496
502
  group.id === groupState.value.id,
497
503
  )
498
504
  ?.accessPointImplementations.find(
499
- (
500
- apImplementation: V1_DataProductArtifactAccessPointImplementation,
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
- return new DataProductViewerState(
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, setshowPreview] = useState(false);
2234
- const [dataProductViewerState, setDataProductViewerState] = useState(
2235
- getDataProductViewerState(
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={() => setshowPreview(!showPreview)}
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 && <ResizablePanelSplitter />}
2400
- {showPreview && (
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} />
@@ -36,6 +36,7 @@ export enum EmbeddedDataType {
36
36
  MODEL_STORE_DATA = 'MODEL_STORE',
37
37
  RELATIONAL_CSV = 'RELATIONAL',
38
38
  DATA_ELEMENT = 'DATA_ELEMENT',
39
+ RELATION_ELEMENTS_DATA = 'RELATION_ELEMENTS_DATA',
39
40
  }
40
41
 
41
42
  export class ExternalFormatState {
@@ -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 {
@@ -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([], body);
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 relational = new RelationalCSVData();
813
+ const relation = new RelationElementsData();
795
814
  data.element = PackageableElementExplicitReference.create(store);
796
- data.data = relational;
815
+ data.data = relation;
797
816
  } else if (store instanceof ModelStore) {
798
817
  const modelStoreData = createBareExternalFormat();
799
818
  data.element = PackageableElementExplicitReference.create(store);