@finos/legend-application-query 13.8.48 → 13.8.50

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 (56) hide show
  1. package/lib/components/Core_LegendQueryApplicationPlugin.d.ts.map +1 -1
  2. package/lib/components/Core_LegendQueryApplicationPlugin.js +52 -18
  3. package/lib/components/Core_LegendQueryApplicationPlugin.js.map +1 -1
  4. package/lib/components/QueryEditor.d.ts.map +1 -1
  5. package/lib/components/QueryEditor.js +14 -2
  6. package/lib/components/QueryEditor.js.map +1 -1
  7. package/lib/components/data-space/DataSpaceInfo.d.ts.map +1 -1
  8. package/lib/components/data-space/DataSpaceInfo.js +13 -9
  9. package/lib/components/data-space/DataSpaceInfo.js.map +1 -1
  10. package/lib/components/data-space/DataSpaceQuerySetup.js +3 -3
  11. package/lib/components/data-space/DataSpaceQuerySetup.js.map +1 -1
  12. package/lib/components/data-space/LegendQueryDataSpaceQueryBuilder.d.ts.map +1 -1
  13. package/lib/components/data-space/LegendQueryDataSpaceQueryBuilder.js +23 -13
  14. package/lib/components/data-space/LegendQueryDataSpaceQueryBuilder.js.map +1 -1
  15. package/lib/index.css +1 -1
  16. package/lib/light-mode.css +1 -1
  17. package/lib/package.json +1 -1
  18. package/lib/stores/QueryEditorStore.d.ts +14 -11
  19. package/lib/stores/QueryEditorStore.d.ts.map +1 -1
  20. package/lib/stores/QueryEditorStore.js +68 -35
  21. package/lib/stores/QueryEditorStore.js.map +1 -1
  22. package/lib/stores/__test-utils__/DataSpaceQueryBuilderStateTestUtils.d.ts +33 -0
  23. package/lib/stores/__test-utils__/DataSpaceQueryBuilderStateTestUtils.d.ts.map +1 -0
  24. package/lib/stores/__test-utils__/DataSpaceQueryBuilderStateTestUtils.js +74 -0
  25. package/lib/stores/__test-utils__/DataSpaceQueryBuilderStateTestUtils.js.map +1 -0
  26. package/lib/stores/data-product/query-builder/LegendQueryDataProductQueryBuilderState.d.ts.map +1 -1
  27. package/lib/stores/data-product/query-builder/LegendQueryDataProductQueryBuilderState.js +3 -3
  28. package/lib/stores/data-product/query-builder/LegendQueryDataProductQueryBuilderState.js.map +1 -1
  29. package/lib/stores/data-space/DataProductQueryCreatorStore.d.ts.map +1 -1
  30. package/lib/stores/data-space/DataProductQueryCreatorStore.js +13 -5
  31. package/lib/stores/data-space/DataProductQueryCreatorStore.js.map +1 -1
  32. package/lib/stores/data-space/DataSpaceTemplateQueryCreatorStore.d.ts +1 -1
  33. package/lib/stores/data-space/DataSpaceTemplateQueryCreatorStore.d.ts.map +1 -1
  34. package/lib/stores/data-space/DataSpaceTemplateQueryCreatorStore.js +11 -4
  35. package/lib/stores/data-space/DataSpaceTemplateQueryCreatorStore.js.map +1 -1
  36. package/lib/stores/data-space/query-builder/LegendQueryDataSpaceQueryBuilderState.d.ts +5 -4
  37. package/lib/stores/data-space/query-builder/LegendQueryDataSpaceQueryBuilderState.d.ts.map +1 -1
  38. package/lib/stores/data-space/query-builder/LegendQueryDataSpaceQueryBuilderState.js +37 -8
  39. package/lib/stores/data-space/query-builder/LegendQueryDataSpaceQueryBuilderState.js.map +1 -1
  40. package/lib/stores/ingest/IngestQueryCreatorStore.d.ts.map +1 -1
  41. package/lib/stores/ingest/IngestQueryCreatorStore.js +2 -2
  42. package/lib/stores/ingest/IngestQueryCreatorStore.js.map +1 -1
  43. package/package.json +8 -8
  44. package/src/components/Core_LegendQueryApplicationPlugin.tsx +72 -33
  45. package/src/components/QueryEditor.tsx +22 -4
  46. package/src/components/data-space/DataSpaceInfo.tsx +28 -11
  47. package/src/components/data-space/DataSpaceQuerySetup.tsx +4 -4
  48. package/src/components/data-space/LegendQueryDataSpaceQueryBuilder.tsx +94 -38
  49. package/src/stores/QueryEditorStore.ts +118 -59
  50. package/src/stores/__test-utils__/DataSpaceQueryBuilderStateTestUtils.ts +138 -0
  51. package/src/stores/data-product/query-builder/LegendQueryDataProductQueryBuilderState.ts +9 -2
  52. package/src/stores/data-space/DataProductQueryCreatorStore.ts +29 -6
  53. package/src/stores/data-space/DataSpaceTemplateQueryCreatorStore.ts +23 -4
  54. package/src/stores/data-space/query-builder/LegendQueryDataSpaceQueryBuilderState.ts +51 -13
  55. package/src/stores/ingest/IngestQueryCreatorStore.ts +5 -1
  56. package/tsconfig.json +1 -0
@@ -31,7 +31,6 @@ import { type QueryEditorStore } from '../../stores/QueryEditorStore.js';
31
31
  import {
32
32
  type DataSpace,
33
33
  type DataSpaceExecutionContext,
34
- DataSpaceQueryBuilderState,
35
34
  DataSpaceSupportEmail,
36
35
  } from '@finos/legend-extension-dsl-data-space/graph';
37
36
  import {
@@ -39,11 +38,13 @@ import {
39
38
  EXTERNAL_APPLICATION_NAVIGATION__generateTaxonomyDataspaceViewUrl,
40
39
  } from '../../__lib__/LegendQueryNavigation.js';
41
40
  import { flowResult } from 'mobx';
42
- import { assertErrorThrown } from '@finos/legend-shared';
41
+ import { assertErrorThrown, guaranteeType } from '@finos/legend-shared';
43
42
  import {
44
43
  ConnectionPointer,
44
+ INTERNAL_ELEMENT_PATH,
45
45
  RelationalDatabaseConnection,
46
46
  } from '@finos/legend-graph';
47
+ import { LegendQueryDataSpaceQueryBuilderState } from '../../stores/data-space/query-builder/LegendQueryDataSpaceQueryBuilderState.js';
47
48
 
48
49
  export const QueryEditorDataspaceInfoModal = observer(
49
50
  (props: {
@@ -55,8 +56,16 @@ export const QueryEditorDataspaceInfoModal = observer(
55
56
  }) => {
56
57
  const { editorStore, dataspace, executionContext, open, closeModal } =
57
58
  props;
59
+ const queryBuilderState = guaranteeType(
60
+ editorStore.queryBuilderState,
61
+ LegendQueryDataSpaceQueryBuilderState,
62
+ `Data product info modal expects a ${LegendQueryDataSpaceQueryBuilderState.name}`,
63
+ );
58
64
  const projectInfo = editorStore.getProjectInfo();
59
65
  const visitElement = async (path: string | undefined): Promise<void> => {
66
+ if (path?.startsWith(`${INTERNAL_ELEMENT_PATH}::`)) {
67
+ return;
68
+ }
60
69
  try {
61
70
  if (projectInfo) {
62
71
  const project = StoreProjectData.serialization.fromJson(
@@ -94,15 +103,14 @@ export const QueryEditorDataspaceInfoModal = observer(
94
103
  }
95
104
  };
96
105
 
97
- const dataSpaceAnalysisResult =
98
- editorStore.queryBuilderState instanceof DataSpaceQueryBuilderState
99
- ? editorStore.queryBuilderState.dataSpaceAnalysisResult
100
- : undefined;
106
+ const dataSpaceAnalysisResult = queryBuilderState.dataSpaceAnalysisResult;
101
107
  const dataSpaceMedata = dataSpaceAnalysisResult?.executionContextsIndex.get(
102
108
  executionContext.name,
103
109
  )?.runtimeMetadata;
104
110
  const defaultRuntimeValue = executionContext.defaultRuntime?.value;
105
111
  const executionContextMappingValue = executionContext.mapping?.value;
112
+ const mappingProviderElement =
113
+ executionContext.mappingProvider?.element.value;
106
114
  const connection = defaultRuntimeValue?.runtimeValue.connections.length
107
115
  ? defaultRuntimeValue.runtimeValue.connections[0]?.storeConnections?.[0]
108
116
  ?.connection instanceof ConnectionPointer
@@ -162,7 +170,7 @@ export const QueryEditorDataspaceInfoModal = observer(
162
170
  className="dataspace-info-modal"
163
171
  >
164
172
  <div className="dataspace-info-modal__header">
165
- <ModalTitle title="About Data Product" />
173
+ <ModalTitle title="About Data Space" />
166
174
  <button
167
175
  className="btn--dark dataspace-info-modal__header__open-btn"
168
176
  title="Close"
@@ -174,7 +182,7 @@ export const QueryEditorDataspaceInfoModal = observer(
174
182
  }
175
183
  onClick={openInTaxonomy}
176
184
  >
177
- Open Data Product
185
+ Open Data Space
178
186
  </button>
179
187
  </div>
180
188
 
@@ -209,9 +217,18 @@ export const QueryEditorDataspaceInfoModal = observer(
209
217
  </div>
210
218
  <div className="dataspace-info-modal__field">
211
219
  <div className="dataspace-info-modal__field__label">
212
- Mapping
220
+ {mappingProviderElement ? 'Data Product' : 'Mapping'}
213
221
  </div>
214
- {executionContextMappingValue ? (
222
+ {mappingProviderElement ? (
223
+ <div
224
+ className="dataspace-info-modal__field__value dataspace-info-modal__field__value--linkable"
225
+ onClick={() =>
226
+ flowResult(visitElement(mappingProviderElement.path))
227
+ }
228
+ >
229
+ {mappingProviderElement.name}
230
+ </div>
231
+ ) : executionContextMappingValue ? (
215
232
  <div
216
233
  className="dataspace-info-modal__field__value dataspace-info-modal__field__value--linkable"
217
234
  onClick={() =>
@@ -299,7 +316,7 @@ export const QueryEditorDataspaceInfoModal = observer(
299
316
  className="dataspace-info-modal__field__value dataspace-info-modal__field__value--linkable"
300
317
  onClick={() => flowResult(visitElement(dataspace.path))}
301
318
  >
302
- Show Data Product Configuration
319
+ Show Data Space Configuration
303
320
  </div>
304
321
  </div>
305
322
  {dataspace.supportInfo instanceof DataSpaceSupportEmail && (
@@ -114,10 +114,10 @@ const DataProductQuerySetupSetupPanelContent = observer(
114
114
  <div className="query-builder__setup__config-group__item">
115
115
  <label
116
116
  className="btn--sm query-builder__setup__config-group__data-product"
117
- title="data product"
117
+ title="data space"
118
118
  htmlFor="query-builder__setup__data-space-selector"
119
119
  >
120
- Data Product
120
+ Data Space
121
121
  </label>
122
122
  <CustomSelectorInput
123
123
  inputId="query-builder__setup__data-space-selector"
@@ -130,7 +130,7 @@ const DataProductQuerySetupSetupPanelContent = observer(
130
130
  }
131
131
  onChange={onDataSpaceOptionChange}
132
132
  value={selectedDataSpaceOption}
133
- placeholder="Search for data product..."
133
+ placeholder="Search for data space..."
134
134
  escapeClearsValue={true}
135
135
  darkMode={
136
136
  !applicationStore.layoutService
@@ -142,7 +142,7 @@ const DataProductQuerySetupSetupPanelContent = observer(
142
142
  tabIndex={-1}
143
143
  className="query-builder__setup__data-space-searcher__btn btn--dark"
144
144
  onClick={openDataSpaceAdvancedSearch}
145
- title="Open advanced search for data product..."
145
+ title="Open advanced search for data space..."
146
146
  >
147
147
  <SearchIcon />
148
148
  </button>
@@ -18,6 +18,7 @@ import { useApplicationStore } from '@finos/legend-application';
18
18
  import {
19
19
  AnchorLinkIcon,
20
20
  CheckIcon,
21
+ CogIcon,
21
22
  compareLabelFn,
22
23
  ControlledDropdownMenu,
23
24
  createFilter,
@@ -39,6 +40,7 @@ import {
39
40
  import {
40
41
  buildExecutionContextOption,
41
42
  DataSpaceAdvancedSearchModal,
43
+ formatExecutionContextOptionLabel,
42
44
  type ExecutionContextOption,
43
45
  } from '@finos/legend-extension-dsl-data-space/application-query';
44
46
  import { resolveExecutionContextMapping } from '@finos/legend-extension-dsl-data-space/graph';
@@ -58,7 +60,8 @@ import { guaranteeNonNullable, guaranteeType } from '@finos/legend-shared';
58
60
  import { DepotEntityWithOrigin } from '@finos/legend-storage';
59
61
  import { flowResult } from 'mobx';
60
62
  import { observer } from 'mobx-react-lite';
61
- import { useEffect } from 'react';
63
+ import { useEffect, useState } from 'react';
64
+ import { LakehouseRuntimeConfigModal } from '../shared/LakehouseRuntimeConfigModal.js';
62
65
  import type { DataProductWithLegacyOption } from '../../stores/data-space/DataProductSelectorState.js';
63
66
  import { formatDataProductOrSpaceOptionLabel } from '../shared/LegendQueryDataProductOptionLabel.js';
64
67
  import type { LegendQueryDataSpaceQueryBuilderState } from '../../stores/data-space/query-builder/LegendQueryDataSpaceQueryBuilderState.js';
@@ -132,7 +135,7 @@ const LegendQueryDataSpaceQueryBuilderSetupPanelContent = observer(
132
135
  // execution context
133
136
  const executionContextOptions = guaranteeNonNullable(
134
137
  queryBuilderState.dataSpace.executionContexts,
135
- `Data product '${queryBuilderState.dataSpace.path}' does not have any execution contexts`,
138
+ `Data space '${queryBuilderState.dataSpace.path}' does not have any execution contexts`,
136
139
  )
137
140
  .map(buildExecutionContextOption)
138
141
  .sort(compareLabelFn);
@@ -147,17 +150,14 @@ const LegendQueryDataSpaceQueryBuilderSetupPanelContent = observer(
147
150
  if (option.value === queryBuilderState.executionContext) {
148
151
  return;
149
152
  }
150
- const currentMappingPath = guaranteeNonNullable(
151
- queryBuilderState.executionContext.mapping,
152
- `Execution context '${queryBuilderState.executionContext.name}' does not have a mapping`,
153
- ).value.path;
154
- const nextMappingPath = guaranteeNonNullable(
155
- option.value.mapping,
156
- `Execution context '${option.value.name}' does not have a mapping`,
157
- ).value.path;
153
+ const currentMappingPath = resolveExecutionContextMapping(
154
+ queryBuilderState.executionContext,
155
+ )?.path;
158
156
  queryBuilderState.setExecutionContext(option.value);
159
157
  await queryBuilderState.propagateExecutionContextChange(
160
- currentMappingPath === nextMappingPath,
158
+ currentMappingPath !== undefined &&
159
+ currentMappingPath ===
160
+ resolveExecutionContextMapping(option.value)?.path,
161
161
  );
162
162
  queryBuilderState.onExecutionContextChange?.(option.value);
163
163
  };
@@ -200,16 +200,21 @@ const LegendQueryDataSpaceQueryBuilderSetupPanelContent = observer(
200
200
  });
201
201
 
202
202
  // class
203
+ const executionContextMapping = guaranteeNonNullable(
204
+ resolveExecutionContextMapping(queryBuilderState.executionContext),
205
+ `Execution context '${queryBuilderState.executionContext.name}' does not have a resolvable mapping`,
206
+ );
203
207
  const classes = resolveUsableDataSpaceClasses(
204
208
  queryBuilderState.dataSpace,
205
- guaranteeNonNullable(
206
- queryBuilderState.executionContext.mapping,
207
- `Execution context '${queryBuilderState.executionContext.name}' does not have a mapping`,
208
- ).value,
209
+ executionContextMapping,
209
210
  queryBuilderState.graphManagerState,
210
211
  queryBuilderState,
211
212
  );
212
213
 
214
+ const injectedLakehouseRuntime = queryBuilderState.injectedLakehouseRuntime;
215
+ const [isLakehouseConfigModalOpen, setIsLakehouseConfigModalOpen] =
216
+ useState(false);
217
+
213
218
  useEffect(() => {
214
219
  flowResult(queryBuilderState.loadEntities()).catch(
215
220
  applicationStore.alertUnhandledError,
@@ -227,7 +232,7 @@ const LegendQueryDataSpaceQueryBuilderSetupPanelContent = observer(
227
232
  <PanelHeader title="properties">
228
233
  <PanelHeaderActions>
229
234
  <PanelHeaderActionItem
230
- title="copy data product query set up link to clipboard"
235
+ title="copy data space query set up link to clipboard"
231
236
  onClick={copyDataSpaceLinkToClipboard}
232
237
  disabled={!queryBuilderState.isDataSpaceLinkable}
233
238
  >
@@ -254,6 +259,18 @@ const LegendQueryDataSpaceQueryBuilderSetupPanelContent = observer(
254
259
  Show Runtime Selector
255
260
  </MenuContentItemLabel>
256
261
  </MenuContentItem>
262
+ {injectedLakehouseRuntime && (
263
+ <MenuContentItem
264
+ onClick={(): void => setIsLakehouseConfigModalOpen(true)}
265
+ >
266
+ <MenuContentItemIcon>
267
+ <CogIcon />
268
+ </MenuContentItemIcon>
269
+ <MenuContentItemLabel>
270
+ Lakehouse Runtime Configuration
271
+ </MenuContentItemLabel>
272
+ </MenuContentItem>
273
+ )}
257
274
  </MenuContent>
258
275
  }
259
276
  menuProps={{
@@ -269,10 +286,10 @@ const LegendQueryDataSpaceQueryBuilderSetupPanelContent = observer(
269
286
  <div className="query-builder__setup__config-group__item">
270
287
  <label
271
288
  className="btn--sm query-builder__setup__config-group__data-product"
272
- title="data product"
289
+ title="data space"
273
290
  htmlFor="query-builder__setup__data-space-selector"
274
291
  >
275
- Data Product
292
+ Data Space
276
293
  </label>
277
294
  <CustomSelectorInput
278
295
  inputId="query-builder__setup__data-space-selector"
@@ -281,7 +298,7 @@ const LegendQueryDataSpaceQueryBuilderSetupPanelContent = observer(
281
298
  isLoading={queryBuilderState.loadEntitiesState.isInProgress}
282
299
  onChange={onOptionChange}
283
300
  value={selectedOption}
284
- placeholder="Search for data product..."
301
+ placeholder="Search for data space..."
285
302
  escapeClearsValue={true}
286
303
  darkMode={
287
304
  !applicationStore.layoutService
@@ -295,7 +312,7 @@ const LegendQueryDataSpaceQueryBuilderSetupPanelContent = observer(
295
312
  tabIndex={-1}
296
313
  className="query-builder__setup__data-space-searcher__btn btn--dark"
297
314
  onClick={openDataSpaceAdvancedSearch}
298
- title="Open advanced search for data product..."
315
+ title="Open advanced search for data space..."
299
316
  >
300
317
  <SearchIcon />
301
318
  </button>
@@ -333,6 +350,7 @@ const LegendQueryDataSpaceQueryBuilderSetupPanelContent = observer(
333
350
  !applicationStore.layoutService
334
351
  .TEMPORARY__isLightColorThemeEnabled
335
352
  }
353
+ formatOptionLabel={formatExecutionContextOptionLabel}
336
354
  />
337
355
  </div>
338
356
  )}
@@ -345,25 +363,52 @@ const LegendQueryDataSpaceQueryBuilderSetupPanelContent = observer(
345
363
  >
346
364
  Runtime
347
365
  </label>
348
- <CustomSelectorInput
349
- inputId="query-builder__setup__runtime-selector"
350
- className="panel__content__form__section__dropdown query-builder__setup__config-group__item__selector"
351
- placeholder="Choose a runtime..."
352
- noMatchMessage="No compatible runtime found for specified execution context"
353
- options={runtimeOptions}
354
- onChange={changeRuntime}
355
- value={selectedRuntimeOption}
356
- darkMode={
357
- !applicationStore.layoutService
358
- .TEMPORARY__isLightColorThemeEnabled
359
- }
360
- filterOption={runtimeFilterOption}
361
- formatOptionLabel={getRuntimeOptionFormatter({
362
- darkMode:
366
+ {injectedLakehouseRuntime ? (
367
+ <div
368
+ className="query-builder__setup__config-group__item__selector"
369
+ title="Lakehouse runtime — configure via the settings menu"
370
+ >
371
+ <CustomSelectorInput
372
+ inputId="query-builder__setup__runtime-selector"
373
+ className="panel__content__form__section__dropdown"
374
+ options={
375
+ selectedRuntimeOption ? [selectedRuntimeOption] : []
376
+ }
377
+ value={selectedRuntimeOption}
378
+ onChange={() => {}}
379
+ disabled={true}
380
+ darkMode={
381
+ !applicationStore.layoutService
382
+ .TEMPORARY__isLightColorThemeEnabled
383
+ }
384
+ formatOptionLabel={getRuntimeOptionFormatter({
385
+ darkMode:
386
+ !applicationStore.layoutService
387
+ .TEMPORARY__isLightColorThemeEnabled,
388
+ })}
389
+ />
390
+ </div>
391
+ ) : (
392
+ <CustomSelectorInput
393
+ inputId="query-builder__setup__runtime-selector"
394
+ className="panel__content__form__section__dropdown query-builder__setup__config-group__item__selector"
395
+ placeholder="Choose a runtime..."
396
+ noMatchMessage="No compatible runtime found for specified execution context"
397
+ options={runtimeOptions}
398
+ onChange={changeRuntime}
399
+ value={selectedRuntimeOption}
400
+ darkMode={
363
401
  !applicationStore.layoutService
364
- .TEMPORARY__isLightColorThemeEnabled,
365
- })}
366
- />
402
+ .TEMPORARY__isLightColorThemeEnabled
403
+ }
404
+ filterOption={runtimeFilterOption}
405
+ formatOptionLabel={getRuntimeOptionFormatter({
406
+ darkMode:
407
+ !applicationStore.layoutService
408
+ .TEMPORARY__isLightColorThemeEnabled,
409
+ })}
410
+ />
411
+ )}
367
412
  </div>
368
413
  )}
369
414
  <div className="query-builder__setup__config-group__item">
@@ -375,6 +420,17 @@ const LegendQueryDataSpaceQueryBuilderSetupPanelContent = observer(
375
420
  />
376
421
  </div>
377
422
  </div>
423
+ {injectedLakehouseRuntime && (
424
+ <LakehouseRuntimeConfigModal
425
+ lakehouseRuntime={injectedLakehouseRuntime}
426
+ open={isLakehouseConfigModalOpen}
427
+ onClose={() => setIsLakehouseConfigModalOpen(false)}
428
+ darkMode={
429
+ !applicationStore.layoutService
430
+ .TEMPORARY__isLightColorThemeEnabled
431
+ }
432
+ />
433
+ )}
378
434
  </div>
379
435
  );
380
436
  },
@@ -50,6 +50,7 @@ import {
50
50
  type ValueSpecification,
51
51
  type GraphInitializationReport,
52
52
  PackageableRuntime,
53
+ createPath,
53
54
  type QueryInfo,
54
55
  GraphManagerState,
55
56
  Query,
@@ -69,6 +70,7 @@ import {
69
70
  cloneQueryTaggedValue,
70
71
  QueryProjectCoordinates,
71
72
  CORE_PURE_PATH,
73
+ INTERNAL_ELEMENT_PATH,
72
74
  isValidFullPath,
73
75
  QUERY_PROFILE_PATH,
74
76
  QueryDataSpaceExecutionContextInfo,
@@ -147,6 +149,7 @@ import {
147
149
  DSL_DataSpace_getGraphManagerExtension,
148
150
  getOwnDataSpace,
149
151
  QUERY_PROFILE_TAG_DATA_SPACE,
152
+ resolveExecutionContextMapping,
150
153
  retrieveAnalyticsResultCache,
151
154
  retrieveDataspaceArtifactsCache,
152
155
  } from '@finos/legend-extension-dsl-data-space/graph';
@@ -810,7 +813,7 @@ export abstract class QueryEditorStore {
810
813
  supportBuildMinimalGraph
811
814
  ) {
812
815
  try {
813
- this.initState.setMessage('Fetching data product analysis result...');
816
+ this.initState.setMessage('Fetching data space analysis result...');
814
817
  const project = StoreProjectData.serialization.fromJson(
815
818
  await this.depotServerClient.getProject(groupId, artifactId),
816
819
  );
@@ -1142,24 +1145,9 @@ export abstract class QueryEditorStore {
1142
1145
  );
1143
1146
  }
1144
1147
 
1145
- /**
1146
- * Resolves the user's lakehouse environment and warehouse, creates a
1147
- * `LakehouseRuntime`, and wraps it in a `PackageableRuntime`.
1148
- *
1149
- * Resolution order:
1150
- * 1. Check local storage (`LakehouseUserInfo`) for a previously persisted value.
1151
- * 2. If not found, fetch from the lakehouse contract server via
1152
- * `getUserEntitlementEnvs()` and persist the result to local storage.
1153
- */
1154
- async createLakehousePackageableRuntime(
1155
- dataProductPath: string,
1156
- gav: {
1157
- groupId: string;
1158
- artifactId: string;
1159
- versionId: string;
1160
- },
1161
- ): Promise<PackageableRuntime> {
1162
- // 1. Check local storage for persisted lakehouse user info
1148
+ async resolveLakehouseEnvAndWarehouse(
1149
+ isSnapshot: boolean,
1150
+ ): Promise<{ env: string; warehouse: string }> {
1163
1151
  const persistedInfo = LegendQueryUserDataHelper.getLakehouseUserInfo(
1164
1152
  this.applicationStore.userDataService,
1165
1153
  );
@@ -1167,7 +1155,6 @@ export abstract class QueryEditorStore {
1167
1155
  let userEnvironment: string | undefined = persistedInfo?.env;
1168
1156
  const userWarehouse: string =
1169
1157
  persistedInfo?.snowflakeWarehouse ?? 'LAKEHOUSE_CONSUMER_DEFAULT_WH';
1170
- // 2. If no persisted environment, fetch from the server
1171
1158
  if (userEnvironment === undefined && this.lakehouseState) {
1172
1159
  try {
1173
1160
  const entitlementEnvs =
@@ -1178,7 +1165,6 @@ export abstract class QueryEditorStore {
1178
1165
  userEnvironment = entitlementEnvs.users
1179
1166
  .map((e) => e.lakehouseEnvironment)
1180
1167
  .at(0);
1181
- // Persist to local storage for future use
1182
1168
  LegendQueryUserDataHelper.persistLakehouseUserInfo(
1183
1169
  this.applicationStore.userDataService,
1184
1170
  {
@@ -1197,30 +1183,82 @@ export abstract class QueryEditorStore {
1197
1183
 
1198
1184
  if (userEnvironment === undefined) {
1199
1185
  throw new Error(
1200
- `Can't query data product '${dataProductPath}': unable to resolve lakehouse user environment. ` +
1201
- `Please ensure your lakehouse entitlements are configured.`,
1186
+ `Unable to resolve lakehouse user environment. Please ensure your lakehouse entitlements are configured.`,
1202
1187
  );
1203
1188
  }
1204
- if (
1205
- isSnapshotVersion(gav.versionId) ||
1206
- gav.versionId === SNAPSHOT_VERSION_ALIAS
1207
- ) {
1189
+ if (isSnapshot) {
1208
1190
  userEnvironment = decorateEnvWithRealm(
1209
1191
  userEnvironment,
1210
1192
  LakehouseEnvironmentType.PRODUCTION_PARALLEL,
1211
1193
  );
1212
1194
  }
1213
- const lakehouseRuntime = new LakehouseRuntime(
1214
- userEnvironment,
1215
- userWarehouse,
1216
- );
1195
+ return { env: userEnvironment, warehouse: userWarehouse };
1196
+ }
1197
+
1198
+ async createLakehousePackageableRuntime(
1199
+ dataProductPath: string,
1200
+ gav: {
1201
+ groupId: string;
1202
+ artifactId: string;
1203
+ versionId: string;
1204
+ },
1205
+ ): Promise<PackageableRuntime> {
1206
+ const isSnapshot =
1207
+ isSnapshotVersion(gav.versionId) ||
1208
+ gav.versionId === SNAPSHOT_VERSION_ALIAS;
1209
+ const { env, warehouse } =
1210
+ await this.resolveLakehouseEnvAndWarehouse(isSnapshot);
1217
1211
  const packageableRuntime = new PackageableRuntime(
1218
1212
  `${dataProductPath}_LakehouseRuntime`,
1219
1213
  );
1220
- packageableRuntime.runtimeValue = lakehouseRuntime;
1214
+ packageableRuntime.runtimeValue = new LakehouseRuntime(env, warehouse);
1221
1215
  return packageableRuntime;
1222
1216
  }
1223
1217
 
1218
+ async createOrGetDataSpaceLakehouseFallbackRuntime(
1219
+ isSnapshot: boolean,
1220
+ ): Promise<PackageableRuntime> {
1221
+ const runtimePath = createPath(INTERNAL_ELEMENT_PATH, 'LakehouseRuntime');
1222
+ const existing =
1223
+ this.graphManagerState.graph.getNullableRuntime(runtimePath);
1224
+ if (existing) {
1225
+ return existing;
1226
+ }
1227
+ const { env, warehouse } =
1228
+ await this.resolveLakehouseEnvAndWarehouse(isSnapshot);
1229
+ const created = new PackageableRuntime('LakehouseRuntime');
1230
+ created.runtimeValue = new LakehouseRuntime(env, warehouse);
1231
+ this.graphManagerState.graph.addElement(created, INTERNAL_ELEMENT_PATH);
1232
+ return created;
1233
+ }
1234
+
1235
+ async attachDataSpaceFallbackRuntimes(
1236
+ dataSpace: DataSpace,
1237
+ dataSpaceAnalysisResult: DataSpaceAnalysisResult | undefined,
1238
+ isSnapshot: boolean,
1239
+ ): Promise<void> {
1240
+ const contexts = dataSpace.executionContexts ?? [];
1241
+ let sharedRuntime: PackageableRuntime | undefined;
1242
+ for (const context of contexts) {
1243
+ const analyticsContext =
1244
+ dataSpaceAnalysisResult?.executionContextsIndex.get(context.name);
1245
+
1246
+ if (
1247
+ context.mappingProvider === undefined ||
1248
+ context.defaultRuntime !== undefined ||
1249
+ (analyticsContext?.compatibleRuntimes.length ?? 0) !== 0
1250
+ ) {
1251
+ continue;
1252
+ }
1253
+ if (!sharedRuntime) {
1254
+ sharedRuntime =
1255
+ await this.createOrGetDataSpaceLakehouseFallbackRuntime(isSnapshot);
1256
+ }
1257
+ context.defaultRuntime =
1258
+ PackageableElementExplicitReference.create(sharedRuntime);
1259
+ }
1260
+ }
1261
+
1224
1262
  /**
1225
1263
  * Centralized method to build a data product query builder state.
1226
1264
  * Used by both the creator flow (new query from data product route/picker)
@@ -1280,7 +1318,10 @@ export abstract class QueryEditorStore {
1280
1318
  versionId,
1281
1319
  },
1282
1320
  );
1283
- this.graphManagerState.graph.addElement(packageableRuntime, '_internal_');
1321
+ this.graphManagerState.graph.addElement(
1322
+ packageableRuntime,
1323
+ INTERNAL_ELEMENT_PATH,
1324
+ );
1284
1325
  }
1285
1326
  // 6. Create query builder state
1286
1327
  const projectInfo = { groupId, artifactId, versionId };
@@ -1835,7 +1876,7 @@ export class ExistingQueryUpdateState {
1835
1876
  * navigating to a chosen revision. A revision is keyed by its `version`
1836
1877
  * identifier, which is passed as the `revisionId` route param.
1837
1878
  */
1838
- const resolveExecutionContext = (
1879
+ export const resolveExecutionContext = (
1839
1880
  dataSpace: DataSpace,
1840
1881
  ex: string | undefined,
1841
1882
  queryMapping: Mapping | undefined,
@@ -1847,20 +1888,22 @@ const resolveExecutionContext = (
1847
1888
  }
1848
1889
  const defaultExecutionContext = dataSpace.defaultExecutionContext;
1849
1890
  if (!defaultExecutionContext) {
1850
- return undefined;
1891
+ return executionContexts[0];
1851
1892
  }
1852
1893
  if (queryMapping && queryRuntime) {
1853
- const defaultExecutionContextMapping = defaultExecutionContext.mapping;
1894
+ const defaultExecutionContextMapping = resolveExecutionContextMapping(
1895
+ defaultExecutionContext,
1896
+ );
1854
1897
  const defaultExecutionContextRuntime =
1855
1898
  defaultExecutionContext.defaultRuntime;
1856
1899
  if (
1857
1900
  defaultExecutionContextMapping &&
1858
1901
  defaultExecutionContextRuntime &&
1859
- defaultExecutionContextMapping.value !== queryMapping &&
1902
+ defaultExecutionContextMapping !== queryMapping &&
1860
1903
  defaultExecutionContextRuntime.value.path !== queryRuntime.path
1861
1904
  ) {
1862
1905
  const matchingExecContexts = executionContexts.filter(
1863
- (ec) => ec.mapping?.value === queryMapping,
1906
+ (ec) => resolveExecutionContextMapping(ec) === queryMapping,
1864
1907
  );
1865
1908
  if (matchingExecContexts.length > 1) {
1866
1909
  const matchRuntime = matchingExecContexts.find(
@@ -2100,6 +2143,12 @@ export class ExistingQueryEditorStore extends QueryEditorStore {
2100
2143
  exec.dataSpacePath,
2101
2144
  this.graphManagerState.graph,
2102
2145
  );
2146
+ await this.attachDataSpaceFallbackRuntimes(
2147
+ dataSpace,
2148
+ dataSpaceAnalysisResult,
2149
+ isSnapshotVersion(queryInfo.versionId) ||
2150
+ queryInfo.versionId === SNAPSHOT_VERSION_ALIAS,
2151
+ );
2103
2152
  const mapping = queryInfo.mapping
2104
2153
  ? this.graphManagerState.graph.getMapping(queryInfo.mapping)
2105
2154
  : undefined;
@@ -2228,36 +2277,43 @@ export class ExistingQueryEditorStore extends QueryEditorStore {
2228
2277
  this.applicationStore.config.options.queryBuilderConfig,
2229
2278
  sourceInfo,
2230
2279
  );
2231
- const matchingExecutionContextMapping = guaranteeNonNullable(
2232
- matchingExecutionContext.mapping,
2233
- `Execution context '${matchingExecutionContext.name}' does not have a mapping`,
2234
- );
2235
- const matchingExecutionContextRuntime = guaranteeNonNullable(
2236
- matchingExecutionContext.defaultRuntime,
2237
- `Execution context '${matchingExecutionContext.name}' does not have a default runtime`,
2280
+ const matchingExecutionContextMapping = resolveExecutionContextMapping(
2281
+ matchingExecutionContext,
2238
2282
  );
2239
- const mappingModelCoverageAnalysisResult =
2240
- dataSpaceAnalysisResult?.mappingToMappingCoverageResult?.get(
2241
- matchingExecutionContextMapping.value.path,
2283
+ if (
2284
+ matchingExecutionContext.mappingProvider &&
2285
+ !matchingExecutionContextMapping
2286
+ ) {
2287
+ throw new Error(
2288
+ `Execution context '${matchingExecutionContext.name}' in data space '${dataSpace.path}' sources its mapping from a data product access point group that does not exist.`,
2242
2289
  );
2290
+ }
2291
+ const mappingModelCoverageAnalysisResult =
2292
+ matchingExecutionContextMapping
2293
+ ? dataSpaceAnalysisResult?.mappingToMappingCoverageResult?.get(
2294
+ matchingExecutionContextMapping.path,
2295
+ )
2296
+ : undefined;
2243
2297
  if (mappingModelCoverageAnalysisResult) {
2244
2298
  dataSpaceQueryBuilderState.explorerState.mappingModelCoverageAnalysisResult =
2245
2299
  mappingModelCoverageAnalysisResult;
2246
2300
  }
2247
- dataSpaceQueryBuilderState.executionContextState.setMapping(
2248
- matchingExecutionContextMapping.value,
2249
- );
2301
+ if (matchingExecutionContextMapping) {
2302
+ dataSpaceQueryBuilderState.executionContextState.setMapping(
2303
+ matchingExecutionContextMapping,
2304
+ );
2305
+ }
2306
+ const matchingExecutionContextRuntime =
2307
+ matchingExecutionContext.defaultRuntime
2308
+ ? new RuntimePointer(matchingExecutionContext.defaultRuntime)
2309
+ : undefined;
2250
2310
  dataSpaceQueryBuilderState.executionContextState.setRuntimeValue(
2251
- new RuntimePointer(
2252
- PackageableElementExplicitReference.create(
2253
- matchingExecutionContextRuntime.value,
2254
- ),
2255
- ),
2311
+ matchingExecutionContextRuntime,
2256
2312
  );
2257
2313
  return dataSpaceQueryBuilderState;
2258
2314
  } else {
2259
- throw new UnsupportedOperationError(
2260
- `Unsupported execution context ${exec.executionKey}`,
2315
+ throw new Error(
2316
+ `Execution context '${exec.executionKey}' does not exist in data space '${dataSpace.path}'.`,
2261
2317
  );
2262
2318
  }
2263
2319
  } else if (exec instanceof QueryExplicitExecutionContextInfo) {
@@ -2347,7 +2403,10 @@ export class ExistingQueryEditorStore extends QueryEditorStore {
2347
2403
  exec.ingestDefinitionPath,
2348
2404
  projectInfo,
2349
2405
  );
2350
- this.graphManagerState.graph.addElement(adhocRuntime, '_internal_');
2406
+ this.graphManagerState.graph.addElement(
2407
+ adhocRuntime,
2408
+ INTERNAL_ELEMENT_PATH,
2409
+ );
2351
2410
 
2352
2411
  // Tracked separately from the closure on `ingestQueryBuilderState` so
2353
2412
  // the swap callback can capture it directly without forming a self-