ydb-embedded-ui 3.2.2 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) 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/Consumers/Consumers.tsx +2 -1
  53. package/dist/containers/Tenant/Diagnostics/Diagnostics.scss +2 -2
  54. package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +3 -4
  55. package/dist/containers/Tenant/Diagnostics/OverloadedShards/OverloadedShards.tsx +1 -1
  56. package/dist/containers/Tenant/Diagnostics/Overview/ChangefeedInfo/ChangefeedInfo.tsx +69 -0
  57. package/dist/containers/Tenant/Diagnostics/Overview/ChangefeedInfo/index.ts +1 -0
  58. package/dist/containers/Tenant/Diagnostics/Overview/Overview.tsx +18 -16
  59. package/dist/containers/Tenant/Diagnostics/Overview/TopicInfo/TopicInfo.tsx +37 -0
  60. package/dist/containers/Tenant/Diagnostics/Overview/TopicInfo/index.ts +1 -0
  61. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/TopicStats.scss +30 -0
  62. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/TopicStats.tsx +94 -0
  63. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/i18n/en.json +3 -0
  64. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/i18n/index.ts +11 -0
  65. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/i18n/ru.json +3 -0
  66. package/dist/containers/Tenant/Diagnostics/Overview/TopicStats/index.ts +1 -0
  67. package/dist/containers/Tenant/Diagnostics/Overview/utils/index.ts +1 -0
  68. package/dist/containers/Tenant/Diagnostics/Overview/utils/prepareTopicSchemaInfo.ts +42 -0
  69. package/dist/containers/Tenant/utils/schema.ts +19 -0
  70. package/dist/containers/Tenants/Tenants.js +2 -1
  71. package/dist/containers/UserSettings/UserSettings.tsx +18 -10
  72. package/dist/services/api.d.ts +8 -1
  73. package/dist/services/api.js +27 -8
  74. package/dist/store/reducers/index.ts +3 -1
  75. package/dist/store/reducers/nodes.ts +148 -14
  76. package/dist/store/reducers/{clusterNodes.js → nodesList.js} +0 -41
  77. package/dist/store/reducers/settings.js +10 -4
  78. package/dist/store/reducers/storage.js +24 -13
  79. package/dist/store/reducers/tenant.js +5 -4
  80. package/dist/store/reducers/tooltip.ts +1 -1
  81. package/dist/store/reducers/topic.ts +52 -0
  82. package/dist/styles/mixins.scss +19 -11
  83. package/dist/types/api/common.ts +5 -0
  84. package/dist/types/api/compute.ts +1 -1
  85. package/dist/types/api/consumer.ts +12 -10
  86. package/dist/types/api/nodes.ts +2 -0
  87. package/dist/types/api/pdisk.ts +1 -0
  88. package/dist/types/api/schema.ts +3 -3
  89. package/dist/types/api/topic.ts +5 -4
  90. package/dist/types/api/vdisk.ts +2 -1
  91. package/dist/types/store/nodes.ts +56 -6
  92. package/dist/types/store/tooltip.ts +1 -1
  93. package/dist/types/store/topic.ts +21 -0
  94. package/dist/utils/constants.ts +5 -1
  95. package/dist/utils/i18n/i18n.ts +10 -2
  96. package/dist/utils/index.js +1 -1
  97. package/dist/utils/timeParsers/__test__/formatDuration.test.ts +50 -0
  98. package/dist/utils/timeParsers/__test__/protobuf.test.ts +74 -0
  99. package/dist/utils/timeParsers/formatDuration.ts +46 -0
  100. package/dist/utils/timeParsers/i18n/en.json +7 -0
  101. package/dist/utils/timeParsers/i18n/index.ts +11 -0
  102. package/dist/utils/timeParsers/i18n/ru.json +7 -0
  103. package/dist/utils/timeParsers/index.ts +2 -0
  104. package/dist/utils/timeParsers/protobuf.ts +36 -0
  105. package/package.json +1 -1
  106. package/dist/components/InfoViewer/schemaInfo/CDCStreamInfo.tsx +0 -48
  107. package/dist/components/InfoViewer/schemaInfo/PersQueueGroupInfo.tsx +0 -30
  108. package/dist/components/InternalLink/InternalLink.js +0 -23
  109. package/dist/containers/Nodes/Nodes.js +0 -213
  110. package/dist/containers/NodesViewer/NodesViewer.js +0 -163
  111. package/dist/containers/NodesViewer/NodesViewer.scss +0 -66
  112. package/dist/containers/Storage/Pdisk/Pdisk.tsx +0 -153
  113. package/dist/containers/Storage/Pdisk/__tests__/colors.tsx +0 -41
  114. package/dist/containers/Storage/Vdisk/Vdisk.js +0 -275
  115. package/dist/containers/Storage/Vdisk/Vdisk.scss +0 -22
  116. package/dist/containers/Storage/Vdisk/__tests__/colors.tsx +0 -163
  117. package/dist/containers/Tenant/Diagnostics/Compute/Compute.js +0 -139
  118. package/dist/containers/Tenant/Diagnostics/Compute/Compute.scss +0 -14
@@ -479,7 +479,7 @@ interface TKeyComponentSchema {
479
479
  TypeId?: number;
480
480
  }
481
481
 
482
- enum EMeteringMode {
482
+ export enum EMeteringMode {
483
483
  METERING_MODE_RESERVED_CAPACITY = 'METERING_MODE_RESERVED_CAPACITY',
484
484
  METERING_MODE_REQUEST_UNITS = 'METERING_MODE_REQUEST_UNITS',
485
485
  }
@@ -526,7 +526,7 @@ interface TMirrorPartitionConfig {
526
526
  SyncWriteTime?: boolean;
527
527
  }
528
528
 
529
- interface TPQPartitionConfig {
529
+ export interface TPQPartitionConfig {
530
530
  MaxCountInPartition?: number;
531
531
  /** int64 */
532
532
  MaxSizeInPartition?: string;
@@ -559,7 +559,7 @@ interface TPQPartitionConfig {
559
559
  MirrorFrom?: TMirrorPartitionConfig;
560
560
  }
561
561
 
562
- interface TPQTabletConfig {
562
+ export interface TPQTabletConfig {
563
563
  /** uint64 */
564
564
  CacheSize?: string;
565
565
  PartitionConfig: TPQPartitionConfig;
@@ -1,5 +1,6 @@
1
1
  /* eslint-disable camelcase */
2
2
 
3
+ import {IProtobufTimeObject} from './common';
3
4
  import {Consumer, Entry, MultipleWindowsStat, PartitionStats, SupportedCodecs} from './consumer';
4
5
 
5
6
  /**
@@ -27,7 +28,7 @@ export interface DescribeTopicResult {
27
28
  *
28
29
  * How long data in partition should be stored.
29
30
  */
30
- retention_period?: string;
31
+ retention_period?: string | IProtobufTimeObject;
31
32
 
32
33
  /**
33
34
  * int64
@@ -98,7 +99,7 @@ interface PartitionInfo {
98
99
  partition_stats?: PartitionStats;
99
100
  }
100
101
 
101
- interface TopicStats {
102
+ export interface TopicStats {
102
103
  /**
103
104
  * int64
104
105
  *
@@ -111,14 +112,14 @@ interface TopicStats {
111
112
  *
112
113
  * Minimum of timestamps of last write among all partitions.
113
114
  */
114
- min_last_write_time?: string;
115
+ min_last_write_time?: string | IProtobufTimeObject;
115
116
 
116
117
  /**
117
118
  * google.protobuf.Duration
118
119
  *
119
120
  * Maximum of differences between write timestamp and create timestamp for all messages, written during last minute.
120
121
  */
121
- max_write_time_lag?: string;
122
+ max_write_time_lag?: string | IProtobufTimeObject;
122
123
 
123
124
  /** How much bytes were written statistics. */
124
125
  bytes_written?: MultipleWindowsStat;
@@ -10,6 +10,7 @@ export interface TVDiskStateInfo {
10
10
  CreateTime?: string;
11
11
  /** uint64 */
12
12
  ChangeTime?: string;
13
+ PDiskId?: number;
13
14
  PDisk?: TPDiskStateInfo;
14
15
  VDiskSlotId?: number;
15
16
  /** uint64 */
@@ -113,7 +114,7 @@ interface TRank {
113
114
  Flag?: EFlag;
114
115
  }
115
116
 
116
- enum EVDiskState {
117
+ export enum EVDiskState {
117
118
  Initial = 'Initial',
118
119
  LocalRecoveryError = 'LocalRecoveryError',
119
120
  SyncGuidRecovery = 'SyncGuidRecovery',
@@ -1,30 +1,80 @@
1
+ import type {IResponseError} from '../api/error';
2
+ import type {TEndpoint, TPoolStats} from '../api/nodes';
3
+ import type {TTabletStateInfo as TComputeTabletStateInfo} from '../api/compute';
4
+ import type {TTabletStateInfo as TFullTabletStateInfo} from '../api/tablet';
5
+ import type {EFlag} from '../api/enums';
6
+
1
7
  import {NodesUptimeFilterValues} from '../../utils/nodes';
2
8
  import {
3
9
  FETCH_NODES,
4
- clearNodes,
10
+ resetNodesState,
5
11
  setDataWasNotLoaded,
6
12
  setNodesUptimeFilter,
13
+ setSearchValue,
7
14
  } from '../../store/reducers/nodes';
8
15
  import {ApiRequestAction} from '../../store/utils';
9
- import {IResponseError} from '../api/error';
10
- import {TNodesInfo} from '../api/nodes';
16
+
17
+ // Since nodes from different endpoints can have different types,
18
+ // This type describes fields, that are expected by tables with nodes
19
+ export interface INodesPreparedEntity {
20
+ NodeId: number;
21
+ Host?: string;
22
+ SystemState?: EFlag;
23
+ DataCenter?: string;
24
+ Rack?: string;
25
+ Version?: string;
26
+ StartTime?: string;
27
+ Uptime: string;
28
+ MemoryUsed?: string;
29
+ PoolStats?: TPoolStats[];
30
+ LoadAverage?: number[];
31
+ Tablets?: TFullTabletStateInfo[] | TComputeTabletStateInfo[];
32
+ TenantName?: string;
33
+ Endpoints?: TEndpoint[];
34
+ }
11
35
 
12
36
  export interface INodesState {
13
37
  loading: boolean;
14
38
  wasLoaded: boolean;
15
39
  nodesUptimeFilter: NodesUptimeFilterValues;
16
- data?: TNodesInfo;
40
+ searchValue: string;
41
+ data?: INodesPreparedEntity[];
42
+ totalNodes?: number;
17
43
  error?: IResponseError;
18
44
  }
19
45
 
20
- type INodesApiRequestAction = ApiRequestAction<typeof FETCH_NODES, TNodesInfo, IResponseError>;
46
+ type INodesApiRequestNodeType = 'static' | 'dynamic' | 'any';
47
+
48
+ // Space - out of space nodes
49
+ // Missing - nodes with missing disks
50
+ type INodesApiRequestProblemType = 'missing' | 'space';
51
+
52
+ export interface INodesApiRequestParams {
53
+ tenant?: string;
54
+ type?: INodesApiRequestNodeType;
55
+ filter?: INodesApiRequestProblemType;
56
+ storage?: boolean;
57
+ tablets?: boolean;
58
+ }
59
+
60
+ export interface INodesHandledResponse {
61
+ Nodes?: INodesPreparedEntity[];
62
+ TotalNodes: number;
63
+ }
64
+
65
+ type INodesApiRequestAction = ApiRequestAction<
66
+ typeof FETCH_NODES,
67
+ INodesHandledResponse,
68
+ IResponseError
69
+ >;
21
70
 
22
71
  export type INodesAction =
23
72
  | INodesApiRequestAction
24
73
  | (
25
- | ReturnType<typeof clearNodes>
26
74
  | ReturnType<typeof setDataWasNotLoaded>
27
75
  | ReturnType<typeof setNodesUptimeFilter>
76
+ | ReturnType<typeof setSearchValue>
77
+ | ReturnType<typeof resetNodesState>
28
78
  );
29
79
 
30
80
  export interface INodesRootStateSlice {
@@ -11,7 +11,7 @@ export interface ITooltipPositions {
11
11
  export interface ITooltipState {
12
12
  toolTipVisible: boolean;
13
13
  positions?: ITooltipPositions;
14
- currentHoveredRef?: EventTarget;
14
+ currentHoveredRef?: EventTarget | null;
15
15
  template: (data: any) => JSX.Element;
16
16
  templateType: ITooltipTemplateType;
17
17
  data?: any;
@@ -0,0 +1,21 @@
1
+ import {FETCH_TOPIC} from '../../store/reducers/topic';
2
+ import type {ApiRequestAction} from '../../store/utils';
3
+ import type {IResponseError} from '../api/error';
4
+ import type {DescribeTopicResult} from '../api/topic';
5
+
6
+ export interface ITopicState {
7
+ loading: boolean;
8
+ wasLoaded: boolean;
9
+ data?: DescribeTopicResult;
10
+ error?: IResponseError;
11
+ }
12
+
13
+ export type ITopicAction = ApiRequestAction<
14
+ typeof FETCH_TOPIC,
15
+ DescribeTopicResult,
16
+ IResponseError
17
+ >;
18
+
19
+ export interface ITopicRootStateSlice {
20
+ topic: ITopicState;
21
+ }
@@ -14,9 +14,12 @@ export const GIGABYTE = 1_000_000_000;
14
14
  export const TERABYTE = 1_000_000_000_000;
15
15
  export const GROUP = 'group';
16
16
 
17
- export const HOUR_IN_SECONDS = 60 * 60;
17
+ export const MINUTE_IN_SECONDS = 60;
18
+ export const HOUR_IN_SECONDS = 60 * MINUTE_IN_SECONDS;
18
19
  export const DAY_IN_SECONDS = 24 * HOUR_IN_SECONDS;
19
20
 
21
+ export const MS_IN_NANOSECONDS = 1000000;
22
+
20
23
  export const TABLET_STATES = {
21
24
  TABLET_VOLATILE_STATE_UNKNOWN: 'unknown',
22
25
  TABLET_VOLATILE_STATE_STOPPED: 'stopped',
@@ -89,6 +92,7 @@ export type IProblemFilterValues = typeof ALL | typeof PROBLEMS;
89
92
 
90
93
  export const THEME_KEY = 'theme';
91
94
  export const INVERTED_DISKS_KEY = 'invertedDisks';
95
+ export const USE_NODES_ENDPOINT_IN_DIAGNOSTICS_KEY = 'useNodesEndpointInDiagnostics';
92
96
  export const SAVED_QUERIES_KEY = 'saved_queries';
93
97
  export const ASIDE_HEADER_COMPACT_KEY = 'asideHeaderCompact';
94
98
  export const QUERIES_HISTORY_KEY = 'queries_history';
@@ -1,8 +1,16 @@
1
1
  import {I18N} from '@gravity-ui/i18n';
2
+ import {configure as configureUiKit} from '@gravity-ui/uikit';
3
+ import {configure as configureYdbUiComponents} from 'ydb-ui-components';
2
4
 
3
- export enum Lang {
5
+ enum Lang {
4
6
  En = 'en',
5
7
  Ru = 'ru',
6
8
  }
7
9
 
8
- export const i18n = new I18N();
10
+ const i18n = new I18N();
11
+
12
+ i18n.setLang(Lang.En);
13
+ configureYdbUiComponents({lang: Lang.En});
14
+ configureUiKit({lang: Lang.En});
15
+
16
+ export {i18n, Lang};
@@ -2,7 +2,7 @@ import numeral from 'numeral';
2
2
  import locales from 'numeral/locales'; // eslint-disable-line no-unused-vars
3
3
 
4
4
  import {i18n} from './i18n';
5
- import {MEGABYTE, TERABYTE, DAY_IN_SECONDS, GIGABYTE} from './constants';
5
+ import {MEGABYTE, TERABYTE, GIGABYTE, DAY_IN_SECONDS} from './constants';
6
6
  import {isNumeric} from './utils';
7
7
 
8
8
  numeral.locale(i18n.lang);
@@ -0,0 +1,50 @@
1
+ import {formatDurationToShortTimeFormat} from '../formatDuration';
2
+ import i18n from '../i18n';
3
+
4
+ describe('formatDurationToShortTimeFormat', () => {
5
+ // 1 - ms
6
+ const timeWithMs = 123;
7
+ const formattedTimeWithMs = i18n('ms', {seconds: 0, ms: 123});
8
+
9
+ // 2 - seconds and ms
10
+ const timeWithSecondsAndMsInMs = 12345;
11
+ const formattedTimeWithSecondsAndMs = i18n('secMs', {seconds: 12, ms: 345});
12
+
13
+ // 3 - minutes
14
+ const timeWithMinutesInMs = 754567;
15
+ const formattedTimeWithMinutes = i18n('minSec', {minutes: 12, seconds: 34});
16
+
17
+ // 4 - hours
18
+ const timeWithHoursInMs = 9245678;
19
+ const formattedTimeWithHours = i18n('hoursMin', {hours: 2, minutes: 34});
20
+
21
+ // 5 - days
22
+ const timeWithDaysInMs = 439234123;
23
+ const formattedTimeWithDays = i18n('daysHours', {days: 5, hours: 2});
24
+
25
+ // 6 - zero
26
+ const formattedZero = i18n('ms', {seconds: 0, ms: 0});
27
+
28
+ it('should return ms on values less than second', () => {
29
+ expect(formatDurationToShortTimeFormat(timeWithMs)).toEqual(formattedTimeWithMs);
30
+ });
31
+ it('should return seconds and ms', () => {
32
+ expect(formatDurationToShortTimeFormat(timeWithSecondsAndMsInMs)).toEqual(
33
+ formattedTimeWithSecondsAndMs,
34
+ );
35
+ });
36
+ it('should return minutes and seconds', () => {
37
+ expect(formatDurationToShortTimeFormat(timeWithMinutesInMs)).toEqual(
38
+ formattedTimeWithMinutes,
39
+ );
40
+ });
41
+ it('should return hours and minutes', () => {
42
+ expect(formatDurationToShortTimeFormat(timeWithHoursInMs)).toEqual(formattedTimeWithHours);
43
+ });
44
+ it('should return days and hours', () => {
45
+ expect(formatDurationToShortTimeFormat(timeWithDaysInMs)).toEqual(formattedTimeWithDays);
46
+ });
47
+ it('should process zero values', () => {
48
+ expect(formatDurationToShortTimeFormat(0)).toEqual(formattedZero);
49
+ });
50
+ });
@@ -0,0 +1,74 @@
1
+ import {
2
+ parseProtobufDurationToMs,
3
+ parseProtobufTimeObjectToMs,
4
+ parseProtobufTimestampToMs,
5
+ } from '../';
6
+
7
+ describe('Protobuf time parsers', () => {
8
+ // 1 - nanoseconds only
9
+ const timeObjectWithNanoseconds = {
10
+ nanos: 123000000,
11
+ };
12
+ const timeWithNanosecondsInMs = 123;
13
+
14
+ // 2 - seconds only
15
+ const timeObjectWithSeconds = {
16
+ seconds: '12',
17
+ };
18
+ const timeWithSecondsInMs = 12000;
19
+ const stringDurationSeconds = '12s';
20
+
21
+ // 3 - days
22
+ const timeObjectWithDays = {
23
+ seconds: '439234',
24
+ nanos: 123000000,
25
+ };
26
+ const timeWithDaysInMs = 439234123;
27
+
28
+ // 4 - TimeStamp
29
+ const timestamp = '2023-01-16T14:26:34.466Z';
30
+ const timestampInMs = 1673879194466;
31
+ const timestampObject = {
32
+ seconds: '1673879194',
33
+ nanos: 466000000,
34
+ };
35
+
36
+ describe('parseProtobufTimeObjectToMs', () => {
37
+ it('should work with timestamp object values', () => {
38
+ expect(parseProtobufTimeObjectToMs(timeObjectWithDays)).toEqual(timeWithDaysInMs);
39
+ });
40
+ it('should work with timestamp object without seconds', () => {
41
+ expect(parseProtobufTimeObjectToMs(timeObjectWithNanoseconds)).toEqual(
42
+ timeWithNanosecondsInMs,
43
+ );
44
+ });
45
+ it('should work with timestamp object without nanos', () => {
46
+ expect(parseProtobufTimeObjectToMs(timeObjectWithSeconds)).toEqual(timeWithSecondsInMs);
47
+ });
48
+ it('should work with empty object values', () => {
49
+ expect(parseProtobufTimeObjectToMs({})).toEqual(0);
50
+ });
51
+ });
52
+ describe('parseProtobufTimestampToMs', () => {
53
+ it('should work with string date values', () => {
54
+ expect(parseProtobufTimestampToMs(timestamp)).toEqual(timestampInMs);
55
+ });
56
+ it('should work with timestamp object values', () => {
57
+ expect(parseProtobufTimestampToMs(timestampObject)).toEqual(timestampInMs);
58
+ });
59
+ it('should work with empty object values', () => {
60
+ expect(parseProtobufTimestampToMs({})).toEqual(0);
61
+ });
62
+ });
63
+ describe('parseProtobufDurationToMs', () => {
64
+ it('should work with string values', () => {
65
+ expect(parseProtobufDurationToMs(stringDurationSeconds)).toEqual(timeWithSecondsInMs);
66
+ });
67
+ it('should work with duration object values', () => {
68
+ expect(parseProtobufDurationToMs(timeObjectWithDays)).toEqual(timeWithDaysInMs);
69
+ });
70
+ it('should work with empty object values', () => {
71
+ expect(parseProtobufDurationToMs({})).toEqual(0);
72
+ });
73
+ });
74
+ });
@@ -0,0 +1,46 @@
1
+ import {DAY_IN_SECONDS, HOUR_IN_SECONDS} from '../constants';
2
+
3
+ import i18n from './i18n';
4
+
5
+ /**
6
+ * Process time difference in ms and returns formated time.
7
+ * Only two major values are returned (days & hours, hours & minutes, minutes & seconds, etc.)
8
+ */
9
+ export const formatDurationToShortTimeFormat = (value: number) => {
10
+ const ms = value % 1000;
11
+ let remain = Math.floor(value / 1000);
12
+
13
+ const days = Math.floor(remain / DAY_IN_SECONDS);
14
+ remain = remain % DAY_IN_SECONDS;
15
+
16
+ const hours = Math.floor(remain / HOUR_IN_SECONDS);
17
+ remain = remain % HOUR_IN_SECONDS;
18
+
19
+ const minutes = Math.floor(remain / 60);
20
+ remain = remain % 60;
21
+
22
+ const seconds = remain;
23
+
24
+ const duration = {
25
+ days,
26
+ hours,
27
+ minutes,
28
+ seconds,
29
+ ms,
30
+ };
31
+
32
+ if (days > 0) {
33
+ return i18n('daysHours', duration);
34
+ }
35
+ if (hours > 0) {
36
+ return i18n('hoursMin', duration);
37
+ }
38
+ if (minutes > 0) {
39
+ return i18n('minSec', duration);
40
+ }
41
+ if (seconds > 0) {
42
+ return i18n('secMs', duration);
43
+ }
44
+
45
+ return i18n('ms', duration);
46
+ };
@@ -0,0 +1,7 @@
1
+ {
2
+ "daysHours": "{{days}}d {{hours}}h",
3
+ "hoursMin": "{{hours}}h {{minutes}}m",
4
+ "minSec": "{{minutes}}m {{seconds}}s",
5
+ "secMs": "{{seconds}}s {{ms}}ms",
6
+ "ms": "{{ms}}ms"
7
+ }
@@ -0,0 +1,11 @@
1
+ import {i18n, Lang} from '../../i18n';
2
+
3
+ import en from './en.json';
4
+ import ru from './ru.json';
5
+
6
+ const COMPONENT = 'ydb-time-parsers';
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,7 @@
1
+ {
2
+ "daysHours": "{{days}}д {{hours}}ч",
3
+ "hoursMin": "{{hours}}ч {{minutes}}м",
4
+ "minSec": "{{minutes}}м {{seconds}}с",
5
+ "secMs": "{{seconds}}с {{ms}}мс",
6
+ "ms": "{{ms}}мс"
7
+ }
@@ -0,0 +1,2 @@
1
+ export * from './formatDuration';
2
+ export * from './protobuf';
@@ -0,0 +1,36 @@
1
+ import type {IProtobufTimeObject} from '../../types/api/common';
2
+
3
+ import {MS_IN_NANOSECONDS} from '../constants';
4
+
5
+ /**
6
+ * Parses Google.protobuf Duration and Timestamp represented by objects
7
+ */
8
+ export const parseProtobufTimeObjectToMs = (value: IProtobufTimeObject): number => {
9
+ const secondsInMs = value.seconds ? Number(value.seconds) * 1000 : 0;
10
+ const nanosecondsInMs = value.nanos ? value.nanos / MS_IN_NANOSECONDS : 0;
11
+ return secondsInMs + nanosecondsInMs;
12
+ };
13
+
14
+ /**
15
+ * Parses Google.protobuf Timestamp to ms.
16
+ * Timestamp could be represented as object or as date string
17
+ */
18
+ export const parseProtobufTimestampToMs = (value: string | IProtobufTimeObject) => {
19
+ if (typeof value === 'string') {
20
+ return Date.parse(value);
21
+ } else {
22
+ return parseProtobufTimeObjectToMs(value);
23
+ }
24
+ };
25
+
26
+ /**
27
+ * Parses Google.protobuf Duration to ms.
28
+ * Duration could be represented as object or as string with format '12345s'
29
+ */
30
+ export const parseProtobufDurationToMs = (value: string | IProtobufTimeObject) => {
31
+ if (typeof value === 'string') {
32
+ return parseInt(value, 10) * 1000;
33
+ } else {
34
+ return parseProtobufTimeObjectToMs(value);
35
+ }
36
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ydb-embedded-ui",
3
- "version": "3.2.2",
3
+ "version": "3.3.0",
4
4
  "files": [
5
5
  "dist"
6
6
  ],
@@ -1,48 +0,0 @@
1
- import type {TEvDescribeSchemeResult, TCdcStreamDescription} from '../../../types/api/schema';
2
- import {useTypedSelector} from '../../../utils/hooks';
3
- import {selectSchemaData} from '../../../store/reducers/schema';
4
- import {getEntityName} from '../../../containers/Tenant/utils';
5
-
6
- import {formatCdcStreamItem, formatPQGroupItem, formatCommonItem} from '../formatters';
7
- import {InfoViewer, InfoViewerItem} from '..';
8
-
9
- const DISPLAYED_FIELDS: Set<keyof TCdcStreamDescription> = new Set(['Mode', 'Format']);
10
-
11
- interface CDCStreamInfoProps {
12
- data?: TEvDescribeSchemeResult;
13
- childrenPaths?: string[];
14
- }
15
-
16
- export const CDCStreamInfo = ({data, childrenPaths}: CDCStreamInfoProps) => {
17
- const pqGroupData = useTypedSelector((state) => selectSchemaData(state, childrenPaths?.[0]));
18
-
19
- const entityName = getEntityName(data?.PathDescription);
20
-
21
- if (!data || !pqGroupData) {
22
- return <div className="error">No {entityName} data</div>;
23
- }
24
-
25
- const cdcStream = data.PathDescription?.CdcStreamDescription;
26
- const pqGroup = pqGroupData?.PathDescription?.PersQueueGroup;
27
-
28
- const info: Array<InfoViewerItem> = [];
29
-
30
- info.push(formatCommonItem('CreateStep', data.PathDescription?.Self?.CreateStep));
31
-
32
- let key: keyof TCdcStreamDescription;
33
- for (key in cdcStream) {
34
- if (DISPLAYED_FIELDS.has(key)) {
35
- info.push(formatCdcStreamItem(key, cdcStream?.[key]));
36
- }
37
- }
38
-
39
- info.push(formatPQGroupItem('Partitions', pqGroup?.Partitions || []));
40
- info.push(
41
- formatPQGroupItem(
42
- 'PQTabletConfig',
43
- pqGroup?.PQTabletConfig || {PartitionConfig: {LifetimeSeconds: 0}},
44
- ),
45
- );
46
-
47
- return <InfoViewer title={entityName} info={info} />;
48
- };
@@ -1,30 +0,0 @@
1
- import type {TEvDescribeSchemeResult} from '../../../types/api/schema';
2
- import {getEntityName} from '../../../containers/Tenant/utils';
3
-
4
- import {formatPQGroupItem} from '../formatters';
5
- import {InfoViewer, InfoViewerItem} from '..';
6
-
7
- interface PersQueueGrouopInfoProps {
8
- data?: TEvDescribeSchemeResult;
9
- }
10
-
11
- export const PersQueueGroupInfo = ({data}: PersQueueGrouopInfoProps) => {
12
- const entityName = getEntityName(data?.PathDescription);
13
-
14
- if (!data) {
15
- return <div className="error">No {entityName} data</div>;
16
- }
17
-
18
- const pqGroup = data.PathDescription?.PersQueueGroup;
19
- const info: Array<InfoViewerItem> = [];
20
-
21
- info.push(formatPQGroupItem('Partitions', pqGroup?.Partitions || []));
22
- info.push(
23
- formatPQGroupItem(
24
- 'PQTabletConfig',
25
- pqGroup?.PQTabletConfig || {PartitionConfig: {LifetimeSeconds: 0}},
26
- ),
27
- );
28
-
29
- return <InfoViewer title={entityName} info={info} />;
30
- };
@@ -1,23 +0,0 @@
1
- import PropTypes from 'prop-types';
2
- import cn from 'bem-cn-lite';
3
-
4
- import {Link} from 'react-router-dom';
5
-
6
- const bLink = cn('yc-link');
7
-
8
- export default function InternalLink({to, children, onClick, className}) {
9
- return to ? (
10
- <Link className={bLink({view: 'normal'}, className)} to={to} onClick={onClick}>
11
- {children}
12
- </Link>
13
- ) : (
14
- children
15
- );
16
- }
17
-
18
- InternalLink.propTypes = {
19
- to: PropTypes.string.isRequired,
20
- children: PropTypes.node,
21
- onClick: PropTypes.func,
22
- className: PropTypes.string,
23
- };