@finos/legend-application-studio 28.19.74 → 28.19.75
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/editor/__test-utils__/EditorComponentTestUtils.d.ts.map +1 -1
- package/lib/components/editor/__test-utils__/EditorComponentTestUtils.js +4 -0
- package/lib/components/editor/__test-utils__/EditorComponentTestUtils.js.map +1 -1
- package/lib/components/editor/panel-group/SQLPlaygroundPanel.d.ts.map +1 -1
- package/lib/components/editor/panel-group/SQLPlaygroundPanel.js +9 -298
- package/lib/components/editor/panel-group/SQLPlaygroundPanel.js.map +1 -1
- package/lib/components/editor/side-bar/Explorer.js +1 -1
- package/lib/components/editor/side-bar/Explorer.js.map +1 -1
- package/lib/index.css +1 -1
- package/lib/package.json +1 -1
- package/lib/stores/editor/EditorStore.d.ts +2 -2
- package/lib/stores/editor/EditorStore.d.ts.map +1 -1
- package/lib/stores/editor/EditorStore.js +3 -3
- package/lib/stores/editor/EditorStore.js.map +1 -1
- package/lib/stores/editor/GraphEditFormModeState.js +1 -1
- package/lib/stores/editor/GraphEditFormModeState.js.map +1 -1
- package/lib/stores/editor/__test-utils__/EditorStoreTestUtils.d.ts.map +1 -1
- package/lib/stores/editor/__test-utils__/EditorStoreTestUtils.js +4 -0
- package/lib/stores/editor/__test-utils__/EditorStoreTestUtils.js.map +1 -1
- package/lib/stores/editor/panel-group/{SQLPlaygroundPanelState.d.ts → StudioSQLPlaygroundPanelState.d.ts} +8 -15
- package/lib/stores/editor/panel-group/StudioSQLPlaygroundPanelState.d.ts.map +1 -0
- package/lib/stores/editor/panel-group/{SQLPlaygroundPanelState.js → StudioSQLPlaygroundPanelState.js} +29 -44
- package/lib/stores/editor/panel-group/StudioSQLPlaygroundPanelState.js.map +1 -0
- package/package.json +11 -11
- package/src/components/editor/__test-utils__/EditorComponentTestUtils.tsx +4 -0
- package/src/components/editor/panel-group/SQLPlaygroundPanel.tsx +12 -576
- package/src/components/editor/side-bar/Explorer.tsx +1 -1
- package/src/stores/editor/EditorStore.ts +3 -3
- package/src/stores/editor/GraphEditFormModeState.ts +1 -1
- package/src/stores/editor/__test-utils__/EditorStoreTestUtils.ts +4 -1
- package/src/stores/editor/panel-group/{SQLPlaygroundPanelState.ts → StudioSQLPlaygroundPanelState.ts} +47 -60
- package/tsconfig.json +1 -1
- package/lib/stores/editor/panel-group/SQLPlaygroundPanelState.d.ts.map +0 -1
- package/lib/stores/editor/panel-group/SQLPlaygroundPanelState.js.map +0 -1
|
@@ -25,40 +25,21 @@ import {
|
|
|
25
25
|
PURE_ConnectionIcon,
|
|
26
26
|
BlankPanelPlaceholder,
|
|
27
27
|
PanelDropZone,
|
|
28
|
-
ResizablePanelSplitterLine,
|
|
29
|
-
PlayIcon,
|
|
30
28
|
PanelLoadingIndicator,
|
|
31
|
-
BlankPanelContent,
|
|
32
29
|
PURE_DatabaseIcon,
|
|
33
30
|
SyncIcon,
|
|
34
|
-
clsx,
|
|
35
|
-
CheckSquareIcon,
|
|
36
|
-
SquareIcon,
|
|
37
31
|
} from '@finos/legend-art';
|
|
38
|
-
import
|
|
32
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
39
33
|
import {
|
|
40
34
|
useApplicationStore,
|
|
41
|
-
useCommands,
|
|
42
35
|
useConditionedApplicationNavigationContext,
|
|
43
36
|
} from '@finos/legend-application';
|
|
44
37
|
import { flowResult } from 'mobx';
|
|
45
|
-
import {
|
|
46
|
-
CODE_EDITOR_LANGUAGE,
|
|
47
|
-
CODE_EDITOR_THEME,
|
|
48
|
-
getBaseCodeEditorOptions,
|
|
49
|
-
} from '@finos/legend-code-editor';
|
|
50
|
-
import {
|
|
51
|
-
editor as monacoEditorAPI,
|
|
52
|
-
languages as monacoLanguagesAPI,
|
|
53
|
-
type IDisposable,
|
|
54
|
-
type IPosition,
|
|
55
|
-
} from 'monaco-editor';
|
|
56
38
|
import {
|
|
57
39
|
PackageableConnection,
|
|
58
40
|
RelationalDatabaseConnection,
|
|
59
41
|
} from '@finos/legend-graph';
|
|
60
42
|
import { LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY } from '../../../__lib__/LegendStudioApplicationNavigationContext.js';
|
|
61
|
-
import { type SQLPlaygroundPanelState } from '../../../stores/editor/panel-group/SQLPlaygroundPanelState.js';
|
|
62
43
|
import { useEditorStore } from '../EditorStoreProvider.js';
|
|
63
44
|
import { PANEL_MODE } from '../../../stores/editor/EditorConfig.js';
|
|
64
45
|
import { useDrag, useDrop } from 'react-dnd';
|
|
@@ -66,24 +47,6 @@ import {
|
|
|
66
47
|
CORE_DND_TYPE,
|
|
67
48
|
type ElementDragSource,
|
|
68
49
|
} from '../../../stores/editor/utils/DnDUtils.js';
|
|
69
|
-
import {
|
|
70
|
-
DataGrid,
|
|
71
|
-
type DataGridCellRendererParams,
|
|
72
|
-
type DataGridColumnDefinition,
|
|
73
|
-
type DataGridDefaultMenuItem,
|
|
74
|
-
type DataGridGetContextMenuItemsParams,
|
|
75
|
-
type DataGridMenuItemDef,
|
|
76
|
-
} from '@finos/legend-lego/data-grid';
|
|
77
|
-
import {
|
|
78
|
-
at,
|
|
79
|
-
isNonNullable,
|
|
80
|
-
isNumber,
|
|
81
|
-
isString,
|
|
82
|
-
isValidURL,
|
|
83
|
-
parseCSVString,
|
|
84
|
-
prettyDuration,
|
|
85
|
-
uniqBy,
|
|
86
|
-
} from '@finos/legend-shared';
|
|
87
50
|
import {
|
|
88
51
|
DatabaseSchemaExplorer,
|
|
89
52
|
DatabaseSchemaExplorerTreeNodeContainer,
|
|
@@ -94,6 +57,7 @@ import {
|
|
|
94
57
|
buildRelationalDatabaseConnectionOption,
|
|
95
58
|
type RelationalDatabaseConnectionOption,
|
|
96
59
|
} from '../editor-group/connection-editor/RelationalDatabaseConnectionEditor.js';
|
|
60
|
+
import { SQLPlaygroundEditorResultPanel } from '@finos/legend-lego/sql-playground';
|
|
97
61
|
|
|
98
62
|
const DATABASE_NODE_DND_TYPE = 'DATABASE_NODE_DND_TYPE';
|
|
99
63
|
type DatabaseNodeDragType = { text: string };
|
|
@@ -121,449 +85,11 @@ const SQLPlaygroundDatabaseSchemaExplorerTreeNodeContainer = observer(
|
|
|
121
85
|
},
|
|
122
86
|
);
|
|
123
87
|
|
|
124
|
-
// List of most popular SQL keywords
|
|
125
|
-
// See https://www.w3schools.com/sql/sql_ref_keywords.asp
|
|
126
|
-
const SQL_KEYWORDS = [
|
|
127
|
-
'AND',
|
|
128
|
-
'AS',
|
|
129
|
-
'ASC',
|
|
130
|
-
'BETWEEN',
|
|
131
|
-
'DESC',
|
|
132
|
-
'DISTINCT',
|
|
133
|
-
'EXEC',
|
|
134
|
-
'EXISTS',
|
|
135
|
-
'FROM',
|
|
136
|
-
'FULL OUTER JOIN',
|
|
137
|
-
'GROUP BY',
|
|
138
|
-
'HAVING',
|
|
139
|
-
'IN',
|
|
140
|
-
'INNER JOIN',
|
|
141
|
-
'IS NULL',
|
|
142
|
-
'IS NOT NULL',
|
|
143
|
-
'JOIN',
|
|
144
|
-
'LEFT JOIN',
|
|
145
|
-
'LIKE',
|
|
146
|
-
'LIMIT',
|
|
147
|
-
'NOT',
|
|
148
|
-
'NOT NULL',
|
|
149
|
-
'OR',
|
|
150
|
-
'ORDER BY',
|
|
151
|
-
'OUTER JOIN',
|
|
152
|
-
'RIGHT JOIN',
|
|
153
|
-
'SELECT',
|
|
154
|
-
'SELECT DISTINCT',
|
|
155
|
-
'SELECT INTO',
|
|
156
|
-
'SELECT TOP',
|
|
157
|
-
'TOP',
|
|
158
|
-
'UNION',
|
|
159
|
-
'UNION ALL',
|
|
160
|
-
'UNIQUE',
|
|
161
|
-
'WHERE',
|
|
162
|
-
];
|
|
163
|
-
|
|
164
|
-
const getKeywordSuggestions = async (
|
|
165
|
-
position: IPosition,
|
|
166
|
-
model: monacoEditorAPI.ITextModel,
|
|
167
|
-
): Promise<monacoLanguagesAPI.CompletionItem[]> =>
|
|
168
|
-
SQL_KEYWORDS.map(
|
|
169
|
-
(keyword) =>
|
|
170
|
-
({
|
|
171
|
-
label: keyword,
|
|
172
|
-
kind: monacoLanguagesAPI.CompletionItemKind.Keyword,
|
|
173
|
-
insertTextRules:
|
|
174
|
-
monacoLanguagesAPI.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
175
|
-
insertText: `${keyword} `,
|
|
176
|
-
}) as monacoLanguagesAPI.CompletionItem,
|
|
177
|
-
);
|
|
178
|
-
|
|
179
|
-
const getDatabaseSchemaEntities = async (
|
|
180
|
-
position: IPosition,
|
|
181
|
-
model: monacoEditorAPI.ITextModel,
|
|
182
|
-
playgroundState: SQLPlaygroundPanelState,
|
|
183
|
-
): Promise<monacoLanguagesAPI.CompletionItem[]> => {
|
|
184
|
-
if (playgroundState.schemaExplorerState?.treeData) {
|
|
185
|
-
return uniqBy(
|
|
186
|
-
Array.from(
|
|
187
|
-
playgroundState.schemaExplorerState.treeData.nodes.values(),
|
|
188
|
-
).map(
|
|
189
|
-
(value) =>
|
|
190
|
-
({
|
|
191
|
-
label: value.label,
|
|
192
|
-
kind: monacoLanguagesAPI.CompletionItemKind.Field,
|
|
193
|
-
insertTextRules:
|
|
194
|
-
monacoLanguagesAPI.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
195
|
-
insertText: `${value.label} `,
|
|
196
|
-
}) as monacoLanguagesAPI.CompletionItem,
|
|
197
|
-
),
|
|
198
|
-
(val) => val.label,
|
|
199
|
-
);
|
|
200
|
-
}
|
|
201
|
-
return [];
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
const PlaygroundSQLCodeEditor = observer(() => {
|
|
205
|
-
const editorStore = useEditorStore();
|
|
206
|
-
const playgroundState = editorStore.sqlPlaygroundState;
|
|
207
|
-
const applicationStore = useApplicationStore();
|
|
208
|
-
const codeEditorRef = useRef<HTMLDivElement>(null);
|
|
209
|
-
const [editor, setEditor] = useState<
|
|
210
|
-
monacoEditorAPI.IStandaloneCodeEditor | undefined
|
|
211
|
-
>();
|
|
212
|
-
const sqlIdentifierSuggestionProviderDisposer = useRef<
|
|
213
|
-
IDisposable | undefined
|
|
214
|
-
>(undefined);
|
|
215
|
-
|
|
216
|
-
useEffect(() => {
|
|
217
|
-
if (!editor && codeEditorRef.current) {
|
|
218
|
-
const element = codeEditorRef.current;
|
|
219
|
-
const newEditor = monacoEditorAPI.create(element, {
|
|
220
|
-
...getBaseCodeEditorOptions(),
|
|
221
|
-
theme: CODE_EDITOR_THEME.DEFAULT_DARK,
|
|
222
|
-
language: CODE_EDITOR_LANGUAGE.SQL,
|
|
223
|
-
padding: {
|
|
224
|
-
top: 10,
|
|
225
|
-
},
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
newEditor.onDidChangeModelContent(() => {
|
|
229
|
-
const currentVal = newEditor.getValue();
|
|
230
|
-
playgroundState.setSQLText(currentVal);
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
// Restore the editor model and view state
|
|
234
|
-
newEditor.setModel(playgroundState.sqlEditorTextModel);
|
|
235
|
-
if (playgroundState.sqlEditorViewState) {
|
|
236
|
-
newEditor.restoreViewState(playgroundState.sqlEditorViewState);
|
|
237
|
-
}
|
|
238
|
-
newEditor.focus(); // focus on the editor initially
|
|
239
|
-
playgroundState.setSQLEditor(newEditor);
|
|
240
|
-
setEditor(newEditor);
|
|
241
|
-
}
|
|
242
|
-
}, [playgroundState, applicationStore, editor]);
|
|
243
|
-
|
|
244
|
-
useCommands(playgroundState);
|
|
245
|
-
|
|
246
|
-
if (editor) {
|
|
247
|
-
sqlIdentifierSuggestionProviderDisposer.current?.dispose();
|
|
248
|
-
sqlIdentifierSuggestionProviderDisposer.current =
|
|
249
|
-
monacoLanguagesAPI.registerCompletionItemProvider(
|
|
250
|
-
CODE_EDITOR_LANGUAGE.SQL,
|
|
251
|
-
{
|
|
252
|
-
triggerCharacters: [],
|
|
253
|
-
provideCompletionItems: async (model, position, context) => {
|
|
254
|
-
let suggestions: monacoLanguagesAPI.CompletionItem[] = [];
|
|
255
|
-
if (
|
|
256
|
-
context.triggerKind ===
|
|
257
|
-
monacoLanguagesAPI.CompletionTriggerKind.Invoke
|
|
258
|
-
) {
|
|
259
|
-
// keywords
|
|
260
|
-
suggestions = suggestions.concat(
|
|
261
|
-
await getKeywordSuggestions(position, model),
|
|
262
|
-
);
|
|
263
|
-
|
|
264
|
-
// database schema entities
|
|
265
|
-
suggestions = suggestions.concat(
|
|
266
|
-
await getDatabaseSchemaEntities(
|
|
267
|
-
position,
|
|
268
|
-
model,
|
|
269
|
-
playgroundState,
|
|
270
|
-
),
|
|
271
|
-
);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
return { suggestions };
|
|
275
|
-
},
|
|
276
|
-
},
|
|
277
|
-
);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// clean up
|
|
281
|
-
useEffect(
|
|
282
|
-
() => (): void => {
|
|
283
|
-
if (editor) {
|
|
284
|
-
// persist editor view state (cursor, scroll, etc.) to restore on re-open
|
|
285
|
-
playgroundState.setSQLEditorViewState(
|
|
286
|
-
editor.saveViewState() ?? undefined,
|
|
287
|
-
);
|
|
288
|
-
editor.dispose();
|
|
289
|
-
|
|
290
|
-
// Dispose the providers properly to avoid ending up with duplicated suggestions
|
|
291
|
-
sqlIdentifierSuggestionProviderDisposer.current?.dispose();
|
|
292
|
-
}
|
|
293
|
-
},
|
|
294
|
-
[playgroundState, editor],
|
|
295
|
-
);
|
|
296
|
-
|
|
297
|
-
const handleDatabaseNodeDrop = useCallback(
|
|
298
|
-
(item: DatabaseNodeDragType): void => {
|
|
299
|
-
if (isString(item.text)) {
|
|
300
|
-
if (playgroundState.sqlEditor) {
|
|
301
|
-
const currentValue = playgroundState.sqlEditorTextModel.getValue();
|
|
302
|
-
const lines = currentValue.split('\n');
|
|
303
|
-
const position = playgroundState.sqlEditor.getPosition() ?? {
|
|
304
|
-
lineNumber: lines.length,
|
|
305
|
-
column: lines.at(-1)?.length ?? 0,
|
|
306
|
-
};
|
|
307
|
-
playgroundState.sqlEditor.executeEdits('', [
|
|
308
|
-
{
|
|
309
|
-
range: {
|
|
310
|
-
startLineNumber: position.lineNumber,
|
|
311
|
-
startColumn: position.column,
|
|
312
|
-
endLineNumber: position.lineNumber,
|
|
313
|
-
endColumn: position.column,
|
|
314
|
-
},
|
|
315
|
-
text: item.text,
|
|
316
|
-
forceMoveMarkers: true,
|
|
317
|
-
},
|
|
318
|
-
]);
|
|
319
|
-
playgroundState.setSQLText(
|
|
320
|
-
playgroundState.sqlEditorTextModel.getValue(),
|
|
321
|
-
);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
},
|
|
325
|
-
[playgroundState],
|
|
326
|
-
);
|
|
327
|
-
const [{ isDatabaseNodeDragOver }, dropConnector] = useDrop<
|
|
328
|
-
DatabaseNodeDragType,
|
|
329
|
-
void,
|
|
330
|
-
{ isDatabaseNodeDragOver: boolean }
|
|
331
|
-
>(
|
|
332
|
-
() => ({
|
|
333
|
-
accept: DATABASE_NODE_DND_TYPE,
|
|
334
|
-
drop: (item): void => handleDatabaseNodeDrop(item),
|
|
335
|
-
collect: (monitor) => ({
|
|
336
|
-
isDatabaseNodeDragOver: monitor.isOver({ shallow: true }),
|
|
337
|
-
}),
|
|
338
|
-
}),
|
|
339
|
-
[handleDatabaseNodeDrop],
|
|
340
|
-
);
|
|
341
|
-
|
|
342
|
-
return (
|
|
343
|
-
<div className="sql-playground__code-editor">
|
|
344
|
-
<PanelDropZone
|
|
345
|
-
className="sql-playground__code-editor__content"
|
|
346
|
-
isDragOver={isDatabaseNodeDragOver}
|
|
347
|
-
dropTargetConnector={dropConnector}
|
|
348
|
-
>
|
|
349
|
-
<div className="code-editor__container">
|
|
350
|
-
<div className="code-editor__body" ref={codeEditorRef} />
|
|
351
|
-
</div>
|
|
352
|
-
</PanelDropZone>
|
|
353
|
-
</div>
|
|
354
|
-
);
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
const parseExecutionResultData = (
|
|
358
|
-
data: string,
|
|
359
|
-
): { rowData: Record<string, string>[]; columns: string[] } | undefined => {
|
|
360
|
-
const lines = data.split('\n').filter((line) => line.trim().length);
|
|
361
|
-
if (lines.length) {
|
|
362
|
-
const columns = parseCSVString(at(lines, 0)) ?? [];
|
|
363
|
-
const rowData = lines
|
|
364
|
-
.slice(1)
|
|
365
|
-
.map((item) => {
|
|
366
|
-
const rowItems = parseCSVString(item);
|
|
367
|
-
if (!rowItems) {
|
|
368
|
-
return undefined;
|
|
369
|
-
}
|
|
370
|
-
const row: Record<string, string> = {};
|
|
371
|
-
columns.forEach((column, idx) => {
|
|
372
|
-
row[column] = rowItems[idx] ?? '';
|
|
373
|
-
});
|
|
374
|
-
return row;
|
|
375
|
-
})
|
|
376
|
-
.filter(isNonNullable);
|
|
377
|
-
return { rowData, columns };
|
|
378
|
-
}
|
|
379
|
-
return undefined;
|
|
380
|
-
};
|
|
381
|
-
|
|
382
|
-
const TDSResultCellRenderer = observer((params: DataGridCellRendererParams) => {
|
|
383
|
-
const cellValue = params.value as string;
|
|
384
|
-
const formattedCellValue = (): string => {
|
|
385
|
-
if (isNumber(cellValue)) {
|
|
386
|
-
return Intl.NumberFormat('en-US', {
|
|
387
|
-
maximumFractionDigits: 4,
|
|
388
|
-
}).format(Number(cellValue));
|
|
389
|
-
}
|
|
390
|
-
return cellValue;
|
|
391
|
-
};
|
|
392
|
-
const cellValueUrlLink =
|
|
393
|
-
isString(cellValue) && isValidURL(cellValue) ? cellValue : undefined;
|
|
394
|
-
|
|
395
|
-
return (
|
|
396
|
-
<div className={clsx('query-builder__result__values__table__cell')}>
|
|
397
|
-
{cellValueUrlLink ? (
|
|
398
|
-
<a href={cellValueUrlLink} target="_blank" rel="noreferrer">
|
|
399
|
-
{cellValueUrlLink}
|
|
400
|
-
</a>
|
|
401
|
-
) : (
|
|
402
|
-
<span>{formattedCellValue()}</span>
|
|
403
|
-
)}
|
|
404
|
-
</div>
|
|
405
|
-
);
|
|
406
|
-
});
|
|
407
|
-
|
|
408
|
-
const PlayGroundSQLExecutionResultGrid = observer(
|
|
409
|
-
(props: {
|
|
410
|
-
result: string;
|
|
411
|
-
useAdvancedGrid?: boolean;
|
|
412
|
-
useLocalMode?: boolean;
|
|
413
|
-
}) => {
|
|
414
|
-
const { result, useAdvancedGrid, useLocalMode } = props;
|
|
415
|
-
const data = parseExecutionResultData(result);
|
|
416
|
-
const applicationStore = useApplicationStore();
|
|
417
|
-
const darkMode =
|
|
418
|
-
!applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled;
|
|
419
|
-
|
|
420
|
-
if (!data) {
|
|
421
|
-
return (
|
|
422
|
-
<BlankPanelContent>{`Can't parse result, displaying raw form:\n${result}`}</BlankPanelContent>
|
|
423
|
-
);
|
|
424
|
-
}
|
|
425
|
-
if (useAdvancedGrid) {
|
|
426
|
-
if (useLocalMode) {
|
|
427
|
-
const localcolDefs = data.columns.map(
|
|
428
|
-
(colName) =>
|
|
429
|
-
({
|
|
430
|
-
minWidth: 50,
|
|
431
|
-
sortable: true,
|
|
432
|
-
resizable: true,
|
|
433
|
-
field: colName,
|
|
434
|
-
flex: 1,
|
|
435
|
-
enablePivot: true,
|
|
436
|
-
enableRowGroup: true,
|
|
437
|
-
enableValue: true,
|
|
438
|
-
allowedAggFuncs: ['count'],
|
|
439
|
-
}) as DataGridColumnDefinition,
|
|
440
|
-
);
|
|
441
|
-
|
|
442
|
-
return (
|
|
443
|
-
<div
|
|
444
|
-
className={clsx('sql-playground__result__grid', {
|
|
445
|
-
'ag-theme-balham': !darkMode,
|
|
446
|
-
'ag-theme-balham-dark': darkMode,
|
|
447
|
-
})}
|
|
448
|
-
>
|
|
449
|
-
<DataGrid
|
|
450
|
-
rowData={data.rowData}
|
|
451
|
-
gridOptions={{
|
|
452
|
-
suppressScrollOnNewData: true,
|
|
453
|
-
rowSelection: {
|
|
454
|
-
mode: 'multiRow',
|
|
455
|
-
checkboxes: false,
|
|
456
|
-
headerCheckbox: false,
|
|
457
|
-
},
|
|
458
|
-
pivotPanelShow: 'always',
|
|
459
|
-
rowGroupPanelShow: 'always',
|
|
460
|
-
cellSelection: true,
|
|
461
|
-
}}
|
|
462
|
-
// NOTE: when column definition changed, we need to force refresh the cell to make sure the cell renderer is updated
|
|
463
|
-
// See https://stackoverflow.com/questions/56341073/how-to-refresh-an-ag-grid-when-a-change-occurs-inside-a-custom-cell-renderer-com
|
|
464
|
-
onRowDataUpdated={(params) => {
|
|
465
|
-
params.api.refreshCells({ force: true });
|
|
466
|
-
}}
|
|
467
|
-
suppressFieldDotNotation={true}
|
|
468
|
-
suppressContextMenu={false}
|
|
469
|
-
columnDefs={localcolDefs}
|
|
470
|
-
sideBar={['columns', 'filters']}
|
|
471
|
-
/>
|
|
472
|
-
</div>
|
|
473
|
-
);
|
|
474
|
-
}
|
|
475
|
-
const colDefs = data.columns.map(
|
|
476
|
-
(colName) =>
|
|
477
|
-
({
|
|
478
|
-
minWidth: 50,
|
|
479
|
-
sortable: true,
|
|
480
|
-
resizable: true,
|
|
481
|
-
field: colName,
|
|
482
|
-
flex: 1,
|
|
483
|
-
cellRenderer: TDSResultCellRenderer,
|
|
484
|
-
filter: true,
|
|
485
|
-
}) as DataGridColumnDefinition,
|
|
486
|
-
);
|
|
487
|
-
const getContextMenuItems = useCallback(
|
|
488
|
-
(
|
|
489
|
-
params: DataGridGetContextMenuItemsParams<{
|
|
490
|
-
[key: string]: string;
|
|
491
|
-
}>,
|
|
492
|
-
): (DataGridDefaultMenuItem | DataGridMenuItemDef)[] => [
|
|
493
|
-
'copy',
|
|
494
|
-
'copyWithHeaders',
|
|
495
|
-
{
|
|
496
|
-
name: 'Copy Row Value',
|
|
497
|
-
action: () => {
|
|
498
|
-
params.api.copySelectedRowsToClipboard();
|
|
499
|
-
},
|
|
500
|
-
},
|
|
501
|
-
],
|
|
502
|
-
[],
|
|
503
|
-
);
|
|
504
|
-
return (
|
|
505
|
-
<div
|
|
506
|
-
className={clsx('sql-playground__result__grid', {
|
|
507
|
-
'ag-theme-balham': !darkMode,
|
|
508
|
-
'ag-theme-balham-dark': darkMode,
|
|
509
|
-
})}
|
|
510
|
-
>
|
|
511
|
-
<DataGrid
|
|
512
|
-
rowData={data.rowData}
|
|
513
|
-
overlayNoRowsTemplate={`<div class="sql-playground__result__grid--empty">No results</div>`}
|
|
514
|
-
gridOptions={{
|
|
515
|
-
suppressScrollOnNewData: true,
|
|
516
|
-
rowSelection: {
|
|
517
|
-
mode: 'multiRow',
|
|
518
|
-
checkboxes: false,
|
|
519
|
-
headerCheckbox: false,
|
|
520
|
-
},
|
|
521
|
-
cellSelection: true,
|
|
522
|
-
}}
|
|
523
|
-
onRowDataUpdated={(params) => {
|
|
524
|
-
params.api.refreshCells({ force: true });
|
|
525
|
-
}}
|
|
526
|
-
suppressFieldDotNotation={true}
|
|
527
|
-
suppressClipboardPaste={false}
|
|
528
|
-
suppressContextMenu={false}
|
|
529
|
-
columnDefs={colDefs}
|
|
530
|
-
getContextMenuItems={(params) => getContextMenuItems(params)}
|
|
531
|
-
/>
|
|
532
|
-
</div>
|
|
533
|
-
);
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
return (
|
|
537
|
-
<div
|
|
538
|
-
className={clsx('sql-playground__result__grid', {
|
|
539
|
-
'ag-theme-balham': !darkMode,
|
|
540
|
-
'ag-theme-balham-dark': darkMode,
|
|
541
|
-
})}
|
|
542
|
-
>
|
|
543
|
-
<DataGrid
|
|
544
|
-
rowData={data.rowData}
|
|
545
|
-
overlayNoRowsTemplate={`<div class="sql-playground__result__grid--empty">No results</div>`}
|
|
546
|
-
alwaysShowVerticalScroll={true}
|
|
547
|
-
suppressFieldDotNotation={true}
|
|
548
|
-
columnDefs={data.columns.map((column) => ({
|
|
549
|
-
minWidth: 50,
|
|
550
|
-
sortable: true,
|
|
551
|
-
resizable: true,
|
|
552
|
-
headerName: column,
|
|
553
|
-
field: column,
|
|
554
|
-
flex: 1,
|
|
555
|
-
}))}
|
|
556
|
-
/>
|
|
557
|
-
</div>
|
|
558
|
-
);
|
|
559
|
-
},
|
|
560
|
-
);
|
|
561
|
-
|
|
562
88
|
type SQLPlaygroundPanelDropTarget = ElementDragSource;
|
|
563
89
|
|
|
564
90
|
export const SQLPlaygroundPanel = observer(() => {
|
|
565
91
|
const editorStore = useEditorStore();
|
|
566
|
-
const playgroundState = editorStore.
|
|
92
|
+
const playgroundState = editorStore.studioSqlPlaygroundState;
|
|
567
93
|
const applicationStore = useApplicationStore();
|
|
568
94
|
|
|
569
95
|
// connection
|
|
@@ -640,26 +166,6 @@ export const SQLPlaygroundPanel = observer(() => {
|
|
|
640
166
|
}
|
|
641
167
|
};
|
|
642
168
|
|
|
643
|
-
const executeRawSQL = (): void => {
|
|
644
|
-
flowResult(playgroundState.executeRawSQL()).catch(
|
|
645
|
-
applicationStore.alertUnhandledError,
|
|
646
|
-
);
|
|
647
|
-
};
|
|
648
|
-
const advancedMode = Boolean(
|
|
649
|
-
editorStore.applicationStore.config.options.queryBuilderConfig
|
|
650
|
-
?.TEMPORARY__enableGridEnterpriseMode,
|
|
651
|
-
);
|
|
652
|
-
const resultDescription = playgroundState.sqlExecutionResult
|
|
653
|
-
? `query ran in ${prettyDuration(
|
|
654
|
-
playgroundState.sqlExecutionResult.sqlDuration,
|
|
655
|
-
{
|
|
656
|
-
ms: true,
|
|
657
|
-
},
|
|
658
|
-
)}`
|
|
659
|
-
: undefined;
|
|
660
|
-
const toggleocalMode = (): void => {
|
|
661
|
-
playgroundState.toggleIsLocalModeEnabled();
|
|
662
|
-
};
|
|
663
169
|
useEffect(() => {
|
|
664
170
|
if (playgroundState.schemaExplorerState) {
|
|
665
171
|
flowResult(
|
|
@@ -668,6 +174,10 @@ export const SQLPlaygroundPanel = observer(() => {
|
|
|
668
174
|
}
|
|
669
175
|
}, [playgroundState, applicationStore, playgroundState.schemaExplorerState]);
|
|
670
176
|
|
|
177
|
+
useEffect(() => {
|
|
178
|
+
playgroundState.fetchSchemaMetaData();
|
|
179
|
+
}, [playgroundState]);
|
|
180
|
+
|
|
671
181
|
useConditionedApplicationNavigationContext(
|
|
672
182
|
LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY.SQL_PLAYGROUND,
|
|
673
183
|
editorStore.activePanelMode === PANEL_MODE.SQL_PLAYGROUND,
|
|
@@ -749,85 +259,11 @@ export const SQLPlaygroundPanel = observer(() => {
|
|
|
749
259
|
<ResizablePanelSplitter />
|
|
750
260
|
<ResizablePanel>
|
|
751
261
|
<div className="panel sql-playground__sql-editor">
|
|
752
|
-
<
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
<ResizablePanelSplitterLine color="var(--color-dark-grey-250)" />
|
|
758
|
-
</ResizablePanelSplitter>
|
|
759
|
-
<ResizablePanel size={300}>
|
|
760
|
-
<div className="panel__header">
|
|
761
|
-
<div className="panel__header__title">
|
|
762
|
-
<div className="panel__header__title__label">
|
|
763
|
-
result
|
|
764
|
-
</div>
|
|
765
|
-
|
|
766
|
-
{playgroundState.executeRawSQLState.isInProgress && (
|
|
767
|
-
<div className="panel__header__title__label__status">
|
|
768
|
-
Running SQL...
|
|
769
|
-
</div>
|
|
770
|
-
)}
|
|
771
|
-
|
|
772
|
-
<div className="query-builder__result__analytics">
|
|
773
|
-
{resultDescription ?? ''}
|
|
774
|
-
</div>
|
|
775
|
-
</div>
|
|
776
|
-
<div className="panel__header__actions query-builder__result__header__actions">
|
|
777
|
-
{advancedMode && (
|
|
778
|
-
<div className="query-builder__result__advanced__mode">
|
|
779
|
-
<div className="query-builder__result__advanced__mode__label">
|
|
780
|
-
Local Mode
|
|
781
|
-
</div>
|
|
782
|
-
<button
|
|
783
|
-
className={clsx(
|
|
784
|
-
'query-builder__result__advanced__mode__toggler__btn',
|
|
785
|
-
{
|
|
786
|
-
'query-builder__result__advanced__mode__toggler__btn--toggled':
|
|
787
|
-
playgroundState.isLocalModeEnabled,
|
|
788
|
-
},
|
|
789
|
-
)}
|
|
790
|
-
onClick={toggleocalMode}
|
|
791
|
-
tabIndex={-1}
|
|
792
|
-
>
|
|
793
|
-
{playgroundState.isLocalModeEnabled ? (
|
|
794
|
-
<CheckSquareIcon />
|
|
795
|
-
) : (
|
|
796
|
-
<SquareIcon />
|
|
797
|
-
)}
|
|
798
|
-
</button>
|
|
799
|
-
</div>
|
|
800
|
-
)}
|
|
801
|
-
|
|
802
|
-
<div className="query-builder__result__execute-btn btn__dropdown-combo btn__dropdown-combo--primary">
|
|
803
|
-
<button
|
|
804
|
-
className="btn__dropdown-combo__label"
|
|
805
|
-
onClick={executeRawSQL}
|
|
806
|
-
disabled={
|
|
807
|
-
playgroundState.executeRawSQLState.isInProgress
|
|
808
|
-
}
|
|
809
|
-
tabIndex={-1}
|
|
810
|
-
>
|
|
811
|
-
<PlayIcon className="btn__dropdown-combo__label__icon" />
|
|
812
|
-
<div className="btn__dropdown-combo__label__title">
|
|
813
|
-
Run Query
|
|
814
|
-
</div>
|
|
815
|
-
</button>
|
|
816
|
-
</div>
|
|
817
|
-
</div>
|
|
818
|
-
</div>
|
|
819
|
-
{playgroundState.sqlExecutionResult !== undefined && (
|
|
820
|
-
<PlayGroundSQLExecutionResultGrid
|
|
821
|
-
result={playgroundState.sqlExecutionResult.value}
|
|
822
|
-
useAdvancedGrid={advancedMode}
|
|
823
|
-
useLocalMode={playgroundState.isLocalModeEnabled}
|
|
824
|
-
/>
|
|
825
|
-
)}
|
|
826
|
-
{playgroundState.sqlExecutionResult === undefined && (
|
|
827
|
-
<div />
|
|
828
|
-
)}
|
|
829
|
-
</ResizablePanel>
|
|
830
|
-
</ResizablePanelGroup>
|
|
262
|
+
<SQLPlaygroundEditorResultPanel
|
|
263
|
+
playgroundState={playgroundState}
|
|
264
|
+
advancedMode={true}
|
|
265
|
+
enableDarkMode={true}
|
|
266
|
+
/>
|
|
831
267
|
</div>
|
|
832
268
|
</ResizablePanel>
|
|
833
269
|
</ResizablePanelGroup>
|
|
@@ -618,7 +618,7 @@ const ExplorerContextMenu = observer(
|
|
|
618
618
|
if (isRelationalDatabaseConnection(node?.packageableElement)) {
|
|
619
619
|
editorStore.panelGroupDisplayState.open();
|
|
620
620
|
editorStore.setActivePanelMode(PANEL_MODE.SQL_PLAYGROUND);
|
|
621
|
-
editorStore.
|
|
621
|
+
editorStore.studioSqlPlaygroundState.setConnection(
|
|
622
622
|
guaranteeType(node?.packageableElement, PackageableConnection),
|
|
623
623
|
);
|
|
624
624
|
}
|
|
@@ -109,7 +109,7 @@ import { GraphEditFormModeState } from './GraphEditFormModeState.js';
|
|
|
109
109
|
import type { GraphEditorMode } from './GraphEditorMode.js';
|
|
110
110
|
import { GraphEditGrammarModeState } from './GraphEditGrammarModeState.js';
|
|
111
111
|
import { GlobalBulkServiceRegistrationState } from './sidebar-state/BulkServiceRegistrationState.js';
|
|
112
|
-
import {
|
|
112
|
+
import { StudioSQLPlaygroundPanelState } from './panel-group/StudioSQLPlaygroundPanelState.js';
|
|
113
113
|
import type { QuickInputState } from './QuickInputState.js';
|
|
114
114
|
import { GlobalEndToEndWorkflowState } from './sidebar-state/end-to-end-workflow/GlobalEndToEndFlowState.js';
|
|
115
115
|
import {
|
|
@@ -183,7 +183,7 @@ export class EditorStore implements CommandRegistrar {
|
|
|
183
183
|
globalEndToEndWorkflowState: GlobalEndToEndWorkflowState;
|
|
184
184
|
devToolState: DevToolPanelState;
|
|
185
185
|
devMetadataState: DevMetadataState;
|
|
186
|
-
|
|
186
|
+
studioSqlPlaygroundState: StudioSQLPlaygroundPanelState;
|
|
187
187
|
|
|
188
188
|
modelImporterState: ModelImporterState;
|
|
189
189
|
projectConfigurationEditorState: ProjectConfigurationEditorState;
|
|
@@ -280,7 +280,7 @@ export class EditorStore implements CommandRegistrar {
|
|
|
280
280
|
this.changeDetectionState = new ChangeDetectionState(this, this.graphState);
|
|
281
281
|
this.devToolState = new DevToolPanelState(this);
|
|
282
282
|
this.devMetadataState = new DevMetadataState(this);
|
|
283
|
-
this.
|
|
283
|
+
this.studioSqlPlaygroundState = new StudioSQLPlaygroundPanelState(this);
|
|
284
284
|
this.embeddedQueryBuilderState = new EmbeddedQueryBuilderState(this);
|
|
285
285
|
// side bar panels
|
|
286
286
|
this.explorerTreeState = new ExplorerTreeState(this);
|
|
@@ -556,7 +556,7 @@ export class GraphEditFormModeState extends GraphEditorMode {
|
|
|
556
556
|
}
|
|
557
557
|
|
|
558
558
|
*onLeave(): GeneratorFn<void> {
|
|
559
|
-
this.editorStore.
|
|
559
|
+
this.editorStore.studioSqlPlaygroundState.setConnection(undefined);
|
|
560
560
|
this.editorStore.tabManagerState.cacheAndClose();
|
|
561
561
|
this.editorStore.applicationStore.layoutService.setShowBackdrop(false);
|
|
562
562
|
}
|