@finos/legend-application-studio 28.5.9 → 28.7.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 (62) hide show
  1. package/lib/__lib__/LegendStudioDocumentation.d.ts +1 -0
  2. package/lib/__lib__/LegendStudioDocumentation.d.ts.map +1 -1
  3. package/lib/__lib__/LegendStudioDocumentation.js +2 -0
  4. package/lib/__lib__/LegendStudioDocumentation.js.map +1 -1
  5. package/lib/components/editor/editor-group/service-editor/ServiceEditor.d.ts.map +1 -1
  6. package/lib/components/editor/editor-group/service-editor/ServiceEditor.js +52 -5
  7. package/lib/components/editor/editor-group/service-editor/ServiceEditor.js.map +1 -1
  8. package/lib/components/editor/side-bar/ProjectOverview.d.ts.map +1 -1
  9. package/lib/components/editor/side-bar/ProjectOverview.js +17 -6
  10. package/lib/components/editor/side-bar/ProjectOverview.js.map +1 -1
  11. package/lib/components/editor/side-bar/WorkspaceReview.d.ts.map +1 -1
  12. package/lib/components/editor/side-bar/WorkspaceReview.js +23 -10
  13. package/lib/components/editor/side-bar/WorkspaceReview.js.map +1 -1
  14. package/lib/index.css +2 -2
  15. package/lib/index.css.map +1 -1
  16. package/lib/package.json +1 -1
  17. package/lib/stores/editor/EditorSDLCState.d.ts +8 -1
  18. package/lib/stores/editor/EditorSDLCState.d.ts.map +1 -1
  19. package/lib/stores/editor/EditorSDLCState.js +43 -2
  20. package/lib/stores/editor/EditorSDLCState.js.map +1 -1
  21. package/lib/stores/editor/EditorStore.d.ts.map +1 -1
  22. package/lib/stores/editor/EditorStore.js +2 -0
  23. package/lib/stores/editor/EditorStore.js.map +1 -1
  24. package/lib/stores/editor/NewElementState.d.ts.map +1 -1
  25. package/lib/stores/editor/NewElementState.js +0 -5
  26. package/lib/stores/editor/NewElementState.js.map +1 -1
  27. package/lib/stores/editor/editor-state/element-editor-state/service/ServiceEditorState.d.ts +17 -0
  28. package/lib/stores/editor/editor-state/element-editor-state/service/ServiceEditorState.d.ts.map +1 -1
  29. package/lib/stores/editor/editor-state/element-editor-state/service/ServiceEditorState.js +54 -1
  30. package/lib/stores/editor/editor-state/element-editor-state/service/ServiceEditorState.js.map +1 -1
  31. package/lib/stores/editor/editor-state/element-editor-state/service/ServiceRegistrationState.d.ts.map +1 -1
  32. package/lib/stores/editor/editor-state/element-editor-state/service/ServiceRegistrationState.js +2 -1
  33. package/lib/stores/editor/editor-state/element-editor-state/service/ServiceRegistrationState.js.map +1 -1
  34. package/lib/stores/editor/sidebar-state/ProjectOverviewState.d.ts +1 -1
  35. package/lib/stores/editor/sidebar-state/ProjectOverviewState.d.ts.map +1 -1
  36. package/lib/stores/editor/sidebar-state/ProjectOverviewState.js +4 -4
  37. package/lib/stores/editor/sidebar-state/ProjectOverviewState.js.map +1 -1
  38. package/lib/stores/editor/sidebar-state/WorkspaceReviewState.d.ts +2 -0
  39. package/lib/stores/editor/sidebar-state/WorkspaceReviewState.d.ts.map +1 -1
  40. package/lib/stores/editor/sidebar-state/WorkspaceReviewState.js +10 -2
  41. package/lib/stores/editor/sidebar-state/WorkspaceReviewState.js.map +1 -1
  42. package/lib/stores/editor/utils/MockDataUtils.d.ts.map +1 -1
  43. package/lib/stores/editor/utils/MockDataUtils.js +8 -4
  44. package/lib/stores/editor/utils/MockDataUtils.js.map +1 -1
  45. package/lib/stores/graph-modifier/DSL_Service_GraphModifierHelper.d.ts +6 -1
  46. package/lib/stores/graph-modifier/DSL_Service_GraphModifierHelper.d.ts.map +1 -1
  47. package/lib/stores/graph-modifier/DSL_Service_GraphModifierHelper.js +21 -3
  48. package/lib/stores/graph-modifier/DSL_Service_GraphModifierHelper.js.map +1 -1
  49. package/package.json +6 -6
  50. package/src/__lib__/LegendStudioDocumentation.ts +3 -0
  51. package/src/components/editor/editor-group/service-editor/ServiceEditor.tsx +400 -135
  52. package/src/components/editor/side-bar/ProjectOverview.tsx +50 -13
  53. package/src/components/editor/side-bar/WorkspaceReview.tsx +35 -22
  54. package/src/stores/editor/EditorSDLCState.ts +61 -0
  55. package/src/stores/editor/EditorStore.ts +2 -0
  56. package/src/stores/editor/NewElementState.ts +0 -6
  57. package/src/stores/editor/editor-state/element-editor-state/service/ServiceEditorState.ts +74 -0
  58. package/src/stores/editor/editor-state/element-editor-state/service/ServiceRegistrationState.ts +4 -1
  59. package/src/stores/editor/sidebar-state/ProjectOverviewState.ts +4 -4
  60. package/src/stores/editor/sidebar-state/WorkspaceReviewState.ts +23 -1
  61. package/src/stores/editor/utils/MockDataUtils.ts +17 -3
  62. package/src/stores/graph-modifier/DSL_Service_GraphModifierHelper.ts +39 -2
@@ -23,7 +23,6 @@ import {
23
23
  ShareIcon,
24
24
  PanelLoadingIndicator,
25
25
  ContextMenu,
26
- SyncIcon,
27
26
  PencilIcon,
28
27
  InfoCircleIcon,
29
28
  TimesIcon,
@@ -338,7 +337,15 @@ const ReleaseEditor = observer(() => {
338
337
  const canCreateVersion =
339
338
  !isCurrentProjectVersionLatest &&
340
339
  !isDispatchingAction &&
341
- editorStore.sdlcServerClient.features.canCreateVersion;
340
+ editorStore.sdlcServerClient.features.canCreateVersion &&
341
+ editorStore.sdlcState.canCreateVersion;
342
+ const disabledCreateVersionTitle = isCurrentProjectVersionLatest
343
+ ? `Can't create version: project version not the latest`
344
+ : !editorStore.sdlcServerClient.features.canCreateVersion
345
+ ? `Can't create version: current svn system does not support creating versions`
346
+ : !editorStore.sdlcState.canCreateVersion
347
+ ? `Can't create version: You do not have the rights to create a version`
348
+ : undefined;
342
349
 
343
350
  // since this can be affected by other users, we refresh it more proactively
344
351
  useEffect(() => {
@@ -381,6 +388,7 @@ const ReleaseEditor = observer(() => {
381
388
  onClick={createMajorRelease}
382
389
  disabled={!canCreateVersion}
383
390
  title={
391
+ disabledCreateVersionTitle ??
384
392
  'Create a major release which comes with backward-incompatible features'
385
393
  }
386
394
  >
@@ -391,6 +399,7 @@ const ReleaseEditor = observer(() => {
391
399
  onClick={createMinorRelease}
392
400
  disabled={!canCreateVersion}
393
401
  title={
402
+ disabledCreateVersionTitle ??
394
403
  'Create a minor release which comes with backward-compatible features'
395
404
  }
396
405
  >
@@ -401,6 +410,7 @@ const ReleaseEditor = observer(() => {
401
410
  onClick={createPatchRelease}
402
411
  disabled={!canCreateVersion}
403
412
  title={
413
+ disabledCreateVersionTitle ??
404
414
  'Create a patch release which comes with backward-compatible bug fixes'
405
415
  }
406
416
  >
@@ -637,7 +647,9 @@ const PatchEditor = observer(() => {
637
647
  Create Patch
638
648
  <DocumentationLink
639
649
  className="project-overview__patch__documentation"
640
- documentationKey={LEGEND_STUDIO_DOCUMENTATION_KEY.CREATE_PATCH}
650
+ documentationKey={
651
+ LEGEND_STUDIO_DOCUMENTATION_KEY.QUESTION_WHAT_ARE_PROJECT_ROLES
652
+ }
641
653
  />
642
654
  </div>
643
655
  </div>
@@ -883,7 +895,8 @@ const OverviewViewer = observer(() => {
883
895
  const initialName = sdlcState.currentProject?.name ?? '';
884
896
  const initialDescription = sdlcState.currentProject?.description ?? '';
885
897
  const initialTags = sdlcState.currentProject?.tags ?? [];
886
- const isDispatchingAction = projectOverviewState.isUpdatingProject;
898
+ const isDispatchingAction =
899
+ projectOverviewState.updatingProjectState.isInProgress;
887
900
  const [projectIdentifier, setProjectIdentifier] = useState(initialName);
888
901
  const [description, setDescription] = useState(initialDescription);
889
902
  const [itemValue, setItemValue] = useState<string>('');
@@ -948,7 +961,6 @@ const OverviewViewer = observer(() => {
948
961
  ),
949
962
  ).catch(applicationStore.alertUnhandledError);
950
963
  };
951
-
952
964
  return (
953
965
  <div className="panel side-bar__panel project-overview__panel project-overview__overview">
954
966
  <div className="panel__header">
@@ -957,14 +969,20 @@ const OverviewViewer = observer(() => {
957
969
  {PROJECT_OVERVIEW_ACTIVITY_MODE.OVERVIEW}
958
970
  </div>
959
971
  </div>
960
- <button
961
- className="panel__header__action side-bar__header__action local-changes__sync-btn"
962
- onClick={handleUpdate}
963
- tabIndex={-1}
964
- title="Update project"
965
- >
966
- <SyncIcon />
967
- </button>
972
+ <div className="panel__header__actions">
973
+ <button
974
+ className="panel__header__action project-overview__update-btn"
975
+ onClick={handleUpdate}
976
+ title="Update Project"
977
+ tabIndex={-1}
978
+ >
979
+ <div className="project-overview__update-btn__label">
980
+ <div className="project-overview__update-btn__label__title">
981
+ Update
982
+ </div>
983
+ </div>
984
+ </button>
985
+ </div>
968
986
  </div>
969
987
  <div className="panel__content project-overview__panel__content">
970
988
  <PanelLoadingIndicator isLoading={isDispatchingAction} />
@@ -981,6 +999,25 @@ const OverviewViewer = observer(() => {
981
999
  onChange={changeProjectIdentifier}
982
1000
  />
983
1001
  </div>
1002
+ {
1003
+ <div className="panel__content__form__section">
1004
+ <div className="panel__content__form__section__header__label">
1005
+ Project User Role
1006
+ <DocumentationLink
1007
+ documentationKey={
1008
+ LEGEND_STUDIO_DOCUMENTATION_KEY.QUESTION_WHAT_ARE_PROJECT_ROLES
1009
+ }
1010
+ />
1011
+ </div>
1012
+ <input
1013
+ className="panel__content__form__section__input"
1014
+ title="Project User Role"
1015
+ disabled={true}
1016
+ spellCheck={false}
1017
+ value={sdlcState.accessRole?.accessRole ?? '(unknown)'}
1018
+ />
1019
+ </div>
1020
+ }
984
1021
  </div>
985
1022
  <div className="panel__content__form">
986
1023
  <div className="panel__content__form__section">
@@ -33,7 +33,10 @@ import { ACTIVITY_MODE } from '../../../stores/editor/EditorConfig.js';
33
33
  import { generateReviewRoute } from '../../../__lib__/LegendStudioNavigation.js';
34
34
  import { LEGEND_STUDIO_TEST_ID } from '../../../__lib__/LegendStudioTesting.js';
35
35
  import { flowResult } from 'mobx';
36
- import type { EntityDiff } from '@finos/legend-server-sdlc';
36
+ import {
37
+ AuthorizableProjectAction,
38
+ type EntityDiff,
39
+ } from '@finos/legend-server-sdlc';
37
40
  import { entityDiffSorter } from '../../../stores/editor/EditorSDLCState.js';
38
41
  import { useEditorStore } from '../EditorStoreProvider.js';
39
42
  import { useLegendStudioApplicationStore } from '../../LegendStudioFrameworkProvider.js';
@@ -128,6 +131,19 @@ export const WorkspaceReview = observer(() => {
128
131
  applicationStore.alertUnhandledError,
129
132
  );
130
133
  };
134
+ // commit Review
135
+ const isCommitReviewDisabled =
136
+ isDispatchingAction ||
137
+ Boolean(!workspaceReview) ||
138
+ workspaceContainsSnapshotDependencies ||
139
+ !workspaceReviewState.canMergeReview;
140
+ const commitReviewTitle = workspaceContainsSnapshotDependencies
141
+ ? `Can't commit review: workspace has snapshot dependencies`
142
+ : !workspaceReviewState.canMergeReview
143
+ ? workspaceReviewState.sdlcState.unAuthorizedActionMessage(
144
+ AuthorizableProjectAction.COMMIT_REVIEW,
145
+ )
146
+ : 'Commit review';
131
147
  const commitReview = (): void => {
132
148
  if (workspaceReview && !isDispatchingAction) {
133
149
  editorStore.localChangesState.alertUnsavedChanges((): void => {
@@ -138,6 +154,20 @@ export const WorkspaceReview = observer(() => {
138
154
  });
139
155
  }
140
156
  };
157
+ // create Review
158
+ const isCreateReviewDisabled =
159
+ isDispatchingAction ||
160
+ Boolean(workspaceReview) ||
161
+ !workspaceReviewState.reviewTitle ||
162
+ workspaceContainsSnapshotDependencies ||
163
+ !workspaceReviewState.canCreateReview;
164
+ const createReviewTitle = workspaceContainsSnapshotDependencies
165
+ ? `Can't create review: workspace has snapshot dependencies`
166
+ : !workspaceReviewState.canCreateReview
167
+ ? workspaceReviewState.sdlcState.unAuthorizedActionMessage(
168
+ AuthorizableProjectAction.SUBMIT_REVIEW,
169
+ )
170
+ : 'Create review';
141
171
  const createReview = (): void => {
142
172
  if (
143
173
  workspaceReviewState.reviewTitle &&
@@ -229,17 +259,8 @@ export const WorkspaceReview = observer(() => {
229
259
  'btn--error': workspaceContainsSnapshotDependencies,
230
260
  })}
231
261
  onClick={createReview}
232
- disabled={
233
- isDispatchingAction ||
234
- Boolean(workspaceReview) ||
235
- !workspaceReviewState.reviewTitle ||
236
- workspaceContainsSnapshotDependencies
237
- }
238
- title={
239
- !workspaceContainsSnapshotDependencies
240
- ? 'Create review'
241
- : `Can't create review: workspace has snapshot dependencies`
242
- }
262
+ disabled={isCreateReviewDisabled}
263
+ title={createReviewTitle}
243
264
  >
244
265
  <PlusIcon />
245
266
  </button>
@@ -283,17 +304,9 @@ export const WorkspaceReview = observer(() => {
283
304
  { 'btn--error': workspaceContainsSnapshotDependencies },
284
305
  )}
285
306
  onClick={commitReview}
286
- disabled={
287
- isDispatchingAction ||
288
- Boolean(!workspaceReview) ||
289
- workspaceContainsSnapshotDependencies
290
- }
307
+ disabled={isCommitReviewDisabled}
291
308
  tabIndex={-1}
292
- title={
293
- !workspaceContainsSnapshotDependencies
294
- ? 'Commit review'
295
- : `Can't commit review: workspace has snapshot dependencies`
296
- }
309
+ title={commitReviewTitle}
297
310
  >
298
311
  <TruncatedGitMergeIcon />
299
312
  </button>
@@ -33,6 +33,7 @@ import {
33
33
  guaranteeNonNullable,
34
34
  assertTrue,
35
35
  ActionState,
36
+ prettyCONSTName,
36
37
  } from '@finos/legend-shared';
37
38
  import { EDITOR_MODE, ACTIVITY_MODE } from './EditorConfig.js';
38
39
  import { type Entity, extractEntityNameFromPath } from '@finos/legend-storage';
@@ -47,6 +48,8 @@ import {
47
48
  Workspace,
48
49
  WorkspaceAccessType,
49
50
  Patch,
51
+ ProjectAccessRole,
52
+ AuthorizableProjectAction,
50
53
  } from '@finos/legend-server-sdlc';
51
54
  import { LEGEND_STUDIO_APP_EVENT } from '../../__lib__/LegendStudioEvent.js';
52
55
 
@@ -73,6 +76,8 @@ export class EditorSDLCState {
73
76
  workspaceWorkflows: Workflow[] = [];
74
77
  projectVersions: Version[] = [];
75
78
  projectPublishedVersions: string[] = [];
79
+ authorizedActions: AuthorizableProjectAction[] | undefined;
80
+ accessRole: ProjectAccessRole | undefined;
76
81
 
77
82
  constructor(editorStore: EditorStore) {
78
83
  makeObservable(this, {
@@ -88,11 +93,15 @@ export class EditorSDLCState {
88
93
  isFetchingProjectVersions: observable,
89
94
  isFetchingProject: observable,
90
95
  projectPublishedVersions: observable,
96
+ authorizedActions: observable,
97
+ accessRole: observable,
91
98
  activeProject: computed,
92
99
  activeWorkspace: computed,
93
100
  activeRevision: computed,
94
101
  activePatch: computed,
95
102
  activeRemoteWorkspaceRevision: computed,
103
+ canCreateWorkspace: computed,
104
+ canCreateVersion: computed,
96
105
  isWorkspaceOutOfSync: computed,
97
106
  setCurrentProject: action,
98
107
  setCurrentPatch: action,
@@ -112,6 +121,7 @@ export class EditorSDLCState {
112
121
  buildProjectLatestRevisionEntityHashesIndex: flow,
113
122
  fetchWorkspaceWorkflows: flow,
114
123
  fetchPublishedProjectVersions: flow,
124
+ fetchAuthorizedActions: flow,
115
125
  });
116
126
 
117
127
  this.editorStore = editorStore;
@@ -153,6 +163,32 @@ export class EditorSDLCState {
153
163
  return this.activeRemoteWorkspaceRevision.id !== this.activeRevision.id;
154
164
  }
155
165
 
166
+ get canCreateWorkspace(): boolean {
167
+ return this.userCanPerformAction(
168
+ AuthorizableProjectAction.CREATE_WORKSPACE,
169
+ );
170
+ }
171
+
172
+ get canCreateVersion(): boolean {
173
+ return this.userCanPerformAction(AuthorizableProjectAction.CREATE_VERSION);
174
+ }
175
+
176
+ unAuthorizedActionMessage(_action: AuthorizableProjectAction): string {
177
+ if (this.accessRole) {
178
+ return `Your role ${
179
+ this.accessRole.accessRole
180
+ } does not allow you to perform the action '${prettyCONSTName(_action)}'`;
181
+ }
182
+ return `Your are not entitled to perform the action: ${_action}`;
183
+ }
184
+
185
+ userCanPerformAction(authorizedAction: AuthorizableProjectAction): boolean {
186
+ return Boolean(
187
+ this.authorizedActions === undefined ||
188
+ this.authorizedActions.includes(authorizedAction),
189
+ );
190
+ }
191
+
156
192
  setCurrentProject(val: Project): void {
157
193
  this.currentProject = val;
158
194
  }
@@ -530,6 +566,31 @@ export class EditorSDLCState {
530
566
  }
531
567
  }
532
568
 
569
+ *fetchAuthorizedActions(): GeneratorFn<void> {
570
+ try {
571
+ const [authorizedActions, accessRoleRaw] = (yield Promise.all([
572
+ this.editorStore.sdlcServerClient.getAutorizedActions(
573
+ this.activeProject.projectId,
574
+ ),
575
+ this.editorStore.sdlcServerClient.getAccessRole(
576
+ this.activeProject.projectId,
577
+ ),
578
+ ])) as [AuthorizableProjectAction[], PlainObject<ProjectAccessRole>];
579
+ this.authorizedActions = authorizedActions;
580
+ this.accessRole = ProjectAccessRole.serialization.fromJson(accessRoleRaw);
581
+ } catch (error) {
582
+ assertErrorThrown(error);
583
+ // if there is an error fetching authorized actions we should set undefined
584
+ this.authorizedActions = undefined;
585
+ this.accessRole = undefined;
586
+ this.editorStore.applicationStore.logService.error(
587
+ LogEvent.create(LEGEND_STUDIO_APP_EVENT.SDLC_MANAGER_FAILURE),
588
+ error,
589
+ );
590
+ this.editorStore.applicationStore.notificationService.notifyError(error);
591
+ }
592
+ }
593
+
533
594
  *fetchPublishedProjectVersions(): GeneratorFn<void> {
534
595
  try {
535
596
  this.fetchPublishedProjectVersionsState.inProgress();
@@ -748,6 +748,7 @@ export class EditorStore implements CommandRegistrar {
748
748
  this.graphState.fetchAvailableRelationalDatabseTypeConfigurations(),
749
749
  this.sdlcState.fetchProjectVersions(),
750
750
  this.sdlcState.fetchPublishedProjectVersions(),
751
+ this.sdlcState.fetchAuthorizedActions(),
751
752
  ]);
752
753
  }
753
754
 
@@ -790,6 +791,7 @@ export class EditorStore implements CommandRegistrar {
790
791
  this.graphState.fetchAvailableRelationalDatabseTypeConfigurations(),
791
792
  this.sdlcState.fetchProjectVersions(),
792
793
  this.sdlcState.fetchPublishedProjectVersions(),
794
+ this.sdlcState.fetchAuthorizedActions(),
793
795
  ]);
794
796
  }
795
797
 
@@ -558,12 +558,6 @@ export class NewServiceDriver extends NewElementDriver<Service> {
558
558
  this.editorStore.changeDetectionState.observerContext,
559
559
  );
560
560
  service_initNewService(service);
561
- const currentUserId =
562
- this.editorStore.graphManagerState.graphManager.TEMPORARY__getEngineConfig()
563
- .currentUserId;
564
- if (currentUserId) {
565
- service.owners = [currentUserId];
566
- }
567
561
  return service;
568
562
  }
569
563
  }
@@ -38,6 +38,8 @@ import {
38
38
  Service,
39
39
  PureSingleExecution,
40
40
  PureMultiExecution,
41
+ DeploymentOwnership,
42
+ UserListOwnership,
41
43
  isStubbed_RawLambda,
42
44
  getValueSpecificationReturnType,
43
45
  type Type,
@@ -48,6 +50,7 @@ import { ServiceTestableState } from './testable/ServiceTestableState.js';
48
50
  import { User } from '@finos/legend-server-sdlc';
49
51
  import { ServicePostValidationsState } from './ServicePostValidationState.js';
50
52
  import { valueSpecReturnTDS } from '@finos/legend-query-builder';
53
+ import { service_setOwnership } from '../../../../graph-modifier/DSL_Service_GraphModifierHelper.js';
51
54
 
52
55
  export enum SERVICE_TAB {
53
56
  GENERAL = 'GENERAL',
@@ -57,6 +60,11 @@ export enum SERVICE_TAB {
57
60
  POST_VALIDATION = 'POST_VALIDATION',
58
61
  }
59
62
 
63
+ enum ServiceOwnershipType {
64
+ DEPLOYMENT_OWNERSHIP = 'deploymentOwnership',
65
+ USERLIST_OWNERSHIP = 'userListOwnership',
66
+ }
67
+
60
68
  export const resolveServiceQueryValueSpec = (
61
69
  service: Service,
62
70
  editorStore: EditorStore,
@@ -101,6 +109,24 @@ export const isServiceQueryTDS = (
101
109
  };
102
110
 
103
111
  export const MINIMUM_SERVICE_OWNERS = 2;
112
+ export const DeploymentOwnershipLabel = 'Deployment';
113
+ export const UserlistOwnershipLabel = 'User List';
114
+ export const OWNERSHIP_OPTIONS = [
115
+ {
116
+ label: DeploymentOwnershipLabel,
117
+ value: ServiceOwnershipType.DEPLOYMENT_OWNERSHIP,
118
+ },
119
+ {
120
+ label: UserlistOwnershipLabel,
121
+ value: ServiceOwnershipType.USERLIST_OWNERSHIP,
122
+ },
123
+ ];
124
+
125
+ export type ServiceOwnerOption = {
126
+ label: string;
127
+ value: string;
128
+ };
129
+
104
130
  export class ServiceEditorState extends ElementEditorState {
105
131
  executionState: ServiceExecutionState;
106
132
  registrationState: ServiceRegistrationState;
@@ -113,10 +139,12 @@ export class ServiceEditorState extends ElementEditorState {
113
139
 
114
140
  makeObservable(this, {
115
141
  executionState: observable,
142
+ selectedOwnership: computed,
116
143
  registrationState: observable,
117
144
  selectedTab: observable,
118
145
  postValidationState: observable,
119
146
  setSelectedTab: action,
147
+ setSelectedOwnership: action,
120
148
  resetExecutionState: action,
121
149
  openToTestTab: action,
122
150
  service: computed,
@@ -153,6 +181,52 @@ export class ServiceEditorState extends ElementEditorState {
153
181
  this.selectedTab = SERVICE_TAB.TEST;
154
182
  }
155
183
 
184
+ get selectedOwnership(): ServiceOwnerOption | undefined {
185
+ const ownership = this.service.ownership;
186
+ if (ownership instanceof DeploymentOwnership) {
187
+ return {
188
+ label: DeploymentOwnershipLabel,
189
+ value: ServiceOwnershipType.DEPLOYMENT_OWNERSHIP,
190
+ };
191
+ } else if (ownership instanceof UserListOwnership) {
192
+ return {
193
+ label: UserlistOwnershipLabel,
194
+ value: ServiceOwnershipType.USERLIST_OWNERSHIP,
195
+ };
196
+ }
197
+ return undefined;
198
+ }
199
+
200
+ setSelectedOwnership(o: ServiceOwnerOption): void {
201
+ switch (o.value) {
202
+ case ServiceOwnershipType.DEPLOYMENT_OWNERSHIP: {
203
+ service_setOwnership(
204
+ this.service,
205
+ new DeploymentOwnership('', this.service),
206
+ );
207
+ break;
208
+ }
209
+ case ServiceOwnershipType.USERLIST_OWNERSHIP: {
210
+ const currentUserId =
211
+ this.editorStore.graphManagerState.graphManager.TEMPORARY__getEngineConfig()
212
+ .currentUserId;
213
+ service_setOwnership(
214
+ this.service,
215
+ new UserListOwnership(
216
+ currentUserId ? [currentUserId] : [],
217
+ this.service,
218
+ ),
219
+ );
220
+ break;
221
+ }
222
+ default: {
223
+ this.editorStore.applicationStore.notificationService.notifyError(
224
+ 'Unsupported ownership type',
225
+ );
226
+ }
227
+ }
228
+ }
229
+
156
230
  resetExecutionState(): void {
157
231
  this.executionState = this.buildExecutionState();
158
232
  }
@@ -354,7 +354,10 @@ export class ServiceRegistrationState extends ServiceConfigState {
354
354
  assertNonEmptyString(owner, `Service can't have an empty owner name`),
355
355
  );
356
356
  assertTrue(
357
- this.service.owners.length >= MINIMUM_SERVICE_OWNERS,
357
+ Boolean(
358
+ this.service.ownership ??
359
+ this.service.owners.length >= MINIMUM_SERVICE_OWNERS,
360
+ ),
358
361
  `Service needs to have at least 2 owners in order to be registered`,
359
362
  );
360
363
  guaranteeNonNullable(
@@ -67,7 +67,7 @@ export class ProjectOverviewState {
67
67
  isCreatingVersion = false;
68
68
  isFetchingProjectWorkspaces = false;
69
69
  isDeletingWorkspace = false;
70
- isUpdatingProject = false;
70
+ updatingProjectState = ActionState.create();
71
71
  isFetchingLatestVersion = false;
72
72
  isFetchingCurrentProjectRevision = false;
73
73
 
@@ -85,7 +85,7 @@ export class ProjectOverviewState {
85
85
  isCreatingVersion: observable,
86
86
  isFetchingProjectWorkspaces: observable,
87
87
  isDeletingWorkspace: observable,
88
- isUpdatingProject: observable,
88
+ updatingProjectState: observable,
89
89
  isFetchingLatestVersion: observable,
90
90
  isFetchingCurrentProjectRevision: observable,
91
91
  setActivityMode: action,
@@ -193,7 +193,7 @@ export class ProjectOverviewState {
193
193
  tags: string[],
194
194
  ): GeneratorFn<void> {
195
195
  try {
196
- this.isUpdatingProject = true;
196
+ this.updatingProjectState.inProgress();
197
197
  yield this.editorStore.sdlcServerClient.updateProject(
198
198
  this.sdlcState.activeProject.projectId,
199
199
  {
@@ -214,7 +214,7 @@ export class ProjectOverviewState {
214
214
  assertErrorThrown(error);
215
215
  this.editorStore.applicationStore.notificationService.notifyError(error);
216
216
  } finally {
217
- this.isUpdatingProject = false;
217
+ this.updatingProjectState.complete();
218
218
  }
219
219
  }
220
220
 
@@ -14,7 +14,14 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import { action, makeObservable, flowResult, observable, flow } from 'mobx';
17
+ import {
18
+ action,
19
+ makeObservable,
20
+ flowResult,
21
+ observable,
22
+ flow,
23
+ computed,
24
+ } from 'mobx';
18
25
  import type { EditorStore } from '../EditorStore.js';
19
26
  import type { EditorSDLCState } from '../EditorSDLCState.js';
20
27
  import { LEGEND_STUDIO_APP_EVENT } from '../../../__lib__/LegendStudioEvent.js';
@@ -36,6 +43,7 @@ import {
36
43
  Review,
37
44
  ReviewState,
38
45
  RevisionAlias,
46
+ AuthorizableProjectAction,
39
47
  } from '@finos/legend-server-sdlc';
40
48
  import { ActionAlertActionType } from '@finos/legend-application';
41
49
 
@@ -69,6 +77,8 @@ export class WorkspaceReviewState {
69
77
  isCreatingWorkspaceReview: observable,
70
78
  isCommittingWorkspaceReview: observable,
71
79
  isRecreatingWorkspaceAfterCommittingReview: observable,
80
+ canCreateReview: computed,
81
+ canMergeReview: computed,
72
82
  setReviewTitle: action,
73
83
  openReviewChange: action,
74
84
  refreshWorkspaceChanges: flow,
@@ -83,6 +93,18 @@ export class WorkspaceReviewState {
83
93
  this.sdlcState = sdlcState;
84
94
  }
85
95
 
96
+ get canCreateReview(): boolean {
97
+ return this.sdlcState.userCanPerformAction(
98
+ AuthorizableProjectAction.SUBMIT_REVIEW,
99
+ );
100
+ }
101
+
102
+ get canMergeReview(): boolean {
103
+ return this.sdlcState.userCanPerformAction(
104
+ AuthorizableProjectAction.COMMIT_REVIEW,
105
+ );
106
+ }
107
+
86
108
  setReviewTitle(val: string): void {
87
109
  this.reviewTitle = val;
88
110
  }
@@ -56,6 +56,7 @@ import {
56
56
  Date as ColumnDate,
57
57
  SemiStructured,
58
58
  Json,
59
+ MILESTONING_VERSION_PROPERTY_SUFFIX,
59
60
  } from '@finos/legend-graph';
60
61
  import {
61
62
  CLASS_PROPERTY_TYPE,
@@ -116,10 +117,23 @@ export const createMockClassInstance = (
116
117
  depth = 0,
117
118
  ): PlainObject => {
118
119
  const properties = traverseNonRequiredProperties
119
- ? getAllClassProperties(_class)
120
- : getAllClassProperties(_class).filter((p) => p.multiplicity.lowerBound);
120
+ ? getAllClassProperties(_class, true)
121
+ : getAllClassProperties(_class, true).filter(
122
+ (p) => p.multiplicity.lowerBound,
123
+ );
124
+ const propertyNames = properties.map((property) => property.name);
125
+ const filteredProperties = properties.filter(
126
+ (prop) =>
127
+ prop.name.endsWith(MILESTONING_VERSION_PROPERTY_SUFFIX.ALL_VERSIONS) ||
128
+ (!propertyNames.includes(
129
+ prop.name + MILESTONING_VERSION_PROPERTY_SUFFIX.ALL_VERSIONS,
130
+ ) &&
131
+ !prop.name.endsWith(
132
+ MILESTONING_VERSION_PROPERTY_SUFFIX.ALL_VERSIONS_IN_RANGE,
133
+ )),
134
+ );
121
135
  const mockData: Record<string, object | string | number | boolean> = {};
122
- properties.forEach((property) => {
136
+ filteredProperties.forEach((property) => {
123
137
  const propertyType = property.genericType.value.rawType;
124
138
  let propertyMockData: PlainObject | string | number | boolean | undefined;
125
139
  switch (getClassPropertyType(propertyType)) {
@@ -23,6 +23,9 @@ import {
23
23
  type RawLambda,
24
24
  type Runtime,
25
25
  type Service,
26
+ DeploymentOwnership,
27
+ type UserListOwnership,
28
+ type ServiceOwnership,
26
29
  type ServiceExecution,
27
30
  type ObserverContext,
28
31
  type ServiceTestSuite,
@@ -47,6 +50,7 @@ import {
47
50
  observe_PostValidationAssertion,
48
51
  TestData,
49
52
  observe_TestData,
53
+ observe_Ownership,
50
54
  } from '@finos/legend-graph';
51
55
  import { addUniqueEntry, deleteEntry, uuid } from '@finos/legend-shared';
52
56
  import { action } from 'mobx';
@@ -158,12 +162,20 @@ export const service_deleteTestSuite = action(
158
162
  },
159
163
  );
160
164
 
165
+ export const service_setOwnership = action(
166
+ (service: Service, value: ServiceOwnership | undefined): void => {
167
+ service.ownership = value ? observe_Ownership(value) : undefined;
168
+ },
169
+ );
170
+
161
171
  export const service_initNewService = action(
162
172
  (service: Service, userId?: string): void => {
163
173
  service.pattern = `/${uuid()}`; // initialize the service pattern with an UUID to avoid people leaving the pattern as /
164
174
  if (userId) {
165
- service.owners.push(userId);
166
- } // this is used to add the current user as the first owner by default
175
+ service.owners = [userId];
176
+ } else {
177
+ service_setOwnership(service, new DeploymentOwnership('', service));
178
+ }
167
179
  },
168
180
  );
169
181
  export const service_setExecution = action(
@@ -180,6 +192,31 @@ export const service_setPattern = action(
180
192
  service.pattern = value;
181
193
  },
182
194
  );
195
+
196
+ export const service_deploymentOwnership = action(
197
+ (deployment: DeploymentOwnership, value: string): void => {
198
+ deployment.identifier = value;
199
+ },
200
+ );
201
+
202
+ export const service_addUserOwnership = action(
203
+ (userList: UserListOwnership, value: string): void => {
204
+ userList.users.push(value);
205
+ },
206
+ );
207
+
208
+ export const service_updateUserOwnership = action(
209
+ (userList: UserListOwnership, value: string, index: number): void => {
210
+ userList.users[index] = value;
211
+ },
212
+ );
213
+
214
+ export const service_deleteValueFromUserOwnership = action(
215
+ (userList: UserListOwnership, index: number): void => {
216
+ userList.users.splice(index, 1);
217
+ },
218
+ );
219
+
183
220
  export const service_setDocumentation = action(
184
221
  (service: Service, value: string): void => {
185
222
  service.documentation = value;