@finos/legend-application-query 13.8.29 → 13.8.31

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 (76) hide show
  1. package/lib/__lib__/LegendQueryEvent.d.ts +3 -0
  2. package/lib/__lib__/LegendQueryEvent.d.ts.map +1 -1
  3. package/lib/__lib__/LegendQueryEvent.js +3 -0
  4. package/lib/__lib__/LegendQueryEvent.js.map +1 -1
  5. package/lib/__lib__/LegendQueryNavigation.d.ts +12 -0
  6. package/lib/__lib__/LegendQueryNavigation.d.ts.map +1 -1
  7. package/lib/__lib__/LegendQueryNavigation.js +16 -0
  8. package/lib/__lib__/LegendQueryNavigation.js.map +1 -1
  9. package/lib/__lib__/LegendQueryTelemetryHelper.d.ts +3 -0
  10. package/lib/__lib__/LegendQueryTelemetryHelper.d.ts.map +1 -1
  11. package/lib/__lib__/LegendQueryTelemetryHelper.js +9 -0
  12. package/lib/__lib__/LegendQueryTelemetryHelper.js.map +1 -1
  13. package/lib/components/Core_LegendQueryApplicationPlugin.d.ts.map +1 -1
  14. package/lib/components/Core_LegendQueryApplicationPlugin.js +15 -0
  15. package/lib/components/Core_LegendQueryApplicationPlugin.js.map +1 -1
  16. package/lib/components/LegendQueryWebApplication.d.ts.map +1 -1
  17. package/lib/components/LegendQueryWebApplication.js +2 -1
  18. package/lib/components/LegendQueryWebApplication.js.map +1 -1
  19. package/lib/components/QueryEditor.d.ts.map +1 -1
  20. package/lib/components/QueryEditor.js +12 -2
  21. package/lib/components/QueryEditor.js.map +1 -1
  22. package/lib/components/data-product/LegendQueryDataProductQueryBuilder.d.ts.map +1 -1
  23. package/lib/components/data-product/LegendQueryDataProductQueryBuilder.js +6 -40
  24. package/lib/components/data-product/LegendQueryDataProductQueryBuilder.js.map +1 -1
  25. package/lib/components/ingest/IngestInfo.d.ts +32 -0
  26. package/lib/components/ingest/IngestInfo.d.ts.map +1 -0
  27. package/lib/components/ingest/IngestInfo.js +63 -0
  28. package/lib/components/ingest/IngestInfo.js.map +1 -0
  29. package/lib/components/ingest/IngestQueryBuilderSetupPanel.d.ts +18 -0
  30. package/lib/components/ingest/IngestQueryBuilderSetupPanel.d.ts.map +1 -0
  31. package/lib/components/ingest/IngestQueryBuilderSetupPanel.js +77 -0
  32. package/lib/components/ingest/IngestQueryBuilderSetupPanel.js.map +1 -0
  33. package/lib/components/ingest/IngestQueryCreator.d.ts +27 -0
  34. package/lib/components/ingest/IngestQueryCreator.d.ts.map +1 -0
  35. package/lib/components/ingest/IngestQueryCreator.js +45 -0
  36. package/lib/components/ingest/IngestQueryCreator.js.map +1 -0
  37. package/lib/components/shared/LakehouseRuntimeConfigModal.d.ts +29 -0
  38. package/lib/components/shared/LakehouseRuntimeConfigModal.d.ts.map +1 -0
  39. package/lib/components/shared/LakehouseRuntimeConfigModal.js +60 -0
  40. package/lib/components/shared/LakehouseRuntimeConfigModal.js.map +1 -0
  41. package/lib/index.css +1 -1
  42. package/lib/light-mode.css +1 -1
  43. package/lib/package.json +1 -1
  44. package/lib/stores/QueryEditorStore.d.ts +10 -0
  45. package/lib/stores/QueryEditorStore.d.ts.map +1 -1
  46. package/lib/stores/QueryEditorStore.js +81 -2
  47. package/lib/stores/QueryEditorStore.js.map +1 -1
  48. package/lib/stores/ingest/IngestLegendQueryBuilderState.d.ts +122 -0
  49. package/lib/stores/ingest/IngestLegendQueryBuilderState.d.ts.map +1 -0
  50. package/lib/stores/ingest/IngestLegendQueryBuilderState.js +191 -0
  51. package/lib/stores/ingest/IngestLegendQueryBuilderState.js.map +1 -0
  52. package/lib/stores/ingest/IngestQueryCreatorStore.d.ts +75 -0
  53. package/lib/stores/ingest/IngestQueryCreatorStore.d.ts.map +1 -0
  54. package/lib/stores/ingest/IngestQueryCreatorStore.js +169 -0
  55. package/lib/stores/ingest/IngestQueryCreatorStore.js.map +1 -0
  56. package/lib/stores/ingest/IngestQueryGraphHelper.d.ts +58 -0
  57. package/lib/stores/ingest/IngestQueryGraphHelper.d.ts.map +1 -0
  58. package/lib/stores/ingest/IngestQueryGraphHelper.js +72 -0
  59. package/lib/stores/ingest/IngestQueryGraphHelper.js.map +1 -0
  60. package/package.json +13 -13
  61. package/src/__lib__/LegendQueryEvent.ts +3 -0
  62. package/src/__lib__/LegendQueryNavigation.ts +35 -0
  63. package/src/__lib__/LegendQueryTelemetryHelper.ts +15 -0
  64. package/src/components/Core_LegendQueryApplicationPlugin.tsx +20 -0
  65. package/src/components/LegendQueryWebApplication.tsx +14 -0
  66. package/src/components/QueryEditor.tsx +25 -1
  67. package/src/components/data-product/LegendQueryDataProductQueryBuilder.tsx +7 -127
  68. package/src/components/ingest/IngestInfo.tsx +197 -0
  69. package/src/components/ingest/IngestQueryBuilderSetupPanel.tsx +204 -0
  70. package/src/components/ingest/IngestQueryCreator.tsx +75 -0
  71. package/src/components/shared/LakehouseRuntimeConfigModal.tsx +148 -0
  72. package/src/stores/QueryEditorStore.ts +131 -1
  73. package/src/stores/ingest/IngestLegendQueryBuilderState.ts +259 -0
  74. package/src/stores/ingest/IngestQueryCreatorStore.ts +262 -0
  75. package/src/stores/ingest/IngestQueryGraphHelper.ts +120 -0
  76. package/tsconfig.json +7 -0
@@ -76,8 +76,10 @@ import {
76
76
  QueryDataProductNativeExecutionContextInfo,
77
77
  QueryDataProductModelAccessExecutionContextInfo,
78
78
  QueryDataProductLakehouseExecutionContextInfo,
79
+ QueryIngestExecutionContextInfo,
79
80
  ModelAccessPointGroup,
80
81
  type DataProduct,
82
+ IngestDefinition,
81
83
  LakehouseRuntime,
82
84
  V1_DATA_PRODUCT_ELEMENT_PROTOCOL_TYPE,
83
85
  V1_DataProductArtifact,
@@ -152,6 +154,11 @@ import { generateDataSpaceQueryCreatorRoute } from '../__lib__/DSL_DataSpace_Leg
152
154
  import { hasDataSpaceInfoBeenVisited } from '../__lib__/LegendQueryUserDataSpaceHelper.js';
153
155
  import { LegendQueryDataSpaceQueryBuilderState } from './data-space/query-builder/LegendQueryDataSpaceQueryBuilderState.js';
154
156
  import { LegendQueryDataProductQueryBuilderState } from './data-product/query-builder/LegendQueryDataProductQueryBuilderState.js';
157
+ import { IngestLegendQueryBuilderState } from './ingest/IngestLegendQueryBuilderState.js';
158
+ import {
159
+ fetchIngestEntitiesByClassifier,
160
+ swapIngestInGraph,
161
+ } from './ingest/IngestQueryGraphHelper.js';
155
162
  import { DataProductSelectorState } from './data-space/DataProductSelectorState.js';
156
163
  import {
157
164
  decorateEnvWithRealm,
@@ -317,6 +324,7 @@ export abstract class QueryEditorStore {
317
324
  showAppInfo = false;
318
325
  showDataspaceInfo = false;
319
326
  showDataProductInfo = false;
327
+ showIngestInfo = false;
320
328
  enableMinialGraphForDataSpaceLoadingPerformance = true;
321
329
 
322
330
  constructor(
@@ -331,6 +339,7 @@ export abstract class QueryEditorStore {
331
339
  showAppInfo: observable,
332
340
  showDataspaceInfo: observable,
333
341
  showDataProductInfo: observable,
342
+ showIngestInfo: observable,
334
343
  queryBuilderState: observable,
335
344
  enableMinialGraphForDataSpaceLoadingPerformance: observable,
336
345
  isPerformingBlockingAction: computed,
@@ -339,6 +348,7 @@ export abstract class QueryEditorStore {
339
348
  setShowAppInfo: action,
340
349
  setShowDataspaceInfo: action,
341
350
  setShowDataProductInfo: action,
351
+ setShowIngestInfo: action,
342
352
  setEnableMinialGraphForDataSpaceLoadingPerformance: action,
343
353
  initialize: flow,
344
354
  buildGraph: flow,
@@ -453,6 +463,10 @@ export abstract class QueryEditorStore {
453
463
  this.showDataProductInfo = val;
454
464
  }
455
465
 
466
+ setShowIngestInfo(val: boolean): void {
467
+ this.showIngestInfo = val;
468
+ }
469
+
456
470
  setShowRegisterServiceModal(val: boolean): void {
457
471
  this.showRegisterServiceModal = val;
458
472
  }
@@ -1926,13 +1940,64 @@ export class ExistingQueryEditorStore extends QueryEditorStore {
1926
1940
  queryInfo?.executionContext instanceof
1927
1941
  QueryDataProductModelAccessExecutionContextInfo ||
1928
1942
  queryInfo?.executionContext instanceof
1929
- QueryDataProductLakehouseExecutionContextInfo
1943
+ QueryDataProductLakehouseExecutionContextInfo ||
1944
+ queryInfo?.executionContext instanceof QueryIngestExecutionContextInfo
1930
1945
  )
1931
1946
  ) {
1932
1947
  yield flowResult(this.buildFullGraph());
1948
+ } else if (
1949
+ queryInfo?.executionContext instanceof QueryIngestExecutionContextInfo
1950
+ ) {
1951
+ // Mirror the creator-store flow: build a graph containing only the
1952
+ // referenced ingest definition. The full project entity fetch is
1953
+ // skipped, but we still list every ingest in the project so the source
1954
+ // dropdown can show them and `swapIngestDefinition` can build a
1955
+ // different one on demand.
1956
+ yield* this.buildIngestGraph(
1957
+ queryInfo.groupId,
1958
+ queryInfo.artifactId,
1959
+ queryInfo.versionId,
1960
+ queryInfo.executionContext.ingestDefinitionPath,
1961
+ );
1933
1962
  }
1934
1963
  }
1935
1964
 
1965
+ /**
1966
+ * Cached ingest entities for the existing-query flow. Populated by
1967
+ * {@link buildIngestGraph} (called from {@link buildGraph}) and consumed by
1968
+ * {@link initQueryBuildStateFromQuery} to back the source dropdown +
1969
+ * {@link IngestLegendQueryBuilderState.swapIngest} callback.
1970
+ */
1971
+ private _ingestEntitiesByPath: Map<string, Entity> = new Map();
1972
+
1973
+ private *buildIngestGraph(
1974
+ groupId: string,
1975
+ artifactId: string,
1976
+ versionId: string,
1977
+ ingestDefinitionPath: string,
1978
+ ): GeneratorFn<void> {
1979
+ yield this.graphManagerState.initializeSystem();
1980
+
1981
+ this._ingestEntitiesByPath = (yield fetchIngestEntitiesByClassifier(
1982
+ this.depotServerClient,
1983
+ groupId,
1984
+ artifactId,
1985
+ versionId,
1986
+ )) as Map<string, Entity>;
1987
+
1988
+ const target = this._ingestEntitiesByPath.get(ingestDefinitionPath);
1989
+ if (!target) {
1990
+ throw new Error(
1991
+ `Can't find ingest definition '${ingestDefinitionPath}' in project ${groupId}:${artifactId}:${versionId}`,
1992
+ );
1993
+ }
1994
+ yield swapIngestInGraph(this.graphManagerState, target, {
1995
+ groupId,
1996
+ artifactId,
1997
+ versionId,
1998
+ });
1999
+ }
2000
+
1936
2001
  override async setUpEditorState(): Promise<void> {
1937
2002
  const queryInfo = await this.graphManagerState.graphManager.getQueryInfo(
1938
2003
  this.queryId,
@@ -2191,6 +2256,71 @@ export class ExistingQueryEditorStore extends QueryEditorStore {
2191
2256
  },
2192
2257
  );
2193
2258
  return queryBuilderState;
2259
+ } else if (exec instanceof QueryIngestExecutionContextInfo) {
2260
+ // The graph was already populated by `buildIngestGraph` (above) with
2261
+ // just the referenced ingest definition. Resolve it, build the adhoc
2262
+ // lakehouse runtime, and stand up the ingest query builder state.
2263
+ const ingestDefinition = guaranteeType(
2264
+ this.graphManagerState.graph.getElement(exec.ingestDefinitionPath),
2265
+ IngestDefinition,
2266
+ `Can't find ingest definition '${exec.ingestDefinitionPath}'`,
2267
+ );
2268
+ const projectInfo = this.getProjectInfo();
2269
+ const adhocRuntime = await this.createLakehousePackageableRuntime(
2270
+ exec.ingestDefinitionPath,
2271
+ projectInfo,
2272
+ );
2273
+ this.graphManagerState.graph.addElement(adhocRuntime, '_internal_');
2274
+
2275
+ // Tracked separately from the closure on `ingestQueryBuilderState` so
2276
+ // the swap callback can capture it directly without forming a self-
2277
+ // reference (which trips the `no-unsafe-assignment` rule).
2278
+ let currentIngestPath = exec.ingestDefinitionPath;
2279
+ const swapIngest = async (path: string): Promise<IngestDefinition> => {
2280
+ // Mirror `IngestQueryCreatorStore.swapIngestDefinition`: rebuild
2281
+ // the requested ingest in place of the current one. The entity
2282
+ // cache was populated up front by `buildIngestGraph`.
2283
+ const entity = this._ingestEntitiesByPath.get(path);
2284
+ if (!entity) {
2285
+ throw new Error(
2286
+ `Can't find ingest definition '${path}' in project ${projectInfo.groupId}:${projectInfo.artifactId}:${projectInfo.versionId}`,
2287
+ );
2288
+ }
2289
+ const next = await swapIngestInGraph(this.graphManagerState, entity, {
2290
+ currentPath: currentIngestPath,
2291
+ groupId: projectInfo.groupId,
2292
+ artifactId: projectInfo.artifactId,
2293
+ versionId: projectInfo.versionId,
2294
+ });
2295
+ currentIngestPath = next.path;
2296
+ return next;
2297
+ };
2298
+
2299
+ const ingestQueryBuilderState = new IngestLegendQueryBuilderState(
2300
+ this.applicationStore,
2301
+ undefined,
2302
+ this.graphManagerState,
2303
+ QueryBuilderDataBrowserWorkflow.INSTANCE,
2304
+ new QueryBuilderActionConfig_QueryApplication(this),
2305
+ ingestDefinition,
2306
+ Array.from(this._ingestEntitiesByPath.keys()),
2307
+ swapIngest,
2308
+ adhocRuntime,
2309
+ projectInfo,
2310
+ this.applicationStore.config.options.queryBuilderConfig,
2311
+ {
2312
+ groupId: projectInfo.groupId,
2313
+ artifactId: projectInfo.artifactId,
2314
+ versionId: projectInfo.versionId,
2315
+ queryId: queryInfo.id,
2316
+ },
2317
+ );
2318
+ await ingestQueryBuilderState.changeAccessorOwner(ingestDefinition);
2319
+ await ingestQueryBuilderState.changeAccessor({
2320
+ tableName: exec.dataSet,
2321
+ });
2322
+ ingestQueryBuilderState.changeSelectedRuntime(adhocRuntime);
2323
+ return ingestQueryBuilderState;
2194
2324
  }
2195
2325
  throw new UnsupportedOperationError(`Unsupported query execution context`);
2196
2326
  }
@@ -0,0 +1,259 @@
1
+ /**
2
+ * Copyright (c) 2020-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 {
18
+ type Accessor,
19
+ type AccessorOwner,
20
+ type GraphManagerState,
21
+ type IngestDefinition,
22
+ type PackageableElement,
23
+ type PackageableRuntime,
24
+ type QueryExecutionContext,
25
+ LakehouseRuntime,
26
+ QueryIngestExecutionContext,
27
+ } from '@finos/legend-graph';
28
+ import {
29
+ AccessorQueryBuilderState,
30
+ type QueryBuilderActionConfig,
31
+ type QueryBuilderConfig,
32
+ type QueryBuilderWorkflowState,
33
+ } from '@finos/legend-query-builder';
34
+ import type { GenericLegendApplicationStore } from '@finos/legend-application';
35
+ import type {
36
+ ProjectGAVCoordinates,
37
+ QueryableSourceInfo,
38
+ } from '@finos/legend-storage';
39
+ import { action, makeObservable, observable, runInAction } from 'mobx';
40
+ import { assertErrorThrown } from '@finos/legend-shared';
41
+ import { renderIngestQueryBuilderSetupPanelContent } from '../../components/ingest/IngestQueryBuilderSetupPanel.js';
42
+ import { generateIngestQueryCreatorRoute } from '../../__lib__/LegendQueryNavigation.js';
43
+
44
+ /**
45
+ * Query builder state for an ingest-backed query.
46
+ *
47
+ * Extends {@link AccessorQueryBuilderState} because an ingest definition is
48
+ * already an `AccessorOwner` (see `AccessorQueryBuilderState.accessorOwners`)
49
+ * and ingest queries don't require a mapping (`requiresMappingForExecution`
50
+ * is already `false` in the base class).
51
+ *
52
+ * The execution context produced here is a {@link QueryIngestExecutionContext}
53
+ * carrying `ingestDefinitionPath` and `dataSet` — the same two values the
54
+ * route handler resolved from the URL.
55
+ */
56
+ export class IngestLegendQueryBuilderState extends AccessorQueryBuilderState {
57
+ /**
58
+ * Currently selected & graph-resolved ingest definition. Observable so the
59
+ * setup panel re-renders when the user picks a different ingest from the
60
+ * source dropdown.
61
+ */
62
+ ingestDefinition: IngestDefinition;
63
+ /**
64
+ * All ingest definition paths discovered in the project (fetched up front
65
+ * via depot's classifier-scoped entities endpoint). Only `ingestDefinition`
66
+ * is actually built into the graph — the others are surfaced as paths so
67
+ * the user can switch without paying the cost of building them all.
68
+ */
69
+ readonly allIngestPaths: string[];
70
+ /**
71
+ * Swap callback wired by {@link IngestQueryCreatorStore}. Deletes the
72
+ * currently built ingest from the graph, builds the target one from its
73
+ * cached entity, and returns the new metamodel instance.
74
+ */
75
+ readonly swapIngest: (path: string) => Promise<IngestDefinition>;
76
+ readonly project: ProjectGAVCoordinates;
77
+ /**
78
+ * Adhoc lakehouse runtime built from the user's lakehouse environment +
79
+ * consumer warehouse, mirroring how `LegendQueryDataProductQueryBuilderState`
80
+ * builds its runtime for model-access / lakehouse access points. Created by
81
+ * the creator store (where the depot/lakehouse clients live) and injected
82
+ * here so the editor can execute against it without a packaged runtime.
83
+ */
84
+ readonly adhocRuntime: PackageableRuntime;
85
+ /**
86
+ * Loading flag flipped while {@link changeIngestDefinition} is fetching /
87
+ * rebuilding the newly selected ingest. Lets the setup panel disable the
88
+ * source dropdown during the swap so we don't get overlapping changes.
89
+ */
90
+ isSwappingIngest = false;
91
+
92
+ constructor(
93
+ applicationStore: GenericLegendApplicationStore,
94
+ accessor: Accessor | undefined,
95
+ graphManagerState: GraphManagerState,
96
+ workflow: QueryBuilderWorkflowState,
97
+ actionConfig: QueryBuilderActionConfig,
98
+ ingestDefinition: IngestDefinition,
99
+ allIngestPaths: string[],
100
+ swapIngest: (path: string) => Promise<IngestDefinition>,
101
+ adhocRuntime: PackageableRuntime,
102
+ project: ProjectGAVCoordinates,
103
+ config?: QueryBuilderConfig | undefined,
104
+ sourceInfo?: QueryableSourceInfo | undefined,
105
+ ) {
106
+ super(
107
+ applicationStore,
108
+ accessor,
109
+ graphManagerState,
110
+ workflow,
111
+ actionConfig,
112
+ config,
113
+ sourceInfo,
114
+ );
115
+ makeObservable(this, {
116
+ ingestDefinition: observable,
117
+ isSwappingIngest: observable,
118
+ changeIngestDefinition: action,
119
+ });
120
+ this.ingestDefinition = ingestDefinition;
121
+ this.allIngestPaths = allIngestPaths;
122
+ this.swapIngest = swapIngest;
123
+ this.adhocRuntime = adhocRuntime;
124
+ this.project = project;
125
+ }
126
+
127
+ override TEMPORARY__setupPanelContentRenderer = (): React.ReactNode =>
128
+ renderIngestQueryBuilderSetupPanelContent(this);
129
+
130
+ /**
131
+ * The currently selected data set. Derived from `sourceAccessor` so it
132
+ * stays in sync with `changeAccessor` (e.g. when the user picks a different
133
+ * data set, or when {@link changeIngestDefinition} re-points the source).
134
+ */
135
+ get dataSet(): string {
136
+ return this.sourceAccessor?.accessor ?? '';
137
+ }
138
+
139
+ override getQueryExecutionContext(): QueryExecutionContext {
140
+ const exec = new QueryIngestExecutionContext();
141
+ exec.ingestDefinitionPath = this.ingestDefinition.path;
142
+ exec.dataSet = this.dataSet;
143
+ return exec;
144
+ }
145
+
146
+ /**
147
+ * The deep-link flow opens the editor against a single, pre-resolved ingest
148
+ * definition. The user shouldn't be picking a different owner, but we still
149
+ * surface the current ingest so the source dropdown reflects the selection
150
+ * instead of rendering the empty "Choose a source..." placeholder.
151
+ *
152
+ * The full list of selectable ingests is exposed separately via
153
+ * {@link allIngestPaths} (the others aren't built into the graph yet — see
154
+ * {@link changeIngestDefinition}).
155
+ */
156
+ override get accessorOwners(): AccessorOwner[] {
157
+ return [this.ingestDefinition];
158
+ }
159
+
160
+ /**
161
+ * Execution always goes through {@link adhocRuntime} (selected by the
162
+ * creator store). Return an empty list so the runtime dropdown is hidden
163
+ * and the user can't override it.
164
+ */
165
+ override get compatibleRuntimes(): PackageableRuntime[] {
166
+ return [];
167
+ }
168
+
169
+ /**
170
+ * The adhoc lakehouse runtime is registered on the graph under the
171
+ * `_internal_` package so the editor can resolve it, but it is not part
172
+ * of the published project. Surface it as a floating execution element so
173
+ * it's bundled into the execution payload at run time (mirrors what the
174
+ * data-product flow does for its lakehouse runtime).
175
+ */
176
+ override get floatingExecutionElements(): PackageableElement[] | undefined {
177
+ return this.graphManagerState.graph.origin !== undefined
178
+ ? [this.adhocRuntime]
179
+ : undefined;
180
+ }
181
+
182
+ /**
183
+ * Convenience accessor: the underlying {@link LakehouseRuntime} on the
184
+ * adhoc runtime, if any. Used by the setup panel to feed the runtime
185
+ * configuration modal.
186
+ */
187
+ get lakehouseRuntime(): LakehouseRuntime | undefined {
188
+ return this.adhocRuntime.runtimeValue instanceof LakehouseRuntime
189
+ ? this.adhocRuntime.runtimeValue
190
+ : undefined;
191
+ }
192
+
193
+ /**
194
+ * Swap the active ingest definition to the one at `path`. Triggered by the
195
+ * source dropdown in {@link IngestQueryBuilderSetupPanel}. Delegates the
196
+ * graph mutation (delete current, build new) to {@link swapIngest} and then
197
+ * rewires the source accessor so the editor reflects the new ingest's
198
+ * datasets.
199
+ */
200
+ async changeIngestDefinition(path: string): Promise<void> {
201
+ if (path === this.ingestDefinition.path || this.isSwappingIngest) {
202
+ return;
203
+ }
204
+ runInAction(() => {
205
+ this.isSwappingIngest = true;
206
+ });
207
+ try {
208
+ const next = await this.swapIngest(path);
209
+ runInAction(() => {
210
+ this.ingestDefinition = next;
211
+ });
212
+ // Re-derive `sourceAccessor` against the new ingest. The previously
213
+ // selected data set most likely doesn't exist on the new ingest, so
214
+ // pick the first available one (if any).
215
+ await this.changeAccessorOwner(next);
216
+ const firstAccessor = this.accessors[0];
217
+ if (firstAccessor) {
218
+ await this.changeAccessor(firstAccessor);
219
+ }
220
+ } catch (error) {
221
+ assertErrorThrown(error);
222
+ this.applicationStore.notificationService.notifyError(error);
223
+ } finally {
224
+ runInAction(() => {
225
+ this.isSwappingIngest = false;
226
+ });
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Copies the deep-link to this ingest query set up (GAV + ingest definition
232
+ * path + data set) to the user's clipboard. Mirrors
233
+ * `LegendQueryDataProductQueryBuilderState.copyDataProductLinkToClipBoard`.
234
+ */
235
+ copyIngestQueryLinkToClipBoard(): void {
236
+ const route =
237
+ this.applicationStore.navigationService.navigator.generateAddress(
238
+ generateIngestQueryCreatorRoute(
239
+ this.project.groupId,
240
+ this.project.artifactId,
241
+ this.project.versionId,
242
+ this.ingestDefinition.path,
243
+ this.dataSet,
244
+ ),
245
+ );
246
+ navigator.clipboard
247
+ .writeText(route)
248
+ .then(() =>
249
+ this.applicationStore.notificationService.notifySuccess(
250
+ 'Copied ingest query set up link to clipboard',
251
+ ),
252
+ )
253
+ .catch(() =>
254
+ this.applicationStore.notificationService.notifyError(
255
+ 'Error copying ingest query set up link to clipboard',
256
+ ),
257
+ );
258
+ }
259
+ }