@finos/legend-application-studio 28.21.46 → 28.21.47

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 (122) hide show
  1. package/lib/__lib__/LegendStudioEvent.d.ts +8 -0
  2. package/lib/__lib__/LegendStudioEvent.d.ts.map +1 -1
  3. package/lib/__lib__/LegendStudioEvent.js +13 -0
  4. package/lib/__lib__/LegendStudioEvent.js.map +1 -1
  5. package/lib/__lib__/LegendStudioTelemetryHelper.d.ts +170 -6
  6. package/lib/__lib__/LegendStudioTelemetryHelper.d.ts.map +1 -1
  7. package/lib/__lib__/LegendStudioTelemetryHelper.js +116 -1
  8. package/lib/__lib__/LegendStudioTelemetryHelper.js.map +1 -1
  9. package/lib/components/editor/editor-group/LegendAISuggestTelemetryHooks.d.ts +30 -0
  10. package/lib/components/editor/editor-group/LegendAISuggestTelemetryHooks.d.ts.map +1 -0
  11. package/lib/components/editor/editor-group/LegendAISuggestTelemetryHooks.js +43 -0
  12. package/lib/components/editor/editor-group/LegendAISuggestTelemetryHooks.js.map +1 -0
  13. package/lib/components/editor/editor-group/RuntimeEditor.d.ts +4 -3
  14. package/lib/components/editor/editor-group/RuntimeEditor.d.ts.map +1 -1
  15. package/lib/components/editor/editor-group/RuntimeEditor.js +90 -36
  16. package/lib/components/editor/editor-group/RuntimeEditor.js.map +1 -1
  17. package/lib/components/editor/editor-group/dataProduct/DataProductEditor.d.ts.map +1 -1
  18. package/lib/components/editor/editor-group/dataProduct/DataProductEditor.js +154 -35
  19. package/lib/components/editor/editor-group/dataProduct/DataProductEditor.js.map +1 -1
  20. package/lib/components/editor/editor-group/dataProduct/DataProductQueryBuilderHelper.d.ts.map +1 -1
  21. package/lib/components/editor/editor-group/dataProduct/DataProductQueryBuilderHelper.js +4 -6
  22. package/lib/components/editor/editor-group/dataProduct/DataProductQueryBuilderHelper.js.map +1 -1
  23. package/lib/components/editor/editor-group/service-editor/ServiceEditor.d.ts.map +1 -1
  24. package/lib/components/editor/editor-group/service-editor/ServiceEditor.js +31 -7
  25. package/lib/components/editor/editor-group/service-editor/ServiceEditor.js.map +1 -1
  26. package/lib/components/editor/editor-group/service-editor/testable/ServiceTestDataEditor.d.ts.map +1 -1
  27. package/lib/components/editor/editor-group/service-editor/testable/ServiceTestDataEditor.js +4 -8
  28. package/lib/components/editor/editor-group/service-editor/testable/ServiceTestDataEditor.js.map +1 -1
  29. package/lib/index.css +1 -1
  30. package/lib/index.d.ts +3 -1
  31. package/lib/index.d.ts.map +1 -1
  32. package/lib/index.js +3 -1
  33. package/lib/index.js.map +1 -1
  34. package/lib/package.json +1 -1
  35. package/lib/stores/editor/EditorStore.d.ts +2 -0
  36. package/lib/stores/editor/EditorStore.d.ts.map +1 -1
  37. package/lib/stores/editor/EditorStore.js +2 -0
  38. package/lib/stores/editor/EditorStore.js.map +1 -1
  39. package/lib/stores/editor/GraphEditFormModeState.d.ts.map +1 -1
  40. package/lib/stores/editor/GraphEditFormModeState.js +21 -10
  41. package/lib/stores/editor/GraphEditFormModeState.js.map +1 -1
  42. package/lib/stores/editor/LegendAISuggestTelemetry.d.ts +143 -0
  43. package/lib/stores/editor/LegendAISuggestTelemetry.d.ts.map +1 -0
  44. package/lib/stores/editor/LegendAISuggestTelemetry.js +376 -0
  45. package/lib/stores/editor/LegendAISuggestTelemetry.js.map +1 -0
  46. package/lib/stores/editor/LegendSQLStudioPlaygroundState.js +3 -3
  47. package/lib/stores/editor/LegendSQLStudioPlaygroundState.js.map +1 -1
  48. package/lib/stores/editor/editor-state/GraphGenerationState.d.ts +14 -3
  49. package/lib/stores/editor/editor-state/GraphGenerationState.d.ts.map +1 -1
  50. package/lib/stores/editor/editor-state/GraphGenerationState.js +33 -21
  51. package/lib/stores/editor/editor-state/GraphGenerationState.js.map +1 -1
  52. package/lib/stores/editor/editor-state/element-editor-state/RuntimeEditorState.d.ts +35 -6
  53. package/lib/stores/editor/editor-state/element-editor-state/RuntimeEditorState.d.ts.map +1 -1
  54. package/lib/stores/editor/editor-state/element-editor-state/RuntimeEditorState.js +74 -29
  55. package/lib/stores/editor/editor-state/element-editor-state/RuntimeEditorState.js.map +1 -1
  56. package/lib/stores/editor/editor-state/element-editor-state/connection/DatabaseBuilderState.d.ts +1 -0
  57. package/lib/stores/editor/editor-state/element-editor-state/connection/DatabaseBuilderState.d.ts.map +1 -1
  58. package/lib/stores/editor/editor-state/element-editor-state/connection/DatabaseBuilderState.js +5 -5
  59. package/lib/stores/editor/editor-state/element-editor-state/connection/DatabaseBuilderState.js.map +1 -1
  60. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.d.ts +12 -0
  61. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.d.ts.map +1 -1
  62. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.js +60 -14
  63. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.js.map +1 -1
  64. package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.js +2 -2
  65. package/lib/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.js.map +1 -1
  66. package/lib/stores/editor/editor-state/element-editor-state/service/ServiceRegistrationState.d.ts.map +1 -1
  67. package/lib/stores/editor/editor-state/element-editor-state/service/ServiceRegistrationState.js +34 -15
  68. package/lib/stores/editor/editor-state/element-editor-state/service/ServiceRegistrationState.js.map +1 -1
  69. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestDataState.d.ts +1 -1
  70. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestDataState.d.ts.map +1 -1
  71. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestDataState.js +3 -11
  72. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestDataState.js.map +1 -1
  73. package/lib/stores/editor/sidebar-state/BulkServiceRegistrationState.d.ts.map +1 -1
  74. package/lib/stores/editor/sidebar-state/BulkServiceRegistrationState.js +30 -2
  75. package/lib/stores/editor/sidebar-state/BulkServiceRegistrationState.js.map +1 -1
  76. package/lib/stores/editor/sidebar-state/LocalChangesState.d.ts.map +1 -1
  77. package/lib/stores/editor/sidebar-state/LocalChangesState.js +1 -0
  78. package/lib/stores/editor/sidebar-state/LocalChangesState.js.map +1 -1
  79. package/lib/stores/editor/sidebar-state/WorkflowManagerState.d.ts +1 -1
  80. package/lib/stores/editor/sidebar-state/WorkflowManagerState.d.ts.map +1 -1
  81. package/lib/stores/editor/sidebar-state/WorkflowManagerState.js +2 -12
  82. package/lib/stores/editor/sidebar-state/WorkflowManagerState.js.map +1 -1
  83. package/lib/stores/editor/utils/MockDataUtils.d.ts +12 -2
  84. package/lib/stores/editor/utils/MockDataUtils.d.ts.map +1 -1
  85. package/lib/stores/editor/utils/MockDataUtils.js +28 -28
  86. package/lib/stores/editor/utils/MockDataUtils.js.map +1 -1
  87. package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.d.ts +2 -0
  88. package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.d.ts.map +1 -1
  89. package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.js +13 -0
  90. package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.js.map +1 -1
  91. package/lib/stores/graph-modifier/DSL_LakehouseRuntime_GraphModifierHelper.d.ts +20 -2
  92. package/lib/stores/graph-modifier/DSL_LakehouseRuntime_GraphModifierHelper.d.ts.map +1 -1
  93. package/lib/stores/graph-modifier/DSL_LakehouseRuntime_GraphModifierHelper.js +38 -4
  94. package/lib/stores/graph-modifier/DSL_LakehouseRuntime_GraphModifierHelper.js.map +1 -1
  95. package/package.json +9 -9
  96. package/src/__lib__/LegendStudioEvent.ts +14 -0
  97. package/src/__lib__/LegendStudioTelemetryHelper.ts +277 -4
  98. package/src/components/editor/editor-group/LegendAISuggestTelemetryHooks.ts +57 -0
  99. package/src/components/editor/editor-group/RuntimeEditor.tsx +282 -132
  100. package/src/components/editor/editor-group/dataProduct/DataProductEditor.tsx +293 -106
  101. package/src/components/editor/editor-group/dataProduct/DataProductQueryBuilderHelper.ts +3 -13
  102. package/src/components/editor/editor-group/service-editor/ServiceEditor.tsx +41 -23
  103. package/src/components/editor/editor-group/service-editor/testable/ServiceTestDataEditor.tsx +5 -11
  104. package/src/index.ts +13 -0
  105. package/src/stores/editor/EditorStore.ts +3 -0
  106. package/src/stores/editor/GraphEditFormModeState.ts +31 -15
  107. package/src/stores/editor/LegendAISuggestTelemetry.ts +592 -0
  108. package/src/stores/editor/LegendSQLStudioPlaygroundState.ts +3 -3
  109. package/src/stores/editor/editor-state/GraphGenerationState.ts +48 -42
  110. package/src/stores/editor/editor-state/element-editor-state/RuntimeEditorState.ts +92 -31
  111. package/src/stores/editor/editor-state/element-editor-state/connection/DatabaseBuilderState.ts +6 -6
  112. package/src/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.ts +75 -17
  113. package/src/stores/editor/editor-state/element-editor-state/function-activator/testable/FunctionTestableState.ts +2 -2
  114. package/src/stores/editor/editor-state/element-editor-state/service/ServiceRegistrationState.ts +41 -23
  115. package/src/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestDataState.ts +12 -21
  116. package/src/stores/editor/sidebar-state/BulkServiceRegistrationState.ts +49 -6
  117. package/src/stores/editor/sidebar-state/LocalChangesState.ts +4 -0
  118. package/src/stores/editor/sidebar-state/WorkflowManagerState.ts +3 -12
  119. package/src/stores/editor/utils/MockDataUtils.ts +33 -32
  120. package/src/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.ts +18 -0
  121. package/src/stores/graph-modifier/DSL_LakehouseRuntime_GraphModifierHelper.ts +62 -7
  122. package/tsconfig.json +2 -0
@@ -153,15 +153,19 @@ export class DEPREACTED_GlobalFileGenerationState {
153
153
  /**
154
154
  * Generated file generations in the graph.
155
155
  * NOTE: This method does not update graph and application only the files are generated.
156
+ *
157
+ * Errors are handled (logged + notified) here and returned rather than
158
+ * re-thrown so the caller can keep generating; the caller owns the
159
+ * `editor.generation.*` telemetry outcome.
156
160
  */
157
161
  *DEPREACTED_generateFiles(
158
162
  generationOutputIndex: Map<string, GenerationOutput[]>,
159
- ): GeneratorFn<void> {
163
+ ): GeneratorFn<Error | undefined> {
160
164
  try {
161
165
  const generationSpecs =
162
166
  this.editorStore.graphManagerState.graph.ownGenerationSpecifications;
163
167
  if (!generationSpecs.length) {
164
- return;
168
+ return undefined;
165
169
  }
166
170
  assertTrue(
167
171
  generationSpecs.length === 1,
@@ -192,25 +196,17 @@ export class DEPREACTED_GlobalFileGenerationState {
192
196
  }
193
197
  generationOutputIndex.set(fileGeneration.value.path, result);
194
198
  }
199
+ return undefined;
195
200
  } catch (error) {
196
201
  assertErrorThrown(error);
197
202
  this.editorStore.applicationStore.logService.error(
198
203
  LogEvent.create(LEGEND_STUDIO_APP_EVENT.GENERATION_FAILURE),
199
204
  error,
200
205
  );
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
- );
211
206
  this.editorStore.graphState.editorStore.applicationStore.notificationService.notifyError(
212
207
  `${error.message}`,
213
208
  );
209
+ return error;
214
210
  }
215
211
  }
216
212
 
@@ -336,13 +332,30 @@ export class GraphGenerationState {
336
332
  telemetryBase,
337
333
  );
338
334
  try {
339
- yield flowResult(this.generateModels());
340
- yield flowResult(this.generateArtifacts());
341
- LegendStudioTelemetryHelper.logEvent_GenerationSucceeded(
342
- this.editorStore.applicationStore.telemetryService,
343
- sourceInfo,
344
- { ...telemetryBase, durationMs: Date.now() - startedAt },
345
- );
335
+ // NOTE: both steps handle (log + notify) their own errors and return
336
+ // them instead of throwing, so artifact generation still runs when
337
+ // model generation fails. We only decide the telemetry outcome here so
338
+ // each run emits exactly one terminal event.
339
+ const modelGenerationError = (yield flowResult(this.generateModels())) as
340
+ | Error
341
+ | undefined;
342
+ const artifactGenerationError = (yield flowResult(
343
+ this.generateArtifacts(),
344
+ )) as Error | undefined;
345
+ const generationError = modelGenerationError ?? artifactGenerationError;
346
+ if (generationError) {
347
+ LegendStudioTelemetryHelper.logEvent_GenerationFailure(
348
+ this.editorStore.applicationStore.telemetryService,
349
+ sourceInfo,
350
+ { ...telemetryBase, errorMessage: generationError.message },
351
+ );
352
+ } else {
353
+ LegendStudioTelemetryHelper.logEvent_GenerationSucceeded(
354
+ this.editorStore.applicationStore.telemetryService,
355
+ sourceInfo,
356
+ { ...telemetryBase, durationMs: Date.now() - startedAt },
357
+ );
358
+ }
346
359
  } catch (error) {
347
360
  assertErrorThrown(error);
348
361
  this.editorStore.applicationStore.logService.error(
@@ -362,13 +375,17 @@ export class GraphGenerationState {
362
375
  }
363
376
  }
364
377
 
365
- *generateModels(): GeneratorFn<void> {
378
+ /**
379
+ * Errors are handled (logged + notified) here and returned rather than
380
+ * re-thrown; see {@link GraphGenerationState.globalGenerate}.
381
+ */
382
+ *generateModels(): GeneratorFn<Error | undefined> {
366
383
  try {
367
384
  this.generatedEntities = new Map<string, Entity[]>(); // reset the map of generated entities
368
385
  const generationSpecs =
369
386
  this.editorStore.graphManagerState.graph.ownGenerationSpecifications;
370
387
  if (!generationSpecs.length) {
371
- return;
388
+ return undefined;
372
389
  }
373
390
  assertTrue(
374
391
  generationSpecs.length === 1,
@@ -404,41 +421,37 @@ export class GraphGenerationState {
404
421
  this.editorStore.graphState.updateGenerationGraphAndApplication(),
405
422
  );
406
423
  }
424
+ return undefined;
407
425
  } catch (error) {
408
426
  assertErrorThrown(error);
409
427
  this.editorStore.applicationStore.logService.error(
410
428
  LogEvent.create(LEGEND_STUDIO_APP_EVENT.GENERATION_FAILURE),
411
429
  error,
412
430
  );
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
- );
422
431
  this.editorStore.graphState.editorStore.applicationStore.notificationService.notifyError(
423
432
  `${error.message}`,
424
433
  );
434
+ return error;
425
435
  }
426
436
  }
427
437
 
428
438
  /**
429
439
  * Generated artifacts generations in graph
430
440
  * NOTE: This method does not update graph and application only the files are generated.
441
+ *
442
+ * Errors are handled (logged + notified) here and returned rather than
443
+ * re-thrown; see {@link GraphGenerationState.globalGenerate}.
431
444
  */
432
- *generateArtifacts(): GeneratorFn<void> {
445
+ *generateArtifacts(): GeneratorFn<Error | undefined> {
433
446
  try {
434
447
  this.emptyGeneratedArtifacts();
435
448
  const generationOutputIndex = new Map<string, GenerationOutput[]>();
436
449
  // handle deprecated file generations
437
- yield flowResult(
450
+ const fileGenerationError = (yield flowResult(
438
451
  this.globalFileGenerationState.DEPREACTED_generateFiles(
439
452
  generationOutputIndex,
440
453
  ),
441
- );
454
+ )) as Error | undefined;
442
455
  let artifacts = new ArtifactGenerationExtensionResult();
443
456
  if (this.enableArtifactGeneration) {
444
457
  artifacts =
@@ -450,24 +463,17 @@ export class GraphGenerationState {
450
463
 
451
464
  // handle results
452
465
  this.processGenerationResult(artifacts, generationOutputIndex);
466
+ return fileGenerationError;
453
467
  } catch (error) {
454
468
  assertErrorThrown(error);
455
469
  this.editorStore.applicationStore.logService.error(
456
470
  LogEvent.create(LEGEND_STUDIO_APP_EVENT.GENERATION_FAILURE),
457
471
  error,
458
472
  );
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
- );
468
473
  this.editorStore.graphState.editorStore.applicationStore.notificationService.notifyError(
469
474
  `${error.message}`,
470
475
  );
476
+ return error;
471
477
  }
472
478
  }
473
479
 
@@ -68,7 +68,9 @@ import {
68
68
  isStubbed_StoreConnections,
69
69
  getAllIdentifiedConnections,
70
70
  generateIdentifiedConnectionId,
71
+ type LakehouseBaseRuntime,
71
72
  LakehouseRuntime,
73
+ LakehouseSingleStoreRuntime,
72
74
  ConcreteFunctionDefinition,
73
75
  } from '@finos/legend-graph';
74
76
  import type { DSL_Mapping_LegendStudioApplicationPlugin_Extension } from '../../../extensions/DSL_Mapping_LegendStudioApplicationPlugin_Extension.js';
@@ -899,42 +901,36 @@ export enum LakehouseRuntimeType {
899
901
  CONNECTION = 'CONNECTION',
900
902
  }
901
903
 
902
- export class LakehouseRuntimeEditorState extends EngineRuntimeEditorState {
903
- declare runtimeValue: LakehouseRuntime;
904
+ export abstract class LakehouseBaseRuntimeEditorState extends EngineRuntimeEditorState {
905
+ /**
906
+ * NOTE: a fresh instance is built by `RuntimeEditorState.setRuntimeValueEditorState`
907
+ * whenever the compute engine swap replaces the concrete runtime class, so this
908
+ * changes exactly when that swap happens -- used to force a remount of the
909
+ * compute-engine-specific fields (e.g. Warehouse) in the UI.
910
+ */
911
+ readonly uuid = uuid();
912
+ declare runtimeValue: LakehouseBaseRuntime;
904
913
  availableEnvs: IngestDeploymentServerConfig[] | undefined;
914
+ /**
915
+ * Only `LakehouseRuntime` can switch out of environment mode today; a
916
+ * subclass whose runtime gains connection support just needs to set this.
917
+ */
905
918
  lakehouseRuntimeType = LakehouseRuntimeType.ENVIRONMENT;
906
919
 
907
- constructor(state: RuntimeEditorState, value: LakehouseRuntime) {
920
+ constructor(state: RuntimeEditorState, value: LakehouseBaseRuntime) {
908
921
  super(state, value);
909
922
  makeObservable(this, {
910
923
  availableEnvs: observable,
924
+ lakehouseRuntimeType: observable,
911
925
  fetchLakehouseSummaries: flow,
912
926
  setEnvSummaries: action,
913
- lakehouseRuntimeType: observable,
914
- setLakehouseRuntimeType: action,
915
927
  envOptions: computed,
916
928
  });
917
929
  this.runtimeValue = value;
918
- // fix when metamodel is more clear on this
919
- if (value.connectionPointer) {
920
- this.lakehouseRuntimeType = LakehouseRuntimeType.CONNECTION;
921
- }
922
- }
923
-
924
- setLakehouseRuntimeType(val: LakehouseRuntimeType): void {
925
- if (val !== this.lakehouseRuntimeType) {
926
- this.lakehouseRuntimeType = val;
927
- if (val === LakehouseRuntimeType.CONNECTION) {
928
- this.runtimeValue.environment = undefined;
929
- this.runtimeValue.warehouse = undefined;
930
- } else {
931
- this.setConnection(undefined);
932
- }
933
- }
934
930
  }
935
931
 
936
- setConnection(val: PackageableConnection | undefined): void {
937
- lakehouseRuntime_setConnection(this.runtimeValue, val);
932
+ protected get isEnvironmentModeActive(): boolean {
933
+ return this.lakehouseRuntimeType === LakehouseRuntimeType.ENVIRONMENT;
938
934
  }
939
935
 
940
936
  get envOptions(): { label: string; value: string }[] {
@@ -972,7 +968,7 @@ export class LakehouseRuntimeEditorState extends EngineRuntimeEditorState {
972
968
  )) as unknown as IngestDeploymentServerConfig[] | undefined;
973
969
  this.setEnvSummaries(res);
974
970
  if (
975
- this.lakehouseRuntimeType === LakehouseRuntimeType.ENVIRONMENT &&
971
+ this.isEnvironmentModeActive &&
976
972
  !this.runtimeValue.environment &&
977
973
  this.envOptions.length
978
974
  ) {
@@ -985,6 +981,40 @@ export class LakehouseRuntimeEditorState extends EngineRuntimeEditorState {
985
981
  }
986
982
  }
987
983
 
984
+ export class LakehouseSingleStoreRuntimeEditorState extends LakehouseBaseRuntimeEditorState {
985
+ declare runtimeValue: LakehouseSingleStoreRuntime;
986
+ }
987
+
988
+ export class LakehouseRuntimeEditorState extends LakehouseBaseRuntimeEditorState {
989
+ declare runtimeValue: LakehouseRuntime;
990
+
991
+ constructor(state: RuntimeEditorState, value: LakehouseRuntime) {
992
+ super(state, value);
993
+ makeObservable(this, {
994
+ setLakehouseRuntimeType: action,
995
+ });
996
+ if (value.connectionPointer) {
997
+ this.lakehouseRuntimeType = LakehouseRuntimeType.CONNECTION;
998
+ }
999
+ }
1000
+
1001
+ setLakehouseRuntimeType(val: LakehouseRuntimeType): void {
1002
+ if (val !== this.lakehouseRuntimeType) {
1003
+ this.lakehouseRuntimeType = val;
1004
+ if (val === LakehouseRuntimeType.CONNECTION) {
1005
+ this.runtimeValue.environment = undefined;
1006
+ this.runtimeValue.warehouse = undefined;
1007
+ } else {
1008
+ this.setConnection(undefined);
1009
+ }
1010
+ }
1011
+ }
1012
+
1013
+ setConnection(val: PackageableConnection | undefined): void {
1014
+ lakehouseRuntime_setConnection(this.runtimeValue, val);
1015
+ }
1016
+ }
1017
+
988
1018
  export class RuntimeEditorState {
989
1019
  /**
990
1020
  * NOTE: used to force component remount on state change
@@ -1002,20 +1032,51 @@ export class RuntimeEditorState {
1002
1032
  ) {
1003
1033
  makeObservable(this, {
1004
1034
  runtimeValueEditorState: observable,
1035
+ setRuntimeValueEditorState: action,
1005
1036
  });
1006
1037
 
1007
1038
  this.editorStore = editorStore;
1008
1039
  this.runtime = runtime;
1009
1040
  this.isEmbeddedRuntime = isEmbeddedRuntime;
1010
- const runtimeValue =
1011
- runtime instanceof RuntimePointer
1012
- ? runtime.packageableRuntime.value.runtimeValue
1013
- : guaranteeType(runtime, EngineRuntime);
1014
- this.runtimeValueEditorState =
1015
- runtimeValue instanceof LakehouseRuntime
1016
- ? new LakehouseRuntimeEditorState(this, runtimeValue)
1041
+ this.runtimeValueEditorState = this.buildRuntimeValueEditorState(
1042
+ this.resolveRuntimeValue(),
1043
+ );
1044
+ }
1045
+
1046
+ private resolveRuntimeValue(): EngineRuntime {
1047
+ return this.runtime instanceof RuntimePointer
1048
+ ? this.runtime.packageableRuntime.value.runtimeValue
1049
+ : guaranteeType(this.runtime, EngineRuntime);
1050
+ }
1051
+
1052
+ private buildRuntimeValueEditorState(
1053
+ runtimeValue: EngineRuntime,
1054
+ ): EngineRuntimeEditorState {
1055
+ return runtimeValue instanceof LakehouseRuntime
1056
+ ? new LakehouseRuntimeEditorState(this, runtimeValue)
1057
+ : runtimeValue instanceof LakehouseSingleStoreRuntime
1058
+ ? new LakehouseSingleStoreRuntimeEditorState(this, runtimeValue)
1017
1059
  : new EngineRuntimeEditorState(this, runtimeValue);
1018
1060
  }
1061
+
1062
+ /**
1063
+ * Rebuilds the concrete runtime-value editor state after the underlying
1064
+ * runtime's concrete type changes in place (e.g. Lakehouse compute-engine
1065
+ * swap between `LakehouseRuntime` and `LakehouseSingleStoreRuntime`).
1066
+ * Carries over already-fetched Lakehouse environment options so callers
1067
+ * don't need to refetch (which requires an auth token round-trip).
1068
+ */
1069
+ setRuntimeValueEditorState(runtimeValue: EngineRuntime): void {
1070
+ const previous = this.runtimeValueEditorState;
1071
+ const next = this.buildRuntimeValueEditorState(runtimeValue);
1072
+ if (
1073
+ previous instanceof LakehouseBaseRuntimeEditorState &&
1074
+ next instanceof LakehouseBaseRuntimeEditorState
1075
+ ) {
1076
+ next.setEnvSummaries(previous.availableEnvs);
1077
+ }
1078
+ this.runtimeValueEditorState = next;
1079
+ }
1019
1080
  }
1020
1081
 
1021
1082
  export class PackageableRuntimeEditorState extends ElementEditorState {
@@ -49,7 +49,6 @@ import {
49
49
  type ExecutionResultWithMetadata,
50
50
  TDSExecutionResult,
51
51
  getColumn,
52
- PrimitiveType,
53
52
  PRIMITIVE_TYPE,
54
53
  TDSRow,
55
54
  type Schema,
@@ -105,7 +104,7 @@ const buildTableToTDSQueryNonNumericWithColumnGrammar = (
105
104
  const db = table.schema._OWNER.path;
106
105
  const PREVIEW_COLUMN_NAME = 'Count Value';
107
106
  const columnGetter = getTDSColumnDerivedProperyFromType(
108
- getPrimitiveTypeFromRelationalType(column.type) ?? PrimitiveType.STRING,
107
+ getPrimitiveTypeFromRelationalType(column.type),
109
108
  );
110
109
  return `|${db}->tableReference(
111
110
  '${schemaName}',
@@ -135,7 +134,7 @@ const buildTableToTDSQueryNumericWithColumnGrammar = (
135
134
  const schemaName = table.schema.name;
136
135
  const db = table.schema._OWNER.path;
137
136
  const columnGetter = getTDSColumnDerivedProperyFromType(
138
- getPrimitiveTypeFromRelationalType(column.type) ?? PrimitiveType.STRING,
137
+ getPrimitiveTypeFromRelationalType(column.type),
139
138
  );
140
139
  return `|${db}->tableReference(
141
140
  '${schemaName}',
@@ -181,9 +180,10 @@ const buildTableToTDSQueryNumericWithColumnGrammar = (
181
180
  )`;
182
181
  };
183
182
 
184
- const buildTableToTDSQueryColumnQuery = (column: Column): [string, boolean] => {
185
- const type =
186
- getPrimitiveTypeFromRelationalType(column.type) ?? PrimitiveType.STRING;
183
+ export const buildTableToTDSQueryColumnQuery = (
184
+ column: Column,
185
+ ): [string, boolean] => {
186
+ const type = getPrimitiveTypeFromRelationalType(column.type);
187
187
  const numerics = [
188
188
  PRIMITIVE_TYPE.NUMBER,
189
189
  PRIMITIVE_TYPE.INTEGER,
@@ -30,6 +30,7 @@ import {
30
30
  AccessPoint,
31
31
  AccessPointGroup,
32
32
  CodeCompletionResult,
33
+ CompilationError,
33
34
  DataElement,
34
35
  DataElementReference,
35
36
  DataProduct,
@@ -136,7 +137,18 @@ export class AccessPointLambdaEditorState extends LambdaEditorState {
136
137
  readonly editorStore: EditorStore;
137
138
  readonly val: LakehouseAccessPointState;
138
139
  lambdaRelationColumns: string[] | undefined;
140
+ /**
141
+ * The engine's error message for this access point's lambda, when the engine
142
+ * answered that it could not compute the lambda's relation type.
143
+ */
144
+ lambdaRelationTypeError: string | undefined;
139
145
  isUpdatingRelationColumns = false;
146
+ /**
147
+ * Hash of the lambda the engine last answered for, with either its relation
148
+ * columns or an error for this lambda. A failure that is not an answer for
149
+ * this lambda (network, whole-batch failure) must leave it unset, so the next
150
+ * update asks the engine again.
151
+ */
140
152
  lastComputedLambdaHash: string | undefined;
141
153
  lastComputedLambdaString: string | undefined;
142
154
 
@@ -149,8 +161,10 @@ export class AccessPointLambdaEditorState extends LambdaEditorState {
149
161
 
150
162
  makeObservable(this, {
151
163
  lambdaRelationColumns: observable,
164
+ lambdaRelationTypeError: observable,
152
165
  isUpdatingRelationColumns: observable,
153
166
  setLambdaRelationColumns: action,
167
+ setLambdaRelationTypeError: action,
154
168
  updateLambdaRelationColumns: flow,
155
169
  });
156
170
  }
@@ -159,6 +173,10 @@ export class AccessPointLambdaEditorState extends LambdaEditorState {
159
173
  this.lambdaRelationColumns = columns;
160
174
  }
161
175
 
176
+ setLambdaRelationTypeError(message: string | undefined): void {
177
+ this.lambdaRelationTypeError = message;
178
+ }
179
+
162
180
  setIsUpdatingRelationColumns(value: boolean): void {
163
181
  this.isUpdatingRelationColumns = value;
164
182
  }
@@ -174,6 +192,7 @@ export class AccessPointLambdaEditorState extends LambdaEditorState {
174
192
  }
175
193
 
176
194
  this.setIsUpdatingRelationColumns(true);
195
+ this.setLambdaRelationTypeError(undefined);
177
196
  try {
178
197
  const relationType =
179
198
  (yield this.editorStore.graphManagerState.graphManager.getLambdaRelationType(
@@ -183,10 +202,22 @@ export class AccessPointLambdaEditorState extends LambdaEditorState {
183
202
  this.setLambdaRelationColumns(
184
203
  relationType.columns.map((column) => column.name),
185
204
  );
186
- } catch {
205
+ this.lastComputedLambdaHash = lambdaHash;
206
+ } catch (error) {
207
+ assertErrorThrown(error);
187
208
  this.setLambdaRelationColumns(undefined);
209
+ if (error instanceof CompilationError) {
210
+ // the engine compiled this lambda and answered with an error for it
211
+ this.setLambdaRelationTypeError(error.message);
212
+ this.lastComputedLambdaHash = lambdaHash;
213
+ } else {
214
+ this.editorStore.applicationStore.logService.error(
215
+ LogEvent.create(GRAPH_MANAGER_EVENT.GRAPH_MANAGER_FAILURE),
216
+ `Can't get relation type for access point '${this.val.accessPoint.id}'`,
217
+ error,
218
+ );
219
+ }
188
220
  } finally {
189
- this.lastComputedLambdaHash = lambdaHash;
190
221
  this.setIsUpdatingRelationColumns(false);
191
222
  }
192
223
  }
@@ -363,7 +394,10 @@ export class LakehouseAccessPointState extends AccessPointState {
363
394
  const lambdaColumns = this.lambdaState.lambdaRelationColumns;
364
395
 
365
396
  if (lambdaColumns === undefined) {
366
- return `Fix compiler errors and make sure AccessPoint ${this.accessPoint.id} returns a Relation type`;
397
+ const engineError = this.lambdaState.lambdaRelationTypeError;
398
+ return `Fix compiler errors and make sure AccessPoint ${this.accessPoint.id} returns a Relation type${
399
+ engineError ? `. Engine error: ${engineError}` : ''
400
+ }`;
367
401
  }
368
402
 
369
403
  return `Sample values columns must match: [${lambdaColumns.join(', ')}]`;
@@ -931,9 +965,10 @@ export class DataProductEditorState extends ElementEditorState {
931
965
  if (entries.length === 0) {
932
966
  return;
933
967
  }
934
- entries.forEach((entry) =>
935
- entry.apState.lambdaState.setIsUpdatingRelationColumns(true),
936
- );
968
+ entries.forEach((entry) => {
969
+ entry.apState.lambdaState.setIsUpdatingRelationColumns(true);
970
+ entry.apState.lambdaState.setLambdaRelationTypeError(undefined);
971
+ });
937
972
  try {
938
973
  const lambdas = new Map<string, RawLambda>(
939
974
  entries.map((entry) => [
@@ -941,28 +976,51 @@ export class DataProductEditorState extends ElementEditorState {
941
976
  entry.apState.accessPoint.func,
942
977
  ]),
943
978
  );
944
- const { results } =
979
+ const { results, errors } =
945
980
  (yield this.editorStore.graphManagerState.graphManager.getBatchLambdasRelationType(
946
981
  lambdas,
947
982
  this.editorStore.graphManagerState.graph,
948
983
  )) as BatchLambdasRelationTypeResult;
949
984
  entries.forEach((entry) => {
985
+ const lambdaState = entry.apState.lambdaState;
950
986
  const relationType = results.get(entry.apState.uuid);
951
- entry.apState.lambdaState.setLambdaRelationColumns(
952
- relationType
953
- ? relationType.columns.map((column) => column.name)
954
- : undefined,
955
- );
987
+ const engineError = errors.get(entry.apState.uuid);
988
+ if (relationType) {
989
+ lambdaState.setLambdaRelationColumns(
990
+ relationType.columns.map((column) => column.name),
991
+ );
992
+ lambdaState.lastComputedLambdaHash = entry.hash;
993
+ } else if (engineError) {
994
+ // the engine answered with an error for this lambda
995
+ lambdaState.setLambdaRelationColumns(undefined);
996
+ lambdaState.setLambdaRelationTypeError(engineError.message);
997
+ lambdaState.lastComputedLambdaHash = entry.hash;
998
+ } else {
999
+ // the engine did not answer for this lambda: leave the hash unset so
1000
+ // the next update asks again
1001
+ lambdaState.setLambdaRelationColumns(undefined);
1002
+ this.editorStore.applicationStore.logService.error(
1003
+ LogEvent.create(GRAPH_MANAGER_EVENT.GRAPH_MANAGER_FAILURE),
1004
+ `Engine returned neither a relation type nor an error for access point '${entry.apState.accessPoint.id}'`,
1005
+ );
1006
+ }
956
1007
  });
957
- } catch {
1008
+ } catch (error) {
1009
+ assertErrorThrown(error);
1010
+ // the whole batch failed, so no access point got an answer: leave their
1011
+ // hashes unset so the next update asks again
1012
+ this.editorStore.applicationStore.logService.error(
1013
+ LogEvent.create(GRAPH_MANAGER_EVENT.GRAPH_MANAGER_FAILURE),
1014
+ `Can't get relation types for access points`,
1015
+ error,
1016
+ );
958
1017
  entries.forEach((entry) =>
959
1018
  entry.apState.lambdaState.setLambdaRelationColumns(undefined),
960
1019
  );
961
1020
  } finally {
962
- entries.forEach((entry) => {
963
- entry.apState.lambdaState.lastComputedLambdaHash = entry.hash;
964
- entry.apState.lambdaState.setIsUpdatingRelationColumns(false);
965
- });
1021
+ entries.forEach((entry) =>
1022
+ entry.apState.lambdaState.setIsUpdatingRelationColumns(false),
1023
+ );
966
1024
  }
967
1025
  }
968
1026
 
@@ -68,7 +68,7 @@ import {
68
68
  Database,
69
69
  DataProduct,
70
70
  IngestDefinition,
71
- LakehouseRuntime,
71
+ LakehouseBaseRuntime,
72
72
  PackageableElementExplicitReference,
73
73
  observe_ValueSpecification,
74
74
  observe_RelationElementsData,
@@ -1058,7 +1058,7 @@ export class FunctionTestableState extends TestablePackageableElementEditorState
1058
1058
  `Function Test Suite Only supports One Runtime at this time. Found ${engineRuntimes.length}`,
1059
1059
  );
1060
1060
  const engineRuntime = guaranteeNonNullable(engineRuntimes[0]);
1061
- if (!(engineRuntime instanceof LakehouseRuntime)) {
1061
+ if (!(engineRuntime instanceof LakehouseBaseRuntime)) {
1062
1062
  assertTrue(
1063
1063
  !(
1064
1064
  engineRuntime.connectionStores.length &&
@@ -301,7 +301,8 @@ export class ServiceRegistrationState extends ServiceConfigState {
301
301
  { servicePath, envCount: this.registrationOptions.length },
302
302
  );
303
303
  const envs: string[] = [];
304
- let errorCount = 0;
304
+ const failedEnvs: string[] = [];
305
+ let lastErrorMessage: string | undefined;
305
306
  const servicePattern = this.service.pattern.startsWith('/')
306
307
  ? this.service.pattern.substring(1)
307
308
  : this.service.pattern;
@@ -317,37 +318,49 @@ export class ServiceRegistrationState extends ServiceConfigState {
317
318
  }
318
319
  } catch (error) {
319
320
  assertErrorThrown(error);
320
- errorCount += 1;
321
+ failedEnvs.push(envConfig.env);
322
+ lastErrorMessage = error.message;
321
323
  this.editorStore.applicationStore.logService.warn(
322
324
  LogEvent.create(
323
325
  LEGEND_STUDIO_APP_EVENT.SERVICE_REGISTRATION_CHECK_FAILURE,
324
326
  ),
325
327
  `Can't check registration status for env '${envConfig.env}': ${error.message}`,
326
328
  );
327
- LegendStudioTelemetryHelper.logEvent_ServiceRegistrationCheckFailure(
328
- this.editorStore.applicationStore.telemetryService,
329
- sourceInfo,
330
- {
331
- servicePath,
332
- env: envConfig.env,
333
- errorMessage: error.message,
334
- },
335
- );
336
329
  }
337
330
  }
338
331
  this.setRegisteredEnvs(envs);
339
332
  this.deploymentCheckState.complete();
340
- LegendStudioTelemetryHelper.logEvent_ServiceRegistrationCheckSucceeded(
341
- this.editorStore.applicationStore.telemetryService,
342
- sourceInfo,
343
- {
344
- servicePath,
345
- envCount: this.registrationOptions.length,
346
- durationMs: Date.now() - startedAt,
347
- registeredEnvCount: envs.length,
348
- errorCount,
349
- },
350
- );
333
+ // A precheck run only counts as failed when no env could be probed at
334
+ // all; partial per-env errors are reported on the success payload.
335
+ const envCount = this.registrationOptions.length;
336
+ const durationMs = Date.now() - startedAt;
337
+ if (lastErrorMessage !== undefined && failedEnvs.length === envCount) {
338
+ LegendStudioTelemetryHelper.logEvent_ServiceRegistrationCheckFailure(
339
+ this.editorStore.applicationStore.telemetryService,
340
+ sourceInfo,
341
+ {
342
+ servicePath,
343
+ envCount,
344
+ durationMs,
345
+ errorCount: failedEnvs.length,
346
+ failedEnvs,
347
+ errorMessage: lastErrorMessage,
348
+ },
349
+ );
350
+ } else {
351
+ LegendStudioTelemetryHelper.logEvent_ServiceRegistrationCheckSucceeded(
352
+ this.editorStore.applicationStore.telemetryService,
353
+ sourceInfo,
354
+ {
355
+ servicePath,
356
+ envCount,
357
+ durationMs,
358
+ registeredEnvCount: envs.length,
359
+ errorCount: failedEnvs.length,
360
+ failedEnvs,
361
+ },
362
+ );
363
+ }
351
364
  }
352
365
 
353
366
  *registerService(): GeneratorFn<void> {
@@ -430,7 +443,12 @@ export class ServiceRegistrationState extends ServiceConfigState {
430
443
  LegendStudioTelemetryHelper.logEvent_ServiceRegistrationSucceeded(
431
444
  this.editorStore.applicationStore.telemetryService,
432
445
  this.editorStore.editorMode.getSourceInfo(),
433
- { ...telemetryBase, durationMs: Date.now() - startedAt },
446
+ {
447
+ ...telemetryBase,
448
+ durationMs: Date.now() - startedAt,
449
+ registeredCount: 1,
450
+ failedCount: 0,
451
+ },
434
452
  );
435
453
  } catch (error) {
436
454
  assertErrorThrown(error);