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
package/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.0.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v2.6.0...v3.0.0) (2022-12-05)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ Updated build config ([11e02c6](https://github.com/ydb-platform/ydb-embedded-ui/commit/11e02c668ef186f058b2ece9d5f1082d0e96e23d))
9
+
10
+ **Before the change**
11
+ - the target dir for the production build was `build/resources`
12
+ - `favicon.png` was placed directly in `build`
13
+
14
+ **After the change**
15
+ - the target dir is `build/static`
16
+ - `favicon.png` is in `build/static`
17
+
18
+ This change is intended to simplify build config and make it closer to the default one. Previously there were some custom tweaks after the build, they caused bugs and were hard to maintain. Now the application builds using the default `create-react-app` config.
19
+
20
+
21
+ ## [2.6.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v2.5.0...v2.6.0) (2022-12-05)
22
+
23
+
24
+ ### Features
25
+
26
+ * **Describe:** add topic data for CDCStream ([3a289d4](https://github.com/ydb-platform/ydb-embedded-ui/commit/3a289d4f6452e3f2d719c0d508f48b389fd044d7))
27
+ * **Diagnostics:** add consumers tab for CdcStream ([22c6efd](https://github.com/ydb-platform/ydb-embedded-ui/commit/22c6efdd39d85ab1585943bc13d88cf03f9bc2ae))
28
+ * **Overview:** add topic data for CDCStream ([be80545](https://github.com/ydb-platform/ydb-embedded-ui/commit/be80545df65a03820265875fedd98c6f181af491))
29
+
30
+
31
+ ### Bug Fixes
32
+
33
+ * **Compute:** update data on path change ([1783240](https://github.com/ydb-platform/ydb-embedded-ui/commit/17832403623ae3e718f47aec508c834cd2e3458c))
34
+ * **Diagnostics:** render db tabs for not root dbs ([7d46ce2](https://github.com/ydb-platform/ydb-embedded-ui/commit/7d46ce2783a58b1ae6e41cae6592e78f95d61bcc))
35
+ * **Healthcheck:** render loader on path change ([ec40f19](https://github.com/ydb-platform/ydb-embedded-ui/commit/ec40f19c0b369de0b8d0658b4a1dd68c5c419c1c))
36
+ * **InfoViewer:** allow multiline values ([17755dc](https://github.com/ydb-platform/ydb-embedded-ui/commit/17755dc2eae7b6fc0a56ff70da95679fc590dccb))
37
+ * **Network:** update data on path change ([588c53f](https://github.com/ydb-platform/ydb-embedded-ui/commit/588c53f80a81376301216a77d9ead95cdff9812f))
38
+ * **SchemaTree:** do not expand childless components ([90468de](https://github.com/ydb-platform/ydb-embedded-ui/commit/90468de74b74e00a66255ba042378c9d7e1cbc27))
39
+ * **Storage:** update data on path change ([f5486bc](https://github.com/ydb-platform/ydb-embedded-ui/commit/f5486bcb2838b9e290c566089980533b4d22d035))
40
+ * **Tablets:** fix postponed data update on path change ([d474c6c](https://github.com/ydb-platform/ydb-embedded-ui/commit/d474c6cb36597f0c720ef3bb8d0360ec73973e26))
41
+ * **TopQueries:** update data on path change ([32d7720](https://github.com/ydb-platform/ydb-embedded-ui/commit/32d77208b8ef09682c41160c60a1a7742b0c6c4c))
42
+
3
43
  ## [2.5.0](https://github.com/ydb-platform/ydb-embedded-ui/compare/v2.4.4...v2.5.0) (2022-11-25)
4
44
 
5
45
 
@@ -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