@finos/legend-query-builder 2.1.2 → 2.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/{src/stores/QueryBuilderBootstraper.ts → lib/application/QueryBuilderDocumentation.d.ts} +6 -8
  2. package/lib/application/QueryBuilderDocumentation.d.ts.map +1 -0
  3. package/lib/application/QueryBuilderDocumentation.js +23 -0
  4. package/lib/application/QueryBuilderDocumentation.js.map +1 -0
  5. package/lib/components/QueryBuilder.js +3 -2
  6. package/lib/components/QueryBuilder.js.map +1 -1
  7. package/lib/components/QueryBuilderConstantExpressionPanel.d.ts.map +1 -1
  8. package/lib/components/QueryBuilderConstantExpressionPanel.js +3 -2
  9. package/lib/components/QueryBuilderConstantExpressionPanel.js.map +1 -1
  10. package/lib/components/QueryBuilderDiffPanel.d.ts.map +1 -1
  11. package/lib/components/QueryBuilderDiffPanel.js +9 -8
  12. package/lib/components/QueryBuilderDiffPanel.js.map +1 -1
  13. package/lib/components/QueryBuilderParametersPanel.js +3 -2
  14. package/lib/components/QueryBuilderParametersPanel.js.map +1 -1
  15. package/lib/components/QueryBuilderResultPanel.d.ts.map +1 -1
  16. package/lib/components/QueryBuilderResultPanel.js +8 -7
  17. package/lib/components/QueryBuilderResultPanel.js.map +1 -1
  18. package/lib/components/QueryBuilderTextEditor.js +3 -2
  19. package/lib/components/QueryBuilderTextEditor.js.map +1 -1
  20. package/lib/components/QueryBuilder_LegendApplicationPlugin.d.ts +2 -1
  21. package/lib/components/QueryBuilder_LegendApplicationPlugin.d.ts.map +1 -1
  22. package/lib/components/QueryBuilder_LegendApplicationPlugin.js +8 -0
  23. package/lib/components/QueryBuilder_LegendApplicationPlugin.js.map +1 -1
  24. package/lib/{stores/QueryBuilderBootstraper.js → components/execution-plan/ExecutionPlanViewer.d.ts} +12 -6
  25. package/lib/components/execution-plan/ExecutionPlanViewer.d.ts.map +1 -0
  26. package/lib/components/execution-plan/ExecutionPlanViewer.js +182 -0
  27. package/lib/components/execution-plan/ExecutionPlanViewer.js.map +1 -0
  28. package/lib/components/execution-plan/SQLExecutionNodeViewer.d.ts +31 -0
  29. package/lib/components/execution-plan/SQLExecutionNodeViewer.d.ts.map +1 -0
  30. package/lib/components/execution-plan/SQLExecutionNodeViewer.js +32 -0
  31. package/lib/components/execution-plan/SQLExecutionNodeViewer.js.map +1 -0
  32. package/lib/components/explorer/QueryBuilderPropertySearchPanel.js +2 -2
  33. package/lib/components/explorer/QueryBuilderPropertySearchPanel.js.map +1 -1
  34. package/lib/components/fetch-structure/QueryBuilderTDSWindowPanel.d.ts.map +1 -1
  35. package/lib/components/fetch-structure/QueryBuilderTDSWindowPanel.js +4 -4
  36. package/lib/components/fetch-structure/QueryBuilderTDSWindowPanel.js.map +1 -1
  37. package/lib/components/shared/BasicValueSpecificationEditor.d.ts.map +1 -1
  38. package/lib/components/shared/BasicValueSpecificationEditor.js +2 -3
  39. package/lib/components/shared/BasicValueSpecificationEditor.js.map +1 -1
  40. package/lib/components/shared/LambdaEditor.d.ts.map +1 -1
  41. package/lib/components/shared/LambdaEditor.js +20 -19
  42. package/lib/components/shared/LambdaEditor.js.map +1 -1
  43. package/lib/index.css +1 -17
  44. package/lib/index.css.map +1 -1
  45. package/lib/index.d.ts +3 -2
  46. package/lib/index.d.ts.map +1 -1
  47. package/lib/index.js +3 -2
  48. package/lib/index.js.map +1 -1
  49. package/lib/package.json +9 -17
  50. package/lib/stores/QueryBuilderResultState.d.ts +1 -1
  51. package/lib/stores/QueryBuilderResultState.d.ts.map +1 -1
  52. package/lib/stores/QueryBuilderResultState.js +2 -1
  53. package/lib/stores/QueryBuilderResultState.js.map +1 -1
  54. package/lib/stores/execution-plan/ExecutionPlanState.d.ts +61 -0
  55. package/lib/stores/execution-plan/ExecutionPlanState.d.ts.map +1 -0
  56. package/lib/stores/execution-plan/ExecutionPlanState.js +118 -0
  57. package/lib/stores/execution-plan/ExecutionPlanState.js.map +1 -0
  58. package/lib/stores/explorer/QueryBuilderPropertySearchState.d.ts +3 -4
  59. package/lib/stores/explorer/QueryBuilderPropertySearchState.d.ts.map +1 -1
  60. package/lib/stores/explorer/QueryBuilderPropertySearchState.js +3 -4
  61. package/lib/stores/explorer/QueryBuilderPropertySearchState.js.map +1 -1
  62. package/lib/stores/filter/QueryBuilderFilterState.d.ts.map +1 -1
  63. package/lib/stores/filter/QueryBuilderFilterState.js.map +1 -1
  64. package/package.json +16 -24
  65. package/{lib/stores/QueryBuilderBootstraper.d.ts → src/application/QueryBuilderDocumentation.ts} +8 -2
  66. package/src/components/QueryBuilder.tsx +2 -2
  67. package/src/components/QueryBuilderConstantExpressionPanel.tsx +2 -2
  68. package/src/components/QueryBuilderDiffPanel.tsx +16 -19
  69. package/src/components/QueryBuilderParametersPanel.tsx +2 -2
  70. package/src/components/QueryBuilderResultPanel.tsx +14 -14
  71. package/src/components/QueryBuilderTextEditor.tsx +4 -4
  72. package/src/components/QueryBuilder_LegendApplicationPlugin.ts +10 -0
  73. package/src/components/execution-plan/ExecutionPlanViewer.tsx +543 -0
  74. package/src/components/execution-plan/SQLExecutionNodeViewer.tsx +46 -0
  75. package/src/components/explorer/QueryBuilderPropertySearchPanel.tsx +2 -2
  76. package/src/components/fetch-structure/QueryBuilderTDSWindowPanel.tsx +12 -18
  77. package/src/components/shared/BasicValueSpecificationEditor.tsx +2 -2
  78. package/src/components/shared/LambdaEditor.tsx +27 -25
  79. package/src/index.ts +3 -2
  80. package/src/stores/QueryBuilderResultState.ts +2 -1
  81. package/src/stores/execution-plan/ExecutionPlanState.ts +153 -0
  82. package/src/stores/explorer/QueryBuilderPropertySearchState.ts +4 -4
  83. package/src/stores/filter/QueryBuilderFilterState.ts +2 -0
  84. package/tsconfig.json +4 -1
  85. package/lib/stores/QueryBuilderBootstraper.d.ts.map +0 -1
  86. package/lib/stores/QueryBuilderBootstraper.js.map +0 -1
@@ -0,0 +1,543 @@
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 { useState } from 'react';
18
+ import {
19
+ type TreeNodeContainerProps,
20
+ type TreeData,
21
+ Dialog,
22
+ ResizablePanelGroup,
23
+ ResizablePanelSplitter,
24
+ ResizablePanel,
25
+ ResizablePanelSplitterLine,
26
+ clsx,
27
+ TreeView,
28
+ ChevronDownIcon,
29
+ ChevronRightIcon,
30
+ MenuContentItem,
31
+ MenuContent,
32
+ DropdownMenu,
33
+ BlankPanelContent,
34
+ PanelContent,
35
+ ModalHeader,
36
+ Modal,
37
+ ModalBody,
38
+ ModalFooter,
39
+ PanelSideBarHeader,
40
+ ModalFooterButton,
41
+ } from '@finos/legend-art';
42
+ import {
43
+ addUniqueEntry,
44
+ filterByType,
45
+ isNonNullable,
46
+ } from '@finos/legend-shared';
47
+ import {
48
+ ExecutionNodeTreeNodeData,
49
+ ExecutionPlanViewTreeNodeData,
50
+ EXECUTION_PLAN_VIEW_MODE,
51
+ type ExecutionPlanState,
52
+ } from '../../stores/execution-plan/ExecutionPlanState.js';
53
+ import { observer } from 'mobx-react-lite';
54
+ import {
55
+ ExecutionPlan,
56
+ ExecutionNode,
57
+ SQLExecutionNode,
58
+ RelationalTDSInstantiationExecutionNode,
59
+ type RawExecutionPlan,
60
+ } from '@finos/legend-graph';
61
+ import { SQLExecutionNodeViewer } from './SQLExecutionNodeViewer.js';
62
+ import { CodeEditor } from '@finos/legend-lego/code-editor';
63
+ import { CODE_EDITOR_LANGUAGE, TAB_SIZE } from '@finos/legend-application';
64
+
65
+ /**
66
+ * @modularize
67
+ * See https://github.com/finos/legend-studio/issues/65
68
+ */
69
+ const generateExecutionNodeLabel = (type: ExecutionNode): string => {
70
+ if (type instanceof SQLExecutionNode) {
71
+ return `SQL Execution Node`;
72
+ } else if (type instanceof RelationalTDSInstantiationExecutionNode) {
73
+ return `Relational TDS Instantiation Execution Node`;
74
+ } else {
75
+ return 'Other';
76
+ }
77
+ };
78
+
79
+ const generateExecutionNodeTreeNodeData = (
80
+ executionNode: ExecutionNode,
81
+ label: string,
82
+ parentNode:
83
+ | ExecutionNodeTreeNodeData
84
+ | ExecutionPlanViewTreeNodeData
85
+ | undefined,
86
+ ): ExecutionNodeTreeNodeData => {
87
+ const executionNodeTreeNode = new ExecutionNodeTreeNodeData(
88
+ executionNode._UUID,
89
+ label,
90
+ executionNode,
91
+ );
92
+
93
+ const childrenIds: string[] = [];
94
+
95
+ executionNode.executionNodes
96
+ .slice()
97
+ .filter(filterByType(ExecutionNode))
98
+ .forEach((childExecutionNode) => {
99
+ addUniqueEntry(childrenIds, childExecutionNode._UUID);
100
+ });
101
+
102
+ executionNodeTreeNode.childrenIds = childrenIds;
103
+
104
+ return executionNodeTreeNode;
105
+ };
106
+
107
+ const generateExecutionPlanTreeNodeData = (
108
+ executionPlan: ExecutionPlan,
109
+ ): ExecutionPlanViewTreeNodeData => {
110
+ const executionPlanNode = new ExecutionPlanViewTreeNodeData(
111
+ `Execution Plan`,
112
+ `Execution Plan`,
113
+ executionPlan,
114
+ );
115
+
116
+ const childrenIds: string[] = [];
117
+
118
+ const rootNodeId = executionPlan.rootExecutionNode._UUID;
119
+ addUniqueEntry(childrenIds, rootNodeId);
120
+ executionPlanNode.childrenIds = childrenIds;
121
+ return executionPlanNode;
122
+ };
123
+
124
+ const getExecutionPlanTreeData = (
125
+ executionPlan: ExecutionPlan,
126
+ ): TreeData<ExecutionPlanViewTreeNodeData | ExecutionNodeTreeNodeData> => {
127
+ const rootIds: string[] = [];
128
+ const nodes = new Map<
129
+ string,
130
+ ExecutionPlanViewTreeNodeData | ExecutionNodeTreeNodeData
131
+ >();
132
+ const executionPlanTreeNode =
133
+ generateExecutionPlanTreeNodeData(executionPlan);
134
+ addUniqueEntry(rootIds, executionPlanTreeNode.id);
135
+ nodes.set(executionPlanTreeNode.id, executionPlanTreeNode);
136
+ return { rootIds, nodes };
137
+ };
138
+
139
+ const ExecutionNodeElementTreeNodeContainer: React.FC<
140
+ TreeNodeContainerProps<
141
+ ExecutionPlanViewTreeNodeData | ExecutionNodeTreeNodeData,
142
+ {
143
+ onNodeExpand: (
144
+ node: ExecutionPlanViewTreeNodeData | ExecutionNodeTreeNodeData,
145
+ ) => void;
146
+ }
147
+ >
148
+ > = (props) => {
149
+ const { node, level, stepPaddingInRem, onNodeSelect, innerProps } = props;
150
+ const { onNodeExpand } = innerProps;
151
+ const isExpandable = Boolean(node.childrenIds?.length);
152
+ const selectNode = (): void => onNodeSelect?.(node);
153
+ const expandNode = (): void => onNodeExpand(node);
154
+ const nodeExpandIcon = isExpandable ? (
155
+ node.isOpen ? (
156
+ <ChevronDownIcon />
157
+ ) : (
158
+ <ChevronRightIcon />
159
+ )
160
+ ) : (
161
+ <div />
162
+ );
163
+
164
+ return (
165
+ <div
166
+ className={clsx(
167
+ 'tree-view__node__container execution-plan-viewer__explorer-tree__node__container',
168
+ {
169
+ 'menu__trigger--on-menu-open': !node.isSelected,
170
+ },
171
+ {
172
+ 'execution-plan-viewer__explorer-tree__node__container--selected':
173
+ node.isSelected,
174
+ },
175
+ )}
176
+ style={{
177
+ paddingLeft: `${(level - 1) * (stepPaddingInRem ?? 1)}rem`,
178
+ }}
179
+ onClick={selectNode}
180
+ >
181
+ <div className="tree-view__node__icon">
182
+ <div className="tree-view__node__expand-icon" onClick={expandNode}>
183
+ {nodeExpandIcon}
184
+ </div>
185
+ </div>
186
+ <button
187
+ className="tree-view__node__label execution-plan-viewer__explorer-tree__node__label"
188
+ tabIndex={-1}
189
+ title={node.id}
190
+ >
191
+ {node.label}
192
+ </button>
193
+ </div>
194
+ );
195
+ };
196
+
197
+ export const ExecutionPlanTree: React.FC<{
198
+ executionPlanState: ExecutionPlanState;
199
+ executionPlan: ExecutionPlan;
200
+ }> = (props) => {
201
+ const { executionPlanState, executionPlan } = props;
202
+ // NOTE: We only need to compute this once so we use lazy initial state syntax
203
+ // See https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
204
+ const [treeData, setTreeData] = useState<
205
+ TreeData<ExecutionPlanViewTreeNodeData | ExecutionNodeTreeNodeData>
206
+ >(() => getExecutionPlanTreeData(executionPlan));
207
+ const onNodeSelect = (
208
+ node: ExecutionPlanViewTreeNodeData | ExecutionNodeTreeNodeData,
209
+ ): void => {
210
+ if (node instanceof ExecutionPlanViewTreeNodeData) {
211
+ executionPlanState.transformMetadataToProtocolJson(node.executionPlan);
212
+ } else if (node instanceof ExecutionNodeTreeNodeData) {
213
+ executionPlanState.transformMetadataToProtocolJson(node.executionNode);
214
+ }
215
+ executionPlanState.setSelectedNode(node);
216
+ };
217
+
218
+ const onNodeExpand = (
219
+ node: ExecutionPlanViewTreeNodeData | ExecutionNodeTreeNodeData,
220
+ ): void => {
221
+ if (node.childrenIds?.length) {
222
+ node.isOpen = !node.isOpen;
223
+ if (node instanceof ExecutionPlanViewTreeNodeData) {
224
+ const rootNode = node.executionPlan.rootExecutionNode;
225
+ const rootNodeTreeNode = generateExecutionNodeTreeNodeData(
226
+ rootNode,
227
+ generateExecutionNodeLabel(rootNode),
228
+ node,
229
+ );
230
+ treeData.nodes.set(rootNodeTreeNode.id, rootNodeTreeNode);
231
+ } else if (node instanceof ExecutionNodeTreeNodeData) {
232
+ if (node.executionNode.executionNodes.length > 0) {
233
+ node.executionNode.executionNodes.forEach((exen) => {
234
+ const executionNodeTreeNode = generateExecutionNodeTreeNodeData(
235
+ exen,
236
+ generateExecutionNodeLabel(exen),
237
+ node,
238
+ );
239
+
240
+ treeData.nodes.set(executionNodeTreeNode.id, executionNodeTreeNode);
241
+ });
242
+ }
243
+ }
244
+ }
245
+
246
+ setTreeData({ ...treeData });
247
+ };
248
+
249
+ const getChildNodes = (
250
+ node: ExecutionPlanViewTreeNodeData | ExecutionNodeTreeNodeData,
251
+ ): (ExecutionPlanViewTreeNodeData | ExecutionNodeTreeNodeData)[] => {
252
+ if (!node.childrenIds || node.childrenIds.length === 0) {
253
+ return [];
254
+ }
255
+ const childrenNodes = node.childrenIds
256
+ .map((id) => treeData.nodes.get(id))
257
+ .filter(isNonNullable);
258
+
259
+ return childrenNodes;
260
+ };
261
+ return (
262
+ <TreeView
263
+ components={{
264
+ TreeNodeContainer: ExecutionNodeElementTreeNodeContainer,
265
+ }}
266
+ treeData={treeData}
267
+ getChildNodes={getChildNodes}
268
+ onNodeSelect={onNodeSelect}
269
+ innerProps={{
270
+ onNodeExpand,
271
+ }}
272
+ />
273
+ );
274
+ };
275
+
276
+ const ExecutionNodeViewer = observer(
277
+ (props: {
278
+ executionNode: ExecutionNode;
279
+ executionPlanState: ExecutionPlanState;
280
+ }) => {
281
+ const { executionNode, executionPlanState } = props;
282
+ if (executionNode instanceof SQLExecutionNode) {
283
+ return (
284
+ <SQLExecutionNodeViewer
285
+ query={executionNode.sqlQuery}
286
+ resultColumns={executionNode.resultColumns}
287
+ executionPlanState={executionPlanState}
288
+ />
289
+ );
290
+ }
291
+ return (
292
+ <BlankPanelContent>
293
+ <div className="execution-node-viewer__unsupported-view">
294
+ <div className="execution-node-viewer__unsupported-view__summary">
295
+ {`Can't display execution node`}
296
+ </div>
297
+ <button
298
+ className="btn--dark execution-node-viewer__unsupported-view__to-text-mode__btn"
299
+ onClick={(): void =>
300
+ executionPlanState.setViewMode(EXECUTION_PLAN_VIEW_MODE.JSON)
301
+ }
302
+ >
303
+ View JSON
304
+ </button>
305
+ </div>
306
+ </BlankPanelContent>
307
+ );
308
+ },
309
+ );
310
+
311
+ const ExecutionPlanViewPanel = observer(
312
+ (props: { displayData: string; executionPlanState: ExecutionPlanState }) => {
313
+ const { displayData, executionPlanState } = props;
314
+ let currentElement;
315
+ if (executionPlanState.selectedNode !== undefined) {
316
+ if (
317
+ executionPlanState.selectedNode instanceof ExecutionPlanViewTreeNodeData
318
+ ) {
319
+ currentElement = executionPlanState.selectedNode.executionPlan;
320
+ } else if (
321
+ executionPlanState.selectedNode instanceof ExecutionNodeTreeNodeData
322
+ ) {
323
+ currentElement = executionPlanState.selectedNode.executionNode;
324
+ }
325
+ }
326
+ const nativeViewModes = Object.values(EXECUTION_PLAN_VIEW_MODE);
327
+
328
+ return (
329
+ <div className="execution-plan-viewer__panel">
330
+ {executionPlanState.selectedNode !== undefined && (
331
+ <>
332
+ <div className="panel__header execution-plan-viewer__panel__header">
333
+ <div className="execution-plan-viewer__panel__header__tabs">
334
+ <button className="execution-plan-viewer__panel__header__tab execution-plan-viewer__panel__header__tab--active">
335
+ {executionPlanState.selectedNode.label}
336
+ </button>
337
+ </div>
338
+ <DropdownMenu
339
+ className="execution-plan-viewer__panel__view-mode__type"
340
+ title="View as..."
341
+ content={
342
+ <MenuContent className="execution-plan-viewer__panel__view-mode__options execution-plan-viewer__panel__view-mode__options--with-group">
343
+ <div className="execution-plan-viewer__panel__view-mode__option__group execution-plan-viewer__panel__view-mode__option__group--native">
344
+ <div className="execution-plan-viewer__panel__view-mode__option__group__name">
345
+ native
346
+ </div>
347
+ <div className="execution-plan-viewer__panel__view-mode__option__group__options">
348
+ {nativeViewModes.map((mode) => (
349
+ <MenuContentItem
350
+ key={mode}
351
+ className="execution-plan-viewer__panel__view-mode__option"
352
+ onClick={(): void =>
353
+ executionPlanState.setViewMode(mode)
354
+ }
355
+ >
356
+ {mode}
357
+ </MenuContentItem>
358
+ ))}
359
+ </div>
360
+ </div>
361
+ </MenuContent>
362
+ }
363
+ menuProps={{
364
+ anchorOrigin: { vertical: 'bottom', horizontal: 'right' },
365
+ transformOrigin: { vertical: 'top', horizontal: 'right' },
366
+ }}
367
+ >
368
+ <div className="execution-plan-viewer__panel__view-mode__type__label">
369
+ {executionPlanState.viewMode}
370
+ </div>
371
+ </DropdownMenu>
372
+ </div>
373
+ <div className="panel__content execution-plan-viewer__panel__content">
374
+ {executionPlanState.viewMode === EXECUTION_PLAN_VIEW_MODE.JSON &&
375
+ Boolean(displayData) && (
376
+ <CodeEditor
377
+ inputValue={displayData}
378
+ isReadOnly={true}
379
+ language={CODE_EDITOR_LANGUAGE.JSON}
380
+ showMiniMap={false}
381
+ />
382
+ )}
383
+ {executionPlanState.viewMode ===
384
+ EXECUTION_PLAN_VIEW_MODE.FORM && (
385
+ <>
386
+ {currentElement instanceof ExecutionNode && (
387
+ <ExecutionNodeViewer
388
+ executionNode={currentElement}
389
+ executionPlanState={executionPlanState}
390
+ />
391
+ )}
392
+ {currentElement instanceof ExecutionPlan && (
393
+ <BlankPanelContent>
394
+ <div className="execution-plan-viewer__unsupported-view">
395
+ <div className="execution-plan-viewer__unsupported-view__summary">
396
+ {`Can't display full execution plan`}
397
+ </div>
398
+ <button
399
+ className="btn--dark execution-plan-viewer__unsupported-view__to-text-mode__btn"
400
+ onClick={(): void =>
401
+ executionPlanState.setViewMode(
402
+ EXECUTION_PLAN_VIEW_MODE.JSON,
403
+ )
404
+ }
405
+ >
406
+ View JSON
407
+ </button>
408
+ </div>
409
+ </BlankPanelContent>
410
+ )}
411
+ </>
412
+ )}
413
+ </div>
414
+ </>
415
+ )}
416
+ </div>
417
+ );
418
+ },
419
+ );
420
+
421
+ const ExecutionPlanViewerContent = observer(
422
+ (props: {
423
+ executionPlanState: ExecutionPlanState;
424
+ rawPlan: RawExecutionPlan;
425
+ }) => {
426
+ const { executionPlanState, rawPlan } = props;
427
+ const plan = executionPlanState.plan;
428
+
429
+ return (
430
+ <div className="execution-plan-viewer__content">
431
+ {plan ? (
432
+ <ResizablePanelGroup orientation="vertical">
433
+ <ResizablePanel size={300} minSize={300}>
434
+ <div className="panel execution-plan-viewer__explorer">
435
+ <PanelSideBarHeader
436
+ darkMode={true}
437
+ title="execution plan explorer"
438
+ />
439
+ <div className="panel__content execution-plan-viewer__explorer__content__container">
440
+ <ExecutionPlanTree
441
+ executionPlanState={executionPlanState}
442
+ executionPlan={plan}
443
+ />
444
+ </div>
445
+ </div>
446
+ </ResizablePanel>
447
+ <ResizablePanelSplitter>
448
+ <ResizablePanelSplitterLine color="var(--color-dark-grey-200)" />
449
+ </ResizablePanelSplitter>
450
+ <ResizablePanel>
451
+ <ExecutionPlanViewPanel
452
+ displayData={executionPlanState.displayData}
453
+ executionPlanState={executionPlanState}
454
+ />
455
+ </ResizablePanel>
456
+ </ResizablePanelGroup>
457
+ ) : (
458
+ <CodeEditor
459
+ inputValue={JSON.stringify(rawPlan, undefined, TAB_SIZE)}
460
+ isReadOnly={true}
461
+ language={CODE_EDITOR_LANGUAGE.JSON}
462
+ showMiniMap={true}
463
+ />
464
+ )}
465
+ </div>
466
+ );
467
+ },
468
+ );
469
+
470
+ export const ExecutionPlanViewer = observer(
471
+ (props: { executionPlanState: ExecutionPlanState }) => {
472
+ const { executionPlanState } = props;
473
+ const closePlanViewer = (): void => {
474
+ executionPlanState.setRawPlan(undefined);
475
+ executionPlanState.setPlan(undefined);
476
+ executionPlanState.setExecutionPlanDisplayData('');
477
+ executionPlanState.setSelectedNode(undefined);
478
+ executionPlanState.setDebugText(undefined);
479
+ };
480
+ const rawPlan = executionPlanState.rawPlan;
481
+
482
+ if (!rawPlan) {
483
+ return null;
484
+ }
485
+ return (
486
+ <Dialog
487
+ open={Boolean(executionPlanState.rawPlan)}
488
+ onClose={closePlanViewer}
489
+ classes={{
490
+ root: 'editor-modal__root-container',
491
+ container: 'editor-modal__container',
492
+ paper: 'editor-modal__content',
493
+ }}
494
+ >
495
+ <Modal className="editor-modal" darkMode={true}>
496
+ <ModalHeader title="Execution Plan" />
497
+ <ModalBody>
498
+ {executionPlanState.debugText ? (
499
+ <ResizablePanelGroup orientation="horizontal">
500
+ <ResizablePanel minSize={100}>
501
+ <ExecutionPlanViewerContent
502
+ executionPlanState={executionPlanState}
503
+ rawPlan={rawPlan}
504
+ />
505
+ </ResizablePanel>
506
+ <ResizablePanelSplitter>
507
+ <ResizablePanelSplitterLine color="var(--color-dark-grey-200)" />
508
+ </ResizablePanelSplitter>
509
+ <ResizablePanel size={200} minSize={28}>
510
+ <div className="panel execution-plan-viewer__debug-panel">
511
+ <div className="panel__header">
512
+ <div className="panel__header__title">
513
+ <div className="panel__header__title__label">
514
+ DEBUG LOG
515
+ </div>
516
+ </div>
517
+ </div>
518
+ <PanelContent>
519
+ <CodeEditor
520
+ inputValue={executionPlanState.debugText}
521
+ isReadOnly={true}
522
+ language={CODE_EDITOR_LANGUAGE.TEXT}
523
+ showMiniMap={true}
524
+ />
525
+ </PanelContent>
526
+ </div>
527
+ </ResizablePanel>
528
+ </ResizablePanelGroup>
529
+ ) : (
530
+ <ExecutionPlanViewerContent
531
+ executionPlanState={executionPlanState}
532
+ rawPlan={rawPlan}
533
+ />
534
+ )}
535
+ </ModalBody>
536
+ <ModalFooter>
537
+ <ModalFooterButton onClick={closePlanViewer} text="Close" />
538
+ </ModalFooter>
539
+ </Modal>
540
+ </Dialog>
541
+ );
542
+ },
543
+ );
@@ -0,0 +1,46 @@
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 } from 'mobx-react-lite';
18
+ import type { ExecutionPlanState } from '../../stores/execution-plan/ExecutionPlanState.js';
19
+ import { format as formatSQL } from 'sql-formatter';
20
+ import type { SQLResultColumn } from '@finos/legend-graph';
21
+ import { CodeEditor } from '@finos/legend-lego/code-editor';
22
+ import { CODE_EDITOR_LANGUAGE } from '@finos/legend-application';
23
+
24
+ /**
25
+ * TODO: Create a new `AbstractPlugin` for this, called `ExecutionPlanViewerPlugin`
26
+ * when we modularize relational and execution plan processing, etc.
27
+ *
28
+ * @modularize
29
+ * See https://github.com/finos/legend-studio/issues/65
30
+ */
31
+ export const SQLExecutionNodeViewer: React.FC<{
32
+ query: string;
33
+ resultColumns: SQLResultColumn[];
34
+ executionPlanState: ExecutionPlanState;
35
+ }> = observer((props) => {
36
+ const { query } = props;
37
+
38
+ return (
39
+ <CodeEditor
40
+ inputValue={formatSQL(query)}
41
+ isReadOnly={true}
42
+ language={CODE_EDITOR_LANGUAGE.SQL}
43
+ showMiniMap={false}
44
+ />
45
+ );
46
+ });
@@ -61,7 +61,7 @@ import {
61
61
  } from './QueryBuilderExplorerPanel.js';
62
62
  import { QueryBuilderPropertyInfoTooltip } from '../shared/QueryBuilderPropertyInfoTooltip.js';
63
63
  import { QUERY_BUILDER_TEST_ID } from '../../application/QueryBuilderTesting.js';
64
- import { TextSearchAdvancedConfigMenu } from '@finos/legend-application';
64
+ import { FuzzySearchAdvancedConfigMenu } from '@finos/legend-application/components';
65
65
 
66
66
  const prettyPropertyNameFromNodeId = (name: string): string => {
67
67
  let propNameArray = name.split('.');
@@ -387,7 +387,7 @@ export const QueryBuilderPropertySearchPanel = observer(
387
387
  horizontal: 'center',
388
388
  }}
389
389
  >
390
- <TextSearchAdvancedConfigMenu
390
+ <FuzzySearchAdvancedConfigMenu
391
391
  configState={propertySearchState.searchConfigurationState}
392
392
  />
393
393
  </BasePopover>
@@ -356,20 +356,18 @@ const QueryBuilderWindowColumnModalEditor = observer(
356
356
  elevation: 7,
357
357
  }}
358
358
  >
359
- <button
359
+ <div
360
360
  className="query-builder__olap__column__operation__operator__badge"
361
- tabIndex={-1}
362
361
  title="Choose Window Function Operator..."
363
362
  >
364
363
  <SigmaIcon />
365
- </button>
366
- <button
364
+ </div>
365
+ <div
367
366
  className="query-builder__olap__column__operation__operator__dropdown__trigger"
368
- tabIndex={-1}
369
367
  title="Choose Window Function Operator..."
370
368
  >
371
369
  <CaretDownIcon />
372
- </button>
370
+ </div>
373
371
  </DropdownMenu>
374
372
  </div>
375
373
  </div>
@@ -918,20 +916,18 @@ const QueryBuilderWindowColumnEditor = observer(
918
916
  elevation: 7,
919
917
  }}
920
918
  >
921
- <button
919
+ <div
922
920
  className="query-builder__olap__column__operation__operator__badge"
923
- tabIndex={-1}
924
921
  title="Choose Window Function Operator..."
925
922
  >
926
923
  <SigmaIcon />
927
- </button>
928
- <button
924
+ </div>
925
+ <div
929
926
  className="query-builder__olap__column__operation__operator__dropdown__trigger"
930
- tabIndex={-1}
931
927
  title="Choose Window Function Operator..."
932
928
  >
933
929
  <CaretDownIcon />
934
- </button>
930
+ </div>
935
931
  </DropdownMenu>
936
932
  </div>
937
933
  </div>
@@ -1058,7 +1054,7 @@ const QueryBuilderWindowColumnEditor = observer(
1058
1054
  elevation: 7,
1059
1055
  }}
1060
1056
  >
1061
- <button
1057
+ <div
1062
1058
  className={clsx(
1063
1059
  'query-builder__olap__column__sortby__operator__badge',
1064
1060
  {
@@ -1066,18 +1062,16 @@ const QueryBuilderWindowColumnEditor = observer(
1066
1062
  Boolean(sortByState),
1067
1063
  },
1068
1064
  )}
1069
- tabIndex={-1}
1070
1065
  title="Choose Window Function SortBy Operator..."
1071
1066
  >
1072
1067
  <SortIcon />
1073
- </button>
1074
- <button
1068
+ </div>
1069
+ <div
1075
1070
  className="query-builder__olap__column__sortby__operator__dropdown__trigger"
1076
- tabIndex={-1}
1077
1071
  title="Choose Window Function SortBy Operator..."
1078
1072
  >
1079
1073
  <CaretDownIcon />
1080
- </button>
1074
+ </div>
1081
1075
  </DropdownMenu>
1082
1076
  </div>
1083
1077
  </div>