@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.
- package/lib/components/__test-utils__/QueryEditorComponentTestUtils.d.ts.map +1 -1
- package/lib/components/__test-utils__/QueryEditorComponentTestUtils.js +2 -2
- package/lib/components/__test-utils__/QueryEditorComponentTestUtils.js.map +1 -1
- package/lib/components/data-product/LegendQueryDataProductQueryBuilder.d.ts.map +1 -1
- package/lib/components/data-product/LegendQueryDataProductQueryBuilder.js +4 -1
- package/lib/components/data-product/LegendQueryDataProductQueryBuilder.js.map +1 -1
- package/lib/components/data-product/QueryDataProductUtil.d.ts +20 -0
- package/lib/components/data-product/QueryDataProductUtil.d.ts.map +1 -0
- package/lib/components/data-product/QueryDataProductUtil.js +31 -0
- package/lib/components/data-product/QueryDataProductUtil.js.map +1 -0
- package/lib/components/data-space/LegendQueryDataSpaceQueryBuilder.d.ts +18 -0
- package/lib/components/data-space/LegendQueryDataSpaceQueryBuilder.d.ts.map +1 -0
- package/lib/components/data-space/LegendQueryDataSpaceQueryBuilder.js +139 -0
- package/lib/components/data-space/LegendQueryDataSpaceQueryBuilder.js.map +1 -0
- package/lib/components/shared/LegendQueryDataProductOptionLabel.d.ts +37 -0
- package/lib/components/shared/LegendQueryDataProductOptionLabel.d.ts.map +1 -0
- package/lib/components/shared/LegendQueryDataProductOptionLabel.js +49 -0
- package/lib/components/shared/LegendQueryDataProductOptionLabel.js.map +1 -0
- package/lib/index.css +2 -2
- package/lib/index.css.map +1 -1
- package/lib/light-mode.css +1 -1
- package/lib/package.json +3 -3
- package/lib/stores/QueryEditorStore.d.ts +2 -1
- package/lib/stores/QueryEditorStore.d.ts.map +1 -1
- package/lib/stores/QueryEditorStore.js +7 -18
- package/lib/stores/QueryEditorStore.js.map +1 -1
- package/lib/stores/data-product/query-builder/DataProductArtifactHelper.js +8 -7
- package/lib/stores/data-product/query-builder/DataProductArtifactHelper.js.map +1 -1
- package/lib/stores/data-product/query-builder/LegendQueryDataProductQueryBuilderState.d.ts +5 -3
- package/lib/stores/data-product/query-builder/LegendQueryDataProductQueryBuilderState.d.ts.map +1 -1
- package/lib/stores/data-product/query-builder/LegendQueryDataProductQueryBuilderState.js +13 -3
- package/lib/stores/data-product/query-builder/LegendQueryDataProductQueryBuilderState.js.map +1 -1
- package/lib/stores/data-space/DataProductQueryCreatorStore.d.ts +1 -1
- package/lib/stores/data-space/DataProductQueryCreatorStore.d.ts.map +1 -1
- package/lib/stores/data-space/DataProductQueryCreatorStore.js +17 -9
- package/lib/stores/data-space/DataProductQueryCreatorStore.js.map +1 -1
- package/lib/stores/data-space/query-builder/LegendQueryDataSpaceQueryBuilderState.d.ts +3 -2
- package/lib/stores/data-space/query-builder/LegendQueryDataSpaceQueryBuilderState.d.ts.map +1 -1
- package/lib/stores/data-space/query-builder/LegendQueryDataSpaceQueryBuilderState.js +5 -15
- package/lib/stores/data-space/query-builder/LegendQueryDataSpaceQueryBuilderState.js.map +1 -1
- package/package.json +8 -8
- package/src/components/__test-utils__/QueryEditorComponentTestUtils.tsx +2 -0
- package/src/components/data-product/LegendQueryDataProductQueryBuilder.tsx +7 -0
- package/src/components/data-product/QueryDataProductUtil.ts +46 -0
- package/src/components/data-space/LegendQueryDataSpaceQueryBuilder.tsx +377 -0
- package/src/components/shared/LegendQueryDataProductOptionLabel.tsx +80 -0
- package/src/stores/QueryEditorStore.ts +11 -18
- package/src/stores/data-product/query-builder/DataProductArtifactHelper.ts +8 -8
- package/src/stores/data-product/query-builder/LegendQueryDataProductQueryBuilderState.ts +19 -4
- package/src/stores/data-space/DataProductQueryCreatorStore.ts +21 -12
- package/src/stores/data-space/query-builder/LegendQueryDataSpaceQueryBuilderState.ts +6 -28
- 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.
|
|
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.
|
|
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
|
-
|
|
39
|
-
if (native) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
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
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
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
|
-
|
|
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
|
-
|
|
765
|
+
this.queryableElement instanceof QueryableDataProduct
|
|
766
|
+
? createQueryDataProductTaggedValue(this.queryableElement.path)
|
|
767
|
+
: createQueryDataSpaceTaggedValue(this.queryableElement.path),
|
|
759
768
|
];
|
|
760
769
|
val.combineTaggedValuesCondition = true;
|
|
761
770
|
}
|