@finos/legend-application-studio 28.21.45 → 28.21.46

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 (138) hide show
  1. package/lib/__lib__/LegendStudioEvent.d.ts +55 -1
  2. package/lib/__lib__/LegendStudioEvent.d.ts.map +1 -1
  3. package/lib/__lib__/LegendStudioEvent.js +77 -0
  4. package/lib/__lib__/LegendStudioEvent.js.map +1 -1
  5. package/lib/__lib__/LegendStudioTelemetryHelper.d.ts +491 -1
  6. package/lib/__lib__/LegendStudioTelemetryHelper.d.ts.map +1 -1
  7. package/lib/__lib__/LegendStudioTelemetryHelper.js +594 -1
  8. package/lib/__lib__/LegendStudioTelemetryHelper.js.map +1 -1
  9. package/lib/components/editor/side-bar/WorkflowManager.d.ts.map +1 -1
  10. package/lib/components/editor/side-bar/WorkflowManager.js +15 -0
  11. package/lib/components/editor/side-bar/WorkflowManager.js.map +1 -1
  12. package/lib/index.css +1 -1
  13. package/lib/index.d.ts +1 -1
  14. package/lib/index.d.ts.map +1 -1
  15. package/lib/index.js +1 -1
  16. package/lib/index.js.map +1 -1
  17. package/lib/package.json +1 -1
  18. package/lib/stores/editor/EditorGraphState.d.ts.map +1 -1
  19. package/lib/stores/editor/EditorGraphState.js +17 -1
  20. package/lib/stores/editor/EditorGraphState.js.map +1 -1
  21. package/lib/stores/editor/EditorStore.d.ts.map +1 -1
  22. package/lib/stores/editor/EditorStore.js +10 -0
  23. package/lib/stores/editor/EditorStore.js.map +1 -1
  24. package/lib/stores/editor/EditorTabManagerState.d.ts +18 -1
  25. package/lib/stores/editor/EditorTabManagerState.d.ts.map +1 -1
  26. package/lib/stores/editor/EditorTabManagerState.js +90 -1
  27. package/lib/stores/editor/EditorTabManagerState.js.map +1 -1
  28. package/lib/stores/editor/EditorTabTelemetryHelper.d.ts +31 -0
  29. package/lib/stores/editor/EditorTabTelemetryHelper.d.ts.map +1 -0
  30. package/lib/stores/editor/EditorTabTelemetryHelper.js +55 -0
  31. package/lib/stores/editor/EditorTabTelemetryHelper.js.map +1 -0
  32. package/lib/stores/editor/GraphEditFormModeState.d.ts.map +1 -1
  33. package/lib/stores/editor/GraphEditFormModeState.js +11 -1
  34. package/lib/stores/editor/GraphEditFormModeState.js.map +1 -1
  35. package/lib/stores/editor/editor-state/GraphGenerationState.d.ts.map +1 -1
  36. package/lib/stores/editor/editor-state/GraphGenerationState.js +25 -0
  37. package/lib/stores/editor/editor-state/GraphGenerationState.js.map +1 -1
  38. package/lib/stores/editor/editor-state/ModelImporterState.d.ts.map +1 -1
  39. package/lib/stores/editor/editor-state/ModelImporterState.js +2 -1
  40. package/lib/stores/editor/editor-state/ModelImporterState.js.map +1 -1
  41. package/lib/stores/editor/editor-state/element-editor-state/ElementExternalFormatGenerationState.d.ts.map +1 -1
  42. package/lib/stores/editor/editor-state/element-editor-state/ElementExternalFormatGenerationState.js +14 -0
  43. package/lib/stores/editor/editor-state/element-editor-state/ElementExternalFormatGenerationState.js.map +1 -1
  44. package/lib/stores/editor/editor-state/element-editor-state/availability/testable/AvailabilityTestableState.d.ts +5 -0
  45. package/lib/stores/editor/editor-state/element-editor-state/availability/testable/AvailabilityTestableState.d.ts.map +1 -1
  46. package/lib/stores/editor/editor-state/element-editor-state/availability/testable/AvailabilityTestableState.js +13 -0
  47. package/lib/stores/editor/editor-state/element-editor-state/availability/testable/AvailabilityTestableState.js.map +1 -1
  48. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.d.ts +5 -0
  49. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.d.ts.map +1 -1
  50. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.js +13 -0
  51. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.js.map +1 -1
  52. package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.d.ts +7 -0
  53. package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.d.ts.map +1 -1
  54. package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.js +19 -0
  55. package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.js.map +1 -1
  56. package/lib/stores/editor/editor-state/element-editor-state/ingest/IngestTestableState.d.ts +5 -0
  57. package/lib/stores/editor/editor-state/element-editor-state/ingest/IngestTestableState.d.ts.map +1 -1
  58. package/lib/stores/editor/editor-state/element-editor-state/ingest/IngestTestableState.js +13 -0
  59. package/lib/stores/editor/editor-state/element-editor-state/ingest/IngestTestableState.js.map +1 -1
  60. package/lib/stores/editor/editor-state/element-editor-state/mapping/testable/MappingTestableState.d.ts +7 -0
  61. package/lib/stores/editor/editor-state/element-editor-state/mapping/testable/MappingTestableState.d.ts.map +1 -1
  62. package/lib/stores/editor/editor-state/element-editor-state/mapping/testable/MappingTestableState.js +34 -0
  63. package/lib/stores/editor/editor-state/element-editor-state/mapping/testable/MappingTestableState.js.map +1 -1
  64. package/lib/stores/editor/editor-state/element-editor-state/service/ServiceRegistrationState.d.ts.map +1 -1
  65. package/lib/stores/editor/editor-state/element-editor-state/service/ServiceRegistrationState.js +29 -0
  66. package/lib/stores/editor/editor-state/element-editor-state/service/ServiceRegistrationState.js.map +1 -1
  67. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestDataState.d.ts.map +1 -1
  68. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestDataState.js +2 -0
  69. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestDataState.js.map +1 -1
  70. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestEditorState.d.ts +3 -0
  71. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestEditorState.d.ts.map +1 -1
  72. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestEditorState.js +7 -0
  73. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestEditorState.js.map +1 -1
  74. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestableState.d.ts.map +1 -1
  75. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestableState.js +46 -17
  76. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestableState.js.map +1 -1
  77. package/lib/stores/editor/editor-state/element-editor-state/testable/TestableEditorState.d.ts +7 -0
  78. package/lib/stores/editor/editor-state/element-editor-state/testable/TestableEditorState.d.ts.map +1 -1
  79. package/lib/stores/editor/editor-state/element-editor-state/testable/TestableEditorState.js +110 -16
  80. package/lib/stores/editor/editor-state/element-editor-state/testable/TestableEditorState.js.map +1 -1
  81. package/lib/stores/editor/editor-state/project-configuration-editor-state/ProjectConfigurationEditorState.d.ts +2 -1
  82. package/lib/stores/editor/editor-state/project-configuration-editor-state/ProjectConfigurationEditorState.d.ts.map +1 -1
  83. package/lib/stores/editor/editor-state/project-configuration-editor-state/ProjectConfigurationEditorState.js +10 -4
  84. package/lib/stores/editor/editor-state/project-configuration-editor-state/ProjectConfigurationEditorState.js.map +1 -1
  85. package/lib/stores/editor/sidebar-state/BulkServiceRegistrationState.d.ts.map +1 -1
  86. package/lib/stores/editor/sidebar-state/BulkServiceRegistrationState.js +11 -0
  87. package/lib/stores/editor/sidebar-state/BulkServiceRegistrationState.js.map +1 -1
  88. package/lib/stores/editor/sidebar-state/ProjectOverviewState.d.ts.map +1 -1
  89. package/lib/stores/editor/sidebar-state/ProjectOverviewState.js +51 -0
  90. package/lib/stores/editor/sidebar-state/ProjectOverviewState.js.map +1 -1
  91. package/lib/stores/editor/sidebar-state/WorkflowManagerState.d.ts +14 -0
  92. package/lib/stores/editor/sidebar-state/WorkflowManagerState.d.ts.map +1 -1
  93. package/lib/stores/editor/sidebar-state/WorkflowManagerState.js +141 -26
  94. package/lib/stores/editor/sidebar-state/WorkflowManagerState.js.map +1 -1
  95. package/lib/stores/editor/sidebar-state/WorkspaceReviewState.d.ts.map +1 -1
  96. package/lib/stores/editor/sidebar-state/WorkspaceReviewState.js +41 -0
  97. package/lib/stores/editor/sidebar-state/WorkspaceReviewState.js.map +1 -1
  98. package/lib/stores/editor/sidebar-state/testable/GlobalTestRunnerState.d.ts.map +1 -1
  99. package/lib/stores/editor/sidebar-state/testable/GlobalTestRunnerState.js +71 -9
  100. package/lib/stores/editor/sidebar-state/testable/GlobalTestRunnerState.js.map +1 -1
  101. package/lib/stores/project-reviewer/ProjectReviewerStore.d.ts.map +1 -1
  102. package/lib/stores/project-reviewer/ProjectReviewerStore.js +45 -0
  103. package/lib/stores/project-reviewer/ProjectReviewerStore.js.map +1 -1
  104. package/lib/stores/workspace-setup/WorkspaceSetupStore.d.ts.map +1 -1
  105. package/lib/stores/workspace-setup/WorkspaceSetupStore.js +48 -4
  106. package/lib/stores/workspace-setup/WorkspaceSetupStore.js.map +1 -1
  107. package/package.json +9 -9
  108. package/src/__lib__/LegendStudioEvent.ts +88 -0
  109. package/src/__lib__/LegendStudioTelemetryHelper.ts +1389 -204
  110. package/src/components/editor/side-bar/WorkflowManager.tsx +24 -0
  111. package/src/index.ts +4 -1
  112. package/src/stores/editor/EditorGraphState.ts +30 -1
  113. package/src/stores/editor/EditorStore.ts +19 -0
  114. package/src/stores/editor/EditorTabManagerState.ts +128 -1
  115. package/src/stores/editor/EditorTabTelemetryHelper.ts +70 -0
  116. package/src/stores/editor/GraphEditFormModeState.ts +19 -1
  117. package/src/stores/editor/editor-state/GraphGenerationState.ts +53 -0
  118. package/src/stores/editor/editor-state/ModelImporterState.ts +2 -0
  119. package/src/stores/editor/editor-state/element-editor-state/ElementExternalFormatGenerationState.ts +30 -0
  120. package/src/stores/editor/editor-state/element-editor-state/availability/testable/AvailabilityTestableState.ts +17 -0
  121. package/src/stores/editor/editor-state/element-editor-state/dataProduct/testable/DataProductTestableState.ts +17 -0
  122. package/src/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.ts +25 -0
  123. package/src/stores/editor/editor-state/element-editor-state/ingest/IngestTestableState.ts +17 -0
  124. package/src/stores/editor/editor-state/element-editor-state/mapping/testable/MappingTestableState.ts +57 -0
  125. package/src/stores/editor/editor-state/element-editor-state/service/ServiceRegistrationState.ts +56 -0
  126. package/src/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestDataState.ts +10 -0
  127. package/src/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestEditorState.ts +9 -0
  128. package/src/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestableState.ts +78 -21
  129. package/src/stores/editor/editor-state/element-editor-state/testable/TestableEditorState.ts +205 -18
  130. package/src/stores/editor/editor-state/project-configuration-editor-state/ProjectConfigurationEditorState.ts +38 -3
  131. package/src/stores/editor/sidebar-state/BulkServiceRegistrationState.ts +27 -0
  132. package/src/stores/editor/sidebar-state/ProjectOverviewState.ts +114 -0
  133. package/src/stores/editor/sidebar-state/WorkflowManagerState.ts +259 -29
  134. package/src/stores/editor/sidebar-state/WorkspaceReviewState.ts +84 -0
  135. package/src/stores/editor/sidebar-state/testable/GlobalTestRunnerState.ts +124 -17
  136. package/src/stores/project-reviewer/ProjectReviewerStore.ts +97 -0
  137. package/src/stores/workspace-setup/WorkspaceSetupStore.ts +87 -4
  138. package/tsconfig.json +1 -0
@@ -64,6 +64,7 @@ import {
64
64
  } from '@finos/legend-shared';
65
65
  import { CODE_EDITOR_LANGUAGE } from '@finos/legend-code-editor';
66
66
  import { CodeEditor } from '@finos/legend-lego/code-editor';
67
+ import { LegendStudioTelemetryHelper } from '../../../__lib__/LegendStudioTelemetryHelper.js';
67
68
 
68
69
  const getWorkflowStatusIcon = (
69
70
  workflowStatus: WorkflowStatus,
@@ -501,6 +502,29 @@ export const WorkflowManager = observer(
501
502
  );
502
503
  }, [applicationStore, workflowManagerState]);
503
504
 
505
+ // Panel open / close telemetry — anchors a dwell time so we can measure
506
+ // engagement per surface. The mount emits `panel.open` and the cleanup
507
+ // emits `panel.close` with `dwellMs`.
508
+ useEffect(() => {
509
+ const openedAt = Date.now();
510
+ const editorStore = workflowManagerState.editorStore;
511
+ LegendStudioTelemetryHelper.logEvent_WorkflowManagerPanelOpened(
512
+ editorStore.applicationStore.telemetryService,
513
+ editorStore.editorMode.getSourceInfo(),
514
+ { scope: workflowManagerState.scope },
515
+ );
516
+ return () => {
517
+ LegendStudioTelemetryHelper.logEvent_WorkflowManagerPanelClosed(
518
+ editorStore.applicationStore.telemetryService,
519
+ editorStore.editorMode.getSourceInfo(),
520
+ {
521
+ scope: workflowManagerState.scope,
522
+ dwellMs: Date.now() - openedAt,
523
+ },
524
+ );
525
+ };
526
+ }, [workflowManagerState]);
527
+
504
528
  return (
505
529
  <div className="panel workflow-manager">
506
530
  <div className="panel__header side-bar__header">
package/src/index.ts CHANGED
@@ -70,7 +70,10 @@ export * from './stores/extensions/DSL_Data_LegendStudioApplicationPlugin_Extens
70
70
  export { DataProductEditorState } from './stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.js';
71
71
  export { ComputeEditorState } from './stores/editor/editor-state/element-editor-state/compute/ComputeEditorState.js';
72
72
  export { LEGEND_STUDIO_DOCUMENTATION_KEY } from './__lib__/LegendStudioDocumentation.js';
73
- export { LegendStudioTelemetryHelper } from './__lib__/LegendStudioTelemetryHelper.js';
73
+ export {
74
+ LegendStudioTelemetryHelper,
75
+ SERVICE_REGISTRATION_TRIGGER,
76
+ } from './__lib__/LegendStudioTelemetryHelper.js';
74
77
 
75
78
  // components
76
79
  export { queryClass } from './components/editor/editor-group/uml-editor/ClassQueryBuilder.js';
@@ -101,7 +101,10 @@ import { CONFIGURATION_EDITOR_TAB } from './editor-state/project-configuration-e
101
101
  import { PACKAGEABLE_ELEMENT_TYPE } from './utils/ModelClassifierUtils.js';
102
102
  import { LEGEND_STUDIO_APP_EVENT } from '../../__lib__/LegendStudioEvent.js';
103
103
  import { LEGEND_STUDIO_SETTING_KEY } from '../../__lib__/LegendStudioSetting.js';
104
- import { LegendStudioTelemetryHelper } from '../../__lib__/LegendStudioTelemetryHelper.js';
104
+ import {
105
+ LegendStudioTelemetryHelper,
106
+ GRAPH_INITIALIZATION_ERROR_KIND,
107
+ } from '../../__lib__/LegendStudioTelemetryHelper.js';
105
108
 
106
109
  export enum GraphBuilderStatus {
107
110
  SUCCEEDED = 'SUCCEEDED',
@@ -328,6 +331,10 @@ export class EditorGraphState {
328
331
  }
329
332
 
330
333
  *buildGraph(entities: Entity[]): GeneratorFn<GraphBuilderResult> {
334
+ LegendStudioTelemetryHelper.logEvent_GraphInitializationLaunched(
335
+ this.editorStore.applicationStore.telemetryService,
336
+ this.editorStore.editorMode.getSourceInfo(),
337
+ );
331
338
  try {
332
339
  this.isInitializingGraph = true;
333
340
  const stopWatch = new StopWatch();
@@ -430,6 +437,28 @@ export class EditorGraphState {
430
437
  LogEvent.create(GRAPH_MANAGER_EVENT.GRAPH_BUILDER_FAILURE),
431
438
  error,
432
439
  );
440
+ const errorKind =
441
+ error instanceof DependencyGraphBuilderError
442
+ ? GRAPH_INITIALIZATION_ERROR_KIND.DEPENDENCY
443
+ : error instanceof GraphDataDeserializationError
444
+ ? GRAPH_INITIALIZATION_ERROR_KIND.DESERIALIZATION
445
+ : error instanceof NetworkClientError
446
+ ? GRAPH_INITIALIZATION_ERROR_KIND.NETWORK
447
+ : GRAPH_INITIALIZATION_ERROR_KIND.OTHER;
448
+ const fallbackToTextMode = !(
449
+ error instanceof DependencyGraphBuilderError ||
450
+ error instanceof GraphDataDeserializationError ||
451
+ error instanceof NetworkClientError
452
+ );
453
+ LegendStudioTelemetryHelper.logEvent_GraphInitializationFailure(
454
+ this.editorStore.applicationStore.telemetryService,
455
+ this.editorStore.editorMode.getSourceInfo(),
456
+ {
457
+ errorKind,
458
+ errorMessage: error.message,
459
+ fallbackToTextMode,
460
+ },
461
+ );
433
462
  if (error instanceof DependencyGraphBuilderError) {
434
463
  this.editorStore.graphManagerState.graphBuildState.fail();
435
464
  // no recovery if dependency models cannot be built, this makes assumption that all dependencies models are compiled successfully
@@ -769,6 +769,17 @@ export class EditorStore implements CommandRegistrar {
769
769
  // - view project
770
770
  // - back to the setup page
771
771
  const createWorkspaceAndRelaunch = async (): Promise<void> => {
772
+ const startTime = Date.now();
773
+ const identity = {
774
+ projectId,
775
+ workspaceId,
776
+ workspaceType: workspaceType.toString(),
777
+ hasPatchReleaseVersion: patchReleaseVersionId !== undefined,
778
+ };
779
+ LegendStudioTelemetryHelper.logEvent_SdlcWorkspaceCreateLaunched(
780
+ this.applicationStore.telemetryService,
781
+ identity,
782
+ );
772
783
  try {
773
784
  this.applicationStore.alertService.setBlockingAlert({
774
785
  message: 'Creating workspace...',
@@ -784,6 +795,10 @@ export class EditorStore implements CommandRegistrar {
784
795
  this.applicationStore.notificationService.notifySuccess(
785
796
  `Workspace '${workspace.workspaceId}' is succesfully created. Reloading application...`,
786
797
  );
798
+ LegendStudioTelemetryHelper.logEvent_SdlcWorkspaceCreateSucceeded(
799
+ this.applicationStore.telemetryService,
800
+ { ...identity, durationMs: Date.now() - startTime },
801
+ );
787
802
  this.applicationStore.navigationService.navigator.reload();
788
803
  } catch (error) {
789
804
  assertErrorThrown(error);
@@ -791,6 +806,10 @@ export class EditorStore implements CommandRegistrar {
791
806
  LogEvent.create(LEGEND_STUDIO_APP_EVENT.WORKSPACE_SETUP_FAILURE),
792
807
  error,
793
808
  );
809
+ LegendStudioTelemetryHelper.logEvent_SdlcWorkspaceCreateFailure(
810
+ this.applicationStore.telemetryService,
811
+ { ...identity, errorMessage: error.message },
812
+ );
794
813
  this.applicationStore.notificationService.notifyError(error);
795
814
  }
796
815
  };
@@ -78,6 +78,12 @@ import { IngestDefinitionEditorState } from './editor-state/element-editor-state
78
78
  import { AvailabilityEditorState } from './editor-state/element-editor-state/availability/AvailabilityEditorState.js';
79
79
  import { ComputeEditorState } from './editor-state/element-editor-state/compute/ComputeEditorState.js';
80
80
  import type { EditorInitialConfiguration } from './editor-state/element-editor-state/ElementEditorInitialConfiguration.js';
81
+ import {
82
+ EDITOR_TAB_CLOSE_TRIGGER,
83
+ EDITOR_TAB_OPEN_TRIGGER,
84
+ LegendStudioTelemetryHelper,
85
+ } from '../../__lib__/LegendStudioTelemetryHelper.js';
86
+ import { getEditorTabTelemetryData } from './EditorTabTelemetryHelper.js';
81
87
 
82
88
  export class EditorTabManagerState extends TabManagerState {
83
89
  readonly editorStore: EditorStore;
@@ -106,6 +112,115 @@ export class EditorTabManagerState extends TabManagerState {
106
112
  this.editorStore = editorStore;
107
113
  }
108
114
 
115
+ /**
116
+ * Per-tab dwell accounting for {@link EDITOR_TAB__CLOSE} telemetry.
117
+ * Keyed by `TabState.uuid`. `activeSince` is set when the tab becomes the
118
+ * current tab and cleared on switch away; `accumulatedMs` is the total time
119
+ * the tab held focus. On close we flush any live `activeSince` and emit
120
+ * the total. The map is pruned on close so it does not leak.
121
+ */
122
+ private tabDwell = new Map<
123
+ string,
124
+ { activeSince: number | undefined; accumulatedMs: number }
125
+ >();
126
+
127
+ private startDwell(tab: TabState): void {
128
+ const entry = this.tabDwell.get(tab.uuid);
129
+ const now = Date.now();
130
+ if (entry) {
131
+ entry.activeSince = now;
132
+ } else {
133
+ this.tabDwell.set(tab.uuid, { activeSince: now, accumulatedMs: 0 });
134
+ }
135
+ }
136
+
137
+ private flushDwell(tab: TabState): number {
138
+ const entry = this.tabDwell.get(tab.uuid);
139
+ if (!entry) {
140
+ return 0;
141
+ }
142
+ if (entry.activeSince !== undefined) {
143
+ entry.accumulatedMs += Date.now() - entry.activeSince;
144
+ entry.activeSince = undefined;
145
+ }
146
+ return entry.accumulatedMs;
147
+ }
148
+
149
+ private emitTabClose(tab: TabState, trigger: EDITOR_TAB_CLOSE_TRIGGER): void {
150
+ const dwellMs = this.flushDwell(tab);
151
+ this.tabDwell.delete(tab.uuid);
152
+ LegendStudioTelemetryHelper.logEvent_EditorTabClosed(
153
+ this.editorStore.applicationStore.telemetryService,
154
+ this.editorStore.editorMode.getSourceInfo(),
155
+ {
156
+ ...getEditorTabTelemetryData(tab, this.editorStore),
157
+ dwellMs,
158
+ trigger,
159
+ },
160
+ );
161
+ }
162
+
163
+ override setCurrentTab(val: TabState | undefined): void {
164
+ const prev = this.currentTab;
165
+ if (prev && prev !== val) {
166
+ this.flushDwell(prev);
167
+ }
168
+ super.setCurrentTab(val);
169
+ if (val && val !== prev) {
170
+ this.startDwell(val);
171
+ }
172
+ }
173
+
174
+ override openTab(
175
+ tab: TabState,
176
+ trigger: EDITOR_TAB_OPEN_TRIGGER = EDITOR_TAB_OPEN_TRIGGER.PROGRAMMATIC,
177
+ ): void {
178
+ const isNew = !this.tabs.some((t) => t.match(tab));
179
+ super.openTab(tab);
180
+ if (isNew) {
181
+ LegendStudioTelemetryHelper.logEvent_EditorTabOpened(
182
+ this.editorStore.applicationStore.telemetryService,
183
+ this.editorStore.editorMode.getSourceInfo(),
184
+ {
185
+ ...getEditorTabTelemetryData(tab, this.editorStore),
186
+ trigger,
187
+ },
188
+ );
189
+ }
190
+ }
191
+
192
+ override closeTab(
193
+ tab: TabState,
194
+ trigger: EDITOR_TAB_CLOSE_TRIGGER = EDITOR_TAB_CLOSE_TRIGGER.USER_CLOSE,
195
+ ): void {
196
+ if (tab.isPinned) {
197
+ return;
198
+ }
199
+ // Emit before super so the tab is still known and dwell can be flushed.
200
+ this.emitTabClose(tab, trigger);
201
+ super.closeTab(tab);
202
+ }
203
+
204
+ override closeAllTabs(
205
+ trigger: EDITOR_TAB_CLOSE_TRIGGER = EDITOR_TAB_CLOSE_TRIGGER.CLOSE_ALL,
206
+ ): void {
207
+ // Only non-pinned tabs are actually closed by the base implementation.
208
+ this.tabs
209
+ .filter((tab) => !tab.isPinned)
210
+ .forEach((tab) => this.emitTabClose(tab, trigger));
211
+ super.closeAllTabs();
212
+ }
213
+
214
+ override closeAllOtherTabs(
215
+ tab: TabState,
216
+ trigger: EDITOR_TAB_CLOSE_TRIGGER = EDITOR_TAB_CLOSE_TRIGGER.CLOSE_OTHERS,
217
+ ): void {
218
+ this.tabs
219
+ .filter((t) => !t.isPinned && t !== tab)
220
+ .forEach((t) => this.emitTabClose(t, trigger));
221
+ super.closeAllOtherTabs(tab);
222
+ }
223
+
109
224
  get dndType(): string {
110
225
  return 'editor.tab-manager.tab';
111
226
  }
@@ -141,7 +256,7 @@ export class EditorTabManagerState extends TabManagerState {
141
256
  currentTabState,
142
257
  currentTabElementPath,
143
258
  };
144
- this.closeAllTabs();
259
+ this.closeAllTabs(EDITOR_TAB_CLOSE_TRIGGER.NAVIGATE_AWAY);
145
260
  }
146
261
 
147
262
  clearTabCache(): void {
@@ -273,6 +388,18 @@ export class EditorTabManagerState extends TabManagerState {
273
388
  return undefined;
274
389
  })
275
390
  .filter(isNonNullable);
391
+ // Emit OPEN telemetry for each restored tab so dwell tracking + dashboards
392
+ // treat cache-recovery as a first-class open (with trigger=restore).
393
+ this.tabs.forEach((tab) => {
394
+ LegendStudioTelemetryHelper.logEvent_EditorTabOpened(
395
+ this.editorStore.applicationStore.telemetryService,
396
+ this.editorStore.editorMode.getSourceInfo(),
397
+ {
398
+ ...getEditorTabTelemetryData(tab, this.editorStore),
399
+ trigger: EDITOR_TAB_OPEN_TRIGGER.RESTORE,
400
+ },
401
+ );
402
+ });
276
403
  this.setCurrentTab(
277
404
  this.findCurrentTab(
278
405
  this.cachedTabs.currentTabState,
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Copyright (c) 2026-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 type { TabState } from '@finos/legend-lego/application';
18
+ import {
19
+ type EditorElementKind,
20
+ EDITOR_TAB_KIND,
21
+ } from '../../__lib__/LegendStudioTelemetryHelper.js';
22
+ import { ArtifactGenerationViewerState } from './editor-state/ArtifactGenerationViewerState.js';
23
+ import { EditorDiffViewerState } from './editor-state/diff-viewer-state/EditorDiffViewerState.js';
24
+ import { ElementEditorState } from './editor-state/element-editor-state/ElementEditorState.js';
25
+ import { EndToEndWorkflowEditorState } from './editor-state/end-to-end-workflow-state/EndToEndWorkflowEditorState.js';
26
+ import { ModelImporterState } from './editor-state/ModelImporterState.js';
27
+ import { ProjectConfigurationEditorState } from './editor-state/project-configuration-editor-state/ProjectConfigurationEditorState.js';
28
+ import type { EditorStore } from './EditorStore.js';
29
+
30
+ /**
31
+ * Classifies a Studio editor {@link TabState} for telemetry.
32
+ * Returns a `tabKind` bucket and, when the tab is an element editor, the
33
+ * `elementKind` (via `EditorGraphState.getPackageableElementType`, which is
34
+ * plugin-aware and so covers extension-provided element kinds) and
35
+ * `elementPath` of the underlying packageable element.
36
+ */
37
+ export const getEditorTabTelemetryData = (
38
+ tab: TabState,
39
+ editorStore: EditorStore,
40
+ ): {
41
+ tabKind: EDITOR_TAB_KIND;
42
+ elementKind?: EditorElementKind | undefined;
43
+ elementPath?: string | undefined;
44
+ } => {
45
+ if (tab instanceof ElementEditorState) {
46
+ return {
47
+ tabKind: EDITOR_TAB_KIND.ELEMENT,
48
+ elementKind: editorStore.graphState.getPackageableElementType(
49
+ tab.element,
50
+ ),
51
+ elementPath: tab.elementPath,
52
+ };
53
+ }
54
+ if (tab instanceof EditorDiffViewerState) {
55
+ return { tabKind: EDITOR_TAB_KIND.ENTITY_DIFF };
56
+ }
57
+ if (tab instanceof ArtifactGenerationViewerState) {
58
+ return { tabKind: EDITOR_TAB_KIND.ARTIFACT_GENERATION };
59
+ }
60
+ if (tab instanceof ModelImporterState) {
61
+ return { tabKind: EDITOR_TAB_KIND.MODEL_IMPORTER };
62
+ }
63
+ if (tab instanceof ProjectConfigurationEditorState) {
64
+ return { tabKind: EDITOR_TAB_KIND.PROJECT_CONFIGURATION };
65
+ }
66
+ if (tab instanceof EndToEndWorkflowEditorState) {
67
+ return { tabKind: EDITOR_TAB_KIND.END_TO_END_WORKFLOW };
68
+ }
69
+ return { tabKind: EDITOR_TAB_KIND.OTHER };
70
+ };
@@ -52,7 +52,10 @@ import {
52
52
  graph_renameElement,
53
53
  } from '../graph-modifier/GraphModifierHelper.js';
54
54
  import { ElementEditorState } from './editor-state/element-editor-state/ElementEditorState.js';
55
- import { LegendStudioTelemetryHelper } from '../../__lib__/LegendStudioTelemetryHelper.js';
55
+ import {
56
+ FORM_MODE_COMPILATION_ERROR_KIND,
57
+ LegendStudioTelemetryHelper,
58
+ } from '../../__lib__/LegendStudioTelemetryHelper.js';
56
59
  import { GraphEditorMode } from './GraphEditorMode.js';
57
60
  import { GlobalBulkServiceRegistrationState } from './sidebar-state/BulkServiceRegistrationState.js';
58
61
  import type { EditorInitialConfiguration } from './editor-state/element-editor-state/ElementEditorInitialConfiguration.js';
@@ -548,6 +551,21 @@ export class GraphEditFormModeState extends GraphEditorMode {
548
551
  GraphCompilationOutcome.FAILED,
549
552
  );
550
553
  }
554
+ // NOTE: the `assertType(error, EngineError, ...)` above guarantees
555
+ // `error` is an `EngineError` here, so the classification only needs
556
+ // to distinguish `CompilationError` from other `EngineError`s.
557
+ LegendStudioTelemetryHelper.logEvent_GraphCompilationFailure(
558
+ this.editorStore.applicationStore.telemetryService,
559
+ this.editorStore.editorMode.getSourceInfo(),
560
+ {
561
+ errorKind:
562
+ error instanceof CompilationError
563
+ ? FORM_MODE_COMPILATION_ERROR_KIND.COMPILATION
564
+ : FORM_MODE_COMPILATION_ERROR_KIND.ENGINE,
565
+ errorMessage: error.message,
566
+ fallbackToTextMode: fallbackToTextModeForDebugging,
567
+ },
568
+ );
551
569
  } finally {
552
570
  this.editorStore.graphState.isRunningGlobalCompile = false;
553
571
  }
@@ -52,6 +52,10 @@ import { ExternalFormatState } from './ExternalFormatState.js';
52
52
  import { ExplorerTreeRootPackageLabel } from '../ExplorerTreeState.js';
53
53
  import type { DSL_Generation_LegendStudioApplicationPlugin_Extension } from '../../extensions/DSL_Generation_LegendStudioApplicationPlugin_Extension.js';
54
54
  import { LEGEND_STUDIO_APP_EVENT } from '../../../__lib__/LegendStudioEvent.js';
55
+ import {
56
+ GENERATION_MODE,
57
+ LegendStudioTelemetryHelper,
58
+ } from '../../../__lib__/LegendStudioTelemetryHelper.js';
55
59
  import {
56
60
  FileSystem_Directory,
57
61
  type FileSystemTreeNodeData,
@@ -194,6 +198,16 @@ export class DEPREACTED_GlobalFileGenerationState {
194
198
  LogEvent.create(LEGEND_STUDIO_APP_EVENT.GENERATION_FAILURE),
195
199
  error,
196
200
  );
201
+ LegendStudioTelemetryHelper.logEvent_GenerationFailure(
202
+ this.editorStore.applicationStore.telemetryService,
203
+ this.editorStore.editorMode.getSourceInfo(),
204
+ {
205
+ mode: GENERATION_MODE.GLOBAL,
206
+ enableArtifactGeneration:
207
+ this.graphGenerationState.enableArtifactGeneration,
208
+ errorMessage: error.message,
209
+ },
210
+ );
197
211
  this.editorStore.graphState.editorStore.applicationStore.notificationService.notifyError(
198
212
  `${error.message}`,
199
213
  );
@@ -310,15 +324,36 @@ export class GraphGenerationState {
310
324
  return;
311
325
  }
312
326
  this.isRunningGlobalGenerate = true;
327
+ const startedAt = Date.now();
328
+ const sourceInfo = this.editorStore.editorMode.getSourceInfo();
329
+ const telemetryBase = {
330
+ mode: GENERATION_MODE.GLOBAL,
331
+ enableArtifactGeneration: this.enableArtifactGeneration,
332
+ };
333
+ LegendStudioTelemetryHelper.logEvent_GenerationLaunched(
334
+ this.editorStore.applicationStore.telemetryService,
335
+ sourceInfo,
336
+ telemetryBase,
337
+ );
313
338
  try {
314
339
  yield flowResult(this.generateModels());
315
340
  yield flowResult(this.generateArtifacts());
341
+ LegendStudioTelemetryHelper.logEvent_GenerationSucceeded(
342
+ this.editorStore.applicationStore.telemetryService,
343
+ sourceInfo,
344
+ { ...telemetryBase, durationMs: Date.now() - startedAt },
345
+ );
316
346
  } catch (error) {
317
347
  assertErrorThrown(error);
318
348
  this.editorStore.applicationStore.logService.error(
319
349
  LogEvent.create(LEGEND_STUDIO_APP_EVENT.GENERATION_FAILURE),
320
350
  error,
321
351
  );
352
+ LegendStudioTelemetryHelper.logEvent_GenerationFailure(
353
+ this.editorStore.applicationStore.telemetryService,
354
+ sourceInfo,
355
+ { ...telemetryBase, errorMessage: error.message },
356
+ );
322
357
  this.editorStore.graphState.editorStore.applicationStore.notificationService.notifyError(
323
358
  `${error.message}`,
324
359
  );
@@ -375,6 +410,15 @@ export class GraphGenerationState {
375
410
  LogEvent.create(LEGEND_STUDIO_APP_EVENT.GENERATION_FAILURE),
376
411
  error,
377
412
  );
413
+ LegendStudioTelemetryHelper.logEvent_GenerationFailure(
414
+ this.editorStore.applicationStore.telemetryService,
415
+ this.editorStore.editorMode.getSourceInfo(),
416
+ {
417
+ mode: GENERATION_MODE.GLOBAL,
418
+ enableArtifactGeneration: this.enableArtifactGeneration,
419
+ errorMessage: error.message,
420
+ },
421
+ );
378
422
  this.editorStore.graphState.editorStore.applicationStore.notificationService.notifyError(
379
423
  `${error.message}`,
380
424
  );
@@ -412,6 +456,15 @@ export class GraphGenerationState {
412
456
  LogEvent.create(LEGEND_STUDIO_APP_EVENT.GENERATION_FAILURE),
413
457
  error,
414
458
  );
459
+ LegendStudioTelemetryHelper.logEvent_GenerationFailure(
460
+ this.editorStore.applicationStore.telemetryService,
461
+ this.editorStore.editorMode.getSourceInfo(),
462
+ {
463
+ mode: GENERATION_MODE.GLOBAL,
464
+ enableArtifactGeneration: this.enableArtifactGeneration,
465
+ errorMessage: error.message,
466
+ },
467
+ );
415
468
  this.editorStore.graphState.editorStore.applicationStore.notificationService.notifyError(
416
469
  `${error.message}`,
417
470
  );
@@ -33,6 +33,7 @@ import {
33
33
  assertTrue,
34
34
  } from '@finos/legend-shared';
35
35
  import { LEGEND_STUDIO_APP_EVENT } from '../../../__lib__/LegendStudioEvent.js';
36
+ import { PROJECT_CONFIG_UPDATE_ACTION } from '../../../__lib__/LegendStudioTelemetryHelper.js';
36
37
  import type { EditorStore } from '../EditorStore.js';
37
38
  import { generateGAVCoordinates, type Entity } from '@finos/legend-storage';
38
39
  import { DEFAULT_TAB_SIZE } from '@finos/legend-application';
@@ -379,6 +380,7 @@ export class ExecuteInputDebugModelImporterEditorState extends ModelImporterEdit
379
380
  await flowResult(
380
381
  this.editorStore.projectConfigurationEditorState.updateProjectConfiguration(
381
382
  updateProjectConfigurationCommand,
383
+ PROJECT_CONFIG_UPDATE_ACTION.UPDATE_CONFIGS,
382
384
  ),
383
385
  );
384
386
  // open the debugger element
@@ -37,6 +37,10 @@ import {
37
37
  import { action, flow, flowResult, makeObservable, observable } from 'mobx';
38
38
  import type { EditorStore } from '../../EditorStore.js';
39
39
  import { LEGEND_STUDIO_APP_EVENT } from '../../../../__lib__/LegendStudioEvent.js';
40
+ import {
41
+ GENERATION_MODE,
42
+ LegendStudioTelemetryHelper,
43
+ } from '../../../../__lib__/LegendStudioTelemetryHelper.js';
40
44
  import { GENERATION_FILE_ROOT_NAME } from '../../utils/FileSystemTreeUtils.js';
41
45
  import {
42
46
  configurationProperty_addConfigurationProperty,
@@ -99,6 +103,22 @@ export class XTGenerationState extends GeneratedFileStructureState {
99
103
 
100
104
  *generate(): GeneratorFn<void> {
101
105
  this.generatingAction.inProgress();
106
+ const startedAt = Date.now();
107
+ const sourceInfo = this.editorStore.editorMode.getSourceInfo();
108
+ const generationType = this.elementXTSchemaGenerationState.description.name;
109
+ const elementPath =
110
+ this.configSpecification.modelUnit.packageableElementIncludes[0]?.value
111
+ .path;
112
+ const telemetryBase = {
113
+ mode: GENERATION_MODE.ELEMENT_SCHEMA,
114
+ elementPath,
115
+ generationType,
116
+ };
117
+ LegendStudioTelemetryHelper.logEvent_GenerationLaunched(
118
+ this.editorStore.applicationStore.telemetryService,
119
+ sourceInfo,
120
+ telemetryBase,
121
+ );
102
122
  try {
103
123
  const modelUnit = this.configSpecification.modelUnit;
104
124
  const properties = [...this.configSpecification.configurationProperties];
@@ -122,6 +142,11 @@ export class XTGenerationState extends GeneratedFileStructureState {
122
142
  )
123
143
  .flat();
124
144
  this.processGenerationResult(output);
145
+ LegendStudioTelemetryHelper.logEvent_GenerationSucceeded(
146
+ this.editorStore.applicationStore.telemetryService,
147
+ sourceInfo,
148
+ { ...telemetryBase, durationMs: Date.now() - startedAt },
149
+ );
125
150
  } catch (error) {
126
151
  assertErrorThrown(error);
127
152
  this.fileSystemState.selectedNode = undefined;
@@ -130,6 +155,11 @@ export class XTGenerationState extends GeneratedFileStructureState {
130
155
  LogEvent.create(LEGEND_STUDIO_APP_EVENT.GENERATION_FAILURE),
131
156
  error,
132
157
  );
158
+ LegendStudioTelemetryHelper.logEvent_GenerationFailure(
159
+ this.editorStore.applicationStore.telemetryService,
160
+ sourceInfo,
161
+ { ...telemetryBase, errorMessage: error.message },
162
+ );
133
163
  this.editorStore.applicationStore.notificationService.notifyError(error);
134
164
  } finally {
135
165
  this.generatingAction.complete();
@@ -50,6 +50,7 @@ import {
50
50
  TestableTestEditorState,
51
51
  TestableTestSuiteEditorState,
52
52
  } from '../../testable/TestableEditorState.js';
53
+ import { TESTABLE_KIND } from '../../../../../../__lib__/LegendStudioTelemetryHelper.js';
53
54
  import { RelationElementState } from '../../data/EmbeddedDataState.js';
54
55
  import { atomicTest_addAssertion } from '../../../../../graph-modifier/Testable_GraphModifierHelper.js';
55
56
  import { externalFormatData_setData } from '../../../../../graph-modifier/DSL_Data_GraphModifierHelper.js';
@@ -345,6 +346,14 @@ export class AvailabilityTestState extends TestableTestEditorState {
345
346
  readonly suiteState: AvailabilityTestSuiteState;
346
347
  override test: AvailabilityBarrierTest;
347
348
 
349
+ override get testableKind(): TESTABLE_KIND {
350
+ return TESTABLE_KIND.AVAILABILITY;
351
+ }
352
+
353
+ override get testablePath(): string {
354
+ return this.suiteState.testableState.availability.path;
355
+ }
356
+
348
357
  constructor(
349
358
  suiteState: AvailabilityTestSuiteState,
350
359
  test: AvailabilityBarrierTest,
@@ -436,6 +445,14 @@ export class AvailabilityTestSuiteState extends TestableTestSuiteEditorState {
436
445
  testToRename: AvailabilityBarrierTest | undefined;
437
446
  readonly testDataState: RelationElementState;
438
447
 
448
+ override get testableKind(): TESTABLE_KIND {
449
+ return TESTABLE_KIND.AVAILABILITY;
450
+ }
451
+
452
+ override get testablePath(): string {
453
+ return this.testableState.availability.path;
454
+ }
455
+
439
456
  constructor(
440
457
  testableState: AvailabilityTestableState,
441
458
  suite: AvailabilityTestSuite,
@@ -77,6 +77,7 @@ import {
77
77
  TestableTestEditorState,
78
78
  TestableTestSuiteEditorState,
79
79
  } from '../../testable/TestableEditorState.js';
80
+ import { TESTABLE_KIND } from '../../../../../../__lib__/LegendStudioTelemetryHelper.js';
80
81
  import { generateVariableExpressionMockValue } from '@finos/legend-query-builder';
81
82
  import {
82
83
  buildRelationElementsDataWithColumns,
@@ -208,6 +209,14 @@ export class DataProductTestState extends TestableTestEditorState {
208
209
  newParameterValueName = '';
209
210
  showNewParameterModal = false;
210
211
 
212
+ override get testableKind(): TESTABLE_KIND {
213
+ return TESTABLE_KIND.DATA_PRODUCT;
214
+ }
215
+
216
+ override get testablePath(): string {
217
+ return this.suiteState.testableState.dataProduct.path;
218
+ }
219
+
211
220
  constructor(
212
221
  suiteState: DataProductTestSuiteState,
213
222
  test: DataProductAccessPointTest,
@@ -637,6 +646,14 @@ export class DataProductTestSuiteState extends TestableTestSuiteEditorState {
637
646
  declare selectTestState: DataProductTestState | undefined;
638
647
  testDataState: DataProductTestDataState;
639
648
 
649
+ override get testableKind(): TESTABLE_KIND {
650
+ return TESTABLE_KIND.DATA_PRODUCT;
651
+ }
652
+
653
+ override get testablePath(): string {
654
+ return this.testableState.dataProduct.path;
655
+ }
656
+
640
657
  constructor(
641
658
  editorStore: EditorStore,
642
659
  testableState: DataProductTestableState,