@finos/legend-query-builder 4.11.4 → 4.11.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. package/lib/components/QueryBuilder.js +1 -1
  2. package/lib/components/QueryBuilder.js.map +1 -1
  3. package/lib/components/execution-plan/SQLExecutionNodeViewer.js +1 -1
  4. package/lib/components/execution-plan/SQLExecutionNodeViewer.js.map +1 -1
  5. package/lib/components/{QueryBuilderResultPanel.d.ts → result/QueryBuilderResultPanel.d.ts} +1 -2
  6. package/lib/components/result/QueryBuilderResultPanel.d.ts.map +1 -0
  7. package/lib/components/result/QueryBuilderResultPanel.js +185 -0
  8. package/lib/components/result/QueryBuilderResultPanel.js.map +1 -0
  9. package/lib/components/result/tds/QueryBuilderTDSGridResult.d.ts +24 -0
  10. package/lib/components/result/tds/QueryBuilderTDSGridResult.d.ts.map +1 -0
  11. package/lib/components/result/tds/QueryBuilderTDSGridResult.js +140 -0
  12. package/lib/components/result/tds/QueryBuilderTDSGridResult.js.map +1 -0
  13. package/lib/components/result/tds/QueryBuilderTDSResultShared.d.ts +41 -0
  14. package/lib/components/result/tds/QueryBuilderTDSResultShared.d.ts.map +1 -0
  15. package/lib/components/result/tds/QueryBuilderTDSResultShared.js +465 -0
  16. package/lib/components/result/tds/QueryBuilderTDSResultShared.js.map +1 -0
  17. package/lib/components/result/tds/QueryBuilderTDSSimpleGridResult.d.ts +24 -0
  18. package/lib/components/result/tds/QueryBuilderTDSSimpleGridResult.d.ts.map +1 -0
  19. package/lib/components/result/tds/QueryBuilderTDSSimpleGridResult.js +47 -0
  20. package/lib/components/result/tds/QueryBuilderTDSSimpleGridResult.js.map +1 -0
  21. package/lib/index.css +2 -2
  22. package/lib/index.css.map +1 -1
  23. package/lib/package.json +1 -1
  24. package/lib/stores/QueryBuilderResultState.d.ts +5 -1
  25. package/lib/stores/QueryBuilderResultState.d.ts.map +1 -1
  26. package/lib/stores/QueryBuilderResultState.js.map +1 -1
  27. package/package.json +2 -2
  28. package/src/components/QueryBuilder.tsx +1 -1
  29. package/src/components/execution-plan/SQLExecutionNodeViewer.tsx +1 -1
  30. package/src/components/result/QueryBuilderResultPanel.tsx +570 -0
  31. package/src/components/result/tds/QueryBuilderTDSGridResult.tsx +239 -0
  32. package/src/components/result/tds/QueryBuilderTDSResultShared.tsx +866 -0
  33. package/src/components/result/tds/QueryBuilderTDSSimpleGridResult.tsx +80 -0
  34. package/src/stores/QueryBuilderResultState.ts +12 -1
  35. package/tsconfig.json +4 -1
  36. package/lib/components/QueryBuilderResultPanel.d.ts.map +0 -1
  37. package/lib/components/QueryBuilderResultPanel.js +0 -633
  38. package/lib/components/QueryBuilderResultPanel.js.map +0 -1
  39. package/src/components/QueryBuilderResultPanel.tsx +0 -1412
@@ -1,633 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- /**
3
- * Copyright (c) 2020-present, Goldman Sachs
4
- *
5
- * Licensed under the Apache License, Version 2.0 (the "License");
6
- * you may not use this file except in compliance with the License.
7
- * You may obtain a copy of the License at
8
- *
9
- * http://www.apache.org/licenses/LICENSE-2.0
10
- *
11
- * Unless required by applicable law or agreed to in writing, software
12
- * distributed under the License is distributed on an "AS IS" BASIS,
13
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- * See the License for the specific language governing permissions and
15
- * limitations under the License.
16
- */
17
- import { BlankPanelContent, PanelLoadingIndicator, PlayIcon, DropdownMenu, MenuContent, MenuContentItem, CaretDownIcon, ContextMenu, clsx, PauseCircleIcon, ExclamationTriangleIcon, PanelContent, MenuContentDivider, Button, SQLIcon, Dialog, Modal, ModalBody, ModalFooter, ModalFooterButton, ModalHeader, PanelDivider, SquareIcon, CheckSquareIcon, } from '@finos/legend-art';
18
- import { format as formatSQL } from 'sql-formatter';
19
- import { observer } from 'mobx-react-lite';
20
- import { flowResult } from 'mobx';
21
- import { InstanceValue, extractExecutionResultValues, TDSExecutionResult, RawExecutionResult, EnumValueInstanceValue, EnumValueExplicitReference, RelationalExecutionActivities, getTDSRowRankByColumnInAsc, PRIMITIVE_TYPE, } from '@finos/legend-graph';
22
- import { ActionAlertActionType, ActionAlertType, DEFAULT_TAB_SIZE, useApplicationStore, } from '@finos/legend-application';
23
- import { assertErrorThrown, guaranteeNonNullable, isBoolean, prettyDuration, filterByType, isValidURL, isString, isNumber, } from '@finos/legend-shared';
24
- import { forwardRef, useRef, useState } from 'react';
25
- import { QueryBuilderDerivationProjectionColumnState, QueryBuilderProjectionColumnState, } from '../stores/fetch-structure/tds/projection/QueryBuilderProjectionColumnState.js';
26
- import { PostFilterConditionState, QueryBuilderPostFilterTreeConditionNodeData, PostFilterValueSpecConditionValueState, } from '../stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.js';
27
- import { QueryBuilderPostFilterOperator_Equal, QueryBuilderPostFilterOperator_NotEqual, } from '../stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_Equal.js';
28
- import { QueryBuilderPostFilterOperator_In, QueryBuilderPostFilterOperator_NotIn, } from '../stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_In.js';
29
- import { QueryBuilderTDSState } from '../stores/fetch-structure/tds/QueryBuilderTDSState.js';
30
- import { instanceValue_setValue, instanceValue_setValues, } from '../stores/shared/ValueSpecificationModifierHelper.js';
31
- import { PARAMETER_SUBMIT_ACTION } from '../stores/shared/LambdaParameterState.js';
32
- import { QUERY_BUILDER_TEST_ID } from '../__lib__/QueryBuilderTesting.js';
33
- import { DataGrid, } from '@finos/legend-lego/data-grid';
34
- import { CODE_EDITOR_LANGUAGE, CodeEditor, } from '@finos/legend-lego/code-editor';
35
- import { ExecutionPlanViewer } from './execution-plan/ExecutionPlanViewer.js';
36
- import { QueryBuilderPostFilterOperator_IsEmpty, QueryBuilderPostFilterOperator_IsNotEmpty, } from '../stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_IsEmpty.js';
37
- import { QueryUsageViewer } from './QueryUsageViewer.js';
38
- import { DEFAULT_LOCALE } from '../graph-manager/QueryBuilderConst.js';
39
- import { DocumentationLink } from '@finos/legend-lego/application';
40
- import { QUERY_BUILDER_DOCUMENTATION_KEY } from '../__lib__/QueryBuilderDocumentation.js';
41
- export const tryToFormatSql = (sql) => {
42
- try {
43
- const formattedSql = formatSQL(sql, { language: 'mysql' });
44
- return formattedSql;
45
- }
46
- catch {
47
- try {
48
- const formattedSql = formatSQL(sql);
49
- return formattedSql;
50
- }
51
- catch {
52
- return sql;
53
- }
54
- }
55
- };
56
- const QueryBuilderGridResultContextMenu = observer(forwardRef(function QueryBuilderResultContextMenu(props, ref) {
57
- const { data, tdsState } = props;
58
- const applicationStore = useApplicationStore();
59
- const postFilterEqualOperator = new QueryBuilderPostFilterOperator_Equal();
60
- const postFilterInOperator = new QueryBuilderPostFilterOperator_In();
61
- const postFilterEmptyOperator = new QueryBuilderPostFilterOperator_IsEmpty();
62
- const postFilterNotEmptyOperator = new QueryBuilderPostFilterOperator_IsNotEmpty();
63
- const postFilterNotEqualOperator = new QueryBuilderPostFilterOperator_NotEqual();
64
- const postFilterNotInOperator = new QueryBuilderPostFilterOperator_NotIn();
65
- const postFilterState = tdsState.postFilterState;
66
- const projectionColumnState = tdsState.projectionColumns
67
- .filter((c) => c.columnName === data?.columnName)
68
- .concat(tdsState.aggregationState.columns
69
- .filter((c) => c.columnName === data?.columnName)
70
- .map((ag) => ag.projectionColumnState))[0];
71
- const getExistingPostFilterNode = (operators, projectionColumnName) => Array.from(postFilterState.nodes.values())
72
- .filter((v) => v instanceof QueryBuilderPostFilterTreeConditionNodeData &&
73
- v.condition.leftConditionValue instanceof
74
- QueryBuilderProjectionColumnState)
75
- .filter((n) => n.condition
76
- .leftConditionValue.columnName ===
77
- (projectionColumnName ?? projectionColumnState?.columnName) &&
78
- operators
79
- .map((op) => op.getLabel())
80
- .includes(n.condition.operator.getLabel()))[0];
81
- const updateFilterConditionValue = (conditionValue, cellData) => {
82
- if (cellData.value) {
83
- instanceValue_setValue(conditionValue, conditionValue instanceof EnumValueInstanceValue
84
- ? EnumValueExplicitReference.create(guaranteeNonNullable((conditionValue.genericType?.ownerReference
85
- .value).values.filter((v) => v.name === cellData.value)[0]))
86
- : cellData.value, 0, tdsState.queryBuilderState.observerContext);
87
- }
88
- };
89
- const generateNewPostFilterConditionNodeData = async (operator, cellData) => {
90
- let postFilterConditionState;
91
- try {
92
- const possibleProjectionColumnState = cellData.columnName
93
- ? tdsState.projectionColumns
94
- .filter((c) => c.columnName === cellData.columnName)
95
- .concat(tdsState.aggregationState.columns
96
- .filter((c) => c.columnName === cellData.columnName)
97
- .map((ag) => ag.projectionColumnState))[0]
98
- : projectionColumnState;
99
- if (possibleProjectionColumnState) {
100
- postFilterConditionState = new PostFilterConditionState(postFilterState, possibleProjectionColumnState, operator);
101
- if (projectionColumnState instanceof
102
- QueryBuilderDerivationProjectionColumnState) {
103
- await flowResult(projectionColumnState.fetchDerivationLambdaReturnType());
104
- }
105
- const defaultFilterConditionValue = postFilterConditionState.operator.getDefaultFilterConditionValue(postFilterConditionState);
106
- postFilterConditionState.buildFromValueSpec(defaultFilterConditionValue);
107
- updateFilterConditionValue(defaultFilterConditionValue, cellData);
108
- postFilterState.addNodeFromNode(new QueryBuilderPostFilterTreeConditionNodeData(undefined, postFilterConditionState), undefined);
109
- }
110
- }
111
- catch (error) {
112
- assertErrorThrown(error);
113
- applicationStore.notificationService.notifyWarning(error.message);
114
- return;
115
- }
116
- };
117
- const updateExistingPostFilterConditionNodeData = (existingPostFilterNode, isFilterBy, cellData, operator) => {
118
- if (operator === postFilterEmptyOperator ||
119
- operator === postFilterNotEmptyOperator) {
120
- const conditionState = existingPostFilterNode.condition;
121
- if (conditionState.operator.getLabel() !== operator.getLabel()) {
122
- conditionState.changeOperator(isFilterBy ? postFilterEmptyOperator : postFilterNotEmptyOperator);
123
- }
124
- return;
125
- }
126
- const conditionState = existingPostFilterNode.condition;
127
- const rightSide = conditionState.rightConditionValue;
128
- if (rightSide instanceof PostFilterValueSpecConditionValueState) {
129
- if (conditionState.operator.getLabel() === operator.getLabel()) {
130
- const doesValueAlreadyExist = rightSide.value instanceof InstanceValue &&
131
- (rightSide.value instanceof EnumValueInstanceValue
132
- ? rightSide.value.values.map((ef) => ef.value.name)
133
- : rightSide.value.values).includes(cellData.value);
134
- if (!doesValueAlreadyExist) {
135
- const currentValueSpecificaton = rightSide.value;
136
- const newValueSpecification = conditionState.operator.getDefaultFilterConditionValue(conditionState);
137
- updateFilterConditionValue(newValueSpecification, cellData);
138
- conditionState.changeOperator(isFilterBy ? postFilterInOperator : postFilterNotInOperator);
139
- instanceValue_setValues(rightSide.value, [currentValueSpecificaton, newValueSpecification], tdsState.queryBuilderState.observerContext);
140
- }
141
- }
142
- else {
143
- const doesValueAlreadyExist = rightSide.value instanceof InstanceValue &&
144
- rightSide.value.values
145
- .filter((v) => v instanceof InstanceValue)
146
- .map((v) => v instanceof EnumValueInstanceValue
147
- ? v.values.map((ef) => ef.value.name)
148
- : v.values)
149
- .flat()
150
- .includes(cellData.value ?? data?.value);
151
- if (!doesValueAlreadyExist) {
152
- const newValueSpecification = (isFilterBy ? postFilterEqualOperator : postFilterNotEqualOperator).getDefaultFilterConditionValue(conditionState);
153
- updateFilterConditionValue(newValueSpecification, cellData);
154
- instanceValue_setValues(rightSide.value, [
155
- ...rightSide.value.values,
156
- newValueSpecification,
157
- ], tdsState.queryBuilderState.observerContext);
158
- }
159
- }
160
- }
161
- };
162
- const getFilterOperator = (isFilterBy, cellData) => {
163
- if (isFilterBy === true) {
164
- if (cellData.value === null) {
165
- return postFilterEmptyOperator;
166
- }
167
- else {
168
- return postFilterEqualOperator;
169
- }
170
- }
171
- else {
172
- if (cellData.value === null) {
173
- return postFilterNotEmptyOperator;
174
- }
175
- else {
176
- return postFilterNotEqualOperator;
177
- }
178
- }
179
- };
180
- const filterByOrOutValue = (isFilterBy, cellData) => {
181
- tdsState.setShowPostFilterPanel(true);
182
- const operator = getFilterOperator(isFilterBy, cellData);
183
- const existingPostFilterNode = getExistingPostFilterNode(cellData.value === null
184
- ? [postFilterEmptyOperator, postFilterNotEmptyOperator]
185
- : isFilterBy
186
- ? [postFilterEqualOperator, postFilterInOperator]
187
- : [postFilterNotEqualOperator, postFilterNotInOperator], cellData.columnName);
188
- existingPostFilterNode === undefined
189
- ? generateNewPostFilterConditionNodeData(operator, cellData).catch(applicationStore.alertUnhandledError)
190
- : updateExistingPostFilterConditionNodeData(existingPostFilterNode, isFilterBy, cellData, operator);
191
- };
192
- const filterByOrOutValues = (isFilterBy) => {
193
- tdsState.queryBuilderState.resultState.selectedCells.forEach((cellData) => {
194
- filterByOrOutValue(isFilterBy, cellData);
195
- });
196
- };
197
- const handleCopyCellValue = applicationStore.guardUnhandledError(() => applicationStore.clipboardService.copyTextToClipboard(data?.value?.toString() ?? ''));
198
- const findRowFromRowIndex = (rowIndex) => {
199
- if (!tdsState.queryBuilderState.resultState.executionResult ||
200
- !(tdsState.queryBuilderState.resultState.executionResult instanceof
201
- TDSExecutionResult)) {
202
- return [''];
203
- }
204
- return (tdsState.queryBuilderState.resultState.executionResult.result.rows[rowIndex]?.values ?? ['']);
205
- };
206
- const handleCopyRowValue = applicationStore.guardUnhandledError(() => applicationStore.clipboardService.copyTextToClipboard(findRowFromRowIndex(tdsState.queryBuilderState.resultState.selectedCells[0]?.coordinates
207
- .rowIndex ?? 0).toString()));
208
- return (_jsxs(MenuContent, { ref: ref, children: [_jsx(MenuContentItem, { disabled: !projectionColumnState, onClick: () => {
209
- filterByOrOutValues(true);
210
- }, children: "Filter By" }), _jsx(MenuContentItem, { disabled: !projectionColumnState, onClick: () => {
211
- filterByOrOutValues(false);
212
- }, children: "Filter Out" }), _jsx(MenuContentDivider, {}), _jsx(MenuContentItem, { onClick: handleCopyCellValue, children: "Copy Cell Value" }), _jsx(MenuContentItem, { onClick: handleCopyRowValue, children: "Copy Row Value" })] }));
213
- }));
214
- const QueryResultCellRenderer = observer((params) => {
215
- const resultState = params.resultState;
216
- const tdsExecutionResult = params.tdsExecutionResult;
217
- const fetchStructureImplementation = resultState.queryBuilderState.fetchStructureState.implementation;
218
- const cellValue = params.value;
219
- const formattedCellValue = () => {
220
- if (isNumber(cellValue)) {
221
- return Intl.NumberFormat(DEFAULT_LOCALE, {
222
- maximumFractionDigits: 4,
223
- }).format(Number(cellValue));
224
- }
225
- return cellValue;
226
- };
227
- const cellValueUrlLink = isString(cellValue) && isValidURL(cellValue) ? cellValue : undefined;
228
- const columnName = params.column?.getColId() ?? '';
229
- const findCoordinatesFromResultValue = (colId, rowNumber) => {
230
- const colIndex = tdsExecutionResult.result.columns.findIndex((col) => col === colId);
231
- return { rowIndex: rowNumber, colIndex: colIndex };
232
- };
233
- const currentCellCoordinates = findCoordinatesFromResultValue(columnName, params.rowIndex);
234
- const cellInFilteredResults = resultState.selectedCells.some((result) => result.coordinates.colIndex === currentCellCoordinates.colIndex &&
235
- result.coordinates.rowIndex === currentCellCoordinates.rowIndex);
236
- const findColumnFromCoordinates = (colIndex) => {
237
- if (!resultState.executionResult ||
238
- !(resultState.executionResult instanceof TDSExecutionResult)) {
239
- return undefined;
240
- }
241
- return resultState.executionResult.result.columns[colIndex];
242
- };
243
- const findResultValueFromCoordinates = (resultCoordinate) => {
244
- const rowIndex = resultCoordinate[0];
245
- const colIndex = resultCoordinate[1];
246
- if (!resultState.executionResult ||
247
- !(resultState.executionResult instanceof TDSExecutionResult)) {
248
- return undefined;
249
- }
250
- if (params.columnApi.getColumnState()[colIndex]?.sort === 'asc') {
251
- resultState.executionResult.result.rows.sort((a, b) => getTDSRowRankByColumnInAsc(a, b, colIndex));
252
- }
253
- else if (params.columnApi.getColumnState()[colIndex]?.sort === 'desc') {
254
- resultState.executionResult.result.rows.sort((a, b) => getTDSRowRankByColumnInAsc(b, a, colIndex));
255
- }
256
- return resultState.executionResult.result.rows[rowIndex]?.values[colIndex];
257
- };
258
- const isCoordinatesSelected = (resultCoordinate) => resultState.selectedCells.some((cell) => cell.coordinates.rowIndex === resultCoordinate.rowIndex &&
259
- cell.coordinates.colIndex === resultCoordinate.colIndex);
260
- const mouseDown = (event) => {
261
- event.preventDefault();
262
- if (event.shiftKey) {
263
- const coordinates = findCoordinatesFromResultValue(columnName, params.rowIndex);
264
- const actualValue = findResultValueFromCoordinates([
265
- coordinates.rowIndex,
266
- coordinates.colIndex,
267
- ]);
268
- resultState.addSelectedCell({
269
- value: actualValue,
270
- columnName: columnName,
271
- coordinates: coordinates,
272
- });
273
- return;
274
- }
275
- if (event.button === 0) {
276
- resultState.setIsSelectingCells(true);
277
- resultState.setSelectedCells([]);
278
- const coordinates = findCoordinatesFromResultValue(columnName, params.rowIndex);
279
- const actualValue = findResultValueFromCoordinates([
280
- coordinates.rowIndex,
281
- coordinates.colIndex,
282
- ]);
283
- resultState.setSelectedCells([
284
- {
285
- value: actualValue,
286
- columnName: columnName,
287
- coordinates: coordinates,
288
- },
289
- ]);
290
- resultState.setMouseOverCell(resultState.selectedCells[0] ?? null);
291
- }
292
- if (event.button === 2) {
293
- const coordinates = findCoordinatesFromResultValue(columnName, params.rowIndex);
294
- const isInSelected = isCoordinatesSelected(coordinates);
295
- if (!isInSelected) {
296
- const actualValue = findResultValueFromCoordinates([
297
- coordinates.rowIndex,
298
- coordinates.colIndex,
299
- ]);
300
- resultState.setSelectedCells([
301
- {
302
- value: actualValue,
303
- columnName: columnName,
304
- coordinates: coordinates,
305
- },
306
- ]);
307
- resultState.setMouseOverCell(resultState.selectedCells[0] ?? null);
308
- }
309
- }
310
- };
311
- const mouseUp = (event) => {
312
- resultState.setIsSelectingCells(false);
313
- };
314
- const mouseOver = (event) => {
315
- if (resultState.isSelectingCells) {
316
- if (resultState.selectedCells.length < 1) {
317
- return;
318
- }
319
- const results = resultState.selectedCells[0];
320
- if (!results) {
321
- return;
322
- }
323
- const firstCorner = results.coordinates;
324
- const secondCorner = findCoordinatesFromResultValue(columnName, params.rowIndex);
325
- resultState.setSelectedCells([results]);
326
- const minRow = Math.min(firstCorner.rowIndex, secondCorner.rowIndex);
327
- const minCol = Math.min(firstCorner.colIndex, secondCorner.colIndex);
328
- const maxRow = Math.max(firstCorner.rowIndex, secondCorner.rowIndex);
329
- const maxCol = Math.max(firstCorner.colIndex, secondCorner.colIndex);
330
- for (let x = minRow; x <= maxRow; x++) {
331
- for (let y = minCol; y <= maxCol; y++) {
332
- const actualValue = findResultValueFromCoordinates([x, y]);
333
- const valueAndColumnId = {
334
- value: actualValue,
335
- columnName: findColumnFromCoordinates(y),
336
- coordinates: {
337
- rowIndex: x,
338
- colIndex: y,
339
- },
340
- };
341
- if (!resultState.selectedCells.find((result) => result.coordinates.colIndex === y &&
342
- result.coordinates.rowIndex === x)) {
343
- resultState.addSelectedCell(valueAndColumnId);
344
- }
345
- }
346
- }
347
- }
348
- resultState.setMouseOverCell(resultState.selectedCells[0] ?? null);
349
- };
350
- return (_jsx(ContextMenu, { content:
351
- // NOTE: we only support this functionality for grid result with a projection fetch-structure
352
- fetchStructureImplementation instanceof QueryBuilderTDSState ? (_jsx(QueryBuilderGridResultContextMenu, { data: resultState.mousedOverCell, tdsState: fetchStructureImplementation })) : null, disabled: !(resultState.queryBuilderState.fetchStructureState
353
- .implementation instanceof QueryBuilderTDSState) ||
354
- !resultState.queryBuilderState.isQuerySupported ||
355
- !resultState.mousedOverCell, menuProps: { elevation: 7 }, className: clsx('ag-theme-balham-dark query-builder__result__tds-grid'), children: _jsx("div", { className: clsx('query-builder__result__values__table__cell', {
356
- 'query-builder__result__values__table__cell--active': cellInFilteredResults,
357
- }), onMouseDown: (event) => mouseDown(event), onMouseUp: (event) => mouseUp(event), onMouseOver: (event) => mouseOver(event), children: cellValueUrlLink ? (_jsx("a", { href: cellValueUrlLink, target: "_blank", rel: "noreferrer", children: cellValueUrlLink })) : (_jsx("span", { children: formattedCellValue() })) }) }));
358
- });
359
- const getColumnCustomizations = (result, columnName) => {
360
- const columnType = result.builder.columns.find((col) => col.name === columnName)?.type;
361
- switch (columnType) {
362
- case PRIMITIVE_TYPE.STRING:
363
- return {
364
- filter: 'agTextColumnFilter',
365
- allowedAggFuncs: ['count'],
366
- };
367
- case PRIMITIVE_TYPE.DATE:
368
- case PRIMITIVE_TYPE.DATETIME:
369
- case PRIMITIVE_TYPE.STRICTDATE:
370
- return {
371
- filter: 'agDateColumnFilter',
372
- allowedAggFuncs: ['count'],
373
- };
374
- case PRIMITIVE_TYPE.DECIMAL:
375
- case PRIMITIVE_TYPE.INTEGER:
376
- case PRIMITIVE_TYPE.FLOAT:
377
- return {
378
- filter: 'agNumberColumnFilter',
379
- allowedAggFuncs: ['count', 'sum', 'max', 'min', 'avg'],
380
- };
381
- default:
382
- return {
383
- allowedAggFuncs: ['count'],
384
- };
385
- }
386
- };
387
- const QueryBuilderGridResult = observer((props) => {
388
- const { executionResult, queryBuilderState } = props;
389
- const [columnAPi, setColumnApi] = useState(undefined);
390
- const resultState = queryBuilderState.resultState;
391
- const isAdvancedModeEnabled = queryBuilderState.isAdvancedModeEnabled;
392
- const colDefs = isAdvancedModeEnabled
393
- ? executionResult.result.columns.map((colName) => {
394
- const col = {
395
- minWidth: 50,
396
- sortable: true,
397
- resizable: true,
398
- field: colName,
399
- flex: 1,
400
- enablePivot: true,
401
- enableRowGroup: true,
402
- enableValue: true,
403
- ...getColumnCustomizations(executionResult, colName),
404
- };
405
- const persistedColumn = resultState.gridConfig.columns.find((c) => c.colId === colName);
406
- if (persistedColumn) {
407
- if (persistedColumn.width) {
408
- col.width = persistedColumn.width;
409
- }
410
- col.pinned = persistedColumn.pinned ?? null;
411
- col.rowGroup = persistedColumn.rowGroup ?? false;
412
- col.rowGroupIndex = persistedColumn.rowGroupIndex ?? null;
413
- col.aggFunc = persistedColumn.aggFunc ?? null;
414
- col.pivot = persistedColumn.pivot ?? false;
415
- col.hide = persistedColumn.hide ?? false;
416
- }
417
- return col;
418
- })
419
- : executionResult.result.columns.map((colName) => ({
420
- minWidth: 50,
421
- sortable: true,
422
- resizable: true,
423
- field: colName,
424
- flex: 1,
425
- cellRenderer: QueryResultCellRenderer,
426
- cellRendererParams: {
427
- resultState: resultState,
428
- tdsExecutionResult: executionResult,
429
- },
430
- }));
431
- const sideBar = isAdvancedModeEnabled ? ['columns', 'filters'] : null;
432
- const rowData = executionResult.result.rows.map((_row, rowIdx) => {
433
- const row = {};
434
- const cols = executionResult.result.columns;
435
- _row.values.forEach((value, colIdx) => {
436
- // `ag-grid` shows `false` value as empty string so we have
437
- // call `.toString()` to avoid this behavior.
438
- // See https://github.com/finos/legend-studio/issues/1008
439
- row[cols[colIdx]] = isBoolean(value) ? String(value) : value;
440
- });
441
- row.rowNumber = rowIdx;
442
- return row;
443
- });
444
- const onSaveGridColumnState = () => {
445
- if (!columnAPi) {
446
- return;
447
- }
448
- resultState.setGridConfig({
449
- columns: columnAPi.getColumnState(),
450
- isPivotModeEnabled: columnAPi.isPivotMode(),
451
- });
452
- };
453
- return (_jsx("div", { className: "query-builder__result__values__table", children: _jsx("div", { className: clsx('ag-theme-balham-dark query-builder__result__tds-grid'), children: isAdvancedModeEnabled ? (_jsx(DataGrid, { rowData: rowData, onGridReady: (params) => {
454
- setColumnApi(params.columnApi);
455
- params.columnApi.setPivotMode(resultState.gridConfig.isPivotModeEnabled);
456
- }, gridOptions: {
457
- suppressScrollOnNewData: true,
458
- getRowId: (data) => data.data.rowNumber,
459
- rowSelection: 'multiple',
460
- pivotPanelShow: 'always',
461
- rowGroupPanelShow: 'always',
462
- },
463
- // NOTE: when column definition changed, we need to force refresh the cell to make sure the cell renderer is updated
464
- // See https://stackoverflow.com/questions/56341073/how-to-refresh-an-ag-grid-when-a-change-occurs-inside-a-custom-cell-renderer-com
465
- onRowDataUpdated: (params) => {
466
- params.api.refreshCells({ force: true });
467
- }, suppressFieldDotNotation: true, suppressContextMenu: !isAdvancedModeEnabled, columnDefs: colDefs, sideBar: sideBar, onColumnVisible: onSaveGridColumnState, onColumnPinned: onSaveGridColumnState, onColumnResized: onSaveGridColumnState, onColumnRowGroupChanged: onSaveGridColumnState, onColumnValueChanged: onSaveGridColumnState, onColumnPivotChanged: onSaveGridColumnState, onColumnPivotModeChanged: onSaveGridColumnState })) : (_jsx(DataGrid, { rowData: rowData, gridOptions: {
468
- suppressScrollOnNewData: true,
469
- getRowId: (data) => data.data.rowNumber,
470
- rowSelection: 'multiple',
471
- },
472
- // NOTE: when column definition changed, we need to force refresh the cell to make sure the cell renderer is updated
473
- // See https://stackoverflow.com/questions/56341073/how-to-refresh-an-ag-grid-when-a-change-occurs-inside-a-custom-cell-renderer-com
474
- onRowDataUpdated: (params) => {
475
- params.api.refreshCells({ force: true });
476
- }, suppressFieldDotNotation: true, suppressContextMenu: !isAdvancedModeEnabled, columnDefs: colDefs })) }) }));
477
- });
478
- const QueryBuilderResultValues = observer((props) => {
479
- const { executionResult, queryBuilderState } = props;
480
- if (executionResult instanceof TDSExecutionResult) {
481
- return (_jsx(QueryBuilderGridResult, { queryBuilderState: queryBuilderState, executionResult: executionResult }));
482
- }
483
- else if (executionResult instanceof RawExecutionResult) {
484
- const inputValue = executionResult.value === null
485
- ? 'null'
486
- : executionResult.value.toString();
487
- return (_jsx(CodeEditor, { language: CODE_EDITOR_LANGUAGE.TEXT, inputValue: inputValue, isReadOnly: true }));
488
- }
489
- return (_jsx(CodeEditor, { language: CODE_EDITOR_LANGUAGE.JSON, inputValue: JSON.stringify(extractExecutionResultValues(executionResult), null, DEFAULT_TAB_SIZE), isReadOnly: true }));
490
- });
491
- export const QueryBuilderResultPanel = observer((props) => {
492
- const { queryBuilderState } = props;
493
- const applicationStore = useApplicationStore();
494
- const resultState = queryBuilderState.resultState;
495
- const queryParametersState = queryBuilderState.parametersState;
496
- const executionResult = resultState.executionResult;
497
- const [showSqlModal, setShowSqlModal] = useState(false);
498
- const relationalActivities = executionResult?.activities;
499
- const executedSqls = relationalActivities
500
- ?.filter(filterByType(RelationalExecutionActivities))
501
- .map((relationalActivity) => relationalActivity.sql);
502
- let executedSql = '';
503
- if (executedSqls?.length && executedSqls.length > 1) {
504
- for (let i = 0; i < executedSqls.length; i++) {
505
- executedSql += `\n--QUERY #${i + 1}\n`;
506
- executedSql += `${executedSqls[i]}\n`;
507
- }
508
- }
509
- else {
510
- executedSql += executedSqls?.[0];
511
- }
512
- const fetchStructureImplementation = queryBuilderState.fetchStructureState.implementation;
513
- const USER_ATTESTATION_MESSAGE = 'I attest that I am aware of the sensitive data leakage risk when exporting queried data. The data I export will only be used by me.';
514
- const exportQueryResults = async (format) => {
515
- if (queryBuilderState.parametersState.parameterStates.length) {
516
- queryParametersState.parameterValuesEditorState.open(() => flowResult(resultState.exportData(format)).catch(applicationStore.alertUnhandledError), PARAMETER_SUBMIT_ACTION.EXPORT);
517
- }
518
- else {
519
- await flowResult(resultState.exportData(format)).catch(applicationStore.alertUnhandledError);
520
- }
521
- };
522
- const confirmExport = (format) => {
523
- applicationStore.alertService.setActionAlertInfo({
524
- message: USER_ATTESTATION_MESSAGE,
525
- type: ActionAlertType.CAUTION,
526
- actions: [
527
- {
528
- label: 'Accept',
529
- type: ActionAlertActionType.PROCEED_WITH_CAUTION,
530
- handler: applicationStore.guardUnhandledError(() => exportQueryResults(format)),
531
- },
532
- {
533
- label: 'Decline',
534
- type: ActionAlertActionType.PROCEED,
535
- default: true,
536
- },
537
- ],
538
- });
539
- };
540
- const allValidationIssues = queryBuilderState.allValidationIssues;
541
- const isSupportedQueryValid = allValidationIssues.length === 0;
542
- const isQueryValid = !queryBuilderState.isQuerySupported || isSupportedQueryValid;
543
- const isQueryValidForAdvancedMode = isQueryValid &&
544
- queryBuilderState.fetchStructureState.implementation instanceof
545
- QueryBuilderTDSState;
546
- const runQuery = () => {
547
- resultState.setSelectedCells([]);
548
- resultState.pressedRunQuery.inProgress();
549
- if (queryParametersState.parameterStates.length) {
550
- queryParametersState.parameterValuesEditorState.open(() => flowResult(resultState.runQuery()).catch(applicationStore.alertUnhandledError), PARAMETER_SUBMIT_ACTION.RUN);
551
- }
552
- else {
553
- flowResult(resultState.runQuery()).catch(applicationStore.alertUnhandledError);
554
- }
555
- resultState.pressedRunQuery.complete();
556
- };
557
- const cancelQuery = applicationStore.guardUnhandledError(() => flowResult(resultState.cancelQuery()));
558
- const generatePlan = applicationStore.guardUnhandledError(() => flowResult(resultState.generatePlan(false)));
559
- const debugPlanGeneration = applicationStore.guardUnhandledError(() => flowResult(resultState.generatePlan(true)));
560
- const allowSettingPreviewLimit = queryBuilderState.isQuerySupported;
561
- const allowSettingAdvancedMode = queryBuilderState.isQuerySupported;
562
- const copyExpression = (value) => {
563
- applicationStore.clipboardService
564
- .copyTextToClipboard(value)
565
- .then(() => applicationStore.notificationService.notifySuccess('SQL Query copied', undefined, 2500))
566
- .catch(applicationStore.alertUnhandledError);
567
- };
568
- const isRunQueryDisabled = !isQueryValid ||
569
- resultState.isGeneratingPlan ||
570
- resultState.pressedRunQuery.isInProgress;
571
- const getResultSetDescription = (_executionResult) => {
572
- const queryDuration = resultState.executionDuration
573
- ? prettyDuration(resultState.executionDuration, {
574
- ms: true,
575
- })
576
- : undefined;
577
- if (_executionResult instanceof TDSExecutionResult) {
578
- const rowLength = _executionResult.result.rows.length;
579
- return `${rowLength} row(s)${queryDuration ? ` in ${queryDuration}` : ''}`;
580
- }
581
- if (!queryDuration) {
582
- return undefined;
583
- }
584
- return `query ran in ${queryDuration}`;
585
- };
586
- const resultDescription = executionResult
587
- ? getResultSetDescription(executionResult)
588
- : undefined;
589
- const [previewLimitValue, setPreviewLimitValue] = useState(resultState.previewLimit);
590
- const changePreviewLimit = (event) => {
591
- setPreviewLimitValue(parseInt(event.target.value, 10));
592
- };
593
- const inputRef = useRef(null);
594
- const getPreviewLimit = () => {
595
- if (isNaN(previewLimitValue) || previewLimitValue === 0) {
596
- setPreviewLimitValue(1);
597
- queryBuilderState.resultState.setPreviewLimit(1);
598
- }
599
- else {
600
- queryBuilderState.resultState.setPreviewLimit(previewLimitValue);
601
- }
602
- };
603
- const onKeyDown = (event) => {
604
- if (event.code === 'Enter') {
605
- getPreviewLimit();
606
- inputRef.current?.focus();
607
- }
608
- else if (event.code === 'Escape') {
609
- inputRef.current?.select();
610
- }
611
- };
612
- const toggleIsAdvancedModeEnabled = () => {
613
- resultState.setExecutionResult(undefined);
614
- queryBuilderState.setIsAdvancedModeEnabled(!queryBuilderState.isAdvancedModeEnabled);
615
- };
616
- return (_jsxs("div", { "data-testid": QUERY_BUILDER_TEST_ID.QUERY_BUILDER_RESULT_PANEL, className: "panel query-builder__result", children: [showSqlModal && executedSqls && (_jsx(Dialog, { open: Boolean(showSqlModal), onClose: () => setShowSqlModal(false), children: _jsxs(Modal, { className: "editor-modal", darkMode: true, children: [_jsx(ModalHeader, { title: "Executed SQL Query" }), _jsx(ModalBody, { className: "query-builder__sql__modal", children: _jsxs(_Fragment, { children: [_jsx(CodeEditor, { inputValue: tryToFormatSql(executedSql), isReadOnly: true, language: CODE_EDITOR_LANGUAGE.SQL, hideMinimap: true }), _jsx(PanelDivider, {})] }) }), _jsxs(ModalFooter, { children: [_jsx(ModalFooterButton, { formatText: false, onClick: () => copyExpression(executedSql), text: "Copy SQL to Clipboard" }), _jsx(ModalFooterButton, { onClick: () => setShowSqlModal(false), text: "Close" })] })] }) })), _jsxs("div", { className: "panel__header", children: [_jsxs("div", { className: "panel__header__title", children: [_jsx("div", { className: "panel__header__title__label", children: "result" }), executedSqls && (_jsx(Button, { onClick: () => setShowSqlModal(true), title: "Executed SQL", className: "query-builder__result__sql__actions", children: _jsx(SQLIcon, {}) })), resultState.pressedRunQuery.isInProgress && (_jsx("div", { className: "panel__header__title__label__status", children: "Running Query..." })), _jsx("div", { "data-testid": QUERY_BUILDER_TEST_ID.QUERY_BUILDER_RESULT_ANALYTICS, className: "query-builder__result__analytics", children: resultDescription ?? '' }), executionResult && resultState.checkForStaleResults && (_jsxs("div", { className: "query-builder__result__stale-status", children: [_jsx("div", { className: "query-builder__result__stale-status__icon", children: _jsx(ExclamationTriangleIcon, {}) }), _jsx("div", { className: "query-builder__result__stale-status__label", children: "Preview data might be stale" })] }))] }), _jsxs("div", { className: "panel__header__actions query-builder__result__header__actions", children: [allowSettingAdvancedMode && (_jsxs("div", { className: "query-builder__result__advanced__mode", children: [_jsxs("div", { className: "query-builder__result__advanced__mode__label", children: ["Advanced Mode", _jsx(DocumentationLink, { title: "The grid in advanced mode performs all operations like grouping, sorting, filtering, etc after initial query execution locally withought reaching out to server. This limits the number of rows to smaller number so they can fit in memory", documentationKey: QUERY_BUILDER_DOCUMENTATION_KEY.QUESTION_HOW_TO_USE_ADVANCED_GRID_MODE })] }), _jsx("button", { className: clsx('query-builder__result__advanced__mode__toggler__btn', {
617
- 'query-builder__result__advanced__mode__toggler__btn--toggled': queryBuilderState.isAdvancedModeEnabled,
618
- }), disabled: !isQueryValidForAdvancedMode, onClick: toggleIsAdvancedModeEnabled, tabIndex: -1, children: queryBuilderState.isAdvancedModeEnabled ? (_jsx(CheckSquareIcon, {})) : (_jsx(SquareIcon, {})) })] })), allowSettingPreviewLimit && (_jsxs("div", { className: "query-builder__result__limit", children: [_jsx("div", { className: "query-builder__result__limit__label", children: "preview limit" }), _jsx("input", { ref: inputRef, className: "input--dark query-builder__result__limit__input", spellCheck: false, type: "number", value: previewLimitValue, onChange: changePreviewLimit, onBlur: getPreviewLimit, onKeyDown: onKeyDown, disabled: !isQueryValid })] })), _jsx("div", { className: "query-builder__result__execute-btn btn__dropdown-combo btn__dropdown-combo--primary", children: resultState.isRunningQuery ? (_jsx("button", { className: "btn__dropdown-combo__canceler", onClick: cancelQuery, tabIndex: -1, disabled: !isQueryValid, children: _jsxs("div", { className: "btn--dark btn--caution btn__dropdown-combo__canceler__label", children: [_jsx(PauseCircleIcon, { className: "btn__dropdown-combo__canceler__label__icon" }), _jsx("div", { className: "btn__dropdown-combo__canceler__label__title", children: "Stop" })] }) })) : (_jsxs(_Fragment, { children: [_jsxs("button", { className: "btn__dropdown-combo__label", onClick: runQuery, tabIndex: -1, title: allValidationIssues.length
619
- ? `Query is not valid:\n${allValidationIssues
620
- .map((issue) => `\u2022 ${issue}`)
621
- .join('\n')}`
622
- : undefined, disabled: isRunQueryDisabled, children: [_jsx(PlayIcon, { className: "btn__dropdown-combo__label__icon" }), _jsx("div", { className: "btn__dropdown-combo__label__title", children: "Run Query" })] }), _jsx(DropdownMenu, { className: "btn__dropdown-combo__dropdown-btn", disabled: isRunQueryDisabled, content: _jsxs(MenuContent, { children: [_jsx(MenuContentItem, { className: "btn__dropdown-combo__option", onClick: generatePlan, disabled: isRunQueryDisabled, children: "Generate Plan" }), _jsx(MenuContentItem, { className: "btn__dropdown-combo__option", onClick: debugPlanGeneration, disabled: isRunQueryDisabled, children: "Debug" })] }), menuProps: {
623
- anchorOrigin: { vertical: 'bottom', horizontal: 'right' },
624
- transformOrigin: { vertical: 'top', horizontal: 'right' },
625
- }, children: _jsx(CaretDownIcon, {}) })] })) }), _jsxs(DropdownMenu, { className: "query-builder__result__export__dropdown", title: "Export", disabled: !isQueryValid, content: _jsxs(MenuContent, { children: [Object.values(fetchStructureImplementation.exportDataFormatOptions).map((format) => (_jsx(MenuContentItem, { className: "query-builder__result__export__dropdown__menu__item", onClick: () => confirmExport(format), children: format }, format))), _jsx(MenuContentItem, { className: "query-builder__result__export__dropdown__menu__item", onClick: () => resultState.setIsQueryUsageViewerOpened(true), disabled: queryBuilderState.changeDetectionState.hasChanged, children: "View Query Usage..." })] }), menuProps: {
626
- anchorOrigin: { vertical: 'bottom', horizontal: 'right' },
627
- transformOrigin: { vertical: 'top', horizontal: 'right' },
628
- elevation: 7,
629
- }, children: [_jsx("div", { className: "query-builder__result__export__dropdown__label", children: "Export" }), _jsx("div", { className: "query-builder__result__export__dropdown__trigger", children: _jsx(CaretDownIcon, {}) })] }), resultState.isQueryUsageViewerOpened && (_jsx(QueryUsageViewer, { resultState: resultState }))] })] }), _jsxs(PanelContent, { children: [_jsx(PanelLoadingIndicator, { isLoading: resultState.isRunningQuery ||
630
- resultState.isGeneratingPlan ||
631
- resultState.exportDataState.isInProgress }), !executionResult && (_jsx(BlankPanelContent, { children: "Build or load a valid query first" })), executionResult && (_jsx("div", { className: "query-builder__result__values", children: _jsx(QueryBuilderResultValues, { executionResult: executionResult, queryBuilderState: queryBuilderState }) }))] }), _jsx(ExecutionPlanViewer, { executionPlanState: resultState.executionPlanState })] }));
632
- });
633
- //# sourceMappingURL=QueryBuilderResultPanel.js.map