@finos/legend-application-query 13.8.8 → 13.8.9

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 +1 -1
  32. package/lib/stores/QueryEditorStore.d.ts.map +1 -1
  33. package/lib/stores/QueryEditorStore.js +42 -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 +22 -2
  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 +60 -46
  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 +36 -4
  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
@@ -42,6 +42,7 @@ import {
42
42
  QueryDataProductNativeExecutionContextInfo,
43
43
  ModelAccessPointGroup,
44
44
  V1_PureGraphManager,
45
+ type V1_DataProductArtifact,
45
46
  } from '@finos/legend-graph';
46
47
  import { DepotServerClient } from '@finos/legend-server-depot';
47
48
  import {
@@ -76,6 +77,7 @@ import {
76
77
  } from '@finos/legend-application/browser';
77
78
  import {
78
79
  generateExistingQueryEditorRoute,
80
+ generateDataProductSampleQueryRoute,
79
81
  LEGEND_QUERY_ROUTE_PATTERN,
80
82
  } from '../../__lib__/LegendQueryNavigation.js';
81
83
  import {
@@ -90,6 +92,8 @@ import {
90
92
  import { LegendQueryDataProductQueryBuilderState } from '../../stores/data-product/query-builder/LegendQueryDataProductQueryBuilderState.js';
91
93
  import { DataProductSelectorState } from '../../stores/data-space/DataProductSelectorState.js';
92
94
  import { DataSpaceTemplateQueryCreatorStore } from '../../stores/data-space/DataSpaceTemplateQueryCreatorStore.js';
95
+ import { DataProductSampleQueryCreatorStore } from '../../stores/data-product/DataProductSampleQueryCreatorStore.js';
96
+ import { DataProductSampleQueryCreator } from '../data-product/DataProductSampleQueryCreator.js';
93
97
 
94
98
  const TEST_QUERY_ID = 'test-query-id';
95
99
  const TEST_GROUP_ID = 'test-group';
@@ -97,6 +101,8 @@ const TEST_ARTIFACT_ID = 'test-artifact';
97
101
  const TEST_VERSION_ID = 'test-version';
98
102
  const TEST_TEMPLATE_QUERY_ID = 'templateQuery';
99
103
  const TEST_DATA_SPACE_PATH = 'domain::COVIDDatapace';
104
+ const TEST_SAMPLE_QUERY_ID = 'sampleQuery';
105
+ const TEST_DATA_PRODUCT_PATH = 'test::MyDataProduct';
100
106
  export const TEST_QUERY_NAME = 'MyTestQuery';
101
107
 
102
108
  export const TEST__provideMockedQueryEditorStore = (customization?: {
@@ -775,13 +781,15 @@ export const TEST__setUpDataProductExistingQueryEditor = async (
775
781
  async () => {
776
782
  /* no-op for tests */
777
783
  },
784
+ async () => {
785
+ throw new Error('Not implemented in tests');
786
+ },
778
787
  new DataProductSelectorState(
779
788
  MOCK__editorStore.depotServerClient,
780
789
  MOCK__editorStore.applicationStore,
781
790
  ),
782
791
  undefined,
783
792
  undefined,
784
- undefined,
785
793
  MOCK__editorStore.applicationStore.config.options.queryBuilderConfig,
786
794
  {
787
795
  groupId: 'test.group',
@@ -845,6 +853,7 @@ export const TEST__setUpDataProductNativeExistingQueryEditor = async (
845
853
  executionKey: string,
846
854
  lambda: RawLambda,
847
855
  entities: PlainObject<Entity>[],
856
+ artifact?: V1_DataProductArtifact | undefined,
848
857
  ): Promise<{
849
858
  renderResult: RenderResult;
850
859
  queryBuilderState: QueryBuilderState;
@@ -974,20 +983,22 @@ export const TEST__setUpDataProductNativeExistingQueryEditor = async (
974
983
  QueryBuilderDataBrowserWorkflow.INSTANCE,
975
984
  new QueryBuilderActionConfig_QueryApplication(MOCK__editorStore),
976
985
  dataProduct,
977
- undefined,
986
+ artifact,
978
987
  executionState,
979
988
  MOCK__editorStore.depotServerClient,
980
989
  { groupId: 'test.group', artifactId: 'test-artifact', versionId: '0.0.0' },
981
990
  async () => {
982
991
  /* no-op for tests */
983
992
  },
993
+ async () => {
994
+ throw new Error('Not implemented in tests');
995
+ },
984
996
  new DataProductSelectorState(
985
997
  MOCK__editorStore.depotServerClient,
986
998
  MOCK__editorStore.applicationStore,
987
999
  ),
988
1000
  undefined,
989
1001
  undefined,
990
- undefined,
991
1002
  MOCK__editorStore.applicationStore.config.options.queryBuilderConfig,
992
1003
  {
993
1004
  groupId: 'test.group',
@@ -1044,3 +1055,216 @@ export const TEST__setUpDataProductNativeExistingQueryEditor = async (
1044
1055
  ),
1045
1056
  };
1046
1057
  };
1058
+
1059
+ export const TEST__provideMockedDataProductSampleQueryCreatorStore =
1060
+ (customization?: {
1061
+ mock?: DataProductSampleQueryCreatorStore;
1062
+ applicationStore?: LegendQueryApplicationStore;
1063
+ pluginManager?: LegendQueryPluginManager;
1064
+ extraPlugins?: AbstractPlugin[];
1065
+ extraPresets?: AbstractPreset[];
1066
+ }): DataProductSampleQueryCreatorStore => {
1067
+ const pluginManager =
1068
+ customization?.pluginManager ?? LegendQueryPluginManager.create();
1069
+ pluginManager
1070
+ .usePlugins([
1071
+ new Core_LegendQueryApplicationPlugin(),
1072
+ ...(customization?.extraPlugins ?? []),
1073
+ ])
1074
+ .usePresets([...(customization?.extraPresets ?? [])])
1075
+ .install();
1076
+ const applicationStore =
1077
+ customization?.applicationStore ??
1078
+ new ApplicationStore(
1079
+ TEST__getTestLegendQueryApplicationConfig(),
1080
+ pluginManager,
1081
+ );
1082
+ const depotServerClient = new DepotServerClient({
1083
+ serverUrl: applicationStore.config.depotServerUrl,
1084
+ });
1085
+ depotServerClient.setTracerService(applicationStore.tracerService);
1086
+ const value =
1087
+ customization?.mock ??
1088
+ new DataProductSampleQueryCreatorStore(
1089
+ applicationStore,
1090
+ depotServerClient,
1091
+ TEST_GROUP_ID,
1092
+ TEST_ARTIFACT_ID,
1093
+ TEST_VERSION_ID,
1094
+ TEST_DATA_PRODUCT_PATH,
1095
+ TEST_SAMPLE_QUERY_ID,
1096
+ { fips: 'value' },
1097
+ );
1098
+ const MOCK__QueryEditorStoreProvider = require('../QueryEditorStoreProvider.js'); // eslint-disable-line @typescript-eslint/no-require-imports,@typescript-eslint/no-unsafe-assignment
1099
+ MOCK__QueryEditorStoreProvider.useQueryEditorStore = createMock();
1100
+ MOCK__QueryEditorStoreProvider.useQueryEditorStore.mockReturnValue(value);
1101
+ return value;
1102
+ };
1103
+
1104
+ export const TEST__setUpDataProductSampleQueryEditor = async (
1105
+ MOCK__editorStore: DataProductSampleQueryCreatorStore,
1106
+ dataProductPath: string,
1107
+ executionKey: string,
1108
+ lambda: RawLambda,
1109
+ entities: PlainObject<Entity>[],
1110
+ artifact: V1_DataProductArtifact,
1111
+ ): Promise<{
1112
+ renderResult: RenderResult;
1113
+ queryBuilderState: QueryBuilderState;
1114
+ }> => {
1115
+ const projectData = {
1116
+ id: 'test-id',
1117
+ groupId: MOCK__editorStore.groupId,
1118
+ artifactId: MOCK__editorStore.artifactId,
1119
+ projectId: 'test-project-id',
1120
+ versions: [MOCK__editorStore.versionId],
1121
+ latestVersion: MOCK__editorStore.versionId,
1122
+ };
1123
+
1124
+ const graphManagerState = MOCK__editorStore.graphManagerState;
1125
+
1126
+ await graphManagerState.graphManager.initialize({
1127
+ env: 'test',
1128
+ tabSize: 2,
1129
+ clientConfig: {},
1130
+ });
1131
+ await graphManagerState.initializeSystem();
1132
+ await graphManagerState.graphManager.buildGraph(
1133
+ graphManagerState.graph,
1134
+ entities as unknown as Entity[],
1135
+ graphManagerState.graphBuildState,
1136
+ );
1137
+
1138
+ const dataProduct = graphManagerState.graph.getDataProduct(dataProductPath);
1139
+ const nativeAccess = guaranteeNonNullable(
1140
+ dataProduct.nativeModelAccess,
1141
+ `Data product '${dataProductPath}' has no native model access`,
1142
+ );
1143
+ const executionState = guaranteeNonNullable(
1144
+ nativeAccess.nativeModelExecutionContexts.find(
1145
+ (ctx) => ctx.key === executionKey,
1146
+ ),
1147
+ `Can't find native execution context '${executionKey}'`,
1148
+ );
1149
+
1150
+ createSpy(
1151
+ MOCK__editorStore.depotServerClient,
1152
+ 'getProject',
1153
+ ).mockResolvedValue(projectData);
1154
+ createSpy(
1155
+ MOCK__editorStore.depotServerClient,
1156
+ 'getEntities',
1157
+ ).mockResolvedValue(entities);
1158
+ createSpy(
1159
+ MOCK__editorStore.depotServerClient,
1160
+ 'getEntitiesByClassifier',
1161
+ ).mockResolvedValue([]);
1162
+ createSpy(
1163
+ MOCK__editorStore.depotServerClient,
1164
+ 'getEntitiesSummaryByClassifier',
1165
+ ).mockResolvedValue([]);
1166
+ createSpy(
1167
+ MOCK__editorStore.depotServerClient,
1168
+ 'getVersions',
1169
+ ).mockResolvedValue([projectData.latestVersion]);
1170
+ createSpy(
1171
+ graphManagerState.graphManager,
1172
+ 'pureCodeToLambda',
1173
+ ).mockResolvedValue(new RawLambda(lambda.parameters, lambda.body));
1174
+ createSpy(
1175
+ graphManagerState.graphManager,
1176
+ 'analyzeMappingModelCoverage',
1177
+ ).mockResolvedValue(
1178
+ graphManagerState.graphManager.buildMappingModelCoverageAnalysisResult(
1179
+ { mappedEntities: [] },
1180
+ executionState.mapping.value,
1181
+ ),
1182
+ );
1183
+
1184
+ const mockQueryBuilderState = new LegendQueryDataProductQueryBuilderState(
1185
+ MOCK__editorStore.applicationStore,
1186
+ graphManagerState,
1187
+ QueryBuilderDataBrowserWorkflow.INSTANCE,
1188
+ new QueryBuilderActionConfig_QueryApplication(MOCK__editorStore),
1189
+ dataProduct,
1190
+ artifact,
1191
+ executionState,
1192
+ MOCK__editorStore.depotServerClient,
1193
+ {
1194
+ groupId: MOCK__editorStore.groupId,
1195
+ artifactId: MOCK__editorStore.artifactId,
1196
+ versionId: MOCK__editorStore.versionId,
1197
+ },
1198
+ async () => {
1199
+ /* no-op for tests */
1200
+ },
1201
+ async () => {
1202
+ throw new Error('Not implemented in tests');
1203
+ },
1204
+ new DataProductSelectorState(
1205
+ MOCK__editorStore.depotServerClient,
1206
+ MOCK__editorStore.applicationStore,
1207
+ ),
1208
+ undefined,
1209
+ undefined,
1210
+ MOCK__editorStore.applicationStore.config.options.queryBuilderConfig,
1211
+ {
1212
+ groupId: MOCK__editorStore.groupId,
1213
+ artifactId: MOCK__editorStore.artifactId,
1214
+ versionId: MOCK__editorStore.versionId,
1215
+ dataProduct: dataProductPath,
1216
+ },
1217
+ );
1218
+ mockQueryBuilderState.initWithDataProduct(
1219
+ dataProduct,
1220
+ undefined,
1221
+ executionState,
1222
+ );
1223
+
1224
+ MOCK__editorStore.buildGraph = createMock();
1225
+ MOCK__editorStore.fetchDataProductArtifact = createMock();
1226
+ (
1227
+ MOCK__editorStore.fetchDataProductArtifact as ReturnType<typeof createMock>
1228
+ ).mockResolvedValue(artifact);
1229
+ MOCK__editorStore.buildDataProductQueryBuilderState = createMock();
1230
+ (
1231
+ MOCK__editorStore.buildDataProductQueryBuilderState as ReturnType<
1232
+ typeof createMock
1233
+ >
1234
+ ).mockResolvedValue(mockQueryBuilderState);
1235
+ graphManagerState.graphManager.initialize = createMock();
1236
+
1237
+ const sampleQueryRoute = generateDataProductSampleQueryRoute(
1238
+ MOCK__editorStore.groupId,
1239
+ MOCK__editorStore.artifactId,
1240
+ MOCK__editorStore.versionId,
1241
+ dataProductPath,
1242
+ MOCK__editorStore.templateQueryId,
1243
+ );
1244
+
1245
+ const renderResult = render(
1246
+ <ApplicationStoreProvider store={MOCK__editorStore.applicationStore}>
1247
+ <TEST__BrowserEnvironmentProvider initialEntries={[sampleQueryRoute]}>
1248
+ <LegendQueryFrameworkProvider>
1249
+ <Routes>
1250
+ <Route
1251
+ path={LEGEND_QUERY_ROUTE_PATTERN.DATA_PRODUCT_SAMPLE_QUERY}
1252
+ element={<DataProductSampleQueryCreator />}
1253
+ />
1254
+ </Routes>
1255
+ </LegendQueryFrameworkProvider>
1256
+ </TEST__BrowserEnvironmentProvider>
1257
+ </ApplicationStoreProvider>,
1258
+ );
1259
+ await waitFor(() =>
1260
+ renderResult.getByTestId(QUERY_BUILDER_TEST_ID.QUERY_BUILDER),
1261
+ );
1262
+
1263
+ return {
1264
+ renderResult,
1265
+ queryBuilderState: guaranteeNonNullable(
1266
+ MOCK__editorStore.queryBuilderState,
1267
+ `Query builder state should have been initialized`,
1268
+ ),
1269
+ };
1270
+ };
@@ -0,0 +1,114 @@
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 { observer, useLocalObservable } from 'mobx-react-lite';
18
+ import { useParams } from '@finos/legend-application/browser';
19
+ import { parseGAVCoordinates } from '@finos/legend-storage';
20
+ import {
21
+ useLegendQueryApplicationStore,
22
+ useLegendQueryBaseStore,
23
+ } from '../LegendQueryFrameworkProvider.js';
24
+ import { DataProductSampleQueryCreatorStore } from '../../stores/data-product/DataProductSampleQueryCreatorStore.js';
25
+ import { QueryEditorStoreContext } from '../QueryEditorStoreProvider.js';
26
+ import {
27
+ DATA_PRODUCT_SAMPLE_QUERY_CREATOR_ROUTE_PATTERN_TOKEN,
28
+ generateDataProductSampleQueryRoute,
29
+ type DataProductSampleQueryPathParams,
30
+ } from '../../__lib__/LegendQueryNavigation.js';
31
+ import { QueryEditor } from '../QueryEditor.js';
32
+ import { guaranteeNonNullable } from '@finos/legend-shared';
33
+ import { useApplicationStore } from '@finos/legend-application';
34
+ import { extractQueryParams } from '../utils/QueryParameterUtils.js';
35
+ import { useEffect } from 'react';
36
+
37
+ const DataProductSampleQueryCreatorStoreProvider: React.FC<{
38
+ children: React.ReactNode;
39
+ gav: string;
40
+ dataProductPath: string;
41
+ sampleQueryId: string;
42
+ params: Record<string, string> | undefined;
43
+ }> = ({ children, gav, dataProductPath, sampleQueryId, params }) => {
44
+ const { groupId, artifactId, versionId } = parseGAVCoordinates(gav);
45
+ const applicationStore = useLegendQueryApplicationStore();
46
+ const baseStore = useLegendQueryBaseStore();
47
+ const store = useLocalObservable(
48
+ () =>
49
+ new DataProductSampleQueryCreatorStore(
50
+ applicationStore,
51
+ baseStore.depotServerClient,
52
+ groupId,
53
+ artifactId,
54
+ versionId,
55
+ dataProductPath,
56
+ sampleQueryId,
57
+ params,
58
+ ),
59
+ );
60
+ return (
61
+ <QueryEditorStoreContext.Provider value={store}>
62
+ {children}
63
+ </QueryEditorStoreContext.Provider>
64
+ );
65
+ };
66
+
67
+ export const DataProductSampleQueryCreator = observer(() => {
68
+ const applicationStore = useApplicationStore();
69
+ const parameters = useParams<DataProductSampleQueryPathParams>();
70
+ const gav = guaranteeNonNullable(
71
+ parameters[DATA_PRODUCT_SAMPLE_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.GAV],
72
+ );
73
+ const dataProductPath = guaranteeNonNullable(
74
+ parameters[
75
+ DATA_PRODUCT_SAMPLE_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.DATA_PRODUCT_PATH
76
+ ],
77
+ );
78
+ const sampleQueryId = guaranteeNonNullable(
79
+ parameters[
80
+ DATA_PRODUCT_SAMPLE_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.SAMPLE_QUERY_ID
81
+ ],
82
+ );
83
+
84
+ const queryParams =
85
+ applicationStore.navigationService.navigator.getCurrentLocationParameters();
86
+ const processed = extractQueryParams(queryParams);
87
+
88
+ useEffect(() => {
89
+ // clear params from URL after extracting
90
+ if (processed && Object.keys(processed).length) {
91
+ const { groupId, artifactId, versionId } = parseGAVCoordinates(gav);
92
+ applicationStore.navigationService.navigator.updateCurrentLocation(
93
+ generateDataProductSampleQueryRoute(
94
+ groupId,
95
+ artifactId,
96
+ versionId,
97
+ dataProductPath,
98
+ sampleQueryId,
99
+ ),
100
+ );
101
+ }
102
+ }, [applicationStore, gav, dataProductPath, sampleQueryId, processed]);
103
+
104
+ return (
105
+ <DataProductSampleQueryCreatorStoreProvider
106
+ gav={gav}
107
+ dataProductPath={dataProductPath}
108
+ sampleQueryId={sampleQueryId}
109
+ params={processed}
110
+ >
111
+ <QueryEditor />
112
+ </DataProductSampleQueryCreatorStoreProvider>
113
+ );
114
+ });
@@ -0,0 +1,219 @@
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
+ PanelHeader,
19
+ BasePopover,
20
+ ClickAwayListener,
21
+ ShareIcon,
22
+ TagIcon,
23
+ } from '@finos/legend-art';
24
+ import { observer } from 'mobx-react-lite';
25
+ import { useRef, useState } from 'react';
26
+ import {
27
+ ActionAlertActionType,
28
+ ActionAlertType,
29
+ useApplicationStore,
30
+ } from '@finos/legend-application';
31
+ import type { V1_SampleQuery } from '@finos/legend-graph';
32
+ import { flowResult } from 'mobx';
33
+ import type { LegendQueryDataProductQueryBuilderState } from '../../stores/data-product/query-builder/LegendQueryDataProductQueryBuilderState.js';
34
+ import { generateDataProductSampleQueryRoute } from '../../__lib__/LegendQueryNavigation.js';
35
+
36
+ const DataProductSampleQueryDialog = observer(
37
+ (props: {
38
+ triggerElement: HTMLElement | null;
39
+ queryBuilderState: LegendQueryDataProductQueryBuilderState;
40
+ sampleQueries: V1_SampleQuery[];
41
+ onClose: () => void;
42
+ }) => {
43
+ const { triggerElement, queryBuilderState, sampleQueries, onClose } = props;
44
+ const applicationStore = useApplicationStore();
45
+
46
+ const loadSampleQuery = async (
47
+ sampleQuery: V1_SampleQuery,
48
+ ): Promise<void> => {
49
+ const query =
50
+ await queryBuilderState.graphManagerState.graphManager.pureCodeToLambda(
51
+ sampleQuery.info.query,
52
+ );
53
+ queryBuilderState.initializeWithQuery(query);
54
+ onClose();
55
+ };
56
+
57
+ const loadQuery = async (sampleQuery: V1_SampleQuery): Promise<void> => {
58
+ if (queryBuilderState.changeDetectionState.hasChanged) {
59
+ applicationStore.alertService.setActionAlertInfo({
60
+ message:
61
+ 'Unsaved changes will be lost if you continue. Do you still want to proceed?',
62
+ type: ActionAlertType.CAUTION,
63
+ actions: [
64
+ {
65
+ label: 'Proceed',
66
+ type: ActionAlertActionType.PROCEED_WITH_CAUTION,
67
+ handler: (): void => {
68
+ flowResult(loadSampleQuery(sampleQuery));
69
+ },
70
+ },
71
+ {
72
+ label: 'Abort',
73
+ type: ActionAlertActionType.PROCEED,
74
+ default: true,
75
+ },
76
+ ],
77
+ });
78
+ } else {
79
+ flowResult(loadSampleQuery(sampleQuery));
80
+ }
81
+ };
82
+
83
+ const visitSampleQuery = (sampleQuery: V1_SampleQuery): void => {
84
+ const id = sampleQuery.info.id;
85
+ if (!id) {
86
+ applicationStore.notificationService.notifyWarning(
87
+ 'Sample query does not have an ID and cannot be visited via URL',
88
+ );
89
+ return;
90
+ }
91
+ const project = queryBuilderState.project;
92
+ applicationStore.navigationService.navigator.visitAddress(
93
+ applicationStore.navigationService.navigator.generateAddress(
94
+ generateDataProductSampleQueryRoute(
95
+ project.groupId,
96
+ project.artifactId,
97
+ project.versionId,
98
+ queryBuilderState.dataProduct.path,
99
+ id,
100
+ ),
101
+ ),
102
+ );
103
+ };
104
+
105
+ return (
106
+ <ClickAwayListener onClickAway={onClose}>
107
+ <div>
108
+ <BasePopover
109
+ open={true}
110
+ slotProps={{
111
+ paper: {
112
+ classes: {
113
+ root: '"query-builder__data-space__template-query-panel__container__root',
114
+ },
115
+ },
116
+ }}
117
+ className="query-builder__data-space__template-query-panel__container"
118
+ onClose={onClose}
119
+ anchorEl={triggerElement}
120
+ anchorOrigin={{
121
+ vertical: 'bottom',
122
+ horizontal: 'left',
123
+ }}
124
+ transformOrigin={{
125
+ vertical: 'top',
126
+ horizontal: 'center',
127
+ }}
128
+ >
129
+ <div className="query-builder__data-space__template-query-panel">
130
+ <div className="query-builder__data-space__template-query-panel__header">
131
+ Sample Queries
132
+ </div>
133
+ {sampleQueries.map((sampleQuery) => (
134
+ <div
135
+ key={sampleQuery.info.id ?? sampleQuery.title}
136
+ className="query-builder__data-space__template-query-panel__query"
137
+ >
138
+ <TagIcon className="query-builder__data-space__template-query-panel__query__icon" />
139
+ <button
140
+ className="query-builder__data-space__template-query-panel__query__entry"
141
+ title="click to load sample query"
142
+ onClick={() => {
143
+ flowResult(loadQuery(sampleQuery));
144
+ }}
145
+ >
146
+ <div className="query-builder__data-space__template-query-panel__query__entry__content">
147
+ <div className="query-builder__data-space__template-query-panel__query__entry__content__title">
148
+ {sampleQuery.title}
149
+ </div>
150
+ {sampleQuery.description && (
151
+ <div className="query-builder__data-space__template-query-panel__query__entry__content__description">
152
+ {sampleQuery.description}
153
+ </div>
154
+ )}
155
+ </div>
156
+ </button>
157
+ <button
158
+ className="query-builder__data-space__template-query-panel__query__share"
159
+ title="Visit..."
160
+ disabled={!sampleQuery.info.id}
161
+ onClick={() => visitSampleQuery(sampleQuery)}
162
+ >
163
+ <ShareIcon />
164
+ <div className="query-builder__data-space__template-query-panel__query__share__label">
165
+ Visit
166
+ </div>
167
+ </button>
168
+ </div>
169
+ ))}
170
+ </div>
171
+ </BasePopover>
172
+ </div>
173
+ </ClickAwayListener>
174
+ );
175
+ },
176
+ );
177
+
178
+ export const DataProductSampleQueryPanel = observer(
179
+ (props: { queryBuilderState: LegendQueryDataProductQueryBuilderState }) => {
180
+ const { queryBuilderState } = props;
181
+ const templateQueryButtonRef = useRef<HTMLButtonElement>(null);
182
+ const [isDialogOpen, setIsDialogOpen] = useState(false);
183
+
184
+ const sampleQueries = queryBuilderState.isNativeMode
185
+ ? (queryBuilderState.dataProductArtifact?.nativeModelAccess
186
+ ?.sampleQueries ?? [])
187
+ : [];
188
+
189
+ if (sampleQueries.length === 0) {
190
+ return null;
191
+ }
192
+
193
+ return (
194
+ <PanelHeader className="query-builder__data-space__template-query">
195
+ <button
196
+ className="query-builder__data-space__template-query__btn"
197
+ ref={templateQueryButtonRef}
198
+ onClick={() => setIsDialogOpen(true)}
199
+ >
200
+ Sample Queries ( {sampleQueries.length} )
201
+ </button>
202
+ {isDialogOpen && (
203
+ <DataProductSampleQueryDialog
204
+ triggerElement={templateQueryButtonRef.current}
205
+ queryBuilderState={queryBuilderState}
206
+ sampleQueries={sampleQueries}
207
+ onClose={() => setIsDialogOpen(false)}
208
+ />
209
+ )}
210
+ </PanelHeader>
211
+ );
212
+ },
213
+ );
214
+
215
+ export const renderDataProductSampleQueryPanelContent = (
216
+ queryBuilderState: LegendQueryDataProductQueryBuilderState,
217
+ ): React.ReactNode => (
218
+ <DataProductSampleQueryPanel queryBuilderState={queryBuilderState} />
219
+ );