ydb-embedded-ui 4.0.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 (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[];