Skip to content

Commit 8c4ecc4

Browse files
fix: show 5 digits size in table info (#461)
1 parent 6323038 commit 8c4ecc4

File tree

15 files changed

+227
-85
lines changed

15 files changed

+227
-85
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import {FormatBytesArgs, formatBytes} from '../../utils/bytesParsers';
2+
3+
type FormattedBytesProps = FormatBytesArgs;
4+
5+
export const FormattedBytes = ({value, withSpeedLabel, ...params}: FormattedBytesProps) => {
6+
const formatted = formatBytes({value, withSpeedLabel, ...params});
7+
const bytes = formatBytes({value, withSpeedLabel, size: 'b'});
8+
9+
return <span title={bytes}>{formatted}</span>;
10+
};
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import type {FormatBytesArgs} from '../../utils/bytesParsers';
2+
import {FormattedBytes} from './FormattedBytes';
3+
4+
export const toFormattedSize = (
5+
value: number | string | undefined,
6+
params?: Omit<FormatBytesArgs, 'value'>,
7+
) => {
8+
if (!value) {
9+
return null;
10+
}
11+
12+
return <FormattedBytes value={value} significantDigits={2} {...params} />;
13+
};

src/components/InfoViewer/formatters/table.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import type {TFollowerGroup, TPartitionConfig, TTableStats} from '../../../types/api/schema';
22
import type {TMetrics} from '../../../types/api/tenant';
3-
import {formatCPU, formatBytes, formatNumber, formatBps, formatDateTime} from '../../../utils';
3+
import {formatCPU, formatNumber, formatBps, formatDateTime} from '../../../utils';
4+
import {toFormattedSize} from '../../FormattedBytes/utils';
45

56
import {createInfoFormatter} from '../utils';
67

78
export const formatTabletMetricsItem = createInfoFormatter<TMetrics>({
89
values: {
910
CPU: formatCPU,
10-
Memory: formatBytes,
11-
Storage: formatBytes,
11+
Memory: toFormattedSize,
12+
Storage: toFormattedSize,
1213
Network: formatBps,
1314
ReadThroughput: formatBps,
1415
WriteThroughput: formatBps,
@@ -37,8 +38,8 @@ export const formatPartitionConfigItem = createInfoFormatter<TPartitionConfig>({
3738

3839
export const formatTableStatsItem = createInfoFormatter<TTableStats>({
3940
values: {
40-
DataSize: formatBytes,
41-
IndexSize: formatBytes,
41+
DataSize: toFormattedSize,
42+
IndexSize: toFormattedSize,
4243
LastAccessTime: formatDateTime,
4344
LastUpdateTime: formatDateTime,
4445
},

src/components/SpeedMultiMeter/SpeedMultiMeter.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {useState} from 'react';
22
import cn from 'bem-cn-lite';
33
import {Popover} from '@gravity-ui/uikit';
44

5-
import {formatBytesCustom, IBytesSizes, IProcessSpeedStats} from '../../utils/bytesParsers';
5+
import {formatBytes, BytesSizes, ProcessSpeedStats} from '../../utils/bytesParsers';
66

77
import './SpeedMultiMeter.scss';
88

@@ -11,8 +11,8 @@ import i18n from './i18n';
1111
const b = cn('speed-multimeter');
1212

1313
interface SpeedMultiMeterProps {
14-
data?: IProcessSpeedStats;
15-
speedSize?: IBytesSizes;
14+
data?: ProcessSpeedStats;
15+
speedSize?: BytesSizes;
1616
withValue?: boolean;
1717
withPopover?: boolean;
1818
}
@@ -27,7 +27,7 @@ export const SpeedMultiMeter = ({
2727
const rawValues = [perMinute, perHour, perDay];
2828

2929
const formatValue = (value: number) =>
30-
formatBytesCustom({value: value, size: speedSize, isSpeed: true});
30+
formatBytes({value: value, size: speedSize, withSpeedLabel: true});
3131

3232
const formattedValues = [
3333
{value: formatValue(perMinute), label: i18n('perMinute')},

src/store/reducers/partitions/types.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type {IResponseError} from '../../../types/api/error';
2-
import type {IProcessSpeedStats} from '../../../utils/bytesParsers';
2+
import type {ProcessSpeedStats} from '../../../utils/bytesParsers';
33
import type {ApiRequestAction} from '../../utils';
44

55
import {FETCH_PARTITIONS, setDataWasNotLoaded, setSelectedConsumer} from './partitions';
@@ -9,8 +9,8 @@ export interface PreparedPartitionData {
99
partitionId: string;
1010
storeSize: string;
1111

12-
writeSpeed: IProcessSpeedStats;
13-
readSpeed?: IProcessSpeedStats;
12+
writeSpeed: ProcessSpeedStats;
13+
readSpeed?: ProcessSpeedStats;
1414

1515
partitionWriteLag: number;
1616
partitionWriteIdleTime: number;

src/types/store/topic.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import {FETCH_TOPIC, cleanTopicData, setDataWasNotLoaded} from '../../store/reducers/topic';
22
import type {ApiRequestAction} from '../../store/utils';
3-
import type {IProcessSpeedStats} from '../../utils/bytesParsers';
3+
import type {ProcessSpeedStats} from '../../utils/bytesParsers';
44
import type {IResponseError} from '../api/error';
55
import type {DescribeTopicResult} from '../api/topic';
66

77
export interface IPreparedConsumerData {
88
name: string | undefined;
9-
readSpeed: IProcessSpeedStats;
9+
readSpeed: ProcessSpeedStats;
1010

1111
writeLag: number;
1212
readLag: number;
@@ -19,7 +19,7 @@ export interface IPreparedTopicStats {
1919
partitionsWriteLag: number;
2020
partitionsIdleTime: number;
2121

22-
writeSpeed: IProcessSpeedStats;
22+
writeSpeed: ProcessSpeedStats;
2323
}
2424

2525
export interface ITopicState {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import {formatBytes} from '../formatBytes';
2+
3+
describe('formatBytes', () => {
4+
it('should work with only value', () => {
5+
expect(formatBytes({value: 100})).toBe('100 B');
6+
expect(formatBytes({value: 100_000})).toBe('100 KB');
7+
expect(formatBytes({value: 100_000_000})).toBe('100 MB');
8+
expect(formatBytes({value: 100_000_000_000})).toBe('100 GB');
9+
expect(formatBytes({value: 100_000_000_000_000})).toBe('100 TB');
10+
});
11+
it('should convert to size', () => {
12+
expect(formatBytes({value: 100_000, size: 'b'})).toBe('100,000 B');
13+
expect(formatBytes({value: 100_000_000_000_000, size: 'gb'})).toBe('100,000 GB');
14+
});
15+
it('should convert without labels', () => {
16+
expect(formatBytes({value: 100_000, size: 'b', withSizeLabel: false})).toBe('100,000');
17+
expect(formatBytes({value: 100_000_000_000_000, size: 'gb', withSizeLabel: false})).toBe(
18+
'100,000',
19+
);
20+
});
21+
it('should convert to speed', () => {
22+
expect(formatBytes({value: 100_000, withSpeedLabel: true})).toBe('100 KB/s');
23+
expect(formatBytes({value: 100_000, size: 'b', withSpeedLabel: true})).toBe('100,000 B/s');
24+
});
25+
it('should return fixed amount of significant digits', () => {
26+
expect(formatBytes({value: 99_000, significantDigits: 2})).toEqual('99,000 B');
27+
expect(formatBytes({value: 100_000, significantDigits: 2})).toEqual('100 KB');
28+
expect(formatBytes({value: 99_000_000_000_000, significantDigits: 2})).toEqual('99,000 GB');
29+
expect(formatBytes({value: 100_000_000_000_000, significantDigits: 2})).toEqual('100 TB');
30+
});
31+
it('shoudl return empty string on invalid data', () => {
32+
expect(formatBytes({value: undefined})).toEqual('');
33+
expect(formatBytes({value: null})).toEqual('');
34+
expect(formatBytes({value: ''})).toEqual('');
35+
expect(formatBytes({value: 'false'})).toEqual('');
36+
expect(formatBytes({value: '123qwe'})).toEqual('');
37+
});
38+
});

src/utils/bytesParsers/convertBytesObjectToSpeed.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type {MultipleWindowsStat} from '../../types/api/consumer';
22

33
import {DAY_IN_SECONDS, HOUR_IN_SECONDS, MINUTE_IN_SECONDS} from '../constants';
44

5-
export interface IProcessSpeedStats {
5+
export interface ProcessSpeedStats {
66
perMinute: number;
77
perHour: number;
88
perDay: number;
@@ -14,7 +14,7 @@ export interface IProcessSpeedStats {
1414
*/
1515
export const convertBytesObjectToSpeed = (
1616
data: MultipleWindowsStat | undefined,
17-
): IProcessSpeedStats => {
17+
): ProcessSpeedStats => {
1818
return {
1919
perMinute:
2020
data && data.per_minute ? Math.round(Number(data.per_minute) / MINUTE_IN_SECONDS) : 0,

src/utils/bytesParsers/formatBytes.ts

+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import {formatNumber} from '..';
2+
import {GIGABYTE, KILOBYTE, MEGABYTE, TERABYTE} from '../constants';
3+
import {isNumeric} from '../utils';
4+
5+
import i18n from './i18n';
6+
7+
const sizes = {
8+
b: {
9+
value: 1,
10+
label: i18n('b'),
11+
},
12+
kb: {
13+
value: KILOBYTE,
14+
label: i18n('kb'),
15+
},
16+
mb: {
17+
value: MEGABYTE,
18+
label: i18n('mb'),
19+
},
20+
gb: {
21+
value: GIGABYTE,
22+
label: i18n('gb'),
23+
},
24+
tb: {
25+
value: TERABYTE,
26+
label: i18n('tb'),
27+
},
28+
};
29+
30+
export type BytesSizes = keyof typeof sizes;
31+
32+
/**
33+
* This function is needed to keep more than 3 digits of the same size.
34+
*
35+
* @param significantDigits - number of digits above 3
36+
* @returns size to format value to get required number of digits
37+
*
38+
* By default value converted to the next size when it's above 1000,
39+
* so we have 900mb and 1gb. To extend it additional significantDigits could be set
40+
*
41+
* significantDigits value added above default 3
42+
*
43+
* significantDigits = 1 - 9 000 mb and 10 gb
44+
*
45+
* significantDigits = 2 - 90 000 mb and 100 gb
46+
*
47+
* significantDigits = 3 - 900 000 mb and 1000 gb
48+
*/
49+
const getSizeWithSignificantDigits = (value: number, significantDigits: number) => {
50+
const multiplier = 10 ** significantDigits;
51+
52+
const tbLevel = sizes.tb.value * multiplier;
53+
const gbLevel = sizes.gb.value * multiplier;
54+
const mbLevel = sizes.mb.value * multiplier;
55+
const kbLevel = sizes.kb.value * multiplier;
56+
57+
let size: BytesSizes = 'b';
58+
59+
if (value >= kbLevel) {
60+
size = 'kb';
61+
}
62+
if (value >= mbLevel) {
63+
size = 'mb';
64+
}
65+
if (value >= gbLevel) {
66+
size = 'gb';
67+
}
68+
if (value >= tbLevel) {
69+
size = 'tb';
70+
}
71+
72+
return size;
73+
};
74+
75+
interface FormatToSizeArgs {
76+
value: number;
77+
size?: BytesSizes;
78+
precision?: number;
79+
}
80+
81+
const formatToSize = ({value, size = 'mb', precision = 0}: FormatToSizeArgs) => {
82+
const result = (Number(value) / sizes[size].value).toFixed(precision);
83+
84+
return formatNumber(result);
85+
};
86+
87+
const addSizeLabel = (result: string, size: BytesSizes) => {
88+
return result + ` ${sizes[size].label}`;
89+
};
90+
91+
const addSpeedLabel = (result: string, size: BytesSizes) => {
92+
return addSizeLabel(result, size) + i18n('perSecond');
93+
};
94+
95+
export type FormatBytesArgs = Omit<FormatToSizeArgs, 'value'> & {
96+
value: number | string | undefined | null;
97+
withSpeedLabel?: boolean;
98+
withSizeLabel?: boolean;
99+
significantDigits?: number;
100+
};
101+
102+
/**
103+
* @param significantDigits - number of digits above 3
104+
*/
105+
export const formatBytes = ({
106+
value,
107+
size,
108+
withSpeedLabel = false,
109+
withSizeLabel = true,
110+
significantDigits = 0,
111+
...params
112+
}: FormatBytesArgs) => {
113+
if (!isNumeric(value)) {
114+
return '';
115+
}
116+
117+
const numValue = Number(value);
118+
119+
const sizeToConvert = size ?? getSizeWithSignificantDigits(numValue, significantDigits);
120+
121+
const result = formatToSize({value: numValue, size: sizeToConvert, ...params});
122+
123+
if (withSpeedLabel) {
124+
return addSpeedLabel(result, sizeToConvert);
125+
}
126+
127+
if (withSizeLabel) {
128+
return addSizeLabel(result, sizeToConvert);
129+
}
130+
131+
return result;
132+
};

src/utils/bytesParsers/formatBytesCustom.ts

-57
This file was deleted.

src/utils/bytesParsers/i18n/en.json

+1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
"kb": "KB",
44
"mb": "MB",
55
"gb": "GB",
6+
"tb": "TB",
67
"perSecond": "/s"
78
}

src/utils/bytesParsers/i18n/ru.json

+1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
"kb": "КБ",
44
"mb": "МБ",
55
"gb": "ГБ",
6+
"tb": "ТБ",
67
"perSecond": ""
78
}

src/utils/bytesParsers/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
export * from './formatBytesCustom';
1+
export * from './formatBytes';
22
export * from './convertBytesObjectToSpeed';

0 commit comments

Comments
 (0)