ydb-embedded-ui 3.2.3 → 3.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/components/InfoViewer/InfoViewer.scss +10 -0
  3. package/dist/components/InfoViewer/InfoViewer.tsx +12 -2
  4. package/dist/components/InfoViewer/formatters/cdcStream.ts +10 -0
  5. package/dist/components/InfoViewer/formatters/index.ts +3 -0
  6. package/dist/components/InfoViewer/formatters/pqGroup.ts +51 -0
  7. package/dist/components/InfoViewer/formatters/schema.ts +1 -29
  8. package/dist/components/InfoViewer/formatters/topicStats.tsx +50 -0
  9. package/dist/components/InfoViewer/schemaInfo/index.ts +0 -2
  10. package/dist/components/InfoViewer/utils.ts +15 -0
  11. package/dist/components/InternalLink/InternalLink.tsx +17 -0
  12. package/dist/components/InternalLink/index.ts +1 -0
  13. package/dist/components/Tablet/Tablet.js +1 -1
  14. package/dist/components/TabletsStatistic/TabletsStatistic.tsx +1 -1
  15. package/dist/components/VerticalBars/VerticalBars.scss +15 -0
  16. package/dist/components/VerticalBars/VerticalBars.tsx +38 -0
  17. package/dist/components/VerticalBars/index.ts +1 -0
  18. package/dist/containers/App/App.js +0 -11
  19. package/dist/containers/App/App.scss +0 -1
  20. package/dist/containers/Cluster/Cluster.tsx +2 -2
  21. package/dist/containers/Nodes/Nodes.scss +5 -1
  22. package/dist/containers/Nodes/Nodes.tsx +196 -0
  23. package/dist/containers/{App → Nodes}/NodesTable.scss +2 -2
  24. package/dist/{utils/getNodesColumns.js → containers/Nodes/getNodesColumns.tsx} +60 -35
  25. package/dist/containers/Nodes/i18n/en.json +3 -0
  26. package/dist/containers/Nodes/i18n/index.ts +11 -0
  27. package/dist/containers/Nodes/i18n/ru.json +3 -0
  28. package/dist/containers/Nodes/index.ts +1 -0
  29. package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.scss +14 -20
  30. package/dist/containers/Storage/DiskStateProgressBar/DiskStateProgressBar.tsx +32 -16
  31. package/dist/containers/Storage/DiskStateProgressBar/index.ts +1 -0
  32. package/dist/containers/Storage/{Pdisk/Pdisk.scss → PDisk/PDisk.scss} +15 -4
  33. package/dist/containers/Storage/PDisk/PDisk.tsx +145 -0
  34. package/dist/containers/Storage/PDisk/__tests__/colors.tsx +37 -0
  35. package/dist/containers/Storage/PDisk/index.ts +1 -0
  36. package/dist/containers/Storage/PDiskPopup/PDiskPopup.scss +3 -0
  37. package/dist/containers/Storage/PDiskPopup/PDiskPopup.tsx +82 -0
  38. package/dist/containers/Storage/PDiskPopup/index.ts +1 -0
  39. package/dist/containers/Storage/Storage.js +1 -1
  40. package/dist/containers/Storage/StorageGroups/StorageGroups.tsx +10 -10
  41. package/dist/containers/Storage/StorageNodes/StorageNodes.scss +1 -0
  42. package/dist/containers/Storage/StorageNodes/StorageNodes.tsx +7 -4
  43. package/dist/containers/Storage/VDisk/VDisk.scss +7 -0
  44. package/dist/containers/Storage/VDisk/VDisk.tsx +148 -0
  45. package/dist/containers/Storage/VDisk/__tests__/colors.tsx +209 -0
  46. package/dist/containers/Storage/VDisk/index.ts +1 -0
  47. package/dist/containers/Storage/VDiskPopup/VDiskPopup.scss +14 -0
  48. package/dist/containers/Storage/VDiskPopup/VDiskPopup.tsx +134 -0
  49. package/dist/containers/Storage/VDiskPopup/index.ts +1 -0
  50. package/dist/containers/Storage/utils/constants.ts +2 -9
  51. package/dist/containers/TabletsFilters/TabletsFilters.js +10 -6
  52. package/dist/containers/Tenant/Diagnostics/Diagnostics.scss +2 -2
  53. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +3 -4
  54. package/dist/containers/Tenant/Diagnostics/OverloadedShards/OverloadedShards.tsx +1 -1
  55. package/dist/containers/Tenant/Diagnostics/Overview/ChangefeedInfo/ChangefeedInfo.tsx +69 -0
  56. package/dist/containers/Tenant/Diagnostics/Overview/ChangefeedInfo/index.ts +1 -0
  57. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +18 -16
  58. package/dist/containers/Tenant/Diagnostics/Overview/TopicInfo/TopicInfo.tsx +37 -0
  59. package/dist/containers/Tenant/Diagnostics/Overview/TopicInfo/index.ts +1 -0
  60. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/TopicStats.scss +30 -0
  61. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/TopicStats.tsx +94 -0
  62. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/i18n/en.json +3 -0
  63. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/i18n/index.ts +11 -0
  64. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/i18n/ru.json +3 -0
  65. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/index.ts +1 -0
  66. package/dist/containers/Tenant/Diagnostics/Overview/utils/index.ts +1 -0
  67. package/dist/containers/Tenant/Diagnostics/Overview/utils/prepareTopicSchemaInfo.ts +42 -0
  68. package/dist/containers/Tenant/utils/schema.ts +19 -0
  69. package/dist/containers/UserSettings/UserSettings.scss +9 -0
  70. package/dist/containers/UserSettings/UserSettings.tsx +41 -11
  71. package/dist/services/api.d.ts +8 -1
  72. package/dist/services/api.js +27 -8
  73. package/dist/store/reducers/index.ts +3 -1
  74. package/dist/store/reducers/nodes.ts +148 -14
  75. package/dist/store/reducers/{clusterNodes.js → nodesList.js} +0 -41
  76. package/dist/store/reducers/settings.js +10 -4
  77. package/dist/store/reducers/storage.js +24 -13
  78. package/dist/store/reducers/tenant.js +5 -4
  79. package/dist/store/reducers/tooltip.ts +1 -1
  80. package/dist/store/reducers/topic.ts +52 -0
  81. package/dist/styles/mixins.scss +19 -11
  82. package/dist/types/api/common.ts +5 -0
  83. package/dist/types/api/compute.ts +1 -1
  84. package/dist/types/api/consumer.ts +12 -10
  85. package/dist/types/api/nodes.ts +2 -0
  86. package/dist/types/api/pdisk.ts +1 -0
  87. package/dist/types/api/schema.ts +3 -3
  88. package/dist/types/api/topic.ts +5 -4
  89. package/dist/types/api/vdisk.ts +2 -1
  90. package/dist/types/store/nodes.ts +56 -6
  91. package/dist/types/store/tooltip.ts +1 -1
  92. package/dist/types/store/topic.ts +21 -0
  93. package/dist/utils/constants.ts +5 -1
  94. package/dist/utils/i18n/i18n.ts +10 -2
  95. package/dist/utils/index.js +1 -1
  96. package/dist/utils/timeParsers/__test__/formatDuration.test.ts +50 -0
  97. package/dist/utils/timeParsers/__test__/protobuf.test.ts +74 -0
  98. package/dist/utils/timeParsers/formatDuration.ts +46 -0
  99. package/dist/utils/timeParsers/i18n/en.json +7 -0
  100. package/dist/utils/timeParsers/i18n/index.ts +11 -0
  101. package/dist/utils/timeParsers/i18n/ru.json +7 -0
  102. package/dist/utils/timeParsers/index.ts +2 -0
  103. package/dist/utils/timeParsers/protobuf.ts +36 -0
  104. package/package.json +1 -1
  105. package/dist/components/InfoViewer/schemaInfo/CDCStreamInfo.tsx +0 -48
  106. package/dist/components/InfoViewer/schemaInfo/PersQueueGroupInfo.tsx +0 -30
  107. package/dist/components/InternalLink/InternalLink.js +0 -23
  108. package/dist/containers/Nodes/Nodes.js +0 -214
  109. package/dist/containers/NodesViewer/NodesViewer.js +0 -163
  110. package/dist/containers/NodesViewer/NodesViewer.scss +0 -66
  111. package/dist/containers/Storage/Pdisk/Pdisk.tsx +0 -153
  112. package/dist/containers/Storage/Pdisk/__tests__/colors.tsx +0 -41
  113. package/dist/containers/Storage/Vdisk/Vdisk.js +0 -275
  114. package/dist/containers/Storage/Vdisk/Vdisk.scss +0 -22
  115. package/dist/containers/Storage/Vdisk/__tests__/colors.tsx +0 -163
  116. package/dist/containers/Tenant/Diagnostics/Compute/Compute.js +0 -139
  117. package/dist/containers/Tenant/Diagnostics/Compute/Compute.scss +0 -14
@@ -23,8 +23,7 @@ import Describe from './Describe/Describe';
23
23
  import HotKeys from './HotKeys/HotKeys';
24
24
  //@ts-ignore
25
25
  import {Heatmap} from '../../Heatmap';
26
- //@ts-ignore
27
- import Compute from './Compute/Compute';
26
+ import {Nodes} from '../../Nodes';
28
27
  //@ts-ignore
29
28
  import {Tablets} from '../../Tablets';
30
29
  import {Consumers} from './Consumers';
@@ -130,8 +129,8 @@ function Diagnostics(props: DiagnosticsProps) {
130
129
  }
131
130
  case GeneralPagesIds.nodes: {
132
131
  return (
133
- <Compute
134
- tenantName={tenantNameString}
132
+ <Nodes
133
+ tenantPath={tenantNameString}
135
134
  additionalNodesInfo={props.additionalNodesInfo}
136
135
  />
137
136
  );
@@ -6,7 +6,7 @@ import DataTable, {Column, Settings, SortOrder} from '@yandex-cloud/react-data-t
6
6
  import {Loader} from '@gravity-ui/uikit';
7
7
 
8
8
  import {DateRange, DateRangeValues} from '../../../../components/DateRange';
9
- import InternalLink from '../../../../components/InternalLink/InternalLink';
9
+ import {InternalLink} from '../../../../components/InternalLink';
10
10
 
11
11
  import HistoryContext from '../../../../contexts/HistoryContext';
12
12
 
@@ -0,0 +1,69 @@
1
+ import type {TEvDescribeSchemeResult} from '../../../../../types/api/schema';
2
+
3
+ import {formatObject, InfoViewer, InfoViewerItem} from '../../../../../components/InfoViewer';
4
+ import {
5
+ formatCdcStreamItem,
6
+ formatCommonItem,
7
+ } from '../../../../../components/InfoViewer/formatters';
8
+
9
+ import {useTypedSelector} from '../../../../../utils/hooks';
10
+ import {selectSchemaData} from '../../../../../store/reducers/schema';
11
+
12
+ import {getEntityName} from '../../../utils';
13
+
14
+ import {TopicStats} from '../TopicStats';
15
+
16
+ import {prepareTopicSchemaInfo} from '../utils';
17
+
18
+ const prepareChangefeedInfo = (
19
+ changefeedData?: TEvDescribeSchemeResult,
20
+ topicData?: TEvDescribeSchemeResult,
21
+ ): Array<InfoViewerItem> => {
22
+ if (!changefeedData && !topicData) {
23
+ return [];
24
+ }
25
+
26
+ const streamDescription = changefeedData?.PathDescription?.CdcStreamDescription;
27
+
28
+ const {Mode, Format} = streamDescription || {};
29
+
30
+ const created = formatCommonItem(
31
+ 'CreateStep',
32
+ changefeedData?.PathDescription?.Self?.CreateStep,
33
+ );
34
+ const changefeedInfo = formatObject(formatCdcStreamItem, {
35
+ Mode,
36
+ Format,
37
+ });
38
+ const topicInfo = prepareTopicSchemaInfo(topicData);
39
+
40
+ return [created, ...changefeedInfo, ...topicInfo];
41
+ };
42
+
43
+ interface ChangefeedProps {
44
+ data?: TEvDescribeSchemeResult;
45
+ childrenPaths?: string[];
46
+ }
47
+
48
+ /** Displays overview for CDCStream EPathType */
49
+ export const ChangefeedInfo = ({data, childrenPaths}: ChangefeedProps) => {
50
+ const entityName = getEntityName(data?.PathDescription);
51
+
52
+ const {error: schemaError} = useTypedSelector((state) => state.schema);
53
+ const pqGroupData = useTypedSelector((state) => selectSchemaData(state, childrenPaths?.[0]));
54
+
55
+ if (schemaError) {
56
+ return <div className="error">{schemaError.statusText}</div>;
57
+ }
58
+
59
+ if (!data || !pqGroupData) {
60
+ return <div className="error">No {entityName} data</div>;
61
+ }
62
+
63
+ return (
64
+ <div>
65
+ <InfoViewer title={entityName} info={prepareChangefeedInfo(data, pqGroupData)} />
66
+ <TopicStats />
67
+ </div>
68
+ );
69
+ };
@@ -0,0 +1 @@
1
+ export * from './ChangefeedInfo';
@@ -6,20 +6,17 @@ import {Loader} from '@gravity-ui/uikit';
6
6
 
7
7
  //@ts-ignore
8
8
  import SchemaInfoViewer from '../../Schema/SchemaInfoViewer/SchemaInfoViewer';
9
- import {
10
- CDCStreamInfo,
11
- TableIndexInfo,
12
- PersQueueGroupInfo,
13
- } from '../../../../components/InfoViewer/schemaInfo';
9
+ import {TableIndexInfo} from '../../../../components/InfoViewer/schemaInfo';
14
10
 
15
- import {
16
- EPathType,
17
- TEvDescribeSchemeResult,
18
- } from '../../../../types/api/schema';
11
+ import {TopicInfo} from './TopicInfo';
12
+ import {ChangefeedInfo} from './ChangefeedInfo';
13
+
14
+ import {EPathType, TEvDescribeSchemeResult} from '../../../../types/api/schema';
19
15
  import {
20
16
  isEntityWithMergedImplementation,
21
17
  isColumnEntityType,
22
18
  isTableType,
19
+ isPathTypeWithTopic,
23
20
  } from '../../utils/schema';
24
21
  //@ts-ignore
25
22
  import {
@@ -28,6 +25,7 @@ import {
28
25
  resetLoadingState,
29
26
  selectSchemaMergedChildrenPaths,
30
27
  } from '../../../../store/reducers/schema';
28
+ import {getTopic} from '../../../../store/reducers/topic';
31
29
  //@ts-ignore
32
30
  import {
33
31
  getOlapStats,
@@ -122,6 +120,10 @@ function Overview({type, tenantName, className}: OverviewProps) {
122
120
  }
123
121
  dispatch(getOlapStats({path: schemaPath}));
124
122
  }
123
+
124
+ if (isPathTypeWithTopic(type)) {
125
+ dispatch(getTopic(currentSchemaPath));
126
+ }
125
127
  },
126
128
  [
127
129
  tenantName,
@@ -163,9 +165,9 @@ function Overview({type, tenantName, className}: OverviewProps) {
163
165
  [EPathType.EPathTypeColumnStore]: undefined,
164
166
  [EPathType.EPathTypeColumnTable]: undefined,
165
167
  [EPathType.EPathTypeCdcStream]: () => (
166
- <CDCStreamInfo data={schemaData} childrenPaths={mergedChildrenPaths} />
168
+ <ChangefeedInfo data={schemaData} childrenPaths={mergedChildrenPaths} />
167
169
  ),
168
- [EPathType.EPathTypePersQueueGroup]: () => <PersQueueGroupInfo data={schemaData} />,
170
+ [EPathType.EPathTypePersQueueGroup]: () => <TopicInfo data={schemaData} />,
169
171
  };
170
172
 
171
173
  return (
@@ -175,11 +177,11 @@ function Overview({type, tenantName, className}: OverviewProps) {
175
177
  );
176
178
  };
177
179
 
178
- return (loading && !wasLoaded) || (isEntityWithMergedImpl && !mergedChildrenPaths) ? (
179
- renderLoader()
180
- ) : (
181
- <div className={className}>{renderContent()}</div>
182
- );
180
+ if ((loading && !wasLoaded) || (isEntityWithMergedImpl && !mergedChildrenPaths)) {
181
+ return renderLoader();
182
+ }
183
+
184
+ return <div className={className}>{renderContent()}</div>;
183
185
  }
184
186
 
185
187
  export default Overview;
@@ -0,0 +1,37 @@
1
+ import type {TEvDescribeSchemeResult} from '../../../../../types/api/schema';
2
+
3
+ import {InfoViewer} from '../../../../../components/InfoViewer';
4
+
5
+ import {useTypedSelector} from '../../../../../utils/hooks';
6
+
7
+ import {getEntityName} from '../../../utils';
8
+
9
+ import {prepareTopicSchemaInfo} from '../utils';
10
+
11
+ import {TopicStats} from '../TopicStats';
12
+
13
+ interface TopicInfoProps {
14
+ data?: TEvDescribeSchemeResult;
15
+ }
16
+
17
+ /** Displays overview for PersQueueGroup EPathType */
18
+ export const TopicInfo = ({data}: TopicInfoProps) => {
19
+ const entityName = getEntityName(data?.PathDescription);
20
+
21
+ const {error: schemaError} = useTypedSelector((state) => state.schema);
22
+
23
+ if (schemaError) {
24
+ return <div className="error">{schemaError.statusText}</div>;
25
+ }
26
+
27
+ if (!data) {
28
+ return <div className="error">No {entityName} data</div>;
29
+ }
30
+
31
+ return (
32
+ <div>
33
+ <InfoViewer title={entityName} info={prepareTopicSchemaInfo(data)} />
34
+ <TopicStats />
35
+ </div>
36
+ );
37
+ };
@@ -0,0 +1 @@
1
+ export * from './TopicInfo';
@@ -0,0 +1,30 @@
1
+ @import '../../../../../styles/mixins.scss';
2
+
3
+ .ydb-overview-topic-stats {
4
+ &__title {
5
+ @include info-viewer-title();
6
+ }
7
+
8
+ .ydb-loader {
9
+ margin-top: 50px;
10
+ }
11
+
12
+ .ydb-bars {
13
+ margin-top: -5px;
14
+ }
15
+
16
+ &__info {
17
+ .info-viewer__label-text_multiline {
18
+ max-width: 150px;
19
+ }
20
+ }
21
+
22
+ &__bytes-written {
23
+ margin-top: 7px;
24
+ padding-left: 20px;
25
+
26
+ .info-viewer__label {
27
+ min-width: 180px;
28
+ }
29
+ }
30
+ }
@@ -0,0 +1,94 @@
1
+ import cn from 'bem-cn-lite';
2
+ import {isEmpty} from 'lodash/fp';
3
+
4
+ import type {DescribeTopicResult} from '../../../../../types/api/topic';
5
+
6
+ import {Loader} from '../../../../../components/Loader';
7
+ import {InfoViewerItem, formatObject, InfoViewer} from '../../../../../components/InfoViewer';
8
+
9
+ import {
10
+ prepareBytesWritten,
11
+ formatTopicStats,
12
+ } from '../../../../../components/InfoViewer/formatters';
13
+
14
+ import {useTypedSelector} from '../../../../../utils/hooks';
15
+ import {formatBps} from '../../../../../utils';
16
+
17
+ import i18n from './i18n';
18
+
19
+ import './TopicStats.scss';
20
+
21
+ const b = cn('ydb-overview-topic-stats');
22
+
23
+ const prepareTopicInfo = (data: DescribeTopicResult): Array<InfoViewerItem> => {
24
+ return [
25
+ ...formatObject(formatTopicStats, {
26
+ ...data.topic_stats,
27
+ }),
28
+ ];
29
+ };
30
+
31
+ const prepareBytesWrittenInfo = (data: DescribeTopicResult): Array<InfoViewerItem> => {
32
+ const preparedBytes = prepareBytesWritten(data?.topic_stats?.bytes_written);
33
+
34
+ return [
35
+ {
36
+ label: 'per minute',
37
+ value: formatBps(preparedBytes.per_minute),
38
+ },
39
+ {
40
+ label: 'per hour',
41
+ value: formatBps(preparedBytes.per_hour),
42
+ },
43
+ {
44
+ label: 'per day',
45
+ value: formatBps(preparedBytes.per_day),
46
+ },
47
+ ];
48
+ };
49
+
50
+ export const TopicStats = () => {
51
+ const {data, error, loading, wasLoaded} = useTypedSelector((state) => state.topic);
52
+
53
+ if (loading && !wasLoaded) {
54
+ return (
55
+ <div className={b()}>
56
+ <Loader size="s" />
57
+ </div>
58
+ );
59
+ }
60
+
61
+ // There are several backed versions with different behaviour
62
+ // Possible returns on older versions:
63
+ // 1. Error when trying to access endpoint
64
+ // 2. No data
65
+ // 3. HTML page of Internal Viewer with an error
66
+ // 4. Data with no topic stats
67
+ // 5. Topic Stats as an empty object
68
+ if (
69
+ error ||
70
+ !data ||
71
+ typeof data !== 'object' ||
72
+ !data.topic_stats ||
73
+ isEmpty(data.topic_stats)
74
+ ) {
75
+ return (
76
+ <div className={b()}>
77
+ <div className={b('title')}>Stats</div>
78
+ <div className="error">{i18n('notSupportedVersion')}</div>
79
+ </div>
80
+ );
81
+ }
82
+
83
+ return (
84
+ <div className={b()}>
85
+ <div className={b('title')}>Stats</div>
86
+ <div className={b('info')}>
87
+ <InfoViewer info={prepareTopicInfo(data)} multilineLabels />
88
+ </div>
89
+ <div className={b('bytes-written')}>
90
+ <InfoViewer info={prepareBytesWrittenInfo(data)} />
91
+ </div>
92
+ </div>
93
+ );
94
+ };
@@ -0,0 +1,3 @@
1
+ {
2
+ "notSupportedVersion": "Topic stats are not supported in the current cluster version. Update cluster version to see topic stats"
3
+ }
@@ -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-overview-topic-stats';
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,3 @@
1
+ {
2
+ "notSupportedVersion": "Статистика топиков не поддерживается в текущей версии. Обновите кластер до более новой версии, чтобы увидеть статистику топиков"
3
+ }
@@ -0,0 +1 @@
1
+ export * from './TopicStats';
@@ -0,0 +1 @@
1
+ export * from './prepareTopicSchemaInfo';
@@ -0,0 +1,42 @@
1
+ import type {
2
+ TEvDescribeSchemeResult,
3
+ TPersQueueGroupDescription,
4
+ TPQTabletConfig,
5
+ TPQPartitionConfig,
6
+ } from '../../../../../types/api/schema';
7
+
8
+ import {formatObject, InfoViewerItem} from '../../../../../components/InfoViewer';
9
+ import {
10
+ formatPQGroupItem,
11
+ formatPQPartitionConfig,
12
+ formatPQTabletConfig,
13
+ } from '../../../../../components/InfoViewer/formatters';
14
+
15
+ export const prepareTopicSchemaInfo = (data?: TEvDescribeSchemeResult): Array<InfoViewerItem> => {
16
+ const pqGroupData = data?.PathDescription?.PersQueueGroup;
17
+
18
+ if (!pqGroupData) {
19
+ return [];
20
+ }
21
+
22
+ const {Partitions = [], PQTabletConfig = {PartitionConfig: {LifetimeSeconds: 0}}} = pqGroupData;
23
+
24
+ const {Codecs, MeteringMode} = pqGroupData?.PQTabletConfig;
25
+ const {WriteSpeedInBytesPerSecond, StorageLimitBytes} =
26
+ pqGroupData?.PQTabletConfig?.PartitionConfig;
27
+
28
+ const pqGeneralInfo = formatObject<TPersQueueGroupDescription>(formatPQGroupItem, {
29
+ Partitions,
30
+ PQTabletConfig,
31
+ });
32
+ const pqPartitionInfo = formatObject<TPQPartitionConfig>(formatPQPartitionConfig, {
33
+ StorageLimitBytes,
34
+ WriteSpeedInBytesPerSecond,
35
+ });
36
+ const pqTabletInfo = formatObject<TPQTabletConfig>(formatPQTabletConfig, {
37
+ Codecs,
38
+ MeteringMode,
39
+ });
40
+
41
+ return [...pqGeneralInfo, ...pqPartitionInfo, ...pqTabletInfo];
42
+ };
@@ -217,3 +217,22 @@ const pathTypeToChildless: Record<EPathType, boolean> = {
217
217
 
218
218
  export const isChildlessPathType = (type?: EPathType, subType?: EPathSubType) =>
219
219
  ((subType && pathSubTypeToChildless[subType]) || (type && pathTypeToChildless[type])) ?? false;
220
+
221
+ // ====================
222
+
223
+ const mapPathTypeToIsWithTopic: Record<EPathType, boolean> = {
224
+ [EPathType.EPathTypeCdcStream]: true,
225
+ [EPathType.EPathTypePersQueueGroup]: true,
226
+
227
+ [EPathType.EPathTypeInvalid]: false,
228
+ [EPathType.EPathTypeColumnStore]: false,
229
+ [EPathType.EPathTypeColumnTable]: false,
230
+ [EPathType.EPathTypeDir]: false,
231
+ [EPathType.EPathTypeTable]: false,
232
+ [EPathType.EPathTypeSubDomain]: false,
233
+ [EPathType.EPathTypeTableIndex]: false,
234
+ [EPathType.EPathTypeExtSubDomain]: false,
235
+ };
236
+
237
+ export const isPathTypeWithTopic = (type?: EPathType) =>
238
+ (type && mapPathTypeToIsWithTopic[type]) ?? false;
@@ -0,0 +1,9 @@
1
+ .ydb-user-settings {
2
+ &__item-with-popup {
3
+ max-width: 180px;
4
+ }
5
+
6
+ &__popup {
7
+ max-width: 370px;
8
+ }
9
+ }
@@ -1,15 +1,25 @@
1
+ import {ReactNode} from 'react';
1
2
  import {connect} from 'react-redux';
3
+ import cn from 'bem-cn-lite';
2
4
 
3
- import {RadioButton, Switch} from '@gravity-ui/uikit';
5
+ import {RadioButton, Switch, HelpPopover} from '@gravity-ui/uikit';
4
6
  import {Settings} from '@gravity-ui/navigation';
5
7
 
6
8
  import favoriteFilledIcon from '../../assets/icons/star.svg';
7
9
  import flaskIcon from '../../assets/icons/flask.svg';
8
10
 
9
- import {INVERTED_DISKS_KEY, THEME_KEY} from '../../utils/constants';
11
+ import {
12
+ INVERTED_DISKS_KEY,
13
+ THEME_KEY,
14
+ USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY,
15
+ } from '../../utils/constants';
10
16
 
11
17
  import {setSettingValue} from '../../store/reducers/settings';
12
18
 
19
+ import './UserSettings.scss';
20
+
21
+ const b = cn('ydb-user-settings');
22
+
13
23
  enum Theme {
14
24
  light = 'light',
15
25
  dark = 'dark',
@@ -25,6 +35,23 @@ function UserSettings(props: any) {
25
35
  props.setSettingValue(INVERTED_DISKS_KEY, String(value));
26
36
  };
27
37
 
38
+ const _onNodesEndpointChangeHandler = (value: boolean) => {
39
+ props.setSettingValue(USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY, String(value));
40
+ };
41
+
42
+ const renderBreakNodesSettingsItem = (title: ReactNode) => {
43
+ return (
44
+ <div className={b('item-with-popup')}>
45
+ {title}
46
+ <HelpPopover
47
+ content="Use /viewer/json/nodes endpoint for Nodes Tab in diagnostics. It returns incorrect data on older versions"
48
+ contentClassName={b('popup')}
49
+ hasArrow={true}
50
+ />
51
+ </div>
52
+ );
53
+ };
54
+
28
55
  return (
29
56
  <Settings>
30
57
  <Settings.Page
@@ -42,11 +69,7 @@ function UserSettings(props: any) {
42
69
  </Settings.Item>
43
70
  </Settings.Section>
44
71
  </Settings.Page>
45
- <Settings.Page
46
- id="experiments"
47
- title="Experiments"
48
- icon={{data: flaskIcon}}
49
- >
72
+ <Settings.Page id="experiments" title="Experiments" icon={{data: flaskIcon}}>
50
73
  <Settings.Section title="Experiments">
51
74
  <Settings.Item title="Inverted disks space indicators">
52
75
  <Switch
@@ -54,6 +77,15 @@ function UserSettings(props: any) {
54
77
  onUpdate={_onInvertedDisksChangeHandler}
55
78
  />
56
79
  </Settings.Item>
80
+ <Settings.Item
81
+ title="Break the Nodes tab in Diagnostics"
82
+ renderTitleComponent={renderBreakNodesSettingsItem}
83
+ >
84
+ <Switch
85
+ checked={props.useNodesEndpointInDiagnostics}
86
+ onUpdate={_onNodesEndpointChangeHandler}
87
+ />
88
+ </Settings.Item>
57
89
  </Settings.Section>
58
90
  </Settings.Page>
59
91
  </Settings>
@@ -61,14 +93,12 @@ function UserSettings(props: any) {
61
93
  }
62
94
 
63
95
  const mapStateToProps = (state: any) => {
64
- const {
65
- theme,
66
- invertedDisks,
67
- } = state.settings.userSettings;
96
+ const {theme, invertedDisks, useNodesEndpointInDiagnostics} = state.settings.userSettings;
68
97
 
69
98
  return {
70
99
  theme,
71
100
  invertedDisks: JSON.parse(invertedDisks),
101
+ useNodesEndpointInDiagnostics: JSON.parse(useNodesEndpointInDiagnostics),
72
102
  };
73
103
  };
74
104
 
@@ -17,10 +17,14 @@ interface Window {
17
17
  tenant: string;
18
18
  filter: string;
19
19
  nodeId: string;
20
- type: 'Groups' | 'Nodes';
21
20
  },
22
21
  axiosOptions?: AxiosOptions,
23
22
  ) => Promise<import('../types/api/storage').TStorageInfo>;
23
+ getNodes: (
24
+ params: import('../types/store/nodes').INodesApiRequestParams,
25
+ axiosOptions?: AxiosOptions,
26
+ ) => Promise<import('../types/api/nodes').TNodesInfo>;
27
+ getCompute: (path: string) => Promise<import('../types/api/compute').TComputeInfo>;
24
28
  sendQuery: <
25
29
  Action extends import('../types/api/query').Actions,
26
30
  Schema extends import('../types/api/query').Schemas = undefined,
@@ -55,6 +59,9 @@ interface Window {
55
59
  getHeatmapData: (params: {
56
60
  path: string;
57
61
  }) => Promise<import('../types/api/schema').TEvDescribeSchemeResult>;
62
+ getTopic: (params: {
63
+ path?: string;
64
+ }) => Promise<import('../types/api/topic').DescribeTopicResult>;
58
65
  [method: string]: Function;
59
66
  };
60
67
  }
@@ -1,7 +1,6 @@
1
1
  import AxiosWrapper from '@gravity-ui/axios-wrapper';
2
2
 
3
3
  import {backend as BACKEND} from '../store';
4
- import {StorageTypes} from '../store/reducers/storage';
5
4
 
6
5
  const config = {withCredentials: !window.custom_backend};
7
6
 
@@ -14,9 +13,6 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
14
13
  getClusterInfo() {
15
14
  return this.get(this.getPath('/viewer/json/cluster'), {tablets: true});
16
15
  }
17
- getNodes(path) {
18
- return this.get(this.getPath('/viewer/json/compute?enums=true'), {path});
19
- }
20
16
  getNodeInfo(id) {
21
17
  return this.get(this.getPath('/viewer/json/sysinfo?enums=true'), {
22
18
  node_id: id,
@@ -35,11 +31,27 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
35
31
  storage: true,
36
32
  });
37
33
  }
38
- getStorageInfo({tenant, filter, nodeId, type}, {concurrentId} = {}) {
34
+ getNodes({tenant, filter, storage, type = 'any', tablets = true}, {concurrentId} = {}) {
35
+ return this.get(
36
+ this.getPath('/viewer/json/nodes?enums=true'),
37
+ {
38
+ tenant,
39
+ with: filter,
40
+ storage,
41
+ type,
42
+ tablets,
43
+ },
44
+ {
45
+ concurrentId,
46
+ },
47
+ );
48
+ }
49
+ getCompute(path) {
50
+ return this.get(this.getPath('/viewer/json/compute?enums=true'), {path});
51
+ }
52
+ getStorageInfo({tenant, filter, nodeId}, {concurrentId} = {}) {
39
53
  return this.get(
40
- this.getPath(
41
- `/viewer/json/${type === StorageTypes.nodes ? 'nodes' : 'storage'}?enums=true`,
42
- ),
54
+ this.getPath(`/viewer/json/storage?enums=true`),
43
55
  {
44
56
  tenant,
45
57
  node_id: nodeId,
@@ -129,6 +141,13 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
129
141
  path,
130
142
  });
131
143
  }
144
+ getTopic({path}) {
145
+ return this.get(this.getPath('/viewer/json/describe_topic'), {
146
+ enums: true,
147
+ include_stats: true,
148
+ path,
149
+ });
150
+ }
132
151
  getPoolInfo(poolName) {
133
152
  return this.get(this.getPath('/viewer/json/storage'), {
134
153
  pool: poolName,
@@ -17,12 +17,13 @@ import network from './network';
17
17
  import pool from './pool';
18
18
  import tenants from './tenants';
19
19
  import tablet from './tablet';
20
+ import topic from './topic';
20
21
  import executeQuery from './executeQuery';
21
22
  import explainQuery from './explainQuery';
22
23
  import tabletsFilters from './tabletsFilters';
23
24
  import settings from './settings';
24
25
  import preview from './preview';
25
- import nodesList from './clusterNodes';
26
+ import nodesList from './nodesList';
26
27
  import describe from './describe';
27
28
  import schemaAcl from './schemaAcl';
28
29
  import executeTopQueries from './executeTopQueries';
@@ -55,6 +56,7 @@ export const rootReducer = {
55
56
  pool,
56
57
  tenants,
57
58
  tablet,
59
+ topic,
58
60
  executeQuery,
59
61
  explainQuery,
60
62
  tabletsFilters,