ydb-embedded-ui 4.8.2 → 4.10.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 (83) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/components/BasicNodeViewer/BasicNodeViewer.tsx +7 -4
  3. package/dist/components/EntityStatus/EntityStatus.js +3 -1
  4. package/dist/components/FormattedBytes/FormattedBytes.tsx +10 -0
  5. package/dist/components/FormattedBytes/utils.tsx +13 -0
  6. package/dist/components/FullNodeViewer/FullNodeViewer.tsx +73 -0
  7. package/dist/components/InfoViewer/formatters/table.ts +6 -5
  8. package/dist/components/NodeHostWrapper/NodeHostWrapper.tsx +2 -2
  9. package/dist/components/ProblemFilter/ProblemFilter.tsx +2 -2
  10. package/dist/components/SpeedMultiMeter/SpeedMultiMeter.tsx +4 -4
  11. package/dist/components/TruncatedQuery/{TruncatedQuery.js → TruncatedQuery.tsx} +10 -8
  12. package/dist/containers/AsideNavigation/AsideNavigation.tsx +6 -6
  13. package/dist/containers/Cluster/Cluster.tsx +10 -6
  14. package/dist/containers/Node/Node.tsx +3 -3
  15. package/dist/containers/Nodes/Nodes.tsx +3 -3
  16. package/dist/containers/Nodes/getNodesColumns.tsx +2 -2
  17. package/dist/containers/Storage/PDisk/PDisk.tsx +2 -7
  18. package/dist/containers/Storage/Storage.tsx +240 -0
  19. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +59 -57
  20. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +23 -25
  21. package/dist/containers/Storage/StorageTypeFilter/StorageTypeFilter.tsx +27 -0
  22. package/dist/containers/Storage/StorageVisibleEntityFilter/StorageVisibleEntityFilter.tsx +31 -0
  23. package/dist/containers/Storage/UsageFilter/UsageFilter.scss +1 -0
  24. package/dist/containers/Storage/UsageFilter/UsageFilter.tsx +17 -17
  25. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +3 -3
  26. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js +7 -4
  27. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.scss +0 -15
  28. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx +10 -3
  29. package/dist/containers/Tenant/{Preview → Query/Preview}/Preview.scss +1 -1
  30. package/dist/containers/Tenant/Query/Preview/Preview.tsx +121 -0
  31. package/dist/containers/Tenant/Query/QueriesHistory/QueriesHistory.tsx +1 -1
  32. package/dist/containers/Tenant/Query/QueryEditor/QueryEditor.js +23 -51
  33. package/dist/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.scss +20 -15
  34. package/dist/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.tsx +74 -27
  35. package/dist/containers/Tenant/Query/SavedQueries/SavedQueries.tsx +1 -1
  36. package/dist/containers/Tenant/Query/i18n/en.json +9 -2
  37. package/dist/containers/Tenant/Query/i18n/ru.json +9 -2
  38. package/dist/containers/Tenants/Tenants.tsx +269 -0
  39. package/dist/containers/UserSettings/i18n/en.json +1 -1
  40. package/dist/containers/UserSettings/i18n/ru.json +1 -1
  41. package/dist/services/api.ts +14 -16
  42. package/dist/store/reducers/executeQuery.ts +2 -3
  43. package/dist/store/reducers/explainQuery.ts +2 -2
  44. package/dist/store/reducers/index.ts +2 -2
  45. package/dist/store/reducers/{nodes.ts → nodes/nodes.ts} +33 -32
  46. package/dist/{types/store/nodes.ts → store/reducers/nodes/types.ts} +24 -27
  47. package/dist/store/reducers/partitions/types.ts +3 -3
  48. package/dist/store/reducers/settings/settings.ts +14 -4
  49. package/dist/store/reducers/settings/types.ts +3 -1
  50. package/dist/store/reducers/storage/constants.ts +10 -0
  51. package/dist/store/reducers/storage/selectors.ts +279 -0
  52. package/dist/store/reducers/storage/storage.ts +191 -0
  53. package/dist/store/reducers/storage/types.ts +92 -0
  54. package/dist/store/reducers/tenants/selectors.ts +46 -0
  55. package/dist/store/reducers/tenants/tenants.ts +21 -14
  56. package/dist/store/reducers/tenants/types.ts +20 -5
  57. package/dist/store/reducers/tenants/utils.ts +68 -0
  58. package/dist/types/additionalProps.ts +8 -0
  59. package/dist/types/api/storage.ts +1 -1
  60. package/dist/types/store/query.ts +5 -6
  61. package/dist/types/store/topic.ts +3 -3
  62. package/dist/utils/bytesParsers/__test__/formatBytes.test.ts +38 -0
  63. package/dist/utils/bytesParsers/convertBytesObjectToSpeed.ts +2 -2
  64. package/dist/utils/bytesParsers/formatBytes.ts +132 -0
  65. package/dist/utils/bytesParsers/i18n/en.json +1 -0
  66. package/dist/utils/bytesParsers/i18n/ru.json +1 -0
  67. package/dist/utils/bytesParsers/index.ts +1 -1
  68. package/dist/utils/constants.ts +1 -0
  69. package/dist/utils/index.js +5 -10
  70. package/dist/utils/nodes.ts +2 -2
  71. package/dist/utils/numeral.ts +8 -0
  72. package/dist/utils/query.ts +12 -0
  73. package/package.json +1 -1
  74. package/dist/components/FullNodeViewer/FullNodeViewer.js +0 -89
  75. package/dist/containers/Node/NodeOverview/NodeOverview.scss +0 -0
  76. package/dist/containers/Node/NodeOverview/NodeOverview.tsx +0 -21
  77. package/dist/containers/Storage/Storage.js +0 -372
  78. package/dist/containers/Tenant/Preview/Preview.js +0 -168
  79. package/dist/containers/Tenant/Query/QueryEditorControls/OldQueryEditorControls.tsx +0 -90
  80. package/dist/containers/Tenant/Query/QueryEditorControls/shared.ts +0 -18
  81. package/dist/containers/Tenants/Tenants.js +0 -363
  82. package/dist/store/reducers/storage.js +0 -438
  83. package/dist/utils/bytesParsers/formatBytesCustom.ts +0 -57
@@ -0,0 +1,191 @@
1
+ import type {Reducer} from 'redux';
2
+ import _ from 'lodash';
3
+
4
+ import {NodesUptimeFilterValues} from '../../../utils/nodes';
5
+ import '../../../services/api';
6
+
7
+ import {createRequestActionTypes, createApiRequest} from '../../utils';
8
+
9
+ import type {StorageAction, StorageState, StorageType, VisibleEntities} from './types';
10
+ import {VISIBLE_ENTITIES, STORAGE_TYPES} from './constants';
11
+
12
+ export const FETCH_STORAGE = createRequestActionTypes('storage', 'FETCH_STORAGE');
13
+
14
+ const SET_INITIAL = 'storage/SET_INITIAL';
15
+ const SET_FILTER = 'storage/SET_FILTER';
16
+ const SET_USAGE_FILTER = 'storage/SET_USAGE_FILTER';
17
+ const SET_VISIBLE_GROUPS = 'storage/SET_VISIBLE_GROUPS';
18
+ const SET_STORAGE_TYPE = 'storage/SET_STORAGE_TYPE';
19
+ const SET_NODES_UPTIME_FILTER = 'storage/SET_NODES_UPTIME_FILTER';
20
+ const SET_DATA_WAS_NOT_LOADED = 'storage/SET_DATA_WAS_NOT_LOADED';
21
+
22
+ const initialState = {
23
+ loading: true,
24
+ wasLoaded: false,
25
+ filter: '',
26
+ usageFilter: [],
27
+ visible: VISIBLE_ENTITIES.missing,
28
+ nodesUptimeFilter: NodesUptimeFilterValues.All,
29
+ type: STORAGE_TYPES.groups,
30
+ };
31
+
32
+ const storage: Reducer<StorageState, StorageAction> = (state = initialState, action) => {
33
+ switch (action.type) {
34
+ case FETCH_STORAGE.REQUEST: {
35
+ return {
36
+ ...state,
37
+ loading: true,
38
+ };
39
+ }
40
+ case FETCH_STORAGE.SUCCESS: {
41
+ return {
42
+ ...state,
43
+ nodes: action.data.nodes,
44
+ groups: action.data.groups,
45
+ loading: false,
46
+ wasLoaded: true,
47
+ error: undefined,
48
+ };
49
+ }
50
+ case FETCH_STORAGE.FAILURE: {
51
+ if (action.error?.isCancelled) {
52
+ return state;
53
+ }
54
+
55
+ return {
56
+ ...state,
57
+ error: action.error,
58
+ loading: false,
59
+ wasLoaded: true,
60
+ };
61
+ }
62
+ case SET_INITIAL: {
63
+ return {
64
+ ...initialState,
65
+ };
66
+ }
67
+ case SET_FILTER: {
68
+ return {
69
+ ...state,
70
+ filter: action.data,
71
+ };
72
+ }
73
+ case SET_USAGE_FILTER: {
74
+ return {
75
+ ...state,
76
+ usageFilter: action.data,
77
+ };
78
+ }
79
+ case SET_VISIBLE_GROUPS: {
80
+ return {
81
+ ...state,
82
+ visible: action.data,
83
+ wasLoaded: false,
84
+ error: undefined,
85
+ };
86
+ }
87
+
88
+ case SET_NODES_UPTIME_FILTER: {
89
+ return {
90
+ ...state,
91
+ nodesUptimeFilter: action.data,
92
+ };
93
+ }
94
+ case SET_STORAGE_TYPE: {
95
+ return {
96
+ ...state,
97
+ type: action.data,
98
+ filter: '',
99
+ usageFilter: [],
100
+ wasLoaded: false,
101
+ error: undefined,
102
+ };
103
+ }
104
+ case SET_DATA_WAS_NOT_LOADED: {
105
+ return {
106
+ ...state,
107
+ wasLoaded: false,
108
+ };
109
+ }
110
+ default:
111
+ return state;
112
+ }
113
+ };
114
+
115
+ export function getStorageInfo(
116
+ {
117
+ tenant,
118
+ visibleEntities,
119
+ nodeId,
120
+ type,
121
+ }: {
122
+ tenant?: string;
123
+ visibleEntities?: VisibleEntities;
124
+ nodeId?: string;
125
+ type?: StorageType;
126
+ },
127
+ {concurrentId}: {concurrentId?: string} = {},
128
+ ) {
129
+ if (type === STORAGE_TYPES.nodes) {
130
+ return createApiRequest({
131
+ request: window.api.getNodes({tenant, visibleEntities, type: 'static'}, {concurrentId}),
132
+ actions: FETCH_STORAGE,
133
+ dataHandler: (data) => ({nodes: data}),
134
+ });
135
+ }
136
+
137
+ return createApiRequest({
138
+ request: window.api.getStorageInfo({tenant, visibleEntities, nodeId}, {concurrentId}),
139
+ actions: FETCH_STORAGE,
140
+ dataHandler: (data) => ({groups: data}),
141
+ });
142
+ }
143
+
144
+ export function setInitialState() {
145
+ return {
146
+ type: SET_INITIAL,
147
+ } as const;
148
+ }
149
+
150
+ export function setStorageType(value: StorageType) {
151
+ return {
152
+ type: SET_STORAGE_TYPE,
153
+ data: value,
154
+ } as const;
155
+ }
156
+
157
+ export function setStorageTextFilter(value: string) {
158
+ return {
159
+ type: SET_FILTER,
160
+ data: value,
161
+ } as const;
162
+ }
163
+
164
+ export function setUsageFilter(value: string[]) {
165
+ return {
166
+ type: SET_USAGE_FILTER,
167
+ data: value,
168
+ } as const;
169
+ }
170
+
171
+ export function setVisibleEntities(value: VisibleEntities) {
172
+ return {
173
+ type: SET_VISIBLE_GROUPS,
174
+ data: value,
175
+ } as const;
176
+ }
177
+
178
+ export function setNodesUptimeFilter(value: NodesUptimeFilterValues) {
179
+ return {
180
+ type: SET_NODES_UPTIME_FILTER,
181
+ data: value,
182
+ } as const;
183
+ }
184
+
185
+ export const setDataWasNotLoaded = () => {
186
+ return {
187
+ type: SET_DATA_WAS_NOT_LOADED,
188
+ } as const;
189
+ };
190
+
191
+ export default storage;
@@ -0,0 +1,92 @@
1
+ import type {IResponseError} from '../../../types/api/error';
2
+ import type {TNodesInfo, TSystemStateInfo} from '../../../types/api/nodes';
3
+ import type {TPDiskStateInfo} from '../../../types/api/pdisk';
4
+ import type {
5
+ TBSGroupStateInfo,
6
+ THiveStorageGroupStats,
7
+ TStorageInfo,
8
+ } from '../../../types/api/storage';
9
+ import type {ValueOf} from '../../../types/common';
10
+ import type {NodesUptimeFilterValues} from '../../../utils/nodes';
11
+ import type {ApiRequestAction} from '../../utils';
12
+
13
+ import {STORAGE_TYPES, VISIBLE_ENTITIES} from './constants';
14
+ import {
15
+ FETCH_STORAGE,
16
+ setDataWasNotLoaded,
17
+ setInitialState,
18
+ setNodesUptimeFilter,
19
+ setStorageTextFilter,
20
+ setStorageType,
21
+ setUsageFilter,
22
+ setVisibleEntities,
23
+ } from './storage';
24
+
25
+ export type VisibleEntities = ValueOf<typeof VISIBLE_ENTITIES>;
26
+ export type StorageType = ValueOf<typeof STORAGE_TYPES>;
27
+
28
+ export interface PreparedStorageNode extends TSystemStateInfo {
29
+ NodeId: number;
30
+ PDisks: TPDiskStateInfo[] | undefined;
31
+
32
+ Missing: number;
33
+ Uptime: string;
34
+ }
35
+
36
+ export type RawStorageGroup = TBSGroupStateInfo & THiveStorageGroupStats;
37
+
38
+ export interface PreparedStorageGroup extends RawStorageGroup {
39
+ PoolName: string | undefined;
40
+
41
+ Read: number;
42
+ Write: number;
43
+ Used: number;
44
+ Limit: number;
45
+ Missing: number;
46
+ UsedSpaceFlag: number;
47
+ Type: string | null;
48
+ }
49
+
50
+ export interface UsageFilter {
51
+ threshold: number;
52
+ count: number;
53
+ }
54
+
55
+ export interface StorageApiRequestParams {
56
+ tenant?: string;
57
+ nodeId?: string;
58
+ visibleEntities?: VisibleEntities;
59
+ }
60
+
61
+ export interface StorageState {
62
+ loading: boolean;
63
+ wasLoaded: boolean;
64
+ filter: string;
65
+ usageFilter: string[];
66
+ visible: VisibleEntities;
67
+ nodesUptimeFilter: NodesUptimeFilterValues;
68
+ type: StorageType;
69
+ nodes?: TNodesInfo;
70
+ groups?: TStorageInfo;
71
+ error?: IResponseError;
72
+ }
73
+
74
+ type GetStorageInfoApiRequestAction = ApiRequestAction<
75
+ typeof FETCH_STORAGE,
76
+ {nodes?: TNodesInfo; groups?: TStorageInfo},
77
+ IResponseError
78
+ >;
79
+
80
+ export type StorageAction =
81
+ | GetStorageInfoApiRequestAction
82
+ | ReturnType<typeof setInitialState>
83
+ | ReturnType<typeof setStorageType>
84
+ | ReturnType<typeof setStorageTextFilter>
85
+ | ReturnType<typeof setUsageFilter>
86
+ | ReturnType<typeof setVisibleEntities>
87
+ | ReturnType<typeof setNodesUptimeFilter>
88
+ | ReturnType<typeof setDataWasNotLoaded>;
89
+
90
+ export interface StorageStateSlice {
91
+ storage: StorageState;
92
+ }
@@ -0,0 +1,46 @@
1
+ import {Selector, createSelector} from 'reselect';
2
+ import {escapeRegExp} from 'lodash';
3
+
4
+ import {EFlag} from '../../../types/api/enums';
5
+
6
+ import type {RootState} from '..';
7
+ import type {ProblemFilterValue} from '../settings/types';
8
+ import {ProblemFilterValues, selectProblemFilter} from '../settings/settings';
9
+
10
+ import type {PreparedTenant, TenantsStateSlice} from './types';
11
+
12
+ // ==== Filters ====
13
+
14
+ const filterTenantsByProblems = (tenants: PreparedTenant[], problemFilter: ProblemFilterValue) => {
15
+ if (problemFilter === ProblemFilterValues.ALL) {
16
+ return tenants;
17
+ }
18
+
19
+ return tenants.filter((tenant) => {
20
+ return tenant.Overall && tenant.Overall !== EFlag.Green;
21
+ });
22
+ };
23
+
24
+ const filteredTenantsBySearch = (tenants: PreparedTenant[], searchQuery: string) => {
25
+ return tenants.filter((item) => {
26
+ const re = new RegExp(escapeRegExp(searchQuery), 'i');
27
+ return re.test(item.Name) || re.test(item.controlPlaneName);
28
+ });
29
+ };
30
+
31
+ // ==== Simple selectors ====
32
+
33
+ export const selectTenants = (state: TenantsStateSlice) => state.tenants.tenants;
34
+ export const selectTenantsSearchValue = (state: TenantsStateSlice) => state.tenants.searchValue;
35
+
36
+ // ==== Complex selectors ====
37
+
38
+ export const selectFilteredTenants: Selector<RootState, PreparedTenant[]> = createSelector(
39
+ [selectTenants, selectProblemFilter, selectTenantsSearchValue],
40
+ (tenants, problemFilter, searchQuery) => {
41
+ let result = filterTenantsByProblems(tenants, problemFilter);
42
+ result = filteredTenantsBySearch(result, searchQuery);
43
+
44
+ return result;
45
+ },
46
+ );
@@ -4,10 +4,13 @@ import '../../../services/api';
4
4
  import {createRequestActionTypes, createApiRequest} from '../../utils';
5
5
 
6
6
  import type {TenantsAction, TenantsState} from './types';
7
+ import {prepareTenants} from './utils';
7
8
 
8
9
  export const FETCH_TENANTS = createRequestActionTypes('tenants', 'FETCH_TENANTS');
9
10
 
10
- const initialState = {loading: true, wasLoaded: false};
11
+ const SET_SEARCH_VALUE = 'tenants/SET_SEARCH_VALUE';
12
+
13
+ const initialState = {loading: true, wasLoaded: false, searchValue: '', tenants: []};
11
14
 
12
15
  const tenants: Reducer<TenantsState, TenantsAction> = (state = initialState, action) => {
13
16
  switch (action.type) {
@@ -33,6 +36,12 @@ const tenants: Reducer<TenantsState, TenantsAction> = (state = initialState, act
33
36
  loading: false,
34
37
  };
35
38
  }
39
+ case SET_SEARCH_VALUE: {
40
+ return {
41
+ ...state,
42
+ searchValue: action.data,
43
+ };
44
+ }
36
45
  default:
37
46
  return state;
38
47
  }
@@ -45,22 +54,20 @@ export function getTenantsInfo(clusterName?: string) {
45
54
  dataHandler: (response, getState) => {
46
55
  const {singleClusterMode} = getState();
47
56
 
48
- if (singleClusterMode) {
49
- return response.TenantInfo;
50
- } else {
51
- return response.TenantInfo?.map((tenant) => {
52
- const node = tenant.Nodes ? tenant.Nodes[0] : {};
53
- const address =
54
- node.Host && node.Endpoints
55
- ? node.Endpoints.find((endpoint) => endpoint.Name === 'http-mon')
56
- ?.Address
57
- : undefined;
58
- const backend = node.Host ? `${node.Host}${address ? address : ''}` : undefined;
59
- return {...tenant, backend};
60
- });
57
+ if (!response.TenantInfo) {
58
+ return [];
61
59
  }
60
+
61
+ return prepareTenants(response.TenantInfo, singleClusterMode);
62
62
  },
63
63
  });
64
64
  }
65
65
 
66
+ export const setSearchValue = (value: string) => {
67
+ return {
68
+ type: SET_SEARCH_VALUE,
69
+ data: value,
70
+ } as const;
71
+ };
72
+
66
73
  export default tenants;
@@ -1,17 +1,32 @@
1
- import {FETCH_TENANTS} from './tenants';
1
+ import {FETCH_TENANTS, setSearchValue} from './tenants';
2
2
 
3
3
  import type {TTenant} from '../../../types/api/tenant';
4
+ import type {IResponseError} from '../../../types/api/error';
4
5
  import type {ApiRequestAction} from '../../utils';
5
6
 
6
7
  export interface PreparedTenant extends TTenant {
7
- backend?: string;
8
+ backend: string | undefined;
9
+ sharedTenantName: string | undefined;
10
+ controlPlaneName: string;
11
+ cpu: number;
12
+ memory: number;
13
+ storage: number;
14
+ nodesCount: number;
15
+ groupsCount: number;
8
16
  }
9
17
 
10
18
  export interface TenantsState {
11
19
  loading: boolean;
12
20
  wasLoaded: boolean;
13
- tenants?: PreparedTenant[];
14
- error?: unknown;
21
+ searchValue: string;
22
+ tenants: PreparedTenant[];
23
+ error?: IResponseError;
15
24
  }
16
25
 
17
- export type TenantsAction = ApiRequestAction<typeof FETCH_TENANTS, PreparedTenant[], unknown>;
26
+ export type TenantsAction =
27
+ | ApiRequestAction<typeof FETCH_TENANTS, PreparedTenant[], IResponseError>
28
+ | ReturnType<typeof setSearchValue>;
29
+
30
+ export interface TenantsStateSlice {
31
+ tenants: TenantsState;
32
+ }
@@ -0,0 +1,68 @@
1
+ import type {TTenant} from '../../../types/api/tenant';
2
+ import {isNumeric} from '../../../utils/utils';
3
+
4
+ const getControlPlaneValue = (tenant: TTenant) => {
5
+ const parts = tenant.Name.split('/');
6
+ const defaultValue = parts.length ? parts[parts.length - 1] : '—';
7
+ const controlPlaneName = tenant.ControlPlane?.name;
8
+
9
+ return controlPlaneName ?? defaultValue;
10
+ };
11
+
12
+ const getTenantBackend = (tenant: TTenant) => {
13
+ const node = tenant.Nodes ? tenant.Nodes[0] : {};
14
+ const address =
15
+ node.Host && node.Endpoints
16
+ ? node.Endpoints.find((endpoint) => endpoint.Name === 'http-mon')?.Address
17
+ : undefined;
18
+ return node.Host ? `${node.Host}${address ? address : ''}` : undefined;
19
+ };
20
+
21
+ const calculateTenantMetrics = (tenant: TTenant) => {
22
+ const {CoresUsed, MemoryUsed, StorageAllocatedSize, Metrics = {}} = tenant;
23
+
24
+ const cpuFromCores = isNumeric(CoresUsed) ? Number(CoresUsed) * 1_000_000 : undefined;
25
+ const cpuFromMetrics = isNumeric(Metrics.CPU) ? Number(Metrics.CPU) : undefined;
26
+
27
+ const cpu = cpuFromCores ?? cpuFromMetrics ?? 0;
28
+
29
+ const rawMemory = MemoryUsed ?? Metrics.Memory;
30
+ const rawStorage = StorageAllocatedSize ?? Metrics.Storage;
31
+
32
+ const memory = isNumeric(rawMemory) ? Number(rawMemory) : 0;
33
+ const storage = isNumeric(rawStorage) ? Number(rawStorage) : 0;
34
+
35
+ return {cpu, memory, storage};
36
+ };
37
+
38
+ const calculateTenantEntities = (tenant: TTenant) => {
39
+ const {StorageGroups, NodeIds} = tenant;
40
+
41
+ const nodesCount = NodeIds?.length ?? 0;
42
+ const groupsCount = isNumeric(StorageGroups) ? Number(StorageGroups) : 0;
43
+
44
+ return {nodesCount, groupsCount};
45
+ };
46
+
47
+ export const prepareTenants = (tenants: TTenant[], useNodeAsBackend: boolean) => {
48
+ return tenants.map((tenant) => {
49
+ const backend = useNodeAsBackend ? getTenantBackend(tenant) : undefined;
50
+ const sharedTenantName = tenants.find((item) => item.Id === tenant.ResourceId)?.Name;
51
+ const controlPlaneName = getControlPlaneValue(tenant);
52
+ const {cpu, memory, storage} = calculateTenantMetrics(tenant);
53
+ const {nodesCount, groupsCount} = calculateTenantEntities(tenant);
54
+
55
+ return {
56
+ ...tenant,
57
+
58
+ backend,
59
+ sharedTenantName,
60
+ controlPlaneName,
61
+ cpu,
62
+ memory,
63
+ storage,
64
+ nodesCount,
65
+ groupsCount,
66
+ };
67
+ });
68
+ };
@@ -1,4 +1,7 @@
1
+ import type {ReactNode} from 'react';
2
+
1
3
  import type {InfoViewerItem} from '../components/InfoViewer';
4
+ import type {ETenantType} from './api/tenant';
2
5
  import type {VersionToColorMap} from './versions';
3
6
 
4
7
  export interface AdditionalVersionsProps {
@@ -14,3 +17,8 @@ export interface AdditionalClusterProps {
14
17
  info?: InfoViewerItem[];
15
18
  links?: ClusterLink[];
16
19
  }
20
+
21
+ export interface AdditionalTenantsProps {
22
+ prepareTenantBackend?: (backend: string | undefined) => string | undefined;
23
+ getMonitoringLink?: (name: string, type: ETenantType) => ReactNode;
24
+ }
@@ -57,7 +57,7 @@ export interface TBSGroupStateInfo {
57
57
  Encryption?: boolean;
58
58
  }
59
59
 
60
- interface THiveStorageGroupStats {
60
+ export interface THiveStorageGroupStats {
61
61
  GroupID?: number;
62
62
  /** uint64 */
63
63
  AcquiredUnits?: string;
@@ -1,3 +1,5 @@
1
+ import {QUERY_ACTIONS, QUERY_MODES} from '../../utils/query';
2
+
1
3
  import type {NetworkError} from '../api/error';
2
4
  import type {
3
5
  KeyValueRow,
@@ -7,6 +9,7 @@ import type {
7
9
  QueryPlan,
8
10
  TKqpStatsQuery,
9
11
  } from '../api/query';
12
+ import type {ValueOf} from '../common';
10
13
 
11
14
  export interface IQueryResult {
12
15
  result?: KeyValueRow[];
@@ -23,12 +26,8 @@ export interface QueryRequestParams {
23
26
 
24
27
  export type QueryError = NetworkError | ErrorResponse;
25
28
 
26
- export enum QueryModes {
27
- scan = 'scan',
28
- script = 'script',
29
- data = 'data',
30
- query = 'query',
31
- }
29
+ export type QueryAction = ValueOf<typeof QUERY_ACTIONS>;
30
+ export type QueryMode = ValueOf<typeof QUERY_MODES>;
32
31
 
33
32
  export interface SavedQuery {
34
33
  name: string;
@@ -1,12 +1,12 @@
1
1
  import {FETCH_TOPIC, cleanTopicData, setDataWasNotLoaded} from '../../store/reducers/topic';
2
2
  import type {ApiRequestAction} from '../../store/utils';
3
- import type {IProcessSpeedStats} from '../../utils/bytesParsers';
3
+ import type {ProcessSpeedStats} from '../../utils/bytesParsers';
4
4
  import type {IResponseError} from '../api/error';
5
5
  import type {DescribeTopicResult} from '../api/topic';
6
6
 
7
7
  export interface IPreparedConsumerData {
8
8
  name: string | undefined;
9
- readSpeed: IProcessSpeedStats;
9
+ readSpeed: ProcessSpeedStats;
10
10
 
11
11
  writeLag: number;
12
12
  readLag: number;
@@ -19,7 +19,7 @@ export interface IPreparedTopicStats {
19
19
  partitionsWriteLag: number;
20
20
  partitionsIdleTime: number;
21
21
 
22
- writeSpeed: IProcessSpeedStats;
22
+ writeSpeed: ProcessSpeedStats;
23
23
  }
24
24
 
25
25
  export interface ITopicState {
@@ -0,0 +1,38 @@
1
+ import {formatBytes} from '../formatBytes';
2
+
3
+ describe('formatBytes', () => {
4
+ it('should work with only value', () => {
5
+ expect(formatBytes({value: 100})).toBe('100 B');
6
+ expect(formatBytes({value: 100_000})).toBe('100 KB');
7
+ expect(formatBytes({value: 100_000_000})).toBe('100 MB');
8
+ expect(formatBytes({value: 100_000_000_000})).toBe('100 GB');
9
+ expect(formatBytes({value: 100_000_000_000_000})).toBe('100 TB');
10
+ });
11
+ it('should convert to size', () => {
12
+ expect(formatBytes({value: 100_000, size: 'b'})).toBe('100,000 B');
13
+ expect(formatBytes({value: 100_000_000_000_000, size: 'gb'})).toBe('100,000 GB');
14
+ });
15
+ it('should convert without labels', () => {
16
+ expect(formatBytes({value: 100_000, size: 'b', withSizeLabel: false})).toBe('100,000');
17
+ expect(formatBytes({value: 100_000_000_000_000, size: 'gb', withSizeLabel: false})).toBe(
18
+ '100,000',
19
+ );
20
+ });
21
+ it('should convert to speed', () => {
22
+ expect(formatBytes({value: 100_000, withSpeedLabel: true})).toBe('100 KB/s');
23
+ expect(formatBytes({value: 100_000, size: 'b', withSpeedLabel: true})).toBe('100,000 B/s');
24
+ });
25
+ it('should return fixed amount of significant digits', () => {
26
+ expect(formatBytes({value: 99_000, significantDigits: 2})).toEqual('99,000 B');
27
+ expect(formatBytes({value: 100_000, significantDigits: 2})).toEqual('100 KB');
28
+ expect(formatBytes({value: 99_000_000_000_000, significantDigits: 2})).toEqual('99,000 GB');
29
+ expect(formatBytes({value: 100_000_000_000_000, significantDigits: 2})).toEqual('100 TB');
30
+ });
31
+ it('shoudl return empty string on invalid data', () => {
32
+ expect(formatBytes({value: undefined})).toEqual('');
33
+ expect(formatBytes({value: null})).toEqual('');
34
+ expect(formatBytes({value: ''})).toEqual('');
35
+ expect(formatBytes({value: 'false'})).toEqual('');
36
+ expect(formatBytes({value: '123qwe'})).toEqual('');
37
+ });
38
+ });
@@ -2,7 +2,7 @@ import type {MultipleWindowsStat} from '../../types/api/consumer';
2
2
 
3
3
  import {DAY_IN_SECONDS, HOUR_IN_SECONDS, MINUTE_IN_SECONDS} from '../constants';
4
4
 
5
- export interface IProcessSpeedStats {
5
+ export interface ProcessSpeedStats {
6
6
  perMinute: number;
7
7
  perHour: number;
8
8
  perDay: number;
@@ -14,7 +14,7 @@ export interface IProcessSpeedStats {
14
14
  */
15
15
  export const convertBytesObjectToSpeed = (
16
16
  data: MultipleWindowsStat | undefined,
17
- ): IProcessSpeedStats => {
17
+ ): ProcessSpeedStats => {
18
18
  return {
19
19
  perMinute:
20
20
  data && data.per_minute ? Math.round(Number(data.per_minute) / MINUTE_IN_SECONDS) : 0,