@finos/legend-application-studio 26.1.1 → 26.1.3

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 (188) hide show
  1. package/lib/__lib__/LegendStudioApplicationNavigationContext.d.ts +2 -1
  2. package/lib/__lib__/LegendStudioApplicationNavigationContext.d.ts.map +1 -1
  3. package/lib/__lib__/LegendStudioApplicationNavigationContext.js +1 -0
  4. package/lib/__lib__/LegendStudioApplicationNavigationContext.js.map +1 -1
  5. package/lib/__lib__/STO_Relational_LegendStudioCommand.d.ts +21 -0
  6. package/lib/__lib__/STO_Relational_LegendStudioCommand.d.ts.map +1 -0
  7. package/lib/__lib__/STO_Relational_LegendStudioCommand.js +28 -0
  8. package/lib/__lib__/STO_Relational_LegendStudioCommand.js.map +1 -0
  9. package/lib/application/LegendStudioApplicationConfig.d.ts +5 -0
  10. package/lib/application/LegendStudioApplicationConfig.d.ts.map +1 -1
  11. package/lib/application/LegendStudioApplicationConfig.js +6 -0
  12. package/lib/application/LegendStudioApplicationConfig.js.map +1 -1
  13. package/lib/components/ElementIconUtils.d.ts +13 -2
  14. package/lib/components/ElementIconUtils.d.ts.map +1 -1
  15. package/lib/components/ElementIconUtils.js +15 -6
  16. package/lib/components/ElementIconUtils.js.map +1 -1
  17. package/lib/components/editor/Editor.d.ts.map +1 -1
  18. package/lib/components/editor/Editor.js +2 -1
  19. package/lib/components/editor/Editor.js.map +1 -1
  20. package/lib/components/editor/QuickInput.d.ts +19 -0
  21. package/lib/components/editor/QuickInput.d.ts.map +1 -0
  22. package/lib/components/editor/QuickInput.js +51 -0
  23. package/lib/components/editor/QuickInput.js.map +1 -0
  24. package/lib/components/editor/__test-utils__/EditorComponentTestUtils.d.ts.map +1 -1
  25. package/lib/components/editor/__test-utils__/EditorComponentTestUtils.js +5 -1
  26. package/lib/components/editor/__test-utils__/EditorComponentTestUtils.js.map +1 -1
  27. package/lib/components/editor/command-center/ProjectSearchCommand.js +3 -3
  28. package/lib/components/editor/command-center/ProjectSearchCommand.js.map +1 -1
  29. package/lib/components/editor/editor-group/EditorGroup.d.ts.map +1 -1
  30. package/lib/components/editor/editor-group/EditorGroup.js +11 -4
  31. package/lib/components/editor/editor-group/EditorGroup.js.map +1 -1
  32. package/lib/components/editor/editor-group/FunctionEditor.d.ts.map +1 -1
  33. package/lib/components/editor/editor-group/FunctionEditor.js +30 -8
  34. package/lib/components/editor/editor-group/FunctionEditor.js.map +1 -1
  35. package/lib/components/editor/editor-group/GenerationSpecificationEditor.js +1 -1
  36. package/lib/components/editor/editor-group/GenerationSpecificationEditor.js.map +1 -1
  37. package/lib/components/editor/editor-group/GrammarTextEditor.d.ts.map +1 -1
  38. package/lib/components/editor/editor-group/GrammarTextEditor.js +99 -100
  39. package/lib/components/editor/editor-group/GrammarTextEditor.js.map +1 -1
  40. package/lib/components/editor/editor-group/INTERNAL__UnknownFunctionActivatorEdtior.d.ts.map +1 -1
  41. package/lib/components/editor/editor-group/INTERNAL__UnknownFunctionActivatorEdtior.js +5 -5
  42. package/lib/components/editor/editor-group/INTERNAL__UnknownFunctionActivatorEdtior.js.map +1 -1
  43. package/lib/components/editor/editor-group/RuntimeEditor.js +1 -1
  44. package/lib/components/editor/editor-group/RuntimeEditor.js.map +1 -1
  45. package/lib/components/editor/editor-group/connection-editor/DatabaseBuilder.d.ts.map +1 -1
  46. package/lib/components/editor/editor-group/connection-editor/DatabaseBuilder.js +27 -19
  47. package/lib/components/editor/editor-group/connection-editor/DatabaseBuilder.js.map +1 -1
  48. package/lib/components/editor/editor-group/connection-editor/RelationalDatabaseConnectionEditor.js +2 -2
  49. package/lib/components/editor/editor-group/diff-editor/EntityChangeConflictEditor.d.ts.map +1 -1
  50. package/lib/components/editor/editor-group/diff-editor/EntityChangeConflictEditor.js +5 -5
  51. package/lib/components/editor/editor-group/diff-editor/EntityChangeConflictEditor.js.map +1 -1
  52. package/lib/components/editor/editor-group/element-generation-editor/FileGenerationEditor.js +2 -2
  53. package/lib/components/editor/editor-group/element-generation-editor/FileGenerationEditor.js.map +1 -1
  54. package/lib/components/editor/editor-group/external-format-editor/DSL_ExternalFormat_BindingElementEditor.js +1 -1
  55. package/lib/components/editor/editor-group/external-format-editor/DSL_ExternalFormat_BindingElementEditor.js.map +1 -1
  56. package/lib/components/editor/editor-group/mapping-editor/ClassMappingEditor.js +1 -1
  57. package/lib/components/editor/editor-group/mapping-editor/ClassMappingEditor.js.map +1 -1
  58. package/lib/components/editor/editor-group/mapping-editor/MappingExplorer.js +2 -2
  59. package/lib/components/editor/editor-group/mapping-editor/MappingExplorer.js.map +1 -1
  60. package/lib/components/editor/editor-group/mapping-editor/PropertyMappingsEditor.js +1 -1
  61. package/lib/components/editor/editor-group/mapping-editor/PropertyMappingsEditor.js.map +1 -1
  62. package/lib/components/editor/editor-group/service-editor/ServiceExecutionQueryEditor.d.ts.map +1 -1
  63. package/lib/components/editor/editor-group/service-editor/ServiceExecutionQueryEditor.js +4 -6
  64. package/lib/components/editor/editor-group/service-editor/ServiceExecutionQueryEditor.js.map +1 -1
  65. package/lib/components/editor/editor-group/service-editor/testable/ServiceTestDataEditor.js +2 -2
  66. package/lib/components/editor/editor-group/service-editor/testable/ServiceTestDataEditor.js.map +1 -1
  67. package/lib/components/editor/editor-group/service-editor/testable/ServiceTestableEditor.d.ts.map +1 -1
  68. package/lib/components/editor/editor-group/service-editor/testable/ServiceTestableEditor.js +2 -2
  69. package/lib/components/editor/editor-group/service-editor/testable/ServiceTestableEditor.js.map +1 -1
  70. package/lib/components/editor/editor-group/uml-editor/AssociationEditor.js +2 -2
  71. package/lib/components/editor/editor-group/uml-editor/AssociationEditor.js.map +1 -1
  72. package/lib/components/editor/editor-group/uml-editor/ClassEditor.js +4 -4
  73. package/lib/components/editor/editor-group/uml-editor/ClassEditor.js.map +1 -1
  74. package/lib/components/editor/panel-group/PanelGroup.d.ts +2 -0
  75. package/lib/components/editor/panel-group/PanelGroup.d.ts.map +1 -1
  76. package/lib/components/editor/panel-group/PanelGroup.js +18 -3
  77. package/lib/components/editor/panel-group/PanelGroup.js.map +1 -1
  78. package/lib/components/editor/panel-group/SQLPlaygroundPanel.d.ts +26 -0
  79. package/lib/components/editor/panel-group/SQLPlaygroundPanel.d.ts.map +1 -0
  80. package/lib/components/editor/panel-group/SQLPlaygroundPanel.js +339 -0
  81. package/lib/components/editor/panel-group/SQLPlaygroundPanel.js.map +1 -0
  82. package/lib/components/editor/side-bar/Explorer.js +5 -6
  83. package/lib/components/editor/side-bar/Explorer.js.map +1 -1
  84. package/lib/components/editor/side-bar/testable/GlobalTestRunner.js +1 -1
  85. package/lib/components/editor/side-bar/testable/GlobalTestRunner.js.map +1 -1
  86. package/lib/components/extensions/Core_LegendStudioApplicationPlugin.d.ts.map +1 -1
  87. package/lib/components/extensions/Core_LegendStudioApplicationPlugin.js +5 -1
  88. package/lib/components/extensions/Core_LegendStudioApplicationPlugin.js.map +1 -1
  89. package/lib/index.css +2 -2
  90. package/lib/index.css.map +1 -1
  91. package/lib/package.json +2 -2
  92. package/lib/stores/editor/EditorConfig.d.ts +2 -1
  93. package/lib/stores/editor/EditorConfig.d.ts.map +1 -1
  94. package/lib/stores/editor/EditorConfig.js +1 -0
  95. package/lib/stores/editor/EditorConfig.js.map +1 -1
  96. package/lib/stores/editor/EditorStore.d.ts +8 -2
  97. package/lib/stores/editor/EditorStore.d.ts.map +1 -1
  98. package/lib/stores/editor/EditorStore.js +19 -4
  99. package/lib/stores/editor/EditorStore.js.map +1 -1
  100. package/lib/stores/editor/GraphEditFormModeState.d.ts.map +1 -1
  101. package/lib/stores/editor/GraphEditFormModeState.js +1 -0
  102. package/lib/stores/editor/GraphEditFormModeState.js.map +1 -1
  103. package/lib/stores/editor/QuickInputState.d.ts +28 -0
  104. package/lib/stores/editor/QuickInputState.d.ts.map +1 -0
  105. package/lib/stores/editor/QuickInputState.js +17 -0
  106. package/lib/stores/editor/QuickInputState.js.map +1 -0
  107. package/lib/stores/editor/editor-state/element-editor-state/FunctionEditorState.d.ts +22 -2
  108. package/lib/stores/editor/editor-state/element-editor-state/FunctionEditorState.d.ts.map +1 -1
  109. package/lib/stores/editor/editor-state/element-editor-state/FunctionEditorState.js +181 -4
  110. package/lib/stores/editor/editor-state/element-editor-state/FunctionEditorState.js.map +1 -1
  111. package/lib/stores/editor/editor-state/element-editor-state/ProtocolValueBuilderState.d.ts.map +1 -1
  112. package/lib/stores/editor/editor-state/element-editor-state/ProtocolValueBuilderState.js +6 -2
  113. package/lib/stores/editor/editor-state/element-editor-state/ProtocolValueBuilderState.js.map +1 -1
  114. package/lib/stores/editor/editor-state/element-editor-state/connection/DatabaseBuilderState.d.ts +19 -18
  115. package/lib/stores/editor/editor-state/element-editor-state/connection/DatabaseBuilderState.d.ts.map +1 -1
  116. package/lib/stores/editor/editor-state/element-editor-state/connection/DatabaseBuilderState.js +167 -157
  117. package/lib/stores/editor/editor-state/element-editor-state/connection/DatabaseBuilderState.js.map +1 -1
  118. package/lib/stores/editor/editor-state/element-editor-state/mapping/MappingElementDecorator.d.ts +3 -3
  119. package/lib/stores/editor/editor-state/element-editor-state/mapping/MappingElementDecorator.d.ts.map +1 -1
  120. package/lib/stores/editor/editor-state/element-editor-state/mapping/MappingElementDecorator.js +2 -2
  121. package/lib/stores/editor/editor-state/element-editor-state/mapping/MappingElementDecorator.js.map +1 -1
  122. package/lib/stores/editor/editor-state/element-editor-state/service/ServiceExecutionState.d.ts +8 -9
  123. package/lib/stores/editor/editor-state/element-editor-state/service/ServiceExecutionState.d.ts.map +1 -1
  124. package/lib/stores/editor/editor-state/element-editor-state/service/ServiceExecutionState.js +9 -14
  125. package/lib/stores/editor/editor-state/element-editor-state/service/ServiceExecutionState.js.map +1 -1
  126. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestDataState.d.ts +4 -4
  127. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestDataState.d.ts.map +1 -1
  128. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestDataState.js +7 -8
  129. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestDataState.js.map +1 -1
  130. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestableState.d.ts +1 -1
  131. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestableState.d.ts.map +1 -1
  132. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestableState.js +3 -1
  133. package/lib/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestableState.js.map +1 -1
  134. package/lib/stores/editor/panel-group/SQLPlaygroundPanelState.d.ts +82 -0
  135. package/lib/stores/editor/panel-group/SQLPlaygroundPanelState.d.ts.map +1 -0
  136. package/lib/stores/editor/panel-group/SQLPlaygroundPanelState.js +328 -0
  137. package/lib/stores/editor/panel-group/SQLPlaygroundPanelState.js.map +1 -0
  138. package/lib/stores/graph-modifier/DSL_Service_GraphModifierHelper.d.ts +1 -1
  139. package/lib/stores/graph-modifier/DSL_Service_GraphModifierHelper.d.ts.map +1 -1
  140. package/lib/stores/graph-modifier/DSL_Service_GraphModifierHelper.js +8 -2
  141. package/lib/stores/graph-modifier/DSL_Service_GraphModifierHelper.js.map +1 -1
  142. package/package.json +12 -12
  143. package/src/__lib__/LegendStudioApplicationNavigationContext.ts +2 -0
  144. package/src/__lib__/STO_Relational_LegendStudioCommand.ts +30 -0
  145. package/src/application/LegendStudioApplicationConfig.ts +7 -0
  146. package/src/components/ElementIconUtils.tsx +23 -8
  147. package/src/components/editor/Editor.tsx +2 -0
  148. package/src/components/editor/QuickInput.tsx +91 -0
  149. package/src/components/editor/__test-utils__/EditorComponentTestUtils.tsx +5 -1
  150. package/src/components/editor/command-center/ProjectSearchCommand.tsx +4 -4
  151. package/src/components/editor/editor-group/EditorGroup.tsx +41 -1
  152. package/src/components/editor/editor-group/FunctionEditor.tsx +145 -5
  153. package/src/components/editor/editor-group/GenerationSpecificationEditor.tsx +1 -1
  154. package/src/components/editor/editor-group/GrammarTextEditor.tsx +134 -131
  155. package/src/components/editor/editor-group/INTERNAL__UnknownFunctionActivatorEdtior.tsx +26 -13
  156. package/src/components/editor/editor-group/RuntimeEditor.tsx +1 -1
  157. package/src/components/editor/editor-group/connection-editor/DatabaseBuilder.tsx +193 -150
  158. package/src/components/editor/editor-group/connection-editor/RelationalDatabaseConnectionEditor.tsx +2 -2
  159. package/src/components/editor/editor-group/diff-editor/EntityChangeConflictEditor.tsx +6 -5
  160. package/src/components/editor/editor-group/element-generation-editor/FileGenerationEditor.tsx +1 -1
  161. package/src/components/editor/editor-group/external-format-editor/DSL_ExternalFormat_BindingElementEditor.tsx +1 -1
  162. package/src/components/editor/editor-group/mapping-editor/ClassMappingEditor.tsx +1 -1
  163. package/src/components/editor/editor-group/mapping-editor/MappingExplorer.tsx +2 -2
  164. package/src/components/editor/editor-group/mapping-editor/PropertyMappingsEditor.tsx +1 -1
  165. package/src/components/editor/editor-group/service-editor/ServiceExecutionQueryEditor.tsx +8 -10
  166. package/src/components/editor/editor-group/service-editor/testable/ServiceTestDataEditor.tsx +2 -2
  167. package/src/components/editor/editor-group/service-editor/testable/ServiceTestableEditor.tsx +9 -3
  168. package/src/components/editor/editor-group/uml-editor/AssociationEditor.tsx +2 -2
  169. package/src/components/editor/editor-group/uml-editor/ClassEditor.tsx +4 -4
  170. package/src/components/editor/panel-group/PanelGroup.tsx +35 -3
  171. package/src/components/editor/panel-group/SQLPlaygroundPanel.tsx +730 -0
  172. package/src/components/editor/side-bar/Explorer.tsx +5 -5
  173. package/src/components/editor/side-bar/testable/GlobalTestRunner.tsx +1 -1
  174. package/src/components/extensions/Core_LegendStudioApplicationPlugin.tsx +9 -3
  175. package/src/stores/editor/EditorConfig.ts +1 -0
  176. package/src/stores/editor/EditorStore.ts +23 -4
  177. package/src/stores/editor/GraphEditFormModeState.ts +1 -0
  178. package/src/stores/editor/QuickInputState.ts +24 -0
  179. package/src/stores/editor/editor-state/element-editor-state/FunctionEditorState.ts +317 -3
  180. package/src/stores/editor/editor-state/element-editor-state/ProtocolValueBuilderState.ts +11 -2
  181. package/src/stores/editor/editor-state/element-editor-state/connection/DatabaseBuilderState.ts +262 -249
  182. package/src/stores/editor/editor-state/element-editor-state/mapping/MappingElementDecorator.ts +5 -5
  183. package/src/stores/editor/editor-state/element-editor-state/service/ServiceExecutionState.ts +19 -23
  184. package/src/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestDataState.ts +9 -9
  185. package/src/stores/editor/editor-state/element-editor-state/service/testable/ServiceTestableState.ts +4 -2
  186. package/src/stores/editor/panel-group/SQLPlaygroundPanelState.ts +485 -0
  187. package/src/stores/graph-modifier/DSL_Service_GraphModifierHelper.ts +13 -1
  188. package/tsconfig.json +5 -0
@@ -0,0 +1,730 @@
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 {
19
+ type TreeNodeContainerProps,
20
+ ResizablePanelGroup,
21
+ ResizablePanel,
22
+ ResizablePanelSplitter,
23
+ clsx,
24
+ TreeView,
25
+ PURE_DatabaseSchemaIcon,
26
+ PURE_DatabaseTableIcon,
27
+ ChevronDownIcon,
28
+ ChevronRightIcon,
29
+ KeyIcon,
30
+ CustomSelectorInput,
31
+ type SelectComponent,
32
+ createFilter,
33
+ PURE_ConnectionIcon,
34
+ BlankPanelPlaceholder,
35
+ PanelDropZone,
36
+ ResizablePanelSplitterLine,
37
+ PlayIcon,
38
+ PanelLoadingIndicator,
39
+ BlankPanelContent,
40
+ TrashIcon,
41
+ } from '@finos/legend-art';
42
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
43
+ import {
44
+ useApplicationStore,
45
+ useCommands,
46
+ useConditionedApplicationNavigationContext,
47
+ } from '@finos/legend-application';
48
+ import { flowResult } from 'mobx';
49
+ import {
50
+ CODE_EDITOR_LANGUAGE,
51
+ CODE_EDITOR_THEME,
52
+ getBaseCodeEditorOptions,
53
+ } from '@finos/legend-lego/code-editor';
54
+ import {
55
+ editor as monacoEditorAPI,
56
+ languages as monacoLanguagesAPI,
57
+ type IDisposable,
58
+ type IPosition,
59
+ } from 'monaco-editor';
60
+ import {
61
+ PackageableConnection,
62
+ RelationalDatabaseConnection,
63
+ guaranteeRelationalDatabaseConnection,
64
+ stringifyDataType,
65
+ } from '@finos/legend-graph';
66
+ import { LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY } from '../../../__lib__/LegendStudioApplicationNavigationContext.js';
67
+ import {
68
+ DatabaseSchemaExplorerTreeColumnNodeData,
69
+ DatabaseSchemaExplorerTreeSchemaNodeData,
70
+ DatabaseSchemaExplorerTreeTableNodeData,
71
+ type DatabaseSchemaExplorerTreeData,
72
+ type DatabaseSchemaExplorerTreeNodeData,
73
+ type SQLPlaygroundPanelState,
74
+ } from '../../../stores/editor/panel-group/SQLPlaygroundPanelState.js';
75
+ import { renderColumnTypeIcon } from '../editor-group/connection-editor/DatabaseEditorHelper.js';
76
+ import { useEditorStore } from '../EditorStoreProvider.js';
77
+ import { PANEL_MODE } from '../../../stores/editor/EditorConfig.js';
78
+ import { useDrag, useDrop } from 'react-dnd';
79
+ import {
80
+ CORE_DND_TYPE,
81
+ type ElementDragSource,
82
+ } from '../../../stores/editor/utils/DnDUtils.js';
83
+ import { DataGrid } from '@finos/legend-lego/data-grid';
84
+ import {
85
+ getNonNullableEntry,
86
+ getNullableEntry,
87
+ getNullableLastEntry,
88
+ isNonNullable,
89
+ isString,
90
+ parseCSVString,
91
+ uniqBy,
92
+ } from '@finos/legend-shared';
93
+
94
+ const getDatabaseSchemaNodeIcon = (
95
+ node: DatabaseSchemaExplorerTreeNodeData,
96
+ ): React.ReactNode => {
97
+ if (node instanceof DatabaseSchemaExplorerTreeSchemaNodeData) {
98
+ return (
99
+ <div className="sql-playground__database-schema-explorer-tree__icon--schema">
100
+ <PURE_DatabaseSchemaIcon />
101
+ </div>
102
+ );
103
+ } else if (node instanceof DatabaseSchemaExplorerTreeTableNodeData) {
104
+ return (
105
+ <div className="sql-playground__database-schema-explorer-tree__icon--table">
106
+ <PURE_DatabaseTableIcon />
107
+ </div>
108
+ );
109
+ } else if (node instanceof DatabaseSchemaExplorerTreeColumnNodeData) {
110
+ return renderColumnTypeIcon(node.column.type);
111
+ }
112
+ return null;
113
+ };
114
+
115
+ const DATABASE_NODE_DND_TYPE = 'DATABASE_NODE_DND_TYPE';
116
+ type DatabaseNodeDragType = { text: string };
117
+
118
+ const DatabaseSchemaExplorerTreeNodeContainer: React.FC<
119
+ TreeNodeContainerProps<
120
+ DatabaseSchemaExplorerTreeNodeData,
121
+ {
122
+ // empty
123
+ }
124
+ >
125
+ > = (props) => {
126
+ const { node, level, stepPaddingInRem, onNodeSelect } = props;
127
+ const isExpandable =
128
+ Boolean(!node.childrenIds || node.childrenIds.length) &&
129
+ !(node instanceof DatabaseSchemaExplorerTreeColumnNodeData);
130
+ const nodeExpandIcon = isExpandable ? (
131
+ node.isOpen ? (
132
+ <ChevronDownIcon />
133
+ ) : (
134
+ <ChevronRightIcon />
135
+ )
136
+ ) : (
137
+ <div />
138
+ );
139
+ const [, nodeDragRef] = useDrag<DatabaseNodeDragType>(
140
+ () => ({
141
+ type: DATABASE_NODE_DND_TYPE,
142
+ item: {
143
+ text:
144
+ node instanceof DatabaseSchemaExplorerTreeTableNodeData
145
+ ? `${node.owner.name}.${node.label}`
146
+ : node.label,
147
+ },
148
+ }),
149
+ [node],
150
+ );
151
+ const nodeTypeIcon = getDatabaseSchemaNodeIcon(node);
152
+ const toggleExpandNode = (): void => onNodeSelect?.(node);
153
+ const isPrimaryKeyColumn =
154
+ node instanceof DatabaseSchemaExplorerTreeColumnNodeData &&
155
+ node.owner.primaryKey.includes(node.column);
156
+
157
+ return (
158
+ <div
159
+ className={clsx('tree-view__node__container')}
160
+ style={{
161
+ paddingLeft: `${level * (stepPaddingInRem ?? 1)}rem`,
162
+ display: 'flex',
163
+ }}
164
+ ref={nodeDragRef}
165
+ onClick={toggleExpandNode}
166
+ >
167
+ <div className="tree-view__node__icon sql-playground__database-schema-explorer-tree__node__icon__group">
168
+ <div className="sql-playground__database-schema-explorer-tree__expand-icon">
169
+ {nodeExpandIcon}
170
+ </div>
171
+ <div className="sql-playground__database-schema-explorer-tree__type-icon">
172
+ {nodeTypeIcon}
173
+ </div>
174
+ </div>
175
+ <div className="tree-view__node__label sql-playground__database-schema-explorer-tree__node__label">
176
+ {node.label}
177
+ {node instanceof DatabaseSchemaExplorerTreeColumnNodeData && (
178
+ <div className="sql-playground__database-schema-explorer-tree__node__type">
179
+ <div className="sql-playground__database-schema-explorer-tree__node__type__label">
180
+ {stringifyDataType(node.column.type)}
181
+ </div>
182
+ </div>
183
+ )}
184
+ {isPrimaryKeyColumn && (
185
+ <div
186
+ className="sql-playground__database-schema-explorer-tree__node__pk"
187
+ title="Primary Key"
188
+ >
189
+ <KeyIcon />
190
+ </div>
191
+ )}
192
+ </div>
193
+ </div>
194
+ );
195
+ };
196
+
197
+ export const DatabaseSchemaExplorer = observer(
198
+ (props: {
199
+ treeData: DatabaseSchemaExplorerTreeData;
200
+ playgroundState: SQLPlaygroundPanelState;
201
+ }) => {
202
+ const { treeData, playgroundState } = props;
203
+ const applicationStore = useApplicationStore();
204
+
205
+ const onNodeSelect = (node: DatabaseSchemaExplorerTreeNodeData): void => {
206
+ flowResult(playgroundState.onNodeSelect(node, treeData)).catch(
207
+ applicationStore.alertUnhandledError,
208
+ );
209
+ };
210
+
211
+ const getChildNodes = (
212
+ node: DatabaseSchemaExplorerTreeNodeData,
213
+ ): DatabaseSchemaExplorerTreeNodeData[] =>
214
+ playgroundState
215
+ .getChildNodes(node, treeData)
216
+ ?.sort((a, b) => a.label.localeCompare(b.label)) ?? [];
217
+
218
+ return (
219
+ <TreeView
220
+ className="sql-playground__database-schema-explorer-tree"
221
+ components={{
222
+ TreeNodeContainer: DatabaseSchemaExplorerTreeNodeContainer,
223
+ }}
224
+ innerProps={{}}
225
+ treeData={treeData}
226
+ onNodeSelect={onNodeSelect}
227
+ getChildNodes={getChildNodes}
228
+ />
229
+ );
230
+ },
231
+ );
232
+
233
+ type RelationalDatabaseConnectionOption = {
234
+ label: React.ReactNode;
235
+ value: PackageableConnection;
236
+ };
237
+ const buildRelationalDatabaseConnectionOption = (
238
+ connection: PackageableConnection,
239
+ ): RelationalDatabaseConnectionOption => {
240
+ const connectionValue = guaranteeRelationalDatabaseConnection(connection);
241
+ return {
242
+ value: connection,
243
+ label: (
244
+ <div className="sql-playground__config__connection-selector__option">
245
+ <div className="sql-playground__config__connection-selector__option__label">
246
+ {connection.name}
247
+ </div>
248
+ <div className="sql-playground__config__connection-selector__option__type">
249
+ {connectionValue.type}
250
+ </div>
251
+ </div>
252
+ ),
253
+ };
254
+ };
255
+
256
+ // List of most popular SQL keywords
257
+ // See https://www.w3schools.com/sql/sql_ref_keywords.asp
258
+ const SQL_KEYWORDS = [
259
+ 'AND',
260
+ 'AS',
261
+ 'ASC',
262
+ 'BETWEEN',
263
+ 'DESC',
264
+ 'DISTINCT',
265
+ 'EXEC',
266
+ 'EXISTS',
267
+ 'FROM',
268
+ 'FULL OUTER JOIN',
269
+ 'GROUP BY',
270
+ 'HAVING',
271
+ 'IN',
272
+ 'INNER JOIN',
273
+ 'IS NULL',
274
+ 'IS NOT NULL',
275
+ 'JOIN',
276
+ 'LEFT JOIN',
277
+ 'LIKE',
278
+ 'LIMIT',
279
+ 'NOT',
280
+ 'NOT NULL',
281
+ 'OR',
282
+ 'ORDER BY',
283
+ 'OUTER JOIN',
284
+ 'RIGHT JOIN',
285
+ 'SELECT',
286
+ 'SELECT DISTINCT',
287
+ 'SELECT INTO',
288
+ 'SELECT TOP',
289
+ 'TOP',
290
+ 'UNION',
291
+ 'UNION ALL',
292
+ 'UNIQUE',
293
+ 'WHERE',
294
+ ];
295
+
296
+ const getKeywordSuggestions = async (
297
+ position: IPosition,
298
+ model: monacoEditorAPI.ITextModel,
299
+ ): Promise<monacoLanguagesAPI.CompletionItem[]> =>
300
+ SQL_KEYWORDS.map(
301
+ (keyword) =>
302
+ ({
303
+ label: keyword,
304
+ kind: monacoLanguagesAPI.CompletionItemKind.Keyword,
305
+ insertTextRules:
306
+ monacoLanguagesAPI.CompletionItemInsertTextRule.InsertAsSnippet,
307
+ insertText: `${keyword} `,
308
+ } as monacoLanguagesAPI.CompletionItem),
309
+ );
310
+
311
+ const getDatabaseSchemaEntities = async (
312
+ position: IPosition,
313
+ model: monacoEditorAPI.ITextModel,
314
+ playgroundState: SQLPlaygroundPanelState,
315
+ ): Promise<monacoLanguagesAPI.CompletionItem[]> => {
316
+ if (playgroundState.treeData) {
317
+ return uniqBy(
318
+ Array.from(playgroundState.treeData.nodes.values()).map(
319
+ (value) =>
320
+ ({
321
+ label: value.label,
322
+ kind: monacoLanguagesAPI.CompletionItemKind.Field,
323
+ insertTextRules:
324
+ monacoLanguagesAPI.CompletionItemInsertTextRule.InsertAsSnippet,
325
+ insertText: `${value.label} `,
326
+ } as monacoLanguagesAPI.CompletionItem),
327
+ ),
328
+ (val) => val.label,
329
+ );
330
+ }
331
+ return [];
332
+ };
333
+
334
+ const PlaygroundSQLCodeEditor = observer(() => {
335
+ const editorStore = useEditorStore();
336
+ const playgroundState = editorStore.sqlPlaygroundState;
337
+ const applicationStore = useApplicationStore();
338
+ const codeEditorRef = useRef<HTMLDivElement>(null);
339
+ const [editor, setEditor] = useState<
340
+ monacoEditorAPI.IStandaloneCodeEditor | undefined
341
+ >();
342
+ const sqlIdentifierSuggestionProviderDisposer = useRef<
343
+ IDisposable | undefined
344
+ >(undefined);
345
+
346
+ const executeRawSQL = (): void => {
347
+ flowResult(playgroundState.executeRawSQL()).catch(
348
+ applicationStore.alertUnhandledError,
349
+ );
350
+ };
351
+ const reset = (): void => {
352
+ playgroundState.resetSQL();
353
+ };
354
+
355
+ useEffect(() => {
356
+ if (!editor && codeEditorRef.current) {
357
+ const element = codeEditorRef.current;
358
+ const newEditor = monacoEditorAPI.create(element, {
359
+ ...getBaseCodeEditorOptions(),
360
+ theme: CODE_EDITOR_THEME.DEFAULT_DARK,
361
+ language: CODE_EDITOR_LANGUAGE.SQL,
362
+ padding: {
363
+ top: 10,
364
+ },
365
+ });
366
+
367
+ newEditor.onDidChangeModelContent(() => {
368
+ const currentVal = newEditor.getValue();
369
+ playgroundState.setSQLText(currentVal);
370
+ });
371
+
372
+ // Restore the editor model and view state
373
+ newEditor.setModel(playgroundState.sqlEditorTextModel);
374
+ if (playgroundState.sqlEditorViewState) {
375
+ newEditor.restoreViewState(playgroundState.sqlEditorViewState);
376
+ }
377
+ newEditor.focus(); // focus on the editor initially
378
+ playgroundState.setSQLEditor(newEditor);
379
+ setEditor(newEditor);
380
+ }
381
+ }, [playgroundState, applicationStore, editor]);
382
+
383
+ useCommands(playgroundState);
384
+
385
+ if (editor) {
386
+ sqlIdentifierSuggestionProviderDisposer.current?.dispose();
387
+ sqlIdentifierSuggestionProviderDisposer.current =
388
+ monacoLanguagesAPI.registerCompletionItemProvider(
389
+ CODE_EDITOR_LANGUAGE.SQL,
390
+ {
391
+ triggerCharacters: [],
392
+ provideCompletionItems: async (model, position, context) => {
393
+ let suggestions: monacoLanguagesAPI.CompletionItem[] = [];
394
+ if (
395
+ context.triggerKind ===
396
+ monacoLanguagesAPI.CompletionTriggerKind.Invoke
397
+ ) {
398
+ // keywords
399
+ suggestions = suggestions.concat(
400
+ await getKeywordSuggestions(position, model),
401
+ );
402
+
403
+ // database schema entities
404
+ suggestions = suggestions.concat(
405
+ await getDatabaseSchemaEntities(
406
+ position,
407
+ model,
408
+ playgroundState,
409
+ ),
410
+ );
411
+ }
412
+
413
+ return { suggestions };
414
+ },
415
+ },
416
+ );
417
+ }
418
+
419
+ // clean up
420
+ useEffect(
421
+ () => (): void => {
422
+ if (editor) {
423
+ // persist editor view state (cursor, scroll, etc.) to restore on re-open
424
+ playgroundState.setSQLEditorViewState(
425
+ editor.saveViewState() ?? undefined,
426
+ );
427
+ editor.dispose();
428
+
429
+ // Dispose the providers properly to avoid ending up with duplicated suggestions
430
+ sqlIdentifierSuggestionProviderDisposer.current?.dispose();
431
+ }
432
+ },
433
+ [playgroundState, editor],
434
+ );
435
+
436
+ const handleDatabaseNodeDrop = useCallback(
437
+ (item: DatabaseNodeDragType): void => {
438
+ if (isString(item.text)) {
439
+ if (playgroundState.sqlEditor) {
440
+ const currentValue = playgroundState.sqlEditorTextModel.getValue();
441
+ const lines = currentValue.split('\n');
442
+ const position = playgroundState.sqlEditor.getPosition() ?? {
443
+ lineNumber: lines.length,
444
+ column: getNullableLastEntry(lines)?.length ?? 0,
445
+ };
446
+ playgroundState.sqlEditor.executeEdits('', [
447
+ {
448
+ range: {
449
+ startLineNumber: position.lineNumber,
450
+ startColumn: position.column,
451
+ endLineNumber: position.lineNumber,
452
+ endColumn: position.column,
453
+ },
454
+ text: item.text,
455
+ forceMoveMarkers: true,
456
+ },
457
+ ]);
458
+ playgroundState.setSQLText(
459
+ playgroundState.sqlEditorTextModel.getValue(),
460
+ );
461
+ }
462
+ }
463
+ },
464
+ [playgroundState],
465
+ );
466
+ const [{ isDatabaseNodeDragOver }, dropConnector] = useDrop<
467
+ DatabaseNodeDragType,
468
+ void,
469
+ { isDatabaseNodeDragOver: boolean }
470
+ >(
471
+ () => ({
472
+ accept: DATABASE_NODE_DND_TYPE,
473
+ drop: (item): void => handleDatabaseNodeDrop(item),
474
+ collect: (monitor) => ({
475
+ isDatabaseNodeDragOver: monitor.isOver({ shallow: true }),
476
+ }),
477
+ }),
478
+ [handleDatabaseNodeDrop],
479
+ );
480
+
481
+ return (
482
+ <div className="sql-playground__code-editor">
483
+ <PanelLoadingIndicator isLoading={playgroundState.isExecutingRawSQL} />
484
+ <div className="sql-playground__code-editor__header">
485
+ <div className="sql-playground__code-editor__header__actions">
486
+ <button
487
+ className="sql-playground__code-editor__header__action"
488
+ tabIndex={-1}
489
+ onClick={executeRawSQL}
490
+ disabled={playgroundState.isExecutingRawSQL}
491
+ title="Execute (Ctrl + Enter)"
492
+ >
493
+ <PlayIcon />
494
+ </button>
495
+ <button
496
+ className="sql-playground__code-editor__header__action"
497
+ tabIndex={-1}
498
+ onClick={reset}
499
+ title="Reset"
500
+ >
501
+ <TrashIcon />
502
+ </button>
503
+ </div>
504
+ </div>
505
+ <PanelDropZone
506
+ className="sql-playground__code-editor__content"
507
+ isDragOver={isDatabaseNodeDragOver}
508
+ dropTargetConnector={dropConnector}
509
+ >
510
+ <div className="code-editor__container">
511
+ <div className="code-editor__body" ref={codeEditorRef} />
512
+ </div>
513
+ </PanelDropZone>
514
+ </div>
515
+ );
516
+ });
517
+
518
+ const parseExecutionResultData = (
519
+ data: string,
520
+ ): { rowData: Record<string, string>[]; columns: string[] } | undefined => {
521
+ const lines = data.split('\n').filter((line) => line.trim().length);
522
+ if (lines.length) {
523
+ const columns = parseCSVString(getNonNullableEntry(lines, 0)) ?? [];
524
+ const rowData = lines
525
+ .slice(1)
526
+ .map((item) => {
527
+ const rowItems = parseCSVString(item);
528
+ if (!rowItems) {
529
+ return undefined;
530
+ }
531
+ const row: Record<string, string> = {};
532
+ columns.forEach((column, idx) => {
533
+ row[column] = getNullableEntry(rowItems, idx) ?? '';
534
+ });
535
+ return row;
536
+ })
537
+ .filter(isNonNullable);
538
+ return { rowData, columns };
539
+ }
540
+ return undefined;
541
+ };
542
+
543
+ const PlayGroundSQLExecutionResultGrid = observer(
544
+ (props: { result: string }) => {
545
+ const { result } = props;
546
+ const data = parseExecutionResultData(result);
547
+
548
+ if (!data) {
549
+ return (
550
+ <BlankPanelContent>{`Can't parse result, displaying raw form:\n${result}`}</BlankPanelContent>
551
+ );
552
+ }
553
+ return (
554
+ <div className="sql-playground__result__grid ag-theme-balham-dark">
555
+ <DataGrid
556
+ rowData={data.rowData}
557
+ overlayNoRowsTemplate={`<div class="sql-playground__result__grid--empty">No results</div>`}
558
+ alwaysShowVerticalScroll={true}
559
+ suppressFieldDotNotation={true}
560
+ columnDefs={data.columns.map((column) => ({
561
+ minWidth: 50,
562
+ sortable: true,
563
+ resizable: true,
564
+ headerName: column,
565
+ field: column,
566
+ flex: 1,
567
+ }))}
568
+ />
569
+ </div>
570
+ );
571
+ },
572
+ );
573
+
574
+ type SQLPlaygroundPanelDropTarget = ElementDragSource;
575
+
576
+ export const SQLPlaygroundPanel = observer(() => {
577
+ const editorStore = useEditorStore();
578
+ const playgroundState = editorStore.sqlPlaygroundState;
579
+ const applicationStore = useApplicationStore();
580
+
581
+ // connection
582
+ const connectionSelectorRef = useRef<SelectComponent>(null);
583
+ const connectionFilterOption = createFilter({
584
+ ignoreCase: true,
585
+ ignoreAccents: false,
586
+ stringify: (option: RelationalDatabaseConnectionOption): string =>
587
+ option.value.path,
588
+ });
589
+ const connectionOptions = editorStore.graphManagerState.usableConnections
590
+ .filter(
591
+ (connection) =>
592
+ connection.connectionValue instanceof RelationalDatabaseConnection,
593
+ )
594
+ .map(buildRelationalDatabaseConnectionOption);
595
+ const selectedConnectionOption = playgroundState.connection
596
+ ? buildRelationalDatabaseConnectionOption(playgroundState.connection)
597
+ : null;
598
+ const changeConnection = (val: RelationalDatabaseConnectionOption): void => {
599
+ if (val.value === playgroundState.connection) {
600
+ return;
601
+ }
602
+ playgroundState.setConnection(val.value);
603
+ };
604
+ const onPickConnection = (): void => {
605
+ editorStore.setQuickInputState({
606
+ title: 'Connection picker',
607
+ placeholder: 'Select a connection...',
608
+ options: connectionOptions,
609
+ getSearchValue: (option: RelationalDatabaseConnectionOption): string =>
610
+ option.value.path,
611
+ onSelect: changeConnection,
612
+ });
613
+ };
614
+
615
+ const handleConnectionDrop = useCallback(
616
+ (item: SQLPlaygroundPanelDropTarget): void => {
617
+ if (item.data.packageableElement instanceof PackageableConnection) {
618
+ if (
619
+ item.data.packageableElement.connectionValue instanceof
620
+ RelationalDatabaseConnection
621
+ ) {
622
+ playgroundState.setConnection(item.data.packageableElement);
623
+ } else {
624
+ applicationStore.notificationService.notifyWarning(
625
+ `Can't use SQL playground with non-relational database connection`,
626
+ );
627
+ }
628
+ }
629
+ },
630
+ [playgroundState, applicationStore],
631
+ );
632
+ const [{ isConnectionDragOver }, dropConnector] = useDrop<
633
+ ElementDragSource,
634
+ void,
635
+ { isConnectionDragOver: boolean }
636
+ >(
637
+ () => ({
638
+ accept: CORE_DND_TYPE.PROJECT_EXPLORER_CONNECTION,
639
+ drop: (item) => handleConnectionDrop(item),
640
+ collect: (monitor) => ({
641
+ isConnectionDragOver: monitor.isOver({ shallow: true }),
642
+ }),
643
+ }),
644
+ [handleConnectionDrop],
645
+ );
646
+
647
+ useEffect(() => {
648
+ flowResult(playgroundState.fetchDatabaseMetadata()).catch(
649
+ applicationStore.alertUnhandledError,
650
+ );
651
+ }, [playgroundState, applicationStore, playgroundState.connection]);
652
+
653
+ useConditionedApplicationNavigationContext(
654
+ LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY.SQL_PLAYGROUND,
655
+ editorStore.activePanelMode === PANEL_MODE.SQL_PLAYGROUND,
656
+ );
657
+
658
+ return (
659
+ <PanelDropZone
660
+ isDragOver={isConnectionDragOver}
661
+ dropTargetConnector={dropConnector}
662
+ >
663
+ <div className="sql-playground">
664
+ {playgroundState.connection && (
665
+ <ResizablePanelGroup orientation="vertical">
666
+ <ResizablePanel size={300}>
667
+ <div className="sql-playground__config">
668
+ <div className="sql-playground__config__connection-selector">
669
+ <div className="sql-playground__config__connection-selector__icon">
670
+ <PURE_ConnectionIcon />
671
+ </div>
672
+ <CustomSelectorInput
673
+ ref={connectionSelectorRef}
674
+ className="sql-playground__config__connection-selector__input"
675
+ options={connectionOptions}
676
+ onChange={changeConnection}
677
+ value={selectedConnectionOption}
678
+ darkMode={true}
679
+ placeholder="Choose a connection..."
680
+ filterOption={connectionFilterOption}
681
+ />
682
+ </div>
683
+ <div className="sql-playground__config__schema-explorer">
684
+ {playgroundState.treeData && (
685
+ <DatabaseSchemaExplorer
686
+ treeData={playgroundState.treeData}
687
+ playgroundState={playgroundState}
688
+ />
689
+ )}
690
+ </div>
691
+ </div>
692
+ </ResizablePanel>
693
+ <ResizablePanelSplitter />
694
+ <ResizablePanel>
695
+ <div className="panel sql-playground__sql-editor">
696
+ <ResizablePanelGroup orientation="horizontal">
697
+ <ResizablePanel>
698
+ <PlaygroundSQLCodeEditor />
699
+ </ResizablePanel>
700
+ <ResizablePanelSplitter>
701
+ <ResizablePanelSplitterLine color="var(--color-dark-grey-250)" />
702
+ </ResizablePanelSplitter>
703
+ <ResizablePanel size={300}>
704
+ {playgroundState.sqlExecutionResult !== undefined && (
705
+ <PlayGroundSQLExecutionResultGrid
706
+ result={playgroundState.sqlExecutionResult}
707
+ />
708
+ )}
709
+ {playgroundState.sqlExecutionResult === undefined && (
710
+ <div />
711
+ )}
712
+ </ResizablePanel>
713
+ </ResizablePanelGroup>
714
+ </div>
715
+ </ResizablePanel>
716
+ </ResizablePanelGroup>
717
+ )}
718
+ {!playgroundState.connection && (
719
+ <BlankPanelPlaceholder
720
+ onClick={onPickConnection}
721
+ clickActionType="add"
722
+ text="Pick a connection to start"
723
+ tooltipText="Drop a connection to start..."
724
+ isDropZoneActive={isConnectionDragOver}
725
+ />
726
+ )}
727
+ </div>
728
+ </PanelDropZone>
729
+ );
730
+ });