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.
- package/CHANGELOG.md +34 -0
- package/dist/components/ClusterInfo/ClusterInfo.tsx +3 -3
- package/dist/{containers/Nodes/NodesTable.scss → components/NodeHostWrapper/NodeHostWrapper.scss} +4 -6
- package/dist/components/NodeHostWrapper/NodeHostWrapper.tsx +60 -0
- package/dist/containers/AsideNavigation/AsideNavigation.tsx +1 -11
- package/dist/containers/Header/Header.tsx +1 -1
- package/dist/containers/Nodes/getNodesColumns.tsx +7 -46
- package/dist/containers/Storage/StorageNodes/StorageNodes.scss +0 -24
- package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +2 -39
- package/dist/containers/Tenant/QueryEditor/QueriesHistory/QueriesHistory.tsx +3 -3
- package/dist/containers/Tenant/QueryEditor/QueryDuration/QueryDuration.scss +8 -0
- package/dist/containers/Tenant/QueryEditor/QueryDuration/QueryDuration.tsx +21 -0
- package/dist/containers/Tenant/QueryEditor/QueryEditor.js +58 -83
- package/dist/containers/Tenant/QueryEditor/QueryEditor.scss +0 -33
- package/dist/containers/Tenant/QueryEditor/QueryEditorControls/OldQueryEditorControls.tsx +83 -0
- package/dist/containers/Tenant/QueryEditor/QueryEditorControls/QueryEditorControls.scss +57 -0
- package/dist/containers/Tenant/QueryEditor/QueryEditorControls/QueryEditorControls.tsx +84 -0
- package/dist/containers/Tenant/QueryEditor/QueryEditorControls/shared.ts +23 -0
- package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.js +12 -23
- package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.js +4 -6
- package/dist/containers/Tenant/QueryEditor/i18n/en.json +3 -0
- package/dist/containers/Tenant/QueryEditor/i18n/index.ts +11 -0
- package/dist/containers/Tenant/QueryEditor/i18n/ru.json +3 -0
- package/dist/containers/Tenants/Tenants.js +1 -1
- package/dist/containers/UserSettings/UserSettings.tsx +30 -1
- package/dist/services/api.ts +383 -0
- package/dist/store/reducers/{cluster.js → cluster/cluster.ts} +9 -14
- package/dist/store/reducers/cluster/types.ts +13 -0
- package/dist/store/reducers/executeQuery.ts +12 -37
- package/dist/store/reducers/executeTopQueries.ts +2 -2
- package/dist/store/reducers/{explainQuery.js → explainQuery.ts} +44 -59
- package/dist/store/reducers/index.ts +5 -4
- package/dist/store/reducers/settings.js +19 -17
- package/dist/store/reducers/{tenants.js → tenants/tenants.ts} +14 -9
- package/dist/store/reducers/tenants/types.ts +17 -0
- package/dist/store/utils.ts +3 -2
- package/dist/types/api/acl.ts +25 -0
- package/dist/types/api/cluster.ts +3 -0
- package/dist/types/api/compute.ts +5 -3
- package/dist/types/api/error.ts +14 -0
- package/dist/types/api/netInfo.ts +48 -0
- package/dist/types/api/nodes.ts +5 -3
- package/dist/types/api/pdisk.ts +11 -2
- package/dist/types/api/query.ts +226 -117
- package/dist/types/api/storage.ts +5 -3
- package/dist/types/api/tenant.ts +18 -3
- package/dist/types/api/vdisk.ts +10 -2
- package/dist/types/api/whoami.ts +19 -0
- package/dist/types/store/executeQuery.ts +4 -8
- package/dist/types/store/explainQuery.ts +38 -0
- package/dist/types/store/query.ts +23 -3
- package/dist/types/window.d.ts +5 -0
- package/dist/utils/constants.ts +2 -1
- package/dist/utils/error.ts +25 -0
- package/dist/utils/hooks/useTypedSelector.ts +2 -2
- package/dist/utils/index.js +0 -49
- package/dist/utils/nodes.ts +3 -1
- package/dist/utils/prepareQueryExplain.ts +7 -24
- package/dist/utils/query.test.ts +153 -231
- package/dist/utils/query.ts +44 -78
- package/dist/utils/timeParsers/i18n/en.json +9 -9
- package/dist/utils/timeParsers/i18n/ru.json +9 -9
- package/dist/utils/timeParsers/parsers.ts +9 -0
- package/dist/utils/utils.js +1 -2
- package/package.json +1 -1
- package/dist/services/api.d.ts +0 -86
- 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);
|
@@ -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} =
|
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 {
|
2
|
-
import '../../services/api';
|
1
|
+
import type {Reducer} from 'redux';
|
3
2
|
|
4
|
-
|
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 =
|
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 {
|
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
|
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
|
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
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
}: {
|
164
|
-
|
165
|
-
|
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 {
|
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>,
|
129
|
+
}) => ThunkAction<Promise<IQueryResult | undefined>, RootState, unknown, AnyAction>;
|
130
130
|
|
131
131
|
export const fetchTopQueries: FetchTopQueries =
|
132
132
|
({database, filters}) =>
|