@finos/legend-application-studio 28.19.55 → 28.19.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finos/legend-application-studio",
3
- "version": "28.19.55",
3
+ "version": "28.19.57",
4
4
  "description": "Legend Studio application core",
5
5
  "keywords": [
6
6
  "legend",
@@ -45,14 +45,14 @@
45
45
  "test:watch": "jest --watch"
46
46
  },
47
47
  "dependencies": {
48
- "@finos/legend-application": "16.0.76",
49
- "@finos/legend-art": "7.1.127",
48
+ "@finos/legend-application": "16.0.77",
49
+ "@finos/legend-art": "7.1.128",
50
50
  "@finos/legend-code-editor": "2.0.128",
51
- "@finos/legend-data-cube": "0.3.38",
52
- "@finos/legend-extension-dsl-data-product": "0.0.15",
51
+ "@finos/legend-data-cube": "0.3.39",
52
+ "@finos/legend-extension-dsl-data-product": "0.0.17",
53
53
  "@finos/legend-graph": "32.3.9",
54
- "@finos/legend-lego": "2.0.139",
55
- "@finos/legend-query-builder": "4.17.51",
54
+ "@finos/legend-lego": "2.0.140",
55
+ "@finos/legend-query-builder": "4.17.52",
56
56
  "@finos/legend-server-depot": "6.1.1",
57
57
  "@finos/legend-server-lakehouse": "0.3.9",
58
58
  "@finos/legend-server-sdlc": "5.3.65",
@@ -79,6 +79,7 @@ import {
79
79
  PURE_MappingIcon,
80
80
  GitBranchIcon,
81
81
  ListIcon,
82
+ PanelLoadingIndicator,
82
83
  } from '@finos/legend-art';
83
84
  import {
84
85
  type ChangeEventHandler,
@@ -104,9 +105,6 @@ import {
104
105
  type Mapping,
105
106
  type PackageableElement,
106
107
  type PackageableRuntime,
107
- type V1_AccessPointGroupInfo,
108
- type V1_AccessPointImplementation,
109
- type V1_DataProductArtifact,
110
108
  DataProductEmbeddedImageIcon,
111
109
  DataProductLibraryIcon,
112
110
  Email,
@@ -400,12 +398,12 @@ const AccessPointGenerationViewer = observer(
400
398
  const { accessPointState, generationOutput } = props;
401
399
  const editorStore = accessPointState.state.state.editorStore;
402
400
  const closeDebug = (): void => {
403
- accessPointState.setShowDebug(false);
401
+ accessPointState.setArtifactContent(undefined);
404
402
  };
405
403
 
406
404
  return (
407
405
  <Dialog
408
- open={accessPointState.showDebug}
406
+ open={accessPointState.artifactGenerationContent !== undefined}
409
407
  onClose={closeDebug}
410
408
  classes={{
411
409
  root: 'editor-modal__root-container',
@@ -466,7 +464,6 @@ export const LakehouseDataProductAccessPointEditor = observer(
466
464
  const [editingTitle, setEditingTitle] = useState(false);
467
465
  const [isHoveringTitle, setIsHoveringTitle] = useState(false);
468
466
  const ref = useRef<HTMLDivElement>(null);
469
- const [debugOutput, setDebugOutput] = useState('');
470
467
 
471
468
  const handleDescriptionEdit = () => setEditingDescription(true);
472
469
  const handleDescriptionBlur = () => {
@@ -500,49 +497,10 @@ export const LakehouseDataProductAccessPointEditor = observer(
500
497
  },
501
498
  );
502
499
 
503
- const debugPlanGeneration = async (): Promise<void> => {
504
- try {
505
- const generatedArtifacts =
506
- await editorStore.graphManagerState.graphManager.generateArtifacts(
507
- editorStore.graphManagerState.graph,
508
- editorStore.graphEditorMode.getGraphTextInputOption(),
509
- [groupState.state.elementPath],
510
- );
511
- const dataProductExtension = 'dataProduct';
512
- const dataProductArtifact = generatedArtifacts.values.filter(
513
- (artifact) => artifact.extension === dataProductExtension,
514
- );
515
- const dataProductContent =
516
- dataProductArtifact[0]?.artifactsByExtensionElements[0]?.files[0]
517
- ?.content ?? null;
518
-
519
- if (dataProductContent) {
520
- const contentJson = JSON.parse(
521
- dataProductContent,
522
- ) as V1_DataProductArtifact;
523
- const apPlanGeneration = contentJson.accessPointGroups
524
- .find(
525
- (group: V1_AccessPointGroupInfo) =>
526
- group.id === groupState.value.id,
527
- )
528
- ?.accessPointImplementations.find(
529
- (apImplementation: V1_AccessPointImplementation) =>
530
- apImplementation.id === accessPoint.id,
531
- );
532
-
533
- setDebugOutput(JSON.stringify(apPlanGeneration, null, 2));
534
- accessPointState.setShowDebug(true);
535
- } else {
536
- throw new Error(
537
- 'Could not find contents of this data product artifact',
538
- );
539
- }
540
- } catch (error) {
541
- editorStore.applicationStore.notificationService.notifyError(
542
- `Failed to fetch access point plan generation: ${error}`,
543
- );
544
- }
545
- };
500
+ const debugPlanGeneration =
501
+ editorStore.applicationStore.guardUnhandledError(() =>
502
+ flowResult(accessPointState.generateArtifact()),
503
+ );
546
504
 
547
505
  const handleRemoveAccessPoint = (): void => {
548
506
  editorStore.applicationStore.alertService.setActionAlertInfo({
@@ -718,11 +676,7 @@ export const LakehouseDataProductAccessPointEditor = observer(
718
676
  <MenuContent>
719
677
  <MenuContentItem
720
678
  className="btn__dropdown-combo__option"
721
- onClick={() => {
722
- debugPlanGeneration().catch(
723
- editorStore.applicationStore.alertUnhandledError,
724
- );
725
- }}
679
+ onClick={debugPlanGeneration}
726
680
  >
727
681
  <div
728
682
  style={{
@@ -896,10 +850,10 @@ export const LakehouseDataProductAccessPointEditor = observer(
896
850
  >
897
851
  <TimesIcon />
898
852
  </button>
899
- {accessPointState.showDebug && (
853
+ {accessPointState.artifactGenerationContent && (
900
854
  <AccessPointGenerationViewer
901
855
  accessPointState={accessPointState}
902
- generationOutput={debugOutput}
856
+ generationOutput={accessPointState.artifactGenerationContent}
903
857
  />
904
858
  )}
905
859
  </div>
@@ -1401,6 +1355,7 @@ const AccessPointGroupEditor = observer(
1401
1355
  className="access-point-editor__group-container"
1402
1356
  data-testid={LEGEND_STUDIO_TEST_ID.ACCESS_POINT_GROUP_EDITOR}
1403
1357
  >
1358
+ <PanelLoadingIndicator isLoading={groupState.isRunningProcess} />
1404
1359
  <div className="access-point-editor__group-container__name-editor">
1405
1360
  {editingName ? (
1406
1361
  <textarea
@@ -42,8 +42,9 @@ import {
42
42
  HammerIcon,
43
43
  TerminalIcon,
44
44
  ResizablePanelSplitterLine,
45
+ GitlabIcon,
45
46
  } from '@finos/legend-art';
46
- import { isNonNullable } from '@finos/legend-shared';
47
+ import { assertErrorThrown, isNonNullable } from '@finos/legend-shared';
47
48
  import {
48
49
  useProjectViewerStore,
49
50
  withProjectViewerStore,
@@ -51,6 +52,7 @@ import {
51
52
  import {
52
53
  type ProjectViewerPathParams,
53
54
  generateSetupRoute,
55
+ generateViewProjectRoute,
54
56
  } from '../../__lib__/LegendStudioNavigation.js';
55
57
  import { ProjectSearchCommand } from '../editor/command-center/ProjectSearchCommand.js';
56
58
  import { flowResult } from 'mobx';
@@ -71,6 +73,9 @@ import { EmbeddedQueryBuilder } from '../editor/EmbeddedQueryBuilder.js';
71
73
  import type { ActivityBarItemConfig } from '@finos/legend-lego/application';
72
74
  import { ActivityBarMenu } from '../editor/ActivityBar.js';
73
75
  import { PanelGroup } from '../editor/panel-group/PanelGroup.js';
76
+ import { StoreProjectData } from '@finos/legend-server-depot';
77
+ import { generateGAVCoordinates } from '@finos/legend-storage';
78
+ import { Project } from '@finos/legend-server-sdlc';
74
79
 
75
80
  const ProjectViewerStatusBar = observer(() => {
76
81
  const params = useParams<ProjectViewerPathParams>();
@@ -82,6 +87,7 @@ const ProjectViewerStatusBar = observer(() => {
82
87
  const extraSDLCInfo = params.revisionId ?? params.versionId ?? 'HEAD';
83
88
  const projectId = params.projectId;
84
89
  const currentProject = editorStore.sdlcState.currentProject;
90
+ const gav = viewerStore.projectGAVCoordinates;
85
91
  const versionBehindProjectHead =
86
92
  viewerStore.currentRevision &&
87
93
  viewerStore.version &&
@@ -117,6 +123,54 @@ const ProjectViewerStatusBar = observer(() => {
117
123
  applicationStore.assistantService.toggleAssistant();
118
124
  const togglePanel = (): void => editorStore.panelGroupDisplayState.toggle();
119
125
 
126
+ const visitProject = async (): Promise<void> => {
127
+ try {
128
+ if (gav) {
129
+ const project = StoreProjectData.serialization.fromJson(
130
+ await editorStore.depotServerClient.getProject(
131
+ gav.groupId,
132
+ gav.artifactId,
133
+ ),
134
+ );
135
+ const sdlcProjectUrl = generateViewProjectRoute(project.projectId);
136
+ applicationStore.navigationService.navigator.visitAddress(
137
+ applicationStore.navigationService.navigator.generateAddress(
138
+ sdlcProjectUrl,
139
+ ),
140
+ );
141
+ }
142
+ } catch (error) {
143
+ assertErrorThrown(error);
144
+ editorStore.applicationStore.notificationService.notifyError(
145
+ `Can't open project.`,
146
+ );
147
+ }
148
+ };
149
+
150
+ const visitWebProjectUrl = async (): Promise<void> => {
151
+ try {
152
+ if (gav) {
153
+ const project = StoreProjectData.serialization.fromJson(
154
+ await editorStore.depotServerClient.getProject(
155
+ gav.groupId,
156
+ gav.artifactId,
157
+ ),
158
+ );
159
+ const sdlcProject = Project.serialization.fromJson(
160
+ await editorStore.sdlcServerClient.getProject(project.projectId),
161
+ );
162
+ applicationStore.navigationService.navigator.visitAddress(
163
+ sdlcProject.webUrl,
164
+ );
165
+ }
166
+ } catch (error) {
167
+ assertErrorThrown(error);
168
+ editorStore.applicationStore.notificationService.notifyError(
169
+ `Can't open project.`,
170
+ );
171
+ }
172
+ };
173
+
120
174
  return (
121
175
  <div
122
176
  data-testid={LEGEND_STUDIO_TEST_ID.STATUS_BAR}
@@ -155,6 +209,36 @@ const ProjectViewerStatusBar = observer(() => {
155
209
  )}
156
210
  </div>
157
211
  )}
212
+ {!currentProject && gav && (
213
+ <div className="editor__status-bar__workspace">
214
+ <div className="editor__status-bar__workspace__icon">
215
+ <CodeBranchIcon />
216
+ </div>
217
+ <div className="editor__status-bar__workspace__project">
218
+ <button
219
+ className="editor__status-bar__workspace__project"
220
+ title="Go to Studio SDLC View of Project"
221
+ tabIndex={-1}
222
+ onClick={() => flowResult(visitProject())}
223
+ >
224
+ {`${generateGAVCoordinates(gav.groupId, gav.artifactId, undefined)}`}
225
+ </button>
226
+ </div>
227
+ /<div></div>
228
+ <div
229
+ onClick={() => flowResult(visitWebProjectUrl())}
230
+ className="editor__status-bar__workspace__workspace"
231
+ >
232
+ {gav.versionId}
233
+ </div>
234
+ <button
235
+ onClick={() => flowResult(visitWebProjectUrl())}
236
+ className="editor__status-bar__workspace__icon"
237
+ >
238
+ <GitlabIcon />
239
+ </button>
240
+ </div>
241
+ )}
158
242
  </div>
159
243
  <div className="editor__status-bar__right">
160
244
  <button
@@ -48,6 +48,10 @@ import {
48
48
  DataProductElementScope,
49
49
  validate_PureExecutionMapping,
50
50
  type V1_RawLineageModel,
51
+ type ArtifactGenerationExtensionResult,
52
+ type V1_DataProductArtifact,
53
+ type V1_AccessPointGroupInfo,
54
+ type V1_AccessPointImplementation,
51
55
  } from '@finos/legend-graph';
52
56
  import type { EditorStore } from '../../../EditorStore.js';
53
57
  import { ElementEditorState } from '../ElementEditorState.js';
@@ -129,6 +133,10 @@ export class AccessPointState {
129
133
  });
130
134
  }
131
135
 
136
+ get isRunningProcess(): boolean {
137
+ return false;
138
+ }
139
+
132
140
  changeGroupState(newGroup: AccessPointGroupState): void {
133
141
  this.state = newGroup;
134
142
  }
@@ -243,22 +251,23 @@ export class AccessPointLambdaEditorState extends LambdaEditorState {
243
251
  export class LakehouseAccessPointState extends AccessPointState {
244
252
  declare accessPoint: LakehouseAccessPoint;
245
253
  lambdaState: AccessPointLambdaEditorState;
246
-
247
- showDebug = false;
248
-
254
+ artifactGenerationContent: string | undefined;
255
+ generatingArtifactState = ActionState.create();
249
256
  // Add lineage state and isGeneratingLineage
250
257
  lineageState: LineageState;
251
- isGeneratingLineage = false;
258
+ generatingLineageAction = ActionState.create();
252
259
 
253
260
  constructor(val: LakehouseAccessPoint, editorState: AccessPointGroupState) {
254
261
  super(val, editorState);
255
262
  makeObservable(this, {
256
263
  lambdaState: observable,
257
- showDebug: observable,
258
- setShowDebug: action,
259
- // Add observables for lineage
264
+ artifactGenerationContent: observable,
265
+ generatingArtifactState: observable,
266
+ generateArtifact: flow,
267
+ isRunningProcess: computed,
260
268
  lineageState: observable,
261
- isGeneratingLineage: observable,
269
+ setArtifactContent: action,
270
+ generatingLineageAction: observable,
262
271
  generateLineage: flow,
263
272
  });
264
273
  this.accessPoint = val;
@@ -268,43 +277,102 @@ export class LakehouseAccessPointState extends AccessPointState {
268
277
  );
269
278
  }
270
279
 
271
- setShowDebug(value: boolean): void {
272
- this.showDebug = value;
280
+ get editorStore(): EditorStore {
281
+ return this.state.state.editorStore;
282
+ }
283
+
284
+ override get isRunningProcess(): boolean {
285
+ return (
286
+ this.generatingArtifactState.isInProgress ||
287
+ this.generatingLineageAction.isInProgress
288
+ );
289
+ }
290
+
291
+ setArtifactContent(val: string | undefined): void {
292
+ this.artifactGenerationContent = val;
273
293
  }
274
294
 
275
295
  *generateLineage(): GeneratorFn<void> {
276
- if (this.isGeneratingLineage) {
296
+ if (this.generatingLineageAction.isInProgress) {
277
297
  return;
278
298
  }
279
299
  try {
280
- this.isGeneratingLineage = true;
300
+ this.generatingLineageAction.inProgress();
281
301
  const lambda = this.accessPoint.func;
282
302
  const lineageRawData =
283
- (yield this.state.state.editorStore.graphManagerState.graphManager.generateLineage(
303
+ (yield this.editorStore.graphManagerState.graphManager.generateLineage(
284
304
  lambda,
285
305
  undefined,
286
306
  undefined,
287
- this.state.state.editorStore.graphManagerState.graph,
307
+ this.editorStore.graphManagerState.graph,
288
308
  undefined,
289
309
  )) as V1_RawLineageModel;
290
310
 
291
311
  const lineageData =
292
- this.state.state.editorStore.graphManagerState.graphManager.buildLineage(
312
+ this.editorStore.graphManagerState.graphManager.buildLineage(
293
313
  lineageRawData,
294
314
  );
295
315
 
296
316
  this.lineageState.setLineageData(lineageData);
317
+ this.generatingLineageAction.complete();
297
318
  } catch (error) {
298
319
  assertErrorThrown(error);
299
- this.state.state.editorStore.applicationStore.logService.error(
320
+ this.editorStore.applicationStore.logService.error(
300
321
  LogEvent.create(GRAPH_MANAGER_EVENT.LINEAGE_GENERATION_FAILURE),
301
322
  error,
302
323
  );
303
- this.state.state.editorStore.applicationStore.notificationService.notifyError(
304
- error,
324
+ this.editorStore.applicationStore.notificationService.notifyError(error);
325
+ } finally {
326
+ this.generatingLineageAction.complete();
327
+ }
328
+ }
329
+
330
+ *generateArtifact(): GeneratorFn<void> {
331
+ try {
332
+ this.generatingArtifactState.inProgress();
333
+ const generatedArtifacts =
334
+ (yield this.editorStore.graphManagerState.graphManager.generateArtifacts(
335
+ this.editorStore.graphManagerState.graph,
336
+ this.editorStore.graphEditorMode.getGraphTextInputOption(),
337
+ [this.state.state.elementPath],
338
+ )) as unknown as ArtifactGenerationExtensionResult;
339
+ const dataProductExtension = 'dataProduct';
340
+ const dataProductArtifact = generatedArtifacts.values.filter(
341
+ (artifact) => artifact.extension === dataProductExtension,
342
+ );
343
+ const dataProductContent =
344
+ dataProductArtifact[0]?.artifactsByExtensionElements[0]?.files[0]
345
+ ?.content ?? null;
346
+
347
+ if (dataProductContent) {
348
+ const contentJson = JSON.parse(
349
+ dataProductContent,
350
+ ) as V1_DataProductArtifact;
351
+ const apPlanGeneration = contentJson.accessPointGroups
352
+ .find(
353
+ (group: V1_AccessPointGroupInfo) =>
354
+ group.id === this.state.value.id,
355
+ )
356
+ ?.accessPointImplementations.find(
357
+ (apImplementation: V1_AccessPointImplementation) =>
358
+ apImplementation.id === this.accessPoint.id,
359
+ );
360
+ this.artifactGenerationContent = JSON.stringify(
361
+ apPlanGeneration,
362
+ null,
363
+ 2,
364
+ );
365
+ } else {
366
+ throw new Error(
367
+ 'Could not find contents of this data product artifact',
368
+ );
369
+ }
370
+ } catch (error) {
371
+ this.editorStore.applicationStore.notificationService.notifyError(
372
+ `Failed to fetch access point plan generation: ${error}`,
305
373
  );
306
374
  } finally {
307
- this.isGeneratingLineage = false;
375
+ this.generatingArtifactState.complete();
308
376
  }
309
377
  }
310
378
  }
@@ -332,6 +400,10 @@ export class AccessPointGroupState {
332
400
  });
333
401
  }
334
402
 
403
+ get isRunningProcess(): boolean {
404
+ return Boolean(this.accessPointStates.find((e) => e.isRunningProcess));
405
+ }
406
+
335
407
  get containsPublicStereotype(): StereotypeReference | undefined {
336
408
  return this.value.stereotypes.find(
337
409
  (stereotype) => stereotype.value === this.publicStereotype,