ydb-embedded-ui 3.5.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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}) =>