ydb-embedded-ui 3.0.0 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/components/ClusterInfo/ClusterInfo.tsx +1 -1
  3. package/dist/components/InfoViewer/InfoViewer.tsx +1 -1
  4. package/dist/components/InfoViewer/schemaInfo/CDCStreamInfo.tsx +5 -2
  5. package/dist/components/InfoViewer/schemaInfo/PersQueueGroupInfo.tsx +6 -5
  6. package/dist/components/InfoViewer/schemaInfo/TableIndexInfo.tsx +5 -2
  7. package/dist/components/ProblemFilter/ProblemFilter.tsx +18 -0
  8. package/dist/components/ProblemFilter/index.ts +1 -0
  9. package/dist/components/UptimeFIlter/UptimeFilter.tsx +4 -3
  10. package/dist/containers/Nodes/Nodes.js +2 -2
  11. package/dist/containers/NodesViewer/NodesViewer.js +2 -2
  12. package/dist/containers/Pool/Pool.js +2 -2
  13. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +2 -3
  14. package/dist/containers/Tenant/Diagnostics/Network/Network.js +2 -2
  15. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +11 -9
  16. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js +6 -3
  17. package/dist/containers/Tenant/Diagnostics/TopShards/DateRange/DateRange.scss +13 -0
  18. package/dist/containers/Tenant/Diagnostics/TopShards/DateRange/DateRange.tsx +75 -0
  19. package/dist/containers/Tenant/Diagnostics/TopShards/DateRange/index.ts +1 -0
  20. package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.scss +17 -1
  21. package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.tsx +278 -0
  22. package/dist/containers/Tenant/Diagnostics/TopShards/i18n/en.json +4 -0
  23. package/dist/containers/Tenant/Diagnostics/TopShards/i18n/index.ts +11 -0
  24. package/dist/containers/Tenant/Diagnostics/TopShards/i18n/ru.json +4 -0
  25. package/dist/containers/Tenant/Diagnostics/TopShards/index.ts +1 -0
  26. package/dist/containers/Tenant/Schema/SchemaInfoViewer/SchemaInfoViewer.js +35 -22
  27. package/dist/containers/Tenant/Schema/SchemaInfoViewer/SchemaInfoViewer.scss +8 -0
  28. package/dist/containers/Tenant/Tenant.tsx +1 -1
  29. package/dist/containers/Tenant/utils/index.ts +8 -0
  30. package/dist/containers/Tenant/utils/schema.ts +45 -0
  31. package/dist/containers/Tenants/Tenants.js +2 -2
  32. package/dist/services/api.d.ts +3 -0
  33. package/dist/services/api.js +1 -1
  34. package/dist/store/reducers/{nodes.js → nodes.ts} +20 -14
  35. package/dist/store/reducers/shardsWorkload.ts +182 -0
  36. package/dist/store/reducers/{tooltip.js → tooltip.ts} +28 -11
  37. package/dist/store/state-url-mapping.js +8 -0
  38. package/dist/types/api/nodes.ts +3 -3
  39. package/dist/types/api/schema.ts +1 -1
  40. package/dist/types/api/tenant.ts +131 -0
  41. package/dist/types/store/nodes.ts +32 -0
  42. package/dist/types/store/shardsWorkload.ts +28 -0
  43. package/dist/types/store/tooltip.ts +25 -0
  44. package/dist/utils/constants.ts +2 -0
  45. package/dist/utils/nodes.ts +4 -4
  46. package/dist/utils/query.ts +1 -1
  47. package/dist/utils/tooltip.js +8 -6
  48. package/package.json +2 -2
  49. package/dist/components/ProblemFilter/ProblemFilter.js +0 -24
  50. package/dist/containers/Tenant/Diagnostics/TopShards/TopShards.js +0 -246
  51. package/dist/store/reducers/shardsWorkload.js +0 -101
  52. package/dist/utils/actionsConstants.js +0 -4
@@ -0,0 +1,278 @@
1
+ import {useState, useContext, useEffect, useMemo} from 'react';
2
+ import {useDispatch} from 'react-redux';
3
+ import cn from 'bem-cn-lite';
4
+
5
+ import DataTable, {Column, Settings, SortOrder} from '@yandex-cloud/react-data-table';
6
+ import {Loader} from '@gravity-ui/uikit';
7
+
8
+ import InternalLink from '../../../../components/InternalLink/InternalLink';
9
+
10
+ import HistoryContext from '../../../../contexts/HistoryContext';
11
+
12
+ import routes, {createHref} from '../../../../routes';
13
+
14
+ import {
15
+ sendShardQuery,
16
+ setShardQueryOptions,
17
+ setTopShardFilters,
18
+ } from '../../../../store/reducers/shardsWorkload';
19
+ import {setCurrentSchemaPath, getSchema} from '../../../../store/reducers/schema';
20
+ import type {IShardsWorkloadFilters} from '../../../../types/store/shardsWorkload';
21
+
22
+ import type {EPathType} from '../../../../types/api/schema';
23
+
24
+ import {formatDateTime, formatNumber} from '../../../../utils';
25
+ import {DEFAULT_TABLE_SETTINGS, HOUR_IN_SECONDS} from '../../../../utils/constants';
26
+ import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
27
+ import {prepareQueryError} from '../../../../utils/query';
28
+
29
+ import {getDefaultNodePath} from '../../../Node/NodePages';
30
+
31
+ import {isColumnEntityType} from '../../utils/schema';
32
+
33
+ import {DateRange, DateRangeValues} from './DateRange';
34
+
35
+ import i18n from './i18n';
36
+ import './TopShards.scss';
37
+
38
+ const b = cn('top-shards');
39
+ const bLink = cn('yc-link');
40
+
41
+ const TABLE_SETTINGS: Settings = {
42
+ ...DEFAULT_TABLE_SETTINGS,
43
+ dynamicRender: false, // no more than 20 rows
44
+ externalSort: true,
45
+ disableSortReset: true,
46
+ defaultOrder: DataTable.DESCENDING,
47
+ };
48
+
49
+ const tableColumnsNames = {
50
+ TabletId: 'TabletId',
51
+ CPUCores: 'CPUCores',
52
+ DataSize: 'DataSize',
53
+ Path: 'Path',
54
+ NodeId: 'NodeId',
55
+ PeakTime: 'PeakTime',
56
+ InFlightTxCount: 'InFlightTxCount',
57
+ };
58
+
59
+ function prepareCPUWorkloadValue(value: string) {
60
+ return `${(Number(value) * 100).toFixed(2)}%`;
61
+ }
62
+
63
+ function stringToDataTableSortOrder(value: string): SortOrder[] | undefined {
64
+ return value
65
+ ? value.split(',').map((columnId) => ({
66
+ columnId,
67
+ order: DataTable.DESCENDING,
68
+ }))
69
+ : undefined;
70
+ }
71
+
72
+ function stringToQuerySortOrder(value: string) {
73
+ return value
74
+ ? value.split(',').map((columnId) => ({
75
+ columnId,
76
+ order: 'DESC',
77
+ }))
78
+ : undefined;
79
+ }
80
+
81
+ function dataTableToStringSortOrder(value: SortOrder | SortOrder[] = []) {
82
+ const sortOrders = Array.isArray(value) ? value : [value];
83
+ return sortOrders.map(({columnId}) => columnId).join(',');
84
+ }
85
+
86
+ interface TopShardsProps {
87
+ tenantPath: string;
88
+ type?: EPathType;
89
+ }
90
+
91
+ export const TopShards = ({tenantPath, type}: TopShardsProps) => {
92
+ const dispatch = useDispatch();
93
+
94
+ const {autorefresh, currentSchemaPath} = useTypedSelector((state) => state.schema);
95
+
96
+ const {
97
+ loading,
98
+ data: {result: data = undefined} = {},
99
+ filters: storeFilters,
100
+ error,
101
+ wasLoaded,
102
+ } = useTypedSelector((state) => state.shardsWorkload);
103
+
104
+ // default date range should be the last hour, but shouldn't propagate into URL until user interacts with the control
105
+ // redux initial value can't be used, as it synchronizes with URL
106
+ const [filters, setFilters] = useState<IShardsWorkloadFilters>(() => {
107
+ if (!storeFilters?.from && !storeFilters?.to) {
108
+ return {
109
+ from: Date.now() - HOUR_IN_SECONDS * 1000,
110
+ to: Date.now(),
111
+ };
112
+ }
113
+
114
+ return storeFilters;
115
+ });
116
+
117
+ const [sortOrder, setSortOrder] = useState(tableColumnsNames.CPUCores);
118
+
119
+ useAutofetcher(
120
+ () => {
121
+ dispatch(
122
+ sendShardQuery({
123
+ database: tenantPath,
124
+ path: currentSchemaPath,
125
+ sortOrder: stringToQuerySortOrder(sortOrder),
126
+ filters,
127
+ }),
128
+ );
129
+ },
130
+ [dispatch, tenantPath, currentSchemaPath, sortOrder, filters],
131
+ autorefresh,
132
+ );
133
+
134
+ // don't show loader for requests triggered by table sort, only for path change
135
+ useEffect(() => {
136
+ dispatch(
137
+ setShardQueryOptions({
138
+ wasLoaded: false,
139
+ data: undefined,
140
+ }),
141
+ );
142
+ }, [dispatch, currentSchemaPath, tenantPath, filters]);
143
+
144
+ const history = useContext(HistoryContext);
145
+
146
+ const onSort = (newSortOrder?: SortOrder | SortOrder[]) => {
147
+ // omit information about sort order to disable ASC order, only DESC makes sense for top shards
148
+ // use a string (and not the DataTable default format) to prevent reference change,
149
+ // which would cause an excess state change, to avoid repeating requests
150
+ setSortOrder(dataTableToStringSortOrder(newSortOrder));
151
+ };
152
+
153
+ const handleDateRangeChange = (value: DateRangeValues) => {
154
+ dispatch(setTopShardFilters(value));
155
+ setFilters(value);
156
+ };
157
+
158
+ const tableColumns: Column<any>[] = useMemo(() => {
159
+ const onSchemaClick = (schemaPath: string) => {
160
+ return () => {
161
+ dispatch(setCurrentSchemaPath(schemaPath));
162
+ dispatch(getSchema({path: schemaPath}));
163
+ history.go(0);
164
+ };
165
+ };
166
+
167
+ return [
168
+ {
169
+ name: tableColumnsNames.Path,
170
+ render: ({value: relativeNodePath}) => {
171
+ return (
172
+ <span
173
+ onClick={onSchemaClick(tenantPath + relativeNodePath)}
174
+ className={bLink({view: 'normal'})}
175
+ >
176
+ {relativeNodePath as string}
177
+ </span>
178
+ );
179
+ },
180
+ sortable: false,
181
+ },
182
+ {
183
+ name: tableColumnsNames.CPUCores,
184
+ render: ({value}) => {
185
+ return prepareCPUWorkloadValue(value as string);
186
+ },
187
+ align: DataTable.RIGHT,
188
+ },
189
+ {
190
+ name: tableColumnsNames.DataSize,
191
+ header: 'DataSize (B)',
192
+ render: ({value}) => {
193
+ return formatNumber(value as number);
194
+ },
195
+ align: DataTable.RIGHT,
196
+ },
197
+ {
198
+ name: tableColumnsNames.TabletId,
199
+ render: ({value}) => {
200
+ return (
201
+ <InternalLink to={createHref(routes.tablet, {id: value})}>
202
+ {value as string}
203
+ </InternalLink>
204
+ );
205
+ },
206
+ sortable: false,
207
+ },
208
+ {
209
+ name: tableColumnsNames.NodeId,
210
+ render: ({value: nodeId}) => {
211
+ return (
212
+ <InternalLink to={getDefaultNodePath(nodeId as string)}>
213
+ {nodeId as string}
214
+ </InternalLink>
215
+ );
216
+ },
217
+ align: DataTable.RIGHT,
218
+ sortable: false,
219
+ },
220
+ {
221
+ name: tableColumnsNames.PeakTime,
222
+ render: ({value}) => formatDateTime(new Date(value as string).valueOf()),
223
+ sortable: false,
224
+ },
225
+ {
226
+ name: tableColumnsNames.InFlightTxCount,
227
+ render: ({value}) => formatNumber(value as number),
228
+ align: DataTable.RIGHT,
229
+ sortable: false,
230
+ },
231
+ ];
232
+ }, [dispatch, history, tenantPath]);
233
+
234
+ const renderLoader = () => {
235
+ return (
236
+ <div className={b('loader')}>
237
+ <Loader size="m" />
238
+ </div>
239
+ );
240
+ };
241
+
242
+ const renderContent = () => {
243
+ if (loading && !wasLoaded) {
244
+ return renderLoader();
245
+ }
246
+
247
+ if (error && !error.isCancelled) {
248
+ return <div className="error">{prepareQueryError(error)}</div>;
249
+ }
250
+
251
+ if (!data || isColumnEntityType(type)) {
252
+ return i18n('no-data');
253
+ }
254
+
255
+ return (
256
+ <div className={b('table')}>
257
+ <DataTable
258
+ columns={tableColumns}
259
+ data={data}
260
+ settings={TABLE_SETTINGS}
261
+ theme="yandex-cloud"
262
+ onSort={onSort}
263
+ sortOrder={stringToDataTableSortOrder(sortOrder)}
264
+ />
265
+ </div>
266
+ );
267
+ };
268
+
269
+ return (
270
+ <div className={b()}>
271
+ <div className={b('controls')}>
272
+ {i18n('description')}
273
+ <DateRange from={filters.from} to={filters.to} onChange={handleDateRangeChange} />
274
+ </div>
275
+ {renderContent()}
276
+ </div>
277
+ );
278
+ };
@@ -0,0 +1,4 @@
1
+ {
2
+ "no-data": "No data",
3
+ "description": "Shards with CPU load over 70% are listed"
4
+ }
@@ -0,0 +1,11 @@
1
+ import {i18n, Lang} from '../../../../../utils/i18n';
2
+
3
+ import en from './en.json';
4
+ import ru from './ru.json';
5
+
6
+ const COMPONENT = 'ydb-diagnostics-top-shards';
7
+
8
+ i18n.registerKeyset(Lang.En, COMPONENT, en);
9
+ i18n.registerKeyset(Lang.Ru, COMPONENT, ru);
10
+
11
+ export default i18n.keyset(COMPONENT);
@@ -0,0 +1,4 @@
1
+ {
2
+ "no-data": "Нет данных",
3
+ "description": "Отображаются шарды с загрузкой CPU выше 70%"
4
+ }
@@ -0,0 +1 @@
1
+ export * from './TopShards';
@@ -1,12 +1,15 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import cn from 'bem-cn-lite';
4
- import './SchemaInfoViewer.scss';
5
4
 
6
5
  import {formatCPU, formatBytes, formatNumber, formatBps, formatDateTime} from '../../../../utils';
7
6
 
8
7
  import {InfoViewer, createInfoFormatter} from '../../../../components/InfoViewer';
9
8
 
9
+ import {getEntityName} from '../../utils';
10
+
11
+ import './SchemaInfoViewer.scss';
12
+
10
13
  const b = cn('schema-info-viewer');
11
14
 
12
15
  const formatTabletMetricsItem = createInfoFormatter({
@@ -68,6 +71,8 @@ class SchemaInfoViewer extends React.Component {
68
71
 
69
72
  renderContent(data) {
70
73
  const {PathDescription = {}} = data;
74
+ const entityName = getEntityName(PathDescription);
75
+
71
76
  const {
72
77
  TableStats = {},
73
78
  TabletMetrics = {},
@@ -99,13 +104,15 @@ class SchemaInfoViewer extends React.Component {
99
104
  } = TableStats;
100
105
  const {FollowerGroups, FollowerCount, CrossDataCenterFollowerCount} = PartitionConfig;
101
106
 
107
+ const generalTableInfo = formatTableStats({
108
+ PartCount,
109
+ RowCount,
110
+ DataSize,
111
+ IndexSize,
112
+ ...restTableStats,
113
+ });
114
+
102
115
  const tableStatsInfo = [
103
- formatTableStats({
104
- PartCount,
105
- RowCount,
106
- DataSize,
107
- IndexSize,
108
- }),
109
116
  formatTableStats({
110
117
  LastAccessTime,
111
118
  LastUpdateTime,
@@ -125,7 +132,6 @@ class SchemaInfoViewer extends React.Component {
125
132
  RangeReads,
126
133
  RangeReadRows,
127
134
  }),
128
- formatTableStats(restTableStats),
129
135
  ];
130
136
 
131
137
  const tabletMetricsInfo = Object.keys(TabletMetrics).map((key) =>
@@ -151,24 +157,30 @@ class SchemaInfoViewer extends React.Component {
151
157
  );
152
158
  }
153
159
 
154
- if ([tabletMetricsInfo, partitionConfigInfo, tableStatsInfo.flat()].flat().length === 0) {
155
- return <div className={b('item')}>Empty</div>;
160
+ if (
161
+ [generalTableInfo, tabletMetricsInfo, partitionConfigInfo, tableStatsInfo.flat()].flat()
162
+ .length === 0
163
+ ) {
164
+ return <div className={b('title')}>{entityName}</div>;
156
165
  }
157
166
 
158
167
  return (
159
- <div className={b('row')}>
160
- {tabletMetricsInfo.length > 0 || partitionConfigInfo.length > 0 ? (
168
+ <div>
169
+ <div>{this.renderItem(generalTableInfo, entityName)}</div>
170
+ <div className={b('row')}>
171
+ {tabletMetricsInfo.length > 0 || partitionConfigInfo.length > 0 ? (
172
+ <div className={b('col')}>
173
+ {this.renderItem(tabletMetricsInfo, 'Tablet Metrics')}
174
+ {this.renderItem(partitionConfigInfo, 'Partition Config')}
175
+ </div>
176
+ ) : null}
161
177
  <div className={b('col')}>
162
- {this.renderItem(tabletMetricsInfo, 'Tablet Metrics')}
163
- {this.renderItem(partitionConfigInfo, 'Partition Config')}
178
+ {tableStatsInfo.map((info, index) => (
179
+ <React.Fragment key={index}>
180
+ {this.renderItem(info, index === 0 ? 'Table Stats' : undefined)}
181
+ </React.Fragment>
182
+ ))}
164
183
  </div>
165
- ) : null}
166
- <div className={b('col')}>
167
- {tableStatsInfo.map((info, index) => (
168
- <React.Fragment key={index}>
169
- {this.renderItem(info, index === 0 ? 'Table Stats' : undefined)}
170
- </React.Fragment>
171
- ))}
172
184
  </div>
173
185
  </div>
174
186
  );
@@ -176,11 +188,12 @@ class SchemaInfoViewer extends React.Component {
176
188
 
177
189
  render() {
178
190
  const {data} = this.props;
191
+ const entityName = getEntityName(data?.PathDescription);
179
192
 
180
193
  if (data) {
181
194
  return <div className={b()}>{this.renderContent(data)}</div>;
182
195
  } else {
183
- return <div className="error">no schema data</div>;
196
+ return <div className="error">No {entityName} data</div>;
184
197
  }
185
198
  }
186
199
  }
@@ -26,4 +26,12 @@
26
26
  grid-template-columns: minmax(max-content, 280px);
27
27
  }
28
28
  }
29
+
30
+ &__title {
31
+ margin: 15px 0 10px;
32
+
33
+ font-size: var(--yc-text-body-2-font-size);
34
+ font-weight: 600;
35
+ line-height: var(--yc-text-body-2-line-height);
36
+ }
29
37
  }
@@ -90,7 +90,7 @@ function Tenant(props: TenantProps) {
90
90
 
91
91
  useEffect(() => {
92
92
  dispatch(disableAutorefresh());
93
- }, [currentSchemaPath, tenantName]);
93
+ }, [currentSchemaPath, tenantName, dispatch]);
94
94
 
95
95
  useEffect(() => {
96
96
  if (tenantName) {
@@ -0,0 +1,8 @@
1
+ import {TPathDescription} from '../../../types/api/schema';
2
+ import {mapPathTypeToEntityName} from './schema';
3
+
4
+ export const getEntityName = (pathDescription?: TPathDescription) => {
5
+ const {PathType, PathSubType} = pathDescription?.Self || {};
6
+
7
+ return mapPathTypeToEntityName(PathType, PathSubType);
8
+ };
@@ -1,5 +1,7 @@
1
1
  import type {NavigationTreeNodeType} from 'ydb-ui-components';
2
+
2
3
  import {EPathSubType, EPathType} from '../../../types/api/schema';
4
+ import {ETenantType} from '../../../types/api/tenant';
3
5
 
4
6
  // this file contains verbose mappings that are typed in a way that ensures
5
7
  // correctness when a new node type or a new path type is added
@@ -41,6 +43,49 @@ export const mapPathTypeToNavigationTreeType = (
41
43
 
42
44
  // ====================
43
45
 
46
+ const pathSubTypeToEntityName: Record<EPathSubType, string | undefined> = {
47
+ [EPathSubType.EPathSubTypeSyncIndexImplTable]: 'Secondary Index Table',
48
+ [EPathSubType.EPathSubTypeAsyncIndexImplTable]: 'Secondary Index Table',
49
+
50
+ [EPathSubType.EPathSubTypeStreamImpl]: undefined,
51
+ [EPathSubType.EPathSubTypeEmpty]: undefined,
52
+ };
53
+
54
+ const pathTypeToEntityName: Record<EPathType, string | undefined> = {
55
+ [EPathType.EPathTypeInvalid]: undefined,
56
+
57
+ [EPathType.EPathTypeSubDomain]: 'Database',
58
+ [EPathType.EPathTypeExtSubDomain]: 'Database',
59
+
60
+ [EPathType.EPathTypeDir]: 'Directory',
61
+ [EPathType.EPathTypeTable]: 'Table',
62
+ [EPathType.EPathTypeTableIndex]: 'Secondary Index',
63
+ [EPathType.EPathTypeColumnStore]: 'Tablestore',
64
+ [EPathType.EPathTypeColumnTable]: 'Columntable',
65
+ [EPathType.EPathTypeCdcStream]: 'Changefeed',
66
+ [EPathType.EPathTypePersQueueGroup]: 'Topic',
67
+ };
68
+
69
+ export const mapPathTypeToEntityName = (
70
+ type?: EPathType,
71
+ subType?: EPathSubType,
72
+ ): string | undefined =>
73
+ (subType && pathSubTypeToEntityName[subType]) || (type && pathTypeToEntityName[type]);
74
+
75
+ // ====================
76
+
77
+ const databaseTypeToDBName: Record<ETenantType, string | undefined> = {
78
+ [ETenantType.UnknownTenantType]: 'Database',
79
+ [ETenantType.Domain]: 'Cluster Root',
80
+ [ETenantType.Dedicated]: 'Dedicated Database',
81
+ [ETenantType.Shared]: 'Shared Database',
82
+ [ETenantType.Serverless]: 'Serverless Database',
83
+ };
84
+
85
+ export const mapDatabaseTypeToDBName = (type?: ETenantType) => type && databaseTypeToDBName[type];
86
+
87
+ // ====================
88
+
44
89
  const pathTypeToIsTable: Record<EPathType, boolean> = {
45
90
  [EPathType.EPathTypeTable]: true,
46
91
  [EPathType.EPathTypeColumnTable]: true,
@@ -10,7 +10,7 @@ import {Loader, TextInput, Button} from '@gravity-ui/uikit';
10
10
  import EntityStatus from '../../components/EntityStatus/EntityStatus';
11
11
  import PoolsGraph from '../../components/PoolsGraph/PoolsGraph';
12
12
  import TabletsStatistic from '../../components/TabletsStatistic/TabletsStatistic';
13
- import ProblemFilter, {problemFilterType} from '../../components/ProblemFilter/ProblemFilter';
13
+ import {ProblemFilter} from '../../components/ProblemFilter';
14
14
  import {Illustration} from '../../components/Illustration';
15
15
  import {AutoFetcher} from '../../utils/autofetcher';
16
16
 
@@ -50,7 +50,7 @@ class Tenants extends React.Component {
50
50
  searchQuery: PropTypes.string,
51
51
  handleSearchQuery: PropTypes.func,
52
52
  setHeader: PropTypes.func,
53
- filter: problemFilterType,
53
+ filter: PropTypes.string,
54
54
  changeFilter: PropTypes.func,
55
55
  cluster: PropTypes.object,
56
56
  singleClusterMode: PropTypes.bool,
@@ -45,6 +45,9 @@ interface Window {
45
45
  getHealthcheckInfo: (
46
46
  database: string,
47
47
  ) => Promise<import('../types/api/healthcheck').HealthCheckAPIResponse>;
48
+ getTenantInfo: (params: {
49
+ path: string;
50
+ }) => Promise<import('../types/api/tenant').TTenantInfo>;
48
51
  [method: string]: Function;
49
52
  };
50
53
  }
@@ -1,4 +1,4 @@
1
- import AxiosWrapper from '@yandex-cloud/axios-wrapper';
1
+ import AxiosWrapper from '@gravity-ui/axios-wrapper';
2
2
 
3
3
  import {backend as BACKEND} from '../store';
4
4
  import {StorageTypes} from '../store/reducers/storage';
@@ -1,8 +1,12 @@
1
- import {createRequestActionTypes, createApiRequest} from '../utils';
1
+ import type {Reducer} from 'redux';
2
+
2
3
  import '../../services/api';
3
4
  import {NodesUptimeFilterValues} from '../../utils/nodes';
5
+ import {INodesAction, INodesRootStateSlice, INodesState} from '../../types/store/nodes';
6
+
7
+ import {createRequestActionTypes, createApiRequest} from '../utils';
4
8
 
5
- const FETCH_NODES = createRequestActionTypes('nodes', 'FETCH_NODES');
9
+ export const FETCH_NODES = createRequestActionTypes('nodes', 'FETCH_NODES');
6
10
 
7
11
  const CLEAR_NODES = 'nodes/CLEAR_NODES';
8
12
  const SET_NODES_UPTIME_FILTER = 'nodes/SET_NODES_UPTIME_FILTER';
@@ -14,13 +18,12 @@ const initialState = {
14
18
  nodesUptimeFilter: NodesUptimeFilterValues.All,
15
19
  };
16
20
 
17
- const nodes = (state = initialState, action) => {
21
+ const nodes: Reducer<INodesState, INodesAction> = (state = initialState, action) => {
18
22
  switch (action.type) {
19
23
  case FETCH_NODES.REQUEST: {
20
24
  return {
21
25
  ...state,
22
26
  loading: true,
23
- requestTime: new Date().getTime(),
24
27
  };
25
28
  }
26
29
  case FETCH_NODES.SUCCESS: {
@@ -45,13 +48,15 @@ const nodes = (state = initialState, action) => {
45
48
  loading: true,
46
49
  data: undefined,
47
50
  wasLoaded: false,
48
- requestTime: new Date().getTime(),
49
51
  error: undefined,
50
52
  };
51
53
  }
52
54
 
53
55
  case SET_NODES_UPTIME_FILTER: {
54
- return {...state, nodesUptimeFilter: action.data};
56
+ return {
57
+ ...state,
58
+ nodesUptimeFilter: action.data,
59
+ };
55
60
  }
56
61
  case SET_DATA_WAS_NOT_LOADED: {
57
62
  return {
@@ -64,26 +69,27 @@ const nodes = (state = initialState, action) => {
64
69
  }
65
70
  };
66
71
 
67
- export function getNodes(path) {
72
+ export function getNodes(path: string) {
68
73
  return createApiRequest({
69
74
  request: window.api.getNodes(path),
70
75
  actions: FETCH_NODES,
71
76
  });
72
77
  }
73
78
 
74
- export const clearNodes = () => ({type: CLEAR_NODES});
79
+ export const clearNodes = () => ({type: CLEAR_NODES} as const);
75
80
 
76
- export const setNodesUptimeFilter = (value) => ({
77
- type: SET_NODES_UPTIME_FILTER,
78
- data: value,
79
- });
81
+ export const setNodesUptimeFilter = (value: NodesUptimeFilterValues) =>
82
+ ({
83
+ type: SET_NODES_UPTIME_FILTER,
84
+ data: value,
85
+ } as const);
80
86
 
81
87
  export const setDataWasNotLoaded = () => {
82
88
  return {
83
89
  type: SET_DATA_WAS_NOT_LOADED,
84
- };
90
+ } as const;
85
91
  };
86
92
 
87
- export const getNodesUptimeFilter = (state) => state.nodes.nodesUptimeFilter;
93
+ export const getNodesUptimeFilter = (state: INodesRootStateSlice) => state.nodes.nodesUptimeFilter;
88
94
 
89
95
  export default nodes;