ydb-embedded-ui 2.5.0 → 2.6.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 +22 -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 +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.6.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v2.5.0...v2.6.0) (2022-12-05)
4
+
5
+
6
+ ### Features
7
+
8
+ * **Describe:** add topic data for CDCStream ([3a289d4](https://github.com/ydb-platform/ydb-embedded-ui/commit/3a289d4f6452e3f2d719c0d508f48b389fd044d7))
9
+ * **Diagnostics:** add consumers tab for CdcStream ([22c6efd](https://github.com/ydb-platform/ydb-embedded-ui/commit/22c6efdd39d85ab1585943bc13d88cf03f9bc2ae))
10
+ * **Overview:** add topic data for CDCStream ([be80545](https://github.com/ydb-platform/ydb-embedded-ui/commit/be80545df65a03820265875fedd98c6f181af491))
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * **Compute:** update data on path change ([1783240](https://github.com/ydb-platform/ydb-embedded-ui/commit/17832403623ae3e718f47aec508c834cd2e3458c))
16
+ * **Diagnostics:** render db tabs for not root dbs ([7d46ce2](https://github.com/ydb-platform/ydb-embedded-ui/commit/7d46ce2783a58b1ae6e41cae6592e78f95d61bcc))
17
+ * **Healthcheck:** render loader on path change ([ec40f19](https://github.com/ydb-platform/ydb-embedded-ui/commit/ec40f19c0b369de0b8d0658b4a1dd68c5c419c1c))
18
+ * **InfoViewer:** allow multiline values ([17755dc](https://github.com/ydb-platform/ydb-embedded-ui/commit/17755dc2eae7b6fc0a56ff70da95679fc590dccb))
19
+ * **Network:** update data on path change ([588c53f](https://github.com/ydb-platform/ydb-embedded-ui/commit/588c53f80a81376301216a77d9ead95cdff9812f))
20
+ * **SchemaTree:** do not expand childless components ([90468de](https://github.com/ydb-platform/ydb-embedded-ui/commit/90468de74b74e00a66255ba042378c9d7e1cbc27))
21
+ * **Storage:** update data on path change ([f5486bc](https://github.com/ydb-platform/ydb-embedded-ui/commit/f5486bcb2838b9e290c566089980533b4d22d035))
22
+ * **Tablets:** fix postponed data update on path change ([d474c6c](https://github.com/ydb-platform/ydb-embedded-ui/commit/d474c6cb36597f0c720ef3bb8d0360ec73973e26))
23
+ * **TopQueries:** update data on path change ([32d7720](https://github.com/ydb-platform/ydb-embedded-ui/commit/32d77208b8ef09682c41160c60a1a7742b0c6c4c))
24
+
3
25
  ## [2.5.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v2.4.4...v2.5.0) (2022-11-25)
4
26
 
5
27
 
@@ -24,10 +24,10 @@
24
24
 
25
25
  &__row {
26
26
  display: flex;
27
- align-items: flex-end;
27
+ align-items: baseline;
28
28
 
29
29
  max-width: 100%;
30
- height: 24px;
30
+ padding-top: 4px;
31
31
  }
32
32
 
33
33
  &__label {
@@ -54,7 +54,7 @@
54
54
  &__value {
55
55
  display: flex;
56
56
 
57
- white-space: nowrap;
57
+ word-break: break-all;
58
58
  }
59
59
 
60
60
  &_size {
@@ -1,31 +1,45 @@
1
1
  import type {TEvDescribeSchemeResult, TCdcStreamDescription} from '../../../types/api/schema';
2
+ import {useTypedSelector} from '../../../utils/hooks';
3
+ import {selectSchemaData} from '../../../store/reducers/schema';
2
4
 
3
- import {formatCdcStreamItem, formatCommonItem} from '../formatters';
5
+ import {formatCdcStreamItem, formatPQGroupItem, formatCommonItem} from '../formatters';
4
6
  import {InfoViewer, InfoViewerItem} from '..';
5
7
 
6
8
  const DISPLAYED_FIELDS: Set<keyof TCdcStreamDescription> = new Set(['Mode', 'Format']);
7
9
 
8
10
  interface CDCStreamInfoProps {
9
11
  data?: TEvDescribeSchemeResult;
12
+ childrenPaths?: string[];
10
13
  }
11
14
 
12
- export const CDCStreamInfo = ({data}: CDCStreamInfoProps) => {
13
- if (!data) {
14
- return <div className="error">No CDC Stream data</div>;
15
+ export const CDCStreamInfo = ({data, childrenPaths}: CDCStreamInfoProps) => {
16
+ const pqGroupData = useTypedSelector((state) => selectSchemaData(state, childrenPaths?.[0]));
17
+
18
+ if (!data || !pqGroupData) {
19
+ return <div className="error">No Changefeed data</div>;
15
20
  }
16
21
 
17
- const TableIndex = data.PathDescription?.CdcStreamDescription;
22
+ const cdcStream = data.PathDescription?.CdcStreamDescription;
23
+ const pqGroup = pqGroupData?.PathDescription?.PersQueueGroup;
24
+
18
25
  const info: Array<InfoViewerItem> = [];
19
26
 
20
- info.push(formatCommonItem('PathType', data.PathDescription?.Self?.PathType));
21
27
  info.push(formatCommonItem('CreateStep', data.PathDescription?.Self?.CreateStep));
22
28
 
23
29
  let key: keyof TCdcStreamDescription;
24
- for (key in TableIndex) {
30
+ for (key in cdcStream) {
25
31
  if (DISPLAYED_FIELDS.has(key)) {
26
- info.push(formatCdcStreamItem(key, TableIndex?.[key]));
32
+ info.push(formatCdcStreamItem(key, cdcStream?.[key]));
27
33
  }
28
34
  }
29
35
 
30
- return <>{info.length ? <InfoViewer info={info}></InfoViewer> : <>Empty</>}</>;
36
+ info.push(formatPQGroupItem('Partitions', pqGroup?.Partitions || []));
37
+ info.push(
38
+ formatPQGroupItem(
39
+ 'PQTabletConfig',
40
+ pqGroup?.PQTabletConfig || {PartitionConfig: {LifetimeSeconds: 0}},
41
+ ),
42
+ );
43
+
44
+ return <InfoViewer title={'Changefeed'} info={info} />;
31
45
  };
@@ -8,7 +8,7 @@ import InternalLink from '../../../components/InternalLink/InternalLink';
8
8
 
9
9
  import routes, {createHref} from '../../../routes';
10
10
  import type {RequiredField} from '../../../types';
11
- import {TPDiskStateInfo, TPDiskState} from '../../../types/api/storage';
11
+ import {TPDiskStateInfo, TPDiskState} from '../../../types/api/pdisk';
12
12
  import {getPDiskId} from '../../../utils';
13
13
  import {getPDiskType} from '../../../utils/pdisk';
14
14
  import {bytesToGB} from '../../../utils/utils';
@@ -96,9 +96,7 @@ function Pdisk(props: PDiskProps) {
96
96
  Realtime &&
97
97
  errorColors.includes(Realtime) &&
98
98
  pdiskData.push({label: 'Realtime', value: Realtime});
99
- Device &&
100
- errorColors.includes(Device) &&
101
- pdiskData.push({label: 'Device', value: Device});
99
+ Device && errorColors.includes(Device) && pdiskData.push({label: 'Device', value: Device});
102
100
  return pdiskData;
103
101
  };
104
102
  /* eslint-enable */
@@ -113,11 +111,7 @@ function Pdisk(props: PDiskProps) {
113
111
  // matches the default offset for popup with arrow out of a sense of beauty
114
112
  offset={[0, 12]}
115
113
  >
116
- <InfoViewer
117
- title="PDisk"
118
- info={preparePdiskData()}
119
- size="s"
120
- />
114
+ <InfoViewer title="PDisk" info={preparePdiskData()} size="s" />
121
115
  </Popup>
122
116
  );
123
117
 
@@ -2,7 +2,7 @@ import {MemoryRouter} from 'react-router-dom';
2
2
 
3
3
  import {renderWithStore} from '../../../../utils/tests/providers';
4
4
 
5
- import {TPDiskState} from '../../../../types/api/storage'
5
+ import {TPDiskState} from '../../../../types/api/pdisk'
6
6
 
7
7
  import PDisk from '../Pdisk'
8
8
 
@@ -25,6 +25,7 @@ import {
25
25
  StorageTypes,
26
26
  setStorageType,
27
27
  setNodesUptimeFilter,
28
+ setDataWasNotLoaded,
28
29
  VisibleEntitiesTitles,
29
30
  getStoragePoolsGroupsCount,
30
31
  getStorageNodesCount,
@@ -71,6 +72,7 @@ class Storage extends React.Component {
71
72
  nodeId: PropTypes.string,
72
73
  nodesUptimeFilter: PropTypes.string,
73
74
  setNodesUptimeFilter: PropTypes.func,
75
+ setDataWasNotLoaded: PropTypes.func,
74
76
  };
75
77
 
76
78
  componentDidMount() {
@@ -106,7 +108,8 @@ class Storage extends React.Component {
106
108
  }
107
109
 
108
110
  componentDidUpdate(prevProps) {
109
- const {visibleEntities, storageType, autorefresh, database} = this.props;
111
+ const {visibleEntities, storageType, autorefresh, database, tenant, setDataWasNotLoaded} =
112
+ this.props;
110
113
 
111
114
  const startFetch = () => {
112
115
  this.getStorageInfo({
@@ -144,6 +147,12 @@ class Storage extends React.Component {
144
147
  restartAutorefresh();
145
148
  }
146
149
  }
150
+
151
+ if (tenant !== prevProps.tenant) {
152
+ setDataWasNotLoaded();
153
+ startFetch();
154
+ restartAutorefresh();
155
+ }
147
156
  }
148
157
 
149
158
  componentWillUnmount() {
@@ -367,6 +376,7 @@ const mapDispatchToProps = {
367
376
  getNodesList,
368
377
  setStorageType,
369
378
  setHeader,
379
+ setDataWasNotLoaded,
370
380
  };
371
381
 
372
382
  export default connect(mapStateToProps, mapDispatchToProps)(Storage);
@@ -9,7 +9,7 @@ import {Stack} from '../../../components/Stack/Stack';
9
9
  //@ts-ignore
10
10
  import EntityStatus from '../../../components/EntityStatus/EntityStatus';
11
11
 
12
- import {TVDiskStateInfo} from '../../../types/api/storage';
12
+ import {TVDiskStateInfo} from '../../../types/api/vdisk';
13
13
  //@ts-ignore
14
14
  import {VisibleEntities} from '../../../store/reducers/storage';
15
15
  //@ts-ignore
@@ -92,7 +92,13 @@ function setSortOrder(visibleEntities: keyof typeof VisibleEntities): SortOrder
92
92
  }
93
93
  }
94
94
 
95
- function StorageGroups({data, tableSettings, visibleEntities, nodes, onShowAll}: StorageGroupsProps) {
95
+ function StorageGroups({
96
+ data,
97
+ tableSettings,
98
+ visibleEntities,
99
+ nodes,
100
+ onShowAll,
101
+ }: StorageGroupsProps) {
96
102
  const allColumns: Column<any>[] = [
97
103
  {
98
104
  name: TableColumnsIds.PoolName,
@@ -123,7 +129,7 @@ function StorageGroups({data, tableSettings, visibleEntities, nodes, onShowAll}:
123
129
  header: tableColumnsNames[TableColumnsIds.Type],
124
130
  render: ({value, row}) => (
125
131
  <>
126
- <Label>{value as string || '—'}</Label>
132
+ <Label>{(value as string) || '—'}</Label>
127
133
  {' '}
128
134
  {row.Encryption && (
129
135
  <Popover
@@ -143,9 +149,8 @@ function StorageGroups({data, tableSettings, visibleEntities, nodes, onShowAll}:
143
149
  name: TableColumnsIds.Missing,
144
150
  header: tableColumnsNames[TableColumnsIds.Missing],
145
151
  width: 100,
146
- render: ({value, row}) => value ? (
147
- <Label theme={getDegradedSeverity(row)}>Degraded: {value}</Label>
148
- ) : '-',
152
+ render: ({value, row}) =>
153
+ value ? <Label theme={getDegradedSeverity(row)}>Degraded: {value}</Label> : '-',
149
154
  align: DataTable.LEFT,
150
155
  defaultOrder: DataTable.DESCENDING,
151
156
  },
@@ -164,10 +169,12 @@ function StorageGroups({data, tableSettings, visibleEntities, nodes, onShowAll}:
164
169
  >
165
170
  {usage}%
166
171
  </Label>
167
- ) : '-';
172
+ ) : (
173
+ '-'
174
+ );
168
175
  },
169
176
  // without a limit exclude usage from sort to display at the bottom
170
- sortAccessor: (row) => row.Limit ? getUsage(row) : null,
177
+ sortAccessor: (row) => (row.Limit ? getUsage(row) : null),
171
178
  align: DataTable.LEFT,
172
179
  },
173
180
  {
@@ -241,35 +248,35 @@ function StorageGroups({data, tableSettings, visibleEntities, nodes, onShowAll}:
241
248
  render: ({value, row}) => (
242
249
  <div className={b('vdisks-wrapper')}>
243
250
  {_.map(value as TVDiskStateInfo[], (el) => {
244
- const donors = Array.isArray(el.Donors) ? el.Donors.filter(isFullDonorData) : [];
251
+ const donors = Array.isArray(el.Donors)
252
+ ? el.Donors.filter(isFullDonorData)
253
+ : [];
245
254
 
246
- return (
247
- donors.length > 0 ? (
248
- <Stack className={b('vdisks-item')} key={stringifyVdiskId(el.VDiskId)}>
249
- <Vdisk
250
- {...el}
251
- PoolName={row[TableColumnsIds.PoolName]}
252
- nodes={nodes}
253
- />
254
- {donors.map((donor) => (
255
- <Vdisk
256
- {...donor}
257
- // donor and acceptor are always in the same group
258
- PoolName={row[TableColumnsIds.PoolName]}
259
- nodes={nodes}
260
- key={stringifyVdiskId(donor.VDiskId)}
261
- />
262
- ))}
263
- </Stack>
264
- ) : (
265
- <div className={b('vdisks-item')} key={stringifyVdiskId(el.VDiskId)}>
255
+ return donors.length > 0 ? (
256
+ <Stack className={b('vdisks-item')} key={stringifyVdiskId(el.VDiskId)}>
257
+ <Vdisk
258
+ {...el}
259
+ PoolName={row[TableColumnsIds.PoolName]}
260
+ nodes={nodes}
261
+ />
262
+ {donors.map((donor) => (
266
263
  <Vdisk
267
- {...el}
264
+ {...donor}
265
+ // donor and acceptor are always in the same group
268
266
  PoolName={row[TableColumnsIds.PoolName]}
269
267
  nodes={nodes}
268
+ key={stringifyVdiskId(donor.VDiskId)}
270
269
  />
271
- </div>
272
- )
270
+ ))}
271
+ </Stack>
272
+ ) : (
273
+ <div className={b('vdisks-item')} key={stringifyVdiskId(el.VDiskId)}>
274
+ <Vdisk
275
+ {...el}
276
+ PoolName={row[TableColumnsIds.PoolName]}
277
+ nodes={nodes}
278
+ />
279
+ </div>
273
280
  );
274
281
  })}
275
282
  </div>
@@ -2,8 +2,6 @@ import React from 'react';
2
2
  import cn from 'bem-cn-lite';
3
3
  import PropTypes from 'prop-types';
4
4
  import {connect} from 'react-redux';
5
- import {withRouter} from 'react-router-dom';
6
- import qs from 'qs';
7
5
 
8
6
  import {Loader} from '@gravity-ui/uikit';
9
7
 
@@ -11,7 +9,7 @@ import NodesViewer from '../../../NodesViewer/NodesViewer';
11
9
 
12
10
  import {backend} from '../../../../store';
13
11
  import {hideTooltip, showTooltip} from '../../../../store/reducers/tooltip';
14
- import {getNodes, clearNodes} from '../../../../store/reducers/nodes';
12
+ import {getNodes, clearNodes, setDataWasNotLoaded} from '../../../../store/reducers/nodes';
15
13
 
16
14
  import './Compute.scss';
17
15
  import {AutoFetcher} from '../../../../utils/autofetcher';
@@ -28,6 +26,7 @@ class Compute extends React.Component {
28
26
  autorefresh: PropTypes.bool,
29
27
  error: PropTypes.object,
30
28
  wasLoaded: PropTypes.bool,
29
+ setDataWasNotLoaded: PropTypes.func,
31
30
  getNodes: PropTypes.func,
32
31
  hideTooltip: PropTypes.func,
33
32
  showTooltip: PropTypes.func,
@@ -50,12 +49,25 @@ class Compute extends React.Component {
50
49
  }
51
50
 
52
51
  componentDidUpdate(prevProps) {
53
- const {autorefresh, getNodes, tenantName} = this.props;
52
+ const {autorefresh, getNodes, setDataWasNotLoaded, tenantName} = this.props;
54
53
 
55
- if (autorefresh && !prevProps.autorefresh) {
56
- getNodes(tenantName);
54
+ const restartAutorefresh = () => {
55
+ this.autofetcher.stop();
57
56
  this.autofetcher.start();
58
57
  this.autofetcher.fetch(() => getNodes(tenantName));
58
+ };
59
+
60
+ if (tenantName !== prevProps.tenantName) {
61
+ setDataWasNotLoaded();
62
+ getNodes(tenantName);
63
+ if (autorefresh) {
64
+ restartAutorefresh();
65
+ }
66
+ }
67
+
68
+ if (autorefresh && !prevProps.autorefresh) {
69
+ getNodes(tenantName);
70
+ restartAutorefresh();
59
71
  }
60
72
  if (!autorefresh && prevProps.autorefresh) {
61
73
  this.autofetcher.stop();
@@ -103,15 +115,10 @@ class Compute extends React.Component {
103
115
  function mapStateToProps(state, ownProps) {
104
116
  const {data, loading, wasLoaded, error} = state.nodes;
105
117
  const {autorefresh} = state.schema;
106
- const {search} = ownProps.location;
107
- const queryParams = qs.parse(search, {
108
- ignoreQueryPrefix: true,
109
- });
110
- const {name: tenantName} = queryParams;
111
118
  const nodes = (data && data.Tenants && data.Tenants[0] && data.Tenants[0].Nodes) || [];
112
119
  return {
120
+ ...ownProps,
113
121
  nodes,
114
- tenantName,
115
122
  loading,
116
123
  wasLoaded,
117
124
  error,
@@ -124,8 +131,9 @@ const mapDispatchToProps = {
124
131
  clearNodes,
125
132
  hideTooltip,
126
133
  showTooltip,
134
+ setDataWasNotLoaded,
127
135
  };
128
136
 
129
137
  const ConnectedCompute = connect(mapStateToProps, mapDispatchToProps)(Compute);
130
138
 
131
- export default withRouter(ConnectedCompute);
139
+ export default ConnectedCompute;
@@ -4,7 +4,8 @@ import block from 'bem-cn-lite';
4
4
 
5
5
  import DataTable, {Column} from '@yandex-cloud/react-data-table';
6
6
 
7
- import { Loader } from '../../../../components/Loader';
7
+ import type {EPathType} from '../../../../types/api/schema';
8
+ import {Loader} from '../../../../components/Loader';
8
9
  import {prepareQueryError} from '../../../../utils/query';
9
10
  import {DEFAULT_TABLE_SETTINGS} from '../../../../utils/constants';
10
11
  import {useAutofetcher, useTypedSelector} from '../../../../utils/hooks';
@@ -15,6 +16,9 @@ import {
15
16
  setCurrentDescribePath,
16
17
  setDataWasNotLoaded,
17
18
  } from '../../../../store/reducers/describe';
19
+ import {selectSchemaMergedChildrenPaths} from '../../../../store/reducers/schema';
20
+
21
+ import {isCdcStreamEntityType} from '../../utils/schema';
18
22
 
19
23
  import i18n from './i18n';
20
24
 
@@ -24,21 +28,33 @@ const b = block('ydb-consumers');
24
28
 
25
29
  interface ConsumersProps {
26
30
  path: string;
31
+ type?: EPathType;
27
32
  }
28
33
 
29
- export const Consumers = ({path}: ConsumersProps) => {
34
+ export const Consumers = ({path, type}: ConsumersProps) => {
30
35
  const dispatch = useDispatch();
31
36
 
37
+ const isCdcStream = isCdcStreamEntityType(type);
38
+
39
+ const mergedChildrenPaths = useTypedSelector((state) =>
40
+ selectSchemaMergedChildrenPaths(state, path, type),
41
+ );
42
+
43
+ const dataPath = isCdcStream ? mergedChildrenPaths?.[0] : path;
44
+
32
45
  const fetchData = useCallback(
33
46
  (isBackground: boolean) => {
34
47
  if (!isBackground) {
35
48
  dispatch(setDataWasNotLoaded());
36
49
  }
37
- dispatch(setCurrentDescribePath(path));
38
- dispatch(getDescribe({path}));
50
+
51
+ if (dataPath) {
52
+ dispatch(setCurrentDescribePath(dataPath));
53
+ dispatch(getDescribe({path: dataPath}));
54
+ }
39
55
  },
40
56
 
41
- [path, dispatch],
57
+ [dispatch, dataPath],
42
58
  );
43
59
 
44
60
  const {autorefresh} = useTypedSelector((state) => state.schema);
@@ -46,7 +62,7 @@ export const Consumers = ({path}: ConsumersProps) => {
46
62
  useAutofetcher(fetchData, [fetchData], autorefresh);
47
63
 
48
64
  const {loading, wasLoaded, error} = useTypedSelector((state) => state.describe);
49
- const consumers = useTypedSelector((state) => selectConsumers(state, path));
65
+ const consumers = useTypedSelector((state) => selectConsumers(state, dataPath));
50
66
 
51
67
  const [consumersToRender, setConsumersToRender] = useState(consumers);
52
68
 
@@ -1,5 +1,5 @@
1
- import {useCallback} from 'react';
2
- import {useDispatch} from 'react-redux';
1
+ import {useCallback, useEffect, useState} from 'react';
2
+ import {shallowEqual, useDispatch} from 'react-redux';
3
3
  import cn from 'bem-cn-lite';
4
4
  // @ts-ignore
5
5
  import JSONTree from 'react-json-inspector';
@@ -13,7 +13,12 @@ import {
13
13
  getDescribe,
14
14
  setDataWasNotLoaded,
15
15
  setCurrentDescribePath,
16
+ getDescribeBatched,
16
17
  } from '../../../../store/reducers/describe';
18
+ import {selectSchemaMergedChildrenPaths} from '../../../../store/reducers/schema';
19
+ import type {EPathType} from '../../../../types/api/schema';
20
+
21
+ import {isEntityWithMergedImplementation} from '../../utils/schema';
17
22
 
18
23
  import './Describe.scss';
19
24
 
@@ -23,17 +28,39 @@ const expandMap = new Map();
23
28
 
24
29
  interface IDescribeProps {
25
30
  tenant: string;
31
+ type?: EPathType;
26
32
  }
27
33
 
28
- const Describe = ({tenant}: IDescribeProps) => {
34
+ const Describe = ({tenant, type}: IDescribeProps) => {
29
35
  const dispatch = useDispatch();
30
36
 
31
37
  const {currentDescribe, error, loading, wasLoaded} = useTypedSelector(
32
38
  (state) => state.describe,
33
39
  );
34
40
 
41
+ const [preparedDescribeData, setPreparedDescribeData] = useState<Object>();
42
+
43
+ useEffect(() => {
44
+ if (currentDescribe) {
45
+ const paths = Object.keys(currentDescribe);
46
+
47
+ if (paths.length === 1) {
48
+ setPreparedDescribeData(currentDescribe[paths[0]]);
49
+ } else {
50
+ setPreparedDescribeData(currentDescribe);
51
+ }
52
+ }
53
+ }, [currentDescribe]);
54
+
35
55
  const {autorefresh, currentSchemaPath} = useTypedSelector((state) => state.schema);
36
56
 
57
+ const isEntityWithMergedImpl = isEntityWithMergedImplementation(type);
58
+
59
+ const mergedChildrenPaths = useTypedSelector(
60
+ (state) => selectSchemaMergedChildrenPaths(state, currentSchemaPath, type),
61
+ shallowEqual,
62
+ );
63
+
37
64
  const fetchData = useCallback(
38
65
  (isBackground: boolean) => {
39
66
  if (!isBackground) {
@@ -41,16 +68,20 @@ const Describe = ({tenant}: IDescribeProps) => {
41
68
  }
42
69
 
43
70
  const path = currentSchemaPath || tenant;
44
-
45
71
  dispatch(setCurrentDescribePath(path));
46
- dispatch(getDescribe({path}));
72
+
73
+ if (!isEntityWithMergedImpl) {
74
+ dispatch(getDescribe({path}));
75
+ } else if (mergedChildrenPaths) {
76
+ dispatch(getDescribeBatched([path, ...mergedChildrenPaths]));
77
+ }
47
78
  },
48
- [currentSchemaPath, tenant, dispatch],
79
+ [currentSchemaPath, tenant, mergedChildrenPaths, isEntityWithMergedImpl, dispatch],
49
80
  );
50
81
 
51
82
  useAutofetcher(fetchData, [fetchData], autorefresh);
52
83
 
53
- if (loading && !wasLoaded) {
84
+ if ((loading && !wasLoaded) || (isEntityWithMergedImpl && !mergedChildrenPaths)) {
54
85
  return <Loader size="m" />;
55
86
  }
56
87
 
@@ -58,7 +89,7 @@ const Describe = ({tenant}: IDescribeProps) => {
58
89
  return <div className={b('message-container', 'error')}>{prepareQueryError(error)}</div>;
59
90
  }
60
91
 
61
- if (!loading && !currentDescribe) {
92
+ if (!loading && !preparedDescribeData) {
62
93
  return <div className={b('message-container')}>Empty</div>;
63
94
  }
64
95
 
@@ -66,7 +97,7 @@ const Describe = ({tenant}: IDescribeProps) => {
66
97
  <div className={b()}>
67
98
  <div className={b('result')}>
68
99
  <JSONTree
69
- data={currentDescribe}
100
+ data={preparedDescribeData}
70
101
  className={b('tree')}
71
102
  onClick={({path}: {path: string}) => {
72
103
  const newValue = !(expandMap.get(path) || false);
@@ -38,6 +38,7 @@ import {GeneralPagesIds, DATABASE_PAGES, getPagesByType} from './DiagnosticsPage
38
38
  //@ts-ignore
39
39
  import {enableAutorefresh, disableAutorefresh} from '../../../store/reducers/schema';
40
40
  import {setTopLevelTab, setDiagnosticsTab} from '../../../store/reducers/tenant';
41
+ import {isDatabaseEntityType} from '../utils/schema';
41
42
 
42
43
  import './Diagnostics.scss';
43
44
 
@@ -66,17 +67,17 @@ function Diagnostics(props: DiagnosticsProps) {
66
67
  ignoreQueryPrefix: true,
67
68
  });
68
69
 
69
- const {name: tenantName} = queryParams;
70
-
71
- const isRoot = currentSchemaPath === tenantName;
70
+ const {name: rootTenantName} = queryParams;
71
+ const tenantName = isDatabaseEntityType(props.type) ? currentSchemaPath : rootTenantName;
72
+ const isDatabase = isDatabaseEntityType(props.type) || currentSchemaPath === rootTenantName;
72
73
 
73
74
  const pages = useMemo(() => {
74
- if (isRoot) {
75
+ if (isDatabase) {
75
76
  return DATABASE_PAGES;
76
77
  }
77
78
 
78
79
  return getPagesByType(props.type);
79
- }, [props.type, isRoot]);
80
+ }, [props.type, isDatabase]);
80
81
 
81
82
  const forwardToDiagnosticTab = (tab: GeneralPagesIds) => {
82
83
  dispatch(setDiagnosticsTab(tab));
@@ -134,10 +135,15 @@ function Diagnostics(props: DiagnosticsProps) {
134
135
  return <TopShards path={tenantNameString} type={type} />;
135
136
  }
136
137
  case GeneralPagesIds.nodes: {
137
- return <Compute additionalNodesInfo={props.additionalNodesInfo} />;
138
+ return (
139
+ <Compute
140
+ tenantName={tenantNameString}
141
+ additionalNodesInfo={props.additionalNodesInfo}
142
+ />
143
+ );
138
144
  }
139
145
  case GeneralPagesIds.tablets: {
140
- return <Tablets path={currentItem.Path} />;
146
+ return <Tablets path={currentSchemaPath} />;
141
147
  }
142
148
  case GeneralPagesIds.storage: {
143
149
  return <Storage tenant={tenantNameString} database={true} />;
@@ -146,7 +152,7 @@ function Diagnostics(props: DiagnosticsProps) {
146
152
  return <Network path={tenantNameString} />;
147
153
  }
148
154
  case GeneralPagesIds.describe: {
149
- return <Describe tenant={tenantNameString} />;
155
+ return <Describe tenant={tenantNameString} type={type} />;
150
156
  }
151
157
  case GeneralPagesIds.hotKeys: {
152
158
  return <HotKeys type={type} />;
@@ -155,7 +161,7 @@ function Diagnostics(props: DiagnosticsProps) {
155
161
  return <Heatmap path={currentItem.Path} />;
156
162
  }
157
163
  case GeneralPagesIds.consumers: {
158
- return <Consumers path={currentSchemaPath} />;
164
+ return <Consumers path={currentSchemaPath} type={type} />;
159
165
  }
160
166
  default: {
161
167
  return <div>No data...</div>;
@@ -87,7 +87,7 @@ export const TABLE_PAGES = [overview, topShards, graph, tablets, hotKeys, descri
87
87
 
88
88
  export const DIR_PAGES = [overview, topShards, describe];
89
89
 
90
- export const CDC_STREAM_PAGES = [overview, describe];
90
+ export const CDC_STREAM_PAGES = [overview, consumers, describe];
91
91
  export const TOPIC_PAGES = [overview, consumers, describe];
92
92
 
93
93
  // verbose mapping to guarantee correct tabs for new path types