@finos/legend-application-query 13.8.29 → 13.8.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/__lib__/LegendQueryEvent.d.ts +3 -0
- package/lib/__lib__/LegendQueryEvent.d.ts.map +1 -1
- package/lib/__lib__/LegendQueryEvent.js +3 -0
- package/lib/__lib__/LegendQueryEvent.js.map +1 -1
- package/lib/__lib__/LegendQueryNavigation.d.ts +12 -0
- package/lib/__lib__/LegendQueryNavigation.d.ts.map +1 -1
- package/lib/__lib__/LegendQueryNavigation.js +16 -0
- package/lib/__lib__/LegendQueryNavigation.js.map +1 -1
- package/lib/__lib__/LegendQueryTelemetryHelper.d.ts +3 -0
- package/lib/__lib__/LegendQueryTelemetryHelper.d.ts.map +1 -1
- package/lib/__lib__/LegendQueryTelemetryHelper.js +9 -0
- package/lib/__lib__/LegendQueryTelemetryHelper.js.map +1 -1
- package/lib/components/Core_LegendQueryApplicationPlugin.d.ts.map +1 -1
- package/lib/components/Core_LegendQueryApplicationPlugin.js +15 -0
- package/lib/components/Core_LegendQueryApplicationPlugin.js.map +1 -1
- package/lib/components/LegendQueryWebApplication.d.ts.map +1 -1
- package/lib/components/LegendQueryWebApplication.js +2 -1
- package/lib/components/LegendQueryWebApplication.js.map +1 -1
- package/lib/components/QueryEditor.d.ts.map +1 -1
- package/lib/components/QueryEditor.js +12 -2
- package/lib/components/QueryEditor.js.map +1 -1
- package/lib/components/data-product/LegendQueryDataProductQueryBuilder.d.ts.map +1 -1
- package/lib/components/data-product/LegendQueryDataProductQueryBuilder.js +6 -40
- package/lib/components/data-product/LegendQueryDataProductQueryBuilder.js.map +1 -1
- package/lib/components/ingest/IngestInfo.d.ts +32 -0
- package/lib/components/ingest/IngestInfo.d.ts.map +1 -0
- package/lib/components/ingest/IngestInfo.js +63 -0
- package/lib/components/ingest/IngestInfo.js.map +1 -0
- package/lib/components/ingest/IngestQueryBuilderSetupPanel.d.ts +18 -0
- package/lib/components/ingest/IngestQueryBuilderSetupPanel.d.ts.map +1 -0
- package/lib/components/ingest/IngestQueryBuilderSetupPanel.js +77 -0
- package/lib/components/ingest/IngestQueryBuilderSetupPanel.js.map +1 -0
- package/lib/components/ingest/IngestQueryCreator.d.ts +27 -0
- package/lib/components/ingest/IngestQueryCreator.d.ts.map +1 -0
- package/lib/components/ingest/IngestQueryCreator.js +45 -0
- package/lib/components/ingest/IngestQueryCreator.js.map +1 -0
- package/lib/components/shared/LakehouseRuntimeConfigModal.d.ts +29 -0
- package/lib/components/shared/LakehouseRuntimeConfigModal.d.ts.map +1 -0
- package/lib/components/shared/LakehouseRuntimeConfigModal.js +60 -0
- package/lib/components/shared/LakehouseRuntimeConfigModal.js.map +1 -0
- package/lib/index.css +1 -1
- package/lib/light-mode.css +1 -1
- package/lib/package.json +1 -1
- package/lib/stores/QueryEditorStore.d.ts +10 -0
- package/lib/stores/QueryEditorStore.d.ts.map +1 -1
- package/lib/stores/QueryEditorStore.js +81 -2
- package/lib/stores/QueryEditorStore.js.map +1 -1
- package/lib/stores/ingest/IngestLegendQueryBuilderState.d.ts +122 -0
- package/lib/stores/ingest/IngestLegendQueryBuilderState.d.ts.map +1 -0
- package/lib/stores/ingest/IngestLegendQueryBuilderState.js +191 -0
- package/lib/stores/ingest/IngestLegendQueryBuilderState.js.map +1 -0
- package/lib/stores/ingest/IngestQueryCreatorStore.d.ts +75 -0
- package/lib/stores/ingest/IngestQueryCreatorStore.d.ts.map +1 -0
- package/lib/stores/ingest/IngestQueryCreatorStore.js +169 -0
- package/lib/stores/ingest/IngestQueryCreatorStore.js.map +1 -0
- package/lib/stores/ingest/IngestQueryGraphHelper.d.ts +58 -0
- package/lib/stores/ingest/IngestQueryGraphHelper.d.ts.map +1 -0
- package/lib/stores/ingest/IngestQueryGraphHelper.js +72 -0
- package/lib/stores/ingest/IngestQueryGraphHelper.js.map +1 -0
- package/package.json +13 -13
- package/src/__lib__/LegendQueryEvent.ts +3 -0
- package/src/__lib__/LegendQueryNavigation.ts +35 -0
- package/src/__lib__/LegendQueryTelemetryHelper.ts +15 -0
- package/src/components/Core_LegendQueryApplicationPlugin.tsx +20 -0
- package/src/components/LegendQueryWebApplication.tsx +14 -0
- package/src/components/QueryEditor.tsx +25 -1
- package/src/components/data-product/LegendQueryDataProductQueryBuilder.tsx +7 -127
- package/src/components/ingest/IngestInfo.tsx +197 -0
- package/src/components/ingest/IngestQueryBuilderSetupPanel.tsx +204 -0
- package/src/components/ingest/IngestQueryCreator.tsx +75 -0
- package/src/components/shared/LakehouseRuntimeConfigModal.tsx +148 -0
- package/src/stores/QueryEditorStore.ts +131 -1
- package/src/stores/ingest/IngestLegendQueryBuilderState.ts +259 -0
- package/src/stores/ingest/IngestQueryCreatorStore.ts +262 -0
- package/src/stores/ingest/IngestQueryGraphHelper.ts +120 -0
- package/tsconfig.json +7 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2020-present, Goldman Sachs
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
Dialog,
|
|
19
|
+
Modal,
|
|
20
|
+
ModalTitle,
|
|
21
|
+
Panel,
|
|
22
|
+
PanelFullContent,
|
|
23
|
+
} from '@finos/legend-art';
|
|
24
|
+
import {
|
|
25
|
+
LATEST_VERSION_ALIAS,
|
|
26
|
+
StoreProjectData,
|
|
27
|
+
VersionedProjectData,
|
|
28
|
+
} from '@finos/legend-server-depot';
|
|
29
|
+
import { observer } from 'mobx-react-lite';
|
|
30
|
+
import { flowResult } from 'mobx';
|
|
31
|
+
import { assertErrorThrown } from '@finos/legend-shared';
|
|
32
|
+
import { LakehouseRuntime } from '@finos/legend-graph';
|
|
33
|
+
import type { QueryEditorStore } from '../../stores/QueryEditorStore.js';
|
|
34
|
+
import type { IngestLegendQueryBuilderState } from '../../stores/ingest/IngestLegendQueryBuilderState.js';
|
|
35
|
+
import { EXTERNAL_APPLICATION_NAVIGATION__generateStudioSDLCProjectViewUrl } from '../../__lib__/LegendQueryNavigation.js';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* "About Ingest" info modal, mirroring `QueryEditorDataProductInfoModal`.
|
|
39
|
+
* Surfaces the project GAV, the ingest name + currently selected data set,
|
|
40
|
+
* the adhoc lakehouse runtime (environment + warehouse), and a link to the
|
|
41
|
+
* ingest definition's configuration in Studio.
|
|
42
|
+
*/
|
|
43
|
+
export const QueryEditorIngestInfoModal = observer(
|
|
44
|
+
(props: {
|
|
45
|
+
editorStore: QueryEditorStore;
|
|
46
|
+
queryBuilderState: IngestLegendQueryBuilderState;
|
|
47
|
+
open: boolean;
|
|
48
|
+
closeModal: () => void;
|
|
49
|
+
}) => {
|
|
50
|
+
const { editorStore, queryBuilderState, open, closeModal } = props;
|
|
51
|
+
const projectInfo = editorStore.getProjectInfo();
|
|
52
|
+
const ingestDefinition = queryBuilderState.ingestDefinition;
|
|
53
|
+
const lakehouseRuntime = queryBuilderState.lakehouseRuntime;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Open the given element path (or the project root if `undefined`) in
|
|
57
|
+
* Studio's SDLC project view. Mirrors `DataProductInfo.visitElement`.
|
|
58
|
+
*/
|
|
59
|
+
const visitElement = async (path: string | undefined): Promise<void> => {
|
|
60
|
+
try {
|
|
61
|
+
if (projectInfo) {
|
|
62
|
+
const project = StoreProjectData.serialization.fromJson(
|
|
63
|
+
await editorStore.depotServerClient.getProject(
|
|
64
|
+
projectInfo.groupId,
|
|
65
|
+
projectInfo.artifactId,
|
|
66
|
+
),
|
|
67
|
+
);
|
|
68
|
+
const versionId =
|
|
69
|
+
projectInfo.versionId === LATEST_VERSION_ALIAS
|
|
70
|
+
? VersionedProjectData.serialization.fromJson(
|
|
71
|
+
await editorStore.depotServerClient.getLatestVersion(
|
|
72
|
+
projectInfo.groupId,
|
|
73
|
+
projectInfo.artifactId,
|
|
74
|
+
),
|
|
75
|
+
).versionId
|
|
76
|
+
: projectInfo.versionId;
|
|
77
|
+
editorStore.applicationStore.navigationService.navigator.visitAddress(
|
|
78
|
+
EXTERNAL_APPLICATION_NAVIGATION__generateStudioSDLCProjectViewUrl(
|
|
79
|
+
editorStore.applicationStore.config.studioApplicationUrl,
|
|
80
|
+
project.projectId,
|
|
81
|
+
versionId,
|
|
82
|
+
path,
|
|
83
|
+
),
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
} catch (error) {
|
|
87
|
+
assertErrorThrown(error);
|
|
88
|
+
editorStore.applicationStore.notificationService.notifyError(
|
|
89
|
+
path
|
|
90
|
+
? `Can't visit element of path: '${path}'`
|
|
91
|
+
: `Can't visit project`,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<Dialog
|
|
98
|
+
open={open}
|
|
99
|
+
onClose={closeModal}
|
|
100
|
+
classes={{ container: 'dataspace-info-modal__container' }}
|
|
101
|
+
slotProps={{
|
|
102
|
+
paper: {
|
|
103
|
+
classes: { root: 'dataspace-info-modal__inner-container' },
|
|
104
|
+
},
|
|
105
|
+
}}
|
|
106
|
+
>
|
|
107
|
+
<Modal
|
|
108
|
+
darkMode={
|
|
109
|
+
!editorStore.applicationStore.layoutService
|
|
110
|
+
.TEMPORARY__isLightColorThemeEnabled
|
|
111
|
+
}
|
|
112
|
+
className="dataspace-info-modal"
|
|
113
|
+
>
|
|
114
|
+
<div className="dataspace-info-modal__header">
|
|
115
|
+
<ModalTitle title="About Ingest" />
|
|
116
|
+
</div>
|
|
117
|
+
<Panel>
|
|
118
|
+
<PanelFullContent>
|
|
119
|
+
{projectInfo && (
|
|
120
|
+
<div className="dataspace-info-modal__field">
|
|
121
|
+
<div className="dataspace-info-modal__field__label">
|
|
122
|
+
Project
|
|
123
|
+
</div>
|
|
124
|
+
<div
|
|
125
|
+
className="dataspace-info-modal__field__value dataspace-info-modal__field__value--linkable"
|
|
126
|
+
onClick={() => flowResult(visitElement(undefined))}
|
|
127
|
+
>
|
|
128
|
+
{`${projectInfo.groupId}:${projectInfo.artifactId}:${projectInfo.versionId}`}
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
)}
|
|
132
|
+
<div className="dataspace-info-modal__field">
|
|
133
|
+
<div className="dataspace-info-modal__field__label">Name</div>
|
|
134
|
+
<div className="dataspace-info-modal__field__value">
|
|
135
|
+
{ingestDefinition.name}
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
<div className="dataspace-info-modal__field">
|
|
139
|
+
<div className="dataspace-info-modal__field__label">Path</div>
|
|
140
|
+
<div className="dataspace-info-modal__field__value">
|
|
141
|
+
{ingestDefinition.path}
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
{queryBuilderState.dataSet && (
|
|
145
|
+
<div className="dataspace-info-modal__field">
|
|
146
|
+
<div className="dataspace-info-modal__field__label">
|
|
147
|
+
Data Set
|
|
148
|
+
</div>
|
|
149
|
+
<div className="dataspace-info-modal__field__value">
|
|
150
|
+
{queryBuilderState.dataSet}
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
)}
|
|
154
|
+
{lakehouseRuntime instanceof LakehouseRuntime && (
|
|
155
|
+
<>
|
|
156
|
+
{lakehouseRuntime.environment && (
|
|
157
|
+
<div className="dataspace-info-modal__field">
|
|
158
|
+
<div className="dataspace-info-modal__field__label">
|
|
159
|
+
Environment
|
|
160
|
+
</div>
|
|
161
|
+
<div className="dataspace-info-modal__field__value">
|
|
162
|
+
{lakehouseRuntime.environment}
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
)}
|
|
166
|
+
{lakehouseRuntime.warehouse && (
|
|
167
|
+
<div className="dataspace-info-modal__field">
|
|
168
|
+
<div className="dataspace-info-modal__field__label">
|
|
169
|
+
Warehouse
|
|
170
|
+
</div>
|
|
171
|
+
<div className="dataspace-info-modal__field__value">
|
|
172
|
+
{lakehouseRuntime.warehouse}
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
)}
|
|
176
|
+
</>
|
|
177
|
+
)}
|
|
178
|
+
<div className="dataspace-info-modal__field">
|
|
179
|
+
<div className="dataspace-info-modal__field__label">
|
|
180
|
+
Configuration
|
|
181
|
+
</div>
|
|
182
|
+
<div
|
|
183
|
+
className="dataspace-info-modal__field__value dataspace-info-modal__field__value--linkable"
|
|
184
|
+
onClick={() =>
|
|
185
|
+
flowResult(visitElement(ingestDefinition.path))
|
|
186
|
+
}
|
|
187
|
+
>
|
|
188
|
+
Show Ingest Configuration
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
</PanelFullContent>
|
|
192
|
+
</Panel>
|
|
193
|
+
</Modal>
|
|
194
|
+
</Dialog>
|
|
195
|
+
);
|
|
196
|
+
},
|
|
197
|
+
);
|
|
@@ -0,0 +1,204 @@
|
|
|
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
|
+
CogIcon,
|
|
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
|
+
} from '@finos/legend-art';
|
|
34
|
+
import { observer } from 'mobx-react-lite';
|
|
35
|
+
import { useState } from 'react';
|
|
36
|
+
import type { IngestLegendQueryBuilderState } from '../../stores/ingest/IngestLegendQueryBuilderState.js';
|
|
37
|
+
import { LakehouseRuntimeConfigModal } from '../shared/LakehouseRuntimeConfigModal.js';
|
|
38
|
+
|
|
39
|
+
type DataSetOption = {
|
|
40
|
+
label: string;
|
|
41
|
+
value: { schemaName?: string | undefined; tableName: string };
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
type IngestOption = {
|
|
45
|
+
label: string;
|
|
46
|
+
value: string;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Setup panel for ingest-backed queries. Mirrors the data-product setup
|
|
51
|
+
* panel's header actions (copy deep-link, edit lakehouse runtime) but
|
|
52
|
+
* skips the source / runtime selectors since the ingest flow is fully
|
|
53
|
+
* resolved from the route + adhoc runtime. The data set selector is kept
|
|
54
|
+
* so the user can switch between datasets of the same ingest definition.
|
|
55
|
+
*/
|
|
56
|
+
const IngestQueryBuilderSetupPanelContent = observer(
|
|
57
|
+
(props: { queryBuilderState: IngestLegendQueryBuilderState }) => {
|
|
58
|
+
const { queryBuilderState } = props;
|
|
59
|
+
const applicationStore = useApplicationStore();
|
|
60
|
+
const [isLakehouseConfigModalOpen, setIsLakehouseConfigModalOpen] =
|
|
61
|
+
useState(false);
|
|
62
|
+
const lakehouseRuntime = queryBuilderState.lakehouseRuntime;
|
|
63
|
+
const showLakehouseConfigButton = Boolean(lakehouseRuntime);
|
|
64
|
+
const darkMode =
|
|
65
|
+
!applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled;
|
|
66
|
+
|
|
67
|
+
// data set selector (driven by the ingest definition's datasets via
|
|
68
|
+
// `AccessorQueryBuilderState.accessorsOptions`).
|
|
69
|
+
const dataSetOptions =
|
|
70
|
+
queryBuilderState.accessorsOptions.sort(compareLabelFn);
|
|
71
|
+
const selectedDataSetOption = queryBuilderState.sourceAccessor
|
|
72
|
+
? (dataSetOptions.find(
|
|
73
|
+
(opt) =>
|
|
74
|
+
opt.value.tableName ===
|
|
75
|
+
queryBuilderState.sourceAccessor?.accessor &&
|
|
76
|
+
opt.value.schemaName === queryBuilderState.sourceAccessor.schema,
|
|
77
|
+
) ?? null)
|
|
78
|
+
: null;
|
|
79
|
+
const changeDataSet = async (val: DataSetOption): Promise<void> => {
|
|
80
|
+
await queryBuilderState.changeAccessor(val.value);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// ingest selector (paths fetched up front; only one ingest is built into
|
|
84
|
+
// the graph at a time — see `IngestQueryCreatorStore.swapIngestDefinition`).
|
|
85
|
+
const ingestOptions: IngestOption[] = queryBuilderState.allIngestPaths
|
|
86
|
+
.map((path) => ({ label: path, value: path }))
|
|
87
|
+
.sort(compareLabelFn);
|
|
88
|
+
const selectedIngestOption: IngestOption | null =
|
|
89
|
+
ingestOptions.find(
|
|
90
|
+
(opt) => opt.value === queryBuilderState.ingestDefinition.path,
|
|
91
|
+
) ?? null;
|
|
92
|
+
const changeIngest = async (val: IngestOption): Promise<void> => {
|
|
93
|
+
await queryBuilderState.changeIngestDefinition(val.value);
|
|
94
|
+
};
|
|
95
|
+
const ingestFilterOption = createFilter({
|
|
96
|
+
ignoreCase: true,
|
|
97
|
+
ignoreAccents: false,
|
|
98
|
+
stringify: (option: { data: IngestOption }): string => option.data.value,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<div className="query-builder__setup__config-group">
|
|
103
|
+
<PanelHeader title="properties">
|
|
104
|
+
<PanelHeaderActions>
|
|
105
|
+
<PanelHeaderActionItem
|
|
106
|
+
title="copy ingest query set up link to clipboard"
|
|
107
|
+
onClick={() => queryBuilderState.copyIngestQueryLinkToClipBoard()}
|
|
108
|
+
>
|
|
109
|
+
<AnchorLinkIcon />
|
|
110
|
+
</PanelHeaderActionItem>
|
|
111
|
+
{showLakehouseConfigButton && (
|
|
112
|
+
<ControlledDropdownMenu
|
|
113
|
+
className="panel__header__action query-builder__setup__config-group__header__dropdown-trigger"
|
|
114
|
+
title="Show Settings..."
|
|
115
|
+
content={
|
|
116
|
+
<MenuContent>
|
|
117
|
+
<MenuContentItem
|
|
118
|
+
onClick={() => setIsLakehouseConfigModalOpen(true)}
|
|
119
|
+
>
|
|
120
|
+
<MenuContentItemIcon>
|
|
121
|
+
<CogIcon />
|
|
122
|
+
</MenuContentItemIcon>
|
|
123
|
+
<MenuContentItemLabel>
|
|
124
|
+
Lakehouse Runtime Configuration
|
|
125
|
+
</MenuContentItemLabel>
|
|
126
|
+
</MenuContentItem>
|
|
127
|
+
</MenuContent>
|
|
128
|
+
}
|
|
129
|
+
menuProps={{
|
|
130
|
+
anchorOrigin: {
|
|
131
|
+
vertical: 'bottom',
|
|
132
|
+
horizontal: 'right',
|
|
133
|
+
},
|
|
134
|
+
transformOrigin: {
|
|
135
|
+
vertical: 'top',
|
|
136
|
+
horizontal: 'right',
|
|
137
|
+
},
|
|
138
|
+
}}
|
|
139
|
+
>
|
|
140
|
+
<MoreVerticalIcon className="query-builder__icon__more-options" />
|
|
141
|
+
</ControlledDropdownMenu>
|
|
142
|
+
)}
|
|
143
|
+
</PanelHeaderActions>
|
|
144
|
+
</PanelHeader>
|
|
145
|
+
<div className="query-builder__setup__config-group__content">
|
|
146
|
+
<div className="query-builder__setup__config-group__item">
|
|
147
|
+
<label
|
|
148
|
+
className="btn--sm query-builder__setup__config-group__item__label"
|
|
149
|
+
title="ingest definition"
|
|
150
|
+
htmlFor="query-builder__setup__ingest-selector"
|
|
151
|
+
>
|
|
152
|
+
Ingest
|
|
153
|
+
</label>
|
|
154
|
+
<CustomSelectorInput
|
|
155
|
+
inputId="query-builder__setup__ingest-selector"
|
|
156
|
+
className="panel__content__form__section__dropdown query-builder__setup__config-group__item__selector"
|
|
157
|
+
placeholder="Choose an ingest..."
|
|
158
|
+
options={ingestOptions}
|
|
159
|
+
onChange={(val: IngestOption): void => {
|
|
160
|
+
changeIngest(val).catch(() => undefined);
|
|
161
|
+
}}
|
|
162
|
+
value={selectedIngestOption}
|
|
163
|
+
darkMode={darkMode}
|
|
164
|
+
disabled={queryBuilderState.isSwappingIngest}
|
|
165
|
+
filterOption={ingestFilterOption}
|
|
166
|
+
/>
|
|
167
|
+
</div>
|
|
168
|
+
<div className="query-builder__setup__config-group__item">
|
|
169
|
+
<label
|
|
170
|
+
className="btn--sm query-builder__setup__config-group__item__label"
|
|
171
|
+
title="data set"
|
|
172
|
+
htmlFor="query-builder__setup__ingest-dataset-selector"
|
|
173
|
+
>
|
|
174
|
+
Data Set
|
|
175
|
+
</label>
|
|
176
|
+
<CustomSelectorInput
|
|
177
|
+
inputId="query-builder__setup__ingest-dataset-selector"
|
|
178
|
+
className="panel__content__form__section__dropdown query-builder__setup__config-group__item__selector"
|
|
179
|
+
placeholder="Choose a data set..."
|
|
180
|
+
options={dataSetOptions}
|
|
181
|
+
onChange={(val: DataSetOption): void => {
|
|
182
|
+
changeDataSet(val).catch(() => undefined);
|
|
183
|
+
}}
|
|
184
|
+
value={selectedDataSetOption}
|
|
185
|
+
darkMode={darkMode}
|
|
186
|
+
/>
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
<LakehouseRuntimeConfigModal
|
|
190
|
+
lakehouseRuntime={lakehouseRuntime}
|
|
191
|
+
open={isLakehouseConfigModalOpen}
|
|
192
|
+
onClose={() => setIsLakehouseConfigModalOpen(false)}
|
|
193
|
+
darkMode={darkMode}
|
|
194
|
+
/>
|
|
195
|
+
</div>
|
|
196
|
+
);
|
|
197
|
+
},
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
export const renderIngestQueryBuilderSetupPanelContent = (
|
|
201
|
+
queryBuilderState: IngestLegendQueryBuilderState,
|
|
202
|
+
): React.ReactNode => (
|
|
203
|
+
<IngestQueryBuilderSetupPanelContent queryBuilderState={queryBuilderState} />
|
|
204
|
+
);
|
|
@@ -0,0 +1,75 @@
|
|
|
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 { observer, useLocalObservable } from 'mobx-react-lite';
|
|
18
|
+
import { parseGAVCoordinates } from '@finos/legend-storage';
|
|
19
|
+
import { guaranteeNonNullable } from '@finos/legend-shared';
|
|
20
|
+
import {
|
|
21
|
+
useLegendQueryApplicationStore,
|
|
22
|
+
useLegendQueryBaseStore,
|
|
23
|
+
} from '../LegendQueryFrameworkProvider.js';
|
|
24
|
+
import {
|
|
25
|
+
INGEST_QUERY_CREATOR_ROUTE_PATTERN_TOKEN,
|
|
26
|
+
type IngestQueryCreatorPathParams,
|
|
27
|
+
} from '../../__lib__/LegendQueryNavigation.js';
|
|
28
|
+
import { IngestQueryCreatorStore } from '../../stores/ingest/IngestQueryCreatorStore.js';
|
|
29
|
+
import { QueryEditorStoreContext } from '../QueryEditorStoreProvider.js';
|
|
30
|
+
import { QueryEditor } from '../QueryEditor.js';
|
|
31
|
+
import { useParams } from '@finos/legend-application/browser';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Route handler for `LEGEND_QUERY_ROUTE_PATTERN.INGEST_QUERY`.
|
|
35
|
+
*
|
|
36
|
+
* Parses `gav`, `ingestDefinitionPath` and `dataSet` from the URL, builds an
|
|
37
|
+
* {@link IngestQueryCreatorStore}, and renders the shared {@link QueryEditor}
|
|
38
|
+
* against it. The store fetches only the ingest definition entity from Depot
|
|
39
|
+
* — no full graph build.
|
|
40
|
+
*/
|
|
41
|
+
export const IngestQueryCreator = observer(() => {
|
|
42
|
+
const applicationStore = useLegendQueryApplicationStore();
|
|
43
|
+
const baseStore = useLegendQueryBaseStore();
|
|
44
|
+
const params = useParams<IngestQueryCreatorPathParams>();
|
|
45
|
+
|
|
46
|
+
const gav = guaranteeNonNullable(
|
|
47
|
+
params[INGEST_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.GAV],
|
|
48
|
+
);
|
|
49
|
+
const ingestDefinitionPath = guaranteeNonNullable(
|
|
50
|
+
params[INGEST_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.INGEST_DEFINITION_PATH],
|
|
51
|
+
);
|
|
52
|
+
const dataSet = guaranteeNonNullable(
|
|
53
|
+
params[INGEST_QUERY_CREATOR_ROUTE_PATTERN_TOKEN.DATA_SET],
|
|
54
|
+
);
|
|
55
|
+
const { groupId, artifactId, versionId } = parseGAVCoordinates(gav);
|
|
56
|
+
|
|
57
|
+
const store = useLocalObservable(
|
|
58
|
+
() =>
|
|
59
|
+
new IngestQueryCreatorStore(
|
|
60
|
+
applicationStore,
|
|
61
|
+
baseStore.depotServerClient,
|
|
62
|
+
groupId,
|
|
63
|
+
artifactId,
|
|
64
|
+
versionId,
|
|
65
|
+
ingestDefinitionPath,
|
|
66
|
+
dataSet,
|
|
67
|
+
),
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<QueryEditorStoreContext.Provider value={store}>
|
|
72
|
+
<QueryEditor />
|
|
73
|
+
</QueryEditorStoreContext.Provider>
|
|
74
|
+
);
|
|
75
|
+
});
|
|
@@ -0,0 +1,148 @@
|
|
|
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
|
+
CogIcon,
|
|
20
|
+
Dialog,
|
|
21
|
+
Modal,
|
|
22
|
+
ModalBody,
|
|
23
|
+
ModalFooter,
|
|
24
|
+
ModalFooterButton,
|
|
25
|
+
ModalHeader,
|
|
26
|
+
ModalHeaderActions,
|
|
27
|
+
ModalTitle,
|
|
28
|
+
TimesIcon,
|
|
29
|
+
} from '@finos/legend-art';
|
|
30
|
+
import type { LakehouseRuntime } from '@finos/legend-graph';
|
|
31
|
+
import { observer } from 'mobx-react-lite';
|
|
32
|
+
import { useEffect, useState } from 'react';
|
|
33
|
+
import { LegendQueryUserDataHelper } from '../../__lib__/LegendQueryUserDataHelper.js';
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Modal for editing a {@link LakehouseRuntime}'s environment and warehouse.
|
|
37
|
+
* Reused by both the data-product and ingest query builder setup panels.
|
|
38
|
+
*/
|
|
39
|
+
export const LakehouseRuntimeConfigModal = observer(
|
|
40
|
+
(props: {
|
|
41
|
+
lakehouseRuntime: LakehouseRuntime | undefined;
|
|
42
|
+
open: boolean;
|
|
43
|
+
onClose: () => void;
|
|
44
|
+
darkMode: boolean;
|
|
45
|
+
}) => {
|
|
46
|
+
const { lakehouseRuntime, open, onClose, darkMode } = props;
|
|
47
|
+
const [env, setEnv] = useState(lakehouseRuntime?.environment ?? '');
|
|
48
|
+
const [warehouse, setWarehouse] = useState(
|
|
49
|
+
lakehouseRuntime?.warehouse ?? '',
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
// sync local state when the modal opens or the runtime changes
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (open && lakehouseRuntime) {
|
|
55
|
+
setEnv(lakehouseRuntime.environment ?? '');
|
|
56
|
+
setWarehouse(lakehouseRuntime.warehouse ?? '');
|
|
57
|
+
}
|
|
58
|
+
}, [open, lakehouseRuntime]);
|
|
59
|
+
|
|
60
|
+
const applicationStore = useApplicationStore();
|
|
61
|
+
|
|
62
|
+
const handleApply = (): void => {
|
|
63
|
+
if (lakehouseRuntime) {
|
|
64
|
+
const newEnv = env || undefined;
|
|
65
|
+
const newWarehouse = warehouse || undefined;
|
|
66
|
+
const hasChanged =
|
|
67
|
+
newEnv !== lakehouseRuntime.environment ||
|
|
68
|
+
newWarehouse !== lakehouseRuntime.warehouse;
|
|
69
|
+
lakehouseRuntime.environment = newEnv;
|
|
70
|
+
lakehouseRuntime.warehouse = newWarehouse;
|
|
71
|
+
if (hasChanged) {
|
|
72
|
+
LegendQueryUserDataHelper.persistLakehouseUserInfo(
|
|
73
|
+
applicationStore.userDataService,
|
|
74
|
+
{
|
|
75
|
+
env: newEnv,
|
|
76
|
+
snowflakeWarehouse: newWarehouse,
|
|
77
|
+
},
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
onClose();
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
if (!lakehouseRuntime) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<Dialog onClose={onClose} open={open}>
|
|
90
|
+
<Modal darkMode={darkMode}>
|
|
91
|
+
<ModalHeader>
|
|
92
|
+
<ModalTitle
|
|
93
|
+
icon={<CogIcon />}
|
|
94
|
+
title="Lakehouse Runtime Configuration"
|
|
95
|
+
/>
|
|
96
|
+
<ModalHeaderActions>
|
|
97
|
+
<button
|
|
98
|
+
className="modal__header__action"
|
|
99
|
+
tabIndex={-1}
|
|
100
|
+
onClick={onClose}
|
|
101
|
+
>
|
|
102
|
+
<TimesIcon />
|
|
103
|
+
</button>
|
|
104
|
+
</ModalHeaderActions>
|
|
105
|
+
</ModalHeader>
|
|
106
|
+
<ModalBody>
|
|
107
|
+
<div className="panel__content__form__section">
|
|
108
|
+
<div className="panel__content__form__section__header__label">
|
|
109
|
+
Environment
|
|
110
|
+
</div>
|
|
111
|
+
<input
|
|
112
|
+
className="panel__content__form__section__input input--dark input--small"
|
|
113
|
+
spellCheck={false}
|
|
114
|
+
value={env}
|
|
115
|
+
placeholder="(optional)"
|
|
116
|
+
onChange={(e) => setEnv(e.target.value)}
|
|
117
|
+
/>
|
|
118
|
+
</div>
|
|
119
|
+
<div className="panel__content__form__section">
|
|
120
|
+
<div className="panel__content__form__section__header__label">
|
|
121
|
+
Warehouse
|
|
122
|
+
</div>
|
|
123
|
+
<input
|
|
124
|
+
className="panel__content__form__section__input input--dark input--small"
|
|
125
|
+
spellCheck={false}
|
|
126
|
+
value={warehouse}
|
|
127
|
+
placeholder="(optional)"
|
|
128
|
+
onChange={(e) => setWarehouse(e.target.value)}
|
|
129
|
+
/>
|
|
130
|
+
</div>
|
|
131
|
+
</ModalBody>
|
|
132
|
+
<ModalFooter>
|
|
133
|
+
<ModalFooterButton
|
|
134
|
+
text="Apply"
|
|
135
|
+
onClick={handleApply}
|
|
136
|
+
type="primary"
|
|
137
|
+
/>
|
|
138
|
+
<ModalFooterButton
|
|
139
|
+
text="Cancel"
|
|
140
|
+
onClick={onClose}
|
|
141
|
+
type="secondary"
|
|
142
|
+
/>
|
|
143
|
+
</ModalFooter>
|
|
144
|
+
</Modal>
|
|
145
|
+
</Dialog>
|
|
146
|
+
);
|
|
147
|
+
},
|
|
148
|
+
);
|