ydb-embedded-ui 4.9.0 → 4.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. package/CHANGELOG.md +25 -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/schema.ts +3 -1
  8. package/dist/components/InfoViewer/formatters/table.ts +6 -5
  9. package/dist/components/ProblemFilter/ProblemFilter.tsx +2 -2
  10. package/dist/components/SpeedMultiMeter/SpeedMultiMeter.tsx +4 -4
  11. package/dist/components/TableWithControlsLayout/TableWithControlsLayout.scss +32 -0
  12. package/dist/components/TableWithControlsLayout/TableWithControlsLayout.tsx +43 -0
  13. package/dist/components/TruncatedQuery/{TruncatedQuery.js → TruncatedQuery.tsx} +10 -8
  14. package/dist/containers/AsideNavigation/AsideNavigation.tsx +8 -8
  15. package/dist/containers/Cluster/Cluster.scss +4 -5
  16. package/dist/containers/Cluster/Cluster.tsx +13 -28
  17. package/dist/containers/Cluster/ClusterInfo/ClusterInfo.scss +4 -0
  18. package/dist/containers/Cluster/ClusterInfo/ClusterInfo.tsx +7 -0
  19. package/dist/containers/Cluster/ClusterInfoSkeleton/ClusterInfoSkeleton.tsx +1 -1
  20. package/dist/containers/Cluster/utils.tsx +0 -11
  21. package/dist/containers/Header/Header.scss +1 -5
  22. package/dist/containers/Node/Node.tsx +3 -3
  23. package/dist/containers/Nodes/Nodes.scss +1 -24
  24. package/dist/containers/Nodes/Nodes.tsx +30 -40
  25. package/dist/containers/Storage/PDisk/PDisk.tsx +2 -7
  26. package/dist/containers/Storage/Storage.scss +1 -14
  27. package/dist/containers/Storage/Storage.tsx +237 -0
  28. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +45 -40
  29. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +12 -16
  30. package/dist/containers/Storage/StorageTypeFilter/StorageTypeFilter.tsx +3 -1
  31. package/dist/containers/Storage/{StorageVisibleEntityFilter/StorageVisibleEntityFilter.tsx → StorageVisibleEntitiesFilter/StorageVisibleEntitiesFilter.tsx} +4 -2
  32. package/dist/containers/Storage/UsageFilter/UsageFilter.scss +1 -0
  33. package/dist/containers/Storage/UsageFilter/UsageFilter.tsx +17 -17
  34. package/dist/containers/Tenant/Diagnostics/Diagnostics.scss +6 -2
  35. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +3 -3
  36. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js +7 -4
  37. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.scss +0 -15
  38. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.tsx +10 -3
  39. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.scss +3 -12
  40. package/dist/containers/Tenant/ObjectSummary/ObjectSummary.tsx +2 -7
  41. package/dist/containers/Tenant/{Preview → Query/Preview}/Preview.scss +1 -1
  42. package/dist/containers/Tenant/Query/Preview/Preview.tsx +121 -0
  43. package/dist/containers/Tenant/Query/QueriesHistory/QueriesHistory.tsx +1 -1
  44. package/dist/containers/Tenant/Query/QueryEditor/QueryEditor.js +6 -8
  45. package/dist/containers/Tenant/Query/SavedQueries/SavedQueries.tsx +1 -1
  46. package/dist/containers/Tenant/Query/i18n/en.json +9 -2
  47. package/dist/containers/Tenant/Query/i18n/ru.json +9 -2
  48. package/dist/containers/Tenants/Tenants.scss +1 -13
  49. package/dist/containers/Tenants/Tenants.tsx +262 -0
  50. package/dist/services/api.ts +8 -3
  51. package/dist/store/reducers/nodes/nodes.ts +4 -112
  52. package/dist/store/reducers/nodes/selectors.ts +74 -0
  53. package/dist/store/reducers/nodes/utils.ts +46 -0
  54. package/dist/store/reducers/partitions/types.ts +3 -3
  55. package/dist/store/reducers/settings/settings.ts +4 -2
  56. package/dist/store/reducers/settings/types.ts +3 -1
  57. package/dist/store/reducers/storage/selectors.ts +279 -0
  58. package/dist/store/reducers/storage/storage.ts +191 -0
  59. package/dist/store/reducers/storage/types.ts +80 -0
  60. package/dist/store/reducers/tenants/selectors.ts +46 -0
  61. package/dist/store/reducers/tenants/tenants.ts +21 -14
  62. package/dist/store/reducers/tenants/types.ts +20 -5
  63. package/dist/store/reducers/tenants/utils.ts +68 -0
  64. package/dist/types/additionalProps.ts +8 -0
  65. package/dist/types/api/compute.ts +27 -2
  66. package/dist/types/api/nodes.ts +12 -1
  67. package/dist/types/api/schema/cdcStream.ts +32 -0
  68. package/dist/types/api/schema/columnEntity.ts +138 -0
  69. package/dist/types/api/schema/externalDataSource.ts +24 -0
  70. package/dist/types/api/schema/externalTable.ts +14 -0
  71. package/dist/types/api/schema/index.ts +10 -0
  72. package/dist/types/api/schema/persQueueGroup.ts +191 -0
  73. package/dist/types/api/schema/schema.ts +299 -0
  74. package/dist/types/api/schema/shared.ts +42 -0
  75. package/dist/types/api/schema/table.ts +616 -0
  76. package/dist/types/api/schema/tableIndex.ts +33 -0
  77. package/dist/types/api/storage.ts +1 -1
  78. package/dist/types/store/topic.ts +3 -3
  79. package/dist/utils/bytesParsers/__test__/formatBytes.test.ts +38 -0
  80. package/dist/utils/bytesParsers/convertBytesObjectToSpeed.ts +2 -2
  81. package/dist/utils/bytesParsers/formatBytes.ts +132 -0
  82. package/dist/utils/bytesParsers/i18n/en.json +1 -0
  83. package/dist/utils/bytesParsers/i18n/ru.json +1 -0
  84. package/dist/utils/bytesParsers/index.ts +1 -1
  85. package/dist/utils/index.js +5 -10
  86. package/dist/utils/numeral.ts +8 -0
  87. package/package.json +1 -1
  88. package/dist/assets/icons/versions.svg +0 -3
  89. package/dist/components/FullNodeViewer/FullNodeViewer.js +0 -89
  90. package/dist/containers/Node/NodeOverview/NodeOverview.scss +0 -0
  91. package/dist/containers/Node/NodeOverview/NodeOverview.tsx +0 -21
  92. package/dist/containers/Storage/Storage.js +0 -350
  93. package/dist/containers/Tenant/Preview/Preview.js +0 -168
  94. package/dist/containers/Tenants/Tenants.js +0 -363
  95. package/dist/store/reducers/storage/storage.js +0 -404
  96. package/dist/types/api/schema.ts +0 -1326
  97. package/dist/utils/bytesParsers/formatBytesCustom.ts +0 -57
@@ -0,0 +1,262 @@
1
+ import cn from 'bem-cn-lite';
2
+ import {useDispatch} from 'react-redux';
3
+
4
+ import DataTable, {Column} from '@gravity-ui/react-data-table';
5
+ import {Button} from '@gravity-ui/uikit';
6
+
7
+ import EntityStatus from '../../components/EntityStatus/EntityStatus';
8
+ import {PoolsGraph} from '../../components/PoolsGraph/PoolsGraph';
9
+ import {TabletsStatistic} from '../../components/TabletsStatistic';
10
+ import {ProblemFilter} from '../../components/ProblemFilter';
11
+ import {Illustration} from '../../components/Illustration';
12
+ import {Search} from '../../components/Search';
13
+ import {TableWithControlsLayout} from '../../components/TableWithControlsLayout/TableWithControlsLayout';
14
+ import {ResponseError} from '../../components/Errors/ResponseError';
15
+
16
+ import type {AdditionalTenantsProps} from '../../types/additionalProps';
17
+ import type {ProblemFilterValue} from '../../store/reducers/settings/types';
18
+ import type {PreparedTenant} from '../../store/reducers/tenants/types';
19
+ import {getTenantsInfo, setSearchValue} from '../../store/reducers/tenants/tenants';
20
+ import {
21
+ selectFilteredTenants,
22
+ selectTenantsSearchValue,
23
+ } from '../../store/reducers/tenants/selectors';
24
+ import {
25
+ changeFilter,
26
+ ProblemFilterValues,
27
+ selectProblemFilter,
28
+ } from '../../store/reducers/settings/settings';
29
+ import {formatCPU, formatBytesToGigabyte, formatNumber} from '../../utils';
30
+ import {DEFAULT_TABLE_SETTINGS} from '../../utils/constants';
31
+ import {useAutofetcher, useTypedSelector} from '../../utils/hooks';
32
+ import {clusterName} from '../../store';
33
+
34
+ import {TenantTabsGroups, TENANT_INFO_TABS, getTenantPath} from '../Tenant/TenantPages';
35
+
36
+ import './Tenants.scss';
37
+
38
+ const b = cn('tenants');
39
+
40
+ interface TenantsProps {
41
+ additionalTenantsProps?: AdditionalTenantsProps;
42
+ }
43
+
44
+ export const Tenants = ({additionalTenantsProps}: TenantsProps) => {
45
+ const dispatch = useDispatch();
46
+
47
+ const {error, loading, wasLoaded} = useTypedSelector((state) => state.tenants);
48
+ const searchValue = useTypedSelector(selectTenantsSearchValue);
49
+ const filteredTenants = useTypedSelector(selectFilteredTenants);
50
+ const problemFilter = useTypedSelector(selectProblemFilter);
51
+
52
+ useAutofetcher(
53
+ () => {
54
+ dispatch(getTenantsInfo(clusterName));
55
+ },
56
+ [dispatch],
57
+ true,
58
+ );
59
+
60
+ const handleProblemFilterChange = (value: string) => {
61
+ dispatch(changeFilter(value as ProblemFilterValue));
62
+ };
63
+
64
+ const handleSearchChange = (value: string) => {
65
+ dispatch(setSearchValue(value));
66
+ };
67
+
68
+ const renderControls = () => {
69
+ return (
70
+ <>
71
+ <Search
72
+ value={searchValue}
73
+ onChange={handleSearchChange}
74
+ placeholder="Database name"
75
+ className={b('search')}
76
+ />
77
+ <ProblemFilter value={problemFilter} onChange={handleProblemFilterChange} />
78
+ </>
79
+ );
80
+ };
81
+
82
+ const renderTable = () => {
83
+ const initialTenantInfoTab = TENANT_INFO_TABS[0].id;
84
+
85
+ const getTenantBackend = (tenant: PreparedTenant) => {
86
+ const backend = tenant.MonitoringEndpoint ?? tenant.backend;
87
+ return additionalTenantsProps?.prepareTenantBackend?.(backend);
88
+ };
89
+
90
+ const columns: Column<PreparedTenant>[] = [
91
+ {
92
+ name: 'Name',
93
+ header: 'Database',
94
+ render: ({row}) => {
95
+ const backend = getTenantBackend(row);
96
+ const isExternalLink = Boolean(backend);
97
+ return (
98
+ <div className={b('name-wrapper')}>
99
+ <EntityStatus
100
+ externalLink={isExternalLink}
101
+ className={b('name')}
102
+ name={row.Name || 'unknown database'}
103
+ withLeftTrim={true}
104
+ status={row.Overall}
105
+ hasClipboardButton
106
+ path={getTenantPath({
107
+ name: row.Name,
108
+ backend,
109
+ [TenantTabsGroups.info]: initialTenantInfoTab,
110
+ })}
111
+ />
112
+ {additionalTenantsProps?.getMonitoringLink?.(row.Name, row.Type)}
113
+ </div>
114
+ );
115
+ },
116
+ width: 440,
117
+ sortable: true,
118
+ defaultOrder: DataTable.DESCENDING,
119
+ },
120
+ {
121
+ name: 'controlPlaneName',
122
+ header: 'Name',
123
+ render: ({row}) => {
124
+ return row.controlPlaneName;
125
+ },
126
+ width: 200,
127
+ sortable: true,
128
+ defaultOrder: DataTable.DESCENDING,
129
+ },
130
+ {
131
+ name: 'Type',
132
+ width: 200,
133
+ render: ({row}) => {
134
+ if (row.Type !== 'Serverless') {
135
+ return row.Type;
136
+ }
137
+
138
+ return (
139
+ <div className={b('type')}>
140
+ <span className={b('type-value')}>{row.Type}</span>
141
+ <Button
142
+ className={b('type-button')}
143
+ onClick={() => handleSearchChange(row.sharedTenantName || '')}
144
+ >
145
+ Show shared
146
+ </Button>
147
+ </div>
148
+ );
149
+ },
150
+ },
151
+ {
152
+ name: 'State',
153
+ width: 90,
154
+ render: ({row}) => (row.State ? row.State.toLowerCase() : '—'),
155
+ customStyle: () => ({textTransform: 'capitalize'}),
156
+ },
157
+ {
158
+ name: 'cpu',
159
+ header: 'CPU',
160
+ width: 80,
161
+ render: ({row}) => {
162
+ // Don't show values below 0.01 when formatted
163
+ if (row.cpu > 10_000) {
164
+ return formatCPU(row.cpu);
165
+ }
166
+ return '—';
167
+ },
168
+ align: DataTable.RIGHT,
169
+ defaultOrder: DataTable.DESCENDING,
170
+ },
171
+ {
172
+ name: 'memory',
173
+ header: 'Memory',
174
+ width: 120,
175
+ render: ({row}) => (row.memory ? formatBytesToGigabyte(row.memory) : '—'),
176
+ align: DataTable.RIGHT,
177
+ defaultOrder: DataTable.DESCENDING,
178
+ },
179
+ {
180
+ name: 'storage',
181
+ header: 'Storage',
182
+ width: 120,
183
+ render: ({row}) => (row.storage ? formatBytesToGigabyte(row.storage) : '—'),
184
+ align: DataTable.RIGHT,
185
+ defaultOrder: DataTable.DESCENDING,
186
+ },
187
+ {
188
+ name: 'nodesCount',
189
+ header: 'Nodes',
190
+ width: 100,
191
+ render: ({row}) => (row.nodesCount ? formatNumber(row.nodesCount) : '—'),
192
+ align: DataTable.RIGHT,
193
+ defaultOrder: DataTable.DESCENDING,
194
+ },
195
+ {
196
+ name: 'groupsCount',
197
+ header: 'Groups',
198
+ width: 100,
199
+ render: ({row}) => (row.groupsCount ? formatNumber(row.groupsCount) : '—'),
200
+ align: DataTable.RIGHT,
201
+ defaultOrder: DataTable.DESCENDING,
202
+ },
203
+ {
204
+ name: 'PoolStats',
205
+ header: 'Pools',
206
+ width: 100,
207
+ sortAccessor: ({PoolStats = []}) =>
208
+ PoolStats.reduce((acc, item) => acc + (item.Usage || 0), 0),
209
+ defaultOrder: DataTable.DESCENDING,
210
+ align: DataTable.LEFT,
211
+ render: ({row}) => <PoolsGraph pools={row.PoolStats} />,
212
+ },
213
+ {
214
+ name: 'Tablets',
215
+ header: 'Tablets States',
216
+ sortable: false,
217
+ width: 430,
218
+ render: ({row}) => {
219
+ const backend = getTenantBackend(row);
220
+
221
+ return row.Tablets ? (
222
+ <TabletsStatistic
223
+ path={row.Name}
224
+ tablets={row.Tablets}
225
+ nodeIds={row.NodeIds || []}
226
+ backend={backend}
227
+ />
228
+ ) : (
229
+ '—'
230
+ );
231
+ },
232
+ },
233
+ ];
234
+
235
+ if (filteredTenants.length === 0 && problemFilter !== ProblemFilterValues.ALL) {
236
+ return <Illustration name="thumbsUp" width="200" />;
237
+ }
238
+
239
+ return (
240
+ <DataTable
241
+ theme="yandex-cloud"
242
+ data={filteredTenants}
243
+ columns={columns}
244
+ settings={DEFAULT_TABLE_SETTINGS}
245
+ emptyDataMessage="No such tenants"
246
+ />
247
+ );
248
+ };
249
+
250
+ if (error) {
251
+ return <ResponseError error={error} />;
252
+ }
253
+
254
+ return (
255
+ <TableWithControlsLayout>
256
+ <TableWithControlsLayout.Controls>{renderControls()}</TableWithControlsLayout.Controls>
257
+ <TableWithControlsLayout.Table loading={loading && !wasLoaded} className={b('table')}>
258
+ {renderTable()}
259
+ </TableWithControlsLayout.Table>
260
+ </TableWithControlsLayout>
261
+ );
262
+ };
@@ -282,19 +282,24 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
282
282
  },
283
283
  {concurrentId}: AxiosOptions = {},
284
284
  ) {
285
+ // Time difference to ensure that timeout from ui will be shown rather than backend error
286
+ const uiTimeout = 9 * 60 * 1000;
287
+ const backendTimeout = 10 * 60 * 1000;
288
+
285
289
  return this.post<QueryAPIResponse<Action, Schema>>(
286
- this.getPath(`/viewer/json/query${schema ? `?schema=${schema}` : ''}`),
290
+ this.getPath(
291
+ `/viewer/json/query?timeout=${backendTimeout}${schema ? `&schema=${schema}` : ''}`,
292
+ ),
287
293
  {
288
294
  query,
289
295
  database,
290
296
  action,
291
297
  stats,
292
- timeout: 600000,
293
298
  },
294
299
  {},
295
300
  {
296
301
  concurrentId,
297
- timeout: 9 * 60 * 1000,
302
+ timeout: uiTimeout,
298
303
  },
299
304
  );
300
305
  }
@@ -1,25 +1,12 @@
1
1
  import type {Reducer} from 'redux';
2
- import {createSelector, Selector} from 'reselect';
3
- import {escapeRegExp} from 'lodash/fp';
4
2
 
5
- import type {ValueOf} from '../../../types/common';
6
3
  import '../../../services/api';
7
- import {HOUR_IN_SECONDS} from '../../../utils/constants';
8
- import {calcUptime, calcUptimeInSeconds} from '../../../utils';
9
4
  import {NodesUptimeFilterValues} from '../../../utils/nodes';
10
- import {EFlag} from '../../../types/api/enums';
11
5
 
12
6
  import {createRequestActionTypes, createApiRequest} from '../../utils';
13
- import {ProblemFilterValues} from '../settings/settings';
14
7
 
15
- import type {
16
- NodesAction,
17
- NodesApiRequestParams,
18
- NodesHandledResponse,
19
- NodesPreparedEntity,
20
- NodesStateSlice,
21
- NodesState,
22
- } from './types';
8
+ import type {NodesAction, NodesApiRequestParams, NodesState} from './types';
9
+ import {prepareComputeNodesData, prepareNodesData} from './utils';
23
10
 
24
11
  export const FETCH_NODES = createRequestActionTypes('nodes', 'FETCH_NODES');
25
12
 
@@ -97,24 +84,7 @@ export function getNodes({tenant, visibleEntities, type = 'any'}: NodesApiReques
97
84
  return createApiRequest({
98
85
  request: window.api.getNodes({tenant, visibleEntities, type}),
99
86
  actions: FETCH_NODES,
100
- dataHandler: (data): NodesHandledResponse => {
101
- const rawNodes = data.Nodes || [];
102
-
103
- const preparedNodes = rawNodes.map((node) => {
104
- return {
105
- ...node?.SystemState,
106
- Tablets: node?.Tablets,
107
- NodeId: node?.NodeId,
108
- Uptime: calcUptime(node?.SystemState?.StartTime),
109
- TenantName: node?.SystemState?.Tenants?.[0],
110
- };
111
- });
112
-
113
- return {
114
- Nodes: preparedNodes,
115
- TotalNodes: Number(data.TotalNodes) || preparedNodes.length,
116
- };
117
- },
87
+ dataHandler: prepareNodesData,
118
88
  });
119
89
  }
120
90
 
@@ -122,31 +92,7 @@ export function getComputeNodes(path: string) {
122
92
  return createApiRequest({
123
93
  request: window.api.getCompute(path),
124
94
  actions: FETCH_NODES,
125
- dataHandler: (data): NodesHandledResponse => {
126
- const preparedNodes: NodesPreparedEntity[] = [];
127
-
128
- if (data.Tenants) {
129
- for (const tenant of data.Tenants) {
130
- if (tenant && tenant.Nodes) {
131
- const tenantNodes = tenant.Nodes.map((node) => {
132
- return {
133
- ...node,
134
- TenantName: tenant.Name,
135
- SystemState: node?.Overall,
136
- Uptime: calcUptime(node?.StartTime),
137
- };
138
- });
139
-
140
- preparedNodes.push(...tenantNodes);
141
- }
142
- }
143
- }
144
-
145
- return {
146
- Nodes: preparedNodes,
147
- TotalNodes: preparedNodes.length,
148
- };
149
- },
95
+ dataHandler: prepareComputeNodesData,
150
96
  });
151
97
  }
152
98
 
@@ -175,58 +121,4 @@ export const setSearchValue = (value: string) => {
175
121
  } as const;
176
122
  };
177
123
 
178
- const getNodesUptimeFilter = (state: NodesStateSlice) => state.nodes.nodesUptimeFilter;
179
- const getSearchValue = (state: NodesStateSlice) => state.nodes.searchValue;
180
- const getNodesList = (state: NodesStateSlice) => state.nodes.data;
181
-
182
- const filterNodesByProblemsStatus = (
183
- nodesList: NodesPreparedEntity[] = [],
184
- problemFilter: ValueOf<typeof ProblemFilterValues>,
185
- ) => {
186
- if (problemFilter === ProblemFilterValues.ALL) {
187
- return nodesList;
188
- }
189
-
190
- return nodesList.filter(({SystemState}) => {
191
- return SystemState && SystemState !== EFlag.Green;
192
- });
193
- };
194
-
195
- export const filterNodesByUptime = (
196
- nodesList: NodesPreparedEntity[] = [],
197
- nodesUptimeFilter: NodesUptimeFilterValues,
198
- ) => {
199
- if (nodesUptimeFilter === NodesUptimeFilterValues.All) {
200
- return nodesList;
201
- }
202
- return nodesList.filter(({StartTime}) => {
203
- return !StartTime || calcUptimeInSeconds(StartTime) < HOUR_IN_SECONDS;
204
- });
205
- };
206
-
207
- const filterNodesBySearchValue = (nodesList: NodesPreparedEntity[] = [], searchValue: string) => {
208
- if (!searchValue) {
209
- return nodesList;
210
- }
211
- const re = new RegExp(escapeRegExp(searchValue), 'i');
212
-
213
- return nodesList.filter((node) => {
214
- return node.Host ? re.test(node.Host) || re.test(String(node.NodeId)) : true;
215
- });
216
- };
217
-
218
- export const getFilteredPreparedNodesList: Selector<
219
- NodesStateSlice,
220
- NodesPreparedEntity[] | undefined
221
- > = createSelector(
222
- [getNodesList, getNodesUptimeFilter, getSearchValue, (state) => state.settings.problemFilter],
223
- (nodesList, uptimeFilter, searchValue, problemFilter) => {
224
- let result = filterNodesByUptime(nodesList, uptimeFilter);
225
- result = filterNodesByProblemsStatus(result, problemFilter);
226
- result = filterNodesBySearchValue(result, searchValue);
227
-
228
- return result;
229
- },
230
- );
231
-
232
124
  export default nodes;
@@ -0,0 +1,74 @@
1
+ import {Selector, createSelector} from 'reselect';
2
+ import {escapeRegExp} from 'lodash';
3
+
4
+ import {EFlag} from '../../../types/api/enums';
5
+ import {calcUptimeInSeconds} from '../../../utils';
6
+ import {HOUR_IN_SECONDS} from '../../../utils/constants';
7
+ import {NodesUptimeFilterValues} from '../../../utils/nodes';
8
+
9
+ import type {ProblemFilterValue} from '../settings/types';
10
+ import type {NodesPreparedEntity, NodesStateSlice} from './types';
11
+ import {ProblemFilterValues} from '../settings/settings';
12
+
13
+ // ==== Filters ====
14
+
15
+ const filterNodesByProblemsStatus = (
16
+ nodesList: NodesPreparedEntity[] = [],
17
+ problemFilter: ProblemFilterValue,
18
+ ) => {
19
+ if (problemFilter === ProblemFilterValues.ALL) {
20
+ return nodesList;
21
+ }
22
+
23
+ return nodesList.filter(({SystemState}) => {
24
+ return SystemState && SystemState !== EFlag.Green;
25
+ });
26
+ };
27
+
28
+ export const filterNodesByUptime = <T extends {StartTime?: string}>(
29
+ nodesList: T[] = [],
30
+ nodesUptimeFilter: NodesUptimeFilterValues,
31
+ ) => {
32
+ if (nodesUptimeFilter === NodesUptimeFilterValues.All) {
33
+ return nodesList;
34
+ }
35
+ return nodesList.filter(({StartTime}) => {
36
+ return !StartTime || calcUptimeInSeconds(StartTime) < HOUR_IN_SECONDS;
37
+ });
38
+ };
39
+
40
+ const filterNodesBySearchValue = (nodesList: NodesPreparedEntity[] = [], searchValue: string) => {
41
+ if (!searchValue) {
42
+ return nodesList;
43
+ }
44
+ const re = new RegExp(escapeRegExp(searchValue), 'i');
45
+
46
+ return nodesList.filter((node) => {
47
+ return node.Host ? re.test(node.Host) || re.test(String(node.NodeId)) : true;
48
+ });
49
+ };
50
+
51
+ // ==== Simple selectors ====
52
+
53
+ const selectNodesUptimeFilter = (state: NodesStateSlice) => state.nodes.nodesUptimeFilter;
54
+ const selectSearchValue = (state: NodesStateSlice) => state.nodes.searchValue;
55
+ const selectNodesList = (state: NodesStateSlice) => state.nodes.data;
56
+
57
+ // ==== Complex selectors ====
58
+
59
+ export const selectFilteredNodes: Selector<NodesStateSlice, NodesPreparedEntity[] | undefined> =
60
+ createSelector(
61
+ [
62
+ selectNodesList,
63
+ selectNodesUptimeFilter,
64
+ selectSearchValue,
65
+ (state) => state.settings.problemFilter,
66
+ ],
67
+ (nodesList, uptimeFilter, searchValue, problemFilter) => {
68
+ let result = filterNodesByUptime(nodesList, uptimeFilter);
69
+ result = filterNodesByProblemsStatus(result, problemFilter);
70
+ result = filterNodesBySearchValue(result, searchValue);
71
+
72
+ return result;
73
+ },
74
+ );
@@ -0,0 +1,46 @@
1
+ import type {TComputeInfo} from '../../../types/api/compute';
2
+ import type {TNodesInfo} from '../../../types/api/nodes';
3
+ import {calcUptime} from '../../../utils';
4
+
5
+ import type {NodesHandledResponse, NodesPreparedEntity} from './types';
6
+
7
+ export const prepareComputeNodesData = (data: TComputeInfo): NodesHandledResponse => {
8
+ const preparedNodes: NodesPreparedEntity[] = [];
9
+
10
+ if (data.Tenants) {
11
+ for (const tenant of data.Tenants) {
12
+ tenant.Nodes?.forEach((node) => {
13
+ preparedNodes.push({
14
+ ...node,
15
+ TenantName: tenant.Name,
16
+ SystemState: node?.Overall,
17
+ Uptime: calcUptime(node?.StartTime),
18
+ });
19
+ });
20
+ }
21
+ }
22
+
23
+ return {
24
+ Nodes: preparedNodes,
25
+ TotalNodes: preparedNodes.length,
26
+ };
27
+ };
28
+
29
+ export const prepareNodesData = (data: TNodesInfo): NodesHandledResponse => {
30
+ const rawNodes = data.Nodes || [];
31
+
32
+ const preparedNodes = rawNodes.map((node) => {
33
+ return {
34
+ ...node.SystemState,
35
+ Tablets: node.Tablets,
36
+ NodeId: node.NodeId,
37
+ Uptime: calcUptime(node.SystemState?.StartTime),
38
+ TenantName: node.SystemState?.Tenants?.[0],
39
+ };
40
+ });
41
+
42
+ return {
43
+ Nodes: preparedNodes,
44
+ TotalNodes: Number(data.TotalNodes) ?? preparedNodes.length,
45
+ };
46
+ };
@@ -1,5 +1,5 @@
1
1
  import type {IResponseError} from '../../../types/api/error';
2
- import type {IProcessSpeedStats} from '../../../utils/bytesParsers';
2
+ import type {ProcessSpeedStats} from '../../../utils/bytesParsers';
3
3
  import type {ApiRequestAction} from '../../utils';
4
4
 
5
5
  import {FETCH_PARTITIONS, setDataWasNotLoaded, setSelectedConsumer} from './partitions';
@@ -9,8 +9,8 @@ export interface PreparedPartitionData {
9
9
  partitionId: string;
10
10
  storeSize: string;
11
11
 
12
- writeSpeed: IProcessSpeedStats;
13
- readSpeed?: IProcessSpeedStats;
12
+ writeSpeed: ProcessSpeedStats;
13
+ readSpeed?: ProcessSpeedStats;
14
14
 
15
15
  partitionWriteLag: number;
16
16
  partitionWriteIdleTime: number;
@@ -1,7 +1,6 @@
1
1
  import type {Reducer} from 'redux';
2
2
  import type {ThunkAction} from 'redux-thunk';
3
3
 
4
- import type {ValueOf} from '../../../types/common';
5
4
  import {
6
5
  SAVED_QUERIES_KEY,
7
6
  THEME_KEY,
@@ -23,6 +22,7 @@ import {TENANT_PAGES_IDS} from '../tenant/constants';
23
22
 
24
23
  import type {RootState} from '..';
25
24
  import type {
25
+ ProblemFilterValue,
26
26
  SetSettingValueAction,
27
27
  SettingsAction,
28
28
  SettingsRootStateSlice,
@@ -133,11 +133,13 @@ export const getParsedSettingValue = (state: SettingsRootStateSlice, name: strin
133
133
  return parseJson(value);
134
134
  };
135
135
 
136
- export const changeFilter = (filter: ValueOf<typeof ProblemFilterValues>) => {
136
+ export const changeFilter = (filter: ProblemFilterValue) => {
137
137
  return {
138
138
  type: CHANGE_PROBLEM_FILTER,
139
139
  data: filter,
140
140
  } as const;
141
141
  };
142
142
 
143
+ export const selectProblemFilter = (state: SettingsRootStateSlice) => state.settings.problemFilter;
144
+
143
145
  export default settings;
@@ -1,8 +1,10 @@
1
1
  import type {ValueOf} from '../../../types/common';
2
2
  import {changeFilter, ProblemFilterValues, SET_SETTING_VALUE} from './settings';
3
3
 
4
+ export type ProblemFilterValue = ValueOf<typeof ProblemFilterValues>;
5
+
4
6
  export interface SettingsState {
5
- problemFilter: ValueOf<typeof ProblemFilterValues>;
7
+ problemFilter: ProblemFilterValue;
6
8
  userSettings: Record<string, string | undefined>;
7
9
  systemSettings: Record<string, string | undefined>;
8
10
  }