ydb-embedded-ui 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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;