@finos/legend-extension-dsl-service 0.0.1

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 (68) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +3 -0
  3. package/lib/components/studio/DSL_Service_LegendStudioApplicationPlugin.d.ts +22 -0
  4. package/lib/components/studio/DSL_Service_LegendStudioApplicationPlugin.d.ts.map +1 -0
  5. package/lib/components/studio/DSL_Service_LegendStudioApplicationPlugin.js +59 -0
  6. package/lib/components/studio/DSL_Service_LegendStudioApplicationPlugin.js.map +1 -0
  7. package/lib/components/studio/ServiceQueryEditor.d.ts +25 -0
  8. package/lib/components/studio/ServiceQueryEditor.d.ts.map +1 -0
  9. package/lib/components/studio/ServiceQueryEditor.js +190 -0
  10. package/lib/components/studio/ServiceQueryEditor.js.map +1 -0
  11. package/lib/components/studio/ServiceQueryEditorReviewAction.d.ts +19 -0
  12. package/lib/components/studio/ServiceQueryEditorReviewAction.d.ts.map +1 -0
  13. package/lib/components/studio/ServiceQueryEditorReviewAction.js +71 -0
  14. package/lib/components/studio/ServiceQueryEditorReviewAction.js.map +1 -0
  15. package/lib/components/studio/ServiceQueryEditorStoreProvider.d.ts +31 -0
  16. package/lib/components/studio/ServiceQueryEditorStoreProvider.d.ts.map +1 -0
  17. package/lib/components/studio/ServiceQueryEditorStoreProvider.js +40 -0
  18. package/lib/components/studio/ServiceQueryEditorStoreProvider.js.map +1 -0
  19. package/lib/components/studio/ServiceQueryEditorWorkspaceStatus.d.ts +19 -0
  20. package/lib/components/studio/ServiceQueryEditorWorkspaceStatus.d.ts.map +1 -0
  21. package/lib/components/studio/ServiceQueryEditorWorkspaceStatus.js +69 -0
  22. package/lib/components/studio/ServiceQueryEditorWorkspaceStatus.js.map +1 -0
  23. package/lib/components/studio/UpdateProjectServiceQuerySetup.d.ts +18 -0
  24. package/lib/components/studio/UpdateProjectServiceQuerySetup.d.ts.map +1 -0
  25. package/lib/components/studio/UpdateProjectServiceQuerySetup.js +182 -0
  26. package/lib/components/studio/UpdateProjectServiceQuerySetup.js.map +1 -0
  27. package/lib/components/studio/UpdateServiceQuerySetup.d.ts +18 -0
  28. package/lib/components/studio/UpdateServiceQuerySetup.d.ts.map +1 -0
  29. package/lib/components/studio/UpdateServiceQuerySetup.js +172 -0
  30. package/lib/components/studio/UpdateServiceQuerySetup.js.map +1 -0
  31. package/lib/index.css +17 -0
  32. package/lib/index.css.map +1 -0
  33. package/lib/index.d.ts +17 -0
  34. package/lib/index.d.ts.map +1 -0
  35. package/lib/index.js +17 -0
  36. package/lib/index.js.map +1 -0
  37. package/lib/package.json +84 -0
  38. package/lib/stores/studio/DSL_Service_LegendStudioRouter.d.ts +53 -0
  39. package/lib/stores/studio/DSL_Service_LegendStudioRouter.d.ts.map +1 -0
  40. package/lib/stores/studio/DSL_Service_LegendStudioRouter.js +62 -0
  41. package/lib/stores/studio/DSL_Service_LegendStudioRouter.js.map +1 -0
  42. package/lib/stores/studio/ServiceQueryEditorStore.d.ts +64 -0
  43. package/lib/stores/studio/ServiceQueryEditorStore.d.ts.map +1 -0
  44. package/lib/stores/studio/ServiceQueryEditorStore.js +260 -0
  45. package/lib/stores/studio/ServiceQueryEditorStore.js.map +1 -0
  46. package/lib/stores/studio/UpdateProjectServiceQuerySetupStore.d.ts +46 -0
  47. package/lib/stores/studio/UpdateProjectServiceQuerySetupStore.d.ts.map +1 -0
  48. package/lib/stores/studio/UpdateProjectServiceQuerySetupStore.js +184 -0
  49. package/lib/stores/studio/UpdateProjectServiceQuerySetupStore.js.map +1 -0
  50. package/lib/stores/studio/UpdateServiceQuerySetupStore.d.ts +48 -0
  51. package/lib/stores/studio/UpdateServiceQuerySetupStore.d.ts.map +1 -0
  52. package/lib/stores/studio/UpdateServiceQuerySetupStore.js +184 -0
  53. package/lib/stores/studio/UpdateServiceQuerySetupStore.js.map +1 -0
  54. package/package.json +84 -0
  55. package/src/components/studio/DSL_Service_LegendStudioApplicationPlugin.tsx +71 -0
  56. package/src/components/studio/ServiceQueryEditor.tsx +551 -0
  57. package/src/components/studio/ServiceQueryEditorReviewAction.tsx +172 -0
  58. package/src/components/studio/ServiceQueryEditorStoreProvider.tsx +89 -0
  59. package/src/components/studio/ServiceQueryEditorWorkspaceStatus.tsx +121 -0
  60. package/src/components/studio/UpdateProjectServiceQuerySetup.tsx +479 -0
  61. package/src/components/studio/UpdateServiceQuerySetup.tsx +476 -0
  62. package/src/index.ts +17 -0
  63. package/src/stores/studio/DSL_Service_LegendStudioRouter.ts +159 -0
  64. package/src/stores/studio/ServiceQueryEditorStore.ts +487 -0
  65. package/src/stores/studio/UpdateProjectServiceQuerySetupStore.ts +281 -0
  66. package/src/stores/studio/UpdateServiceQuerySetupStore.ts +314 -0
  67. package/tsconfig.json +58 -0
  68. package/tsconfig.package.json +38 -0
@@ -0,0 +1,487 @@
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 {
18
+ ActionAlertActionType,
19
+ ActionAlertType,
20
+ } from '@finos/legend-application';
21
+ import {
22
+ type LegendStudioApplicationStore,
23
+ type ServiceRegistrationEnvironmentConfig,
24
+ EditorStore,
25
+ LEGEND_STUDIO_APP_EVENT,
26
+ MINIMUM_SERVICE_OWNERS,
27
+ generateServiceManagementUrl,
28
+ pureExecution_setFunction,
29
+ } from '@finos/legend-application-studio';
30
+ import {
31
+ type Service,
32
+ type ServiceRegistrationResult,
33
+ GraphManagerState,
34
+ PureExecution,
35
+ CORE_PURE_PATH,
36
+ ServiceExecutionMode,
37
+ } from '@finos/legend-graph';
38
+ import {
39
+ type QueryBuilderState,
40
+ ServiceQueryBuilderState,
41
+ } from '@finos/legend-query-builder';
42
+ import {
43
+ type DepotServerClient,
44
+ ProjectData,
45
+ } from '@finos/legend-server-depot';
46
+ import {
47
+ type SDLCServerClient,
48
+ type Revision,
49
+ WorkspaceType,
50
+ EntityChangeType,
51
+ } from '@finos/legend-server-sdlc';
52
+ import {
53
+ type GeneratorFn,
54
+ type PlainObject,
55
+ assertErrorThrown,
56
+ assertType,
57
+ LogEvent,
58
+ HttpStatus,
59
+ NetworkClientError,
60
+ assertNonNullable,
61
+ ActionState,
62
+ assertTrue,
63
+ assertNonEmptyString,
64
+ guaranteeType,
65
+ guaranteeNonNullable,
66
+ } from '@finos/legend-shared';
67
+ import type { Entity } from '@finos/legend-storage';
68
+ import {
69
+ action,
70
+ computed,
71
+ flow,
72
+ flowResult,
73
+ makeObservable,
74
+ observable,
75
+ } from 'mobx';
76
+ import { parseServiceCoordinates } from './DSL_Service_LegendStudioRouter.js';
77
+
78
+ type ProjectServiceCoordinates = {
79
+ projectId: string;
80
+ groupWorkspaceId: string;
81
+ servicePath: string;
82
+ };
83
+
84
+ export abstract class ServiceQueryEditorStore extends EditorStore {
85
+ queryBuilderState?: QueryBuilderState | undefined;
86
+ _service?: Service | undefined;
87
+ showNewServiceModal = false;
88
+
89
+ showServiceRegistrationModal = false;
90
+ registerServiceState = ActionState.create();
91
+ readonly serviceRegistrationEnvConfigs: ServiceRegistrationEnvironmentConfig[] =
92
+ [];
93
+ currentServiceRegistrationEnvConfig?:
94
+ | ServiceRegistrationEnvironmentConfig
95
+ | undefined;
96
+
97
+ showSubmitReviewModal = false;
98
+
99
+ constructor(
100
+ applicationStore: LegendStudioApplicationStore,
101
+ sdlcServerClient: SDLCServerClient,
102
+ depotServerClient: DepotServerClient,
103
+ ) {
104
+ const graphManagerState = new GraphManagerState(
105
+ applicationStore.pluginManager,
106
+ applicationStore.log,
107
+ );
108
+ super(
109
+ applicationStore,
110
+ sdlcServerClient,
111
+ depotServerClient,
112
+ graphManagerState,
113
+ );
114
+
115
+ makeObservable(this, {
116
+ queryBuilderState: observable,
117
+ _service: observable,
118
+ service: computed,
119
+ showNewServiceModal: observable,
120
+ showServiceRegistrationModal: observable,
121
+ currentServiceRegistrationEnvConfig: observable,
122
+ showSubmitReviewModal: observable,
123
+ setCurrentServiceRegistrationEnvConfig: action,
124
+ setShowNewServiceModal: action,
125
+ setShowServiceRegistrationModal: action,
126
+ setShowSubmitReviewModal: observable,
127
+ initializeWithServiceQuery: flow,
128
+ saveWorkspace: flow,
129
+ recreateWorkspace: flow,
130
+ registerService: flow,
131
+ });
132
+
133
+ this.serviceRegistrationEnvConfigs =
134
+ this.applicationStore.config.options.TEMPORARY__serviceRegistrationConfig.filter(
135
+ (config) =>
136
+ config.modes.includes(ServiceExecutionMode.SEMI_INTERACTIVE),
137
+ );
138
+ if (this.serviceRegistrationEnvConfigs.length) {
139
+ this.currentServiceRegistrationEnvConfig =
140
+ this.serviceRegistrationEnvConfigs[0];
141
+ }
142
+ }
143
+
144
+ get service(): Service {
145
+ return guaranteeNonNullable(
146
+ this._service,
147
+ `Service query editor store has not been initialized properly`,
148
+ );
149
+ }
150
+
151
+ setShowNewServiceModal(val: boolean): void {
152
+ this.showNewServiceModal = val;
153
+ }
154
+
155
+ setShowServiceRegistrationModal(val: boolean): void {
156
+ this.showServiceRegistrationModal = val;
157
+ }
158
+
159
+ setCurrentServiceRegistrationEnvConfig(
160
+ val: ServiceRegistrationEnvironmentConfig | undefined,
161
+ ): void {
162
+ this.currentServiceRegistrationEnvConfig = val;
163
+ }
164
+
165
+ setShowSubmitReviewModal(val: boolean): void {
166
+ this.showSubmitReviewModal = val;
167
+ }
168
+
169
+ abstract fetchServiceInformation(): Promise<ProjectServiceCoordinates>;
170
+
171
+ *initializeWithServiceQuery(): GeneratorFn<void> {
172
+ try {
173
+ const serviceInfo =
174
+ (yield this.fetchServiceInformation()) as ProjectServiceCoordinates;
175
+
176
+ yield flowResult(
177
+ this.initialize(
178
+ serviceInfo.projectId,
179
+ serviceInfo.groupWorkspaceId,
180
+ WorkspaceType.GROUP,
181
+ ),
182
+ );
183
+
184
+ // initialize the query builder state
185
+ this._service = this.graphManagerState.graph.getService(
186
+ serviceInfo.servicePath,
187
+ );
188
+
189
+ assertType(
190
+ this.service.execution,
191
+ PureExecution,
192
+ `Can't process service execution: only Pure execution is supported`,
193
+ );
194
+
195
+ const queryBuilderState = new ServiceQueryBuilderState(
196
+ this.applicationStore,
197
+ this.graphManagerState,
198
+ this.service,
199
+ undefined,
200
+ );
201
+
202
+ // leverage initialization of query builder state to ensure we handle unsupported queries
203
+ queryBuilderState.initializeWithQuery(this.service.execution.func),
204
+ (this.queryBuilderState = queryBuilderState);
205
+ } catch (error) {
206
+ assertErrorThrown(error);
207
+ this.applicationStore.log.error(
208
+ LogEvent.create(LEGEND_STUDIO_APP_EVENT.WORKSPACE_SETUP_FAILURE),
209
+ error,
210
+ );
211
+ this.applicationStore.notifyError(error);
212
+ }
213
+ }
214
+
215
+ *saveWorkspace(
216
+ serviceEntity: Entity,
217
+ createNew: boolean,
218
+ onSuccess?: () => void,
219
+ ): GeneratorFn<void> {
220
+ if (
221
+ !this.queryBuilderState ||
222
+ this.localChangesState.pushChangesState.isInProgress ||
223
+ this.workspaceUpdaterState.isUpdatingWorkspace
224
+ ) {
225
+ return;
226
+ }
227
+
228
+ this.localChangesState.pushChangesState.inProgress();
229
+ this.applicationStore.setBlockingAlert({
230
+ message: `Saving workspace...`,
231
+ prompt: 'Please do not close the application',
232
+ showLoading: true,
233
+ });
234
+
235
+ try {
236
+ yield flowResult(
237
+ this.sdlcState.fetchRemoteWorkspaceRevision(
238
+ this.sdlcState.activeProject.projectId,
239
+ this.sdlcState.activeWorkspace,
240
+ ),
241
+ );
242
+ if (this.sdlcState.isWorkspaceOutOfSync) {
243
+ return;
244
+ }
245
+
246
+ // push the changes
247
+ // NOTE: change detection in query-builder should be enough
248
+ // to make sure that this change is valid
249
+ const entityChange = {
250
+ classifierPath: CORE_PURE_PATH.SERVICE,
251
+ entityPath: serviceEntity.path,
252
+ content: serviceEntity.content,
253
+ type: createNew ? EntityChangeType.CREATE : EntityChangeType.MODIFY,
254
+ };
255
+
256
+ const nullableRevisionChange =
257
+ (yield this.sdlcServerClient.performEntityChanges(
258
+ this.sdlcState.activeProject.projectId,
259
+ this.sdlcState.activeWorkspace,
260
+ {
261
+ message: `updated service ${serviceEntity.path} query`,
262
+ entityChanges: [entityChange],
263
+ revisionId: this.sdlcState.activeRevision.id,
264
+ },
265
+ )) as PlainObject<Revision> | undefined;
266
+ assertNonNullable(
267
+ nullableRevisionChange,
268
+ `Can't save workspace: empty change set was pushed - this may be due to an error with change detection`,
269
+ );
270
+
271
+ this.applicationStore.log.info(
272
+ LogEvent.create(LEGEND_STUDIO_APP_EVENT.WORKSPACE_LOCAL_CHANGES_PUSHED),
273
+ );
274
+
275
+ onSuccess?.();
276
+ } catch (error) {
277
+ assertErrorThrown(error);
278
+ this.applicationStore.log.error(
279
+ LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE),
280
+ error,
281
+ );
282
+ if (
283
+ error instanceof NetworkClientError &&
284
+ error.response.status === HttpStatus.CONFLICT
285
+ ) {
286
+ // NOTE: a confict here indicates that the reference revision ID sent along with update call
287
+ // does not match the HEAD of the workspace, therefore, we need to prompt user to refresh the application
288
+ this.applicationStore.notifyWarning(
289
+ `Can't save workspace: current workspace revision is not the latest; please backup your work and refresh the application`,
290
+ );
291
+ } else {
292
+ this.applicationStore.notifyError(error);
293
+ }
294
+ } finally {
295
+ this.applicationStore.setBlockingAlert(undefined);
296
+ this.localChangesState.pushChangesState.complete();
297
+ }
298
+ }
299
+
300
+ *recreateWorkspace(): GeneratorFn<void> {
301
+ try {
302
+ this.setBlockingAlert({
303
+ message: 'Re-creating workspace...',
304
+ prompt: 'Please do not close the application',
305
+ showLoading: true,
306
+ });
307
+ yield this.sdlcServerClient.deleteWorkspace(
308
+ this.sdlcState.activeProject.projectId,
309
+ this.sdlcState.activeWorkspace,
310
+ );
311
+ yield this.sdlcServerClient.createWorkspace(
312
+ this.sdlcState.activeProject.projectId,
313
+ this.sdlcState.activeWorkspace.workspaceId,
314
+ this.sdlcState.activeWorkspace.workspaceType,
315
+ );
316
+ this.applicationStore.navigator.reload();
317
+ } catch (error) {
318
+ assertErrorThrown(error);
319
+ this.applicationStore.log.error(
320
+ LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE),
321
+ error,
322
+ );
323
+ this.applicationStore.notifyError(error);
324
+ } finally {
325
+ this.setBlockingAlert(undefined);
326
+ }
327
+ }
328
+
329
+ updateServiceQuery(): void {
330
+ assertNonNullable(
331
+ this.queryBuilderState,
332
+ `Service query editor store has not been initialized properly`,
333
+ );
334
+ pureExecution_setFunction(
335
+ guaranteeType(this.service.execution, PureExecution),
336
+ this.queryBuilderState.buildQuery(),
337
+ );
338
+ }
339
+
340
+ *registerService(overridePattern?: string | undefined): GeneratorFn<void> {
341
+ const registrationConfig = this.currentServiceRegistrationEnvConfig;
342
+ if (registrationConfig) {
343
+ try {
344
+ this.registerServiceState.inProgress();
345
+
346
+ // make sure the service query is updated
347
+ this.updateServiceQuery();
348
+
349
+ // validate owners
350
+ this.service.owners.forEach((owner) =>
351
+ assertNonEmptyString(owner, `Service can't have an empty owner name`),
352
+ );
353
+ assertTrue(
354
+ this.service.owners.length >= MINIMUM_SERVICE_OWNERS,
355
+ `Service needs to have at least 2 owners in order to be registered`,
356
+ );
357
+
358
+ this.registerServiceState.setMessage(`Registering service...`);
359
+ const serviceRegistrationResult =
360
+ (yield this.graphManagerState.graphManager.registerService(
361
+ this.service,
362
+ this.graphManagerState.graph,
363
+ this.projectConfigurationEditorState.currentProjectConfiguration
364
+ .groupId,
365
+ this.projectConfigurationEditorState.currentProjectConfiguration
366
+ .artifactId,
367
+ undefined, // if not specified, we will use the latest revision version (i.e. SNAPSHOT/HEAD)
368
+ registrationConfig.executionUrl,
369
+ ServiceExecutionMode.SEMI_INTERACTIVE,
370
+ {
371
+ TEMPORARY__semiInteractiveOverridePattern: overridePattern,
372
+ },
373
+ )) as ServiceRegistrationResult;
374
+
375
+ this.registerServiceState.setMessage(`Activating service...`);
376
+ yield this.graphManagerState.graphManager.activateService(
377
+ registrationConfig.executionUrl,
378
+ serviceRegistrationResult.serviceInstanceId,
379
+ );
380
+ this.setShowServiceRegistrationModal(false);
381
+
382
+ assertNonEmptyString(
383
+ serviceRegistrationResult.pattern,
384
+ 'Service registration pattern is missing or empty',
385
+ );
386
+
387
+ this.setActionAlertInfo({
388
+ message: `Service with pattern ${serviceRegistrationResult.pattern} registered and activated`,
389
+ prompt:
390
+ 'You can now launch and monitor the operation of your service',
391
+ type: ActionAlertType.STANDARD,
392
+ actions: [
393
+ {
394
+ label: 'Launch Service',
395
+ type: ActionAlertActionType.PROCEED,
396
+ handler: (): void => {
397
+ this.applicationStore.navigator.openNewWindow(
398
+ generateServiceManagementUrl(
399
+ registrationConfig.managementUrl,
400
+ serviceRegistrationResult.pattern,
401
+ ),
402
+ );
403
+ },
404
+ default: true,
405
+ },
406
+ {
407
+ label: 'Close',
408
+ type: ActionAlertActionType.PROCEED_WITH_CAUTION,
409
+ },
410
+ ],
411
+ });
412
+ } catch (error) {
413
+ assertErrorThrown(error);
414
+ this.applicationStore.log.error(
415
+ LogEvent.create(LEGEND_STUDIO_APP_EVENT.SERVICE_REGISTRATION_FAILURE),
416
+ error,
417
+ );
418
+ this.applicationStore.notifyError(error);
419
+ } finally {
420
+ this.registerServiceState.reset();
421
+ this.registerServiceState.setMessage(undefined);
422
+ }
423
+ }
424
+ }
425
+ }
426
+
427
+ export class ServiceQueryUpdaterStore extends ServiceQueryEditorStore {
428
+ readonly serviceCoordinates: string;
429
+ readonly groupWorkspaceId: string;
430
+
431
+ constructor(
432
+ applicationStore: LegendStudioApplicationStore,
433
+ sdlcServerClient: SDLCServerClient,
434
+ depotServerClient: DepotServerClient,
435
+ serviceCoordinates: string,
436
+ groupWorkspaceId: string,
437
+ ) {
438
+ super(applicationStore, sdlcServerClient, depotServerClient);
439
+
440
+ this.serviceCoordinates = serviceCoordinates;
441
+ this.groupWorkspaceId = groupWorkspaceId;
442
+ }
443
+
444
+ async fetchServiceInformation(): Promise<ProjectServiceCoordinates> {
445
+ const { groupId, artifactId, servicePath } = parseServiceCoordinates(
446
+ this.serviceCoordinates,
447
+ );
448
+ const project = ProjectData.serialization.fromJson(
449
+ await this.depotServerClient.getProject(groupId, artifactId),
450
+ );
451
+
452
+ return {
453
+ projectId: project.projectId,
454
+ groupWorkspaceId: this.groupWorkspaceId,
455
+ servicePath,
456
+ };
457
+ }
458
+ }
459
+
460
+ export class ProjectServiceQueryUpdaterStore extends ServiceQueryEditorStore {
461
+ readonly projectId: string;
462
+ readonly groupWorkspaceId: string;
463
+ readonly servicePath: string;
464
+
465
+ constructor(
466
+ applicationStore: LegendStudioApplicationStore,
467
+ sdlcServerClient: SDLCServerClient,
468
+ depotServerClient: DepotServerClient,
469
+ projectId: string,
470
+ groupWorkspaceId: string,
471
+ servicePath: string,
472
+ ) {
473
+ super(applicationStore, sdlcServerClient, depotServerClient);
474
+
475
+ this.projectId = projectId;
476
+ this.groupWorkspaceId = groupWorkspaceId;
477
+ this.servicePath = servicePath;
478
+ }
479
+
480
+ async fetchServiceInformation(): Promise<ProjectServiceCoordinates> {
481
+ return {
482
+ projectId: this.projectId,
483
+ groupWorkspaceId: this.groupWorkspaceId,
484
+ servicePath: this.servicePath,
485
+ };
486
+ }
487
+ }