ydb-embedded-ui 3.5.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/components/ClusterInfo/ClusterInfo.tsx +3 -3
  3. package/dist/{containers/Nodes/NodesTable.scss → components/NodeHostWrapper/NodeHostWrapper.scss} +4 -6
  4. package/dist/components/NodeHostWrapper/NodeHostWrapper.tsx +60 -0
  5. package/dist/containers/AsideNavigation/AsideNavigation.tsx +1 -11
  6. package/dist/containers/Header/Header.tsx +1 -1
  7. package/dist/containers/Nodes/getNodesColumns.tsx +7 -46
  8. package/dist/containers/Storage/StorageNodes/StorageNodes.scss +0 -24
  9. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +2 -39
  10. package/dist/containers/Tenant/QueryEditor/QueriesHistory/QueriesHistory.tsx +3 -3
  11. package/dist/containers/Tenant/QueryEditor/QueryDuration/QueryDuration.scss +8 -0
  12. package/dist/containers/Tenant/QueryEditor/QueryDuration/QueryDuration.tsx +21 -0
  13. package/dist/containers/Tenant/QueryEditor/QueryEditor.js +58 -83
  14. package/dist/containers/Tenant/QueryEditor/QueryEditor.scss +0 -33
  15. package/dist/containers/Tenant/QueryEditor/QueryEditorControls/OldQueryEditorControls.tsx +83 -0
  16. package/dist/containers/Tenant/QueryEditor/QueryEditorControls/QueryEditorControls.scss +57 -0
  17. package/dist/containers/Tenant/QueryEditor/QueryEditorControls/QueryEditorControls.tsx +84 -0
  18. package/dist/containers/Tenant/QueryEditor/QueryEditorControls/shared.ts +23 -0
  19. package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.js +12 -23
  20. package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.js +4 -6
  21. package/dist/containers/Tenant/QueryEditor/i18n/en.json +3 -0
  22. package/dist/containers/Tenant/QueryEditor/i18n/index.ts +11 -0
  23. package/dist/containers/Tenant/QueryEditor/i18n/ru.json +3 -0
  24. package/dist/containers/Tenants/Tenants.js +1 -1
  25. package/dist/containers/UserSettings/UserSettings.tsx +30 -1
  26. package/dist/services/api.ts +383 -0
  27. package/dist/store/reducers/{cluster.js → cluster/cluster.ts} +9 -14
  28. package/dist/store/reducers/cluster/types.ts +13 -0
  29. package/dist/store/reducers/executeQuery.ts +12 -37
  30. package/dist/store/reducers/executeTopQueries.ts +2 -2
  31. package/dist/store/reducers/{explainQuery.js → explainQuery.ts} +44 -59
  32. package/dist/store/reducers/index.ts +5 -4
  33. package/dist/store/reducers/settings.js +19 -17
  34. package/dist/store/reducers/{tenants.js → tenants/tenants.ts} +14 -9
  35. package/dist/store/reducers/tenants/types.ts +17 -0
  36. package/dist/store/utils.ts +3 -2
  37. package/dist/types/api/acl.ts +25 -0
  38. package/dist/types/api/cluster.ts +3 -0
  39. package/dist/types/api/compute.ts +5 -3
  40. package/dist/types/api/error.ts +14 -0
  41. package/dist/types/api/netInfo.ts +48 -0
  42. package/dist/types/api/nodes.ts +5 -3
  43. package/dist/types/api/pdisk.ts +11 -2
  44. package/dist/types/api/query.ts +226 -117
  45. package/dist/types/api/storage.ts +5 -3
  46. package/dist/types/api/tenant.ts +18 -3
  47. package/dist/types/api/vdisk.ts +10 -2
  48. package/dist/types/api/whoami.ts +19 -0
  49. package/dist/types/store/executeQuery.ts +4 -8
  50. package/dist/types/store/explainQuery.ts +38 -0
  51. package/dist/types/store/query.ts +23 -3
  52. package/dist/types/window.d.ts +5 -0
  53. package/dist/utils/constants.ts +2 -1
  54. package/dist/utils/error.ts +25 -0
  55. package/dist/utils/hooks/useTypedSelector.ts +2 -2
  56. package/dist/utils/index.js +0 -49
  57. package/dist/utils/nodes.ts +3 -1
  58. package/dist/utils/prepareQueryExplain.ts +7 -24
  59. package/dist/utils/query.test.ts +153 -231
  60. package/dist/utils/query.ts +44 -78
  61. package/dist/utils/timeParsers/i18n/en.json +9 -9
  62. package/dist/utils/timeParsers/i18n/ru.json +9 -9
  63. package/dist/utils/timeParsers/parsers.ts +9 -0
  64. package/dist/utils/utils.js +1 -2
  65. package/package.json +1 -1
  66. package/dist/services/api.d.ts +0 -86
  67. package/dist/services/api.js +0 -278
@@ -0,0 +1,11 @@
1
+ import {i18n, Lang} from '../../../../utils/i18n';
2
+
3
+ import en from './en.json';
4
+ import ru from './ru.json';
5
+
6
+ const COMPONENT = 'ydb-query-editor';
7
+
8
+ i18n.registerKeyset(Lang.En, COMPONENT, en);
9
+ i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
10
+
11
+ export default i18n.keyset(COMPONENT);
@@ -0,0 +1,3 @@
1
+ {
2
+ "controls.query-mode-selector_type": "Тип:"
3
+ }
@@ -20,7 +20,7 @@ import {formatCPU, formatBytesToGigabyte, formatNumber} from '../../utils';
20
20
  import {hideTooltip, showTooltip} from '../../store/reducers/tooltip';
21
21
  import {withSearch} from '../../HOCS';
22
22
  import {ALL, DEFAULT_TABLE_SETTINGS, TENANT_INITIAL_TAB_KEY} from '../../utils/constants';
23
- import {getTenantsInfo} from '../../store/reducers/tenants';
23
+ import {getTenantsInfo} from '../../store/reducers/tenants/tenants';
24
24
  import {changeFilter, getSettingValue} from '../../store/reducers/settings';
25
25
  import {setHeader} from '../../store/reducers/header';
26
26
 
@@ -9,6 +9,7 @@ import favoriteFilledIcon from '../../assets/icons/star.svg';
9
9
  import flaskIcon from '../../assets/icons/flask.svg';
10
10
 
11
11
  import {
12
+ ENABLE_QUERY_MODES_FOR_EXPLAIN,
12
13
  INVERTED_DISKS_KEY,
13
14
  THEME_KEY,
14
15
  USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY,
@@ -39,6 +40,10 @@ function UserSettings(props: any) {
39
40
  props.setSettingValue(USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY, String(value));
40
41
  };
41
42
 
43
+ const _onExplainQueryModesChangeHandler = (value: boolean) => {
44
+ props.setSettingValue(ENABLE_QUERY_MODES_FOR_EXPLAIN, String(value));
45
+ };
46
+
42
47
  const renderBreakNodesSettingsItem = (title: ReactNode) => {
43
48
  return (
44
49
  <div className={b('item-with-popup')}>
@@ -52,6 +57,19 @@ function UserSettings(props: any) {
52
57
  );
53
58
  };
54
59
 
60
+ const renderEnableExplainQueryModesItem = (title: ReactNode) => {
61
+ return (
62
+ <div className={b('item-with-popup')}>
63
+ {title}
64
+ <HelpPopover
65
+ content="Enable script | scan query mode selector for both run and explain. May not work on some versions"
66
+ contentClassName={b('popup')}
67
+ hasArrow={true}
68
+ />
69
+ </div>
70
+ );
71
+ };
72
+
55
73
  return (
56
74
  <Settings>
57
75
  <Settings.Page
@@ -86,6 +104,15 @@ function UserSettings(props: any) {
86
104
  onUpdate={_onNodesEndpointChangeHandler}
87
105
  />
88
106
  </Settings.Item>
107
+ <Settings.Item
108
+ title="Enable query modes for explain"
109
+ renderTitleComponent={renderEnableExplainQueryModesItem}
110
+ >
111
+ <Switch
112
+ checked={props.enableQueryModesForExplain}
113
+ onUpdate={_onExplainQueryModesChangeHandler}
114
+ />
115
+ </Settings.Item>
89
116
  </Settings.Section>
90
117
  </Settings.Page>
91
118
  </Settings>
@@ -93,12 +120,14 @@ function UserSettings(props: any) {
93
120
  }
94
121
 
95
122
  const mapStateToProps = (state: any) => {
96
- const {theme, invertedDisks, useNodesEndpointInDiagnostics} = state.settings.userSettings;
123
+ const {theme, invertedDisks, useNodesEndpointInDiagnostics, enableQueryModesForExplain} =
124
+ state.settings.userSettings;
97
125
 
98
126
  return {
99
127
  theme,
100
128
  invertedDisks: JSON.parse(invertedDisks),
101
129
  useNodesEndpointInDiagnostics: JSON.parse(useNodesEndpointInDiagnostics),
130
+ enableQueryModesForExplain: JSON.parse(enableQueryModesForExplain),
102
131
  };
103
132
  };
104
133
 
@@ -0,0 +1,383 @@
1
+ import AxiosWrapper from '@gravity-ui/axios-wrapper';
2
+
3
+ import type {
4
+ Actions,
5
+ ExplainActions,
6
+ ExplainResponse,
7
+ QueryAPIResponse,
8
+ Schemas,
9
+ } from '../types/api/query';
10
+ import type {
11
+ TDomainKey,
12
+ TEvTabletStateResponse,
13
+ UnmergedTEvTabletStateResponse,
14
+ } from '../types/api/tablet';
15
+ import type {TMetaInfo} from '../types/api/acl';
16
+ import type {TClusterInfo} from '../types/api/cluster';
17
+ import type {TComputeInfo} from '../types/api/compute';
18
+ import type {DescribeConsumerResult} from '../types/api/consumer';
19
+ import type {HealthCheckAPIResponse} from '../types/api/healthcheck';
20
+ import type {TNetInfo} from '../types/api/netInfo';
21
+ import type {TNodesInfo} from '../types/api/nodes';
22
+ import type {TEvNodesInfo} from '../types/api/nodesList';
23
+ import type {TEvDescribeSchemeResult} from '../types/api/schema';
24
+ import type {TStorageInfo} from '../types/api/storage';
25
+ import type {TEvSystemStateResponse} from '../types/api/systemState';
26
+ import type {TTenantInfo, TTenants} from '../types/api/tenant';
27
+ import type {DescribeTopicResult} from '../types/api/topic';
28
+ import type {TEvPDiskStateResponse} from '../types/api/pdisk';
29
+ import type {TEvVDiskStateResponse} from '../types/api/vdisk';
30
+ import type {TUserToken} from '../types/api/whoami';
31
+ import type {INodesApiRequestParams} from '../types/store/nodes';
32
+
33
+ import {backend as BACKEND} from '../store';
34
+
35
+ const config = {withCredentials: !window.custom_backend};
36
+
37
+ const settingsApi = window.web_version ? window.systemSettings?.settingsApi : undefined;
38
+
39
+ type AxiosOptions = {
40
+ concurrentId?: string;
41
+ };
42
+
43
+ export class YdbEmbeddedAPI extends AxiosWrapper {
44
+ getPath(path: string) {
45
+ return `${BACKEND}${path}`;
46
+ }
47
+ getClusterInfo(clusterName?: string) {
48
+ return this.get<TClusterInfo>(this.getPath('/viewer/json/cluster'), {
49
+ name: clusterName,
50
+ tablets: true,
51
+ });
52
+ }
53
+ getNodeInfo(id?: string) {
54
+ return this.get<TEvSystemStateResponse>(this.getPath('/viewer/json/sysinfo?enums=true'), {
55
+ node_id: id,
56
+ });
57
+ }
58
+ getTenants(clusterName?: string) {
59
+ return this.get<TTenantInfo>(this.getPath('/viewer/json/tenantinfo'), {
60
+ tablets: 1,
61
+ storage: 1,
62
+ cluster_name: clusterName,
63
+ });
64
+ }
65
+ getTenantInfo({path}: {path: string}) {
66
+ return this.get<TTenantInfo>(this.getPath('/viewer/json/tenantinfo'), {
67
+ path,
68
+ tablets: true,
69
+ storage: true,
70
+ });
71
+ }
72
+ getNodes(
73
+ {tenant, filter, storage, type = 'any', tablets = true}: INodesApiRequestParams,
74
+ {concurrentId}: AxiosOptions = {},
75
+ ) {
76
+ return this.get<TNodesInfo>(
77
+ this.getPath('/viewer/json/nodes?enums=true'),
78
+ {
79
+ tenant,
80
+ with: filter,
81
+ storage,
82
+ type,
83
+ tablets,
84
+ },
85
+ {
86
+ concurrentId,
87
+ },
88
+ );
89
+ }
90
+ getCompute(path: string) {
91
+ return this.get<TComputeInfo>(this.getPath('/viewer/json/compute?enums=true'), {path});
92
+ }
93
+ getStorageInfo(
94
+ {
95
+ tenant,
96
+ filter,
97
+ nodeId,
98
+ }: {
99
+ tenant: string;
100
+ filter: string;
101
+ nodeId: string;
102
+ },
103
+ {concurrentId}: AxiosOptions = {},
104
+ ) {
105
+ return this.get<TStorageInfo>(
106
+ this.getPath(`/viewer/json/storage?enums=true`),
107
+ {
108
+ tenant,
109
+ node_id: nodeId,
110
+ with: filter,
111
+ },
112
+ {
113
+ concurrentId,
114
+ },
115
+ );
116
+ }
117
+ getPdiskInfo(nodeId: string | number, pdiskId: string | number) {
118
+ return this.get<TEvPDiskStateResponse>(this.getPath('/viewer/json/pdiskinfo?enums=true'), {
119
+ filter: `(NodeId=${nodeId}${pdiskId ? `;PDiskId=${pdiskId}` : ''})`,
120
+ });
121
+ }
122
+ getVdiskInfo({
123
+ vdiskId,
124
+ pdiskId,
125
+ nodeId,
126
+ }: {
127
+ vdiskId: string | number;
128
+ pdiskId: string | number;
129
+ nodeId: string | number;
130
+ }) {
131
+ return this.get<TEvVDiskStateResponse>(this.getPath('/viewer/json/vdiskinfo?enums=true'), {
132
+ filter: `(VDiskId=${vdiskId ?? ''};PDiskId=${pdiskId ?? ''};NodeId=${nodeId ?? ''})`,
133
+ });
134
+ }
135
+ getGroupInfo(groupId: string | number) {
136
+ return this.get<TStorageInfo>(this.getPath('/viewer/json/storage?enums=true'), {
137
+ group_id: groupId,
138
+ });
139
+ }
140
+ getHostInfo() {
141
+ return this.get<TEvSystemStateResponse>(
142
+ this.getPath('/viewer/json/sysinfo?node_id=.&enums=true'),
143
+ {},
144
+ );
145
+ }
146
+ getTabletsInfo({nodes = [], path}: {nodes?: string[]; path?: string}) {
147
+ const filter = nodes.length > 0 && `(NodeId=[${nodes.join(',')}])`;
148
+ return this.get<TEvTabletStateResponse>(this.getPath('/viewer/json/tabletinfo'), {
149
+ filter,
150
+ path,
151
+ enums: true,
152
+ });
153
+ }
154
+ getSchema({path}: {path: string}, {concurrentId}: AxiosOptions = {}) {
155
+ return this.get<TEvDescribeSchemeResult>(
156
+ this.getPath('/viewer/json/describe'),
157
+ {
158
+ path,
159
+ enums: true,
160
+ backup: false,
161
+ private: true,
162
+ partition_config: true,
163
+ partition_stats: true,
164
+ partitioning_info: true,
165
+ subs: 1,
166
+ },
167
+ {concurrentId: concurrentId || `getSchema|${path}`},
168
+ );
169
+ }
170
+ getDescribe({path}: {path: string}, {concurrentId}: AxiosOptions = {}) {
171
+ return this.get<TEvDescribeSchemeResult>(
172
+ this.getPath('/viewer/json/describe'),
173
+ {
174
+ path,
175
+ enums: true,
176
+ partition_stats: true,
177
+ subs: 0,
178
+ },
179
+ {concurrentId: concurrentId || `getDescribe|${path}`},
180
+ );
181
+ }
182
+ getSchemaAcl({path}: {path: string}) {
183
+ return this.get<TMetaInfo>(
184
+ this.getPath('/viewer/json/acl'),
185
+ {
186
+ path,
187
+ },
188
+ {concurrentId: `getSchemaAcl|${path}`},
189
+ );
190
+ }
191
+ getHeatmapData({path}: {path: string}) {
192
+ return this.get<TEvDescribeSchemeResult>(this.getPath('/viewer/json/describe'), {
193
+ path,
194
+ enums: true,
195
+ backup: false,
196
+ children: false,
197
+ partition_config: false,
198
+ partition_stats: true,
199
+ });
200
+ }
201
+ getNetwork(path: string) {
202
+ return this.get<TNetInfo>(this.getPath('/viewer/json/netinfo'), {
203
+ enums: true,
204
+ path,
205
+ });
206
+ }
207
+ getTopic({path}: {path?: string}, {concurrentId}: AxiosOptions = {}) {
208
+ return this.get<DescribeTopicResult>(
209
+ this.getPath('/viewer/json/describe_topic'),
210
+ {
211
+ enums: true,
212
+ include_stats: true,
213
+ path,
214
+ },
215
+ {concurrentId: concurrentId || 'getTopic'},
216
+ );
217
+ }
218
+ getConsumer(
219
+ {path, consumer}: {path?: string; consumer?: string},
220
+ {concurrentId}: AxiosOptions = {},
221
+ ) {
222
+ return this.get<DescribeConsumerResult>(
223
+ this.getPath('/viewer/json/describe_consumer'),
224
+ {
225
+ enums: true,
226
+ include_stats: true,
227
+ path,
228
+ consumer,
229
+ },
230
+ {concurrentId: concurrentId || 'getConsumer'},
231
+ );
232
+ }
233
+ getPoolInfo(poolName: string) {
234
+ return this.get<TStorageInfo>(this.getPath('/viewer/json/storage'), {
235
+ pool: poolName,
236
+ enums: true,
237
+ });
238
+ }
239
+ getTablet({id}: {id?: string}) {
240
+ return this.get<TEvTabletStateResponse>(
241
+ this.getPath(`/viewer/json/tabletinfo?filter=(TabletId=${id})`),
242
+ {
243
+ enums: true,
244
+ },
245
+ );
246
+ }
247
+ getTabletHistory({id}: {id?: string}) {
248
+ return this.get<UnmergedTEvTabletStateResponse>(
249
+ this.getPath(`/viewer/json/tabletinfo?filter=(TabletId=${id})`),
250
+ {
251
+ enums: true,
252
+ merge: false,
253
+ },
254
+ );
255
+ }
256
+ getNodesList() {
257
+ return this.get<TEvNodesInfo>(this.getPath('/viewer/json/nodelist'), {enums: true});
258
+ }
259
+ getTenantsList() {
260
+ return this.get<TTenants>(this.getPath('/viewer/json/tenants'), {
261
+ enums: true,
262
+ state: 0,
263
+ });
264
+ }
265
+ sendQuery<Action extends Actions, Schema extends Schemas = undefined>(
266
+ {
267
+ query,
268
+ database,
269
+ action,
270
+ stats,
271
+ schema,
272
+ }: {
273
+ query?: string;
274
+ database?: string;
275
+ action?: Action;
276
+ stats?: string;
277
+ schema?: Schema;
278
+ },
279
+ {concurrentId}: AxiosOptions = {},
280
+ ) {
281
+ return this.post<QueryAPIResponse<Action, Schema>>(
282
+ this.getPath(`/viewer/json/query${schema ? `?schema=${schema}` : ''}`),
283
+ {
284
+ query,
285
+ database,
286
+ action,
287
+ stats,
288
+ timeout: 600000,
289
+ },
290
+ null,
291
+ {
292
+ concurrentId,
293
+ timeout: 9 * 60 * 1000,
294
+ },
295
+ );
296
+ }
297
+ getExplainQuery<Action extends ExplainActions>(
298
+ query: string,
299
+ database: string,
300
+ action: Action,
301
+ ) {
302
+ return this.post<ExplainResponse<Action>>(
303
+ this.getPath('/viewer/json/query'),
304
+ {
305
+ query,
306
+ database,
307
+ action: action || 'explain',
308
+ timeout: 600000,
309
+ },
310
+ null,
311
+ );
312
+ }
313
+ getExplainQueryAst(query: string, database: string) {
314
+ return this.post<ExplainResponse<'explain-ast'>>(
315
+ this.getPath('/viewer/json/query'),
316
+ {
317
+ query,
318
+ database,
319
+ action: 'explain-ast',
320
+ timeout: 600000,
321
+ },
322
+ null,
323
+ );
324
+ }
325
+ getHotKeys(path: string, enableSampling: boolean) {
326
+ return this.get(this.getPath('/viewer/json/hotkeys'), {
327
+ path,
328
+ enable_sampling: enableSampling,
329
+ });
330
+ }
331
+ getHealthcheckInfo(database: string) {
332
+ return this.get<HealthCheckAPIResponse>(this.getPath('/viewer/json/healthcheck'), {
333
+ tenant: database,
334
+ });
335
+ }
336
+ killTablet(id?: string) {
337
+ return this.get<string>(this.getPath(`/tablets?KillTabletID=${id}`), null);
338
+ }
339
+ stopTablet(id?: string, hiveId?: string) {
340
+ return this.get<string>(
341
+ this.getPath(`/tablets/app?TabletID=${hiveId}&page=StopTablet&tablet=${id}`),
342
+ null,
343
+ );
344
+ }
345
+ resumeTablet(id?: string, hiveId?: string) {
346
+ return this.get<string>(
347
+ this.getPath(`/tablets/app?TabletID=${hiveId}&page=ResumeTablet&tablet=${id}`),
348
+ null,
349
+ );
350
+ }
351
+ getTabletDescribe(tenantId: TDomainKey) {
352
+ return this.get<TEvDescribeSchemeResult>(this.getPath('/viewer/json/describe'), {
353
+ schemeshard_id: tenantId?.SchemeShard,
354
+ path_id: tenantId?.PathId,
355
+ });
356
+ }
357
+ postSetting(name: string, value: string) {
358
+ return this.request({
359
+ method: 'PATCH',
360
+ url: settingsApi || '',
361
+ data: {[name]: value},
362
+ });
363
+ }
364
+ authenticate(user: string, password: string) {
365
+ return this.post(
366
+ this.getPath('/login'),
367
+ {
368
+ user,
369
+ password,
370
+ },
371
+ null,
372
+ );
373
+ }
374
+ logout() {
375
+ return this.post(this.getPath('/logout'), null, null);
376
+ }
377
+ whoami() {
378
+ return this.get<TUserToken>(this.getPath('/viewer/json/whoami'), null);
379
+ }
380
+ }
381
+
382
+ const api = new YdbEmbeddedAPI({config: config});
383
+ window.api = api;
@@ -1,11 +1,14 @@
1
- import {createRequestActionTypes, createApiRequest} from '../utils';
2
- import '../../services/api';
1
+ import type {Reducer} from 'redux';
3
2
 
4
- const FETCH_CLUSTER = createRequestActionTypes('cluster', 'FETCH_CLUSTER');
3
+ import '../../../services/api';
4
+ import {createRequestActionTypes, createApiRequest} from '../../utils';
5
+ import type {ClusterAction, ClusterState} from './types';
6
+
7
+ export const FETCH_CLUSTER = createRequestActionTypes('cluster', 'FETCH_CLUSTER');
5
8
 
6
9
  const initialState = {loading: true, wasLoaded: false};
7
10
 
8
- const cluster = function (state = initialState, action) {
11
+ const cluster: Reducer<ClusterState, ClusterAction> = (state = initialState, action) => {
9
12
  switch (action.type) {
10
13
  case FETCH_CLUSTER.REQUEST: {
11
14
  return {
@@ -14,17 +17,9 @@ const cluster = function (state = initialState, action) {
14
17
  };
15
18
  }
16
19
  case FETCH_CLUSTER.SUCCESS: {
17
- const {data} = action;
18
- const clusterInfo = data.cluster ? data.cluster.cluster : data;
19
- const clusterName = data.cluster?.title || data.Name;
20
20
  return {
21
21
  ...state,
22
- data: {
23
- ...clusterInfo,
24
- balancer: data.cluster?.balancer,
25
- solomon: data.cluster?.solomon,
26
- Name: clusterName,
27
- },
22
+ data: action.data,
28
23
  loading: false,
29
24
  wasLoaded: true,
30
25
  error: undefined,
@@ -42,7 +37,7 @@ const cluster = function (state = initialState, action) {
42
37
  }
43
38
  };
44
39
 
45
- export function getClusterInfo(clusterName) {
40
+ export function getClusterInfo(clusterName?: string) {
46
41
  return createApiRequest({
47
42
  request: window.api.getClusterInfo(clusterName),
48
43
  actions: FETCH_CLUSTER,
@@ -0,0 +1,13 @@
1
+ import {FETCH_CLUSTER} from './cluster';
2
+
3
+ import type {TClusterInfo} from '../../../types/api/cluster';
4
+ import type {ApiRequestAction} from '../../utils';
5
+
6
+ export interface ClusterState {
7
+ loading: boolean;
8
+ wasLoaded: boolean;
9
+ data?: TClusterInfo;
10
+ error?: unknown;
11
+ }
12
+
13
+ export type ClusterAction = ApiRequestAction<typeof FETCH_CLUSTER, TClusterInfo, unknown>;
@@ -1,21 +1,20 @@
1
1
  import type {Reducer} from 'redux';
2
2
 
3
- import type {Actions} from '../../types/api/query';
3
+ import type {ExecuteActions} from '../../types/api/query';
4
4
  import type {
5
5
  ExecuteQueryAction,
6
6
  ExecuteQueryState,
7
7
  MonacoHotKeyAction,
8
- RunAction,
9
8
  } from '../../types/store/executeQuery';
9
+ import type {QueryRequestParams, QueryModes} from '../../types/store/query';
10
10
  import {getValueFromLS, parseJson} from '../../utils/utils';
11
- import {QUERIES_HISTORY_KEY, QUERY_INITIAL_RUN_ACTION_KEY} from '../../utils/constants';
11
+ import {QUERIES_HISTORY_KEY} from '../../utils/constants';
12
12
  import {parseQueryAPIExecuteResponse} from '../../utils/query';
13
+ import {parseQueryError} from '../../utils/error';
13
14
  import '../../services/api';
14
15
 
15
16
  import {createRequestActionTypes, createApiRequest} from '../utils';
16
17
 
17
- import {readSavedSettingsValue} from './settings';
18
-
19
18
  const MAXIMUM_QUERIES_IN_HISTORY = 20;
20
19
 
21
20
  export const SEND_QUERY = createRequestActionTypes('query', 'SEND_QUERY');
@@ -24,18 +23,12 @@ const CHANGE_USER_INPUT = 'query/CHANGE_USER_INPUT';
24
23
  const SAVE_QUERY_TO_HISTORY = 'query/SAVE_QUERY_TO_HISTORY';
25
24
  const GO_TO_PREVIOUS_QUERY = 'query/GO_TO_PREVIOUS_QUERY';
26
25
  const GO_TO_NEXT_QUERY = 'query/GO_TO_NEXT_QUERY';
27
- const SELECT_RUN_ACTION = 'query/SELECT_RUN_ACTION';
28
26
  const MONACO_HOT_KEY = 'query/MONACO_HOT_KEY';
29
27
 
30
28
  const queriesHistoryInitial: string[] = parseJson(getValueFromLS(QUERIES_HISTORY_KEY, '[]'));
31
29
 
32
30
  const sliceLimit = queriesHistoryInitial.length - MAXIMUM_QUERIES_IN_HISTORY;
33
31
 
34
- export const RUN_ACTIONS_VALUES = {
35
- script: 'execute-script',
36
- scan: 'execute-scan',
37
- } as const;
38
-
39
32
  export const MONACO_HOT_KEY_ACTIONS = {
40
33
  sendQuery: 'sendQuery',
41
34
  goPrev: 'goPrev',
@@ -53,7 +46,6 @@ const initialState = {
53
46
  ? MAXIMUM_QUERIES_IN_HISTORY - 1
54
47
  : queriesHistoryInitial.length - 1,
55
48
  },
56
- runAction: readSavedSettingsValue(QUERY_INITIAL_RUN_ACTION_KEY, RUN_ACTIONS_VALUES.script),
57
49
  monacoHotKey: null,
58
50
  };
59
51
 
@@ -79,22 +71,14 @@ const executeQuery: Reducer<ExecuteQueryState, ExecuteQueryAction> = (
79
71
  error: undefined,
80
72
  };
81
73
  }
82
- // 401 Unauthorized error is handled by GenericAPI
83
74
  case SEND_QUERY.FAILURE: {
84
75
  return {
85
76
  ...state,
86
- error: action.error || 'Unauthorized',
77
+ error: parseQueryError(action.error),
87
78
  loading: false,
88
79
  };
89
80
  }
90
81
 
91
- case SELECT_RUN_ACTION: {
92
- return {
93
- ...state,
94
- runAction: action.data,
95
- };
96
- }
97
-
98
82
  case CHANGE_USER_INPUT: {
99
83
  return {
100
84
  ...state,
@@ -156,15 +140,13 @@ const executeQuery: Reducer<ExecuteQueryState, ExecuteQueryAction> = (
156
140
  }
157
141
  };
158
142
 
159
- export const sendQuery = ({
160
- query,
161
- database,
162
- action,
163
- }: {
164
- query: string;
165
- database: string;
166
- action: Actions;
167
- }) => {
143
+ interface SendQueryParams extends QueryRequestParams {
144
+ mode?: QueryModes;
145
+ }
146
+
147
+ export const sendExecuteQuery = ({query, database, mode}: SendQueryParams) => {
148
+ const action: ExecuteActions = mode ? `execute-${mode}` : 'execute';
149
+
168
150
  return createApiRequest({
169
151
  request: window.api.sendQuery({
170
152
  schema: 'modern',
@@ -185,13 +167,6 @@ export const saveQueryToHistory = (query: string) => {
185
167
  } as const;
186
168
  };
187
169
 
188
- export const selectRunAction = (value: RunAction) => {
189
- return {
190
- type: SELECT_RUN_ACTION,
191
- data: value,
192
- } as const;
193
- };
194
-
195
170
  export const goToPreviousQuery = () => {
196
171
  return {
197
172
  type: GO_TO_PREVIOUS_QUERY,
@@ -13,7 +13,7 @@ import {parseQueryAPIExecuteResponse} from '../../utils/query';
13
13
 
14
14
  import {createRequestActionTypes, createApiRequest} from '../utils';
15
15
 
16
- import type {IRootState} from '.';
16
+ import type {RootState} from '.';
17
17
 
18
18
  export const FETCH_TOP_QUERIES = createRequestActionTypes('top-queries', 'FETCH_TOP_QUERIES');
19
19
  const SET_TOP_QUERIES_STATE = 'top-queries/SET_TOP_QUERIES_STATE';
@@ -126,7 +126,7 @@ const executeTopQueries: Reducer<ITopQueriesState, ITopQueriesAction> = (
126
126
  type FetchTopQueries = (params: {
127
127
  database: string;
128
128
  filters?: ITopQueriesFilters;
129
- }) => ThunkAction<Promise<IQueryResult | undefined>, IRootState, unknown, AnyAction>;
129
+ }) => ThunkAction<Promise<IQueryResult | undefined>, RootState, unknown, AnyAction>;
130
130
 
131
131
  export const fetchTopQueries: FetchTopQueries =
132
132
  ({database, filters}) =>