ydb-embedded-ui 2.5.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/components/InfoViewer/InfoViewer.scss +3 -3
  3. package/dist/components/InfoViewer/schemaInfo/CDCStreamInfo.tsx +23 -9
  4. package/dist/containers/Storage/Pdisk/Pdisk.tsx +3 -9
  5. package/dist/containers/Storage/Pdisk/__tests__/colors.tsx +1 -1
  6. package/dist/containers/Storage/Storage.js +11 -1
  7. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +39 -32
  8. package/dist/containers/Tenant/Diagnostics/Compute/Compute.js +21 -13
  9. package/dist/containers/Tenant/Diagnostics/Consumers/Consumers.tsx +22 -6
  10. package/dist/containers/Tenant/Diagnostics/Describe/Describe.tsx +40 -9
  11. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +15 -9
  12. package/dist/containers/Tenant/Diagnostics/DiagnosticsPages.ts +1 -1
  13. package/dist/containers/Tenant/Diagnostics/Healthcheck/Healthcheck.tsx +13 -5
  14. package/dist/containers/Tenant/Diagnostics/Network/Network.js +17 -4
  15. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +50 -16
  16. package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js +16 -2
  17. package/dist/containers/Tenant/Diagnostics/TopQueries/TopQueries.js +1 -0
  18. package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx +2 -2
  19. package/dist/containers/Tenant/utils/schema.ts +84 -0
  20. package/dist/services/api.d.ts +17 -11
  21. package/dist/store/reducers/describe.ts +56 -14
  22. package/dist/store/reducers/healthcheckInfo.ts +23 -8
  23. package/dist/store/reducers/network.js +22 -1
  24. package/dist/store/reducers/nodes.js +13 -0
  25. package/dist/store/reducers/schema.ts +84 -11
  26. package/dist/store/reducers/storage.js +13 -0
  27. package/dist/types/api/enums.ts +10 -0
  28. package/dist/types/api/nodes.ts +96 -0
  29. package/dist/types/api/pdisk.ts +48 -0
  30. package/dist/types/api/schema.ts +148 -9
  31. package/dist/types/api/storage.ts +3 -173
  32. package/dist/types/api/tablet.ts +97 -0
  33. package/dist/types/api/vdisk.ts +120 -0
  34. package/dist/types/store/describe.ts +8 -2
  35. package/dist/types/store/healthcheck.ts +12 -0
  36. package/dist/types/store/schema.ts +7 -1
  37. package/dist/utils/pdisk.ts +1 -1
  38. package/dist/utils/storage.ts +1 -1
  39. package/package.json +3 -2
@@ -10,6 +10,7 @@ import {
10
10
  getHealthcheckInfo,
11
11
  selectIssuesTreeById,
12
12
  selectIssuesTreesRoots,
13
+ setDataWasNotLoaded,
13
14
  } from '../../../../store/reducers/healthcheckInfo';
14
15
 
15
16
  import {Details} from './Details';
@@ -43,14 +44,21 @@ export const Healthcheck = (props: HealthcheckProps) => {
43
44
 
44
45
  const {autorefresh} = useTypedSelector((state) => state.schema);
45
46
 
46
- const fetchHealthcheck = useCallback(() => {
47
- dispatch(getHealthcheckInfo(tenant));
48
- }, [dispatch, tenant]);
47
+ const fetchHealthcheck = useCallback(
48
+ (isBackground = true) => {
49
+ if (!isBackground) {
50
+ dispatch(setDataWasNotLoaded());
51
+ }
52
+
53
+ dispatch(getHealthcheckInfo(tenant));
54
+ },
55
+ [dispatch, tenant],
56
+ );
49
57
 
50
58
  useAutofetcher(
51
- () => {
59
+ (isBackground) => {
52
60
  if (fetchData) {
53
- fetchHealthcheck();
61
+ fetchHealthcheck(isBackground);
54
62
  }
55
63
  },
56
64
  [fetchData, fetchHealthcheck],
@@ -12,7 +12,7 @@ import Icon from '../../../../components/Icon/Icon';
12
12
  import ProblemFilter, {problemFilterType} from '../../../../components/ProblemFilter/ProblemFilter';
13
13
  import {Illustration} from '../../../../components/Illustration';
14
14
 
15
- import {getNetworkInfo} from '../../../../store/reducers/network';
15
+ import {getNetworkInfo, setDataWasNotLoaded} from '../../../../store/reducers/network';
16
16
  import {hideTooltip, showTooltip} from '../../../../store/reducers/tooltip';
17
17
  import {ALL, PROBLEMS} from '../../../../utils/constants';
18
18
  import {changeFilter} from '../../../../store/reducers/settings';
@@ -26,6 +26,7 @@ const b = cn('network');
26
26
  class Network extends React.Component {
27
27
  static propTypes = {
28
28
  getNetworkInfo: PropTypes.func,
29
+ setDataWasNotLoaded: PropTypes.func,
29
30
  netWorkInfo: PropTypes.object,
30
31
  hideTooltip: PropTypes.func,
31
32
  showTooltip: PropTypes.func,
@@ -75,16 +76,27 @@ class Network extends React.Component {
75
76
  }
76
77
 
77
78
  componentDidUpdate(prevProps) {
78
- const {autorefresh, path} = this.props;
79
+ const {autorefresh, path, setDataWasNotLoaded, getNetworkInfo} = this.props;
79
80
 
80
- if (autorefresh && !prevProps.autorefresh) {
81
- getNetworkInfo(path);
81
+ const restartAutorefresh = () => {
82
+ this.autofetcher.stop();
82
83
  this.autofetcher.start();
83
84
  this.autofetcher.fetch(() => getNetworkInfo(path));
85
+ };
86
+
87
+ if (autorefresh && !prevProps.autorefresh) {
88
+ getNetworkInfo(path);
89
+ restartAutorefresh();
84
90
  }
85
91
  if (!autorefresh && prevProps.autorefresh) {
86
92
  this.autofetcher.stop();
87
93
  }
94
+
95
+ if (path !== prevProps.path) {
96
+ setDataWasNotLoaded();
97
+ getNetworkInfo(path);
98
+ restartAutorefresh();
99
+ }
88
100
  }
89
101
 
90
102
  componentWillUnmount() {
@@ -364,6 +376,7 @@ const mapDispatchToProps = {
364
376
  hideTooltip,
365
377
  showTooltip,
366
378
  changeFilter,
379
+ setDataWasNotLoaded,
367
380
  };
368
381
 
369
382
  export default connect(mapStateToProps, mapDispatchToProps)(Network);
@@ -1,5 +1,5 @@
1
- import {ReactNode, useMemo} from 'react';
2
- import {useDispatch, useSelector} from 'react-redux';
1
+ import {ReactNode, useCallback, useMemo} from 'react';
2
+ import {shallowEqual, useDispatch, useSelector} from 'react-redux';
3
3
  import cn from 'bem-cn-lite';
4
4
 
5
5
  import {Loader} from '@gravity-ui/uikit';
@@ -13,15 +13,24 @@ import {
13
13
  } from '../../../../components/InfoViewer/schemaInfo';
14
14
 
15
15
  import {EPathType, TColumnTableDescription} from '../../../../types/api/schema';
16
- import {isColumnEntityType, isTableType} from '../../utils/schema';
16
+ import {
17
+ isEntityWithMergedImplementation,
18
+ isColumnEntityType,
19
+ isTableType,
20
+ } from '../../utils/schema';
17
21
  //@ts-ignore
18
- import {getSchema, resetLoadingState} from '../../../../store/reducers/schema';
22
+ import {
23
+ getSchema,
24
+ getSchemaBatched,
25
+ resetLoadingState,
26
+ selectSchemaMergedChildrenPaths,
27
+ } from '../../../../store/reducers/schema';
19
28
  //@ts-ignore
20
29
  import {
21
30
  getOlapStats,
22
31
  resetLoadingState as resetOlapLoadingState,
23
32
  } from '../../../../store/reducers/olapStats';
24
- import {useAutofetcher} from '../../../../utils/hooks';
33
+ import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
25
34
 
26
35
  import './Overview.scss';
27
36
 
@@ -60,9 +69,7 @@ interface OverviewProps {
60
69
 
61
70
  const b = cn('kv-tenant-overview');
62
71
 
63
- function Overview(props: OverviewProps) {
64
- const {tenantName, type} = props;
65
-
72
+ function Overview({type, tenantName, className}: OverviewProps) {
66
73
  const dispatch = useDispatch();
67
74
 
68
75
  const {
@@ -78,14 +85,31 @@ function Overview(props: OverviewProps) {
78
85
 
79
86
  const loading = schemaLoading || olapStatsLoading;
80
87
 
81
- useAutofetcher(
82
- (isBackground) => {
88
+ const isEntityWithMergedImpl = isEntityWithMergedImplementation(type);
89
+
90
+ // There is a circular dependency here. Fetch data depends on children paths
91
+ // When data in store updated on fetch request,
92
+ // new object is set there, so source children array is updated
93
+ // This updates selector, the selector returns a new array, and data is fetched again
94
+ // To prevent it, shallowEqual, which compares array content, was added
95
+ const mergedChildrenPaths = useTypedSelector(
96
+ (state) => selectSchemaMergedChildrenPaths(state, currentSchemaPath, type),
97
+ shallowEqual,
98
+ );
99
+
100
+ const fetchData = useCallback(
101
+ (isBackground: boolean) => {
83
102
  if (!isBackground) {
84
103
  dispatch(resetLoadingState());
85
104
  }
86
105
 
87
106
  const schemaPath = currentSchemaPath || tenantName;
88
- dispatch(getSchema({path: schemaPath}));
107
+
108
+ if (!isEntityWithMergedImpl) {
109
+ dispatch(getSchema({path: schemaPath}));
110
+ } else if (mergedChildrenPaths) {
111
+ dispatch(getSchemaBatched([schemaPath, ...mergedChildrenPaths]));
112
+ }
89
113
 
90
114
  if (isTableType(type) && isColumnEntityType(type)) {
91
115
  if (!isBackground) {
@@ -94,10 +118,18 @@ function Overview(props: OverviewProps) {
94
118
  dispatch(getOlapStats({path: schemaPath}));
95
119
  }
96
120
  },
97
- [currentSchemaPath, dispatch, tenantName, type],
98
- autorefresh,
121
+ [
122
+ tenantName,
123
+ currentSchemaPath,
124
+ type,
125
+ isEntityWithMergedImpl,
126
+ mergedChildrenPaths,
127
+ dispatch,
128
+ ],
99
129
  );
100
130
 
131
+ useAutofetcher(fetchData, [fetchData], autorefresh);
132
+
101
133
  const tableSchema =
102
134
  currentItem?.PathDescription?.Table || currentItem?.PathDescription?.ColumnTableDescription;
103
135
 
@@ -128,7 +160,9 @@ function Overview(props: OverviewProps) {
128
160
  [EPathType.EPathTypeExtSubDomain]: undefined,
129
161
  [EPathType.EPathTypeColumnStore]: undefined,
130
162
  [EPathType.EPathTypeColumnTable]: undefined,
131
- [EPathType.EPathTypeCdcStream]: () => <CDCStreamInfo data={schemaData} />,
163
+ [EPathType.EPathTypeCdcStream]: () => (
164
+ <CDCStreamInfo data={schemaData} childrenPaths={mergedChildrenPaths} />
165
+ ),
132
166
  [EPathType.EPathTypePersQueueGroup]: () => <PersQueueGroupInfo data={schemaData} />,
133
167
  };
134
168
 
@@ -139,10 +173,10 @@ function Overview(props: OverviewProps) {
139
173
  );
140
174
  };
141
175
 
142
- return loading && !wasLoaded ? (
176
+ return (loading && !wasLoaded) || (isEntityWithMergedImpl && !mergedChildrenPaths) ? (
143
177
  renderLoader()
144
178
  ) : (
145
- <div className={props.className}>{renderContent()}</div>
179
+ <div className={className}>{renderContent()}</div>
146
180
  );
147
181
  }
148
182
 
@@ -53,6 +53,7 @@ class TenantOverview extends React.Component {
53
53
 
54
54
  componentDidMount() {
55
55
  const {tenantName, autorefresh, getTenantInfo} = this.props;
56
+ getTenantInfo({path: tenantName});
56
57
  this.autofetcher = new AutoFetcher();
57
58
  if (autorefresh) {
58
59
  this.autofetcher.start();
@@ -62,12 +63,25 @@ class TenantOverview extends React.Component {
62
63
 
63
64
  componentDidUpdate(prevProps) {
64
65
  const {autorefresh, tenantName, getTenantInfo} = this.props;
65
- if (autorefresh && !prevProps.autorefresh) {
66
- getTenantInfo({path: tenantName});
66
+
67
+ const restartAutorefresh = () => {
67
68
  this.autofetcher.stop();
68
69
  this.autofetcher.start();
69
70
  this.autofetcher.fetch(() => getTenantInfo({path: tenantName}));
71
+ };
72
+
73
+ if (prevProps.tenantName !== this.props.tenantName) {
74
+ getTenantInfo({path: tenantName});
75
+ if (autorefresh) {
76
+ restartAutorefresh();
77
+ }
70
78
  }
79
+
80
+ if (autorefresh && !prevProps.autorefresh) {
81
+ getTenantInfo({path: tenantName});
82
+ restartAutorefresh();
83
+ }
84
+
71
85
  if (!autorefresh && prevProps.autorefresh) {
72
86
  this.autofetcher.stop();
73
87
  }
@@ -90,6 +90,7 @@ class TopQueries extends React.Component {
90
90
  wasLoaded: false,
91
91
  data: undefined,
92
92
  });
93
+ this.getTopQueries();
93
94
  }
94
95
  }
95
96
 
@@ -6,7 +6,7 @@ import {NavigationTree} from 'ydb-ui-components';
6
6
  import {setCurrentSchemaPath, preloadSchemas} from '../../../../store/reducers/schema';
7
7
  import type {EPathType, TEvDescribeSchemeResult} from '../../../../types/api/schema';
8
8
 
9
- import {mapPathTypeToNavigationTreeType} from '../../utils/schema';
9
+ import {isChildlessPathType, mapPathTypeToNavigationTreeType} from '../../utils/schema';
10
10
  import {getActions} from '../../utils/schemaActions';
11
11
 
12
12
  interface SchemaTreeProps {
@@ -42,7 +42,7 @@ export function SchemaTree(props: SchemaTreeProps) {
42
42
  type: mapPathTypeToNavigationTreeType(PathType, PathSubType),
43
43
  // FIXME: should only be explicitly set to true for tables with indexes
44
44
  // at the moment of writing there is no property to determine this, fix later
45
- expandable: true,
45
+ expandable: !isChildlessPathType(PathType, PathSubType),
46
46
  };
47
47
  });
48
48
 
@@ -88,3 +88,87 @@ const pathTypeToIsColumn: Record<EPathType, boolean> = {
88
88
  };
89
89
 
90
90
  export const isColumnEntityType = (type?: EPathType) => (type && pathTypeToIsColumn[type]) ?? false;
91
+
92
+ // ====================
93
+
94
+ const pathTypeToIsDatabase: Record<EPathType, boolean> = {
95
+ [EPathType.EPathTypeSubDomain]: true,
96
+ [EPathType.EPathTypeExtSubDomain]: true,
97
+
98
+ [EPathType.EPathTypeInvalid]: false,
99
+ [EPathType.EPathTypeDir]: false,
100
+ [EPathType.EPathTypeColumnStore]: false,
101
+ [EPathType.EPathTypeColumnTable]: false,
102
+ [EPathType.EPathTypeTable]: false,
103
+ [EPathType.EPathTypeTableIndex]: false,
104
+ [EPathType.EPathTypeCdcStream]: false,
105
+ [EPathType.EPathTypePersQueueGroup]: false,
106
+ };
107
+
108
+ export const isDatabaseEntityType = (type?: EPathType) =>
109
+ (type && pathTypeToIsDatabase[type]) ?? false;
110
+
111
+ // ====================
112
+
113
+ const pathTypeToIsCdcStream: Record<EPathType, boolean> = {
114
+ [EPathType.EPathTypeCdcStream]: true,
115
+
116
+ [EPathType.EPathTypeInvalid]: false,
117
+ [EPathType.EPathTypeColumnStore]: false,
118
+ [EPathType.EPathTypeColumnTable]: false,
119
+ [EPathType.EPathTypeDir]: false,
120
+ [EPathType.EPathTypeTable]: false,
121
+ [EPathType.EPathTypeSubDomain]: false,
122
+ [EPathType.EPathTypeTableIndex]: false,
123
+ [EPathType.EPathTypeExtSubDomain]: false,
124
+ [EPathType.EPathTypePersQueueGroup]: false,
125
+ };
126
+
127
+ export const isCdcStreamEntityType = (type?: EPathType) =>
128
+ (type && pathTypeToIsCdcStream[type]) ?? false;
129
+
130
+ // ====================
131
+
132
+ const pathTypeToEntityWithMergedImplementation: Record<EPathType, boolean> = {
133
+ [EPathType.EPathTypeCdcStream]: true,
134
+
135
+ [EPathType.EPathTypePersQueueGroup]: false,
136
+ [EPathType.EPathTypeInvalid]: false,
137
+ [EPathType.EPathTypeColumnStore]: false,
138
+ [EPathType.EPathTypeColumnTable]: false,
139
+ [EPathType.EPathTypeDir]: false,
140
+ [EPathType.EPathTypeTable]: false,
141
+ [EPathType.EPathTypeSubDomain]: false,
142
+ [EPathType.EPathTypeTableIndex]: false,
143
+ [EPathType.EPathTypeExtSubDomain]: false,
144
+ };
145
+
146
+ export const isEntityWithMergedImplementation = (type?: EPathType) =>
147
+ (type && pathTypeToEntityWithMergedImplementation[type]) ?? false;
148
+
149
+ // ====================
150
+
151
+ const pathSubTypeToChildless: Record<EPathSubType, boolean> = {
152
+ [EPathSubType.EPathSubTypeSyncIndexImplTable]: true,
153
+ [EPathSubType.EPathSubTypeAsyncIndexImplTable]: true,
154
+
155
+ [EPathSubType.EPathSubTypeStreamImpl]: false,
156
+ [EPathSubType.EPathSubTypeEmpty]: false,
157
+ };
158
+
159
+ const pathTypeToChildless: Record<EPathType, boolean> = {
160
+ [EPathType.EPathTypeCdcStream]: true,
161
+ [EPathType.EPathTypePersQueueGroup]: true,
162
+
163
+ [EPathType.EPathTypeInvalid]: false,
164
+ [EPathType.EPathTypeColumnStore]: false,
165
+ [EPathType.EPathTypeColumnTable]: false,
166
+ [EPathType.EPathTypeDir]: false,
167
+ [EPathType.EPathTypeTable]: false,
168
+ [EPathType.EPathTypeSubDomain]: false,
169
+ [EPathType.EPathTypeTableIndex]: false,
170
+ [EPathType.EPathTypeExtSubDomain]: false,
171
+ };
172
+
173
+ export const isChildlessPathType = (type?: EPathType, subType?: EPathSubType) =>
174
+ ((subType && pathSubTypeToChildless[subType]) || (type && pathTypeToChildless[type])) ?? false;
@@ -8,25 +8,29 @@ interface Window {
8
8
  params: {path: string},
9
9
  axiosOptions?: AxiosOptions,
10
10
  ) => Promise<import('../types/api/schema').TEvDescribeSchemeResult>;
11
+ getDescribe: (
12
+ params: {path: string},
13
+ axiosOptions?: AxiosOptions,
14
+ ) => Promise<import('../types/api/schema').TEvDescribeSchemeResult>;
11
15
  getStorageInfo: (
12
16
  params: {
13
- tenant: string,
14
- filter: string,
15
- nodeId: string,
16
- type: 'Groups' | 'Nodes',
17
+ tenant: string;
18
+ filter: string;
19
+ nodeId: string;
20
+ type: 'Groups' | 'Nodes';
17
21
  },
18
22
  axiosOptions?: AxiosOptions,
19
23
  ) => Promise<import('../types/api/storage').TStorageInfo>;
20
24
  sendQuery: <
21
25
  Action extends import('../types/api/query').Actions,
22
- Schema extends import('../types/api/query').Schemas = undefined
26
+ Schema extends import('../types/api/query').Schemas = undefined,
23
27
  >(
24
28
  params: {
25
- query?: string,
26
- database?: string,
27
- action?: Action,
28
- stats?: string,
29
- schema?: Schema,
29
+ query?: string;
30
+ database?: string;
31
+ action?: Action;
32
+ stats?: string;
33
+ schema?: Schema;
30
34
  },
31
35
  axiosOptions?: AxiosOptions,
32
36
  ) => Promise<import('../types/api/query').QueryAPIResponse<Action, Schema>>;
@@ -38,7 +42,9 @@ interface Window {
38
42
  query: string,
39
43
  database: string,
40
44
  ) => Promise<import('../types/api/query').QueryAPIExplainResponse<'explain-ast'>>;
41
- getHealthcheckInfo: (database: string) => Promise<import('../types/api/healthcheck').HealthCheckAPIResponse>,
45
+ getHealthcheckInfo: (
46
+ database: string,
47
+ ) => Promise<import('../types/api/healthcheck').HealthCheckAPIResponse>;
42
48
  [method: string]: Function;
43
49
  };
44
50
  }
@@ -3,7 +3,13 @@ import {Reducer} from 'redux';
3
3
 
4
4
  import '../../services/api';
5
5
  import {IConsumer} from '../../types/api/consumers';
6
- import {IDescribeRootStateSlice, IDescribeState, IDescribeAction} from '../../types/store/describe';
6
+ import {
7
+ IDescribeRootStateSlice,
8
+ IDescribeState,
9
+ IDescribeAction,
10
+ IDescribeHandledResponse,
11
+ IDescribeData,
12
+ } from '../../types/store/describe';
7
13
  import {createRequestActionTypes, createApiRequest} from '../utils';
8
14
 
9
15
  export const FETCH_DESCRIBE = createRequestActionTypes('describe', 'FETCH_DESCRIBE');
@@ -27,16 +33,8 @@ const describe: Reducer<IDescribeState, IDescribeAction> = (state = initialState
27
33
  };
28
34
  }
29
35
  case FETCH_DESCRIBE.SUCCESS: {
30
- const data = action.data;
31
-
32
- const isCurrentDescribePath = data.Path === state.currentDescribePath;
33
-
34
- let newData = state.data;
35
-
36
- if (data.Path) {
37
- newData = JSON.parse(JSON.stringify(state.data));
38
- newData[data.Path] = data;
39
- }
36
+ const isCurrentDescribePath = action.data.path === state.currentDescribePath;
37
+ const newData = {...state.data, ...action.data.data};
40
38
 
41
39
  if (!isCurrentDescribePath) {
42
40
  return {
@@ -48,7 +46,7 @@ const describe: Reducer<IDescribeState, IDescribeAction> = (state = initialState
48
46
  return {
49
47
  ...state,
50
48
  data: newData,
51
- currentDescribe: data,
49
+ currentDescribe: action.data.currentDescribe,
52
50
  loading: false,
53
51
  wasLoaded: true,
54
52
  error: undefined,
@@ -97,7 +95,7 @@ export const setDataWasNotLoaded = () => {
97
95
  };
98
96
 
99
97
  // Consumers selectors
100
- const selectConsumersNames = (state: IDescribeRootStateSlice, path: string | undefined) =>
98
+ const selectConsumersNames = (state: IDescribeRootStateSlice, path?: string) =>
101
99
  path
102
100
  ? state.describe.data[path]?.PathDescription?.PersQueueGroup?.PQTabletConfig?.ReadRules
103
101
  : undefined;
@@ -106,9 +104,53 @@ export const selectConsumers: Selector<IDescribeRootStateSlice, IConsumer[], [st
106
104
  createSelector(selectConsumersNames, (names = []) => names.map((name) => ({name})));
107
105
 
108
106
  export function getDescribe({path}: {path: string}) {
107
+ const request = window.api.getDescribe({path});
108
+ return createApiRequest({
109
+ request,
110
+ actions: FETCH_DESCRIBE,
111
+ dataHandler: (data): IDescribeHandledResponse => {
112
+ const dataPath = data.Path;
113
+ const currentDescribe: IDescribeData = {};
114
+ const newData: IDescribeData = {};
115
+
116
+ if (dataPath) {
117
+ currentDescribe[dataPath] = data;
118
+ newData[dataPath] = data;
119
+ }
120
+
121
+ return {
122
+ path: dataPath,
123
+ currentDescribe,
124
+ data: newData,
125
+ };
126
+ },
127
+ });
128
+ }
129
+
130
+ export function getDescribeBatched(paths: string[]) {
131
+ const requestsArray = paths.map((p) => window.api.getDescribe({path: p}));
132
+
133
+ const request = Promise.all(requestsArray);
109
134
  return createApiRequest({
110
- request: window.api.getDescribe({path}),
135
+ request,
111
136
  actions: FETCH_DESCRIBE,
137
+ dataHandler: (data): IDescribeHandledResponse => {
138
+ const currentDescribe: IDescribeData = {};
139
+ const newData: IDescribeData = {};
140
+
141
+ data.forEach((dataItem) => {
142
+ if (dataItem.Path) {
143
+ newData[dataItem.Path] = dataItem;
144
+ currentDescribe[dataItem.Path] = dataItem;
145
+ }
146
+ });
147
+
148
+ return {
149
+ path: data[0].Path,
150
+ currentDescribe,
151
+ data: newData,
152
+ };
153
+ },
112
154
  });
113
155
  }
114
156
 
@@ -9,21 +9,23 @@ import {
9
9
  IHealthcheckInfoState,
10
10
  IHealthcheckInfoRootStateSlice,
11
11
  IIssuesTree,
12
+ IHealthCheckInfoAction,
12
13
  } from '../../types/store/healthcheck';
13
- import {HealthCheckAPIResponse, IssueLog, StatusFlag} from '../../types/api/healthcheck';
14
- import {IResponseError} from '../../types/api/error';
14
+ import {IssueLog, StatusFlag} from '../../types/api/healthcheck';
15
15
 
16
16
  import '../../services/api';
17
- import {createRequestActionTypes, createApiRequest, ApiRequestAction} from '../utils';
17
+ import {createRequestActionTypes, createApiRequest} from '../utils';
18
18
 
19
- const FETCH_HEALTHCHECK = createRequestActionTypes('cluster', 'FETCH_HEALTHCHECK');
19
+ export const FETCH_HEALTHCHECK = createRequestActionTypes('cluster', 'FETCH_HEALTHCHECK');
20
+
21
+ const SET_DATA_WAS_NOT_LOADED = 'healthcheckInfo/SET_DATA_WAS_NOT_LOADED';
20
22
 
21
23
  const initialState = {loading: false, wasLoaded: false};
22
24
 
23
- const healthcheckInfo: Reducer<
24
- IHealthcheckInfoState,
25
- ApiRequestAction<typeof FETCH_HEALTHCHECK, HealthCheckAPIResponse, IResponseError>
26
- > = function (state = initialState, action) {
25
+ const healthcheckInfo: Reducer<IHealthcheckInfoState, IHealthCheckInfoAction> = function (
26
+ state = initialState,
27
+ action,
28
+ ) {
27
29
  switch (action.type) {
28
30
  case FETCH_HEALTHCHECK.REQUEST: {
29
31
  return {
@@ -49,6 +51,13 @@ const healthcheckInfo: Reducer<
49
51
  loading: false,
50
52
  };
51
53
  }
54
+
55
+ case SET_DATA_WAS_NOT_LOADED: {
56
+ return {
57
+ ...state,
58
+ wasLoaded: false,
59
+ };
60
+ }
52
61
  default:
53
62
  return state;
54
63
  }
@@ -127,4 +136,10 @@ export function getHealthcheckInfo(database: string) {
127
136
  });
128
137
  }
129
138
 
139
+ export const setDataWasNotLoaded = () => {
140
+ return {
141
+ type: SET_DATA_WAS_NOT_LOADED,
142
+ } as const;
143
+ };
144
+
130
145
  export default healthcheckInfo;
@@ -6,7 +6,15 @@ const FETCH_ALL_NODES_NETWORK = createRequestActionTypes(
6
6
  'FETCH_ALL_NODES_NETWORK',
7
7
  );
8
8
 
9
- const network = (state = {data: {}, loading: true, wasLoaded: false}, action) => {
9
+ const SET_DATA_WAS_NOT_LOADED = 'network/SET_DATA_WAS_NOT_LOADED';
10
+
11
+ const initialState = {
12
+ data: {},
13
+ loading: false,
14
+ wasLoaded: false,
15
+ };
16
+
17
+ const network = (state = initialState, action) => {
10
18
  switch (action.type) {
11
19
  case FETCH_ALL_NODES_NETWORK.REQUEST: {
12
20
  return {
@@ -30,11 +38,24 @@ const network = (state = {data: {}, loading: true, wasLoaded: false}, action) =>
30
38
  loading: false,
31
39
  };
32
40
  }
41
+
42
+ case SET_DATA_WAS_NOT_LOADED: {
43
+ return {
44
+ ...state,
45
+ wasLoaded: false,
46
+ };
47
+ }
33
48
  default:
34
49
  return state;
35
50
  }
36
51
  };
37
52
 
53
+ export const setDataWasNotLoaded = () => {
54
+ return {
55
+ type: SET_DATA_WAS_NOT_LOADED,
56
+ };
57
+ };
58
+
38
59
  export const getNetworkInfo = (tenant) => {
39
60
  return createApiRequest({
40
61
  request: window.api.getNetwork(tenant),
@@ -6,6 +6,7 @@ const FETCH_NODES = createRequestActionTypes('nodes', 'FETCH_NODES');
6
6
 
7
7
  const CLEAR_NODES = 'nodes/CLEAR_NODES';
8
8
  const SET_NODES_UPTIME_FILTER = 'nodes/SET_NODES_UPTIME_FILTER';
9
+ const SET_DATA_WAS_NOT_LOADED = 'nodes/SET_DATA_WAS_NOT_LOADED';
9
10
 
10
11
  const initialState = {
11
12
  loading: true,
@@ -52,6 +53,12 @@ const nodes = (state = initialState, action) => {
52
53
  case SET_NODES_UPTIME_FILTER: {
53
54
  return {...state, nodesUptimeFilter: action.data};
54
55
  }
56
+ case SET_DATA_WAS_NOT_LOADED: {
57
+ return {
58
+ ...state,
59
+ wasLoaded: false,
60
+ };
61
+ }
55
62
  default:
56
63
  return state;
57
64
  }
@@ -71,6 +78,12 @@ export const setNodesUptimeFilter = (value) => ({
71
78
  data: value,
72
79
  });
73
80
 
81
+ export const setDataWasNotLoaded = () => {
82
+ return {
83
+ type: SET_DATA_WAS_NOT_LOADED,
84
+ };
85
+ };
86
+
74
87
  export const getNodesUptimeFilter = (state) => state.nodes.nodesUptimeFilter;
75
88
 
76
89
  export default nodes;