@finos/legend-application 4.0.3 → 5.1.0

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 (145) hide show
  1. package/lib/application/LegendApplication.d.ts +9 -1
  2. package/lib/application/LegendApplication.d.ts.map +1 -1
  3. package/lib/application/LegendApplication.js +54 -1
  4. package/lib/application/LegendApplication.js.map +1 -1
  5. package/lib/components/VirtualAssistant.d.ts.map +1 -1
  6. package/lib/components/VirtualAssistant.js +91 -11
  7. package/lib/components/VirtualAssistant.js.map +1 -1
  8. package/lib/components/{BasicValueSpecificationEditor.d.ts → shared/BasicValueSpecificationEditor.d.ts} +2 -2
  9. package/lib/components/shared/BasicValueSpecificationEditor.d.ts.map +1 -0
  10. package/lib/components/{BasicValueSpecificationEditor.js → shared/BasicValueSpecificationEditor.js} +29 -20
  11. package/lib/components/shared/BasicValueSpecificationEditor.js.map +1 -0
  12. package/lib/components/{CustomDatePicker.d.ts → shared/CustomDatePicker.d.ts} +1 -1
  13. package/lib/components/shared/CustomDatePicker.d.ts.map +1 -0
  14. package/lib/components/{CustomDatePicker.js → shared/CustomDatePicker.js} +20 -20
  15. package/lib/components/shared/CustomDatePicker.js.map +1 -0
  16. package/lib/components/{DocumentationLink.d.ts → shared/DocumentationLink.d.ts} +0 -0
  17. package/lib/components/shared/DocumentationLink.d.ts.map +1 -0
  18. package/lib/components/{DocumentationLink.js → shared/DocumentationLink.js} +1 -1
  19. package/lib/components/shared/DocumentationLink.js.map +1 -0
  20. package/lib/components/{LambdaEditor.d.ts → shared/LambdaEditor.d.ts} +1 -1
  21. package/lib/components/shared/LambdaEditor.d.ts.map +1 -0
  22. package/lib/components/{LambdaEditor.js → shared/LambdaEditor.js} +3 -3
  23. package/lib/components/shared/LambdaEditor.js.map +1 -0
  24. package/lib/components/{LambdaParameterValuesEditor.d.ts → shared/LambdaParameterValuesEditor.d.ts} +1 -1
  25. package/lib/components/shared/LambdaParameterValuesEditor.d.ts.map +1 -0
  26. package/lib/components/{LambdaParameterValuesEditor.js → shared/LambdaParameterValuesEditor.js} +2 -2
  27. package/lib/components/shared/LambdaParameterValuesEditor.js.map +1 -0
  28. package/lib/components/shared/PackageableElementOptionRenderer.d.ts +21 -0
  29. package/lib/components/shared/PackageableElementOptionRenderer.d.ts.map +1 -0
  30. package/lib/components/shared/PackageableElementOptionRenderer.js +8 -0
  31. package/lib/components/shared/PackageableElementOptionRenderer.js.map +1 -0
  32. package/lib/components/{TextInputEditor.d.ts → shared/TextInputEditor.d.ts} +1 -1
  33. package/lib/components/shared/TextInputEditor.d.ts.map +1 -0
  34. package/lib/components/{TextInputEditor.js → shared/TextInputEditor.js} +2 -2
  35. package/lib/components/shared/TextInputEditor.js.map +1 -0
  36. package/lib/components/shared/execution-plan-viewer/ExecutionPlanViewer.d.ts +28 -0
  37. package/lib/components/shared/execution-plan-viewer/ExecutionPlanViewer.d.ts.map +1 -0
  38. package/lib/components/shared/execution-plan-viewer/ExecutionPlanViewer.js +182 -0
  39. package/lib/components/shared/execution-plan-viewer/ExecutionPlanViewer.js.map +1 -0
  40. package/lib/components/shared/execution-plan-viewer/SQLExecutionNodeViewer.d.ts +31 -0
  41. package/lib/components/shared/execution-plan-viewer/SQLExecutionNodeViewer.d.ts.map +1 -0
  42. package/lib/components/shared/execution-plan-viewer/SQLExecutionNodeViewer.js +32 -0
  43. package/lib/components/shared/execution-plan-viewer/SQLExecutionNodeViewer.js.map +1 -0
  44. package/lib/const.d.ts +0 -3
  45. package/lib/const.d.ts.map +1 -1
  46. package/lib/const.js +0 -3
  47. package/lib/const.js.map +1 -1
  48. package/lib/index.css +2 -2
  49. package/lib/index.css.map +1 -1
  50. package/lib/index.d.ts +12 -9
  51. package/lib/index.d.ts.map +1 -1
  52. package/lib/index.js +13 -9
  53. package/lib/index.js.map +1 -1
  54. package/lib/stores/ApplicationEvent.d.ts +4 -2
  55. package/lib/stores/ApplicationEvent.d.ts.map +1 -1
  56. package/lib/stores/ApplicationEvent.js +4 -2
  57. package/lib/stores/ApplicationEvent.js.map +1 -1
  58. package/lib/stores/ApplicationStore.d.ts.map +1 -1
  59. package/lib/stores/ApplicationStore.js +2 -1
  60. package/lib/stores/ApplicationStore.js.map +1 -1
  61. package/lib/stores/LegendApplicationAssistantService.d.ts +7 -2
  62. package/lib/stores/LegendApplicationAssistantService.d.ts.map +1 -1
  63. package/lib/stores/LegendApplicationAssistantService.js +22 -10
  64. package/lib/stores/LegendApplicationAssistantService.js.map +1 -1
  65. package/lib/stores/LegendApplicationConfig.d.ts +8 -5
  66. package/lib/stores/LegendApplicationConfig.d.ts.map +1 -1
  67. package/lib/stores/LegendApplicationConfig.js +15 -13
  68. package/lib/stores/LegendApplicationConfig.js.map +1 -1
  69. package/lib/stores/LegendApplicationDocumentationService.d.ts +34 -24
  70. package/lib/stores/LegendApplicationDocumentationService.d.ts.map +1 -1
  71. package/lib/stores/LegendApplicationDocumentationService.js +64 -60
  72. package/lib/stores/LegendApplicationDocumentationService.js.map +1 -1
  73. package/lib/stores/LegendApplicationPlugin.d.ts +14 -3
  74. package/lib/stores/LegendApplicationPlugin.d.ts.map +1 -1
  75. package/lib/stores/LegendApplicationPlugin.js.map +1 -1
  76. package/lib/stores/PureLanguageSupport.d.ts.map +1 -1
  77. package/lib/stores/PureLanguageSupport.js +8 -3
  78. package/lib/stores/PureLanguageSupport.js.map +1 -1
  79. package/lib/stores/shared/ExecutionPlanState.d.ts +62 -0
  80. package/lib/stores/shared/ExecutionPlanState.d.ts.map +1 -0
  81. package/lib/stores/shared/ExecutionPlanState.js +118 -0
  82. package/lib/stores/shared/ExecutionPlanState.js.map +1 -0
  83. package/lib/stores/{LambdaEditorState.d.ts → shared/LambdaEditorState.d.ts} +0 -0
  84. package/lib/stores/shared/LambdaEditorState.d.ts.map +1 -0
  85. package/lib/stores/{LambdaEditorState.js → shared/LambdaEditorState.js} +0 -0
  86. package/lib/stores/shared/LambdaEditorState.js.map +1 -0
  87. package/lib/stores/{LambdaParameterState.d.ts → shared/LambdaParameterState.d.ts} +1 -3
  88. package/lib/stores/shared/LambdaParameterState.d.ts.map +1 -0
  89. package/lib/stores/{LambdaParameterState.js → shared/LambdaParameterState.js} +3 -52
  90. package/lib/stores/shared/LambdaParameterState.js.map +1 -0
  91. package/lib/stores/{PackageableElementOption.d.ts → shared/PackageableElementOption.d.ts} +0 -0
  92. package/lib/stores/shared/PackageableElementOption.d.ts.map +1 -0
  93. package/lib/stores/{PackageableElementOption.js → shared/PackageableElementOption.js} +0 -0
  94. package/lib/stores/shared/PackageableElementOption.js.map +1 -0
  95. package/lib/stores/{ValueSpecificationModifierHelper.d.ts → shared/ValueSpecificationModifierHelper.d.ts} +0 -0
  96. package/lib/stores/shared/ValueSpecificationModifierHelper.d.ts.map +1 -0
  97. package/lib/stores/{ValueSpecificationModifierHelper.js → shared/ValueSpecificationModifierHelper.js} +0 -0
  98. package/lib/stores/shared/ValueSpecificationModifierHelper.js.map +1 -0
  99. package/package.json +13 -12
  100. package/src/application/LegendApplication.tsx +100 -4
  101. package/src/components/VirtualAssistant.tsx +106 -16
  102. package/src/components/{BasicValueSpecificationEditor.tsx → shared/BasicValueSpecificationEditor.tsx} +50 -19
  103. package/src/components/{CustomDatePicker.tsx → shared/CustomDatePicker.tsx} +44 -27
  104. package/src/components/{DocumentationLink.tsx → shared/DocumentationLink.tsx} +1 -1
  105. package/src/components/{LambdaEditor.tsx → shared/LambdaEditor.tsx} +4 -4
  106. package/src/components/{LambdaParameterValuesEditor.tsx → shared/LambdaParameterValuesEditor.tsx} +5 -3
  107. package/src/components/shared/PackageableElementOptionRenderer.tsx +40 -0
  108. package/src/components/{TextInputEditor.tsx → shared/TextInputEditor.tsx} +2 -2
  109. package/src/components/shared/execution-plan-viewer/ExecutionPlanViewer.tsx +550 -0
  110. package/src/components/shared/execution-plan-viewer/SQLExecutionNodeViewer.tsx +46 -0
  111. package/src/const.ts +0 -5
  112. package/src/index.ts +16 -13
  113. package/src/stores/ApplicationEvent.ts +4 -2
  114. package/src/stores/ApplicationStore.ts +3 -1
  115. package/src/stores/LegendApplicationAssistantService.ts +30 -16
  116. package/src/stores/LegendApplicationConfig.ts +28 -27
  117. package/src/stores/LegendApplicationDocumentationService.ts +128 -124
  118. package/src/stores/LegendApplicationPlugin.ts +17 -3
  119. package/src/stores/PureLanguageSupport.ts +8 -3
  120. package/src/stores/shared/ExecutionPlanState.ts +154 -0
  121. package/src/stores/{LambdaEditorState.ts → shared/LambdaEditorState.ts} +0 -0
  122. package/src/stores/{LambdaParameterState.ts → shared/LambdaParameterState.ts} +2 -100
  123. package/src/stores/{PackageableElementOption.ts → shared/PackageableElementOption.ts} +0 -0
  124. package/src/stores/{ValueSpecificationModifierHelper.ts → shared/ValueSpecificationModifierHelper.ts} +0 -0
  125. package/tsconfig.json +14 -10
  126. package/lib/components/BasicValueSpecificationEditor.d.ts.map +0 -1
  127. package/lib/components/BasicValueSpecificationEditor.js.map +0 -1
  128. package/lib/components/CustomDatePicker.d.ts.map +0 -1
  129. package/lib/components/CustomDatePicker.js.map +0 -1
  130. package/lib/components/DocumentationLink.d.ts.map +0 -1
  131. package/lib/components/DocumentationLink.js.map +0 -1
  132. package/lib/components/LambdaEditor.d.ts.map +0 -1
  133. package/lib/components/LambdaEditor.js.map +0 -1
  134. package/lib/components/LambdaParameterValuesEditor.d.ts.map +0 -1
  135. package/lib/components/LambdaParameterValuesEditor.js.map +0 -1
  136. package/lib/components/TextInputEditor.d.ts.map +0 -1
  137. package/lib/components/TextInputEditor.js.map +0 -1
  138. package/lib/stores/LambdaEditorState.d.ts.map +0 -1
  139. package/lib/stores/LambdaEditorState.js.map +0 -1
  140. package/lib/stores/LambdaParameterState.d.ts.map +0 -1
  141. package/lib/stores/LambdaParameterState.js.map +0 -1
  142. package/lib/stores/PackageableElementOption.d.ts.map +0 -1
  143. package/lib/stores/PackageableElementOption.js.map +0 -1
  144. package/lib/stores/ValueSpecificationModifierHelper.d.ts.map +0 -1
  145. package/lib/stores/ValueSpecificationModifierHelper.js.map +0 -1
@@ -0,0 +1,550 @@
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
+ } from '@finos/legend-art';
35
+ import {
36
+ addUniqueEntry,
37
+ filterByType,
38
+ isNonNullable,
39
+ } from '@finos/legend-shared';
40
+ import {
41
+ ExecutionNodeTreeNodeData,
42
+ ExecutionPlanViewTreeNodeData,
43
+ EXECUTION_PLAN_VIEW_MODE,
44
+ type ExecutionPlanState,
45
+ } from '../../../stores/shared/ExecutionPlanState.js';
46
+ import { observer } from 'mobx-react-lite';
47
+ import {
48
+ ExecutionPlan,
49
+ ExecutionNode,
50
+ SQLExecutionNode,
51
+ RelationalTDSInstantiationExecutionNode,
52
+ type RawExecutionPlan,
53
+ } from '@finos/legend-graph';
54
+ import { EDITOR_LANGUAGE, TAB_SIZE } from '../../../const.js';
55
+ import { TextInputEditor } from '../TextInputEditor.js';
56
+ import { SQLExecutionNodeViewer } from './SQLExecutionNodeViewer.js';
57
+
58
+ /**
59
+ * @modularize
60
+ * See https://github.com/finos/legend-studio/issues/65
61
+ */
62
+ const generateExecutionNodeLabel = (type: ExecutionNode): string => {
63
+ if (type instanceof SQLExecutionNode) {
64
+ return `SQL Execution Node`;
65
+ } else if (type instanceof RelationalTDSInstantiationExecutionNode) {
66
+ return `Relational TDS Instantiation Execution Node`;
67
+ } else {
68
+ return 'Other';
69
+ }
70
+ };
71
+
72
+ const generateExecutionNodeTreeNodeData = (
73
+ executionNode: ExecutionNode,
74
+ label: string,
75
+ parentNode:
76
+ | ExecutionNodeTreeNodeData
77
+ | ExecutionPlanViewTreeNodeData
78
+ | undefined,
79
+ ): ExecutionNodeTreeNodeData => {
80
+ const executionNodeTreeNode = new ExecutionNodeTreeNodeData(
81
+ executionNode._UUID,
82
+ label,
83
+ executionNode,
84
+ );
85
+
86
+ const childrenIds: string[] = [];
87
+
88
+ executionNode.executionNodes
89
+ .slice()
90
+ .filter(filterByType(ExecutionNode))
91
+ .forEach((childExecutionNode) => {
92
+ addUniqueEntry(childrenIds, childExecutionNode._UUID);
93
+ });
94
+
95
+ executionNodeTreeNode.childrenIds = childrenIds;
96
+
97
+ return executionNodeTreeNode;
98
+ };
99
+
100
+ const generateExecutionPlanTreeNodeData = (
101
+ executionPlan: ExecutionPlan,
102
+ ): ExecutionPlanViewTreeNodeData => {
103
+ const executionPlanNode = new ExecutionPlanViewTreeNodeData(
104
+ `Execution Plan`,
105
+ `Execution Plan`,
106
+ executionPlan,
107
+ );
108
+
109
+ const childrenIds: string[] = [];
110
+
111
+ const rootNodeId = executionPlan.rootExecutionNode._UUID;
112
+ addUniqueEntry(childrenIds, rootNodeId);
113
+ executionPlanNode.childrenIds = childrenIds;
114
+ return executionPlanNode;
115
+ };
116
+
117
+ const getExecutionPlanTreeData = (
118
+ executionPlan: ExecutionPlan,
119
+ ): TreeData<ExecutionPlanViewTreeNodeData | ExecutionNodeTreeNodeData> => {
120
+ const rootIds: string[] = [];
121
+ const nodes = new Map<
122
+ string,
123
+ ExecutionPlanViewTreeNodeData | ExecutionNodeTreeNodeData
124
+ >();
125
+ const executionPlanTreeNode =
126
+ generateExecutionPlanTreeNodeData(executionPlan);
127
+ addUniqueEntry(rootIds, executionPlanTreeNode.id);
128
+ nodes.set(executionPlanTreeNode.id, executionPlanTreeNode);
129
+ return { rootIds, nodes };
130
+ };
131
+
132
+ const ExecutionNodeElementTreeNodeContainer: React.FC<
133
+ TreeNodeContainerProps<
134
+ ExecutionPlanViewTreeNodeData | ExecutionNodeTreeNodeData,
135
+ {
136
+ onNodeExpand: (
137
+ node: ExecutionPlanViewTreeNodeData | ExecutionNodeTreeNodeData,
138
+ ) => void;
139
+ }
140
+ >
141
+ > = (props) => {
142
+ const { node, level, stepPaddingInRem, onNodeSelect, innerProps } = props;
143
+ const { onNodeExpand } = innerProps;
144
+ const isExpandable = Boolean(node.childrenIds?.length);
145
+ const selectNode = (): void => onNodeSelect?.(node);
146
+ const expandNode = (): void => onNodeExpand(node);
147
+ const nodeExpandIcon = isExpandable ? (
148
+ node.isOpen ? (
149
+ <ChevronDownIcon />
150
+ ) : (
151
+ <ChevronRightIcon />
152
+ )
153
+ ) : (
154
+ <div />
155
+ );
156
+
157
+ return (
158
+ <div
159
+ className={clsx(
160
+ 'tree-view__node__container execution-plan-viewer__explorer-tree__node__container',
161
+ {
162
+ 'menu__trigger--on-menu-open': !node.isSelected,
163
+ },
164
+ {
165
+ 'execution-plan-viewer__explorer-tree__node__container--selected':
166
+ node.isSelected,
167
+ },
168
+ )}
169
+ style={{
170
+ paddingLeft: `${(level - 1) * (stepPaddingInRem ?? 1)}rem`,
171
+ }}
172
+ onClick={selectNode}
173
+ >
174
+ <div className="tree-view__node__icon">
175
+ <div className="tree-view__node__expand-icon" onClick={expandNode}>
176
+ {nodeExpandIcon}
177
+ </div>
178
+ </div>
179
+ <button
180
+ className="tree-view__node__label execution-plan-viewer__explorer-tree__node__label"
181
+ tabIndex={-1}
182
+ title={node.id}
183
+ >
184
+ {node.label}
185
+ </button>
186
+ </div>
187
+ );
188
+ };
189
+
190
+ export const ExecutionPlanTree: React.FC<{
191
+ executionPlanState: ExecutionPlanState;
192
+ executionPlan: ExecutionPlan;
193
+ }> = (props) => {
194
+ const { executionPlanState, executionPlan } = props;
195
+ // NOTE: We only need to compute this once so we use lazy initial state syntax
196
+ // See https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
197
+ const [treeData, setTreeData] = useState<
198
+ TreeData<ExecutionPlanViewTreeNodeData | ExecutionNodeTreeNodeData>
199
+ >(() => getExecutionPlanTreeData(executionPlan));
200
+ const onNodeSelect = (
201
+ node: ExecutionPlanViewTreeNodeData | ExecutionNodeTreeNodeData,
202
+ ): void => {
203
+ if (node instanceof ExecutionPlanViewTreeNodeData) {
204
+ executionPlanState.transformMetadataToProtocolJson(node.executionPlan);
205
+ } else if (node instanceof ExecutionNodeTreeNodeData) {
206
+ executionPlanState.transformMetadataToProtocolJson(node.executionNode);
207
+ }
208
+ executionPlanState.setSelectedNode(node);
209
+ };
210
+
211
+ const onNodeExpand = (
212
+ node: ExecutionPlanViewTreeNodeData | ExecutionNodeTreeNodeData,
213
+ ): void => {
214
+ if (node.childrenIds?.length) {
215
+ node.isOpen = !node.isOpen;
216
+ if (node instanceof ExecutionPlanViewTreeNodeData) {
217
+ const rootNode = node.executionPlan.rootExecutionNode;
218
+ const rootNodeTreeNode = generateExecutionNodeTreeNodeData(
219
+ rootNode,
220
+ generateExecutionNodeLabel(rootNode),
221
+ node,
222
+ );
223
+ treeData.nodes.set(rootNodeTreeNode.id, rootNodeTreeNode);
224
+ } else if (node instanceof ExecutionNodeTreeNodeData) {
225
+ if (node.executionNode.executionNodes.length > 0) {
226
+ node.executionNode.executionNodes.forEach((exen) => {
227
+ const executionNodeTreeNode = generateExecutionNodeTreeNodeData(
228
+ exen,
229
+ generateExecutionNodeLabel(exen),
230
+ node,
231
+ );
232
+
233
+ treeData.nodes.set(executionNodeTreeNode.id, executionNodeTreeNode);
234
+ });
235
+ }
236
+ }
237
+ }
238
+
239
+ setTreeData({ ...treeData });
240
+ };
241
+
242
+ const getChildNodes = (
243
+ node: ExecutionPlanViewTreeNodeData | ExecutionNodeTreeNodeData,
244
+ ): (ExecutionPlanViewTreeNodeData | ExecutionNodeTreeNodeData)[] => {
245
+ if (!node.childrenIds || node.childrenIds.length === 0) {
246
+ return [];
247
+ }
248
+ const childrenNodes = node.childrenIds
249
+ .map((id) => treeData.nodes.get(id))
250
+ .filter(isNonNullable);
251
+
252
+ return childrenNodes;
253
+ };
254
+ return (
255
+ <TreeView
256
+ components={{
257
+ TreeNodeContainer: ExecutionNodeElementTreeNodeContainer,
258
+ }}
259
+ treeData={treeData}
260
+ getChildNodes={getChildNodes}
261
+ onNodeSelect={onNodeSelect}
262
+ innerProps={{
263
+ onNodeExpand,
264
+ }}
265
+ />
266
+ );
267
+ };
268
+
269
+ const ExecutionNodeViewer = observer(
270
+ (props: {
271
+ executionNode: ExecutionNode;
272
+ executionPlanState: ExecutionPlanState;
273
+ }) => {
274
+ const { executionNode, executionPlanState } = props;
275
+ if (executionNode instanceof SQLExecutionNode) {
276
+ return (
277
+ <SQLExecutionNodeViewer
278
+ query={executionNode.sqlQuery}
279
+ resultColumns={executionNode.resultColumns}
280
+ executionPlanState={executionPlanState}
281
+ />
282
+ );
283
+ }
284
+ return (
285
+ <BlankPanelContent>
286
+ <div className="execution-node-viewer__unsupported-view">
287
+ <div className="execution-node-viewer__unsupported-view__summary">
288
+ {`Can't display execution node`}
289
+ </div>
290
+ <button
291
+ className="btn--dark execution-node-viewer__unsupported-view__to-text-mode__btn"
292
+ onClick={(): void =>
293
+ executionPlanState.setViewMode(EXECUTION_PLAN_VIEW_MODE.JSON)
294
+ }
295
+ >
296
+ View JSON
297
+ </button>
298
+ </div>
299
+ </BlankPanelContent>
300
+ );
301
+ },
302
+ );
303
+
304
+ const ExecutionPlanViewPanel = observer(
305
+ (props: { displayData: string; executionPlanState: ExecutionPlanState }) => {
306
+ const { displayData, executionPlanState } = props;
307
+ let currentElement;
308
+ if (executionPlanState.selectedNode !== undefined) {
309
+ if (
310
+ executionPlanState.selectedNode instanceof ExecutionPlanViewTreeNodeData
311
+ ) {
312
+ currentElement = executionPlanState.selectedNode.executionPlan;
313
+ } else if (
314
+ executionPlanState.selectedNode instanceof ExecutionNodeTreeNodeData
315
+ ) {
316
+ currentElement = executionPlanState.selectedNode.executionNode;
317
+ }
318
+ }
319
+ const nativeViewModes = Object.values(EXECUTION_PLAN_VIEW_MODE);
320
+
321
+ return (
322
+ <div className="execution-plan-viewer__panel">
323
+ {executionPlanState.selectedNode !== undefined && (
324
+ <>
325
+ <div className="panel__header execution-plan-viewer__panel__header">
326
+ <div className="execution-plan-viewer__panel__header__tabs">
327
+ <button className="execution-plan-viewer__panel__header__tab execution-plan-viewer__panel__header__tab--active">
328
+ {executionPlanState.selectedNode.label}
329
+ </button>
330
+ </div>
331
+ <DropdownMenu
332
+ className="execution-plan-viewer__panel__view-mode"
333
+ content={
334
+ <MenuContent className="execution-plan-viewer__panel__view-mode__options execution-plan-viewer__panel__view-mode__options--with-group">
335
+ <div className="execution-plan-viewer__panel__view-mode__option__group execution-plan-viewer__panel__view-mode__option__group--native">
336
+ <div className="execution-plan-viewer__panel__view-mode__option__group__name">
337
+ native
338
+ </div>
339
+ <div className="execution-plan-viewer__panel__view-mode__option__group__options">
340
+ {nativeViewModes.map((mode) => (
341
+ <MenuContentItem
342
+ key={mode}
343
+ className="execution-plan-viewer__panel__view-mode__option"
344
+ onClick={(): void =>
345
+ executionPlanState.setViewMode(mode)
346
+ }
347
+ >
348
+ {mode}
349
+ </MenuContentItem>
350
+ ))}
351
+ </div>
352
+ </div>
353
+ </MenuContent>
354
+ }
355
+ menuProps={{
356
+ anchorOrigin: { vertical: 'bottom', horizontal: 'right' },
357
+ transformOrigin: { vertical: 'top', horizontal: 'right' },
358
+ }}
359
+ >
360
+ <button
361
+ className="execution-plan-viewer__panel__view-mode__type"
362
+ title="View as..."
363
+ >
364
+ <div className="execution-plan-viewer__panel__view-mode__type__label">
365
+ {executionPlanState.viewMode}
366
+ </div>
367
+ </button>
368
+ </DropdownMenu>
369
+ </div>
370
+ <div className="panel__content execution-plan-viewer__panel__content">
371
+ {executionPlanState.viewMode === EXECUTION_PLAN_VIEW_MODE.JSON &&
372
+ Boolean(displayData) && (
373
+ <TextInputEditor
374
+ inputValue={displayData}
375
+ isReadOnly={true}
376
+ language={EDITOR_LANGUAGE.JSON}
377
+ showMiniMap={false}
378
+ />
379
+ )}
380
+ {executionPlanState.viewMode ===
381
+ EXECUTION_PLAN_VIEW_MODE.FORM && (
382
+ <>
383
+ {currentElement instanceof ExecutionNode && (
384
+ <ExecutionNodeViewer
385
+ executionNode={currentElement}
386
+ executionPlanState={executionPlanState}
387
+ />
388
+ )}
389
+ {currentElement instanceof ExecutionPlan && (
390
+ <BlankPanelContent>
391
+ <div className="execution-plan-viewer__unsupported-view">
392
+ <div className="execution-plan-viewer__unsupported-view__summary">
393
+ {`Can't display full execution plan`}
394
+ </div>
395
+ <button
396
+ className="btn--dark execution-plan-viewer__unsupported-view__to-text-mode__btn"
397
+ onClick={(): void =>
398
+ executionPlanState.setViewMode(
399
+ EXECUTION_PLAN_VIEW_MODE.JSON,
400
+ )
401
+ }
402
+ >
403
+ View JSON
404
+ </button>
405
+ </div>
406
+ </BlankPanelContent>
407
+ )}
408
+ </>
409
+ )}
410
+ </div>
411
+ </>
412
+ )}
413
+ </div>
414
+ );
415
+ },
416
+ );
417
+
418
+ const ExecutionPlanViewerContent = observer(
419
+ (props: {
420
+ executionPlanState: ExecutionPlanState;
421
+ rawPlan: RawExecutionPlan;
422
+ }) => {
423
+ const { executionPlanState, rawPlan } = props;
424
+ const plan = executionPlanState.plan;
425
+
426
+ return (
427
+ <div className="execution-plan-viewer__content">
428
+ {plan ? (
429
+ <ResizablePanelGroup orientation="vertical">
430
+ <ResizablePanel size={300} minSize={300}>
431
+ <div className="panel execution-plan-viewer__explorer">
432
+ <div className="panel__header side-bar__header">
433
+ <div className="panel__header__title">
434
+ <div className="panel__header__title__content side-bar__header__title__content">
435
+ EXECUTION PLAN EXPLORER
436
+ </div>
437
+ </div>
438
+ </div>
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
+ <TextInputEditor
459
+ inputValue={JSON.stringify(rawPlan, undefined, TAB_SIZE)}
460
+ isReadOnly={true}
461
+ language={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
+ <div className="modal modal--dark editor-modal">
496
+ <div className="modal__header">
497
+ <div className="modal__title">Execution Plan</div>
498
+ </div>
499
+ <div className="modal__body">
500
+ {executionPlanState.debugText ? (
501
+ <ResizablePanelGroup orientation="horizontal">
502
+ <ResizablePanel minSize={100}>
503
+ <ExecutionPlanViewerContent
504
+ executionPlanState={executionPlanState}
505
+ rawPlan={rawPlan}
506
+ />
507
+ </ResizablePanel>
508
+ <ResizablePanelSplitter>
509
+ <ResizablePanelSplitterLine color="var(--color-dark-grey-200)" />
510
+ </ResizablePanelSplitter>
511
+ <ResizablePanel size={200} minSize={28}>
512
+ <div className="panel execution-plan-viewer__debug-panel">
513
+ <div className="panel__header">
514
+ <div className="panel__header__title">
515
+ <div className="panel__header__title__label">
516
+ DEBUG LOG
517
+ </div>
518
+ </div>
519
+ </div>
520
+ <div className="panel__content">
521
+ <TextInputEditor
522
+ inputValue={executionPlanState.debugText}
523
+ isReadOnly={true}
524
+ language={EDITOR_LANGUAGE.TEXT}
525
+ showMiniMap={true}
526
+ />
527
+ </div>
528
+ </div>
529
+ </ResizablePanel>
530
+ </ResizablePanelGroup>
531
+ ) : (
532
+ <ExecutionPlanViewerContent
533
+ executionPlanState={executionPlanState}
534
+ rawPlan={rawPlan}
535
+ />
536
+ )}
537
+ </div>
538
+ <div className="modal__footer">
539
+ <button
540
+ className="btn modal__footer__close-btn"
541
+ onClick={closePlanViewer}
542
+ >
543
+ Close
544
+ </button>
545
+ </div>
546
+ </div>
547
+ </Dialog>
548
+ );
549
+ },
550
+ );
@@ -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/shared/ExecutionPlanState.js';
19
+ import { format } from 'sql-formatter';
20
+ import type { SQLResultColumn } from '@finos/legend-graph';
21
+ import { TextInputEditor } from '../TextInputEditor.js';
22
+ import { EDITOR_LANGUAGE } from '../../../const.js';
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
+ <TextInputEditor
40
+ inputValue={format(query)}
41
+ isReadOnly={true}
42
+ language={EDITOR_LANGUAGE.SQL}
43
+ showMiniMap={false}
44
+ />
45
+ );
46
+ });
package/src/const.ts CHANGED
@@ -14,11 +14,6 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- export const DATE_TIME_FORMAT_WITH_MILLISECONDS =
18
- "yyyy-MM-dd'T'HH:mm:ss.SSSxxxx";
19
- export const DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ssxxxx";
20
- export const DATE_FORMAT = 'yyyy-MM-dd';
21
-
22
17
  export const TAB_SIZE = 2;
23
18
 
24
19
  export const MONOSPACED_FONT_FAMILY = 'Roboto Mono';
package/src/index.ts CHANGED
@@ -18,32 +18,21 @@ export * from './const.js';
18
18
 
19
19
  export * from './application/LegendApplicationPluginManager.js';
20
20
  export * from './application/LegendApplication.js';
21
-
22
21
  export * from './components/ApplicationStoreProvider.js';
23
22
  export * from './components/WebApplicationNavigatorProvider.js';
24
23
  export * from './components/LegendApplicationComponentFrameworkProvider.js';
25
24
  export * from './components/LegendApplicationNavigationContextServiceUtils.js';
26
-
27
25
  export * from './components/ApplicationStoreProviderTestUtils.js';
28
26
  export * from './components/WebApplicationNavigatorProviderTestUtils.js';
29
-
30
27
  // TODO: consider moving this to `LegendApplicationComponentFrameworkProvider`
31
28
  // once we think we can add virtual assistant support for all apps
32
29
  export * from './components/VirtualAssistant.js';
33
30
 
34
- export * from './components/DocumentationLink.js';
35
- export * from './components/TextInputEditor.js';
36
- export * from './components/LambdaEditor.js';
37
- export * from './components/BasicValueSpecificationEditor.js';
38
- export * from './components/LambdaParameterValuesEditor.js';
39
-
40
31
  export * from './stores/ApplicationStore.js';
41
32
  export * from './stores/ApplicationTelemetry.js';
42
33
  export * from './stores/ApplicationEvent.js';
43
34
  export * from './stores/LegendApplicationConfig.js';
44
35
  export { WebApplicationNavigator } from './stores/WebApplicationNavigator.js';
45
- export { LambdaEditorState } from './stores/LambdaEditorState.js';
46
- export * from './stores/PackageableElementOption.js';
47
36
  export * from './stores/LegendApplicationDocumentationService.js';
48
37
  export * from './stores/LegendApplicationEventService.js';
49
38
  export * from './stores/LegendApplicationAssistantService.js';
@@ -51,5 +40,19 @@ export * from './stores/LegendApplicationNavigationContextService.js';
51
40
  export * from './stores/LegendApplicationPlugin.js';
52
41
 
53
42
  export * from './stores/ApplicationStoreTestUtils.js';
54
- export * from './stores/ValueSpecificationModifierHelper.js';
55
- export * from './stores/LambdaParameterState.js';
43
+
44
+ // ------------------------------------------- Shared components -------------------------------------------
45
+
46
+ export * from './components/shared/DocumentationLink.js';
47
+ export * from './components/shared/TextInputEditor.js';
48
+ export * from './components/shared/LambdaEditor.js';
49
+ export * from './components/shared/BasicValueSpecificationEditor.js';
50
+ export * from './components/shared/LambdaParameterValuesEditor.js';
51
+ export * from './components/shared/PackageableElementOptionRenderer.js';
52
+ export * from './components/shared/execution-plan-viewer/ExecutionPlanViewer.js';
53
+
54
+ export { LambdaEditorState } from './stores/shared/LambdaEditorState.js';
55
+ export * from './stores/shared/PackageableElementOption.js';
56
+ export * from './stores/shared/LambdaParameterState.js';
57
+ export * from './stores/shared/ValueSpecificationModifierHelper.js';
58
+ export * from './stores/shared/ExecutionPlanState.js';
@@ -21,8 +21,10 @@ export enum APPLICATION_EVENT {
21
21
  ILLEGAL_APPLICATION_STATE_OCCURRED = 'application.error.illegal-state',
22
22
  APPLICATION_CONFIGURATION_FAILURE = 'application.configuration.failure',
23
23
 
24
- APPLICATION_DOCUMTENTION_LOAD_SKIPPED = 'application.load.documentation.skipped',
25
- APPLICATION_CONTEXTUAL_DOCUMTENTION_LOAD_SKIPPED = 'application.load.contextual-documentation.skipped',
24
+ APPLICATION_DOCUMENTATION_FETCH_FAILURE = 'application.fetch.documentation.failure',
25
+ APPLICATION_DOCUMENTATION_LOAD_SKIPPED = 'application.load.documentation.skipped',
26
+ APPLICATION_DOCUMENTATION_REQUIREMENT_CHECK_FAILURE = 'application.load.documentation.requirement-check.failure',
27
+ APPLICATION_CONTEXTUAL_DOCUMENTATION_LOAD_SKIPPED = 'application.load.contextual-documentation.skipped',
26
28
 
27
29
  APPLICATION_LOADED = 'application.load.success',
28
30
  APPLICATION_LOAD_FAILURE = 'application.load.failure',
@@ -147,11 +147,13 @@ export class ApplicationStore<T extends LegendApplicationConfig> {
147
147
  this.config = config;
148
148
  this.navigator = navigator;
149
149
  this.pluginManager = pluginManager;
150
+ // NOTE: set the logger first so other loading could use the configured logger
151
+ this.log.registerPlugins(pluginManager.getLoggerPlugins());
152
+
150
153
  this.navigationContextService =
151
154
  new LegendApplicationNavigationContextService();
152
155
  this.documentationService = new LegendApplicationDocumentationService(this);
153
156
  this.assistantService = new LegendApplicationAssistantService(this);
154
- this.log.registerPlugins(pluginManager.getLoggerPlugins());
155
157
  this.telemetryService.registerPlugins(
156
158
  pluginManager.getTelemetryServicePlugins(),
157
159
  );