@finos/legend-application-studio 28.19.79 → 28.19.81

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 (23) hide show
  1. package/lib/components/editor/editor-group/dataProduct/DataProductEditor.d.ts +0 -5
  2. package/lib/components/editor/editor-group/dataProduct/DataProductEditor.d.ts.map +1 -1
  3. package/lib/components/editor/editor-group/dataProduct/DataProductEditor.js +53 -42
  4. package/lib/components/editor/editor-group/dataProduct/DataProductEditor.js.map +1 -1
  5. package/lib/index.css +2 -2
  6. package/lib/index.css.map +1 -1
  7. package/lib/package.json +1 -1
  8. package/lib/stores/editor/NewElementState.d.ts.map +1 -1
  9. package/lib/stores/editor/NewElementState.js +1 -2
  10. package/lib/stores/editor/NewElementState.js.map +1 -1
  11. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.d.ts +3 -10
  12. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.d.ts.map +1 -1
  13. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.js +4 -41
  14. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.js.map +1 -1
  15. package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.d.ts +5 -4
  16. package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.d.ts.map +1 -1
  17. package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.js +22 -10
  18. package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.js.map +1 -1
  19. package/package.json +16 -16
  20. package/src/components/editor/editor-group/dataProduct/DataProductEditor.tsx +159 -141
  21. package/src/stores/editor/NewElementState.ts +0 -2
  22. package/src/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.ts +1 -63
  23. package/src/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.ts +47 -18
@@ -80,6 +80,7 @@ import {
80
80
  GitBranchIcon,
81
81
  ListIcon,
82
82
  PanelLoadingIndicator,
83
+ GearSuggestIcon,
83
84
  } from '@finos/legend-art';
84
85
  import {
85
86
  type ChangeEventHandler,
@@ -101,13 +102,11 @@ import { CodeEditor } from '@finos/legend-lego/code-editor';
101
102
  import {
102
103
  type DataProductElement,
103
104
  type DataProductElementScope,
104
- type DataProductRuntimeInfo,
105
105
  type Expertise,
106
106
  type GraphManagerState,
107
107
  type LakehouseAccessPoint,
108
108
  type Mapping,
109
109
  type PackageableElement,
110
- type PackageableRuntime,
111
110
  type DataProduct,
112
111
  type DataProductDiagram,
113
112
  DataProductEmbeddedImageIcon,
@@ -124,6 +123,8 @@ import {
124
123
  ExternalDataProductType,
125
124
  DataProductLink,
126
125
  observer_DataProductLink,
126
+ DataProduct_Region,
127
+ DataProduct_DeliveryFrequency,
127
128
  } from '@finos/legend-graph';
128
129
  import {
129
130
  accessPoint_setClassification,
@@ -139,8 +140,6 @@ import {
139
140
  supportInfo_setDocumentationUrl,
140
141
  supportInfo_setFaqUrl,
141
142
  supportInfo_setLinkLabel,
142
- runtimeInfo_setId,
143
- runtimeInfo_setDescription,
144
143
  supportInfo_setSupportUrl,
145
144
  supportInfo_setWebsite,
146
145
  dataProduct_setType,
@@ -153,6 +152,9 @@ import {
153
152
  accessPointGroup_setTitle,
154
153
  accessPoint_setDescription,
155
154
  accessPoint_setTitle,
155
+ dataProduct_setOperationalMetadataIfAbsent,
156
+ operationalMetadata_deleteCoverageRegion,
157
+ operationalMetadata_addCoverageRegion,
156
158
  dataProductDiagram_setTitle,
157
159
  dataProductDiagram_setDescription,
158
160
  } from '../../../../stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.js';
@@ -1266,140 +1268,6 @@ const AccessPointGroupPublicToggle = observer(
1266
1268
  },
1267
1269
  );
1268
1270
 
1269
- export const CompatibleRuntimesEditor = observer(
1270
- (props: { groupState: ModelAccessPointGroupState }) => {
1271
- const { groupState } = props;
1272
- const group = groupState.value;
1273
-
1274
- const handleSelectRuntime = (runtime: DataProductRuntimeInfo) => {
1275
- groupState.setDefaultRuntime(runtime);
1276
- };
1277
-
1278
- // Event handlers
1279
- const handleAddRuntime = (option: {
1280
- label: string;
1281
- value: PackageableRuntime;
1282
- }): void => {
1283
- if (typeof option.value === 'object') {
1284
- groupState.addCompatibleRuntime(option.value);
1285
- }
1286
- };
1287
-
1288
- const handleRemoveRuntime = (runtime: DataProductRuntimeInfo): void => {
1289
- groupState.removeCompatibleRuntime(runtime);
1290
- };
1291
-
1292
- const handleRuntimeTitleChange = (
1293
- runtimeInfo: DataProductRuntimeInfo,
1294
- value: string | undefined,
1295
- ): void => {
1296
- runtimeInfo_setId(runtimeInfo, value ?? '');
1297
- };
1298
-
1299
- const handleRuntimeDescriptionChange = (
1300
- runtimeInfo: DataProductRuntimeInfo,
1301
- value: string | undefined,
1302
- ): void => {
1303
- runtimeInfo_setDescription(runtimeInfo, value);
1304
- };
1305
-
1306
- // ListEditor component renderers
1307
- const RuntimeComponent = observer(
1308
- (runtimeComponentProps: {
1309
- item: DataProductRuntimeInfo;
1310
- }): React.ReactElement => {
1311
- const { item } = runtimeComponentProps;
1312
-
1313
- return (
1314
- <>
1315
- <div className="panel__content__form__section__list__item__content">
1316
- <div className="panel__content__form__section__header__label">
1317
- Default?
1318
- </div>
1319
- <input
1320
- type="radio"
1321
- name="defaultRuntimeRadio"
1322
- value={item.id}
1323
- checked={group.defaultRuntime === item}
1324
- onChange={() => handleSelectRuntime(item)}
1325
- />
1326
- </div>
1327
- <div className="panel__content__form__section__list__item__content">
1328
- <div className="panel__content__form__section__header__label">
1329
- Runtime
1330
- </div>
1331
- <div className="panel__content__form__section__list__item__content__title">
1332
- {item.runtime.value.path}
1333
- </div>
1334
- </div>
1335
- <div className="panel__content__form__section__list__item__form">
1336
- <PanelFormTextField
1337
- name="Title"
1338
- value={item.id}
1339
- update={(value) => handleRuntimeTitleChange(item, value)}
1340
- placeholder="Enter title"
1341
- className="dataSpace-editor__general__diagrams__title"
1342
- />
1343
- <PanelFormTextField
1344
- name="Description"
1345
- value={item.description ?? ''}
1346
- update={(value) => handleRuntimeDescriptionChange(item, value)}
1347
- placeholder="Enter description"
1348
- className="dataSpace-editor__general__diagrams__description"
1349
- />
1350
- </div>
1351
- </>
1352
- );
1353
- },
1354
- );
1355
-
1356
- const NewRuntimeComponent = observer(
1357
- (newRuntimeProps: {
1358
- onFinishEditing: () => void;
1359
- }): React.ReactElement => {
1360
- const { onFinishEditing } = newRuntimeProps;
1361
-
1362
- return (
1363
- <div className="panel__content__form__section__list__new-item__input">
1364
- <CustomSelectorInput
1365
- options={groupState.getCompatibleRuntimeOptions()}
1366
- onChange={(event: {
1367
- label: string;
1368
- value: PackageableRuntime;
1369
- }) => {
1370
- onFinishEditing();
1371
- handleAddRuntime(event);
1372
- }}
1373
- placeholder="Select a runtime to add..."
1374
- darkMode={
1375
- !groupState.state.editorStore.applicationStore.layoutService
1376
- .TEMPORARY__isLightColorThemeEnabled
1377
- }
1378
- />
1379
- </div>
1380
- );
1381
- },
1382
- );
1383
-
1384
- return (
1385
- <ListEditor
1386
- title="Compatible Runtimes"
1387
- prompt="Add compatible runtimes to include in this Data Product. Set a title and description for each runtime."
1388
- items={group.compatibleRuntimes}
1389
- keySelector={(element: DataProductRuntimeInfo) =>
1390
- element.runtime.value.path
1391
- }
1392
- ItemComponent={RuntimeComponent}
1393
- NewItemComponent={NewRuntimeComponent}
1394
- handleRemoveItem={handleRemoveRuntime}
1395
- isReadOnly={groupState.state.isReadOnly}
1396
- emptyMessage="No runtimes specified"
1397
- emptyClassName="data-product-editor__empty-runtime"
1398
- />
1399
- );
1400
- },
1401
- );
1402
-
1403
1271
  export const CompatibleDiagramsEditor = observer(
1404
1272
  (props: { groupState: ModelAccessPointGroupState }) => {
1405
1273
  const { groupState } = props;
@@ -1678,7 +1546,6 @@ const ModelAccessPointGroupEditor = observer(
1678
1546
  <LongArrowRightIcon />
1679
1547
  </button>
1680
1548
  </div>
1681
- <CompatibleRuntimesEditor groupState={groupState} />
1682
1549
  <CompatibleDiagramsEditor groupState={groupState} />
1683
1550
  <FeaturedElementsEditor
1684
1551
  groupState={groupState}
@@ -2181,6 +2048,11 @@ const DataProductSidebar = observer(
2181
2048
  title: 'Access Point Groups',
2182
2049
  icon: <GroupWorkIcon />,
2183
2050
  },
2051
+ {
2052
+ label: DATA_PRODUCT_TAB.OPERATIONAL,
2053
+ title: 'Operational Metadata',
2054
+ icon: <GearSuggestIcon />,
2055
+ },
2184
2056
  {
2185
2057
  label: DATA_PRODUCT_TAB.SUPPORT,
2186
2058
  icon: <QuestionCircleIcon />,
@@ -2206,8 +2078,8 @@ const DataProductSidebar = observer(
2206
2078
  title={activity.title ?? activity.label}
2207
2079
  style={{
2208
2080
  flexDirection: 'column',
2209
- fontSize: '12px',
2210
- margin: '1rem 0rem',
2081
+ fontSize: '10px',
2082
+ margin: '0.5rem 0rem',
2211
2083
  }}
2212
2084
  >
2213
2085
  {activity.icon}
@@ -3026,6 +2898,145 @@ const getDataProductViewerState = (
3026
2898
  return dataProductViewerState;
3027
2899
  };
3028
2900
 
2901
+ const OperationalTab = observer(
2902
+ (props: {
2903
+ dataProductEditorState: DataProductEditorState;
2904
+ isReadOnly: boolean;
2905
+ }) => {
2906
+ const { dataProductEditorState, isReadOnly } = props;
2907
+ const product = dataProductEditorState.product;
2908
+ const CHOOSE_REGION = 'Add Coverage Region...';
2909
+
2910
+ const getCoverageRegionOptions = () => {
2911
+ const existingRegions =
2912
+ product.operationalMetadata?.coverageRegions ?? [];
2913
+ return Object.values(DataProduct_Region)
2914
+ .filter((region) => !existingRegions.includes(region))
2915
+ .map((region) => ({
2916
+ label: region,
2917
+ value: region,
2918
+ }));
2919
+ };
2920
+
2921
+ const handleAddRegion = (
2922
+ val: { label: string; value: DataProduct_Region } | null,
2923
+ ): void => {
2924
+ dataProduct_setOperationalMetadataIfAbsent(product);
2925
+ if (product.operationalMetadata && val) {
2926
+ operationalMetadata_addCoverageRegion(
2927
+ product.operationalMetadata,
2928
+ val.value,
2929
+ );
2930
+ }
2931
+ };
2932
+
2933
+ const handleRemoveRegion = (region: DataProduct_Region) => {
2934
+ if (product.operationalMetadata) {
2935
+ operationalMetadata_deleteCoverageRegion(
2936
+ product.operationalMetadata,
2937
+ region,
2938
+ );
2939
+ }
2940
+ };
2941
+
2942
+ const handleUpdateFrequencyChange = (
2943
+ val: { label: string; value: DataProduct_DeliveryFrequency } | null,
2944
+ ): void => {
2945
+ dataProduct_setOperationalMetadataIfAbsent(product);
2946
+ if (product.operationalMetadata && val) {
2947
+ product.operationalMetadata.updateFrequency = val.value;
2948
+ }
2949
+ };
2950
+
2951
+ return (
2952
+ <div>
2953
+ <div
2954
+ className="panel__content__form__section__header__label"
2955
+ style={{ paddingLeft: '1rem' }}
2956
+ >
2957
+ Operational Metadata
2958
+ </div>
2959
+ <div
2960
+ className="panel__content__form__section__header__prompt"
2961
+ style={{ paddingLeft: '1rem' }}
2962
+ >
2963
+ Configure operational metadata for this Data Product.
2964
+ </div>
2965
+ <div className="data-product-editor__operational-input">
2966
+ <div
2967
+ className="panel__content__form__section__header__label"
2968
+ style={{ justifyContent: 'space-between', width: '45rem' }}
2969
+ >
2970
+ Coverage Regions
2971
+ </div>
2972
+ <div className="panel__content__form__section__header__prompt">
2973
+ Select the regions this Data Product covers.
2974
+ </div>
2975
+ <div className="panel__content__form__section__list__id-list">
2976
+ {product.operationalMetadata?.coverageRegions?.map((region) => (
2977
+ <div
2978
+ className="panel__content__form__section__list__item"
2979
+ key={region}
2980
+ >
2981
+ {region}
2982
+ <button
2983
+ className="panel__content__form__section__list__item__remove-btn"
2984
+ disabled={dataProductEditorState.isReadOnly}
2985
+ onClick={() => handleRemoveRegion(region)}
2986
+ tabIndex={-1}
2987
+ >
2988
+ <TimesIcon />
2989
+ </button>
2990
+ </div>
2991
+ ))}
2992
+ </div>
2993
+ <div className="panel__content__form__section__list__new-item__input">
2994
+ <CustomSelectorInput
2995
+ options={getCoverageRegionOptions()}
2996
+ onChange={handleAddRegion}
2997
+ placeholder={CHOOSE_REGION}
2998
+ darkMode={true}
2999
+ disabled={isReadOnly}
3000
+ />
3001
+ </div>
3002
+ </div>
3003
+ <div className="data-product-editor__operational-input">
3004
+ <div
3005
+ className="panel__content__form__section__header__label"
3006
+ style={{ justifyContent: 'space-between', width: '45rem' }}
3007
+ >
3008
+ Update Frequency
3009
+ </div>
3010
+ <div className="panel__content__form__section__header__prompt">
3011
+ Select the update frequency of this Data Product.
3012
+ </div>
3013
+ <div className="panel__content__form__section__list__new-item__input">
3014
+ <CustomSelectorInput
3015
+ options={Object.values(DataProduct_DeliveryFrequency).map(
3016
+ (region) => ({
3017
+ label: region,
3018
+ value: region,
3019
+ }),
3020
+ )}
3021
+ onChange={handleUpdateFrequencyChange}
3022
+ value={
3023
+ product.operationalMetadata?.updateFrequency
3024
+ ? {
3025
+ label: product.operationalMetadata.updateFrequency,
3026
+ value: product.operationalMetadata.updateFrequency,
3027
+ }
3028
+ : null
3029
+ }
3030
+ darkMode={true}
3031
+ disabled={isReadOnly}
3032
+ />
3033
+ </div>
3034
+ </div>
3035
+ </div>
3036
+ );
3037
+ },
3038
+ );
3039
+
3029
3040
  export const DataProductEditor = observer(() => {
3030
3041
  const editorStore = useEditorStore();
3031
3042
  const dataProductEditorState =
@@ -3085,6 +3096,13 @@ export const DataProductEditor = observer(() => {
3085
3096
  isReadOnly={isReadOnly}
3086
3097
  />
3087
3098
  );
3099
+ case DATA_PRODUCT_TAB.OPERATIONAL:
3100
+ return (
3101
+ <OperationalTab
3102
+ dataProductEditorState={dataProductEditorState}
3103
+ isReadOnly={isReadOnly}
3104
+ />
3105
+ );
3088
3106
  default:
3089
3107
  return null;
3090
3108
  }
@@ -89,7 +89,6 @@ import {
89
89
  AccessPointGroup,
90
90
  ModelAccessPointGroup,
91
91
  stub_Mapping,
92
- DataProductRuntimeInfo,
93
92
  InternalDataProductType,
94
93
  } from '@finos/legend-graph';
95
94
  import type { DSL_Mapping_LegendStudioApplicationPlugin_Extension } from '../extensions/DSL_Mapping_LegendStudioApplicationPlugin_Extension.js';
@@ -560,7 +559,6 @@ export class NewLakehouseDataProductDriver extends NewElementDriver<DataProduct>
560
559
  defaultGroup.id = 'default';
561
560
  defaultGroup.mapping =
562
561
  PackageableElementExplicitReference.create(stub_Mapping());
563
- defaultGroup.defaultRuntime = new DataProductRuntimeInfo();
564
562
  dataProduct_addAccessPointGroup(dataProduct, defaultGroup);
565
563
  }
566
564
 
@@ -33,20 +33,16 @@ import {
33
33
  getStereotype,
34
34
  type StereotypeReference,
35
35
  ModelAccessPointGroup,
36
- PackageableRuntime,
37
36
  Association,
38
37
  Class,
39
38
  Enumeration,
40
39
  Package,
41
- observe_DataProductRuntimeInfo,
42
40
  PackageableElementExplicitReference,
43
- DataProductRuntimeInfo,
44
41
  type DataProductElement,
45
42
  type Mapping,
46
43
  Expertise,
47
44
  observe_DataProductElementScope,
48
45
  DataProductElementScope,
49
- validate_PureExecutionMapping,
50
46
  type V1_RawLineageModel,
51
47
  type ArtifactGenerationExtensionResult,
52
48
  type V1_DataProductArtifact,
@@ -92,7 +88,6 @@ import {
92
88
  uuid,
93
89
  swapEntry,
94
90
  returnUndefOnError,
95
- deepEqual,
96
91
  } from '@finos/legend-shared';
97
92
  import {
98
93
  accessPointGroup_swapAccessPoints,
@@ -104,11 +99,8 @@ import {
104
99
  dataProduct_deleteAccessPointGroup,
105
100
  modelAccessPointGroup_removeDiagram,
106
101
  dataProduct_swapAccessPointGroups,
107
- modelAccessPointGroup_addCompatibleRuntime,
108
102
  modelAccessPointGroup_addElement,
109
- modelAccessPointGroup_removeCompatibleRuntime,
110
103
  modelAccessPointGroup_removeElement,
111
- modelAccessPointGroup_setDefaultRuntime,
112
104
  modelAccessPointGroup_setElementExclude,
113
105
  modelAccessPointGroup_setMapping,
114
106
  dataProduct_setSupportInfoIfAbsent,
@@ -130,6 +122,7 @@ export enum DATA_PRODUCT_TAB {
130
122
  HOME = 'Home',
131
123
  SUPPORT = 'Support',
132
124
  APG = 'APG',
125
+ OPERATIONAL = 'Operational',
133
126
  }
134
127
 
135
128
  export enum DATA_PRODUCT_TYPE {
@@ -782,23 +775,6 @@ export class ModelAccessPointGroupState extends AccessPointGroupState {
782
775
  );
783
776
  }
784
777
 
785
- setDefaultRuntime(runtime: DataProductRuntimeInfo) {
786
- modelAccessPointGroup_setDefaultRuntime(this.value, runtime);
787
- }
788
-
789
- addCompatibleRuntime(runtime: PackageableRuntime): void {
790
- const newRuntime = observe_DataProductRuntimeInfo(
791
- new DataProductRuntimeInfo(),
792
- );
793
- newRuntime.id = runtime.name;
794
- newRuntime.runtime = PackageableElementExplicitReference.create(runtime);
795
- modelAccessPointGroup_addCompatibleRuntime(this.value, newRuntime);
796
-
797
- if (deepEqual(this.value.defaultRuntime, new DataProductRuntimeInfo())) {
798
- this.setDefaultRuntime(newRuntime);
799
- }
800
- }
801
-
802
778
  getCompatibleDiagramOptions(): {
803
779
  label: string;
804
780
  value: PackageableElement;
@@ -816,17 +792,6 @@ export class ModelAccessPointGroupState extends AccessPointGroupState {
816
792
  }));
817
793
  }
818
794
 
819
- removeCompatibleRuntime(runtime: DataProductRuntimeInfo): void {
820
- modelAccessPointGroup_removeCompatibleRuntime(this.value, runtime);
821
- if (runtime === this.value.defaultRuntime) {
822
- if (!this.value.compatibleRuntimes.length) {
823
- this.setDefaultRuntime(new DataProductRuntimeInfo());
824
- } else if (this.value.compatibleRuntimes[0]) {
825
- this.setDefaultRuntime(this.value.compatibleRuntimes[0]);
826
- }
827
- }
828
- }
829
-
830
795
  addDiagram = (option: { label: string; value: PackageableElement }): void => {
831
796
  const diagramValue = option.value;
832
797
  const newDiagram = new DataProductDiagram();
@@ -859,33 +824,6 @@ export class ModelAccessPointGroupState extends AccessPointGroupState {
859
824
  modelAccessPointGroup_setElementExclude(element, value);
860
825
  }
861
826
 
862
- override hasErrors(): boolean {
863
- return Boolean(
864
- super.hasErrors() ||
865
- !validate_PureExecutionMapping(this.value.mapping.value) ||
866
- !deepEqual(this.value.defaultRuntime, new DataProductRuntimeInfo()),
867
- );
868
- }
869
-
870
- getCompatibleRuntimeOptions(): {
871
- label: string;
872
- value: PackageableRuntime;
873
- }[] {
874
- const currentRuntimes = this.value.compatibleRuntimes.map(
875
- (runtimePointer) => runtimePointer.runtime.value,
876
- );
877
- return this.state.editorStore.graphManagerState.graph.allOwnElements
878
- .filter(
879
- (element): element is PackageableRuntime =>
880
- element instanceof PackageableRuntime,
881
- )
882
- .filter((runtime) => !currentRuntimes.includes(runtime))
883
- .map((runtime) => ({
884
- label: runtime.path,
885
- value: runtime,
886
- }));
887
- }
888
-
889
827
  isValidDataProductElement(
890
828
  element: PackageableElement,
891
829
  ): element is DataProductElement {
@@ -39,6 +39,10 @@ import {
39
39
  type ExternalDataProductType,
40
40
  observe_Expertise,
41
41
  type Expertise,
42
+ observe_OperationalMetadata,
43
+ DataProductOperationalMetadata,
44
+ type DataProduct_DeliveryFrequency,
45
+ type DataProduct_Region,
42
46
  } from '@finos/legend-graph';
43
47
  import { addUniqueEntry, deleteEntry, swapEntry } from '@finos/legend-shared';
44
48
  import { action } from 'mobx';
@@ -97,12 +101,6 @@ export const accessPointGroup_setTitle = action(
97
101
  },
98
102
  );
99
103
 
100
- export const modelAccessPointGroup_setDefaultRuntime = action(
101
- (group: ModelAccessPointGroup, runtime: DataProductRuntimeInfo) => {
102
- group.defaultRuntime = runtime;
103
- },
104
- );
105
-
106
104
  export const modelAccessPointGroup_setMapping = action(
107
105
  (
108
106
  group: ModelAccessPointGroup,
@@ -112,18 +110,6 @@ export const modelAccessPointGroup_setMapping = action(
112
110
  },
113
111
  );
114
112
 
115
- export const modelAccessPointGroup_addCompatibleRuntime = action(
116
- (group: ModelAccessPointGroup, runtime: DataProductRuntimeInfo) => {
117
- addUniqueEntry(group.compatibleRuntimes, runtime);
118
- },
119
- );
120
-
121
- export const modelAccessPointGroup_removeCompatibleRuntime = action(
122
- (group: ModelAccessPointGroup, runtime: DataProductRuntimeInfo): void => {
123
- deleteEntry(group.compatibleRuntimes, runtime);
124
- },
125
- );
126
-
127
113
  export const modelAccessPointGroup_addElement = action(
128
114
  (group: ModelAccessPointGroup, element: DataProductElementScope): void => {
129
115
  addUniqueEntry(group.featuredElements, element);
@@ -380,3 +366,46 @@ export const supportInfo_deleteEmail = action(
380
366
  }
381
367
  },
382
368
  );
369
+
370
+ export const dataProduct_setOperationalMetadataIfAbsent = action(
371
+ (product: DataProduct) => {
372
+ if (!product.operationalMetadata) {
373
+ product.operationalMetadata = observe_OperationalMetadata(
374
+ new DataProductOperationalMetadata(),
375
+ );
376
+ }
377
+ },
378
+ );
379
+
380
+ export const operationalMetadata_setUpdateFrequency = action(
381
+ (
382
+ operationalMetadata: DataProductOperationalMetadata,
383
+ updateFrequency: DataProduct_DeliveryFrequency,
384
+ ) => {
385
+ operationalMetadata.updateFrequency = updateFrequency;
386
+ },
387
+ );
388
+
389
+ export const operationalMetadata_addCoverageRegion = action(
390
+ (
391
+ operationalMetadata: DataProductOperationalMetadata,
392
+ region: DataProduct_Region,
393
+ ) => {
394
+ if (!operationalMetadata.coverageRegions) {
395
+ operationalMetadata.coverageRegions = [region];
396
+ } else {
397
+ addUniqueEntry(operationalMetadata.coverageRegions, region);
398
+ }
399
+ },
400
+ );
401
+
402
+ export const operationalMetadata_deleteCoverageRegion = action(
403
+ (
404
+ operationalMetadata: DataProductOperationalMetadata,
405
+ region: DataProduct_Region,
406
+ ) => {
407
+ if (operationalMetadata.coverageRegions) {
408
+ deleteEntry(operationalMetadata.coverageRegions, region);
409
+ }
410
+ },
411
+ );