@finos/legend-application-studio 28.18.131 → 28.18.133
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/application/LegendIngestionConfiguration.d.ts +1 -0
- package/lib/application/LegendIngestionConfiguration.d.ts.map +1 -1
- package/lib/application/LegendIngestionConfiguration.js +3 -1
- package/lib/application/LegendIngestionConfiguration.js.map +1 -1
- package/lib/components/editor/Editor.d.ts.map +1 -1
- package/lib/components/editor/Editor.js +2 -1
- package/lib/components/editor/Editor.js.map +1 -1
- package/lib/components/editor/editor-group/dataProduct/DataPoductEditor.d.ts.map +1 -1
- package/lib/components/editor/editor-group/dataProduct/DataPoductEditor.js +52 -4
- package/lib/components/editor/editor-group/dataProduct/DataPoductEditor.js.map +1 -1
- package/lib/index.css +1 -1
- package/lib/package.json +1 -1
- package/lib/stores/editor/EditorStore.d.ts +2 -1
- package/lib/stores/editor/EditorStore.d.ts.map +1 -1
- package/lib/stores/editor/EditorStore.js +24 -11
- package/lib/stores/editor/EditorStore.js.map +1 -1
- package/lib/stores/editor/EditorTabManagerState.js +1 -1
- package/lib/stores/editor/EditorTabManagerState.js.map +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/ElementEditorInitialConfiguration.d.ts +7 -0
- package/lib/stores/editor/editor-state/element-editor-state/ElementEditorInitialConfiguration.d.ts.map +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/ElementEditorInitialConfiguration.js +17 -0
- package/lib/stores/editor/editor-state/element-editor-state/ElementEditorInitialConfiguration.js.map +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.d.ts +17 -3
- 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 +73 -3
- package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.js.map +1 -1
- package/lib/stores/ingestion/AdhocDataProductDeployResponse.d.ts +21 -0
- package/lib/stores/ingestion/AdhocDataProductDeployResponse.d.ts.map +1 -0
- package/lib/stores/ingestion/AdhocDataProductDeployResponse.js +24 -0
- package/lib/stores/ingestion/AdhocDataProductDeployResponse.js.map +1 -0
- package/lib/stores/ingestion/IngestDeploymentServerClient.d.ts +4 -0
- package/lib/stores/ingestion/IngestDeploymentServerClient.d.ts.map +1 -1
- package/lib/stores/ingestion/IngestDeploymentServerClient.js +5 -0
- package/lib/stores/ingestion/IngestDeploymentServerClient.js.map +1 -1
- package/lib/stores/ingestion/IngestionManager.d.ts +4 -1
- package/lib/stores/ingestion/IngestionManager.d.ts.map +1 -1
- package/lib/stores/ingestion/IngestionManager.js +25 -7
- package/lib/stores/ingestion/IngestionManager.js.map +1 -1
- package/lib/stores/lazy-text-editor/LazyTextEditorStore.d.ts.map +1 -1
- package/lib/stores/lazy-text-editor/LazyTextEditorStore.js +1 -1
- package/lib/stores/lazy-text-editor/LazyTextEditorStore.js.map +1 -1
- package/package.json +6 -6
- package/src/application/LegendIngestionConfiguration.ts +3 -1
- package/src/components/editor/Editor.tsx +2 -0
- package/src/components/editor/editor-group/dataProduct/DataPoductEditor.tsx +146 -0
- package/src/stores/editor/EditorStore.ts +38 -20
- package/src/stores/editor/EditorTabManagerState.ts +1 -1
- package/src/stores/editor/editor-state/element-editor-state/ElementEditorInitialConfiguration.ts +26 -0
- package/src/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.ts +120 -2
- package/src/stores/ingestion/AdhocDataProductDeployResponse.ts +29 -0
- package/src/stores/ingestion/IngestDeploymentServerClient.ts +18 -0
- package/src/stores/ingestion/IngestionManager.ts +51 -11
- package/src/stores/lazy-text-editor/LazyTextEditorStore.ts +1 -0
- package/tsconfig.json +1 -0
@@ -20,7 +20,7 @@ import {
|
|
20
20
|
usingModelSchema,
|
21
21
|
} from '@finos/legend-shared';
|
22
22
|
import type { AuthProviderProps } from 'react-oidc-context';
|
23
|
-
import { createModelSchema, primitive, raw } from 'serializr';
|
23
|
+
import { createModelSchema, optional, primitive, raw } from 'serializr';
|
24
24
|
|
25
25
|
export class IngestDeploymentOIDC {
|
26
26
|
redirectPath!: string;
|
@@ -51,6 +51,7 @@ export class IngestDeploymentServerConfig {
|
|
51
51
|
export class IngestionDeploymentConfiguration {
|
52
52
|
oidcConfig!: IngestDeploymentOIDC;
|
53
53
|
defaultServer!: IngestDeploymentServerConfig;
|
54
|
+
useDefaultServer: boolean | undefined;
|
54
55
|
|
55
56
|
static readonly serialization = new SerializationFactory(
|
56
57
|
createModelSchema(IngestionDeploymentConfiguration, {
|
@@ -58,6 +59,7 @@ export class IngestionDeploymentConfiguration {
|
|
58
59
|
defaultServer: usingModelSchema(
|
59
60
|
IngestDeploymentServerConfig.serialization.schema,
|
60
61
|
),
|
62
|
+
useDefaultServer: optional(primitive()),
|
61
63
|
}),
|
62
64
|
);
|
63
65
|
}
|
@@ -161,6 +161,7 @@ export const Editor = withEditorStore(
|
|
161
161
|
patchReleaseVersionId,
|
162
162
|
workspaceId,
|
163
163
|
workspaceType,
|
164
|
+
studioParams,
|
164
165
|
),
|
165
166
|
).catch(applicationStore.alertUnhandledError);
|
166
167
|
}, [
|
@@ -170,6 +171,7 @@ export const Editor = withEditorStore(
|
|
170
171
|
projectId,
|
171
172
|
workspaceId,
|
172
173
|
workspaceType,
|
174
|
+
studioParams,
|
173
175
|
]);
|
174
176
|
|
175
177
|
useEffect(() => {
|
@@ -18,6 +18,7 @@ import { observer } from 'mobx-react-lite';
|
|
18
18
|
import { useEditorStore } from '../../EditorStoreProvider.js';
|
19
19
|
import {
|
20
20
|
DataProductEditorState,
|
21
|
+
generateUrlToDeployOnOpen,
|
21
22
|
LakehouseAccessPointState,
|
22
23
|
} from '../../../../stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.js';
|
23
24
|
import {
|
@@ -34,11 +35,21 @@ import {
|
|
34
35
|
TimesIcon,
|
35
36
|
PlusIcon,
|
36
37
|
PanelHeaderActionItem,
|
38
|
+
RocketIcon,
|
39
|
+
Modal,
|
40
|
+
ModalHeader,
|
41
|
+
ModalTitle,
|
42
|
+
ModalBody,
|
43
|
+
ModalFooter,
|
44
|
+
ModalFooterButton,
|
37
45
|
} from '@finos/legend-art';
|
38
46
|
import { useRef, useState, useEffect } from 'react';
|
39
47
|
import { filterByType } from '@finos/legend-shared';
|
40
48
|
import { InlineLambdaEditor } from '@finos/legend-query-builder';
|
41
49
|
import { flowResult } from 'mobx';
|
50
|
+
import { useAuth } from 'react-oidc-context';
|
51
|
+
import { CODE_EDITOR_LANGUAGE } from '@finos/legend-code-editor';
|
52
|
+
import { CodeEditor } from '@finos/legend-lego/code-editor';
|
42
53
|
|
43
54
|
const NewAccessPointAccessPOint = observer(
|
44
55
|
(props: { dataProductEditorState: DataProductEditorState }) => {
|
@@ -254,6 +265,83 @@ const DataProductEditorSplashScreen = observer(
|
|
254
265
|
},
|
255
266
|
);
|
256
267
|
|
268
|
+
const DataproductDeploymenetModal = observer(
|
269
|
+
(props: { state: DataProductEditorState }) => {
|
270
|
+
const { state } = props;
|
271
|
+
const applicationStore = state.editorStore.applicationStore;
|
272
|
+
return (
|
273
|
+
<Dialog
|
274
|
+
open={state.deploymentState.isInProgress}
|
275
|
+
classes={{ container: 'search-modal__container' }}
|
276
|
+
>
|
277
|
+
<Modal
|
278
|
+
darkMode={
|
279
|
+
!applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled
|
280
|
+
}
|
281
|
+
className="database-builder"
|
282
|
+
>
|
283
|
+
<ModalHeader>
|
284
|
+
<ModalTitle title="Deploy Data Product" />
|
285
|
+
</ModalHeader>
|
286
|
+
<ModalBody>
|
287
|
+
<div>{state.deploymentState.message}</div>
|
288
|
+
</ModalBody>
|
289
|
+
</Modal>
|
290
|
+
</Dialog>
|
291
|
+
);
|
292
|
+
},
|
293
|
+
);
|
294
|
+
|
295
|
+
const DataProductDeploymentResponseModal = observer(
|
296
|
+
(props: { state: DataProductEditorState }) => {
|
297
|
+
const { state } = props;
|
298
|
+
const applicationStore = state.editorStore.applicationStore;
|
299
|
+
const closeModal = (): void => state.setDeployResponse(undefined);
|
300
|
+
return (
|
301
|
+
<Dialog
|
302
|
+
open={Boolean(state.deployResponse)}
|
303
|
+
classes={{
|
304
|
+
root: 'editor-modal__root-container',
|
305
|
+
container: 'editor-modal__container',
|
306
|
+
paper: 'editor-modal__content',
|
307
|
+
}}
|
308
|
+
onClose={closeModal}
|
309
|
+
>
|
310
|
+
<Modal
|
311
|
+
darkMode={
|
312
|
+
!applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled
|
313
|
+
}
|
314
|
+
className="editor-modal"
|
315
|
+
>
|
316
|
+
<ModalHeader>
|
317
|
+
<ModalTitle title="Validation Error" />
|
318
|
+
</ModalHeader>
|
319
|
+
<ModalBody>
|
320
|
+
<PanelContent>
|
321
|
+
<CodeEditor
|
322
|
+
inputValue={JSON.stringify(
|
323
|
+
state.deployResponse?.content ?? {},
|
324
|
+
null,
|
325
|
+
2,
|
326
|
+
)}
|
327
|
+
isReadOnly={true}
|
328
|
+
language={CODE_EDITOR_LANGUAGE.JSON}
|
329
|
+
/>
|
330
|
+
</PanelContent>
|
331
|
+
</ModalBody>
|
332
|
+
<ModalFooter>
|
333
|
+
<ModalFooterButton
|
334
|
+
onClick={closeModal}
|
335
|
+
text="Close"
|
336
|
+
type="secondary"
|
337
|
+
/>
|
338
|
+
</ModalFooter>
|
339
|
+
</Modal>
|
340
|
+
</Dialog>
|
341
|
+
);
|
342
|
+
},
|
343
|
+
);
|
344
|
+
|
257
345
|
export const DataProductEditor = observer(() => {
|
258
346
|
const editorStore = useEditorStore();
|
259
347
|
const dataProductEditorState =
|
@@ -266,6 +354,30 @@ export const DataProductEditor = observer(() => {
|
|
266
354
|
const openNewModal = () => {
|
267
355
|
dataProductEditorState.setAccessPointModal(true);
|
268
356
|
};
|
357
|
+
const auth = useAuth();
|
358
|
+
const deployDataProduct = (): void => {
|
359
|
+
// Trigger OAuth flow if not authenticated
|
360
|
+
if (!auth.isAuthenticated) {
|
361
|
+
// remove this redirect if we move to do oauth at the beginning of opening studio
|
362
|
+
auth
|
363
|
+
.signinRedirect({
|
364
|
+
state: generateUrlToDeployOnOpen(dataProductEditorState),
|
365
|
+
})
|
366
|
+
.catch(editorStore.applicationStore.alertUnhandledError);
|
367
|
+
return;
|
368
|
+
}
|
369
|
+
// Use the token for deployment
|
370
|
+
const token = auth.user?.access_token;
|
371
|
+
if (token) {
|
372
|
+
flowResult(dataProductEditorState.deploy(token)).catch(
|
373
|
+
editorStore.applicationStore.alertUnhandledError,
|
374
|
+
);
|
375
|
+
} else {
|
376
|
+
editorStore.applicationStore.notificationService.notifyError(
|
377
|
+
'Authentication failed. No token available.',
|
378
|
+
);
|
379
|
+
}
|
380
|
+
};
|
269
381
|
|
270
382
|
useEffect(() => {
|
271
383
|
flowResult(dataProductEditorState.convertAccessPointsFuncObjects()).catch(
|
@@ -273,6 +385,18 @@ export const DataProductEditor = observer(() => {
|
|
273
385
|
);
|
274
386
|
}, [dataProductEditorState]);
|
275
387
|
|
388
|
+
useEffect(() => {
|
389
|
+
if (dataProductEditorState.deployOnOpen) {
|
390
|
+
flowResult(dataProductEditorState.deploy(auth.user?.access_token)).catch(
|
391
|
+
editorStore.applicationStore.alertUnhandledError,
|
392
|
+
);
|
393
|
+
}
|
394
|
+
}, [
|
395
|
+
auth,
|
396
|
+
editorStore.applicationStore.alertUnhandledError,
|
397
|
+
dataProductEditorState,
|
398
|
+
]);
|
399
|
+
|
276
400
|
return (
|
277
401
|
<div className="data-product-editor">
|
278
402
|
<div className="panel">
|
@@ -286,6 +410,20 @@ export const DataProductEditor = observer(() => {
|
|
286
410
|
<div className="panel__header__title__label">data product</div>
|
287
411
|
<div className="panel__header__title__content">{product.name}</div>
|
288
412
|
</div>
|
413
|
+
<PanelHeaderActions>
|
414
|
+
<div className="btn__dropdown-combo btn__dropdown-combo--primary">
|
415
|
+
<button
|
416
|
+
className="btn__dropdown-combo__label"
|
417
|
+
onClick={deployDataProduct}
|
418
|
+
title={dataProductEditorState.deployValidationMessage}
|
419
|
+
tabIndex={-1}
|
420
|
+
disabled={!dataProductEditorState.deployValidationMessage}
|
421
|
+
>
|
422
|
+
<RocketIcon className="btn__dropdown-combo__label__icon" />
|
423
|
+
<div className="btn__dropdown-combo__label__title">Deploy</div>
|
424
|
+
</button>
|
425
|
+
</div>
|
426
|
+
</PanelHeaderActions>
|
289
427
|
</div>
|
290
428
|
<div className="panel">
|
291
429
|
<PanelHeader>
|
@@ -324,6 +462,14 @@ export const DataProductEditor = observer(() => {
|
|
324
462
|
dataProductEditorState={dataProductEditorState}
|
325
463
|
/>
|
326
464
|
)}
|
465
|
+
{dataProductEditorState.deploymentState.isInProgress && (
|
466
|
+
<DataproductDeploymenetModal state={dataProductEditorState} />
|
467
|
+
)}
|
468
|
+
{dataProductEditorState.deployResponse && (
|
469
|
+
<DataProductDeploymentResponseModal
|
470
|
+
state={dataProductEditorState}
|
471
|
+
/>
|
472
|
+
)}
|
327
473
|
</div>
|
328
474
|
</div>
|
329
475
|
</div>
|
@@ -566,6 +566,34 @@ export class EditorStore implements CommandRegistrar {
|
|
566
566
|
this.explorerTreeState = new ExplorerTreeState(this);
|
567
567
|
}
|
568
568
|
|
569
|
+
deCodeStudioQueryParams(
|
570
|
+
studioParams: Partial<StudioQueryParams> | undefined,
|
571
|
+
): void {
|
572
|
+
if (this.editorConfig) {
|
573
|
+
// If editor config is already set, we do not decode again
|
574
|
+
return;
|
575
|
+
}
|
576
|
+
if (!studioParams) {
|
577
|
+
// If studio params are not provided, we do not decode
|
578
|
+
return;
|
579
|
+
}
|
580
|
+
const editorConfig = studioParams[LEGEND_STUDIO_QUERY_PARAMS.EDITOR_CONFIG];
|
581
|
+
if (!editorConfig) {
|
582
|
+
return;
|
583
|
+
}
|
584
|
+
const _config = returnUndefOnError(() =>
|
585
|
+
EditorInitialConfiguration.serialization.fromJson(
|
586
|
+
JSON.parse(atob(editorConfig)),
|
587
|
+
),
|
588
|
+
);
|
589
|
+
const config = guaranteeNonNullable(
|
590
|
+
_config,
|
591
|
+
`error reading and serializing config ${editorConfig}`,
|
592
|
+
);
|
593
|
+
|
594
|
+
this.editorConfig = config;
|
595
|
+
}
|
596
|
+
|
569
597
|
internalizeEntityPath(
|
570
598
|
params: Partial<WorkspaceEditorPathParams>,
|
571
599
|
studioParams: Partial<StudioQueryParams> | undefined,
|
@@ -574,27 +602,14 @@ export class EditorStore implements CommandRegistrar {
|
|
574
602
|
const workspaceType = params.groupWorkspaceId
|
575
603
|
? WorkspaceType.GROUP
|
576
604
|
: WorkspaceType.USER;
|
577
|
-
|
578
|
-
studioParams?.[LEGEND_STUDIO_QUERY_PARAMS.EDITOR_CONFIG];
|
605
|
+
|
579
606
|
const workspaceId = guaranteeNonNullable(
|
580
607
|
params.groupWorkspaceId ?? params.workspaceId,
|
581
608
|
`Workspace/group workspace ID is not provided`,
|
582
609
|
);
|
583
610
|
if (entityPath) {
|
584
611
|
this.initialEntityPath = entityPath;
|
585
|
-
|
586
|
-
const _config = returnUndefOnError(() =>
|
587
|
-
EditorInitialConfiguration.serialization.fromJson(
|
588
|
-
JSON.parse(atob(editorConfig)),
|
589
|
-
),
|
590
|
-
);
|
591
|
-
const config = guaranteeNonNullable(
|
592
|
-
_config,
|
593
|
-
`error reading and serializing config ${editorConfig}`,
|
594
|
-
);
|
595
|
-
|
596
|
-
this.editorConfig = config;
|
597
|
-
}
|
612
|
+
this.deCodeStudioQueryParams(studioParams);
|
598
613
|
this.applicationStore.navigationService.navigator.updateCurrentLocation(
|
599
614
|
generateEditorRoute(
|
600
615
|
guaranteeNonNullable(projectId),
|
@@ -616,6 +631,7 @@ export class EditorStore implements CommandRegistrar {
|
|
616
631
|
patchReleaseVersionId: string | undefined,
|
617
632
|
workspaceId: string,
|
618
633
|
workspaceType: WorkspaceType,
|
634
|
+
studioParams: Partial<StudioQueryParams> | undefined,
|
619
635
|
): GeneratorFn<void> {
|
620
636
|
if (!this.initState.isInInitialState) {
|
621
637
|
/**
|
@@ -647,7 +663,7 @@ export class EditorStore implements CommandRegistrar {
|
|
647
663
|
return;
|
648
664
|
}
|
649
665
|
this.initState.inProgress();
|
650
|
-
|
666
|
+
this.deCodeStudioQueryParams(studioParams);
|
651
667
|
// TODO: when we genericize the way to initialize an application page
|
652
668
|
this.applicationStore.assistantService.setIsHidden(false);
|
653
669
|
|
@@ -792,8 +808,12 @@ export class EditorStore implements CommandRegistrar {
|
|
792
808
|
env: this.applicationStore.config.env,
|
793
809
|
tabSize: DEFAULT_TAB_SIZE,
|
794
810
|
clientConfig: {
|
795
|
-
baseUrl:
|
796
|
-
|
811
|
+
baseUrl:
|
812
|
+
this.editorConfig?.engineServerUrl ??
|
813
|
+
this.applicationStore.config.engineServerUrl,
|
814
|
+
queryBaseUrl:
|
815
|
+
this.editorConfig?.engineQueryServerUrl ??
|
816
|
+
this.applicationStore.config.engineQueryServerUrl,
|
797
817
|
enableCompression: true,
|
798
818
|
payloadDebugger,
|
799
819
|
},
|
@@ -1068,8 +1088,6 @@ export class EditorStore implements CommandRegistrar {
|
|
1068
1088
|
this.graphManagerState.graph.getElement(this.initialEntityPath),
|
1069
1089
|
this.editorConfig,
|
1070
1090
|
);
|
1071
|
-
// we may not want to set as undefined if using it for other things
|
1072
|
-
this.editorConfig = undefined;
|
1073
1091
|
} catch {
|
1074
1092
|
const elementPath = this.initialEntityPath;
|
1075
1093
|
this.initialEntityPath = undefined;
|
@@ -182,7 +182,7 @@ export class EditorTabManagerState extends TabManagerState {
|
|
182
182
|
} else if (element instanceof Service) {
|
183
183
|
return new ServiceEditorState(this.editorStore, element);
|
184
184
|
} else if (element instanceof DataProduct) {
|
185
|
-
return new DataProductEditorState(this.editorStore, element);
|
185
|
+
return new DataProductEditorState(this.editorStore, element, config);
|
186
186
|
} else if (element instanceof GenerationSpecification) {
|
187
187
|
return new GenerationSpecificationEditorState(this.editorStore, element);
|
188
188
|
} else if (element instanceof FileGenerationSpecification) {
|
package/src/stores/editor/editor-state/element-editor-state/ElementEditorInitialConfiguration.ts
CHANGED
@@ -48,6 +48,18 @@ export class IngestElementEditorInitialConfiguration extends ElementEditorInitia
|
|
48
48
|
);
|
49
49
|
}
|
50
50
|
|
51
|
+
export class DataProductElementEditorInitialConfiguration extends ElementEditorInitialConfiguration {
|
52
|
+
deployOnOpen?: boolean;
|
53
|
+
type = PACKAGEABLE_ELEMENT_TYPE._DATA_PRODUCT;
|
54
|
+
|
55
|
+
static readonly serialization = new SerializationFactory(
|
56
|
+
createModelSchema(DataProductElementEditorInitialConfiguration, {
|
57
|
+
_type: usingConstantValueSchema(PACKAGEABLE_ELEMENT_TYPE._DATA_PRODUCT),
|
58
|
+
deployOnOpen: optional(primitive()),
|
59
|
+
}),
|
60
|
+
);
|
61
|
+
}
|
62
|
+
|
51
63
|
const serializeElementEditorInitialConfiguration = (
|
52
64
|
protocol: ElementEditorInitialConfiguration,
|
53
65
|
): PlainObject<ElementEditorInitialConfiguration> => {
|
@@ -56,6 +68,11 @@ const serializeElementEditorInitialConfiguration = (
|
|
56
68
|
IngestElementEditorInitialConfiguration.serialization.schema,
|
57
69
|
protocol,
|
58
70
|
);
|
71
|
+
} else if (protocol instanceof DataProductElementEditorInitialConfiguration) {
|
72
|
+
return serialize(
|
73
|
+
DataProductElementEditorInitialConfiguration.serialization.schema,
|
74
|
+
protocol,
|
75
|
+
);
|
59
76
|
}
|
60
77
|
throw new UnsupportedOperationError(
|
61
78
|
`Can't serialize element config`,
|
@@ -72,6 +89,11 @@ const deseralizeElementEditorInitialConfiguration = (
|
|
72
89
|
IngestElementEditorInitialConfiguration.serialization.schema,
|
73
90
|
json,
|
74
91
|
);
|
92
|
+
case PACKAGEABLE_ELEMENT_TYPE._DATA_PRODUCT:
|
93
|
+
return deserialize(
|
94
|
+
DataProductElementEditorInitialConfiguration.serialization.schema,
|
95
|
+
json,
|
96
|
+
);
|
75
97
|
default: {
|
76
98
|
throw new UnsupportedOperationError(
|
77
99
|
`Can't deserialize element config`,
|
@@ -83,6 +105,8 @@ const deseralizeElementEditorInitialConfiguration = (
|
|
83
105
|
|
84
106
|
export class EditorInitialConfiguration {
|
85
107
|
elementEditorConfiguration?: ElementEditorInitialConfiguration;
|
108
|
+
engineServerUrl: string | undefined;
|
109
|
+
engineQueryServerUrl?: string | undefined;
|
86
110
|
|
87
111
|
static readonly serialization = new SerializationFactory(
|
88
112
|
createModelSchema(EditorInitialConfiguration, {
|
@@ -92,6 +116,8 @@ export class EditorInitialConfiguration {
|
|
92
116
|
deseralizeElementEditorInitialConfiguration,
|
93
117
|
),
|
94
118
|
),
|
119
|
+
engineServerUrl: optional(primitive()),
|
120
|
+
engineQueryServerUrl: optional(primitive()),
|
95
121
|
}),
|
96
122
|
);
|
97
123
|
}
|
package/src/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.ts
CHANGED
@@ -18,6 +18,7 @@ import {
|
|
18
18
|
DataProduct,
|
19
19
|
LakehouseAccessPoint,
|
20
20
|
type PackageableElement,
|
21
|
+
type IngestDefinition,
|
21
22
|
type AccessPoint,
|
22
23
|
stub_RawLambda,
|
23
24
|
LakehouseTargetEnv,
|
@@ -40,6 +41,9 @@ import {
|
|
40
41
|
LogEvent,
|
41
42
|
deleteEntry,
|
42
43
|
filterByType,
|
44
|
+
ActionState,
|
45
|
+
guaranteeNonNullable,
|
46
|
+
assertTrue,
|
43
47
|
} from '@finos/legend-shared';
|
44
48
|
import {
|
45
49
|
dataProduct_addAccessPoint,
|
@@ -47,6 +51,13 @@ import {
|
|
47
51
|
dataProduct_deleteAccessPoint,
|
48
52
|
} from '../../../../graph-modifier/DSL_DataProduct_GraphModifierHelper.js';
|
49
53
|
import { LambdaEditorState } from '@finos/legend-query-builder';
|
54
|
+
import type { IngestionManager } from '../../../../ingestion/IngestionManager.js';
|
55
|
+
import {
|
56
|
+
DataProductElementEditorInitialConfiguration,
|
57
|
+
EditorInitialConfiguration,
|
58
|
+
} from '../ElementEditorInitialConfiguration.js';
|
59
|
+
import { EXTERNAL_APPLICATION_NAVIGATION__generateUrlWithEditorConfig } from '../../../../../__lib__/LegendStudioNavigation.js';
|
60
|
+
import type { AdhocDataProductDeployResponse } from '../../../../ingestion/AdhocDataProductDeployResponse.js';
|
50
61
|
|
51
62
|
export class AccessPointState {
|
52
63
|
readonly state: AccessPointGroupState;
|
@@ -181,10 +192,10 @@ export class AccessPointGroupState {
|
|
181
192
|
|
182
193
|
constructor(val: AccessPointGroup, editorState: DataProductEditorState) {
|
183
194
|
this.value = val;
|
195
|
+
this.state = editorState;
|
184
196
|
this.accessPointStates = val.accessPoints.map((e) =>
|
185
197
|
this.buildAccessPointState(e),
|
186
198
|
);
|
187
|
-
this.state = editorState;
|
188
199
|
makeObservable(this, {
|
189
200
|
value: observable,
|
190
201
|
accessPointStates: observable,
|
@@ -213,12 +224,41 @@ export class AccessPointGroupState {
|
|
213
224
|
}
|
214
225
|
}
|
215
226
|
|
227
|
+
const createEditorInitialConfiguration = (): EditorInitialConfiguration => {
|
228
|
+
const config = new EditorInitialConfiguration();
|
229
|
+
const ingest = new DataProductElementEditorInitialConfiguration();
|
230
|
+
ingest.deployOnOpen = true;
|
231
|
+
config.elementEditorConfiguration = ingest;
|
232
|
+
return config;
|
233
|
+
};
|
234
|
+
|
235
|
+
const editorInitialConfigToBase64 = (val: EditorInitialConfiguration): string =>
|
236
|
+
btoa(JSON.stringify(EditorInitialConfiguration.serialization.toJson(val)));
|
237
|
+
|
238
|
+
export const generateUrlToDeployOnOpen = (
|
239
|
+
val: DataProductEditorState,
|
240
|
+
): string => {
|
241
|
+
return val.editorStore.applicationStore.navigationService.navigator.generateAddress(
|
242
|
+
EXTERNAL_APPLICATION_NAVIGATION__generateUrlWithEditorConfig(
|
243
|
+
val.editorStore.editorMode.generateElementLink(val.product.path),
|
244
|
+
editorInitialConfigToBase64(createEditorInitialConfiguration()),
|
245
|
+
),
|
246
|
+
);
|
247
|
+
};
|
248
|
+
|
216
249
|
export class DataProductEditorState extends ElementEditorState {
|
217
250
|
accessPointModal = false;
|
251
|
+
deploymentState = ActionState.create();
|
218
252
|
accessPointGroupStates: AccessPointGroupState[] = [];
|
219
253
|
isConvertingTransformLambdaObjects = false;
|
254
|
+
deployOnOpen = false;
|
255
|
+
deployResponse: AdhocDataProductDeployResponse | undefined;
|
220
256
|
|
221
|
-
constructor(
|
257
|
+
constructor(
|
258
|
+
editorStore: EditorStore,
|
259
|
+
element: PackageableElement,
|
260
|
+
config?: EditorInitialConfiguration,
|
261
|
+
) {
|
222
262
|
super(editorStore, element);
|
223
263
|
|
224
264
|
makeObservable(this, {
|
@@ -226,6 +266,11 @@ export class DataProductEditorState extends ElementEditorState {
|
|
226
266
|
accessPointModal: observable,
|
227
267
|
accessPointGroupStates: observable,
|
228
268
|
isConvertingTransformLambdaObjects: observable,
|
269
|
+
deploy: flow,
|
270
|
+
deployOnOpen: observable,
|
271
|
+
deployResponse: observable,
|
272
|
+
setDeployOnOpen: action,
|
273
|
+
setDeployResponse: action,
|
229
274
|
setAccessPointModal: action,
|
230
275
|
addAccessPoint: action,
|
231
276
|
convertAccessPointsFuncObjects: flow,
|
@@ -233,6 +278,20 @@ export class DataProductEditorState extends ElementEditorState {
|
|
233
278
|
this.accessPointGroupStates = this.product.accessPointGroups.map(
|
234
279
|
(e) => new AccessPointGroupState(e, this),
|
235
280
|
);
|
281
|
+
const elementConfig = config?.elementEditorConfiguration;
|
282
|
+
if (elementConfig instanceof DataProductElementEditorInitialConfiguration) {
|
283
|
+
this.deployOnOpen = elementConfig.deployOnOpen ?? false;
|
284
|
+
}
|
285
|
+
}
|
286
|
+
|
287
|
+
setDeployOnOpen(value: boolean): void {
|
288
|
+
this.deployOnOpen = value;
|
289
|
+
}
|
290
|
+
|
291
|
+
setDeployResponse(
|
292
|
+
response: AdhocDataProductDeployResponse | undefined,
|
293
|
+
): void {
|
294
|
+
this.deployResponse = response;
|
236
295
|
}
|
237
296
|
|
238
297
|
*convertAccessPointsFuncObjects(): GeneratorFn<void> {
|
@@ -303,6 +362,38 @@ export class DataProductEditorState extends ElementEditorState {
|
|
303
362
|
return new AccessPointGroupState(group, this);
|
304
363
|
}
|
305
364
|
|
365
|
+
*deploy(token: string | undefined): GeneratorFn<void> {
|
366
|
+
try {
|
367
|
+
assertTrue(
|
368
|
+
this.validForDeployment,
|
369
|
+
'Data product definition is not valid for deployment',
|
370
|
+
);
|
371
|
+
this.deploymentState.inProgress();
|
372
|
+
// The grammar we provide will be for the current data product + all ingests (used for compilation)
|
373
|
+
const grammar =
|
374
|
+
(yield this.editorStore.graphManagerState.graphManager.elementsToPureCode(
|
375
|
+
[...this.editorStore.graphManagerState.graph.ingests, this.product],
|
376
|
+
)) as unknown as string;
|
377
|
+
|
378
|
+
const response = (yield guaranteeNonNullable(
|
379
|
+
this.ingestionManager,
|
380
|
+
).deployDataProduct(
|
381
|
+
grammar,
|
382
|
+
guaranteeNonNullable(this.associatedIngest?.appDirDeployment),
|
383
|
+
this.deploymentState,
|
384
|
+
token,
|
385
|
+
)) as unknown as AdhocDataProductDeployResponse;
|
386
|
+
this.setDeployResponse(response);
|
387
|
+
} catch (error) {
|
388
|
+
assertErrorThrown(error);
|
389
|
+
this.editorStore.applicationStore.notificationService.notifyError(
|
390
|
+
`Ingest definition failed to deploy: ${error.message}`,
|
391
|
+
);
|
392
|
+
} finally {
|
393
|
+
this.deploymentState.complete();
|
394
|
+
}
|
395
|
+
}
|
396
|
+
|
306
397
|
get product(): DataProduct {
|
307
398
|
return guaranteeType(
|
308
399
|
this.element,
|
@@ -311,10 +402,37 @@ export class DataProductEditorState extends ElementEditorState {
|
|
311
402
|
);
|
312
403
|
}
|
313
404
|
|
405
|
+
get validForDeployment(): boolean {
|
406
|
+
return Boolean(
|
407
|
+
this.associatedIngest?.appDirDeployment && this.ingestionManager,
|
408
|
+
);
|
409
|
+
}
|
410
|
+
|
314
411
|
get accessPoints(): AccessPoint[] {
|
315
412
|
return this.product.accessPointGroups.map((e) => e.accessPoints).flat();
|
316
413
|
}
|
317
414
|
|
415
|
+
get ingestionManager(): IngestionManager | undefined {
|
416
|
+
return this.editorStore.ingestionManager;
|
417
|
+
}
|
418
|
+
|
419
|
+
get deployValidationMessage(): string {
|
420
|
+
if (!this.associatedIngest?.appDirDeployment) {
|
421
|
+
return 'No app dir deployment found';
|
422
|
+
} else if (!this.ingestionManager) {
|
423
|
+
return 'No ingestion manager found';
|
424
|
+
}
|
425
|
+
return 'Deploy';
|
426
|
+
}
|
427
|
+
|
428
|
+
// We need to get the associated Ingest to get the app dir deployment
|
429
|
+
// We could do a more in depth check on the access point lambdas to check which ingest it uses but for now
|
430
|
+
// we will assume all ingests have the same DID
|
431
|
+
// we get the last one, to prioritize the ones in the current project followed by dependency ones
|
432
|
+
get associatedIngest(): IngestDefinition | undefined {
|
433
|
+
return this.editorStore.graphManagerState.graph.ingests.slice(-1)[0];
|
434
|
+
}
|
435
|
+
|
318
436
|
override reprocess(
|
319
437
|
newElement: PackageableElement,
|
320
438
|
editorStore: EditorStore,
|
@@ -0,0 +1,29 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright (c) 2020-present, Goldman Sachs
|
3
|
+
*
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
* you may not use this file except in compliance with the License.
|
6
|
+
* You may obtain a copy of the License at
|
7
|
+
*
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
*
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
* See the License for the specific language governing permissions and
|
14
|
+
* limitations under the License.
|
15
|
+
*/
|
16
|
+
|
17
|
+
import type { PlainObject } from '@finos/legend-shared';
|
18
|
+
|
19
|
+
export class AdhocDataProductDeployResponse {
|
20
|
+
content!: PlainObject;
|
21
|
+
}
|
22
|
+
|
23
|
+
export const createAdhocDataProductDeployResponse = (
|
24
|
+
json: PlainObject<AdhocDataProductDeployResponse>,
|
25
|
+
): AdhocDataProductDeployResponse => {
|
26
|
+
const response = new AdhocDataProductDeployResponse();
|
27
|
+
response.content = json;
|
28
|
+
return response;
|
29
|
+
};
|
@@ -26,9 +26,12 @@ import type {
|
|
26
26
|
IngestDefinitionValidationResponse,
|
27
27
|
} from './IngestionDeploymentResponse.js';
|
28
28
|
import type { IngestDeploymentServerConfig } from '../../application/LegendIngestionConfiguration.js';
|
29
|
+
import type { AdhocDataProductDeployResponse } from './AdhocDataProductDeployResponse.js';
|
29
30
|
|
30
31
|
export class IngestDeploymentServerClient extends AbstractServerClient {
|
31
32
|
environmentClassification: string;
|
33
|
+
|
34
|
+
private DATA_PRODUCT_URL = 'data-product';
|
32
35
|
constructor(config: IngestDeploymentServerConfig) {
|
33
36
|
super({
|
34
37
|
baseUrl: config.ingestServerUrl,
|
@@ -49,6 +52,9 @@ export class IngestDeploymentServerClient extends AbstractServerClient {
|
|
49
52
|
Authorization: `Bearer ${token}`,
|
50
53
|
});
|
51
54
|
|
55
|
+
private _dataProduct = (): string =>
|
56
|
+
`${this.baseUrl}/${this.DATA_PRODUCT_URL}/api/entitlements/sdlc/deploy/definitions`;
|
57
|
+
|
52
58
|
private _ingest = (): string =>
|
53
59
|
`${this.baseUrl}/api/ingest/sdlc/deploy/definitions`;
|
54
60
|
|
@@ -80,4 +86,16 @@ export class IngestDeploymentServerClient extends AbstractServerClient {
|
|
80
86
|
this._tokenWithTextPlain(token),
|
81
87
|
);
|
82
88
|
}
|
89
|
+
|
90
|
+
deployDataProduct(
|
91
|
+
fullGrammar: string,
|
92
|
+
token: string | undefined,
|
93
|
+
): Promise<PlainObject<AdhocDataProductDeployResponse>> {
|
94
|
+
return this.post(
|
95
|
+
`${this._dataProduct()}`,
|
96
|
+
fullGrammar,
|
97
|
+
undefined,
|
98
|
+
this._tokenWithTextPlain(token),
|
99
|
+
);
|
100
|
+
}
|
83
101
|
}
|