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.
- package/CHANGELOG.md +27 -0
- package/dist/components/BasicNodeViewer/BasicNodeViewer.tsx +7 -4
- package/dist/components/EntityStatus/EntityStatus.js +3 -1
- package/dist/components/FormattedBytes/FormattedBytes.tsx +10 -0
- package/dist/components/FormattedBytes/utils.tsx +13 -0
- package/dist/components/FullNodeViewer/FullNodeViewer.tsx +73 -0
- package/dist/components/InfoViewer/formatters/table.ts +6 -5
- package/dist/components/NodeHostWrapper/NodeHostWrapper.tsx +2 -2
- package/dist/components/ProblemFilter/ProblemFilter.tsx +2 -2
- package/dist/components/SpeedMultiMeter/SpeedMultiMeter.tsx +4 -4
- package/dist/components/TruncatedQuery/{TruncatedQuery.js → TruncatedQuery.tsx} +10 -8
- package/dist/containers/AsideNavigation/AsideNavigation.tsx +6 -6
- package/dist/containers/Cluster/Cluster.tsx +10 -6
- package/dist/containers/Node/Node.tsx +3 -3
- package/dist/containers/Nodes/Nodes.tsx +3 -3
- package/dist/containers/Nodes/getNodesColumns.tsx +2 -2
- package/dist/containers/Storage/PDisk/PDisk.tsx +2 -7
- package/dist/containers/Storage/Storage.tsx +240 -0
- package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +59 -57
- package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +23 -25
- package/dist/containers/Storage/StorageTypeFilter/StorageTypeFilter.tsx +27 -0
- package/dist/containers/Storage/StorageVisibleEntityFilter/StorageVisibleEntityFilter.tsx +31 -0
- package/dist/containers/Storage/UsageFilter/UsageFilter.scss +1 -0
- package/dist/containers/Storage/UsageFilter/UsageFilter.tsx +17 -17
- package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +3 -3
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js +7 -4
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.scss +0 -15
- package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx +10 -3
- package/dist/containers/Tenant/{Preview → Query/Preview}/Preview.scss +1 -1
- package/dist/containers/Tenant/Query/Preview/Preview.tsx +121 -0
- package/dist/containers/Tenant/Query/QueriesHistory/QueriesHistory.tsx +1 -1
- package/dist/containers/Tenant/Query/QueryEditor/QueryEditor.js +23 -51
- package/dist/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.scss +20 -15
- package/dist/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.tsx +74 -27
- package/dist/containers/Tenant/Query/SavedQueries/SavedQueries.tsx +1 -1
- package/dist/containers/Tenant/Query/i18n/en.json +9 -2
- package/dist/containers/Tenant/Query/i18n/ru.json +9 -2
- package/dist/containers/Tenants/Tenants.tsx +269 -0
- package/dist/containers/UserSettings/i18n/en.json +1 -1
- package/dist/containers/UserSettings/i18n/ru.json +1 -1
- package/dist/services/api.ts +14 -16
- package/dist/store/reducers/executeQuery.ts +2 -3
- package/dist/store/reducers/explainQuery.ts +2 -2
- package/dist/store/reducers/index.ts +2 -2
- package/dist/store/reducers/{nodes.ts → nodes/nodes.ts} +33 -32
- package/dist/{types/store/nodes.ts → store/reducers/nodes/types.ts} +24 -27
- package/dist/store/reducers/partitions/types.ts +3 -3
- package/dist/store/reducers/settings/settings.ts +14 -4
- package/dist/store/reducers/settings/types.ts +3 -1
- package/dist/store/reducers/storage/constants.ts +10 -0
- package/dist/store/reducers/storage/selectors.ts +279 -0
- package/dist/store/reducers/storage/storage.ts +191 -0
- package/dist/store/reducers/storage/types.ts +92 -0
- package/dist/store/reducers/tenants/selectors.ts +46 -0
- package/dist/store/reducers/tenants/tenants.ts +21 -14
- package/dist/store/reducers/tenants/types.ts +20 -5
- package/dist/store/reducers/tenants/utils.ts +68 -0
- package/dist/types/additionalProps.ts +8 -0
- package/dist/types/api/storage.ts +1 -1
- package/dist/types/store/query.ts +5 -6
- package/dist/types/store/topic.ts +3 -3
- package/dist/utils/bytesParsers/__test__/formatBytes.test.ts +38 -0
- package/dist/utils/bytesParsers/convertBytesObjectToSpeed.ts +2 -2
- package/dist/utils/bytesParsers/formatBytes.ts +132 -0
- package/dist/utils/bytesParsers/i18n/en.json +1 -0
- package/dist/utils/bytesParsers/i18n/ru.json +1 -0
- package/dist/utils/bytesParsers/index.ts +1 -1
- package/dist/utils/constants.ts +1 -0
- package/dist/utils/index.js +5 -10
- package/dist/utils/nodes.ts +2 -2
- package/dist/utils/numeral.ts +8 -0
- package/dist/utils/query.ts +12 -0
- package/package.json +1 -1
- package/dist/components/FullNodeViewer/FullNodeViewer.js +0 -89
- package/dist/containers/Node/NodeOverview/NodeOverview.scss +0 -0
- package/dist/containers/Node/NodeOverview/NodeOverview.tsx +0 -21
- package/dist/containers/Storage/Storage.js +0 -372
- package/dist/containers/Tenant/Preview/Preview.js +0 -168
- package/dist/containers/Tenant/Query/QueryEditorControls/OldQueryEditorControls.tsx +0 -90
- package/dist/containers/Tenant/Query/QueryEditorControls/shared.ts +0 -18
- package/dist/containers/Tenants/Tenants.js +0 -363
- package/dist/store/reducers/storage.js +0 -438
- 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
|
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 (
|
49
|
-
return
|
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
|
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
|
-
|
14
|
-
|
21
|
+
searchValue: string;
|
22
|
+
tenants: PreparedTenant[];
|
23
|
+
error?: IResponseError;
|
15
24
|
}
|
16
25
|
|
17
|
-
export type TenantsAction =
|
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
|
+
}
|
@@ -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
|
27
|
-
|
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 {
|
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:
|
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:
|
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
|
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
|
-
):
|
17
|
+
): ProcessSpeedStats => {
|
18
18
|
return {
|
19
19
|
perMinute:
|
20
20
|
data && data.per_minute ? Math.round(Number(data.per_minute) / MINUTE_IN_SECONDS) : 0,
|