@finos/legend-application-query 13.8.8 → 13.8.10

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 (66) hide show
  1. package/lib/__lib__/LegendQueryNavigation.d.ts +16 -2
  2. package/lib/__lib__/LegendQueryNavigation.d.ts.map +1 -1
  3. package/lib/__lib__/LegendQueryNavigation.js +21 -2
  4. package/lib/__lib__/LegendQueryNavigation.js.map +1 -1
  5. package/lib/components/Core_LegendQueryApplicationPlugin.d.ts +2 -1
  6. package/lib/components/Core_LegendQueryApplicationPlugin.d.ts.map +1 -1
  7. package/lib/components/Core_LegendQueryApplicationPlugin.js +13 -0
  8. package/lib/components/Core_LegendQueryApplicationPlugin.js.map +1 -1
  9. package/lib/components/LegendQueryWebApplication.d.ts.map +1 -1
  10. package/lib/components/LegendQueryWebApplication.js +2 -1
  11. package/lib/components/LegendQueryWebApplication.js.map +1 -1
  12. package/lib/components/__test-utils__/QueryEditorComponentTestUtils.d.ts +14 -2
  13. package/lib/components/__test-utils__/QueryEditorComponentTestUtils.d.ts.map +1 -1
  14. package/lib/components/__test-utils__/QueryEditorComponentTestUtils.js +91 -5
  15. package/lib/components/__test-utils__/QueryEditorComponentTestUtils.js.map +1 -1
  16. package/lib/components/data-product/DataProductSampleQueryCreator.d.ts +19 -0
  17. package/lib/components/data-product/DataProductSampleQueryCreator.d.ts.map +1 -0
  18. package/lib/components/data-product/DataProductSampleQueryCreator.js +53 -0
  19. package/lib/components/data-product/DataProductSampleQueryCreator.js.map +1 -0
  20. package/lib/components/data-product/DataProductSampleQueryPanel.d.ts +23 -0
  21. package/lib/components/data-product/DataProductSampleQueryPanel.d.ts.map +1 -0
  22. package/lib/components/data-product/DataProductSampleQueryPanel.js +95 -0
  23. package/lib/components/data-product/DataProductSampleQueryPanel.js.map +1 -0
  24. package/lib/index.css +1 -1
  25. package/lib/light-mode.css +1 -1
  26. package/lib/package.json +1 -1
  27. package/lib/stores/BaseTemplateQueryCreatorStore.d.ts +68 -0
  28. package/lib/stores/BaseTemplateQueryCreatorStore.d.ts.map +1 -0
  29. package/lib/stores/BaseTemplateQueryCreatorStore.js +120 -0
  30. package/lib/stores/BaseTemplateQueryCreatorStore.js.map +1 -0
  31. package/lib/stores/QueryEditorStore.d.ts +2 -2
  32. package/lib/stores/QueryEditorStore.d.ts.map +1 -1
  33. package/lib/stores/QueryEditorStore.js +64 -30
  34. package/lib/stores/QueryEditorStore.js.map +1 -1
  35. package/lib/stores/data-product/DataProductSampleQueryCreatorStore.d.ts +37 -0
  36. package/lib/stores/data-product/DataProductSampleQueryCreatorStore.d.ts.map +1 -0
  37. package/lib/stores/data-product/DataProductSampleQueryCreatorStore.js +60 -0
  38. package/lib/stores/data-product/DataProductSampleQueryCreatorStore.js.map +1 -0
  39. package/lib/stores/data-product/query-builder/DataProductArtifactHelper.d.ts.map +1 -1
  40. package/lib/stores/data-product/query-builder/DataProductArtifactHelper.js +9 -0
  41. package/lib/stores/data-product/query-builder/DataProductArtifactHelper.js.map +1 -1
  42. package/lib/stores/data-product/query-builder/LegendQueryDataProductQueryBuilderState.d.ts +12 -2
  43. package/lib/stores/data-product/query-builder/LegendQueryDataProductQueryBuilderState.d.ts.map +1 -1
  44. package/lib/stores/data-product/query-builder/LegendQueryDataProductQueryBuilderState.js +40 -6
  45. package/lib/stores/data-product/query-builder/LegendQueryDataProductQueryBuilderState.js.map +1 -1
  46. package/lib/stores/data-space/DataProductQueryCreatorStore.js +1 -1
  47. package/lib/stores/data-space/DataProductQueryCreatorStore.js.map +1 -1
  48. package/lib/stores/data-space/DataSpaceTemplateQueryCreatorStore.d.ts +14 -18
  49. package/lib/stores/data-space/DataSpaceTemplateQueryCreatorStore.d.ts.map +1 -1
  50. package/lib/stores/data-space/DataSpaceTemplateQueryCreatorStore.js +15 -80
  51. package/lib/stores/data-space/DataSpaceTemplateQueryCreatorStore.js.map +1 -1
  52. package/package.json +13 -13
  53. package/src/__lib__/LegendQueryNavigation.ts +43 -5
  54. package/src/components/Core_LegendQueryApplicationPlugin.tsx +17 -0
  55. package/src/components/LegendQueryWebApplication.tsx +5 -0
  56. package/src/components/__test-utils__/QueryEditorComponentTestUtils.tsx +227 -3
  57. package/src/components/data-product/DataProductSampleQueryCreator.tsx +114 -0
  58. package/src/components/data-product/DataProductSampleQueryPanel.tsx +219 -0
  59. package/src/stores/BaseTemplateQueryCreatorStore.ts +203 -0
  60. package/src/stores/QueryEditorStore.ts +105 -51
  61. package/src/stores/data-product/DataProductSampleQueryCreatorStore.ts +135 -0
  62. package/src/stores/data-product/query-builder/DataProductArtifactHelper.ts +13 -0
  63. package/src/stores/data-product/query-builder/LegendQueryDataProductQueryBuilderState.ts +70 -7
  64. package/src/stores/data-space/DataProductQueryCreatorStore.ts +1 -1
  65. package/src/stores/data-space/DataSpaceTemplateQueryCreatorStore.ts +31 -126
  66. package/tsconfig.json +4 -0
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Copyright (c) 2026-present, Goldman Sachs
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import {
18
+ type Query,
19
+ type QuerySearchSpecification,
20
+ type RawLambda,
21
+ type ValueSpecification,
22
+ QueryProjectCoordinates,
23
+ extractElementNameFromPath,
24
+ } from '@finos/legend-graph';
25
+ import { type DepotServerClient } from '@finos/legend-server-depot';
26
+ import {
27
+ assertErrorThrown,
28
+ LogEvent,
29
+ uuid,
30
+ type GeneratorFn,
31
+ } from '@finos/legend-shared';
32
+ import type { QueryBuilderState } from '@finos/legend-query-builder';
33
+ import {
34
+ type ProjectGAVCoordinates,
35
+ parseGACoordinates,
36
+ } from '@finos/legend-storage';
37
+ import {
38
+ type QueryPersistConfiguration,
39
+ QueryEditorStore,
40
+ } from './QueryEditorStore.js';
41
+ import type { LegendQueryApplicationStore } from './LegendQueryBaseStore.js';
42
+ import { createQueryClassTaggedValue } from '@finos/legend-extension-dsl-data-space/application';
43
+ import { processQueryParameters } from '../components/utils/QueryParameterUtils.js';
44
+ import { LEGEND_QUERY_APP_EVENT } from '../__lib__/LegendQueryEvent.js';
45
+
46
+ /**
47
+ * Abstract base store for template/sample query creator flows.
48
+ */
49
+ export abstract class BaseTemplateQueryCreatorStore extends QueryEditorStore {
50
+ readonly groupId: string;
51
+ readonly artifactId: string;
52
+ readonly versionId: string;
53
+ readonly templateQueryId: string;
54
+ templateQueryTitle?: string;
55
+ urlQueryParamValues: Record<string, string> | undefined;
56
+
57
+ constructor(
58
+ applicationStore: LegendQueryApplicationStore,
59
+ depotServerClient: DepotServerClient,
60
+ groupId: string,
61
+ artifactId: string,
62
+ versionId: string,
63
+ templateQueryId: string,
64
+ urlQueryParamValues: Record<string, string> | undefined,
65
+ ) {
66
+ super(applicationStore, depotServerClient);
67
+ this.groupId = groupId;
68
+ this.artifactId = artifactId;
69
+ this.versionId = versionId;
70
+ this.templateQueryId = templateQueryId;
71
+ this.urlQueryParamValues = urlQueryParamValues;
72
+ }
73
+
74
+ getProjectInfo(): ProjectGAVCoordinates {
75
+ return {
76
+ groupId: this.groupId,
77
+ artifactId: this.artifactId,
78
+ versionId: this.versionId,
79
+ };
80
+ }
81
+
82
+ override *buildGraph(): GeneratorFn<void> {
83
+ // do nothing — graph is built inside initializeQueryBuilderState
84
+ }
85
+
86
+ /**
87
+ * Returns the entity path (DataSpace path or DataProduct path) used for
88
+ * display names in persist configuration.
89
+ */
90
+ abstract getEntityPath(): string;
91
+
92
+ /**
93
+ * Returns the tagged values to attach to the search specification.
94
+ */
95
+ abstract getSearchTaggedValues(): {
96
+ profile: string;
97
+ tag: string;
98
+ value: string;
99
+ }[];
100
+
101
+ /**
102
+ * Returns the tagged values to attach to the persisted query
103
+ */
104
+ abstract getQueryDecoratorTaggedValues():
105
+ | { profile: string; tag: string; value: string }[]
106
+ | undefined;
107
+
108
+ /**
109
+ * Resolves URL query parameters into a default parameter values map.
110
+ * Shared by both DataSpace and DataProduct template/sample query flows.
111
+ */
112
+ async resolveDefaultParameters(
113
+ query: RawLambda,
114
+ ): Promise<Map<string, ValueSpecification> | undefined> {
115
+ const processedQueryParamValues = processQueryParameters(
116
+ query,
117
+ undefined,
118
+ this.urlQueryParamValues,
119
+ this.graphManagerState,
120
+ );
121
+ if (processedQueryParamValues?.size) {
122
+ try {
123
+ return await this.graphManagerState.graphManager.pureCodeToValueSpecifications(
124
+ processedQueryParamValues,
125
+ this.graphManagerState.graph,
126
+ );
127
+ } catch (error) {
128
+ assertErrorThrown(error);
129
+ this.applicationStore.logService.error(
130
+ LogEvent.create(LEGEND_QUERY_APP_EVENT.GENERIC_FAILURE),
131
+ `Error resolving preset query param values: ${error.message}`,
132
+ );
133
+ }
134
+ }
135
+ return undefined;
136
+ }
137
+
138
+ getPersistConfiguration(
139
+ lambda: RawLambda,
140
+ options?: { update: boolean | undefined },
141
+ ): QueryPersistConfiguration {
142
+ const entityPath = this.getEntityPath();
143
+ return {
144
+ defaultName: options?.update
145
+ ? `${extractElementNameFromPath(entityPath)}`
146
+ : `New Query for ${extractElementNameFromPath(entityPath)}[${this.templateQueryId}]`,
147
+ decorator: (query: Query): void => {
148
+ query.id = uuid();
149
+ query.groupId = this.groupId;
150
+ query.artifactId = this.artifactId;
151
+ query.versionId = this.versionId;
152
+ const decoratorTaggedValues = this.getQueryDecoratorTaggedValues();
153
+ if (decoratorTaggedValues) {
154
+ const taggedValues = decoratorTaggedValues.map((tv) => ({
155
+ profile: tv.profile,
156
+ tag: tv.tag,
157
+ value: tv.value,
158
+ }));
159
+ if (this.queryBuilderState?.sourceClass) {
160
+ taggedValues.push(
161
+ createQueryClassTaggedValue(
162
+ this.queryBuilderState.sourceClass.path,
163
+ ),
164
+ );
165
+ }
166
+ query.taggedValues = taggedValues;
167
+ } else if (this.queryBuilderState?.sourceClass) {
168
+ // Subclass handles tagged values directly (e.g., DataSpace only sets class tag)
169
+ query.taggedValues = [
170
+ createQueryClassTaggedValue(
171
+ this.queryBuilderState.sourceClass.path,
172
+ ),
173
+ ];
174
+ }
175
+ },
176
+ };
177
+ }
178
+
179
+ override decorateSearchSpecification(
180
+ val: QuerySearchSpecification,
181
+ ): QuerySearchSpecification {
182
+ const currentProjectCoordinates = new QueryProjectCoordinates();
183
+ currentProjectCoordinates.groupId = this.groupId;
184
+ currentProjectCoordinates.artifactId = this.artifactId;
185
+ val.projectCoordinates = [
186
+ currentProjectCoordinates,
187
+ ...Array.from(
188
+ this.graphManagerState.graph.dependencyManager.projectDependencyModelsIndex.keys(),
189
+ ).map((dependencyKey) => {
190
+ const { groupId, artifactId } = parseGACoordinates(dependencyKey);
191
+ const coordinates = new QueryProjectCoordinates();
192
+ coordinates.groupId = groupId;
193
+ coordinates.artifactId = artifactId;
194
+ return coordinates;
195
+ }),
196
+ ];
197
+ val.taggedValues = this.getSearchTaggedValues();
198
+ val.combineTaggedValuesCondition = true;
199
+ return val;
200
+ }
201
+
202
+ abstract override initializeQueryBuilderState(): Promise<QueryBuilderState>;
203
+ }
@@ -42,7 +42,7 @@ import {
42
42
  } from '@finos/legend-shared';
43
43
  import {
44
44
  type LightQuery,
45
- NativeModelExecutionContext,
45
+ type NativeModelExecutionContext,
46
46
  type RawLambda,
47
47
  type Runtime,
48
48
  type Service,
@@ -83,6 +83,8 @@ import {
83
83
  V1_ModelAccessPointGroupInfo,
84
84
  DataProductAccessType,
85
85
  type DataProductAnalysisQueryResult,
86
+ LakehouseAccessPoint,
87
+ type Accessor,
86
88
  } from '@finos/legend-graph';
87
89
  import {
88
90
  generateExistingQueryEditorRoute,
@@ -128,8 +130,9 @@ import {
128
130
  QueryBuilderDataBrowserWorkflow,
129
131
  QueryBuilderActionConfig,
130
132
  QUERY_LOADER_DEFAULT_QUERY_SEARCH_LIMIT,
131
- NativeModelDataProductExecutionState,
132
133
  ModelAccessPointDataProductExecutionState,
134
+ LakehouseDataProductExecutionState,
135
+ resolveDataProductAccessor,
133
136
  } from '@finos/legend-query-builder';
134
137
  import { LegendQueryUserDataHelper } from '../__lib__/LegendQueryUserDataHelper.js';
135
138
  import { LegendQueryTelemetryHelper } from '../__lib__/LegendQueryTelemetryHelper.js';
@@ -1045,24 +1048,53 @@ export abstract class QueryEditorStore {
1045
1048
  resolveDataProductExecutionState(
1046
1049
  dataProduct: DataProduct,
1047
1050
  accessId: string | undefined,
1048
- ): NativeModelExecutionContext | ModelAccessPointGroup {
1051
+ ):
1052
+ | NativeModelExecutionContext
1053
+ | ModelAccessPointGroup
1054
+ | LakehouseAccessPoint {
1049
1055
  // Search model access point groups
1050
1056
  const modelGroups = dataProduct.accessPointGroups.filter(
1051
1057
  filterByType(ModelAccessPointGroup),
1052
1058
  );
1053
- if (!modelGroups.length) {
1054
- throw new UnsupportedOperationError(
1055
- `Only Data Product with Model Access Points are currently supported in Query. ${dataProduct.path} does not have any Model Access Point Groups.`,
1056
- );
1057
- }
1058
1059
  if (accessId) {
1059
1060
  const matchingGroup = modelGroups.find((g) => g.id === accessId);
1060
1061
  if (matchingGroup) {
1061
1062
  return matchingGroup;
1062
1063
  }
1063
1064
  }
1064
- throw new UnsupportedOperationError(
1065
- `Can't resolve execution state for data product '${dataProduct.path}'${accessId ? ` with access ID '${accessId}'` : ''}`,
1065
+ // Search native model execution contexts
1066
+ const nativeAccess = dataProduct.nativeModelAccess;
1067
+ if (nativeAccess && accessId) {
1068
+ const matchingContext = nativeAccess.nativeModelExecutionContexts.find(
1069
+ (ctx) => ctx.key === accessId,
1070
+ );
1071
+ if (matchingContext) {
1072
+ return matchingContext;
1073
+ }
1074
+ }
1075
+
1076
+ // Fall back: prioritize first model access point group over native default
1077
+ const firstGroup = modelGroups[0];
1078
+ if (firstGroup) {
1079
+ return firstGroup;
1080
+ }
1081
+
1082
+ // Search lakehouse access points
1083
+ const lakehouseAccessPoints = dataProduct.accessPointGroups
1084
+ .flatMap((group) => group.accessPoints)
1085
+ .filter(filterByType(LakehouseAccessPoint));
1086
+ if (accessId) {
1087
+ const matchingLakehouseAP = lakehouseAccessPoints.find(
1088
+ (ap) => ap.id === accessId,
1089
+ );
1090
+ if (matchingLakehouseAP) {
1091
+ return matchingLakehouseAP;
1092
+ }
1093
+ }
1094
+
1095
+ return guaranteeNonNullable(
1096
+ modelGroups[0] ?? nativeAccess?.defaultExecutionContext,
1097
+ `Can't resolve execution state for data product '${dataProduct.path}'${accessId ? ` with access ID '${accessId}'` : ''}. Data product must have model access point groups or native model access.`,
1066
1098
  );
1067
1099
  }
1068
1100
 
@@ -1089,7 +1121,7 @@ export abstract class QueryEditorStore {
1089
1121
  );
1090
1122
 
1091
1123
  let userEnvironment: string | undefined = persistedInfo?.env;
1092
- const userWarehouse: string | undefined =
1124
+ const userWarehouse: string =
1093
1125
  persistedInfo?.snowflakeWarehouse ?? 'LAKEHOUSE_CONSUMER_DEFAULT_WH';
1094
1126
  // 2. If no persisted environment, fetch from the server
1095
1127
  if (userEnvironment === undefined && this.lakehouseState) {
@@ -1180,16 +1212,6 @@ export abstract class QueryEditorStore {
1180
1212
  accessId,
1181
1213
  artifact,
1182
1214
  );
1183
- // 3.5. Create a LakehouseRuntime and add it to the graph
1184
- const packageableRuntime = await this.createLakehousePackageableRuntime(
1185
- dataProductPath,
1186
- {
1187
- groupId,
1188
- artifactId,
1189
- versionId,
1190
- },
1191
- );
1192
- this.graphManagerState.graph.addElement(packageableRuntime, '_internal_');
1193
1215
  // 4. Get the data product from the built graph
1194
1216
  const dataProduct =
1195
1217
  this.graphManagerState.graph.getDataProduct(dataProductPath);
@@ -1199,6 +1221,23 @@ export abstract class QueryEditorStore {
1199
1221
  accessId,
1200
1222
  );
1201
1223
 
1224
+ // 5.5. Create LakehouseRuntime if the resolved state is a model access
1225
+ // point group or a lakehouse access point
1226
+ let packageableRuntime: PackageableRuntime | undefined;
1227
+ if (
1228
+ resolvedState instanceof ModelAccessPointGroup ||
1229
+ resolvedState instanceof LakehouseAccessPoint
1230
+ ) {
1231
+ packageableRuntime = await this.createLakehousePackageableRuntime(
1232
+ dataProductPath,
1233
+ {
1234
+ groupId,
1235
+ artifactId,
1236
+ versionId,
1237
+ },
1238
+ );
1239
+ this.graphManagerState.graph.addElement(packageableRuntime, '_internal_');
1240
+ }
1202
1241
  // 6. Create query builder state
1203
1242
  const projectInfo = { groupId, artifactId, versionId };
1204
1243
  const sourceInfo = {
@@ -1219,6 +1258,7 @@ export abstract class QueryEditorStore {
1219
1258
  this.depotServerClient,
1220
1259
  projectInfo,
1221
1260
  onDataProductChange,
1261
+ (path, gav) => this.createLakehousePackageableRuntime(path, gav),
1222
1262
  productSelectorState ??
1223
1263
  new DataProductSelectorState(
1224
1264
  this.depotServerClient,
@@ -1226,44 +1266,58 @@ export abstract class QueryEditorStore {
1226
1266
  ),
1227
1267
  onLegacyDataSpaceChange,
1228
1268
  undefined,
1229
- undefined,
1230
1269
  this.applicationStore.config.options.queryBuilderConfig,
1231
1270
  sourceInfo,
1232
1271
  );
1233
- // Pass pre-resolved state to avoid double-resolution
1234
- queryBuilderState.initWithDataProduct(
1235
- dataProduct,
1236
- undefined,
1237
- resolvedState,
1238
- );
1239
1272
 
1240
- // 7. Wire in mapping coverage result
1241
- const mappingCoverageResult =
1242
- dataProductAnalysisResult.dataProductAnalysis.mappingToMappingCoverageResult?.get(
1243
- dataProductAnalysisResult.targetMappingPath,
1273
+ if (
1274
+ dataProductAnalysisResult.dataProductAnalysis
1275
+ .mappingToMappingCoverageResult
1276
+ ) {
1277
+ queryBuilderState.mappingToMappingCoverageResult =
1278
+ dataProductAnalysisResult.dataProductAnalysis.mappingToMappingCoverageResult;
1279
+ }
1280
+
1281
+ let accessor: Accessor | undefined;
1282
+ if (resolvedState instanceof LakehouseAccessPoint) {
1283
+ accessor = resolveDataProductAccessor(
1284
+ dataProduct,
1285
+ resolvedState,
1286
+ this.graphManagerState.graph,
1287
+ artifact,
1288
+ undefined,
1244
1289
  );
1245
- if (mappingCoverageResult) {
1246
- queryBuilderState.explorerState.mappingModelCoverageAnalysisResult =
1247
- mappingCoverageResult;
1248
1290
  }
1291
+ // Pass pre-resolved state to avoid double-resolution
1292
+ queryBuilderState.initWithDataProduct(dataProduct, accessor, resolvedState);
1249
1293
 
1250
- // init
1251
- const execValue = dataProductAnalysisResult.targetExecState;
1252
- queryBuilderState.executionState =
1253
- execValue instanceof NativeModelExecutionContext
1254
- ? new NativeModelDataProductExecutionState(execValue, queryBuilderState)
1255
- : new ModelAccessPointDataProductExecutionState(
1256
- execValue,
1257
- queryBuilderState,
1258
- ).withAdhocRuntime();
1259
- if (queryBuilderState.executionState.mapping) {
1260
- queryBuilderState.changeMapping(queryBuilderState.executionState.mapping);
1294
+ // 8. Wire in lakehouse runtime and adhoc-runtime flag for MODEL or LAKEHOUSE mode
1295
+ if (
1296
+ queryBuilderState.executionState instanceof
1297
+ ModelAccessPointDataProductExecutionState
1298
+ ) {
1299
+ queryBuilderState.executionState.withAdhocRuntime();
1300
+ if (packageableRuntime) {
1301
+ queryBuilderState.changeRuntime(
1302
+ new RuntimePointer(
1303
+ PackageableElementExplicitReference.create(packageableRuntime),
1304
+ ),
1305
+ );
1306
+ }
1307
+ } else if (
1308
+ queryBuilderState.executionState instanceof
1309
+ LakehouseDataProductExecutionState
1310
+ ) {
1311
+ queryBuilderState.executionState.withAdhocRuntime();
1312
+ if (packageableRuntime) {
1313
+ queryBuilderState.executionState.selectedRuntime = packageableRuntime;
1314
+ queryBuilderState.changeRuntime(
1315
+ new RuntimePointer(
1316
+ PackageableElementExplicitReference.create(packageableRuntime),
1317
+ ),
1318
+ );
1319
+ }
1261
1320
  }
1262
- queryBuilderState.changeRuntime(
1263
- new RuntimePointer(
1264
- PackageableElementExplicitReference.create(packageableRuntime),
1265
- ),
1266
- );
1267
1321
  return queryBuilderState;
1268
1322
  }
1269
1323
  }
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Copyright (c) 2026-present, Goldman Sachs
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { DataProductAccessType } from '@finos/legend-graph';
18
+ import { type DepotServerClient } from '@finos/legend-server-depot';
19
+ import { IllegalStateError, guaranteeNonNullable } from '@finos/legend-shared';
20
+ import type { QueryBuilderState } from '@finos/legend-query-builder';
21
+ import type { LegendQueryApplicationStore } from '../LegendQueryBaseStore.js';
22
+ import { generateDataProductSampleQueryRoute } from '../../__lib__/LegendQueryNavigation.js';
23
+ import { DataProductSelectorState } from '../data-space/DataProductSelectorState.js';
24
+ import { createQueryDataProductTaggedValue } from '../../components/data-product/QueryDataProductUtil.js';
25
+ import { BaseTemplateQueryCreatorStore } from '../BaseTemplateQueryCreatorStore.js';
26
+
27
+ export class DataProductSampleQueryCreatorStore extends BaseTemplateQueryCreatorStore {
28
+ readonly dataProductPath: string;
29
+
30
+ constructor(
31
+ applicationStore: LegendQueryApplicationStore,
32
+ depotServerClient: DepotServerClient,
33
+ groupId: string,
34
+ artifactId: string,
35
+ versionId: string,
36
+ dataProductPath: string,
37
+ sampleQueryId: string,
38
+ urlQueryParamValues: Record<string, string> | undefined,
39
+ ) {
40
+ super(
41
+ applicationStore,
42
+ depotServerClient,
43
+ groupId,
44
+ artifactId,
45
+ versionId,
46
+ sampleQueryId,
47
+ urlQueryParamValues,
48
+ );
49
+ this.dataProductPath = dataProductPath;
50
+ }
51
+
52
+ override getEditorRoute(): string {
53
+ return generateDataProductSampleQueryRoute(
54
+ this.groupId,
55
+ this.artifactId,
56
+ this.versionId,
57
+ this.dataProductPath,
58
+ this.templateQueryId,
59
+ );
60
+ }
61
+
62
+ override getEntityPath(): string {
63
+ return this.dataProductPath;
64
+ }
65
+
66
+ override getSearchTaggedValues(): {
67
+ profile: string;
68
+ tag: string;
69
+ value: string;
70
+ }[] {
71
+ return [createQueryDataProductTaggedValue(this.dataProductPath)];
72
+ }
73
+
74
+ override getQueryDecoratorTaggedValues():
75
+ | { profile: string; tag: string; value: string }[]
76
+ | undefined {
77
+ return [createQueryDataProductTaggedValue(this.dataProductPath)];
78
+ }
79
+
80
+ async initializeQueryBuilderState(): Promise<QueryBuilderState> {
81
+ const artifact = await this.fetchDataProductArtifact(
82
+ this.groupId,
83
+ this.artifactId,
84
+ this.versionId,
85
+ this.dataProductPath,
86
+ );
87
+
88
+ const nativeModelAccess = guaranteeNonNullable(
89
+ artifact.nativeModelAccess,
90
+ `Data product '${this.dataProductPath}' does not have native model access`,
91
+ );
92
+ const sampleQuery = nativeModelAccess.sampleQueries?.find(
93
+ (sq) => sq.info.id === this.templateQueryId,
94
+ );
95
+ if (!sampleQuery) {
96
+ throw new IllegalStateError(
97
+ `Can't find sample query with id '${this.templateQueryId}' in data product '${this.dataProductPath}'`,
98
+ );
99
+ }
100
+ this.templateQueryTitle = sampleQuery.title;
101
+
102
+ const accessId =
103
+ sampleQuery.info.executionContextKey ??
104
+ nativeModelAccess.defaultExecutionContext;
105
+
106
+ const queryBuilderState = await this.buildDataProductQueryBuilderState(
107
+ this.groupId,
108
+ this.artifactId,
109
+ this.versionId,
110
+ this.dataProductPath,
111
+ artifact,
112
+ accessId,
113
+ DataProductAccessType.NATIVE,
114
+ async () => {
115
+ this.applicationStore.notificationService.notifyWarning(
116
+ `Can't switch data product while visiting a sample query`,
117
+ );
118
+ },
119
+ undefined,
120
+ new DataProductSelectorState(
121
+ this.depotServerClient,
122
+ this.applicationStore,
123
+ ),
124
+ );
125
+
126
+ const query = await this.graphManagerState.graphManager.pureCodeToLambda(
127
+ sampleQuery.info.query,
128
+ );
129
+
130
+ const defaultParameters = await this.resolveDefaultParameters(query);
131
+ queryBuilderState.initializeWithQuery(query, defaultParameters);
132
+
133
+ return queryBuilderState;
134
+ }
135
+ }
@@ -33,6 +33,19 @@ export const resolveDefaultDataProductAccessType = (
33
33
  id: modelAcessGroup.id,
34
34
  };
35
35
  }
36
+
37
+ // start on ModelAPG if it exists, then fallback to native access
38
+ const nativeAccess = dataProductArtifact.nativeModelAccess;
39
+ if (nativeAccess) {
40
+ const defaultKey = nativeAccess.defaultExecutionContext;
41
+ const native =
42
+ nativeAccess.nativeModelExecutionContexts.find(
43
+ (ctx) => ctx.key === defaultKey,
44
+ ) ?? nativeAccess.nativeModelExecutionContexts[0];
45
+ if (native) {
46
+ return { type: DataProductAccessType.NATIVE, id: native.key };
47
+ }
48
+ }
36
49
  throw new Error(
37
50
  `Data Product not supported for querying on legend query ${dataProductArtifact.dataProduct.path}. Must contain a model access point or native model access.`,
38
51
  );