ydb-embedded-ui 4.0.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. package/CHANGELOG.md +12 -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/Tenants/Tenants.js +1 -1
  11. package/dist/services/api.ts +383 -0
  12. package/dist/store/reducers/{cluster.js → cluster/cluster.ts} +9 -14
  13. package/dist/store/reducers/cluster/types.ts +13 -0
  14. package/dist/store/reducers/executeTopQueries.ts +2 -2
  15. package/dist/store/reducers/index.ts +5 -4
  16. package/dist/store/reducers/settings.js +1 -14
  17. package/dist/store/reducers/{tenants.js → tenants/tenants.ts} +14 -9
  18. package/dist/store/reducers/tenants/types.ts +17 -0
  19. package/dist/store/utils.ts +3 -2
  20. package/dist/types/api/acl.ts +25 -0
  21. package/dist/types/api/cluster.ts +3 -0
  22. package/dist/types/api/compute.ts +5 -3
  23. package/dist/types/api/netInfo.ts +48 -0
  24. package/dist/types/api/nodes.ts +5 -3
  25. package/dist/types/api/pdisk.ts +11 -2
  26. package/dist/types/api/storage.ts +5 -3
  27. package/dist/types/api/tenant.ts +18 -3
  28. package/dist/types/api/vdisk.ts +10 -2
  29. package/dist/types/api/whoami.ts +19 -0
  30. package/dist/types/window.d.ts +5 -0
  31. package/dist/utils/hooks/useTypedSelector.ts +2 -2
  32. package/dist/utils/nodes.ts +3 -1
  33. package/package.json +1 -1
  34. package/dist/services/api.d.ts +0 -87
  35. package/dist/services/api.js +0 -278
@@ -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>;
@@ -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}) =>
@@ -1,7 +1,7 @@
1
1
  import {combineReducers} from 'redux';
2
2
 
3
3
  import nodes from './nodes';
4
- import cluster from './cluster';
4
+ import cluster from './cluster/cluster';
5
5
  import tenant from './tenant';
6
6
  import storage from './storage';
7
7
  import node from './node';
@@ -15,7 +15,7 @@ import schema from './schema';
15
15
  import host from './host';
16
16
  import network from './network';
17
17
  import pool from './pool';
18
- import tenants from './tenants';
18
+ import tenants from './tenants/tenants';
19
19
  import tablet from './tablet';
20
20
  import topic from './topic';
21
21
  import consumer from './consumer';
@@ -82,7 +82,8 @@ const combinedReducer = combineReducers({
82
82
  ...rootReducer,
83
83
  });
84
84
 
85
- export type IRootReducer = typeof combinedReducer;
86
- export type IRootState = ReturnType<IRootReducer>;
85
+ export type RootReducer = typeof combinedReducer;
86
+ export type RootState = ReturnType<RootReducer>;
87
+ export type GetState = () => RootState;
87
88
 
88
89
  export default combinedReducer;
@@ -26,16 +26,6 @@ export function readSavedSettingsValue(key, defaultValue) {
26
26
  return savedValue ?? defaultValue;
27
27
  }
28
28
 
29
- // navigation managed its compact state internally before, and its approach is not compatible with settings
30
- // try reading the old localStorage entry to use it as a default value, for backward compatibility
31
- // assume it is safe to remove this code block if it is at least a few months old
32
- // there a two of these, search for a similar comment
33
- let legacyAsideNavCompactState = '';
34
- try {
35
- legacyAsideNavCompactState = String(JSON.parse(getValueFromLS('nvAsideHeader')).isCompact);
36
- localStorage.removeItem('nvAsideHeader');
37
- } catch {}
38
-
39
29
  export const initialState = {
40
30
  problemFilter: ALL,
41
31
  userSettings: {
@@ -53,10 +43,7 @@ export const initialState = {
53
43
  [SAVED_QUERIES_KEY]: readSavedSettingsValue(SAVED_QUERIES_KEY, '[]'),
54
44
  [TENANT_INITIAL_TAB_KEY]: readSavedSettingsValue(TENANT_INITIAL_TAB_KEY),
55
45
  [QUERY_INITIAL_MODE_KEY]: readSavedSettingsValue(QUERY_INITIAL_MODE_KEY, QueryModes.script),
56
- [ASIDE_HEADER_COMPACT_KEY]: readSavedSettingsValue(
57
- ASIDE_HEADER_COMPACT_KEY,
58
- legacyAsideNavCompactState || 'true',
59
- ),
46
+ [ASIDE_HEADER_COMPACT_KEY]: readSavedSettingsValue(ASIDE_HEADER_COMPACT_KEY, 'true'),
60
47
  [PARTITIONS_SELECTED_COLUMNS_KEY]: readSavedSettingsValue(PARTITIONS_SELECTED_COLUMNS_KEY),
61
48
  },
62
49
  systemSettings,
@@ -1,12 +1,15 @@
1
- import _ from 'lodash';
2
- import {createRequestActionTypes, createApiRequest} from '../utils';
3
- import '../../services/api';
1
+ import type {Reducer} from 'redux';
4
2
 
5
- const FETCH_TENANTS = createRequestActionTypes('tenants', 'FETCH_TENANTS');
3
+ import '../../../services/api';
4
+ import {createRequestActionTypes, createApiRequest} from '../../utils';
6
5
 
7
- const initialState = {loading: true, wasLoaded: false, data: {}};
6
+ import type {TenantsAction, TenantsState} from './types';
8
7
 
9
- const tenants = function (state = initialState, action) {
8
+ export const FETCH_TENANTS = createRequestActionTypes('tenants', 'FETCH_TENANTS');
9
+
10
+ const initialState = {loading: true, wasLoaded: false};
11
+
12
+ const tenants: Reducer<TenantsState, TenantsAction> = (state = initialState, action) => {
10
13
  switch (action.type) {
11
14
  case FETCH_TENANTS.REQUEST: {
12
15
  return {
@@ -35,20 +38,22 @@ const tenants = function (state = initialState, action) {
35
38
  }
36
39
  };
37
40
 
38
- export function getTenantsInfo(clusterName) {
41
+ export function getTenantsInfo(clusterName?: string) {
39
42
  return createApiRequest({
40
43
  request: window.api.getTenants(clusterName),
41
44
  actions: FETCH_TENANTS,
42
45
  dataHandler: (response, getState) => {
43
46
  const {singleClusterMode} = getState();
47
+
44
48
  if (singleClusterMode) {
45
49
  return response.TenantInfo;
46
50
  } else {
47
- return response.databases?.map((tenant) => {
51
+ return response.TenantInfo?.map((tenant) => {
48
52
  const node = tenant.Nodes ? tenant.Nodes[0] : {};
49
53
  const address =
50
54
  node.Host && node.Endpoints
51
- ? _.find(node.Endpoints, {Name: 'http-mon'})?.Address
55
+ ? node.Endpoints.find((endpoint) => endpoint.Name === 'http-mon')
56
+ ?.Address
52
57
  : undefined;
53
58
  const backend = node.Host ? `${node.Host}${address ? address : ''}` : undefined;
54
59
  return {...tenant, backend};
@@ -0,0 +1,17 @@
1
+ import {FETCH_TENANTS} from './tenants';
2
+
3
+ import type {TTenant} from '../../../types/api/tenant';
4
+ import type {ApiRequestAction} from '../../utils';
5
+
6
+ export interface PreparedTenant extends TTenant {
7
+ backend?: string;
8
+ }
9
+
10
+ export interface TenantsState {
11
+ loading: boolean;
12
+ wasLoaded: boolean;
13
+ tenants?: PreparedTenant[];
14
+ error?: unknown;
15
+ }
16
+
17
+ export type TenantsAction = ApiRequestAction<typeof FETCH_TENANTS, PreparedTenant[], unknown>;
@@ -4,6 +4,7 @@ import {AxiosResponse} from 'axios';
4
4
  import createToast from '../utils/createToast';
5
5
 
6
6
  import {SET_UNAUTHENTICATED} from './reducers/authentication';
7
+ import type {GetState} from './reducers';
7
8
 
8
9
  export const nop = (result: any) => result;
9
10
 
@@ -24,7 +25,7 @@ const isAxiosResponse = (response: any): response is AxiosResponse =>
24
25
  type CreateApiRequestParams<Actions, Response, HandledResponse> = {
25
26
  actions: Actions;
26
27
  request: Promise<Response>;
27
- dataHandler?: (data: Response, getState?: () => any) => HandledResponse;
28
+ dataHandler?: (data: Response, getState: GetState) => HandledResponse;
28
29
  };
29
30
 
30
31
  export function createApiRequest<
@@ -36,7 +37,7 @@ export function createApiRequest<
36
37
  request,
37
38
  dataHandler = nop,
38
39
  }: CreateApiRequestParams<Actions, Response, HandledResponse>) {
39
- const doRequest = async function (dispatch: Dispatch, getState: () => any) {
40
+ const doRequest = async function (dispatch: Dispatch, getState: GetState) {
40
41
  dispatch({
41
42
  type: actions.REQUEST,
42
43
  });
@@ -0,0 +1,25 @@
1
+ /**
2
+ * endpoint: /viewer/json/acl
3
+ *
4
+ * source: https://github.com/ydb-platform/ydb/blob/main/ydb/core/viewer/protos/viewer.proto
5
+ *
6
+ * incomplete, only fields that present for ACL requests
7
+ */
8
+ export interface TMetaInfo {
9
+ Common: TMetaCommonInfo;
10
+ }
11
+
12
+ /** incomplete */
13
+ export interface TMetaCommonInfo {
14
+ Path: string;
15
+ Owner?: string;
16
+ ACL?: TACE[];
17
+ }
18
+
19
+ interface TACE {
20
+ AccessType: string;
21
+ AccessRights?: string[];
22
+ Subject: string;
23
+ InheritanceType?: string[];
24
+ AccessRule: string;
25
+ }
@@ -31,4 +31,7 @@ export interface TClusterInfo {
31
31
  Tenants?: string;
32
32
  /** uint64 */
33
33
  Tablets?: string;
34
+
35
+ Balancer?: string; // additional
36
+ Solomon?: string; // additional
34
37
  }
@@ -2,9 +2,11 @@ import {EFlag} from './enums';
2
2
  import {TEndpoint, TLegacyNodeLocation, TPoolStats} from './nodes';
3
3
  import {TMetrics} from './tenant';
4
4
 
5
- // endpoint: viewer/json/compute
6
- // source: https://github.com/ydb-platform/ydb/blob/main/ydb/core/viewer/protos/viewer.proto
7
-
5
+ /**
6
+ * endpoint: viewer/json/compute
7
+ *
8
+ * source: https://github.com/ydb-platform/ydb/blob/main/ydb/core/viewer/protos/viewer.proto
9
+ */
8
10
  export interface TComputeInfo {
9
11
  Overall: EFlag;
10
12
  Tenants?: TComputeTenantInfo[];