Skip to content

Commit cba78ed

Browse files
committed
feat: use monaco snippets for query templates
1 parent 2632b90 commit cba78ed

File tree

6 files changed

+112
-59
lines changed

6 files changed

+112
-59
lines changed

src/containers/Tenant/Query/NewSQL/NewSQL.tsx

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,16 @@ import React from 'react';
33
import {ChevronDown} from '@gravity-ui/icons';
44
import {Button, DropdownMenu} from '@gravity-ui/uikit';
55

6-
import {changeUserInput} from '../../../../store/reducers/executeQuery';
7-
import {useTypedDispatch} from '../../../../utils/hooks';
86
import {useChangeInputWithConfirmation} from '../../../../utils/hooks/withConfirmation/useChangeInputWithConfirmation';
7+
import {insertSnipperToEditor} from '../../../../utils/monaco/insertSnippet';
98
import {bindActions} from '../../utils/newSQLQueryActions';
109

1110
import i18n from './i18n';
1211

1312
export function NewSQL() {
14-
const dispatch = useTypedDispatch();
15-
16-
const insertTemplate = React.useCallback(
17-
(input: string) => {
18-
dispatch(changeUserInput({input}));
19-
},
20-
[dispatch],
21-
);
13+
const insertTemplate = React.useCallback((input: string) => {
14+
insertSnipperToEditor(input);
15+
}, []);
2216

2317
const onTemplateClick = useChangeInputWithConfirmation(insertTemplate);
2418

src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,22 @@ export default function QueryEditor(props: QueryEditorProps) {
200200
}
201201
});
202202

203+
const editorWillUnmount = () => {
204+
window.ydbEditor = undefined;
205+
};
206+
203207
const editorDidMount = (editor: Monaco.editor.IStandaloneCodeEditor, monaco: typeof Monaco) => {
208+
window.ydbEditor = editor;
204209
const keybindings = getKeyBindings(monaco);
210+
monaco.editor.registerCommand('insertSnippetToEditor', (_asessor, input: string) => {
211+
//suggestController is not properly typed yet in monaco-editor package
212+
const contribution = editor.getContribution<any>('snippetController2');
213+
if (contribution) {
214+
editor.focus();
215+
editor.setValue('');
216+
contribution.insert(input);
217+
}
218+
});
205219
initResizeHandler(editor);
206220
initUserPrompt(editor, getLastQueryText);
207221
editor.focus();
@@ -333,6 +347,7 @@ export default function QueryEditor(props: QueryEditorProps) {
333347
onChange={onChange}
334348
editorDidMount={editorDidMount}
335349
theme={`vs-${theme}`}
350+
editorWillUnmount={editorWillUnmount}
336351
/>
337352
</div>
338353
</div>

src/containers/Tenant/utils/schemaActions.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ import copy from 'copy-to-clipboard';
22
import type {NavigationTreeNodeType, NavigationTreeProps} from 'ydb-ui-components';
33

44
import type {AppDispatch} from '../../../store';
5-
import {changeUserInput} from '../../../store/reducers/executeQuery';
65
import type {GetTableSchemaDataParams} from '../../../store/reducers/tableSchemaData';
76
import {TENANT_PAGES_IDS, TENANT_QUERY_TABS_ID} from '../../../store/reducers/tenant/constants';
87
import {setQueryTab, setTenantPage} from '../../../store/reducers/tenant/tenant';
98
import type {QuerySettings} from '../../../types/store/query';
109
import createToast from '../../../utils/createToast';
10+
import {insertSnipperToEditor} from '../../../utils/monaco/insertSnippet';
1111
import {transformPath} from '../ObjectSummary/transformPath';
1212
import type {SchemaData} from '../Schema/SchemaViewer/types';
1313
import i18n from '../i18n';
@@ -75,13 +75,13 @@ const bindActions = (
7575
})
7676
: Promise.resolve(undefined);
7777

78-
userInputDataPromise.then((tableData) => {
79-
dispatch(changeUserInput({input: tmpl({...params, tableData})}));
80-
});
81-
78+
//order is important here: firstly we should open query tab and initialize editor (it will be set to window.ydbEditor), after that it is possible to insert snippet
8279
dispatch(setTenantPage(TENANT_PAGES_IDS.query));
8380
dispatch(setQueryTab(TENANT_QUERY_TABS_ID.newQuery));
8481
setActivePath(params.path);
82+
userInputDataPromise.then((tableData) => {
83+
insertSnipperToEditor(tmpl({...params, tableData}));
84+
});
8585
};
8686
if (getConfirmation) {
8787
const confirmedPromise = getConfirmation();

src/containers/Tenant/utils/schemaQueryTemplates.ts

Lines changed: 80 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@ export interface SchemaQueryParams {
99
export type TemplateFn = (params?: SchemaQueryParams) => string;
1010

1111
export const createTableTemplate = (params?: SchemaQueryParams) => {
12+
const tableName = params?.relativePath
13+
? `\`${params?.relativePath}/my_row_table\``
14+
: '${1:my_row_table}';
1215
return `-- docs: https://ydb.tech/en/docs/yql/reference/syntax/create_table
13-
CREATE TABLE \`${params?.relativePath || '$path'}/ydb_row_table\` (
16+
CREATE TABLE ${tableName} (
1417
category_id Uint64 NOT NULL,
1518
id Uint64,
1619
expire_at Datetime,
@@ -42,8 +45,11 @@ WITH (
4245
)`;
4346
};
4447
export const createColumnTableTemplate = (params?: SchemaQueryParams) => {
48+
const tableName = params?.relativePath
49+
? `\`${params?.relativePath}/my_column_table\``
50+
: '${1:my_column_table}';
4551
return `-- docs: https://ydb.tech/en/docs/yql/reference/syntax/create_table#olap-tables
46-
CREATE TABLE \`${params?.relativePath || '$path'}/ydb_column_table\` (
52+
CREATE TABLE ${tableName} (
4753
id Int64 NOT NULL,
4854
author Text,
4955
title Text,
@@ -57,63 +63,76 @@ export const createAsyncReplicationTemplate = () => {
5763
return `CREATE OBJECT secret_name (TYPE SECRET) WITH value="secret_value";
5864
5965
CREATE ASYNC REPLICATION my_replication
60-
FOR \`/remote_database/table_name\` AS \`local_table_name\` --[, \`/remote_database/another_table_name\` AS \`another_local_table_name\` ...]
66+
FOR \`/\${1:<remote_database>}/\${2:table_name}\` AS \${3:local_table_name} --[, \`/remote_database/another_table_name\` AS \`another_local_table_name\` ...]
6167
WITH (
62-
CONNECTION_STRING="grpcs://mydb.ydb.tech:2135/?database=/remote_database",
68+
CONNECTION_STRING="grpcs://mydb.ydb.tech:2135/?database=/\${1:remote_database}",
6369
TOKEN_SECRET_NAME = "secret_name"
6470
-- ENDPOINT="mydb.ydb.tech:2135",
65-
-- DATABASE=\`/remote_database\`,
71+
-- DATABASE=\`\${1:/remote_database}\`,
6672
-- USER="user",
6773
-- PASSWORD_SECRET_NAME="your_password"
6874
);`;
6975
};
7076
export const alterTableTemplate = (params?: SchemaQueryParams) => {
77+
const path = params?.relativePath ? `\`${params?.relativePath}\`` : '${1:<my_table>}';
78+
7179
return `-- docs: https://ydb.tech/docs/en/yql/reference/syntax/alter_table/
7280
73-
ALTER TABLE \`${params?.relativePath || '$path'}\`
81+
ALTER TABLE ${path}
7482
-- RENAME TO new_table_name
7583
-- DROP COLUMN some_existing_column
7684
ADD COLUMN numeric_column Int32;`;
7785
};
7886
export const selectQueryTemplate = (params?: SchemaQueryParams) => {
87+
const path = params?.relativePath ? `\`${params?.relativePath}\`` : '${1:<my_table>}';
7988
const columns = params?.tableData?.map((column) => '`' + column.name + '`').join(', ') || '*';
8089

8190
return `SELECT ${columns}
82-
FROM \`${params?.relativePath || '$path'}\`
91+
FROM ${path}
8392
LIMIT 10;`;
8493
};
8594
export const upsertQueryTemplate = (params?: SchemaQueryParams) => {
95+
const path = params?.relativePath ? `\`${params?.relativePath}\`` : '${1:<my_table>}';
8696
const columns =
87-
params?.tableData?.map((column) => `\`${column.name}\``).join(', ') || `\`id\`, \`name\``;
97+
params?.tableData?.map((column) => `\`${column.name}\``).join(', ') || `id, name`;
8898

89-
return `UPSERT INTO \`${params?.relativePath || '$path'}\`
99+
return `UPSERT INTO ${path}
90100
( ${columns} )
91101
VALUES ( );`;
92102
};
93103

94104
export const dropExternalTableTemplate = (params?: SchemaQueryParams) => {
95-
return `DROP EXTERNAL TABLE \`${params?.relativePath || '$path'}\`;`;
105+
const path = params?.relativePath ? `\`${params?.relativePath}\`` : '${1:my_table}';
106+
return `DROP EXTERNAL TABLE ${path};`;
96107
};
97108

98109
export const createExternalTableTemplate = (params?: SchemaQueryParams) => {
99110
// Remove data source name from path
100111
// to create table in the same folder with data source
101112
const targetPath = params?.relativePath.split('/').slice(0, -1).join('/');
102113

103-
return `CREATE EXTERNAL TABLE \`${targetPath || '$path'}/my_external_table\` (
114+
const target = targetPath
115+
? `\`${targetPath}/my_external_table\``
116+
: '${1:<path_to_table>}/${2:my_external_table_name}';
117+
118+
const source = params?.relativePath
119+
? `${params.relativePath}`
120+
: '${1:path_to_table}/${3:data_source_name}';
121+
return `CREATE EXTERNAL TABLE ${target} (
104122
column1 Int,
105123
column2 Int
106124
) WITH (
107-
DATA_SOURCE="${params?.relativePath || '$path'}",
125+
DATA_SOURCE="${source}",
108126
LOCATION="",
109127
FORMAT="json_as_string",
110128
\`file_pattern\`=""
111129
);`;
112130
};
113131

114132
export const createTopicTemplate = (params?: SchemaQueryParams) => {
133+
const path = params?.relativePath ? `\`${params?.relativePath}\`/my_topic` : '${1:my_topic}';
115134
return `-- docs: https://ydb.tech/docs/en/yql/reference/syntax/create-topic
116-
CREATE TOPIC \`${params?.relativePath || '$path'}/my_topic\` (
135+
CREATE TOPIC ${path} (
117136
CONSUMER consumer1,
118137
CONSUMER consumer2 WITH (read_from = Datetime('1970-01-01T00:00:00Z')) -- Sets up the message write time starting from which the consumer will receive data.
119138
-- Value type: Datetime OR Timestamp OR integer (unix-timestamp in the numeric format).
@@ -133,8 +152,9 @@ CREATE TOPIC \`${params?.relativePath || '$path'}/my_topic\` (
133152
};
134153

135154
export const alterTopicTemplate = (params?: SchemaQueryParams) => {
155+
const path = params?.relativePath ? `\`${params?.relativePath}\`` : '${1:<my_topic>}';
136156
return `-- docs: https://ydb.tech/en/docs/yql/reference/syntax/alter_topic
137-
ALTER TOPIC \`${params?.relativePath || '$path'}\`
157+
ALTER TOPIC ${path}
138158
ADD CONSUMER new_consumer WITH (read_from = Datetime('1970-01-01T00:00:00Z')), -- Sets up the message write time starting from which the consumer will receive data.
139159
-- Value type: Datetime OR Timestamp OR integer (unix-timestamp in the numeric format).
140160
-- Default value: now
@@ -155,43 +175,50 @@ ALTER TOPIC \`${params?.relativePath || '$path'}\`
155175
};
156176

157177
export const dropTopicTemplate = (params?: SchemaQueryParams) => {
158-
return `DROP TOPIC \`${params?.relativePath || '$path'}\`;`;
178+
const path = params?.relativePath ? `\`${params?.relativePath}\`` : '${1:<my_topic>}';
179+
return `DROP TOPIC ${path};`;
159180
};
160181

161182
export const createViewTemplate = (params?: SchemaQueryParams) => {
162-
return `CREATE VIEW \`${params?.relativePath || '$path'}/my_view\` WITH (security_invoker = TRUE) AS SELECT 1;`;
183+
const path = params?.relativePath ? `\`${params?.relativePath}\`/my_view` : '${1:my_view}';
184+
return `CREATE VIEW ${path} WITH (security_invoker = TRUE) AS SELECT 1;`;
163185
};
164186

165187
export const dropViewTemplate = (params?: SchemaQueryParams) => {
166-
return `DROP VIEW \`${params?.relativePath || '$path'}\`;`;
188+
const path = params?.relativePath ? `\`${params?.relativePath}\`` : '${1:<my_view>}';
189+
return `DROP VIEW ${path};`;
167190
};
168191
export const dropAsyncReplicationTemplate = (params?: SchemaQueryParams) => {
169-
return `DROP ASYNC REPLICATION \`${params?.relativePath || '$path'}\`;`;
192+
const path = params?.relativePath ? `\`${params?.relativePath}\`` : '${1:<my_replication>}';
193+
return `DROP ASYNC REPLICATION ${path};`;
170194
};
171195

172196
export const alterAsyncReplicationTemplate = (params?: SchemaQueryParams) => {
173-
return `ALTER ASYNC REPLICATION \`${params?.relativePath || '$path'}\` SET (STATE = "DONE", FAILOVER_MODE = "FORCE");`;
197+
const path = params?.relativePath ? `\`${params?.relativePath}\`` : '${1:<my_replication>}';
198+
return `ALTER ASYNC REPLICATION ${path} SET (STATE = "DONE", FAILOVER_MODE = "FORCE");`;
174199
};
175200

176201
export const addTableIndex = (params?: SchemaQueryParams) => {
177-
return `ALTER TABLE \`${params?.relativePath || '$path'}\` ADD INDEX \`$indexName\` GLOBAL ON (\`$columnName\`);`;
202+
const path = params?.relativePath ? `\`${params?.relativePath}\`` : '${1:<my_table>}';
203+
return `ALTER TABLE ${path} ADD INDEX \${2:index_name} GLOBAL ON (\${3:<column_name>});`;
178204
};
179205

180206
export const dropTableIndex = (params?: SchemaQueryParams) => {
181207
const indexName = params?.relativePath.split('/').pop();
182208
const path = params?.relativePath.split('/').slice(0, -1).join('/');
183-
return `ALTER TABLE \`${path || '$path'}\` DROP INDEX \`${indexName || '$indexName'}\`;`;
209+
return `ALTER TABLE \`${path || '${1:<my_table>}'}\` DROP INDEX ${indexName || '${2:<index_name>}'};`;
184210
};
185211

186212
export const createCdcStreamTemplate = (params?: SchemaQueryParams) => {
213+
const path = params?.relativePath ? `\`${params?.relativePath}\`` : '${1:<my_table>}';
187214
return `-- docs: https://ydb.tech/docs/en/yql/reference/syntax/create_changefeed
188-
ALTER TABLE \`${params?.relativePath || '$path'}\` ADD CHANGEFEED $name WITH (
189-
MODE = $mode, -- KEYS_ONLY, UPDATES, NEW_IMAGE, OLD_IMAGE, or NEW_AND_OLD_IMAGES
190-
FORMAT = $format, -- JSON or DEBEZIUM_JSON
191-
VIRTUAL_TIMESTAMPS = $virtualTimestamps, -- true or false
192-
RETENTION_PERIOD = $retentionPeriod, -- Interval value, e.g., Interval('PT24H')
193-
TOPIC_MIN_ACTIVE_PARTITIONS = $topicMinActivePartitions,
194-
INITIAL_SCAN = $initialScan -- true or false
215+
ALTER TABLE ${path} ADD CHANGEFEED \${2:changefeed_name} WITH (
216+
MODE = \${3:mode}, -- KEYS_ONLY, UPDATES, NEW_IMAGE, OLD_IMAGE, or NEW_AND_OLD_IMAGES
217+
FORMAT = \${4:format}, -- JSON or DEBEZIUM_JSON
218+
VIRTUAL_TIMESTAMPS = \${5:virtualTimestamps}, -- true or false
219+
RETENTION_PERIOD = \${6:retentionPeriod}, -- Interval value, e.g., Interval('PT24H')
220+
TOPIC_MIN_ACTIVE_PARTITIONS = \${7:topicMinActivePartitions},
221+
INITIAL_SCAN = \${8:initialScan} -- true or false
195222
)
196223
197224
-- MODE options:
@@ -204,46 +231,50 @@ ALTER TABLE \`${params?.relativePath || '$path'}\` ADD CHANGEFEED $name WITH (
204231

205232
export const createGroupTemplate = () => {
206233
return `-- docs: https://ydb.tech/docs/en/yql/reference/syntax/create-group
207-
CREATE GROUP $group_name
234+
CREATE GROUP \${1:group_name}
208235
-- group_name: The name of the group. It may contain lowercase Latin letters and digits.`;
209236
};
210237

211238
export const createUserTemplate = () => {
212239
return `-- docs: https://ydb.tech/docs/en/yql/reference/syntax/create-user
213-
CREATE USER $user_name [option]
240+
CREATE USER \${1:user_name} [option]
214241
-- user_name: The name of the user. It may contain lowercase Latin letters and digits.
215242
-- option: The password of the user:
216243
-- PASSWORD 'password' creates a user with the password password. The ENCRYPTED option is always enabled.
217244
-- PASSWORD NULL creates a user with an empty password.`;
218245
};
219246

220247
export const deleteRowsTemplate = (params?: SchemaQueryParams) => {
248+
const path = params?.relativePath ? `\`${params?.relativePath}\`` : '${1:<my_table>}';
221249
return `-- docs: https://ydb.tech/docs/en/yql/reference/syntax/delete
222-
DELETE FROM \`${params?.relativePath || '$path'}\`
223-
WHERE Key1 == $key1 AND Key2 >= $key2;`;
250+
DELETE FROM ${path}
251+
WHERE Key1 == \${2:key1} AND Key2 >= \${3:key2};`;
224252
};
225253

226254
export const dropGroupTemplate = () => {
227255
return `-- docs: https://ydb.tech/docs/en/yql/reference/syntax/drop-group
228-
DROP GROUP [ IF EXISTS ] $group_name [, ...]
256+
DROP GROUP [ IF EXISTS ] \${1:<group_name>} [, ...]
229257
230258
-- IF EXISTS: Suppress an error if the group doesn't exist.
231259
-- group_name: The name of the group to be deleted.`;
232260
};
233261

234262
export const dropUserTemplate = () => {
235263
return `-- docs: https://ydb.tech/docs/en/yql/reference/syntax/drop-user
236-
DROP USER [ IF EXISTS ] $user_name [, ...]
264+
DROP USER [ IF EXISTS ] \${1:<user_name>} [, ...]
237265
238266
-- IF EXISTS: Suppress an error if the user doesn't exist.
239267
-- user_name: The name of the user to be deleted.`;
240268
};
241269

242270
export const grantPrivilegeTemplate = (params?: SchemaQueryParams) => {
271+
const path = params?.relativePath
272+
? `\`${params?.relativePath}\``
273+
: '${2:<path_to_scheme_object>}';
243274
return `
244-
GRANT $permission_name [, ...] | ALL [PRIVILEGES]
245-
ON \`${params?.relativePath || '$path_to_scheme_object'}\` [, ...]
246-
TO $role_name [, ...]
275+
GRANT \${1:<permission_name>} [, ...] | ALL [PRIVILEGES]
276+
ON ${path} [, ...]
277+
TO \${3:<role_name>} [, ...]
247278
[WITH GRANT OPTION]
248279
249280
-- permission_name: The name of the access right to schema objects that needs to be assigned.
@@ -256,10 +287,13 @@ TO $role_name [, ...]
256287
};
257288

258289
export const revokePrivilegeTemplate = (params?: SchemaQueryParams) => {
290+
const path = params?.relativePath
291+
? `\`${params?.relativePath}\``
292+
: '${2:<path_to_scheme_object>}';
259293
return `
260-
REVOKE [GRANT OPTION FOR] $permission_name [, ...] | ALL [PRIVILEGES]
261-
ON \`${params?.relativePath || '$path_to_scheme_object'}\` [, ...]
262-
FROM $role_name [, ...]
294+
REVOKE [GRANT OPTION FOR] \${1:<permission_name>} [, ...] | ALL [PRIVILEGES]
295+
ON ${path} [, ...]
296+
FROM \${3:<role_name>} [, ...]
263297
264298
-- permission_name: The name of the access right to schema objects that needs to be revoked.
265299
-- path_to_scheme_object: The path to the schema object from which rights are being revoked.
@@ -270,12 +304,14 @@ FROM $role_name [, ...]
270304
};
271305

272306
export const updateTableTemplate = (params?: SchemaQueryParams) => {
307+
const path = params?.relativePath ? `\`${params?.relativePath}\`` : '${1:<my_table>}';
273308
return `-- docs: https://ydb.tech/docs/en/yql/reference/syntax/update
274-
UPDATE \`${params?.relativePath || '$path'}\`
275-
SET Value1 = YQL::ToString($value2 + 1), Value2 = $value2 - 1
276-
WHERE Key1 > $key1;`;
309+
UPDATE ${path}
310+
SET Value1 = YQL::ToString(\${2:value2} + 1), Value2 = \${3:value2} - 1
311+
WHERE Key1 > \${4:key1};`;
277312
};
278313

279314
export const dropTableTemplate = (params?: SchemaQueryParams) => {
280-
return `DROP TABLE \`${params?.relativePath || '$path'}\`;`;
315+
const path = params?.relativePath ? `\`${params?.relativePath}\`` : '${1:<my_table>}';
316+
return `DROP TABLE ${path};`;
281317
};

src/types/window.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ interface Window {
3232
Rum?: RumCounter;
3333
};
3434

35+
ydbEditor?: Monaco.editor.IStandaloneCodeEditor;
36+
3537
web_version?: boolean;
3638
custom_backend?: string;
3739
meta_backend?: string;

src/utils/monaco/insertSnippet.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export function insertSnipperToEditor(input: string) {
2+
if (!window.ydbEditor) {
3+
console.error('Monaco editor not found');
4+
}
5+
window.ydbEditor?.trigger(undefined, 'insertSnippetToEditor', input);
6+
}

0 commit comments

Comments
 (0)