@finos/legend-application-query 13.8.0 → 13.8.2

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 (52) hide show
  1. package/lib/components/__test-utils__/QueryEditorComponentTestUtils.d.ts.map +1 -1
  2. package/lib/components/__test-utils__/QueryEditorComponentTestUtils.js +2 -2
  3. package/lib/components/__test-utils__/QueryEditorComponentTestUtils.js.map +1 -1
  4. package/lib/components/data-product/LegendQueryDataProductQueryBuilder.d.ts.map +1 -1
  5. package/lib/components/data-product/LegendQueryDataProductQueryBuilder.js +4 -1
  6. package/lib/components/data-product/LegendQueryDataProductQueryBuilder.js.map +1 -1
  7. package/lib/components/data-product/QueryDataProductUtil.d.ts +20 -0
  8. package/lib/components/data-product/QueryDataProductUtil.d.ts.map +1 -0
  9. package/lib/components/data-product/QueryDataProductUtil.js +31 -0
  10. package/lib/components/data-product/QueryDataProductUtil.js.map +1 -0
  11. package/lib/components/data-space/LegendQueryDataSpaceQueryBuilder.d.ts +18 -0
  12. package/lib/components/data-space/LegendQueryDataSpaceQueryBuilder.d.ts.map +1 -0
  13. package/lib/components/data-space/LegendQueryDataSpaceQueryBuilder.js +139 -0
  14. package/lib/components/data-space/LegendQueryDataSpaceQueryBuilder.js.map +1 -0
  15. package/lib/components/shared/LegendQueryDataProductOptionLabel.d.ts +37 -0
  16. package/lib/components/shared/LegendQueryDataProductOptionLabel.d.ts.map +1 -0
  17. package/lib/components/shared/LegendQueryDataProductOptionLabel.js +49 -0
  18. package/lib/components/shared/LegendQueryDataProductOptionLabel.js.map +1 -0
  19. package/lib/index.css +2 -2
  20. package/lib/index.css.map +1 -1
  21. package/lib/light-mode.css +1 -1
  22. package/lib/package.json +3 -3
  23. package/lib/stores/QueryEditorStore.d.ts +2 -1
  24. package/lib/stores/QueryEditorStore.d.ts.map +1 -1
  25. package/lib/stores/QueryEditorStore.js +7 -18
  26. package/lib/stores/QueryEditorStore.js.map +1 -1
  27. package/lib/stores/data-product/query-builder/DataProductArtifactHelper.js +8 -7
  28. package/lib/stores/data-product/query-builder/DataProductArtifactHelper.js.map +1 -1
  29. package/lib/stores/data-product/query-builder/LegendQueryDataProductQueryBuilderState.d.ts +5 -3
  30. package/lib/stores/data-product/query-builder/LegendQueryDataProductQueryBuilderState.d.ts.map +1 -1
  31. package/lib/stores/data-product/query-builder/LegendQueryDataProductQueryBuilderState.js +13 -3
  32. package/lib/stores/data-product/query-builder/LegendQueryDataProductQueryBuilderState.js.map +1 -1
  33. package/lib/stores/data-space/DataProductQueryCreatorStore.d.ts +1 -1
  34. package/lib/stores/data-space/DataProductQueryCreatorStore.d.ts.map +1 -1
  35. package/lib/stores/data-space/DataProductQueryCreatorStore.js +17 -9
  36. package/lib/stores/data-space/DataProductQueryCreatorStore.js.map +1 -1
  37. package/lib/stores/data-space/query-builder/LegendQueryDataSpaceQueryBuilderState.d.ts +3 -2
  38. package/lib/stores/data-space/query-builder/LegendQueryDataSpaceQueryBuilderState.d.ts.map +1 -1
  39. package/lib/stores/data-space/query-builder/LegendQueryDataSpaceQueryBuilderState.js +5 -15
  40. package/lib/stores/data-space/query-builder/LegendQueryDataSpaceQueryBuilderState.js.map +1 -1
  41. package/package.json +8 -8
  42. package/src/components/__test-utils__/QueryEditorComponentTestUtils.tsx +2 -0
  43. package/src/components/data-product/LegendQueryDataProductQueryBuilder.tsx +7 -0
  44. package/src/components/data-product/QueryDataProductUtil.ts +46 -0
  45. package/src/components/data-space/LegendQueryDataSpaceQueryBuilder.tsx +377 -0
  46. package/src/components/shared/LegendQueryDataProductOptionLabel.tsx +80 -0
  47. package/src/stores/QueryEditorStore.ts +11 -18
  48. package/src/stores/data-product/query-builder/DataProductArtifactHelper.ts +8 -8
  49. package/src/stores/data-product/query-builder/LegendQueryDataProductQueryBuilderState.ts +19 -4
  50. package/src/stores/data-space/DataProductQueryCreatorStore.ts +21 -12
  51. package/src/stores/data-space/query-builder/LegendQueryDataSpaceQueryBuilderState.ts +6 -28
  52. package/tsconfig.json +3 -0
@@ -0,0 +1,377 @@
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 { useApplicationStore } from '@finos/legend-application';
18
+ import {
19
+ AnchorLinkIcon,
20
+ CheckIcon,
21
+ compareLabelFn,
22
+ ControlledDropdownMenu,
23
+ createFilter,
24
+ CustomSelectorInput,
25
+ MenuContent,
26
+ MenuContentItem,
27
+ MenuContentItemIcon,
28
+ MenuContentItemLabel,
29
+ MoreVerticalIcon,
30
+ PanelHeader,
31
+ PanelHeaderActionItem,
32
+ PanelHeaderActions,
33
+ SearchIcon,
34
+ } from '@finos/legend-art';
35
+ import {
36
+ ResolvedDataSpaceEntityWithOrigin,
37
+ resolveUsableDataSpaceClasses,
38
+ } from '@finos/legend-extension-dsl-data-space/application';
39
+ import {
40
+ buildExecutionContextOption,
41
+ DataSpaceAdvancedSearchModal,
42
+ type ExecutionContextOption,
43
+ } from '@finos/legend-extension-dsl-data-space/application-query';
44
+ import {
45
+ getMappingCompatibleRuntimes,
46
+ PackageableElementExplicitReference,
47
+ RuntimePointer,
48
+ type PackageableRuntime,
49
+ type Runtime,
50
+ } from '@finos/legend-graph';
51
+ import {
52
+ buildRuntimeValueOption,
53
+ getRuntimeOptionFormatter,
54
+ QueryBuilderClassSelector,
55
+ } from '@finos/legend-query-builder';
56
+ import { guaranteeNonNullable, guaranteeType } from '@finos/legend-shared';
57
+ import { DepotEntityWithOrigin } from '@finos/legend-storage';
58
+ import { flowResult } from 'mobx';
59
+ import { observer } from 'mobx-react-lite';
60
+ import { useEffect } from 'react';
61
+ import type { DataProductWithLegacyOption } from '../../stores/data-space/DataProductSelectorState.js';
62
+ import { formatDataProductOrSpaceOptionLabel } from '../shared/LegendQueryDataProductOptionLabel.js';
63
+ import type { LegendQueryDataSpaceQueryBuilderState } from '../../stores/data-space/query-builder/LegendQueryDataSpaceQueryBuilderState.js';
64
+
65
+ const resolveExecutionContextRuntimes = (
66
+ queryBuilderState: LegendQueryDataSpaceQueryBuilderState,
67
+ ): PackageableRuntime[] => {
68
+ if (queryBuilderState.dataSpaceAnalysisResult) {
69
+ const executionContext = Array.from(
70
+ queryBuilderState.dataSpaceAnalysisResult.executionContextsIndex.values(),
71
+ ).find(
72
+ (e) =>
73
+ e.mapping.path ===
74
+ queryBuilderState.executionContext.mapping.value.path,
75
+ );
76
+ return guaranteeNonNullable(executionContext).compatibleRuntimes;
77
+ }
78
+ return getMappingCompatibleRuntimes(
79
+ queryBuilderState.executionContext.mapping.value,
80
+ queryBuilderState.graphManagerState.usableRuntimes,
81
+ );
82
+ };
83
+
84
+ /**
85
+ * LegendQuery-specific DataSpace query builder setup panel. Unlike the base
86
+ * DataSpaceQueryBuilderSetupPanelContent, this component explicitly knows about
87
+ * both legacy data spaces and data products as combined options in the first
88
+ * dropdown, mirroring the same awareness in LegendQueryDataProductQueryBuilderState.
89
+ */
90
+ const LegendQueryDataSpaceQueryBuilderSetupPanelContent = observer(
91
+ (props: { queryBuilderState: LegendQueryDataSpaceQueryBuilderState }) => {
92
+ const { queryBuilderState } = props;
93
+ const applicationStore = useApplicationStore();
94
+
95
+ // combined first dropdown: legacy data spaces + data products from depot
96
+ const legacyOptions = queryBuilderState.dataSpaceOptions; // applies prioritizeEntityFunc
97
+ const productOptions: DataProductWithLegacyOption[] = (
98
+ queryBuilderState.productSelectorState.dataProducts ?? []
99
+ ).map((e) => ({ label: e.name, value: e }));
100
+ const allOptions: DataProductWithLegacyOption[] = [
101
+ ...legacyOptions,
102
+ ...productOptions,
103
+ ];
104
+
105
+ const selectedOption = queryBuilderState.selectedDataSpaceOption;
106
+
107
+ const onOptionChange = (
108
+ option: DataProductWithLegacyOption | null,
109
+ ): void => {
110
+ if (!option) {
111
+ return;
112
+ }
113
+ const value = option.value;
114
+ if (value instanceof ResolvedDataSpaceEntityWithOrigin) {
115
+ queryBuilderState.queryChatState?.abort();
116
+ queryBuilderState
117
+ .onDataSpaceChange(value)
118
+ .catch(queryBuilderState.applicationStore.alertUnhandledError);
119
+ } else if (value instanceof DepotEntityWithOrigin) {
120
+ queryBuilderState.queryChatState?.abort();
121
+ queryBuilderState.onDataProductChange(value);
122
+ }
123
+ };
124
+
125
+ const openDataSpaceAdvancedSearch = (): void => {
126
+ if (queryBuilderState.isAdvancedDataSpaceSearchEnabled) {
127
+ queryBuilderState.showAdvancedSearchPanel(queryBuilderState.dataSpace);
128
+ }
129
+ };
130
+
131
+ // execution context
132
+ const executionContextOptions =
133
+ queryBuilderState.dataSpace.executionContexts
134
+ .map(buildExecutionContextOption)
135
+ .sort(compareLabelFn);
136
+ const showExecutionContextOptions = executionContextOptions.length > 1;
137
+ const selectedExecutionContextOption = buildExecutionContextOption(
138
+ queryBuilderState.executionContext,
139
+ );
140
+
141
+ const onExecutionContextOptionChange = async (
142
+ option: ExecutionContextOption,
143
+ ): Promise<void> => {
144
+ if (option.value === queryBuilderState.executionContext) {
145
+ return;
146
+ }
147
+ const currentMapping =
148
+ queryBuilderState.executionContext.mapping.value.path;
149
+ queryBuilderState.setExecutionContext(option.value);
150
+ await queryBuilderState.propagateExecutionContextChange(
151
+ currentMapping === option.value.mapping.value.path,
152
+ );
153
+ queryBuilderState.onExecutionContextChange?.(option.value);
154
+ };
155
+
156
+ const handleExecutionContextOptionChange = (
157
+ option: ExecutionContextOption,
158
+ ): void => {
159
+ flowResult(onExecutionContextOptionChange(option));
160
+ };
161
+
162
+ // runtime
163
+ const runtimeOptions = resolveExecutionContextRuntimes(queryBuilderState)
164
+ .map(
165
+ (rt) =>
166
+ new RuntimePointer(PackageableElementExplicitReference.create(rt)),
167
+ )
168
+ .map(buildRuntimeValueOption)
169
+ .sort(compareLabelFn);
170
+ const selectedRuntimeOption = queryBuilderState.executionContextState
171
+ .runtimeValue
172
+ ? buildRuntimeValueOption(
173
+ queryBuilderState.executionContextState.runtimeValue,
174
+ )
175
+ : null;
176
+ const changeRuntime = (option: { value: Runtime }): void => {
177
+ if (
178
+ option.value === queryBuilderState.executionContextState.runtimeValue
179
+ ) {
180
+ return;
181
+ }
182
+ queryBuilderState.changeRuntime(option.value);
183
+ queryBuilderState.onRuntimeChange?.(option.value);
184
+ };
185
+ const runtimeFilterOption = createFilter({
186
+ ignoreCase: true,
187
+ ignoreAccents: false,
188
+ stringify: (option: { data: { value: Runtime } }): string =>
189
+ guaranteeType(option.data.value, RuntimePointer).packageableRuntime
190
+ .value.path,
191
+ });
192
+
193
+ // class
194
+ const classes = resolveUsableDataSpaceClasses(
195
+ queryBuilderState.dataSpace,
196
+ queryBuilderState.executionContext.mapping.value,
197
+ queryBuilderState.graphManagerState,
198
+ queryBuilderState,
199
+ );
200
+
201
+ useEffect(() => {
202
+ flowResult(queryBuilderState.loadEntities()).catch(
203
+ applicationStore.alertUnhandledError,
204
+ );
205
+ }, [queryBuilderState, applicationStore]);
206
+
207
+ const copyDataSpaceLinkToClipboard = (): void => {
208
+ if (queryBuilderState.isDataSpaceLinkable) {
209
+ queryBuilderState.copyDataSpaceLinkToClipboard();
210
+ }
211
+ };
212
+
213
+ return (
214
+ <div className="query-builder__setup__config-group">
215
+ <PanelHeader title="properties">
216
+ <PanelHeaderActions>
217
+ <PanelHeaderActionItem
218
+ title="copy data product query set up link to clipboard"
219
+ onClick={copyDataSpaceLinkToClipboard}
220
+ disabled={!queryBuilderState.isDataSpaceLinkable}
221
+ >
222
+ <AnchorLinkIcon />
223
+ </PanelHeaderActionItem>
224
+ <ControlledDropdownMenu
225
+ className="panel__header__action query-builder__setup__config-group__header__dropdown-trigger"
226
+ title="Show Settings..."
227
+ content={
228
+ <MenuContent>
229
+ <MenuContentItem
230
+ onClick={(): void =>
231
+ queryBuilderState.setShowRuntimeSelector(
232
+ !queryBuilderState.showRuntimeSelector,
233
+ )
234
+ }
235
+ >
236
+ <MenuContentItemIcon>
237
+ {queryBuilderState.showRuntimeSelector ? (
238
+ <CheckIcon />
239
+ ) : null}
240
+ </MenuContentItemIcon>
241
+ <MenuContentItemLabel>
242
+ Show Runtime Selector
243
+ </MenuContentItemLabel>
244
+ </MenuContentItem>
245
+ </MenuContent>
246
+ }
247
+ menuProps={{
248
+ anchorOrigin: { vertical: 'bottom', horizontal: 'right' },
249
+ transformOrigin: { vertical: 'top', horizontal: 'right' },
250
+ }}
251
+ >
252
+ <MoreVerticalIcon className="query-builder__icon__more-options" />
253
+ </ControlledDropdownMenu>
254
+ </PanelHeaderActions>
255
+ </PanelHeader>
256
+ <div className="query-builder__setup__config-group__content">
257
+ <div className="query-builder__setup__config-group__item">
258
+ <label
259
+ className="btn--sm query-builder__setup__config-group__data-product"
260
+ title="data product"
261
+ htmlFor="query-builder__setup__data-space-selector"
262
+ >
263
+ Data Product
264
+ </label>
265
+ <CustomSelectorInput
266
+ inputId="query-builder__setup__data-space-selector"
267
+ className="panel__content__form__section__dropdown query-builder__setup__config-group__item__selector"
268
+ options={allOptions}
269
+ isLoading={queryBuilderState.loadEntitiesState.isInProgress}
270
+ onChange={onOptionChange}
271
+ value={selectedOption}
272
+ placeholder="Search for data product..."
273
+ escapeClearsValue={true}
274
+ darkMode={
275
+ !applicationStore.layoutService
276
+ .TEMPORARY__isLightColorThemeEnabled
277
+ }
278
+ formatOptionLabel={formatDataProductOrSpaceOptionLabel}
279
+ />
280
+ {queryBuilderState.isAdvancedDataSpaceSearchEnabled && (
281
+ <>
282
+ <button
283
+ tabIndex={-1}
284
+ className="query-builder__setup__data-space-searcher__btn btn--dark"
285
+ onClick={openDataSpaceAdvancedSearch}
286
+ title="Open advanced search for data product..."
287
+ >
288
+ <SearchIcon />
289
+ </button>
290
+ {queryBuilderState.advancedSearchState && (
291
+ <DataSpaceAdvancedSearchModal
292
+ searchState={queryBuilderState.advancedSearchState}
293
+ onClose={() => queryBuilderState.hideAdvancedSearchPanel()}
294
+ />
295
+ )}
296
+ </>
297
+ )}
298
+ </div>
299
+ {Boolean(showExecutionContextOptions) && (
300
+ <div className="query-builder__setup__config-group__item">
301
+ <label
302
+ className="btn--sm query-builder__setup__config-group__item__label"
303
+ title="execution context"
304
+ htmlFor="query-builder__setup__context-selector"
305
+ >
306
+ Context
307
+ </label>
308
+ <CustomSelectorInput
309
+ inputId="query-builder__setup__context-selector"
310
+ className="panel__content__form__section__dropdown query-builder__setup__config-group__item__selector"
311
+ placeholder="Choose an execution context..."
312
+ options={executionContextOptions}
313
+ disabled={
314
+ executionContextOptions.length < 1 ||
315
+ (executionContextOptions.length === 1 &&
316
+ Boolean(selectedExecutionContextOption))
317
+ }
318
+ onChange={handleExecutionContextOptionChange}
319
+ value={selectedExecutionContextOption}
320
+ darkMode={
321
+ !applicationStore.layoutService
322
+ .TEMPORARY__isLightColorThemeEnabled
323
+ }
324
+ />
325
+ </div>
326
+ )}
327
+ {queryBuilderState.showRuntimeSelector && (
328
+ <div className="query-builder__setup__config-group__item">
329
+ <label
330
+ className="btn--sm query-builder__setup__config-group__item__label"
331
+ title="runtime"
332
+ htmlFor="query-builder__setup__runtime-selector"
333
+ >
334
+ Runtime
335
+ </label>
336
+ <CustomSelectorInput
337
+ inputId="query-builder__setup__runtime-selector"
338
+ className="panel__content__form__section__dropdown query-builder__setup__config-group__item__selector"
339
+ placeholder="Choose a runtime..."
340
+ noMatchMessage="No compatible runtime found for specified execution context"
341
+ options={runtimeOptions}
342
+ onChange={changeRuntime}
343
+ value={selectedRuntimeOption}
344
+ darkMode={
345
+ !applicationStore.layoutService
346
+ .TEMPORARY__isLightColorThemeEnabled
347
+ }
348
+ filterOption={runtimeFilterOption}
349
+ formatOptionLabel={getRuntimeOptionFormatter({
350
+ darkMode:
351
+ !applicationStore.layoutService
352
+ .TEMPORARY__isLightColorThemeEnabled,
353
+ })}
354
+ />
355
+ </div>
356
+ )}
357
+ <div className="query-builder__setup__config-group__item">
358
+ <QueryBuilderClassSelector
359
+ queryBuilderState={queryBuilderState}
360
+ classes={classes}
361
+ onClassChange={queryBuilderState.onClassChange}
362
+ noMatchMessage="No compatible entity found for specified execution context"
363
+ />
364
+ </div>
365
+ </div>
366
+ </div>
367
+ );
368
+ },
369
+ );
370
+
371
+ export const renderLegendQueryDataSpaceQueryBuilderSetupPanelContent = (
372
+ queryBuilderState: LegendQueryDataSpaceQueryBuilderState,
373
+ ): React.ReactNode => (
374
+ <LegendQueryDataSpaceQueryBuilderSetupPanelContent
375
+ queryBuilderState={queryBuilderState}
376
+ />
377
+ );
@@ -0,0 +1,80 @@
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 { ResolvedDataSpaceEntityWithOrigin } from '@finos/legend-extension-dsl-data-space/application';
18
+ import {
19
+ type DepotEntityWithOrigin,
20
+ generateGAVCoordinates,
21
+ } from '@finos/legend-storage';
22
+ import type { DataProductWithLegacyOption } from '../../stores/data-space/DataProductSelectorState.js';
23
+
24
+ /**
25
+ * Inline badge rendered next to every DataProduct (Lakehouse) entry
26
+ * in a combined data-space / data-product selector dropdown.
27
+ */
28
+ export const DataProductLakehouseBadge = (): React.ReactNode => (
29
+ <span className="query-builder__setup__data-product__option__tag query-builder__setup__data-product__option__tag--lakehouse">
30
+ Lakehouse
31
+ </span>
32
+ );
33
+
34
+ /**
35
+ * Option label for a combined dropdown that shows both legacy DataSpaces and
36
+ * depot DataProducts. DataProduct entries get a "Lakehouse" badge.
37
+ */
38
+ export const formatDataProductOrSpaceOptionLabel = (
39
+ option: DataProductWithLegacyOption,
40
+ ): React.ReactNode => {
41
+ const { label, value } = option;
42
+ const isDataProduct = !(value instanceof ResolvedDataSpaceEntityWithOrigin);
43
+ const title = isDataProduct
44
+ ? `${label} - ${value.path} - ${generateGAVCoordinates(value.origin?.groupId ?? '', value.origin?.artifactId ?? '', value.origin?.versionId ?? '')}`
45
+ : `${label} - ${value.path}${
46
+ value.origin
47
+ ? ` - ${generateGAVCoordinates(value.origin.groupId, value.origin.artifactId, value.origin.versionId)}`
48
+ : ''
49
+ }`;
50
+ return (
51
+ <div className="query-builder__setup__data-product__option" title={title}>
52
+ <div className="query-builder__setup__data-product__option__label">
53
+ {label}
54
+ </div>
55
+ {isDataProduct && <DataProductLakehouseBadge />}
56
+ </div>
57
+ );
58
+ };
59
+
60
+ /**
61
+ * Option label for a DataProduct-only dropdown. Entries that are legacy
62
+ * DataSpaces (ResolvedDataSpaceEntityWithOrigin) do NOT get the badge; only
63
+ * actual Lakehouse DataProduct entries do.
64
+ */
65
+ export const formatDataProductOptionLabel = (option: {
66
+ label: string;
67
+ value: DepotEntityWithOrigin;
68
+ }): React.ReactNode => {
69
+ const { label, value } = option;
70
+ const isDataProduct = !(value instanceof ResolvedDataSpaceEntityWithOrigin);
71
+ const title = `${label} - ${value.path} - ${generateGAVCoordinates(value.origin?.groupId ?? '', value.origin?.artifactId ?? '', value.origin?.versionId ?? '')}`;
72
+ return (
73
+ <div className="query-builder__setup__data-product__option" title={title}>
74
+ <div className="query-builder__setup__data-product__option__label">
75
+ {label}
76
+ </div>
77
+ {isDataProduct && <DataProductLakehouseBadge />}
78
+ </div>
79
+ );
80
+ };
@@ -215,7 +215,7 @@ export class QueryCreatorState {
215
215
  'Query builder state required to build query to edit',
216
216
  );
217
217
  this.createQueryState.inProgress();
218
- const rawLambda = queryBuilderState.buildQuery();
218
+ const rawLambda = queryBuilderState.buildQueryForPersistence();
219
219
  const config = this.editorStore.getPersistConfiguration(rawLambda, {
220
220
  update: true,
221
221
  });
@@ -1047,27 +1047,16 @@ export abstract class QueryEditorStore {
1047
1047
  const modelGroups = dataProduct.accessPointGroups.filter(
1048
1048
  filterByType(ModelAccessPointGroup),
1049
1049
  );
1050
+ if (!modelGroups.length) {
1051
+ throw new UnsupportedOperationError(
1052
+ `Only Data Product with Model Access Points are currently supported in Query. ${dataProduct.path} does not have any Model Access Point Groups.`,
1053
+ );
1054
+ }
1050
1055
  if (accessId) {
1051
1056
  const matchingGroup = modelGroups.find((g) => g.id === accessId);
1052
1057
  if (matchingGroup) {
1053
1058
  return matchingGroup;
1054
1059
  }
1055
- // Search native execution contexts
1056
- const matchingNative =
1057
- dataProduct.nativeModelAccess?.nativeModelExecutionContexts.find(
1058
- (ctx) => ctx.key === accessId,
1059
- );
1060
- if (matchingNative) {
1061
- return matchingNative;
1062
- }
1063
- } else {
1064
- // No accessId: fall back to defaults
1065
- if (dataProduct.nativeModelAccess) {
1066
- return dataProduct.nativeModelAccess.defaultExecutionContext;
1067
- }
1068
- if (modelGroups.length > 0) {
1069
- return guaranteeNonNullable(modelGroups[0]);
1070
- }
1071
1060
  }
1072
1061
  throw new UnsupportedOperationError(
1073
1062
  `Can't resolve execution state for data product '${dataProduct.path}'${accessId ? ` with access ID '${accessId}'` : ''}`,
@@ -1172,6 +1161,9 @@ export abstract class QueryEditorStore {
1172
1161
  accessId: string,
1173
1162
  dataProductAccessType: DataProductAccessType,
1174
1163
  onDataProductChange: (val: DepotEntityWithOrigin) => Promise<void>,
1164
+ onLegacyDataSpaceChange?:
1165
+ | ((val: ResolvedDataSpaceEntityWithOrigin) => void)
1166
+ | undefined,
1175
1167
  productSelectorState?: DataProductSelectorState | undefined,
1176
1168
  ): Promise<LegendQueryDataProductQueryBuilderState> {
1177
1169
  // 3. Build minimal graph and get analysis result
@@ -1229,6 +1221,7 @@ export abstract class QueryEditorStore {
1229
1221
  this.depotServerClient,
1230
1222
  this.applicationStore,
1231
1223
  ),
1224
+ onLegacyDataSpaceChange,
1232
1225
  undefined,
1233
1226
  undefined,
1234
1227
  this.applicationStore.config.options.queryBuilderConfig,
@@ -1628,7 +1621,7 @@ export class ExistingQueryUpdateState {
1628
1621
  this.editorStore.queryBuilderState,
1629
1622
  'Query builder state required to build query to edit',
1630
1623
  );
1631
- const rawLambda = queryBuilderState.buildQuery();
1624
+ const rawLambda = queryBuilderState.buildQueryForPersistence();
1632
1625
  const config = this.editorStore.getPersistConfiguration(rawLambda, {
1633
1626
  update: true,
1634
1627
  });
@@ -34,14 +34,14 @@ export const resolveDefaultDataProductAccessType = (
34
34
  };
35
35
  }
36
36
 
37
- const native =
38
- dataProductArtifact.nativeModelAccess?.nativeModelExecutionContexts[0];
39
- if (native) {
40
- return {
41
- type: DataProductAccessType.NATIVE,
42
- id: native.key,
43
- };
44
- }
37
+ // const native =
38
+ // dataProductArtifact.nativeModelAccess?.nativeModelExecutionContexts[0];
39
+ // if (native) {
40
+ // return {
41
+ // type: DataProductAccessType.NATIVE,
42
+ // id: native.key,
43
+ // };
44
+ // }
45
45
  throw new Error(
46
46
  `Data Product not supported for querying on legend query ${dataProductArtifact.dataProduct.path}. Must contain a model access point or native model access.`,
47
47
  );
@@ -22,7 +22,6 @@ import {
22
22
  type QueryBuilderActionConfig,
23
23
  type QueryBuilderConfig,
24
24
  type QueryBuilderWorkflowState,
25
- type ExtraOptionsConfig,
26
25
  } from '@finos/legend-query-builder';
27
26
  import { renderLegendDataProductQueryBuilderSetupPanelContent } from '../../../components/data-product/LegendQueryDataProductQueryBuilder.js';
28
27
  import type { LegendQueryApplicationStore } from '../../LegendQueryBaseStore.js';
@@ -51,6 +50,7 @@ import {
51
50
  } from '../../../__lib__/LegendQueryNavigation.js';
52
51
  import { compareLabelFn } from '@finos/legend-art';
53
52
  import type { DataProductSelectorState } from '../../data-space/DataProductSelectorState.js';
53
+ import { ResolvedDataSpaceEntityWithOrigin } from '@finos/legend-extension-dsl-data-space/application';
54
54
  import type { GeneratorFn } from '@finos/legend-shared';
55
55
  import { flowResult } from 'mobx';
56
56
 
@@ -58,7 +58,9 @@ export class LegendQueryDataProductQueryBuilderState extends DataProductQueryBui
58
58
  declare applicationStore: LegendQueryApplicationStore;
59
59
  depotServerClient: DepotServerClient;
60
60
  project: ProjectGAVCoordinates;
61
- declare extraOptionsConfig: ExtraOptionsConfig<DepotEntityWithOrigin>;
61
+ readonly onLegacyDataSpaceChange:
62
+ | ((val: ResolvedDataSpaceEntityWithOrigin) => void)
63
+ | undefined;
62
64
  productSelectorState: DataProductSelectorState;
63
65
 
64
66
  constructor(
@@ -73,6 +75,9 @@ export class LegendQueryDataProductQueryBuilderState extends DataProductQueryBui
73
75
  project: ProjectGAVCoordinates,
74
76
  onDataProductChange: (val: DepotEntityWithOrigin) => Promise<void>,
75
77
  productSelectorState: DataProductSelectorState,
78
+ onLegacyDataSpaceChange?:
79
+ | ((val: ResolvedDataSpaceEntityWithOrigin) => void)
80
+ | undefined,
76
81
  onExecutionContextChange?:
77
82
  | ((val: NativeModelExecutionContext) => void)
78
83
  | undefined,
@@ -98,6 +103,18 @@ export class LegendQueryDataProductQueryBuilderState extends DataProductQueryBui
98
103
  this.project = project;
99
104
  this.depotServerClient = depotServerClient;
100
105
  this.productSelectorState = productSelectorState;
106
+ this.onLegacyDataSpaceChange = onLegacyDataSpaceChange;
107
+ }
108
+
109
+ override handleDataProductChange(val: DepotEntityWithOrigin): void {
110
+ if (
111
+ val instanceof ResolvedDataSpaceEntityWithOrigin &&
112
+ this.onLegacyDataSpaceChange
113
+ ) {
114
+ this.onLegacyDataSpaceChange(val);
115
+ } else {
116
+ super.handleDataProductChange(val);
117
+ }
101
118
  }
102
119
 
103
120
  override get dataProductOptions(): DataProductOption[] {
@@ -115,8 +132,6 @@ export class LegendQueryDataProductQueryBuilderState extends DataProductQueryBui
115
132
  }
116
133
 
117
134
  override *loadEntities(): GeneratorFn<void> {
118
- yield flowResult(super.loadEntities());
119
- // also ensure the shared selector has loaded depot-level entities
120
135
  if (!this.productSelectorState.isCompletelyLoaded) {
121
136
  yield flowResult(this.productSelectorState.loadProducts());
122
137
  }
@@ -60,10 +60,11 @@ import {
60
60
  import type { LegendQueryApplicationStore } from '../LegendQueryBaseStore.js';
61
61
  import {
62
62
  type DataSpaceQueryBuilderState,
63
- ResolvedDataSpaceEntityWithOrigin,
63
+ type ResolvedDataSpaceEntityWithOrigin,
64
64
  createQueryClassTaggedValue,
65
65
  createQueryDataSpaceTaggedValue,
66
66
  } from '@finos/legend-extension-dsl-data-space/application';
67
+ import { createQueryDataProductTaggedValue } from '../../components/data-product/QueryDataProductUtil.js';
67
68
  import { DataProductSelectorState } from './DataProductSelectorState.js';
68
69
  import { LegendQueryUserDataHelper } from '../../__lib__/LegendQueryUserDataHelper.js';
69
70
  import {
@@ -397,15 +398,14 @@ export class DataProductQueryCreatorStore extends QueryEditorStore {
397
398
  queryableDataProduct.id,
398
399
  queryableDataProduct.dataProductType as DataProductAccessType,
399
400
  async (dataProductInfo: DepotEntityWithOrigin) => {
400
- if (dataProductInfo instanceof ResolvedDataSpaceEntityWithOrigin) {
401
- flowResult(this.changeDataSpace(dataProductInfo)).catch(
402
- this.applicationStore.alertUnhandledError,
403
- );
404
- } else {
405
- flowResult(this.changeDataProduct(dataProductInfo)).catch(
406
- this.applicationStore.alertUnhandledError,
407
- );
408
- }
401
+ flowResult(this.changeDataProduct(dataProductInfo)).catch(
402
+ this.applicationStore.alertUnhandledError,
403
+ );
404
+ },
405
+ (dataSpaceInfo: ResolvedDataSpaceEntityWithOrigin) => {
406
+ flowResult(this.changeDataSpace(dataSpaceInfo)).catch(
407
+ this.applicationStore.alertUnhandledError,
408
+ );
409
409
  },
410
410
  this.productSelectorState,
411
411
  );
@@ -666,7 +666,14 @@ export class DataProductQueryCreatorStore extends QueryEditorStore {
666
666
  createQueryClassTaggedValue(this.queryBuilderState.class.path),
667
667
  );
668
668
  }
669
- taggedValues.push(createQueryDataSpaceTaggedValue(element.path));
669
+ if (element instanceof QueryableDataProduct) {
670
+ // For new DataProducts, use the 'dataProduct' tagged value so the
671
+ // query is not mis-classified as a DataSpace when reopened.
672
+ taggedValues.push(createQueryDataProductTaggedValue(element.path));
673
+ } else {
674
+ // Legacy DataSpace-based products use the 'dataSpace' tagged value.
675
+ taggedValues.push(createQueryDataSpaceTaggedValue(element.path));
676
+ }
670
677
  query.taggedValues = taggedValues;
671
678
  },
672
679
  };
@@ -755,7 +762,9 @@ export class DataProductQueryCreatorStore extends QueryEditorStore {
755
762
  }),
756
763
  ];
757
764
  val.taggedValues = [
758
- createQueryDataSpaceTaggedValue(this.queryableElement.path),
765
+ this.queryableElement instanceof QueryableDataProduct
766
+ ? createQueryDataProductTaggedValue(this.queryableElement.path)
767
+ : createQueryDataSpaceTaggedValue(this.queryableElement.path),
759
768
  ];
760
769
  val.combineTaggedValuesCondition = true;
761
770
  }