@finos/legend-application-studio 28.19.116 → 28.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/__lib__/LegendStudioEvent.d.ts +1 -0
- package/lib/__lib__/LegendStudioEvent.d.ts.map +1 -1
- package/lib/__lib__/LegendStudioEvent.js +1 -0
- package/lib/__lib__/LegendStudioEvent.js.map +1 -1
- package/lib/components/editor/editor-group/accessor/AccessorQueryBuilder.d.ts +22 -0
- package/lib/components/editor/editor-group/accessor/AccessorQueryBuilder.d.ts.map +1 -0
- package/lib/components/editor/editor-group/accessor/AccessorQueryBuilder.js +43 -0
- package/lib/components/editor/editor-group/accessor/AccessorQueryBuilder.js.map +1 -0
- package/lib/components/editor/editor-group/accessor/AccessorQueryBuilderHelper.d.ts.map +1 -1
- package/lib/components/editor/editor-group/accessor/AccessorQueryBuilderHelper.js +8 -16
- package/lib/components/editor/editor-group/accessor/AccessorQueryBuilderHelper.js.map +1 -1
- package/lib/components/editor/editor-group/dataProduct/DataProductEditor.d.ts.map +1 -1
- package/lib/components/editor/editor-group/dataProduct/DataProductEditor.js +111 -37
- package/lib/components/editor/editor-group/dataProduct/DataProductEditor.js.map +1 -1
- package/lib/components/editor/editor-group/dataProduct/DataProductQueryBuilderHelper.d.ts.map +1 -1
- package/lib/components/editor/editor-group/dataProduct/DataProductQueryBuilderHelper.js +2 -2
- package/lib/components/editor/editor-group/dataProduct/DataProductQueryBuilderHelper.js.map +1 -1
- package/lib/components/editor/editor-group/service-editor/ServiceRegistrationEditor.d.ts.map +1 -1
- package/lib/components/editor/editor-group/service-editor/ServiceRegistrationEditor.js +12 -3
- package/lib/components/editor/editor-group/service-editor/ServiceRegistrationEditor.js.map +1 -1
- package/lib/components/editor/editor-group/uml-editor/ClassQueryBuilder.d.ts +3 -1
- package/lib/components/editor/editor-group/uml-editor/ClassQueryBuilder.d.ts.map +1 -1
- package/lib/components/editor/editor-group/uml-editor/ClassQueryBuilder.js +4 -5
- package/lib/components/editor/editor-group/uml-editor/ClassQueryBuilder.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/element-editor-state/dataProduct/DataProductEditorState.d.ts +2 -1
- 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 +12 -4
- package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.js.map +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/service/ServiceRegistrationState.d.ts +4 -0
- package/lib/stores/editor/editor-state/element-editor-state/service/ServiceRegistrationState.d.ts.map +1 -1
- package/lib/stores/editor/editor-state/element-editor-state/service/ServiceRegistrationState.js +29 -0
- package/lib/stores/editor/editor-state/element-editor-state/service/ServiceRegistrationState.js.map +1 -1
- package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.d.ts +5 -1
- package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.d.ts.map +1 -1
- package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.js +12 -0
- package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.js.map +1 -1
- package/package.json +9 -9
- package/src/__lib__/LegendStudioEvent.ts +1 -0
- package/src/components/editor/editor-group/accessor/AccessorQueryBuilder.tsx +81 -0
- package/src/components/editor/editor-group/accessor/{AccessorQueryBuilderHelper.ts → AccessorQueryBuilderHelper.tsx} +14 -1
- package/src/components/editor/editor-group/dataProduct/DataProductEditor.tsx +225 -34
- package/src/components/editor/editor-group/dataProduct/DataProductQueryBuilderHelper.ts +4 -3
- package/src/components/editor/editor-group/service-editor/ServiceRegistrationEditor.tsx +67 -1
- package/src/components/editor/editor-group/uml-editor/ClassQueryBuilder.tsx +16 -6
- package/src/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.ts +14 -5
- package/src/stores/editor/editor-state/element-editor-state/service/ServiceRegistrationState.ts +39 -0
- package/src/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.ts +27 -0
- package/tsconfig.json +2 -1
|
@@ -91,11 +91,22 @@ import {
|
|
|
91
91
|
useState,
|
|
92
92
|
} from 'react';
|
|
93
93
|
import {
|
|
94
|
+
assertErrorThrown,
|
|
94
95
|
filterByType,
|
|
96
|
+
guaranteeNonNullable,
|
|
95
97
|
guaranteeType,
|
|
98
|
+
isNonNullable,
|
|
96
99
|
UserSearchService,
|
|
97
100
|
} from '@finos/legend-shared';
|
|
98
|
-
import {
|
|
101
|
+
import {
|
|
102
|
+
AccessorQueryBuilderState,
|
|
103
|
+
getCompatibleRuntimesFromAccessorOwner,
|
|
104
|
+
InlineLambdaEditor,
|
|
105
|
+
LineageViewer,
|
|
106
|
+
QueryBuilderActionConfig,
|
|
107
|
+
QueryBuilderAdvancedWorkflowState,
|
|
108
|
+
type QueryBuilderState,
|
|
109
|
+
} from '@finos/legend-query-builder';
|
|
99
110
|
import { action, autorun, flowResult } from 'mobx';
|
|
100
111
|
import { useAuth } from 'react-oidc-context';
|
|
101
112
|
import { CODE_EDITOR_LANGUAGE } from '@finos/legend-code-editor';
|
|
@@ -126,6 +137,11 @@ import {
|
|
|
126
137
|
observer_DataProductLink,
|
|
127
138
|
DataProduct_Region,
|
|
128
139
|
DataProduct_DeliveryFrequency,
|
|
140
|
+
AppDirOwner,
|
|
141
|
+
AppDirNode,
|
|
142
|
+
observe_AppDirOwner,
|
|
143
|
+
observe_AppDirNode,
|
|
144
|
+
AppDirLevel,
|
|
129
145
|
} from '@finos/legend-graph';
|
|
130
146
|
import {
|
|
131
147
|
accessPoint_setClassification,
|
|
@@ -159,6 +175,10 @@ import {
|
|
|
159
175
|
dataProductDiagram_setTitle,
|
|
160
176
|
dataProductDiagram_setDescription,
|
|
161
177
|
operationalMetadata_setUpdateFrequency,
|
|
178
|
+
dataProduct_setOwner,
|
|
179
|
+
appDirOwner_setProduction,
|
|
180
|
+
appDirOwner_setProdParallel,
|
|
181
|
+
appDirNode_setAppDirId,
|
|
162
182
|
} from '../../../../stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.js';
|
|
163
183
|
import { LEGEND_STUDIO_TEST_ID } from '../../../../__lib__/LegendStudioTesting.js';
|
|
164
184
|
import { LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY } from '../../../../__lib__/LegendStudioApplicationNavigationContext.js';
|
|
@@ -802,6 +822,90 @@ export const LakehouseDataProductAccessPointEditor = observer(
|
|
|
802
822
|
const generateLineage = editorStore.applicationStore.guardUnhandledError(
|
|
803
823
|
() => flowResult(accessPointState.generateLineage()),
|
|
804
824
|
);
|
|
825
|
+
|
|
826
|
+
const editLambdaInQueryBuilder =
|
|
827
|
+
editorStore.applicationStore.guardUnhandledError(async () => {
|
|
828
|
+
const embeddedQueryBuilderState = editorStore.embeddedQueryBuilderState;
|
|
829
|
+
const applicationStore = editorStore.applicationStore;
|
|
830
|
+
const ingestDefinition =
|
|
831
|
+
editorStore.graphManagerState.graph.ownIngests.find(isNonNullable);
|
|
832
|
+
if (!ingestDefinition) {
|
|
833
|
+
applicationStore.notificationService.notifyError(
|
|
834
|
+
'No ingest definition found in this project',
|
|
835
|
+
);
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
await flowResult(
|
|
839
|
+
embeddedQueryBuilderState.setEmbeddedQueryBuilderConfiguration({
|
|
840
|
+
setupQueryBuilderState: async (): Promise<QueryBuilderState> => {
|
|
841
|
+
const queryBuilderState = new AccessorQueryBuilderState(
|
|
842
|
+
applicationStore,
|
|
843
|
+
undefined,
|
|
844
|
+
editorStore.graphManagerState,
|
|
845
|
+
QueryBuilderAdvancedWorkflowState.INSTANCE,
|
|
846
|
+
QueryBuilderActionConfig.INSTANCE,
|
|
847
|
+
applicationStore.config.options.queryBuilderConfig,
|
|
848
|
+
editorStore.editorMode.getSourceInfo(),
|
|
849
|
+
);
|
|
850
|
+
queryBuilderState.changeAccessorOwner(ingestDefinition);
|
|
851
|
+
const compatibleRuntimes = getCompatibleRuntimesFromAccessorOwner(
|
|
852
|
+
ingestDefinition,
|
|
853
|
+
editorStore.graphManagerState,
|
|
854
|
+
);
|
|
855
|
+
if (compatibleRuntimes.length > 0) {
|
|
856
|
+
queryBuilderState.changeSelectedRuntime(
|
|
857
|
+
guaranteeNonNullable(compatibleRuntimes[0]),
|
|
858
|
+
);
|
|
859
|
+
}
|
|
860
|
+
queryBuilderState.initializeWithQuery(accessPoint.func);
|
|
861
|
+
return queryBuilderState;
|
|
862
|
+
},
|
|
863
|
+
actionConfigs: [
|
|
864
|
+
{
|
|
865
|
+
key: 'save-query-btn',
|
|
866
|
+
renderer: (
|
|
867
|
+
queryBuilderState: QueryBuilderState,
|
|
868
|
+
): React.ReactNode => {
|
|
869
|
+
const save = applicationStore.guardUnhandledError(
|
|
870
|
+
async () => {
|
|
871
|
+
try {
|
|
872
|
+
const rawLambda =
|
|
873
|
+
queryBuilderState.buildQueryForPersistence();
|
|
874
|
+
accessPoint.func = rawLambda;
|
|
875
|
+
embeddedQueryBuilderState.setEmbeddedQueryBuilderConfiguration(
|
|
876
|
+
undefined,
|
|
877
|
+
);
|
|
878
|
+
await flowResult(
|
|
879
|
+
lambdaEditorState.convertLambdaObjectToGrammarString({
|
|
880
|
+
pretty: true,
|
|
881
|
+
}),
|
|
882
|
+
);
|
|
883
|
+
} catch (error) {
|
|
884
|
+
assertErrorThrown(error);
|
|
885
|
+
applicationStore.notificationService.notifyError(
|
|
886
|
+
`Can't save access point lambda: ${error.message}`,
|
|
887
|
+
);
|
|
888
|
+
}
|
|
889
|
+
},
|
|
890
|
+
);
|
|
891
|
+
return (
|
|
892
|
+
<button
|
|
893
|
+
className="query-builder__dialog__header__custom-action"
|
|
894
|
+
tabIndex={-1}
|
|
895
|
+
disabled={props.isReadOnly}
|
|
896
|
+
onClick={save}
|
|
897
|
+
>
|
|
898
|
+
Save Lambda
|
|
899
|
+
</button>
|
|
900
|
+
);
|
|
901
|
+
},
|
|
902
|
+
},
|
|
903
|
+
],
|
|
904
|
+
disableCompile: true,
|
|
905
|
+
}),
|
|
906
|
+
);
|
|
907
|
+
});
|
|
908
|
+
|
|
805
909
|
return (
|
|
806
910
|
<PanelDnDEntry
|
|
807
911
|
ref={ref}
|
|
@@ -866,17 +970,7 @@ export const LakehouseDataProductAccessPointEditor = observer(
|
|
|
866
970
|
disabled={props.isReadOnly}
|
|
867
971
|
title="Edit sample values"
|
|
868
972
|
style={{
|
|
869
|
-
border: '1px solid var(--color-blue-200)',
|
|
870
|
-
borderRadius: '4px',
|
|
871
|
-
padding: '0.5rem 0.75rem',
|
|
872
|
-
background: 'var(--color-blue-200)',
|
|
873
973
|
cursor: props.isReadOnly ? 'not-allowed' : 'pointer',
|
|
874
|
-
display: 'flex',
|
|
875
|
-
alignItems: 'center',
|
|
876
|
-
gap: '0.5rem',
|
|
877
|
-
color: 'white',
|
|
878
|
-
fontSize: '1.2rem',
|
|
879
|
-
whiteSpace: 'nowrap',
|
|
880
974
|
}}
|
|
881
975
|
>
|
|
882
976
|
<PencilEditIcon />
|
|
@@ -894,16 +988,7 @@ export const LakehouseDataProductAccessPointEditor = observer(
|
|
|
894
988
|
border: accessPointState.hasRelationElementMismatch
|
|
895
989
|
? '2px solid var(--color-red-300)'
|
|
896
990
|
: '1px solid var(--color-blue-200)',
|
|
897
|
-
borderRadius: '4px',
|
|
898
|
-
padding: '0.5rem 0.75rem',
|
|
899
|
-
background: 'var(--color-blue-200)',
|
|
900
991
|
cursor: props.isReadOnly ? 'not-allowed' : 'pointer',
|
|
901
|
-
display: 'flex',
|
|
902
|
-
alignItems: 'center',
|
|
903
|
-
gap: '0.5rem',
|
|
904
|
-
color: 'white',
|
|
905
|
-
fontSize: '1.2rem',
|
|
906
|
-
whiteSpace: 'nowrap',
|
|
907
992
|
}}
|
|
908
993
|
>
|
|
909
994
|
<PencilEditIcon />
|
|
@@ -918,17 +1003,7 @@ export const LakehouseDataProductAccessPointEditor = observer(
|
|
|
918
1003
|
disabled={props.isReadOnly}
|
|
919
1004
|
title="Add sample values"
|
|
920
1005
|
style={{
|
|
921
|
-
border: '1px solid var(--color-blue-200)',
|
|
922
|
-
borderRadius: '4px',
|
|
923
|
-
padding: '0.5rem 0.75rem',
|
|
924
|
-
background: 'var(--color-blue-200)',
|
|
925
1006
|
cursor: props.isReadOnly ? 'not-allowed' : 'pointer',
|
|
926
|
-
display: 'flex',
|
|
927
|
-
alignItems: 'center',
|
|
928
|
-
gap: '0.5rem',
|
|
929
|
-
color: 'white',
|
|
930
|
-
fontSize: '1.2rem',
|
|
931
|
-
whiteSpace: 'nowrap',
|
|
932
1007
|
}}
|
|
933
1008
|
>
|
|
934
1009
|
<PlusIcon />
|
|
@@ -1138,6 +1213,15 @@ export const LakehouseDataProductAccessPointEditor = observer(
|
|
|
1138
1213
|
<div className="access-point-editor__generic-entry">
|
|
1139
1214
|
<div className="access-point-editor__entry__container">
|
|
1140
1215
|
<div className="access-point-editor__entry">
|
|
1216
|
+
<button
|
|
1217
|
+
className="access-point-editor__query-builder-btn"
|
|
1218
|
+
onClick={editLambdaInQueryBuilder}
|
|
1219
|
+
disabled={props.isReadOnly}
|
|
1220
|
+
title="Edit lambda with query builder"
|
|
1221
|
+
>
|
|
1222
|
+
<PencilEditIcon />
|
|
1223
|
+
<span>Query Builder</span>
|
|
1224
|
+
</button>
|
|
1141
1225
|
<InlineLambdaEditor
|
|
1142
1226
|
className={'access-point-editor__lambda-editor'}
|
|
1143
1227
|
disabled={
|
|
@@ -2370,6 +2454,112 @@ const DataProductIconEditor = observer(
|
|
|
2370
2454
|
},
|
|
2371
2455
|
);
|
|
2372
2456
|
|
|
2457
|
+
const DataProductOwnershipEditor = observer(
|
|
2458
|
+
(props: { product: DataProduct; isReadOnly: boolean }) => {
|
|
2459
|
+
const { product, isReadOnly } = props;
|
|
2460
|
+
const owner =
|
|
2461
|
+
product.owner instanceof AppDirOwner ? product.owner : undefined;
|
|
2462
|
+
|
|
2463
|
+
const ensureOwner = (): AppDirOwner => {
|
|
2464
|
+
if (owner) {
|
|
2465
|
+
return owner;
|
|
2466
|
+
}
|
|
2467
|
+
const newOwner = observe_AppDirOwner(new AppDirOwner());
|
|
2468
|
+
dataProduct_setOwner(product, newOwner);
|
|
2469
|
+
return newOwner;
|
|
2470
|
+
};
|
|
2471
|
+
|
|
2472
|
+
const handleProductionChange: ChangeEventHandler<HTMLInputElement> = (
|
|
2473
|
+
event,
|
|
2474
|
+
) => {
|
|
2475
|
+
const currentOwner = ensureOwner();
|
|
2476
|
+
const val = event.target.value;
|
|
2477
|
+
if (val === '') {
|
|
2478
|
+
appDirOwner_setProduction(currentOwner, undefined);
|
|
2479
|
+
if (!currentOwner.prodParallel) {
|
|
2480
|
+
dataProduct_setOwner(product, undefined);
|
|
2481
|
+
}
|
|
2482
|
+
return;
|
|
2483
|
+
}
|
|
2484
|
+
const num = Number(val);
|
|
2485
|
+
if (Number.isNaN(num)) {
|
|
2486
|
+
return;
|
|
2487
|
+
}
|
|
2488
|
+
if (currentOwner.production) {
|
|
2489
|
+
appDirNode_setAppDirId(currentOwner.production, num);
|
|
2490
|
+
return;
|
|
2491
|
+
}
|
|
2492
|
+
const node = observe_AppDirNode(new AppDirNode());
|
|
2493
|
+
node.appDirId = num;
|
|
2494
|
+
node.level = AppDirLevel.DEPLOYMENT;
|
|
2495
|
+
appDirOwner_setProduction(currentOwner, node);
|
|
2496
|
+
};
|
|
2497
|
+
|
|
2498
|
+
const handleProdParallelChange: ChangeEventHandler<HTMLInputElement> = (
|
|
2499
|
+
event,
|
|
2500
|
+
) => {
|
|
2501
|
+
const currentOwner = ensureOwner();
|
|
2502
|
+
const val = event.target.value;
|
|
2503
|
+
if (val === '') {
|
|
2504
|
+
appDirOwner_setProdParallel(currentOwner, undefined);
|
|
2505
|
+
if (!currentOwner.production) {
|
|
2506
|
+
dataProduct_setOwner(product, undefined);
|
|
2507
|
+
}
|
|
2508
|
+
return;
|
|
2509
|
+
}
|
|
2510
|
+
const num = Number(val);
|
|
2511
|
+
if (Number.isNaN(num)) {
|
|
2512
|
+
return;
|
|
2513
|
+
}
|
|
2514
|
+
if (currentOwner.prodParallel) {
|
|
2515
|
+
appDirNode_setAppDirId(currentOwner.prodParallel, num);
|
|
2516
|
+
return;
|
|
2517
|
+
}
|
|
2518
|
+
const node = observe_AppDirNode(new AppDirNode());
|
|
2519
|
+
node.appDirId = num;
|
|
2520
|
+
node.level = AppDirLevel.DEPLOYMENT;
|
|
2521
|
+
appDirOwner_setProdParallel(currentOwner, node);
|
|
2522
|
+
};
|
|
2523
|
+
|
|
2524
|
+
return (
|
|
2525
|
+
<>
|
|
2526
|
+
<div className="panel__content__form__section">
|
|
2527
|
+
<div className="panel__content__form__section__header__label">
|
|
2528
|
+
Ownership
|
|
2529
|
+
</div>
|
|
2530
|
+
<div className="panel__content__form__section__header__prompt">
|
|
2531
|
+
Set the AppDir ownership for this Data Product.
|
|
2532
|
+
</div>
|
|
2533
|
+
<div className="panel__content__form__section__header__prompt">
|
|
2534
|
+
Production AppDir ID
|
|
2535
|
+
</div>
|
|
2536
|
+
<input
|
|
2537
|
+
className="input input-group__input panel__content__form__section__input input--dark input--small"
|
|
2538
|
+
type="number"
|
|
2539
|
+
disabled={isReadOnly}
|
|
2540
|
+
value={owner?.production?.appDirId ?? ''}
|
|
2541
|
+
onChange={handleProductionChange}
|
|
2542
|
+
placeholder="Enter production AppDir ID"
|
|
2543
|
+
/>
|
|
2544
|
+
</div>
|
|
2545
|
+
<div className="panel__content__form__section">
|
|
2546
|
+
<div className="panel__content__form__section__header__prompt">
|
|
2547
|
+
Prod Parallel AppDir ID
|
|
2548
|
+
</div>
|
|
2549
|
+
<input
|
|
2550
|
+
className="input input-group__input panel__content__form__section__input input--dark input--small"
|
|
2551
|
+
type="number"
|
|
2552
|
+
disabled={isReadOnly}
|
|
2553
|
+
value={owner?.prodParallel?.appDirId ?? ''}
|
|
2554
|
+
onChange={handleProdParallelChange}
|
|
2555
|
+
placeholder="Enter prod parallel AppDir ID"
|
|
2556
|
+
/>
|
|
2557
|
+
</div>
|
|
2558
|
+
</>
|
|
2559
|
+
);
|
|
2560
|
+
},
|
|
2561
|
+
);
|
|
2562
|
+
|
|
2373
2563
|
const HomeTab = observer(
|
|
2374
2564
|
(props: {
|
|
2375
2565
|
dataProductEditorState: DataProductEditorState;
|
|
@@ -2543,6 +2733,10 @@ const HomeTab = observer(
|
|
|
2543
2733
|
)}
|
|
2544
2734
|
</div>
|
|
2545
2735
|
<DataProductIconEditor product={product} isReadOnly={isReadOnly} />
|
|
2736
|
+
<DataProductOwnershipEditor
|
|
2737
|
+
product={product}
|
|
2738
|
+
isReadOnly={isReadOnly}
|
|
2739
|
+
/>
|
|
2546
2740
|
</div>
|
|
2547
2741
|
</div>
|
|
2548
2742
|
);
|
|
@@ -3341,10 +3535,7 @@ export const DataProductEditor = observer(() => {
|
|
|
3341
3535
|
</PanelHeaderActions>
|
|
3342
3536
|
</div>
|
|
3343
3537
|
|
|
3344
|
-
<div
|
|
3345
|
-
className="panel"
|
|
3346
|
-
style={{ padding: '1rem', flexDirection: 'row' }}
|
|
3347
|
-
>
|
|
3538
|
+
<div className="panel data-product-editor__content-panel">
|
|
3348
3539
|
<DataProductSidebar dataProductEditorState={dataProductEditorState} />
|
|
3349
3540
|
<ResizablePanelGroup orientation="vertical">
|
|
3350
3541
|
<ResizablePanel>{renderActivivtyBarTab()}</ResizablePanel>
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
DataProductQueryBuilderState,
|
|
28
28
|
QueryBuilderActionConfig,
|
|
29
29
|
QueryBuilderAdvancedWorkflowState,
|
|
30
|
-
|
|
30
|
+
resolveDataProductAccessor,
|
|
31
31
|
} from '@finos/legend-query-builder';
|
|
32
32
|
import type { DepotEntityWithOrigin } from '@finos/legend-storage';
|
|
33
33
|
import {
|
|
@@ -91,11 +91,12 @@ export const queryDataProduct = async (
|
|
|
91
91
|
defaultExecutionContext.func,
|
|
92
92
|
editorStore.graphManagerState.graph,
|
|
93
93
|
);
|
|
94
|
-
accessor =
|
|
95
|
-
relationMetadata,
|
|
94
|
+
accessor = resolveDataProductAccessor(
|
|
96
95
|
dataProduct,
|
|
97
96
|
defaultExecutionContext,
|
|
98
97
|
editorStore.graphManagerState.graph,
|
|
98
|
+
undefined,
|
|
99
|
+
relationMetadata,
|
|
99
100
|
);
|
|
100
101
|
}
|
|
101
102
|
await flowResult(
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { observer } from 'mobx-react-lite';
|
|
18
|
+
import { useEffect } from 'react';
|
|
18
19
|
import { ServiceEditorState } from '../../../../stores/editor/editor-state/element-editor-state/service/ServiceEditorState.js';
|
|
19
20
|
import {
|
|
20
21
|
clsx,
|
|
@@ -22,6 +23,8 @@ import {
|
|
|
22
23
|
CustomSelectorInput,
|
|
23
24
|
CheckSquareIcon,
|
|
24
25
|
SquareIcon,
|
|
26
|
+
ExternalLinkSquareIcon,
|
|
27
|
+
CircleNotchIcon,
|
|
25
28
|
} from '@finos/legend-art';
|
|
26
29
|
import { prettyCONSTName } from '@finos/legend-shared';
|
|
27
30
|
import { LEGEND_STUDIO_TEST_ID } from '../../../../__lib__/LegendStudioTesting.js';
|
|
@@ -30,7 +33,10 @@ import { flowResult } from 'mobx';
|
|
|
30
33
|
import { useEditorStore } from '../../EditorStoreProvider.js';
|
|
31
34
|
import { useApplicationStore } from '@finos/legend-application';
|
|
32
35
|
import { MASTER_SNAPSHOT_ALIAS } from '@finos/legend-server-depot';
|
|
33
|
-
import {
|
|
36
|
+
import {
|
|
37
|
+
LATEST_PROJECT_REVISION,
|
|
38
|
+
generateServiceManagementUrl,
|
|
39
|
+
} from '../../../../stores/editor/editor-state/element-editor-state/service/ServiceRegistrationState.js';
|
|
34
40
|
|
|
35
41
|
export const ServiceRegistrationEditor = observer(() => {
|
|
36
42
|
const editorStore = useEditorStore();
|
|
@@ -136,6 +142,12 @@ export const ServiceRegistrationEditor = observer(() => {
|
|
|
136
142
|
!selectedServiceType ||
|
|
137
143
|
registrationState.registrationState.isInProgress;
|
|
138
144
|
|
|
145
|
+
useEffect(() => {
|
|
146
|
+
flowResult(registrationState.checkServiceRegistration()).catch(
|
|
147
|
+
applicationStore.alertUnhandledError,
|
|
148
|
+
);
|
|
149
|
+
}, [registrationState, applicationStore]);
|
|
150
|
+
|
|
139
151
|
return (
|
|
140
152
|
<div
|
|
141
153
|
data-testid={LEGEND_STUDIO_TEST_ID.SERVICE_REGISTRATION_EDITOR}
|
|
@@ -169,6 +181,60 @@ export const ServiceRegistrationEditor = observer(() => {
|
|
|
169
181
|
{`${registrationState.registrationState.message}...`}
|
|
170
182
|
</div>
|
|
171
183
|
)}
|
|
184
|
+
<div className="panel__content__form__section">
|
|
185
|
+
<div className="panel__content__form__section__header__label">
|
|
186
|
+
Deployment Status
|
|
187
|
+
</div>
|
|
188
|
+
<div className="panel__content__form__section__header__prompt">
|
|
189
|
+
Environments where this service is currently deployed
|
|
190
|
+
</div>
|
|
191
|
+
<div className="service-registration-editor__deployment-status">
|
|
192
|
+
{registrationState.deploymentCheckState.isInProgress && (
|
|
193
|
+
<div className="service-registration-editor__deployment-status__loading">
|
|
194
|
+
<CircleNotchIcon className="service-registration-editor__deployment-status__loading-icon" />
|
|
195
|
+
<span>Fetching deployment status...</span>
|
|
196
|
+
</div>
|
|
197
|
+
)}
|
|
198
|
+
{registrationState.deploymentCheckState.hasCompleted &&
|
|
199
|
+
registrationState.registeredEnvs.length === 0 && (
|
|
200
|
+
<div className="service-registration-editor__deployment-status__empty">
|
|
201
|
+
Service is not deployed to any environment
|
|
202
|
+
</div>
|
|
203
|
+
)}
|
|
204
|
+
{registrationState.deploymentCheckState.hasCompleted &&
|
|
205
|
+
registrationState.registeredEnvs.length > 0 && (
|
|
206
|
+
<div className="service-registration-editor__deployment-status__envs">
|
|
207
|
+
<span className="service-registration-editor__deployment-status__label">
|
|
208
|
+
Service is deployed to
|
|
209
|
+
</span>
|
|
210
|
+
{registrationState.registeredEnvs.map((env) => {
|
|
211
|
+
const envConfig =
|
|
212
|
+
registrationState.registrationOptions.find(
|
|
213
|
+
(e) => e.env === env,
|
|
214
|
+
);
|
|
215
|
+
return envConfig ? (
|
|
216
|
+
<a
|
|
217
|
+
key={env}
|
|
218
|
+
className="service-editor__deployment-link"
|
|
219
|
+
href={generateServiceManagementUrl(
|
|
220
|
+
envConfig.managementUrl,
|
|
221
|
+
serviceState.service.pattern,
|
|
222
|
+
)}
|
|
223
|
+
target="_blank"
|
|
224
|
+
rel="noopener noreferrer"
|
|
225
|
+
title={`Open in ${env}`}
|
|
226
|
+
>
|
|
227
|
+
<span className="service-editor__deployment-link__env">
|
|
228
|
+
{env.toUpperCase()}
|
|
229
|
+
</span>
|
|
230
|
+
<ExternalLinkSquareIcon />
|
|
231
|
+
</a>
|
|
232
|
+
) : null;
|
|
233
|
+
})}
|
|
234
|
+
</div>
|
|
235
|
+
)}
|
|
236
|
+
</div>
|
|
237
|
+
</div>
|
|
172
238
|
<div className="panel__content__form__section">
|
|
173
239
|
<div className="panel__content__form__section__header__label">
|
|
174
240
|
Activate Service
|
|
@@ -183,7 +183,9 @@ enum PROMOTE_QUERY_TYPE {
|
|
|
183
183
|
|
|
184
184
|
export const NewFunctionModal = observer(
|
|
185
185
|
(props: {
|
|
186
|
-
_class
|
|
186
|
+
_class?: Class | undefined;
|
|
187
|
+
defaultFunctionName?: string | undefined;
|
|
188
|
+
defaultPackagePath?: string | undefined;
|
|
187
189
|
close: () => void;
|
|
188
190
|
showModal: boolean;
|
|
189
191
|
promoteToFunction: (
|
|
@@ -192,18 +194,26 @@ export const NewFunctionModal = observer(
|
|
|
192
194
|
) => Promise<void>;
|
|
193
195
|
isReadOnly?: boolean;
|
|
194
196
|
}) => {
|
|
195
|
-
const {
|
|
197
|
+
const {
|
|
198
|
+
isReadOnly,
|
|
199
|
+
close,
|
|
200
|
+
_class,
|
|
201
|
+
defaultFunctionName,
|
|
202
|
+
defaultPackagePath,
|
|
203
|
+
showModal,
|
|
204
|
+
promoteToFunction,
|
|
205
|
+
} = props;
|
|
196
206
|
const editorStore = useEditorStore();
|
|
197
207
|
const applicationStore = editorStore.applicationStore;
|
|
198
208
|
const nameRef = useRef<HTMLInputElement>(null);
|
|
199
|
-
const defaultFunctionname =
|
|
200
|
-
|
|
201
|
-
: `QueryFunction
|
|
209
|
+
const defaultFunctionname =
|
|
210
|
+
defaultFunctionName ??
|
|
211
|
+
(_class ? `${_class.name}_QueryFunction` : `QueryFunction`);
|
|
202
212
|
const [functionPath, setFunctionPath] =
|
|
203
213
|
useState<string>(defaultFunctionname);
|
|
204
214
|
const [packagePath, funcName] = resolvePackagePathAndElementName(
|
|
205
215
|
functionPath,
|
|
206
|
-
_class?.package?.path ?? 'model::functions',
|
|
216
|
+
defaultPackagePath ?? _class?.package?.path ?? 'model::functions',
|
|
207
217
|
);
|
|
208
218
|
const [isValid, setIsValid] = useState(true);
|
|
209
219
|
|
package/src/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.ts
CHANGED
|
@@ -63,6 +63,8 @@ import {
|
|
|
63
63
|
DataProductDiagram,
|
|
64
64
|
observe_DataProductDiagram,
|
|
65
65
|
stub_Mapping,
|
|
66
|
+
AppDirOwner,
|
|
67
|
+
type AppDirNode,
|
|
66
68
|
} from '@finos/legend-graph';
|
|
67
69
|
import type { EditorStore } from '../../../EditorStore.js';
|
|
68
70
|
import { ElementEditorState } from '../ElementEditorState.js';
|
|
@@ -1192,7 +1194,7 @@ export class DataProductEditorState extends ElementEditorState {
|
|
|
1192
1194
|
this.ingestionManager,
|
|
1193
1195
|
).deployDataProduct(
|
|
1194
1196
|
grammar,
|
|
1195
|
-
guaranteeNonNullable(this.
|
|
1197
|
+
guaranteeNonNullable(this.appDirDeployment),
|
|
1196
1198
|
(val: string) =>
|
|
1197
1199
|
this.editorStore.applicationStore.alertService.setBlockingAlert({
|
|
1198
1200
|
message: val,
|
|
@@ -1261,9 +1263,7 @@ export class DataProductEditorState extends ElementEditorState {
|
|
|
1261
1263
|
}
|
|
1262
1264
|
|
|
1263
1265
|
get validForDeployment(): boolean {
|
|
1264
|
-
return Boolean(
|
|
1265
|
-
this.associatedIngest?.appDirDeployment && this.ingestionManager,
|
|
1266
|
-
);
|
|
1266
|
+
return Boolean(this.appDirDeployment && this.ingestionManager);
|
|
1267
1267
|
}
|
|
1268
1268
|
|
|
1269
1269
|
get accessPoints(): AccessPoint[] {
|
|
@@ -1275,7 +1275,7 @@ export class DataProductEditorState extends ElementEditorState {
|
|
|
1275
1275
|
}
|
|
1276
1276
|
|
|
1277
1277
|
get deployValidationMessage(): string {
|
|
1278
|
-
if (!this.
|
|
1278
|
+
if (!this.appDirDeployment) {
|
|
1279
1279
|
return 'No app dir deployment found';
|
|
1280
1280
|
} else if (!this.ingestionManager) {
|
|
1281
1281
|
return 'No ingestion manager found';
|
|
@@ -1283,6 +1283,15 @@ export class DataProductEditorState extends ElementEditorState {
|
|
|
1283
1283
|
return 'Deploy';
|
|
1284
1284
|
}
|
|
1285
1285
|
|
|
1286
|
+
// Use explicit owner from the data product if available, otherwise fall back to the associated ingest's app dir deployment
|
|
1287
|
+
get appDirDeployment(): AppDirNode | undefined {
|
|
1288
|
+
const owner = this.product.owner;
|
|
1289
|
+
if (owner instanceof AppDirOwner) {
|
|
1290
|
+
return owner.production;
|
|
1291
|
+
}
|
|
1292
|
+
return this.associatedIngest?.appDirDeployment;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1286
1295
|
// We need to get the associated Ingest to get the app dir deployment
|
|
1287
1296
|
// We could do a more in depth check on the access point lambdas to check which ingest it uses but for now
|
|
1288
1297
|
// we will assume all ingests have the same DID
|
package/src/stores/editor/editor-state/element-editor-state/service/ServiceRegistrationState.ts
CHANGED
|
@@ -255,7 +255,9 @@ export class ServiceConfigState {
|
|
|
255
255
|
|
|
256
256
|
export class ServiceRegistrationState extends ServiceConfigState {
|
|
257
257
|
readonly service: Service;
|
|
258
|
+
readonly deploymentCheckState = ActionState.create();
|
|
258
259
|
activatePostRegistration = true;
|
|
260
|
+
registeredEnvs: string[] = [];
|
|
259
261
|
|
|
260
262
|
constructor(
|
|
261
263
|
editorStore: EditorStore,
|
|
@@ -267,8 +269,11 @@ export class ServiceRegistrationState extends ServiceConfigState {
|
|
|
267
269
|
|
|
268
270
|
makeObservable(this, {
|
|
269
271
|
activatePostRegistration: observable,
|
|
272
|
+
registeredEnvs: observable,
|
|
270
273
|
setActivatePostRegistration: action,
|
|
274
|
+
setRegisteredEnvs: action,
|
|
271
275
|
registerService: flow,
|
|
276
|
+
checkServiceRegistration: flow,
|
|
272
277
|
});
|
|
273
278
|
|
|
274
279
|
this.service = service;
|
|
@@ -277,6 +282,40 @@ export class ServiceRegistrationState extends ServiceConfigState {
|
|
|
277
282
|
this.activatePostRegistration = val;
|
|
278
283
|
}
|
|
279
284
|
|
|
285
|
+
setRegisteredEnvs(envs: string[]): void {
|
|
286
|
+
this.registeredEnvs = envs;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
*checkServiceRegistration(): GeneratorFn<void> {
|
|
290
|
+
this.deploymentCheckState.inProgress();
|
|
291
|
+
const envs: string[] = [];
|
|
292
|
+
const servicePattern = this.service.pattern.startsWith('/')
|
|
293
|
+
? this.service.pattern.substring(1)
|
|
294
|
+
: this.service.pattern;
|
|
295
|
+
for (const envConfig of this.registrationOptions) {
|
|
296
|
+
try {
|
|
297
|
+
const isRegistered =
|
|
298
|
+
(yield this.editorStore.graphManagerState.graphManager.checkServiceRegisteredByPattern(
|
|
299
|
+
envConfig.executionUrl,
|
|
300
|
+
servicePattern,
|
|
301
|
+
)) as boolean;
|
|
302
|
+
if (isRegistered) {
|
|
303
|
+
envs.push(envConfig.env);
|
|
304
|
+
}
|
|
305
|
+
} catch (error) {
|
|
306
|
+
assertErrorThrown(error);
|
|
307
|
+
this.editorStore.applicationStore.logService.warn(
|
|
308
|
+
LogEvent.create(
|
|
309
|
+
LEGEND_STUDIO_APP_EVENT.SERVICE_REGISTRATION_CHECK_FAILURE,
|
|
310
|
+
),
|
|
311
|
+
`Can't check registration status for env '${envConfig.env}': ${error.message}`,
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
this.setRegisteredEnvs(envs);
|
|
316
|
+
this.deploymentCheckState.complete();
|
|
317
|
+
}
|
|
318
|
+
|
|
280
319
|
*registerService(): GeneratorFn<void> {
|
|
281
320
|
try {
|
|
282
321
|
this.registrationState.inProgress();
|
|
@@ -43,6 +43,9 @@ import {
|
|
|
43
43
|
DataProductOperationalMetadata,
|
|
44
44
|
type DataProduct_DeliveryFrequency,
|
|
45
45
|
type DataProduct_Region,
|
|
46
|
+
type DataProductOwner,
|
|
47
|
+
type AppDirOwner,
|
|
48
|
+
type AppDirNode,
|
|
46
49
|
} from '@finos/legend-graph';
|
|
47
50
|
import { addUniqueEntry, deleteEntry, swapEntry } from '@finos/legend-shared';
|
|
48
51
|
import { action } from 'mobx';
|
|
@@ -409,3 +412,27 @@ export const operationalMetadata_deleteCoverageRegion = action(
|
|
|
409
412
|
}
|
|
410
413
|
},
|
|
411
414
|
);
|
|
415
|
+
|
|
416
|
+
export const dataProduct_setOwner = action(
|
|
417
|
+
(product: DataProduct, owner: DataProductOwner | undefined) => {
|
|
418
|
+
product.owner = owner;
|
|
419
|
+
},
|
|
420
|
+
);
|
|
421
|
+
|
|
422
|
+
export const appDirOwner_setProduction = action(
|
|
423
|
+
(owner: AppDirOwner, appDirNode: AppDirNode | undefined) => {
|
|
424
|
+
owner.production = appDirNode;
|
|
425
|
+
},
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
export const appDirOwner_setProdParallel = action(
|
|
429
|
+
(owner: AppDirOwner, appDirNode: AppDirNode | undefined) => {
|
|
430
|
+
owner.prodParallel = appDirNode;
|
|
431
|
+
},
|
|
432
|
+
);
|
|
433
|
+
|
|
434
|
+
export const appDirNode_setAppDirId = action(
|
|
435
|
+
(node: AppDirNode, appDirId: number) => {
|
|
436
|
+
node.appDirId = appDirId;
|
|
437
|
+
},
|
|
438
|
+
);
|
package/tsconfig.json
CHANGED
|
@@ -72,7 +72,6 @@
|
|
|
72
72
|
"./src/application/LegendStudioApplicationConfig.ts",
|
|
73
73
|
"./src/application/LegendStudioPluginManager.ts",
|
|
74
74
|
"./src/components/__test-utils__/EmbeddedQueryBuilderTestUtils.ts",
|
|
75
|
-
"./src/components/editor/editor-group/accessor/AccessorQueryBuilderHelper.ts",
|
|
76
75
|
"./src/components/editor/editor-group/dataProduct/DataProductQueryBuilderHelper.ts",
|
|
77
76
|
"./src/components/shared/StudioSDLCErrors.ts",
|
|
78
77
|
"./src/stores/LegendStudioApplicationPlugin.ts",
|
|
@@ -248,6 +247,8 @@
|
|
|
248
247
|
"./src/components/editor/editor-group/ProtocolValueBuilder.tsx",
|
|
249
248
|
"./src/components/editor/editor-group/RuntimeEditor.tsx",
|
|
250
249
|
"./src/components/editor/editor-group/UnsupportedElementEditor.tsx",
|
|
250
|
+
"./src/components/editor/editor-group/accessor/AccessorQueryBuilder.tsx",
|
|
251
|
+
"./src/components/editor/editor-group/accessor/AccessorQueryBuilderHelper.tsx",
|
|
251
252
|
"./src/components/editor/editor-group/connection-editor/ConnectionEditor.tsx",
|
|
252
253
|
"./src/components/editor/editor-group/connection-editor/DatabaseBuilderWizard.tsx",
|
|
253
254
|
"./src/components/editor/editor-group/connection-editor/DatabaseEditorHelper.tsx",
|