@finos/legend-application-studio 22.5.0 → 22.6.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.
Files changed (38) hide show
  1. package/lib/components/editor/ActivityBar.d.ts.map +1 -1
  2. package/lib/components/editor/ActivityBar.js +6 -1
  3. package/lib/components/editor/ActivityBar.js.map +1 -1
  4. package/lib/components/editor/edit-panel/service-editor/BulkServiceRegistrationEditor.d.ts +20 -0
  5. package/lib/components/editor/edit-panel/service-editor/BulkServiceRegistrationEditor.d.ts.map +1 -0
  6. package/lib/components/editor/edit-panel/service-editor/BulkServiceRegistrationEditor.js +98 -0
  7. package/lib/components/editor/edit-panel/service-editor/BulkServiceRegistrationEditor.js.map +1 -0
  8. package/lib/components/editor/side-bar/RegisterService.d.ts +8 -0
  9. package/lib/components/editor/side-bar/RegisterService.d.ts.map +1 -0
  10. package/lib/components/editor/side-bar/RegisterService.js +35 -0
  11. package/lib/components/editor/side-bar/RegisterService.js.map +1 -0
  12. package/lib/components/editor/side-bar/SideBar.d.ts.map +1 -1
  13. package/lib/components/editor/side-bar/SideBar.js +3 -0
  14. package/lib/components/editor/side-bar/SideBar.js.map +1 -1
  15. package/lib/index.css +2 -2
  16. package/lib/index.css.map +1 -1
  17. package/lib/package.json +1 -1
  18. package/lib/stores/EditorConfig.d.ts +2 -1
  19. package/lib/stores/EditorConfig.d.ts.map +1 -1
  20. package/lib/stores/EditorConfig.js +1 -0
  21. package/lib/stores/EditorConfig.js.map +1 -1
  22. package/lib/stores/EditorStore.d.ts +2 -0
  23. package/lib/stores/EditorStore.d.ts.map +1 -1
  24. package/lib/stores/EditorStore.js +3 -0
  25. package/lib/stores/EditorStore.js.map +1 -1
  26. package/lib/stores/sidebar-state/BulkServiceRegistrationState.d.ts +51 -0
  27. package/lib/stores/sidebar-state/BulkServiceRegistrationState.d.ts.map +1 -0
  28. package/lib/stores/sidebar-state/BulkServiceRegistrationState.js +237 -0
  29. package/lib/stores/sidebar-state/BulkServiceRegistrationState.js.map +1 -0
  30. package/package.json +5 -5
  31. package/src/components/editor/ActivityBar.tsx +6 -0
  32. package/src/components/editor/edit-panel/service-editor/BulkServiceRegistrationEditor.tsx +283 -0
  33. package/src/components/editor/side-bar/RegisterService.tsx +139 -0
  34. package/src/components/editor/side-bar/SideBar.tsx +9 -0
  35. package/src/stores/EditorConfig.ts +1 -0
  36. package/src/stores/EditorStore.ts +6 -0
  37. package/src/stores/sidebar-state/BulkServiceRegistrationState.ts +370 -0
  38. package/tsconfig.json +3 -0
@@ -0,0 +1,139 @@
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
+ import { useState } from 'react';
17
+ import { observer } from 'mobx-react-lite';
18
+ import { LEGEND_STUDIO_TEST_ID } from '../../LegendStudioTestID.js';
19
+ import { useEditorStore } from '../EditorStoreProvider.js';
20
+ import {
21
+ ContextMenu,
22
+ PanelContent,
23
+ PURE_ServiceIcon,
24
+ clsx,
25
+ PlayIcon,
26
+ Dialog,
27
+ Modal,
28
+ TimesIcon,
29
+ EmptyWindowRestoreIcon,
30
+ WindowMaximizeIcon,
31
+ } from '@finos/legend-art';
32
+ import type { BulkServiceRegistrationState } from '../../../stores/sidebar-state/BulkServiceRegistrationState.js';
33
+ import { BulkServiceRegistrationEditor } from '../edit-panel/service-editor/BulkServiceRegistrationEditor.js';
34
+ import { noop } from '@finos/legend-shared';
35
+
36
+ export const RegisterService = observer(
37
+ (props: { bulkServiceRegistrationState: BulkServiceRegistrationState }) => {
38
+ const editorStore = useEditorStore();
39
+ const services = editorStore.graphManagerState.graph.ownServices;
40
+ const [open, setOpen] = useState(false);
41
+
42
+ const [isMaximized, setIsMaximized] = useState(false);
43
+ const toggleMaximize = (): void => setIsMaximized(!isMaximized);
44
+
45
+ const renderTestables = (): React.ReactNode => (
46
+ <>
47
+ {services.map((service) => (
48
+ <ContextMenu key={service._UUID}>
49
+ <div
50
+ className={clsx(
51
+ 'tree-view__node__container global-test-runner__explorer__testable-tree__node__container',
52
+ )}
53
+ >
54
+ <div className="global-test-runner__explorer__testable-tree__node__result__icon__type">
55
+ <PURE_ServiceIcon />
56
+ </div>
57
+ <div className="global-test-runner__item__link__content">
58
+ <span className="global-test-runner__item__link__content__id">
59
+ {service.name}
60
+ </span>
61
+ </div>
62
+ </div>
63
+ </ContextMenu>
64
+ ))}
65
+ </>
66
+ );
67
+
68
+ return (
69
+ <div
70
+ data-testid={LEGEND_STUDIO_TEST_ID.GLOBAL_TEST_RUNNER}
71
+ className="panel global-test-runner"
72
+ >
73
+ <div className="panel__header side-bar__header">
74
+ <div className="panel__header__title global-test-runner__header__title">
75
+ <div className="panel__header__title__content side-bar__header__title__content">
76
+ REGISTER SERVICES
77
+ </div>
78
+ </div>
79
+ <div className="panel__header__actions side-bar__header__actions"></div>
80
+ <button
81
+ className="panel__header__action side-bar__header__action global-test-runner__refresh-btn"
82
+ onClick={() => setOpen(true)}
83
+ tabIndex={-1}
84
+ title="Register All Services"
85
+ >
86
+ <PlayIcon />
87
+ </button>
88
+ <Dialog onClose={noop} open={open}>
89
+ <Modal
90
+ darkMode={true}
91
+ className={clsx('editor-modal query-builder__dialog', {
92
+ 'query-builder__dialog--expanded': isMaximized,
93
+ })}
94
+ >
95
+ <div className="query-builder__dialog__header">
96
+ <div className="query-builder__dialog__header__actions"></div>
97
+ <button
98
+ className="query-builder__dialog__header__action"
99
+ tabIndex={-1}
100
+ onClick={toggleMaximize}
101
+ >
102
+ {isMaximized ? (
103
+ <EmptyWindowRestoreIcon />
104
+ ) : (
105
+ <WindowMaximizeIcon />
106
+ )}
107
+ </button>
108
+ <button
109
+ className="query-builder__dialog__header__action"
110
+ tabIndex={-1}
111
+ onClick={() => setOpen(false)}
112
+ >
113
+ <TimesIcon />
114
+ </button>
115
+ </div>
116
+ <BulkServiceRegistrationEditor />;
117
+ </Modal>
118
+ </Dialog>
119
+ </div>
120
+ <div className="panel__content side-bar__content">
121
+ <div className="panel side-bar__panel">
122
+ <div className="panel__header">
123
+ <div className="panel__header__title">
124
+ <div className="panel__header__title__content">SERVICES</div>
125
+ </div>
126
+ <div
127
+ className="side-bar__panel__header__changes-count"
128
+ data-testid={
129
+ LEGEND_STUDIO_TEST_ID.SIDEBAR_PANEL_HEADER__CHANGES_COUNT
130
+ }
131
+ ></div>
132
+ </div>
133
+ <PanelContent>{renderTestables()}</PanelContent>
134
+ </div>
135
+ </div>
136
+ </div>
137
+ );
138
+ },
139
+ );
@@ -25,6 +25,7 @@ import { ProjectOverview } from './ProjectOverview.js';
25
25
  import { WorkflowManager } from './WorkflowManager.js';
26
26
  import { useEditorStore } from '../EditorStoreProvider.js';
27
27
  import { GlobalTestRunner } from './testable/GlobalTestRunner.js';
28
+ import { RegisterService } from './RegisterService.js';
28
29
 
29
30
  /**
30
31
  * Wrapper component around different implementations of sidebar, such as to view domain, to manage SDLC, etc.
@@ -58,6 +59,14 @@ export const SideBar = observer(() => {
58
59
  globalTestRunnerState={editorStore.globalTestRunnerState}
59
60
  />
60
61
  );
62
+ case ACTIVITY_MODE.REGISTER_SERVICES:
63
+ return (
64
+ <RegisterService
65
+ bulkServiceRegistrationState={
66
+ editorStore.bulkServiceRegistrationState
67
+ }
68
+ />
69
+ );
61
70
  default:
62
71
  return null;
63
72
  }
@@ -36,6 +36,7 @@ export enum ACTIVITY_MODE {
36
36
  PROJECT_OVERVIEW = 'PROJECT_OVERVIEW',
37
37
  WORKFLOW_MANAGER = 'WORKFLOW_MANAGER',
38
38
  GLOBAL_TEST_RUNNER = 'GLOBAL_TEST_RUNNER',
39
+ REGISTER_SERVICES = 'REGISTER_SERVICES',
39
40
  }
40
41
 
41
42
  export enum AUX_PANEL_MODE {
@@ -138,6 +138,7 @@ import { EmbeddedQueryBuilderState } from './EmbeddedQueryBuilderState.js';
138
138
  import { LEGEND_STUDIO_COMMAND_KEY } from './LegendStudioCommand.js';
139
139
  import { EditorTabManagerState } from './EditorTabManagerState.js';
140
140
  import type { ProjectViewerEditorMode } from './project-viewer/ProjectViewerEditorMode.js';
141
+ import { BulkServiceRegistrationState } from './sidebar-state/BulkServiceRegistrationState.js';
141
142
 
142
143
  export abstract class EditorExtensionState {
143
144
  /**
@@ -183,6 +184,7 @@ export class EditorStore implements CommandRegistrar {
183
184
  devToolState: DevToolState;
184
185
  embeddedQueryBuilderState: EmbeddedQueryBuilderState;
185
186
  newElementState: NewElementState;
187
+ bulkServiceRegistrationState: BulkServiceRegistrationState;
186
188
  /**
187
189
  * Since we want to share element generation state across all element in the editor, we will create 1 element generate state
188
190
  * per file generation configuration type.
@@ -281,6 +283,10 @@ export class EditorStore implements CommandRegistrar {
281
283
  this.sdlcState,
282
284
  );
283
285
  this.newElementState = new NewElementState(this);
286
+ this.bulkServiceRegistrationState = new BulkServiceRegistrationState(
287
+ this,
288
+ this.sdlcState,
289
+ );
284
290
  // special (singleton) editors
285
291
  this.grammarTextEditorState = new GrammarTextEditorState(this);
286
292
  this.modelImporterState = new ModelImporterState(this);
@@ -0,0 +1,370 @@
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
+ import { action, computed, makeObservable, observable, flow } from 'mobx';
17
+ import type { EditorSDLCState } from '../EditorSDLCState.js';
18
+ import type { EditorStore } from '../EditorStore.js';
19
+ import {
20
+ type PureExecution,
21
+ areMultiplicitiesEqual,
22
+ buildLambdaVariableExpressions,
23
+ generateMultiplicityString,
24
+ Multiplicity,
25
+ VariableExpression,
26
+ ServiceExecutionMode,
27
+ type BulkRegistrationResultFail,
28
+ BulkRegistrationResultSuccess,
29
+ type BulkServiceRegistrationResult,
30
+ } from '@finos/legend-graph';
31
+ import {
32
+ type GeneratorFn,
33
+ ActionState,
34
+ assertNonEmptyString,
35
+ assertTrue,
36
+ filterByType,
37
+ getNullableFirstElement,
38
+ guaranteeNonNullable,
39
+ prettyCONSTName,
40
+ UnsupportedOperationError,
41
+ assertErrorThrown,
42
+ LogEvent,
43
+ } from '@finos/legend-shared';
44
+ import { Version } from '@finos/legend-server-sdlc';
45
+ import { MINIMUM_SERVICE_OWNERS } from '../editor-state/element-editor-state/service/ServiceEditorState.js';
46
+ import { ServiceRegistrationEnvironmentConfig } from '../../application/LegendStudioApplicationConfig.js';
47
+ import { LEGEND_STUDIO_APP_EVENT } from '../LegendStudioAppEvent.js';
48
+ import { generateServiceManagementUrl } from '../editor-state/element-editor-state/service/ServiceRegistrationState.js';
49
+
50
+ export const LATEST_PROJECT_REVISION = 'Latest Project Revision';
51
+ const getServiceExecutionMode = (mode: string): ServiceExecutionMode => {
52
+ switch (mode) {
53
+ case ServiceExecutionMode.FULL_INTERACTIVE:
54
+ return ServiceExecutionMode.FULL_INTERACTIVE;
55
+ case ServiceExecutionMode.SEMI_INTERACTIVE:
56
+ return ServiceExecutionMode.SEMI_INTERACTIVE;
57
+ case ServiceExecutionMode.PROD:
58
+ return ServiceExecutionMode.PROD;
59
+ default:
60
+ throw new UnsupportedOperationError(
61
+ `Encountered unsupported service execution mode '${mode}'`,
62
+ );
63
+ }
64
+ };
65
+ interface ServiceVersionOption {
66
+ label: string;
67
+ value: Version | string;
68
+ }
69
+ interface ServiceRegistrationResult {
70
+ successfulServices: string[];
71
+ failedServices: string[];
72
+ serviceLinks: string[];
73
+ }
74
+ export class ServiceConfigState {
75
+ readonly editorStore: EditorStore;
76
+ readonly registrationOptions: ServiceRegistrationEnvironmentConfig[] = [];
77
+ readonly registrationState = ActionState.create();
78
+ registrationResult: ServiceRegistrationResult | undefined;
79
+ serviceEnv?: string | undefined;
80
+ serviceExecutionMode?: ServiceExecutionMode | undefined;
81
+ projectVersion?: Version | string | undefined;
82
+ enableModesWithVersioning: boolean;
83
+ TEMPORARY__useStoreModel = false;
84
+
85
+ constructor(
86
+ editorStore: EditorStore,
87
+ registrationOptions: ServiceRegistrationEnvironmentConfig[],
88
+ enableModesWithVersioning: boolean,
89
+ ) {
90
+ makeObservable(this, {
91
+ serviceEnv: observable,
92
+ serviceExecutionMode: observable,
93
+ projectVersion: observable,
94
+ enableModesWithVersioning: observable,
95
+ TEMPORARY__useStoreModel: observable,
96
+ executionModes: computed,
97
+ options: computed,
98
+ versionOptions: computed,
99
+ setServiceEnv: action,
100
+ setServiceExecutionMode: action,
101
+ setProjectVersion: action,
102
+
103
+ setUseStoreModelWithFullInteractive: action,
104
+ initialize: action,
105
+
106
+ updateVersion: action,
107
+ updateType: action,
108
+ updateEnv: action,
109
+ });
110
+
111
+ this.editorStore = editorStore;
112
+ this.registrationOptions = registrationOptions;
113
+ this.enableModesWithVersioning = enableModesWithVersioning;
114
+ this.initialize();
115
+ this.registrationState.setMessageFormatter(prettyCONSTName);
116
+ }
117
+
118
+ get options(): ServiceRegistrationEnvironmentConfig[] {
119
+ if (this.enableModesWithVersioning) {
120
+ return this.registrationOptions;
121
+ }
122
+ return this.registrationOptions
123
+ .map((_envConfig) => {
124
+ const envConfig = new ServiceRegistrationEnvironmentConfig();
125
+ envConfig.env = _envConfig.env;
126
+ envConfig.executionUrl = _envConfig.executionUrl;
127
+ envConfig.managementUrl = _envConfig.managementUrl;
128
+ // NOTE: For projects that we cannot create a version for, only fully-interactive mode is supported
129
+ envConfig.modes = _envConfig.modes.filter(
130
+ (mode) => mode === ServiceExecutionMode.FULL_INTERACTIVE,
131
+ );
132
+ return envConfig;
133
+ })
134
+ .filter((envConfig) => envConfig.modes.length);
135
+ }
136
+
137
+ get executionModes(): ServiceExecutionMode[] {
138
+ return (
139
+ this.options.find((e) => e.env === this.serviceEnv)?.modes ?? []
140
+ ).map(getServiceExecutionMode);
141
+ }
142
+
143
+ get versionOptions(): ServiceVersionOption[] | undefined {
144
+ if (
145
+ this.enableModesWithVersioning &&
146
+ this.serviceExecutionMode !== ServiceExecutionMode.FULL_INTERACTIVE
147
+ ) {
148
+ const options: ServiceVersionOption[] =
149
+ this.editorStore.sdlcState.projectVersions.map((version) => ({
150
+ label: version.id.id,
151
+ value: version,
152
+ }));
153
+ if (this.serviceExecutionMode !== ServiceExecutionMode.PROD) {
154
+ return [
155
+ {
156
+ label: prettyCONSTName(LATEST_PROJECT_REVISION),
157
+ value: LATEST_PROJECT_REVISION,
158
+ },
159
+ ...options,
160
+ ];
161
+ }
162
+ return options;
163
+ }
164
+ return undefined;
165
+ }
166
+
167
+ setServiceEnv(val: string | undefined): void {
168
+ this.serviceEnv = val;
169
+ }
170
+
171
+ setServiceExecutionMode(val: ServiceExecutionMode | undefined): void {
172
+ this.serviceExecutionMode = val;
173
+ }
174
+
175
+ setProjectVersion(val: Version | string | undefined): void {
176
+ this.projectVersion = val;
177
+ }
178
+
179
+ setUseStoreModelWithFullInteractive(val: boolean): void {
180
+ this.TEMPORARY__useStoreModel = val;
181
+ }
182
+
183
+ initialize(): void {
184
+ this.serviceEnv = getNullableFirstElement(this.registrationOptions)?.env;
185
+ this.serviceExecutionMode = this.executionModes[0];
186
+ this.updateVersion();
187
+ }
188
+
189
+ updateVersion(): void {
190
+ if (this.serviceExecutionMode === ServiceExecutionMode.SEMI_INTERACTIVE) {
191
+ this.projectVersion = LATEST_PROJECT_REVISION;
192
+ } else if (this.serviceExecutionMode === ServiceExecutionMode.PROD) {
193
+ this.projectVersion = this.editorStore.sdlcState.projectVersions[0];
194
+ } else {
195
+ this.projectVersion = undefined;
196
+ }
197
+ }
198
+
199
+ updateType(val: ServiceExecutionMode | undefined): void {
200
+ this.setServiceExecutionMode(val);
201
+ this.updateVersion();
202
+ }
203
+
204
+ updateEnv(val: string | undefined): void {
205
+ this.setServiceEnv(val);
206
+ this.setServiceExecutionMode(this.executionModes[0]);
207
+ }
208
+ }
209
+
210
+ export class BulkServiceRegistrationState {
211
+ editorStore: EditorStore;
212
+ sdlcState: EditorSDLCState;
213
+ serviceConfigState: ServiceConfigState;
214
+ showSuccessModel = false;
215
+
216
+ constructor(editorStore: EditorStore, sdlcState: EditorSDLCState) {
217
+ makeObservable(this, {
218
+ showSuccessModel: observable,
219
+ editorStore: false,
220
+ sdlcState: false,
221
+ registerServices: flow,
222
+ setSuccessModal: action,
223
+ });
224
+ this.editorStore = editorStore;
225
+ this.sdlcState = sdlcState;
226
+ this.serviceConfigState = new ServiceConfigState(
227
+ editorStore,
228
+ editorStore.applicationStore.config.options.TEMPORARY__serviceRegistrationConfig,
229
+ editorStore.sdlcServerClient.featuresConfigHasBeenFetched &&
230
+ editorStore.sdlcServerClient.features.canCreateVersion,
231
+ );
232
+ }
233
+ setSuccessModal(val: boolean): void {
234
+ this.showSuccessModel = val;
235
+ }
236
+ *registerServices(): GeneratorFn<void> {
237
+ const successfulServices: string[] = [];
238
+ const failedServices: string[] = [];
239
+ const serviceManagementURL: string[] = [];
240
+
241
+ this.serviceConfigState.registrationState.inProgress();
242
+ this.validateServiceForRegistration();
243
+ try {
244
+ const projectConfig = guaranteeNonNullable(
245
+ this.editorStore.projectConfigurationEditorState.projectConfiguration,
246
+ );
247
+
248
+ const versionInput =
249
+ this.serviceConfigState.projectVersion instanceof Version
250
+ ? this.serviceConfigState.projectVersion.id.id
251
+ : undefined;
252
+
253
+ const config = guaranteeNonNullable(
254
+ this.serviceConfigState.options.find(
255
+ (info) => info.env === this.serviceConfigState.serviceEnv,
256
+ ),
257
+ );
258
+ const serviceRegistrationResult =
259
+ (yield this.editorStore.graphManagerState.graphManager.bulkServiceRegistration(
260
+ this.editorStore.graphManagerState.graph.ownServices,
261
+ this.editorStore.graphManagerState.graph,
262
+ projectConfig.groupId,
263
+ projectConfig.artifactId,
264
+ versionInput,
265
+ config.executionUrl,
266
+ guaranteeNonNullable(this.serviceConfigState.serviceExecutionMode),
267
+ {
268
+ TEMPORARY__useStoreModel:
269
+ this.serviceConfigState.TEMPORARY__useStoreModel,
270
+ },
271
+ )) as BulkServiceRegistrationResult[];
272
+
273
+ serviceRegistrationResult.forEach((result) => {
274
+ if (result instanceof BulkRegistrationResultSuccess) {
275
+ const serviceURL = generateServiceManagementUrl(
276
+ config.managementUrl,
277
+ result.pattern,
278
+ );
279
+ serviceManagementURL.push(serviceURL);
280
+ successfulServices.push(result.pattern);
281
+ } else {
282
+ failedServices.push(
283
+ `${result.servicePath} ERROR: ${
284
+ (result as BulkRegistrationResultFail).errorMessage
285
+ }`,
286
+ );
287
+ }
288
+ });
289
+ this.serviceConfigState.registrationResult = {
290
+ successfulServices: successfulServices,
291
+ serviceLinks: serviceManagementURL,
292
+ failedServices: failedServices,
293
+ };
294
+ this.showSuccessModel = true;
295
+ } catch (error) {
296
+ assertErrorThrown(error);
297
+ this.editorStore.applicationStore.logService.error(
298
+ LogEvent.create(LEGEND_STUDIO_APP_EVENT.SERVICE_REGISTRATION_FAILURE),
299
+ error,
300
+ );
301
+ this.editorStore.applicationStore.notificationService.notifyError(error);
302
+ } finally {
303
+ this.serviceConfigState.registrationState.reset();
304
+ this.serviceConfigState.registrationState.setMessage(undefined);
305
+ }
306
+ }
307
+
308
+ validateServiceForRegistration(): void {
309
+ const services = this.editorStore.graphManagerState.graph.ownServices;
310
+
311
+ services.forEach((service) => {
312
+ service.owners.forEach((owner) =>
313
+ assertNonEmptyString(owner, `Service can't have an empty owner name`),
314
+ );
315
+ assertTrue(
316
+ service.owners.length >= MINIMUM_SERVICE_OWNERS,
317
+ `Service needs to have at least 2 owners in order to be registered`,
318
+ );
319
+ guaranteeNonNullable(
320
+ this.serviceConfigState.serviceEnv,
321
+ 'Service registration environment can not be empty',
322
+ );
323
+ guaranteeNonNullable(
324
+ this.serviceConfigState.serviceExecutionMode,
325
+ 'Service type can not be empty',
326
+ );
327
+ if (
328
+ this.serviceConfigState.serviceExecutionMode ===
329
+ ServiceExecutionMode.PROD ||
330
+ this.serviceConfigState.serviceExecutionMode ===
331
+ ServiceExecutionMode.SEMI_INTERACTIVE
332
+ ) {
333
+ guaranteeNonNullable(
334
+ this.serviceConfigState.projectVersion,
335
+ 'Service version can not be empty in Semi-interactive and Prod service type',
336
+ );
337
+ }
338
+
339
+ // validate service parameter multiplicities
340
+ const SUPPORTED_SERVICE_PARAMETER_MULTIPLICITIES = [
341
+ Multiplicity.ONE,
342
+ Multiplicity.ZERO_MANY,
343
+ Multiplicity.ZERO_ONE,
344
+ ];
345
+ const invalidParams = buildLambdaVariableExpressions(
346
+ (service.execution as PureExecution).func,
347
+ this.editorStore.graphManagerState,
348
+ )
349
+ .filter(filterByType(VariableExpression))
350
+ .filter(
351
+ (p) =>
352
+ !SUPPORTED_SERVICE_PARAMETER_MULTIPLICITIES.some((m) =>
353
+ areMultiplicitiesEqual(m, p.multiplicity),
354
+ ),
355
+ );
356
+ assertTrue(
357
+ invalidParams.length === 0,
358
+ `Parameter(s)${invalidParams.map(
359
+ (p) =>
360
+ ` ${p.name}: [${generateMultiplicityString(
361
+ p.multiplicity.lowerBound,
362
+ p.multiplicity.upperBound,
363
+ )}]`,
364
+ )} has/have unsupported multiplicity. Supported multiplicities include ${SUPPORTED_SERVICE_PARAMETER_MULTIPLICITIES.map(
365
+ (m) => ` [${generateMultiplicityString(m.lowerBound, m.upperBound)}]`,
366
+ )}.`,
367
+ );
368
+ });
369
+ }
370
+ }
package/tsconfig.json CHANGED
@@ -143,6 +143,7 @@
143
143
  "./src/stores/shared/modifier/STO_Relational_GraphModifierHelper.ts",
144
144
  "./src/stores/shared/modifier/Testable_GraphModifierHelper.ts",
145
145
  "./src/stores/shared/testable/TestableUtils.ts",
146
+ "./src/stores/sidebar-state/BulkServiceRegistrationState.ts",
146
147
  "./src/stores/sidebar-state/LocalChangesState.ts",
147
148
  "./src/stores/sidebar-state/ProjectDependantEditorState.ts",
148
149
  "./src/stores/sidebar-state/ProjectOverviewState.ts",
@@ -221,6 +222,7 @@
221
222
  "./src/components/editor/edit-panel/mapping-editor/relational/TableOrViewSourceTree.tsx",
222
223
  "./src/components/editor/edit-panel/project-configuration-editor/ProjectConfigurationEditor.tsx",
223
224
  "./src/components/editor/edit-panel/project-configuration-editor/ProjectDependencyEditor.tsx",
225
+ "./src/components/editor/edit-panel/service-editor/BulkServiceRegistrationEditor.tsx",
224
226
  "./src/components/editor/edit-panel/service-editor/NewServiceModal.tsx",
225
227
  "./src/components/editor/edit-panel/service-editor/ServiceEditor.tsx",
226
228
  "./src/components/editor/edit-panel/service-editor/ServiceExecutionEditor.tsx",
@@ -244,6 +246,7 @@
244
246
  "./src/components/editor/side-bar/LocalChanges.tsx",
245
247
  "./src/components/editor/side-bar/ProjectDependantsEditor.tsx",
246
248
  "./src/components/editor/side-bar/ProjectOverview.tsx",
249
+ "./src/components/editor/side-bar/RegisterService.tsx",
247
250
  "./src/components/editor/side-bar/SideBar.tsx",
248
251
  "./src/components/editor/side-bar/WorkflowManager.tsx",
249
252
  "./src/components/editor/side-bar/WorkspaceReview.tsx",