Compare commits
29 Commits
main
...
chore/metr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce6381429e | ||
|
|
a44c7fcc20 | ||
|
|
680161a4af | ||
|
|
6ec55f60df | ||
|
|
d71d5e917f | ||
|
|
cb82b020ec | ||
|
|
34eea2dd8a | ||
|
|
22118c7825 | ||
|
|
6c66409516 | ||
|
|
e5907449ce | ||
|
|
0aea80c60f | ||
|
|
1d31ef81f3 | ||
|
|
f14618fe10 | ||
|
|
8bb7f7c058 | ||
|
|
f831749575 | ||
|
|
a86ee0f941 | ||
|
|
d4df241792 | ||
|
|
c2d140be5c | ||
|
|
220b2d3a78 | ||
|
|
78920de08a | ||
|
|
511ed22cfa | ||
|
|
599ea1bf2f | ||
|
|
68c2dc3745 | ||
|
|
b9c62a2d1a | ||
|
|
68daf6a552 | ||
|
|
d6ddc47a30 | ||
|
|
8474925d44 | ||
|
|
18e4453b0d | ||
|
|
003d89e476 |
@@ -9,6 +9,7 @@ export interface UpdateMetricMetadataProps {
|
||||
metricType: MetricType;
|
||||
temporality?: Temporality;
|
||||
isMonotonic?: boolean;
|
||||
unit?: string;
|
||||
}
|
||||
|
||||
export interface UpdateMetricMetadataResponse {
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { formattedValueToString, getValueFormat } from '@grafana/data';
|
||||
import {
|
||||
CustomGraphUnitToUniversalUnit,
|
||||
UniversalUnitToGrafanaUnit,
|
||||
} from 'components/YAxisUnitSelector/constants';
|
||||
import { UniversalYAxisUnit } from 'components/YAxisUnitSelector/types';
|
||||
|
||||
export const getYAxisFormattedValue = (
|
||||
value: string,
|
||||
@@ -47,9 +52,34 @@ export const getYAxisFormattedValue = (
|
||||
};
|
||||
|
||||
export const getToolTipValue = (value: string, format?: string): string => {
|
||||
const universalMappingExists = format && format in UniversalUnitToGrafanaUnit;
|
||||
const universalMappingNotFound =
|
||||
format &&
|
||||
format in UniversalYAxisUnit &&
|
||||
!(format in UniversalUnitToGrafanaUnit);
|
||||
|
||||
let processedFormat = universalMappingExists
|
||||
? UniversalUnitToGrafanaUnit[format as UniversalYAxisUnit]
|
||||
: format;
|
||||
// If using universal units but a compatible mapping is not found, use `short` for numeric formatting
|
||||
if (universalMappingNotFound) {
|
||||
processedFormat = 'short';
|
||||
}
|
||||
try {
|
||||
return formattedValueToString(
|
||||
getValueFormat(format)(parseFloat(value), undefined, undefined, undefined),
|
||||
const valueFormat = getValueFormat(processedFormat)(
|
||||
parseFloat(value),
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
);
|
||||
// For universal units, check if it requires a custom suffix
|
||||
const suffix = valueFormat?.suffix?.trim() || '';
|
||||
if (universalMappingExists && suffix in CustomGraphUnitToUniversalUnit) {
|
||||
return `${valueFormat.text} ${CustomGraphUnitToUniversalUnit[suffix]}`;
|
||||
}
|
||||
return (
|
||||
formattedValueToString(valueFormat) +
|
||||
(universalMappingNotFound ? ` ${format}` : '')
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import './styles.scss';
|
||||
|
||||
import { Select } from 'antd';
|
||||
import { DefaultOptionType } from 'antd/es/select';
|
||||
|
||||
import { UniversalYAxisUnitMappings, Y_AXIS_CATEGORIES } from './constants';
|
||||
import { UniversalYAxisUnit, YAxisUnitSelectorProps } from './types';
|
||||
import { mapMetricUnitToUniversalUnit } from './utils';
|
||||
|
||||
function YAxisUnitSelector({
|
||||
value,
|
||||
onChange,
|
||||
placeholder = 'Please select a unit',
|
||||
loading = false,
|
||||
}: YAxisUnitSelectorProps): JSX.Element {
|
||||
const universalUnit = mapMetricUnitToUniversalUnit(value);
|
||||
|
||||
const handleSearch = (
|
||||
searchTerm: string,
|
||||
currentOption: DefaultOptionType | undefined,
|
||||
): boolean => {
|
||||
if (!currentOption?.value) return false;
|
||||
|
||||
const search = searchTerm.toLowerCase();
|
||||
const unitId = currentOption.value.toString().toLowerCase();
|
||||
const unitLabel = currentOption.children?.toString().toLowerCase() || '';
|
||||
|
||||
// Check label and id
|
||||
if (unitId.includes(search) || unitLabel.includes(search)) return true;
|
||||
|
||||
// Check aliases (from the mapping) using array iteration
|
||||
const aliases = Array.from(
|
||||
UniversalYAxisUnitMappings[currentOption.value as UniversalYAxisUnit] ?? [],
|
||||
);
|
||||
|
||||
return aliases.some((alias) => alias.toLowerCase().includes(search));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="y-axis-unit-selector-component">
|
||||
<Select
|
||||
showSearch
|
||||
value={universalUnit}
|
||||
onChange={onChange}
|
||||
placeholder={placeholder}
|
||||
filterOption={(input, option): boolean => handleSearch(input, option)}
|
||||
loading={loading}
|
||||
>
|
||||
{Y_AXIS_CATEGORIES.map((category) => (
|
||||
<Select.OptGroup key={category.name} label={category.name}>
|
||||
{category.units.map((unit) => (
|
||||
<Select.Option key={unit.id} value={unit.id}>
|
||||
{unit.name}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select.OptGroup>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default YAxisUnitSelector;
|
||||
@@ -0,0 +1,68 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
|
||||
import YAxisUnitSelector from '../YAxisUnitSelector';
|
||||
|
||||
describe('YAxisUnitSelector', () => {
|
||||
const mockOnChange = jest.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
mockOnChange.mockClear();
|
||||
});
|
||||
|
||||
it('renders with default placeholder', () => {
|
||||
render(<YAxisUnitSelector value="" onChange={mockOnChange} />);
|
||||
expect(screen.getByText('Please select a unit')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with custom placeholder', () => {
|
||||
render(
|
||||
<YAxisUnitSelector
|
||||
value=""
|
||||
onChange={mockOnChange}
|
||||
placeholder="Custom placeholder"
|
||||
/>,
|
||||
);
|
||||
expect(screen.queryByText('Custom placeholder')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('calls onChange when a value is selected', () => {
|
||||
render(<YAxisUnitSelector value="" onChange={mockOnChange} />);
|
||||
const select = screen.getByRole('combobox');
|
||||
|
||||
fireEvent.mouseDown(select);
|
||||
const option = screen.getByText('Bytes (B)');
|
||||
fireEvent.click(option);
|
||||
|
||||
expect(mockOnChange).toHaveBeenCalledWith('By', {
|
||||
children: 'Bytes (B)',
|
||||
key: 'By',
|
||||
value: 'By',
|
||||
});
|
||||
});
|
||||
|
||||
it('filters options based on search input', () => {
|
||||
render(<YAxisUnitSelector value="" onChange={mockOnChange} />);
|
||||
const select = screen.getByRole('combobox');
|
||||
|
||||
fireEvent.mouseDown(select);
|
||||
const input = screen.getByRole('combobox');
|
||||
fireEvent.change(input, { target: { value: 'byte' } });
|
||||
|
||||
expect(screen.getByText('Bytes/sec')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows all categories and their units', () => {
|
||||
render(<YAxisUnitSelector value="" onChange={mockOnChange} />);
|
||||
const select = screen.getByRole('combobox');
|
||||
|
||||
fireEvent.mouseDown(select);
|
||||
|
||||
// Check for category headers
|
||||
expect(screen.getByText('Data')).toBeInTheDocument();
|
||||
expect(screen.getByText('Time')).toBeInTheDocument();
|
||||
|
||||
// Check for some common units
|
||||
expect(screen.getByText('Bytes (B)')).toBeInTheDocument();
|
||||
expect(screen.getByText('Seconds (s)')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
import {
|
||||
getUniversalNameFromMetricUnit,
|
||||
mapMetricUnitToUniversalUnit,
|
||||
} from '../utils';
|
||||
|
||||
describe('YAxisUnitSelector utils', () => {
|
||||
describe('mapMetricUnitToUniversalUnit', () => {
|
||||
it('maps known units correctly', () => {
|
||||
expect(mapMetricUnitToUniversalUnit('bytes')).toBe('By');
|
||||
expect(mapMetricUnitToUniversalUnit('seconds')).toBe('s');
|
||||
expect(mapMetricUnitToUniversalUnit('bytes_per_second')).toBe('By/s');
|
||||
});
|
||||
|
||||
it('returns null or self for unknown units', () => {
|
||||
expect(mapMetricUnitToUniversalUnit('unknown_unit')).toBe('unknown_unit');
|
||||
expect(mapMetricUnitToUniversalUnit('')).toBe(null);
|
||||
expect(mapMetricUnitToUniversalUnit(undefined)).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getUniversalNameFromMetricUnit', () => {
|
||||
it('returns human readable names for known units', () => {
|
||||
expect(getUniversalNameFromMetricUnit('bytes')).toBe('Bytes (B)');
|
||||
expect(getUniversalNameFromMetricUnit('seconds')).toBe('Seconds (s)');
|
||||
expect(getUniversalNameFromMetricUnit('bytes_per_second')).toBe('Bytes/sec');
|
||||
});
|
||||
|
||||
it('returns original unit for unknown units', () => {
|
||||
expect(getUniversalNameFromMetricUnit('unknown_unit')).toBe('unknown_unit');
|
||||
expect(getUniversalNameFromMetricUnit('')).toBe('-');
|
||||
expect(getUniversalNameFromMetricUnit(undefined)).toBe('-');
|
||||
});
|
||||
|
||||
it('handles case variations', () => {
|
||||
expect(getUniversalNameFromMetricUnit('bytes')).toBe('Bytes (B)');
|
||||
expect(getUniversalNameFromMetricUnit('s')).toBe('Seconds (s)');
|
||||
});
|
||||
});
|
||||
});
|
||||
549
frontend/src/components/YAxisUnitSelector/constants.ts
Normal file
549
frontend/src/components/YAxisUnitSelector/constants.ts
Normal file
@@ -0,0 +1,549 @@
|
||||
import { UniversalYAxisUnit, YAxisUnit } from './types';
|
||||
|
||||
export const UniversalYAxisUnitMappings: Record<
|
||||
UniversalYAxisUnit,
|
||||
Set<YAxisUnit>
|
||||
> = {
|
||||
[UniversalYAxisUnit.SECONDS]: new Set([
|
||||
YAxisUnit.AWS_SECONDS,
|
||||
YAxisUnit.UCUM_SECONDS,
|
||||
YAxisUnit.OPEN_METRICS_SECONDS,
|
||||
]),
|
||||
[UniversalYAxisUnit.MICROSECONDS]: new Set([
|
||||
YAxisUnit.AWS_MICROSECONDS,
|
||||
YAxisUnit.UCUM_MICROSECONDS,
|
||||
YAxisUnit.OPEN_METRICS_MICROSECONDS,
|
||||
]),
|
||||
[UniversalYAxisUnit.MILLISECONDS]: new Set([
|
||||
YAxisUnit.AWS_MILLISECONDS,
|
||||
YAxisUnit.UCUM_MILLISECONDS,
|
||||
YAxisUnit.OPEN_METRICS_MILLISECONDS,
|
||||
]),
|
||||
[UniversalYAxisUnit.BYTES]: new Set([
|
||||
YAxisUnit.AWS_BYTES,
|
||||
YAxisUnit.UCUM_BYTES,
|
||||
YAxisUnit.OPEN_METRICS_BYTES,
|
||||
]),
|
||||
[UniversalYAxisUnit.KILOBYTES]: new Set([
|
||||
YAxisUnit.AWS_KILOBYTES,
|
||||
YAxisUnit.UCUM_KILOBYTES,
|
||||
YAxisUnit.OPEN_METRICS_KILOBYTES,
|
||||
]),
|
||||
[UniversalYAxisUnit.MEGABYTES]: new Set([
|
||||
YAxisUnit.AWS_MEGABYTES,
|
||||
YAxisUnit.UCUM_MEGABYTES,
|
||||
YAxisUnit.OPEN_METRICS_MEGABYTES,
|
||||
]),
|
||||
[UniversalYAxisUnit.GIGABYTES]: new Set([
|
||||
YAxisUnit.AWS_GIGABYTES,
|
||||
YAxisUnit.UCUM_GIGABYTES,
|
||||
YAxisUnit.OPEN_METRICS_GIGABYTES,
|
||||
]),
|
||||
[UniversalYAxisUnit.TERABYTES]: new Set([
|
||||
YAxisUnit.AWS_TERABYTES,
|
||||
YAxisUnit.UCUM_TERABYTES,
|
||||
YAxisUnit.OPEN_METRICS_TERABYTES,
|
||||
]),
|
||||
[UniversalYAxisUnit.BYTES_SECOND]: new Set([
|
||||
YAxisUnit.AWS_BYTES_SECOND,
|
||||
YAxisUnit.UCUM_BYTES_SECOND,
|
||||
YAxisUnit.OPEN_METRICS_BYTES_SECOND,
|
||||
]),
|
||||
[UniversalYAxisUnit.KILOBYTES_SECOND]: new Set([
|
||||
YAxisUnit.AWS_KILOBYTES_SECOND,
|
||||
YAxisUnit.UCUM_KILOBYTES_SECOND,
|
||||
YAxisUnit.OPEN_METRICS_KILOBYTES_SECOND,
|
||||
]),
|
||||
[UniversalYAxisUnit.MEGABYTES_SECOND]: new Set([
|
||||
YAxisUnit.AWS_MEGABYTES_SECOND,
|
||||
YAxisUnit.UCUM_MEGABYTES_SECOND,
|
||||
YAxisUnit.OPEN_METRICS_MEGABYTES_SECOND,
|
||||
]),
|
||||
[UniversalYAxisUnit.GIGABYTES_SECOND]: new Set([
|
||||
YAxisUnit.AWS_GIGABYTES_SECOND,
|
||||
YAxisUnit.UCUM_GIGABYTES_SECOND,
|
||||
YAxisUnit.OPEN_METRICS_GIGABYTES_SECOND,
|
||||
]),
|
||||
[UniversalYAxisUnit.TERABYTES_SECOND]: new Set([
|
||||
YAxisUnit.AWS_TERABYTES_SECOND,
|
||||
YAxisUnit.UCUM_TERABYTES_SECOND,
|
||||
YAxisUnit.OPEN_METRICS_TERABYTES_SECOND,
|
||||
]),
|
||||
[UniversalYAxisUnit.BITS]: new Set([
|
||||
YAxisUnit.AWS_BITS,
|
||||
YAxisUnit.UCUM_BITS,
|
||||
YAxisUnit.OPEN_METRICS_BITS,
|
||||
]),
|
||||
[UniversalYAxisUnit.KILOBITS]: new Set([
|
||||
YAxisUnit.AWS_KILOBITS,
|
||||
YAxisUnit.UCUM_KILOBITS,
|
||||
YAxisUnit.OPEN_METRICS_KILOBITS,
|
||||
]),
|
||||
[UniversalYAxisUnit.MEGABITS]: new Set([
|
||||
YAxisUnit.AWS_MEGABITS,
|
||||
YAxisUnit.UCUM_MEGABITS,
|
||||
YAxisUnit.OPEN_METRICS_MEGABITS,
|
||||
]),
|
||||
[UniversalYAxisUnit.GIGABITS]: new Set([
|
||||
YAxisUnit.AWS_GIGABITS,
|
||||
YAxisUnit.UCUM_GIGABITS,
|
||||
YAxisUnit.OPEN_METRICS_GIGABITS,
|
||||
]),
|
||||
[UniversalYAxisUnit.TERABITS]: new Set([
|
||||
YAxisUnit.AWS_TERABITS,
|
||||
YAxisUnit.UCUM_TERABITS,
|
||||
YAxisUnit.OPEN_METRICS_TERABITS,
|
||||
]),
|
||||
[UniversalYAxisUnit.BITS_SECOND]: new Set([
|
||||
YAxisUnit.AWS_BITS_SECOND,
|
||||
YAxisUnit.UCUM_BITS_SECOND,
|
||||
YAxisUnit.OPEN_METRICS_BITS_SECOND,
|
||||
]),
|
||||
[UniversalYAxisUnit.KILOBITS_SECOND]: new Set([
|
||||
YAxisUnit.AWS_KILOBITS_SECOND,
|
||||
YAxisUnit.UCUM_KILOBITS_SECOND,
|
||||
YAxisUnit.OPEN_METRICS_KILOBITS_SECOND,
|
||||
]),
|
||||
[UniversalYAxisUnit.MEGABITS_SECOND]: new Set([
|
||||
YAxisUnit.AWS_MEGABITS_SECOND,
|
||||
YAxisUnit.UCUM_MEGABITS_SECOND,
|
||||
YAxisUnit.OPEN_METRICS_MEGABITS_SECOND,
|
||||
]),
|
||||
[UniversalYAxisUnit.GIGABITS_SECOND]: new Set([
|
||||
YAxisUnit.AWS_GIGABITS_SECOND,
|
||||
YAxisUnit.UCUM_GIGABITS_SECOND,
|
||||
YAxisUnit.OPEN_METRICS_GIGABITS_SECOND,
|
||||
]),
|
||||
[UniversalYAxisUnit.TERABITS_SECOND]: new Set([
|
||||
YAxisUnit.AWS_TERABITS_SECOND,
|
||||
YAxisUnit.UCUM_TERABITS_SECOND,
|
||||
YAxisUnit.OPEN_METRICS_TERABITS_SECOND,
|
||||
]),
|
||||
[UniversalYAxisUnit.COUNT]: new Set([
|
||||
YAxisUnit.AWS_COUNT,
|
||||
YAxisUnit.UCUM_COUNT,
|
||||
YAxisUnit.OPEN_METRICS_COUNT,
|
||||
]),
|
||||
[UniversalYAxisUnit.COUNT_SECOND]: new Set([
|
||||
YAxisUnit.AWS_COUNT_SECOND,
|
||||
YAxisUnit.UCUM_COUNT_SECOND,
|
||||
YAxisUnit.OPEN_METRICS_COUNT_SECOND,
|
||||
]),
|
||||
[UniversalYAxisUnit.PERCENT]: new Set([
|
||||
YAxisUnit.AWS_PERCENT,
|
||||
YAxisUnit.UCUM_PERCENT,
|
||||
YAxisUnit.OPEN_METRICS_PERCENT,
|
||||
]),
|
||||
[UniversalYAxisUnit.NONE]: new Set([
|
||||
YAxisUnit.AWS_NONE,
|
||||
YAxisUnit.UCUM_NONE,
|
||||
YAxisUnit.OPEN_METRICS_NONE,
|
||||
]),
|
||||
[UniversalYAxisUnit.DAYS]: new Set([
|
||||
YAxisUnit.UCUM_DAYS,
|
||||
YAxisUnit.OPEN_METRICS_DAYS,
|
||||
]),
|
||||
[UniversalYAxisUnit.HOURS]: new Set([
|
||||
YAxisUnit.UCUM_HOURS,
|
||||
YAxisUnit.OPEN_METRICS_HOURS,
|
||||
]),
|
||||
[UniversalYAxisUnit.MINUTES]: new Set([
|
||||
YAxisUnit.UCUM_MINUTES,
|
||||
YAxisUnit.OPEN_METRICS_MINUTES,
|
||||
]),
|
||||
[UniversalYAxisUnit.NANOSECONDS]: new Set([
|
||||
YAxisUnit.UCUM_NANOSECONDS,
|
||||
YAxisUnit.OPEN_METRICS_NANOSECONDS,
|
||||
]),
|
||||
[UniversalYAxisUnit.PETABYTES]: new Set([
|
||||
YAxisUnit.UCUM_PEBIBYTES,
|
||||
YAxisUnit.OPEN_METRICS_PEBIBYTES,
|
||||
]),
|
||||
[UniversalYAxisUnit.PETABYTES_SECOND]: new Set([
|
||||
YAxisUnit.UCUM_PEBIBYTES_SECOND,
|
||||
YAxisUnit.OPEN_METRICS_PEBIBYTES_SECOND,
|
||||
]),
|
||||
[UniversalYAxisUnit.PETABITS]: new Set([YAxisUnit.UCUM_PETABITS]),
|
||||
[UniversalYAxisUnit.PETABITS_SECOND]: new Set([
|
||||
YAxisUnit.UCUM_PEBIBITS_SECOND,
|
||||
YAxisUnit.OPEN_METRICS_PEBIBITS_SECOND,
|
||||
]),
|
||||
[UniversalYAxisUnit.COUNT_MINUTE]: new Set([
|
||||
YAxisUnit.UCUM_COUNTS_MINUTE,
|
||||
YAxisUnit.OPEN_METRICS_COUNTS_MINUTE,
|
||||
]),
|
||||
[UniversalYAxisUnit.OPS_SECOND]: new Set([
|
||||
YAxisUnit.UCUM_OPS_SECOND,
|
||||
YAxisUnit.OPEN_METRICS_OPS_SECOND,
|
||||
]),
|
||||
[UniversalYAxisUnit.OPS_MINUTE]: new Set([
|
||||
YAxisUnit.UCUM_OPS_MINUTE,
|
||||
YAxisUnit.OPEN_METRICS_OPS_MINUTE,
|
||||
]),
|
||||
[UniversalYAxisUnit.REQUESTS_SECOND]: new Set([
|
||||
YAxisUnit.UCUM_REQUESTS_SECOND,
|
||||
YAxisUnit.OPEN_METRICS_REQUESTS_SECOND,
|
||||
]),
|
||||
[UniversalYAxisUnit.READS_SECOND]: new Set([
|
||||
YAxisUnit.UCUM_READS_SECOND,
|
||||
YAxisUnit.OPEN_METRICS_READS_SECOND,
|
||||
]),
|
||||
[UniversalYAxisUnit.WRITES_SECOND]: new Set([
|
||||
YAxisUnit.UCUM_WRITES_SECOND,
|
||||
YAxisUnit.OPEN_METRICS_WRITES_SECOND,
|
||||
]),
|
||||
[UniversalYAxisUnit.READS_MINUTE]: new Set([
|
||||
YAxisUnit.UCUM_READS_MINUTE,
|
||||
YAxisUnit.OPEN_METRICS_READS_MINUTE,
|
||||
]),
|
||||
[UniversalYAxisUnit.WRITES_MINUTE]: new Set([
|
||||
YAxisUnit.UCUM_WRITES_MINUTE,
|
||||
YAxisUnit.OPEN_METRICS_WRITES_MINUTE,
|
||||
]),
|
||||
[UniversalYAxisUnit.IOOPS_SECOND]: new Set([
|
||||
YAxisUnit.UCUM_IOPS_SECOND,
|
||||
YAxisUnit.OPEN_METRICS_IOPS_SECOND,
|
||||
]),
|
||||
[UniversalYAxisUnit.PERCENT_UNIT]: new Set([
|
||||
YAxisUnit.OPEN_METRICS_PERCENT_UNIT,
|
||||
]),
|
||||
};
|
||||
|
||||
export const Y_AXIS_UNIT_NAMES: Record<UniversalYAxisUnit, string> = {
|
||||
[UniversalYAxisUnit.SECONDS]: 'Seconds (s)',
|
||||
[UniversalYAxisUnit.MILLISECONDS]: 'Milliseconds (ms)',
|
||||
[UniversalYAxisUnit.MICROSECONDS]: 'Microseconds (µs)',
|
||||
[UniversalYAxisUnit.BYTES]: 'Bytes (B)',
|
||||
[UniversalYAxisUnit.KILOBYTES]: 'Kilobytes (KB)',
|
||||
[UniversalYAxisUnit.MEGABYTES]: 'Megabytes (MB)',
|
||||
[UniversalYAxisUnit.GIGABYTES]: 'Gigabytes (GB)',
|
||||
[UniversalYAxisUnit.TERABYTES]: 'Terabytes (TB)',
|
||||
[UniversalYAxisUnit.PETABYTES]: 'Petabytes (PB)',
|
||||
[UniversalYAxisUnit.BITS]: 'Bits (b)',
|
||||
[UniversalYAxisUnit.KILOBITS]: 'Kilobits (Kb)',
|
||||
[UniversalYAxisUnit.MEGABITS]: 'Megabits (Mb)',
|
||||
[UniversalYAxisUnit.GIGABITS]: 'Gigabits (Gb)',
|
||||
[UniversalYAxisUnit.TERABITS]: 'Terabits (Tb)',
|
||||
[UniversalYAxisUnit.PETABITS]: 'Petabits (Pb)',
|
||||
[UniversalYAxisUnit.BYTES_SECOND]: 'Bytes/sec',
|
||||
[UniversalYAxisUnit.KILOBYTES_SECOND]: 'Kilobytes/sec',
|
||||
[UniversalYAxisUnit.MEGABYTES_SECOND]: 'Megabytes/sec',
|
||||
[UniversalYAxisUnit.GIGABYTES_SECOND]: 'Gigabytes/sec',
|
||||
[UniversalYAxisUnit.TERABYTES_SECOND]: 'Terabytes/sec',
|
||||
[UniversalYAxisUnit.PETABYTES_SECOND]: 'Petabytes/sec',
|
||||
[UniversalYAxisUnit.BITS_SECOND]: 'Bits/sec',
|
||||
[UniversalYAxisUnit.KILOBITS_SECOND]: 'Kilobits/sec',
|
||||
[UniversalYAxisUnit.MEGABITS_SECOND]: 'Megabits/sec',
|
||||
[UniversalYAxisUnit.GIGABITS_SECOND]: 'Gigabits/sec',
|
||||
[UniversalYAxisUnit.TERABITS_SECOND]: 'Terabits/sec',
|
||||
[UniversalYAxisUnit.PETABITS_SECOND]: 'Petabits/sec',
|
||||
[UniversalYAxisUnit.COUNT]: 'Count',
|
||||
[UniversalYAxisUnit.COUNT_SECOND]: 'Count/sec',
|
||||
[UniversalYAxisUnit.PERCENT]: 'Percent (0 - 100)',
|
||||
[UniversalYAxisUnit.NONE]: 'None',
|
||||
[UniversalYAxisUnit.DAYS]: 'Days',
|
||||
[UniversalYAxisUnit.HOURS]: 'Hours',
|
||||
[UniversalYAxisUnit.MINUTES]: 'Minutes',
|
||||
[UniversalYAxisUnit.NANOSECONDS]: 'Nanoseconds',
|
||||
[UniversalYAxisUnit.COUNT_MINUTE]: 'Count/min',
|
||||
[UniversalYAxisUnit.OPS_SECOND]: 'Ops/sec',
|
||||
[UniversalYAxisUnit.OPS_MINUTE]: 'Ops/min',
|
||||
[UniversalYAxisUnit.REQUESTS_SECOND]: 'Requests/sec',
|
||||
[UniversalYAxisUnit.READS_SECOND]: 'Reads/sec',
|
||||
[UniversalYAxisUnit.WRITES_SECOND]: 'Writes/sec',
|
||||
[UniversalYAxisUnit.READS_MINUTE]: 'Reads/min',
|
||||
[UniversalYAxisUnit.WRITES_MINUTE]: 'Writes/min',
|
||||
[UniversalYAxisUnit.IOOPS_SECOND]: 'IOPS/sec',
|
||||
[UniversalYAxisUnit.PERCENT_UNIT]: 'Percent (0.0 - 1.0)',
|
||||
};
|
||||
|
||||
export const Y_AXIS_CATEGORIES = [
|
||||
{
|
||||
name: 'Time ',
|
||||
units: [
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.SECONDS],
|
||||
id: UniversalYAxisUnit.SECONDS,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.MILLISECONDS],
|
||||
id: UniversalYAxisUnit.MILLISECONDS,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.MICROSECONDS],
|
||||
id: UniversalYAxisUnit.MICROSECONDS,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.NANOSECONDS],
|
||||
id: UniversalYAxisUnit.NANOSECONDS,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.MINUTES],
|
||||
id: UniversalYAxisUnit.MINUTES,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.HOURS],
|
||||
id: UniversalYAxisUnit.HOURS,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.DAYS],
|
||||
id: UniversalYAxisUnit.DAYS,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Data ',
|
||||
units: [
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.BYTES],
|
||||
id: UniversalYAxisUnit.BYTES,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.KILOBYTES],
|
||||
id: UniversalYAxisUnit.KILOBYTES,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.MEGABYTES],
|
||||
id: UniversalYAxisUnit.MEGABYTES,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.GIGABYTES],
|
||||
id: UniversalYAxisUnit.GIGABYTES,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.TERABYTES],
|
||||
id: UniversalYAxisUnit.TERABYTES,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.PETABYTES],
|
||||
id: UniversalYAxisUnit.PETABYTES,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.BITS],
|
||||
id: UniversalYAxisUnit.BITS,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.KILOBITS],
|
||||
id: UniversalYAxisUnit.KILOBITS,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.MEGABITS],
|
||||
id: UniversalYAxisUnit.MEGABITS,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.GIGABITS],
|
||||
id: UniversalYAxisUnit.GIGABITS,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.TERABITS],
|
||||
id: UniversalYAxisUnit.TERABITS,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.PETABITS],
|
||||
id: UniversalYAxisUnit.PETABITS,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Data Rate',
|
||||
units: [
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.BYTES_SECOND],
|
||||
id: UniversalYAxisUnit.BYTES_SECOND,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.KILOBYTES_SECOND],
|
||||
id: UniversalYAxisUnit.KILOBYTES_SECOND,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.MEGABYTES_SECOND],
|
||||
id: UniversalYAxisUnit.MEGABYTES_SECOND,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.GIGABYTES_SECOND],
|
||||
id: UniversalYAxisUnit.GIGABYTES_SECOND,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.TERABYTES_SECOND],
|
||||
id: UniversalYAxisUnit.TERABYTES_SECOND,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.PETABYTES_SECOND],
|
||||
id: UniversalYAxisUnit.PETABYTES_SECOND,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.BITS_SECOND],
|
||||
id: UniversalYAxisUnit.BITS_SECOND,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.KILOBITS_SECOND],
|
||||
id: UniversalYAxisUnit.KILOBITS_SECOND,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.MEGABITS_SECOND],
|
||||
id: UniversalYAxisUnit.MEGABITS_SECOND,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.GIGABITS_SECOND],
|
||||
id: UniversalYAxisUnit.GIGABITS_SECOND,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.TERABITS_SECOND],
|
||||
id: UniversalYAxisUnit.TERABITS_SECOND,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.PETABITS_SECOND],
|
||||
id: UniversalYAxisUnit.PETABITS_SECOND,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Count',
|
||||
units: [
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.COUNT],
|
||||
id: UniversalYAxisUnit.COUNT,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.COUNT_SECOND],
|
||||
id: UniversalYAxisUnit.COUNT_SECOND,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.COUNT_MINUTE],
|
||||
id: UniversalYAxisUnit.COUNT_MINUTE,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Operations',
|
||||
units: [
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.OPS_SECOND],
|
||||
id: UniversalYAxisUnit.OPS_SECOND,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.OPS_MINUTE],
|
||||
id: UniversalYAxisUnit.OPS_MINUTE,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.REQUESTS_SECOND],
|
||||
id: UniversalYAxisUnit.REQUESTS_SECOND,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.READS_SECOND],
|
||||
id: UniversalYAxisUnit.READS_SECOND,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.WRITES_SECOND],
|
||||
id: UniversalYAxisUnit.WRITES_SECOND,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.READS_MINUTE],
|
||||
id: UniversalYAxisUnit.READS_MINUTE,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.WRITES_MINUTE],
|
||||
id: UniversalYAxisUnit.WRITES_MINUTE,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.IOOPS_SECOND],
|
||||
id: UniversalYAxisUnit.IOOPS_SECOND,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Percentage',
|
||||
units: [
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.PERCENT],
|
||||
id: UniversalYAxisUnit.PERCENT,
|
||||
},
|
||||
{
|
||||
name: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.PERCENT_UNIT],
|
||||
id: UniversalYAxisUnit.PERCENT_UNIT,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const UniversalUnitToGrafanaUnit: Record<UniversalYAxisUnit, string> = {
|
||||
// Time
|
||||
[UniversalYAxisUnit.DAYS]: 'd',
|
||||
[UniversalYAxisUnit.HOURS]: 'h',
|
||||
[UniversalYAxisUnit.MINUTES]: 'm',
|
||||
[UniversalYAxisUnit.SECONDS]: 's',
|
||||
[UniversalYAxisUnit.MILLISECONDS]: 'ms',
|
||||
[UniversalYAxisUnit.MICROSECONDS]: 'µs',
|
||||
[UniversalYAxisUnit.NANOSECONDS]: 'ns',
|
||||
|
||||
// Data (Grafana uses 1024-based IEC format)
|
||||
[UniversalYAxisUnit.BYTES]: 'decbytes',
|
||||
[UniversalYAxisUnit.KILOBYTES]: 'deckbytes',
|
||||
[UniversalYAxisUnit.MEGABYTES]: 'decmbytes',
|
||||
[UniversalYAxisUnit.GIGABYTES]: 'decgbytes',
|
||||
[UniversalYAxisUnit.TERABYTES]: 'dectbytes',
|
||||
[UniversalYAxisUnit.PETABYTES]: 'decpbytes',
|
||||
|
||||
// Data Rate
|
||||
[UniversalYAxisUnit.BYTES_SECOND]: 'Bps',
|
||||
[UniversalYAxisUnit.KILOBYTES_SECOND]: 'KBs',
|
||||
[UniversalYAxisUnit.MEGABYTES_SECOND]: 'MBs',
|
||||
[UniversalYAxisUnit.GIGABYTES_SECOND]: 'GBs',
|
||||
[UniversalYAxisUnit.TERABYTES_SECOND]: 'TBs',
|
||||
[UniversalYAxisUnit.PETABYTES_SECOND]: 'PBs',
|
||||
|
||||
// Bits
|
||||
[UniversalYAxisUnit.BITS]: 'bits',
|
||||
[UniversalYAxisUnit.KILOBITS]: 'kbytes',
|
||||
[UniversalYAxisUnit.MEGABITS]: 'mbytes',
|
||||
[UniversalYAxisUnit.GIGABITS]: 'gbytes',
|
||||
[UniversalYAxisUnit.TERABITS]: 'tbytes',
|
||||
[UniversalYAxisUnit.PETABITS]: 'pbytes',
|
||||
|
||||
// Bit Rate
|
||||
[UniversalYAxisUnit.BITS_SECOND]: 'bps',
|
||||
[UniversalYAxisUnit.KILOBITS_SECOND]: 'Kbits',
|
||||
[UniversalYAxisUnit.MEGABITS_SECOND]: 'Mbits',
|
||||
[UniversalYAxisUnit.GIGABITS_SECOND]: 'Gbits',
|
||||
[UniversalYAxisUnit.TERABITS_SECOND]: 'Tbits',
|
||||
[UniversalYAxisUnit.PETABITS_SECOND]: 'Pbits',
|
||||
|
||||
// Count
|
||||
[UniversalYAxisUnit.COUNT]: 'short',
|
||||
[UniversalYAxisUnit.COUNT_SECOND]: 'cps',
|
||||
[UniversalYAxisUnit.COUNT_MINUTE]: 'cpm',
|
||||
|
||||
// Operations
|
||||
[UniversalYAxisUnit.OPS_SECOND]: 'ops',
|
||||
[UniversalYAxisUnit.OPS_MINUTE]: 'opm',
|
||||
|
||||
// Requests
|
||||
[UniversalYAxisUnit.REQUESTS_SECOND]: 'reqps',
|
||||
|
||||
// Reads/Writes
|
||||
[UniversalYAxisUnit.READS_SECOND]: 'rps',
|
||||
[UniversalYAxisUnit.WRITES_SECOND]: 'wps',
|
||||
[UniversalYAxisUnit.READS_MINUTE]: 'rpm',
|
||||
[UniversalYAxisUnit.WRITES_MINUTE]: 'wpm',
|
||||
|
||||
// IO Operations
|
||||
[UniversalYAxisUnit.IOOPS_SECOND]: 'iops',
|
||||
|
||||
// Percent
|
||||
[UniversalYAxisUnit.PERCENT]: 'percent',
|
||||
[UniversalYAxisUnit.PERCENT_UNIT]: 'percentunit',
|
||||
|
||||
// None
|
||||
[UniversalYAxisUnit.NONE]: 'none',
|
||||
};
|
||||
|
||||
export const CustomGraphUnitToUniversalUnit: Record<
|
||||
string,
|
||||
UniversalYAxisUnit
|
||||
> = {
|
||||
KiB: UniversalYAxisUnit.KILOBITS,
|
||||
MiB: UniversalYAxisUnit.MEGABITS,
|
||||
GiB: UniversalYAxisUnit.GIGABITS,
|
||||
TiB: UniversalYAxisUnit.TERABITS,
|
||||
PiB: UniversalYAxisUnit.PETABITS,
|
||||
};
|
||||
3
frontend/src/components/YAxisUnitSelector/index.ts
Normal file
3
frontend/src/components/YAxisUnitSelector/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import YAxisUnitSelector from './YAxisUnitSelector';
|
||||
|
||||
export default YAxisUnitSelector;
|
||||
5
frontend/src/components/YAxisUnitSelector/styles.scss
Normal file
5
frontend/src/components/YAxisUnitSelector/styles.scss
Normal file
@@ -0,0 +1,5 @@
|
||||
.y-axis-unit-selector-component {
|
||||
.ant-select {
|
||||
width: 220px;
|
||||
}
|
||||
}
|
||||
286
frontend/src/components/YAxisUnitSelector/types.ts
Normal file
286
frontend/src/components/YAxisUnitSelector/types.ts
Normal file
@@ -0,0 +1,286 @@
|
||||
export interface YAxisUnitSelectorProps {
|
||||
value: string | undefined;
|
||||
onChange: (value: UniversalYAxisUnit) => void;
|
||||
placeholder?: string;
|
||||
loading?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export enum UniversalYAxisUnit {
|
||||
// Time
|
||||
DAYS = 'd',
|
||||
HOURS = 'h',
|
||||
MINUTES = 'min',
|
||||
SECONDS = 's',
|
||||
MICROSECONDS = 'us',
|
||||
MILLISECONDS = 'ms',
|
||||
NANOSECONDS = 'ns',
|
||||
|
||||
// Data
|
||||
BYTES = 'By',
|
||||
KILOBYTES = 'kBy',
|
||||
MEGABYTES = 'MBy',
|
||||
GIGABYTES = 'GBy',
|
||||
TERABYTES = 'TBy',
|
||||
PETABYTES = 'PBy',
|
||||
|
||||
// Data Rate
|
||||
BYTES_SECOND = 'By/s',
|
||||
KILOBYTES_SECOND = 'kBy/s',
|
||||
MEGABYTES_SECOND = 'MBy/s',
|
||||
GIGABYTES_SECOND = 'GBy/s',
|
||||
TERABYTES_SECOND = 'TBy/s',
|
||||
PETABYTES_SECOND = 'PBy/s',
|
||||
|
||||
// Bits
|
||||
BITS = 'bit',
|
||||
KILOBITS = 'kbit',
|
||||
MEGABITS = 'Mbit',
|
||||
GIGABITS = 'Gbit',
|
||||
TERABITS = 'Tbit',
|
||||
PETABITS = 'Pbit',
|
||||
|
||||
// Bit Rate
|
||||
BITS_SECOND = 'bit/s',
|
||||
KILOBITS_SECOND = 'kbit/s',
|
||||
MEGABITS_SECOND = 'Mbit/s',
|
||||
GIGABITS_SECOND = 'Gbit/s',
|
||||
TERABITS_SECOND = 'Tbit/s',
|
||||
PETABITS_SECOND = 'Pbit/s',
|
||||
|
||||
// Count
|
||||
COUNT = '{count}',
|
||||
COUNT_SECOND = '{count}/s',
|
||||
COUNT_MINUTE = '{count}/min',
|
||||
|
||||
// Operations
|
||||
OPS_SECOND = '{ops}/s',
|
||||
OPS_MINUTE = '{ops}/min',
|
||||
|
||||
// Requests
|
||||
REQUESTS_SECOND = '{req}/s',
|
||||
|
||||
// Reads/Writes
|
||||
READS_SECOND = '{read}/s',
|
||||
WRITES_SECOND = '{write}/s',
|
||||
READS_MINUTE = '{read}/min',
|
||||
WRITES_MINUTE = '{write}/min',
|
||||
|
||||
// IO Operations
|
||||
IOOPS_SECOND = '{iops}/s',
|
||||
|
||||
// Percent
|
||||
PERCENT = '%',
|
||||
PERCENT_UNIT = 'percentunit',
|
||||
NONE = '1',
|
||||
}
|
||||
|
||||
export enum YAxisUnit {
|
||||
AWS_SECONDS = 'Seconds',
|
||||
UCUM_SECONDS = 's',
|
||||
OPEN_METRICS_SECONDS = 'seconds',
|
||||
|
||||
AWS_MICROSECONDS = 'Microseconds',
|
||||
UCUM_MICROSECONDS = 'us',
|
||||
OPEN_METRICS_MICROSECONDS = 'microseconds',
|
||||
|
||||
AWS_MILLISECONDS = 'Milliseconds',
|
||||
UCUM_MILLISECONDS = 'ms',
|
||||
OPEN_METRICS_MILLISECONDS = 'milliseconds',
|
||||
|
||||
AWS_BYTES = 'Bytes',
|
||||
UCUM_BYTES = 'By',
|
||||
OPEN_METRICS_BYTES = 'bytes',
|
||||
|
||||
AWS_KILOBYTES = 'Kilobytes',
|
||||
UCUM_KILOBYTES = 'kBy',
|
||||
OPEN_METRICS_KILOBYTES = 'kilobytes',
|
||||
|
||||
AWS_MEGABYTES = 'Megabytes',
|
||||
UCUM_MEGABYTES = 'MBy',
|
||||
OPEN_METRICS_MEGABYTES = 'megabytes',
|
||||
|
||||
AWS_GIGABYTES = 'Gigabytes',
|
||||
UCUM_GIGABYTES = 'GBy',
|
||||
OPEN_METRICS_GIGABYTES = 'gigabytes',
|
||||
|
||||
AWS_TERABYTES = 'Terabytes',
|
||||
UCUM_TERABYTES = 'TBy',
|
||||
OPEN_METRICS_TERABYTES = 'terabytes',
|
||||
|
||||
AWS_BYTES_SECOND = 'Bytes/Second',
|
||||
UCUM_BYTES_SECOND = 'By/s',
|
||||
OPEN_METRICS_BYTES_SECOND = 'bytes_per_second',
|
||||
|
||||
AWS_KILOBYTES_SECOND = 'Kilobytes/Second',
|
||||
UCUM_KILOBYTES_SECOND = 'kBy/s',
|
||||
OPEN_METRICS_KILOBYTES_SECOND = 'kilobytes_per_second',
|
||||
|
||||
AWS_MEGABYTES_SECOND = 'Megabytes/Second',
|
||||
UCUM_MEGABYTES_SECOND = 'MBy/s',
|
||||
OPEN_METRICS_MEGABYTES_SECOND = 'megabytes_per_second',
|
||||
|
||||
AWS_GIGABYTES_SECOND = 'Gigabytes/Second',
|
||||
UCUM_GIGABYTES_SECOND = 'GBy/s',
|
||||
OPEN_METRICS_GIGABYTES_SECOND = 'gigabytes_per_second',
|
||||
|
||||
AWS_TERABYTES_SECOND = 'Terabytes/Second',
|
||||
UCUM_TERABYTES_SECOND = 'TBy/s',
|
||||
OPEN_METRICS_TERABYTES_SECOND = 'terabytes_per_second',
|
||||
|
||||
AWS_BITS = 'Bits',
|
||||
UCUM_BITS = 'bit',
|
||||
OPEN_METRICS_BITS = 'bits',
|
||||
|
||||
AWS_KILOBITS = 'Kilobits',
|
||||
UCUM_KILOBITS = 'kbit',
|
||||
OPEN_METRICS_KILOBITS = 'kilobits',
|
||||
|
||||
AWS_MEGABITS = 'Megabits',
|
||||
UCUM_MEGABITS = 'Mbit',
|
||||
OPEN_METRICS_MEGABITS = 'megabits',
|
||||
|
||||
AWS_GIGABITS = 'Gigabits',
|
||||
UCUM_GIGABITS = 'Gbit',
|
||||
OPEN_METRICS_GIGABITS = 'gigabits',
|
||||
|
||||
AWS_TERABITS = 'Terabits',
|
||||
UCUM_TERABITS = 'Tbit',
|
||||
OPEN_METRICS_TERABITS = 'terabits',
|
||||
|
||||
UCUM_PETABITS = 'Pbit',
|
||||
|
||||
AWS_BITS_SECOND = 'Bits/Second',
|
||||
UCUM_BITS_SECOND = 'bit/s',
|
||||
OPEN_METRICS_BITS_SECOND = 'bits_per_second',
|
||||
|
||||
AWS_KILOBITS_SECOND = 'Kilobits/Second',
|
||||
UCUM_KILOBITS_SECOND = 'kbit/s',
|
||||
OPEN_METRICS_KILOBITS_SECOND = 'kilobits_per_second',
|
||||
|
||||
AWS_MEGABITS_SECOND = 'Megabits/Second',
|
||||
UCUM_MEGABITS_SECOND = 'Mbit/s',
|
||||
OPEN_METRICS_MEGABITS_SECOND = 'megabits_per_second',
|
||||
|
||||
AWS_GIGABITS_SECOND = 'Gigabits/Second',
|
||||
UCUM_GIGABITS_SECOND = 'Gbit/s',
|
||||
OPEN_METRICS_GIGABITS_SECOND = 'gigabits_per_second',
|
||||
|
||||
AWS_TERABITS_SECOND = 'Terabits/Second',
|
||||
UCUM_TERABITS_SECOND = 'Tbit/s',
|
||||
OPEN_METRICS_TERABITS_SECOND = 'terabits_per_second',
|
||||
|
||||
AWS_COUNT = 'Count',
|
||||
UCUM_COUNT = '{count}',
|
||||
OPEN_METRICS_COUNT = 'count',
|
||||
|
||||
AWS_COUNT_SECOND = 'Count/Second',
|
||||
UCUM_COUNT_SECOND = '{count}/s',
|
||||
OPEN_METRICS_COUNT_SECOND = 'count_per_second',
|
||||
|
||||
AWS_PERCENT = 'Percent',
|
||||
UCUM_PERCENT = '%',
|
||||
OPEN_METRICS_PERCENT = 'ratio',
|
||||
|
||||
AWS_NONE = 'None',
|
||||
UCUM_NONE = '1',
|
||||
OPEN_METRICS_NONE = 'none',
|
||||
|
||||
UCUM_NANOSECONDS = 'ns',
|
||||
OPEN_METRICS_NANOSECONDS = 'nanoseconds',
|
||||
|
||||
UCUM_MINUTES = 'min',
|
||||
OPEN_METRICS_MINUTES = 'minutes',
|
||||
|
||||
UCUM_HOURS = 'h',
|
||||
OPEN_METRICS_HOURS = 'hours',
|
||||
|
||||
UCUM_DAYS = 'd',
|
||||
OPEN_METRICS_DAYS = 'days',
|
||||
|
||||
UCUM_KIBIBYTES = 'KiBy',
|
||||
OPEN_METRICS_KIBIBYTES = 'kibibytes',
|
||||
|
||||
UCUM_MEBIBYTES = 'MiBy',
|
||||
OPEN_METRICS_MEBIBYTES = 'mebibytes',
|
||||
|
||||
UCUM_GIBIBYTES = 'GiBy',
|
||||
OPEN_METRICS_GIBIBYTES = 'gibibytes',
|
||||
|
||||
UCUM_TEBIBYTES = 'TiBy',
|
||||
OPEN_METRICS_TEBIBYTES = 'tebibytes',
|
||||
|
||||
UCUM_PEBIBYTES = 'PiBy',
|
||||
OPEN_METRICS_PEBIBYTES = 'pebibytes',
|
||||
|
||||
UCUM_KIBIBYTES_SECOND = 'KiBy/s',
|
||||
OPEN_METRICS_KIBIBYTES_SECOND = 'kibibytes_per_second',
|
||||
|
||||
UCUM_KIBIBITS_SECOND = 'Kibit/s',
|
||||
OPEN_METRICS_KIBIBITS_SECOND = 'kibibits_per_second',
|
||||
|
||||
UCUM_MEBIBYTES_SECOND = 'MiBy/s',
|
||||
OPEN_METRICS_MEBIBYTES_SECOND = 'mebibytes_per_second',
|
||||
|
||||
UCUM_MEBIBITS_SECOND = 'Mibit/s',
|
||||
OPEN_METRICS_MEBIBITS_SECOND = 'mebibits_per_second',
|
||||
|
||||
UCUM_GIBIBYTES_SECOND = 'GiBy/s',
|
||||
OPEN_METRICS_GIBIBYTES_SECOND = 'gibibytes_per_second',
|
||||
|
||||
UCUM_GIBIBITS_SECOND = 'Gibit/s',
|
||||
OPEN_METRICS_GIBIBITS_SECOND = 'gibibits_per_second',
|
||||
|
||||
UCUM_TEBIBYTES_SECOND = 'TiBy/s',
|
||||
OPEN_METRICS_TEBIBYTES_SECOND = 'tebibytes_per_second',
|
||||
|
||||
UCUM_TEBIBITS_SECOND = 'Tibit/s',
|
||||
OPEN_METRICS_TEBIBITS_SECOND = 'tebibits_per_second',
|
||||
|
||||
UCUM_PEBIBYTES_SECOND = 'PiBy/s',
|
||||
OPEN_METRICS_PEBIBYTES_SECOND = 'pebibytes_per_second',
|
||||
|
||||
UCUM_PEBIBITS_SECOND = 'Pibit/s',
|
||||
OPEN_METRICS_PEBIBITS_SECOND = 'pebibits_per_second',
|
||||
|
||||
UCUM_TRUE_FALSE = '{bool}',
|
||||
OPEN_METRICS_TRUE_FALSE = 'boolean_true_false',
|
||||
|
||||
UCUM_YES_NO = '{bool}',
|
||||
OPEN_METRICS_YES_NO = 'boolean_yes_no',
|
||||
|
||||
UCUM_COUNTS_SECOND = '{count}/s',
|
||||
OPEN_METRICS_COUNTS_SECOND = 'counts_per_second',
|
||||
|
||||
UCUM_OPS_SECOND = '{ops}/s',
|
||||
OPEN_METRICS_OPS_SECOND = 'ops_per_second',
|
||||
|
||||
UCUM_REQUESTS_SECOND = '{requests}/s',
|
||||
OPEN_METRICS_REQUESTS_SECOND = 'requests_per_second',
|
||||
|
||||
UCUM_READS_SECOND = '{reads}/s',
|
||||
OPEN_METRICS_READS_SECOND = 'reads_per_second',
|
||||
|
||||
UCUM_WRITES_SECOND = '{writes}/s',
|
||||
OPEN_METRICS_WRITES_SECOND = 'writes_per_second',
|
||||
|
||||
UCUM_IOPS_SECOND = '{iops}/s',
|
||||
OPEN_METRICS_IOPS_SECOND = 'io_ops_per_second',
|
||||
|
||||
UCUM_COUNTS_MINUTE = '{count}/min',
|
||||
OPEN_METRICS_COUNTS_MINUTE = 'counts_per_minute',
|
||||
|
||||
UCUM_OPS_MINUTE = '{ops}/min',
|
||||
OPEN_METRICS_OPS_MINUTE = 'ops_per_minute',
|
||||
|
||||
UCUM_READS_MINUTE = '{reads}/min',
|
||||
OPEN_METRICS_READS_MINUTE = 'reads_per_minute',
|
||||
|
||||
UCUM_WRITES_MINUTE = '{writes}/min',
|
||||
OPEN_METRICS_WRITES_MINUTE = 'writes_per_minute',
|
||||
|
||||
UCUM_PETABYTES = 'PBy',
|
||||
OPEN_METRICS_PETABYTES = 'petabytes',
|
||||
|
||||
OPEN_METRICS_PERCENT_UNIT = 'percentunit',
|
||||
}
|
||||
33
frontend/src/components/YAxisUnitSelector/utils.tsx
Normal file
33
frontend/src/components/YAxisUnitSelector/utils.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { UniversalYAxisUnitMappings, Y_AXIS_UNIT_NAMES } from './constants';
|
||||
import { UniversalYAxisUnit, YAxisUnit } from './types';
|
||||
|
||||
export const mapMetricUnitToUniversalUnit = (
|
||||
unit: string | undefined,
|
||||
): UniversalYAxisUnit | null => {
|
||||
if (!unit) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const universalUnit = Object.values(UniversalYAxisUnit).find(
|
||||
(u) => UniversalYAxisUnitMappings[u].has(unit as YAxisUnit) || unit === u,
|
||||
);
|
||||
|
||||
return universalUnit || (unit as UniversalYAxisUnit) || null;
|
||||
};
|
||||
|
||||
export const getUniversalNameFromMetricUnit = (
|
||||
unit: string | undefined,
|
||||
): string => {
|
||||
if (!unit) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
const universalUnit = mapMetricUnitToUniversalUnit(unit);
|
||||
if (!universalUnit) {
|
||||
return unit;
|
||||
}
|
||||
|
||||
const universalName = Y_AXIS_UNIT_NAMES[universalUnit];
|
||||
|
||||
return universalName || unit || '-';
|
||||
};
|
||||
@@ -68,6 +68,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
.y-axis-unit-selector-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.detection-method-container {
|
||||
margin: 24px 0;
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
getCategoryByOptionId,
|
||||
getCategorySelectOptionByName,
|
||||
} from 'container/NewWidget/RightContainer/alertFomatCategories';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
AlertDef,
|
||||
@@ -43,10 +42,10 @@ function RuleOptions({
|
||||
setAlertDef,
|
||||
queryCategory,
|
||||
queryOptions,
|
||||
yAxisUnit,
|
||||
}: RuleOptionsProps): JSX.Element {
|
||||
// init namespace for translations
|
||||
const { t } = useTranslation('alerts');
|
||||
const { currentQuery } = useQueryBuilder();
|
||||
|
||||
const { ruleType } = alertDef;
|
||||
|
||||
@@ -365,7 +364,7 @@ function RuleOptions({
|
||||
</InlineSelect>
|
||||
);
|
||||
|
||||
const selectedCategory = getCategoryByOptionId(currentQuery?.unit || '');
|
||||
const selectedCategory = getCategoryByOptionId(yAxisUnit);
|
||||
|
||||
const categorySelectOptions = getCategorySelectOptionByName(
|
||||
selectedCategory?.name,
|
||||
@@ -515,5 +514,6 @@ interface RuleOptionsProps {
|
||||
setAlertDef: (a: AlertDef) => void;
|
||||
queryCategory: EQueryType;
|
||||
queryOptions: DefaultOptionType[];
|
||||
yAxisUnit: string;
|
||||
}
|
||||
export default RuleOptions;
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Button, FormInstance, Modal, SelectProps, Typography } from 'antd';
|
||||
import saveAlertApi from 'api/alerts/save';
|
||||
import testAlertApi from 'api/alerts/testAlert';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import YAxisUnitSelector from 'components/YAxisUnitSelector';
|
||||
import { ALERTS_DATA_SOURCE_MAP } from 'constants/alerts';
|
||||
import { FeatureKeys } from 'constants/features';
|
||||
import { QueryParams } from 'constants/query';
|
||||
@@ -13,7 +14,6 @@ import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
import ROUTES from 'constants/routes';
|
||||
import QueryTypeTag from 'container/NewWidget/LeftContainer/QueryTypeTag';
|
||||
import PlotTag from 'container/NewWidget/LeftContainer/WidgetGraph/PlotTag';
|
||||
import { BuilderUnitsFilter } from 'container/QueryBuilder/filters';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
@@ -57,7 +57,7 @@ import {
|
||||
StepContainer,
|
||||
StepHeading,
|
||||
} from './styles';
|
||||
import { getSelectedQueryOptions } from './utils';
|
||||
import { getSelectedQueryOptions, useGetYAxisUnitFromQuery } from './utils';
|
||||
|
||||
export enum AlertDetectionTypes {
|
||||
THRESHOLD_ALERT = 'threshold_rule',
|
||||
@@ -684,6 +684,8 @@ function FormAlertRules({
|
||||
|
||||
const isAlertNameMissing = !formInstance.getFieldValue('alert');
|
||||
|
||||
const yAxisUnitFromQuery = useGetYAxisUnitFromQuery(stagedQuery);
|
||||
|
||||
const onUnitChangeHandler = (value: string): void => {
|
||||
setYAxisUnit(value);
|
||||
// reset target unit
|
||||
@@ -696,6 +698,10 @@ function FormAlertRules({
|
||||
}));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setYAxisUnit(yAxisUnitFromQuery || '');
|
||||
}, [yAxisUnitFromQuery]);
|
||||
|
||||
const isChannelConfigurationValid =
|
||||
alertDef?.broadcastToAll ||
|
||||
(alertDef.preferredChannels && alertDef.preferredChannels.length > 0);
|
||||
@@ -801,10 +807,10 @@ function FormAlertRules({
|
||||
</div>
|
||||
|
||||
<StepContainer>
|
||||
<BuilderUnitsFilter
|
||||
onChange={onUnitChangeHandler}
|
||||
yAxisUnit={yAxisUnit}
|
||||
/>
|
||||
<div className="y-axis-unit-selector-container">
|
||||
<Typography.Text>Y-Axis Unit</Typography.Text>
|
||||
<YAxisUnitSelector value={yAxisUnit} onChange={onUnitChangeHandler} />
|
||||
</div>
|
||||
</StepContainer>
|
||||
|
||||
<div className="steps-container">
|
||||
@@ -844,6 +850,7 @@ function FormAlertRules({
|
||||
alertDef={alertDef}
|
||||
setAlertDef={setAlertDef}
|
||||
queryOptions={queryOptions}
|
||||
yAxisUnit={yAxisUnit}
|
||||
/>
|
||||
|
||||
{renderBasicInfo()}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import { SelectProps } from 'antd';
|
||||
import { useGetMetricUnits } from 'container/MetricsExplorer/Explorer/utils';
|
||||
import { Time } from 'container/TopNav/DateTimeSelection/config';
|
||||
import getStartEndRangeTime from 'lib/getStartEndRangeTime';
|
||||
import getStep from 'lib/getStep';
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
IBuilderFormula,
|
||||
IBuilderQuery,
|
||||
IClickHouseQuery,
|
||||
IPromQLQuery,
|
||||
Query,
|
||||
} from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
// toChartInterval converts eval window to chart selection time interval
|
||||
export const toChartInterval = (evalWindow: string | undefined): Time => {
|
||||
@@ -62,3 +66,31 @@ export const getSelectedQueryOptions = (
|
||||
label: 'queryName' in query ? query.queryName : query.name,
|
||||
value: 'queryName' in query ? query.queryName : query.name,
|
||||
}));
|
||||
|
||||
export const useGetYAxisUnitFromQuery = (
|
||||
query: Query | null,
|
||||
): string | null => {
|
||||
const metricNames = useMemo(() => {
|
||||
if (!query) {
|
||||
return [];
|
||||
}
|
||||
return query.builder.queryData.map((query) => query.aggregateAttribute.key);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [JSON.stringify(query)]);
|
||||
|
||||
const { units } = useGetMetricUnits(
|
||||
metricNames,
|
||||
query?.builder.queryData[0].dataSource === DataSource.METRICS,
|
||||
);
|
||||
|
||||
return useMemo(() => {
|
||||
if (!query || units.length === 0) {
|
||||
return null;
|
||||
}
|
||||
const areAllUnitsSame = units.every((unit) => unit === units[0]);
|
||||
if (areAllUnitsSame) {
|
||||
return units[0];
|
||||
}
|
||||
return null;
|
||||
}, [query, units]);
|
||||
};
|
||||
|
||||
@@ -57,6 +57,20 @@
|
||||
.explore-content {
|
||||
margin-top: 10px;
|
||||
|
||||
.y-axis-unit-selector-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.save-unit-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-space {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 20px;
|
||||
@@ -74,6 +88,14 @@
|
||||
.time-series-view {
|
||||
min-width: 100%;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
.no-unit-warning {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
right: 40px;
|
||||
z-index: 1000;
|
||||
}
|
||||
}
|
||||
|
||||
.time-series-container {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import './Explorer.styles.scss';
|
||||
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { Switch } from 'antd';
|
||||
import { Switch, Tooltip } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import ExplorerOptionWrapper from 'container/ExplorerOptions/ExplorerOptionWrapper';
|
||||
@@ -20,10 +20,11 @@ import { generateExportToDashboardLink } from 'utils/dashboard/generateExportToD
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { MetricsExplorerEventKeys, MetricsExplorerEvents } from '../events';
|
||||
import MetricDetails from '../MetricDetails/MetricDetails';
|
||||
import QuerySection from './QuerySection';
|
||||
import TimeSeries from './TimeSeries';
|
||||
import { ExplorerTabs } from './types';
|
||||
import { splitQueryIntoOneChartPerQuery } from './utils';
|
||||
import { splitQueryIntoOneChartPerQuery, useGetMetricUnits } from './utils';
|
||||
|
||||
const ONE_CHART_PER_QUERY_ENABLED_KEY = 'isOneChartPerQueryEnabled';
|
||||
|
||||
@@ -35,6 +36,31 @@ function Explorer(): JSX.Element {
|
||||
currentQuery,
|
||||
} = useQueryBuilder();
|
||||
const { safeNavigate } = useSafeNavigate();
|
||||
const [isMetricDetailsOpen, setIsMetricDetailsOpen] = useState(false);
|
||||
|
||||
const metricNames = useMemo(
|
||||
() =>
|
||||
stagedQuery?.builder.queryData.map(
|
||||
(query) => query.aggregateAttribute.key,
|
||||
) ?? [],
|
||||
[stagedQuery],
|
||||
);
|
||||
|
||||
const {
|
||||
units,
|
||||
metrics,
|
||||
isLoading: isMetricUnitsLoading,
|
||||
isError: isMetricUnitsError,
|
||||
} = useGetMetricUnits(metricNames);
|
||||
|
||||
const areAllMetricUnitsSame = useMemo(
|
||||
() =>
|
||||
!isMetricUnitsLoading &&
|
||||
!isMetricUnitsError &&
|
||||
units.length > 0 &&
|
||||
units.every((unit) => unit === units[0]),
|
||||
[units, isMetricUnitsLoading, isMetricUnitsError],
|
||||
);
|
||||
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const isOneChartPerQueryEnabled =
|
||||
@@ -43,7 +69,31 @@ function Explorer(): JSX.Element {
|
||||
const [showOneChartPerQuery, toggleShowOneChartPerQuery] = useState(
|
||||
isOneChartPerQueryEnabled,
|
||||
);
|
||||
const [disableOneChartPerQuery, toggleDisableOneChartPerQuery] = useState(
|
||||
false,
|
||||
);
|
||||
const [selectedTab] = useState<ExplorerTabs>(ExplorerTabs.TIME_SERIES);
|
||||
const [yAxisUnit, setYAxisUnit] = useState<string>('');
|
||||
|
||||
useEffect(() => {
|
||||
if (units.length === 0) {
|
||||
setYAxisUnit('');
|
||||
} else if (units.length === 1 && units[0] !== '') {
|
||||
setYAxisUnit(units[0]);
|
||||
} else if (areAllMetricUnitsSame && units[0] !== '') {
|
||||
setYAxisUnit(units[0]);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [JSON.stringify(units), areAllMetricUnitsSame]);
|
||||
|
||||
useEffect(() => {
|
||||
if (units.length > 1 && !areAllMetricUnitsSame) {
|
||||
toggleShowOneChartPerQuery(true);
|
||||
toggleDisableOneChartPerQuery(true);
|
||||
} else {
|
||||
toggleDisableOneChartPerQuery(false);
|
||||
}
|
||||
}, [units, areAllMetricUnitsSame]);
|
||||
|
||||
const handleToggleShowOneChartPerQuery = (): void => {
|
||||
toggleShowOneChartPerQuery(!showOneChartPerQuery);
|
||||
@@ -53,15 +103,20 @@ function Explorer(): JSX.Element {
|
||||
});
|
||||
};
|
||||
|
||||
const exportDefaultQuery = useMemo(
|
||||
() =>
|
||||
updateAllQueriesOperators(
|
||||
currentQuery || initialQueriesMap[DataSource.METRICS],
|
||||
PANEL_TYPES.TIME_SERIES,
|
||||
DataSource.METRICS,
|
||||
),
|
||||
[currentQuery, updateAllQueriesOperators],
|
||||
);
|
||||
const exportDefaultQuery = useMemo(() => {
|
||||
const query = updateAllQueriesOperators(
|
||||
currentQuery || initialQueriesMap[DataSource.METRICS],
|
||||
PANEL_TYPES.TIME_SERIES,
|
||||
DataSource.METRICS,
|
||||
);
|
||||
if (yAxisUnit) {
|
||||
return {
|
||||
...query,
|
||||
unit: yAxisUnit,
|
||||
};
|
||||
}
|
||||
return query;
|
||||
}, [currentQuery, updateAllQueriesOperators, yAxisUnit]);
|
||||
|
||||
useShareBuilderUrl(exportDefaultQuery);
|
||||
|
||||
@@ -75,8 +130,16 @@ function Explorer(): JSX.Element {
|
||||
|
||||
const widgetId = uuid();
|
||||
|
||||
let query = queryToExport || exportDefaultQuery;
|
||||
if (yAxisUnit) {
|
||||
query = {
|
||||
...query,
|
||||
unit: yAxisUnit,
|
||||
};
|
||||
}
|
||||
|
||||
const dashboardEditView = generateExportToDashboardLink({
|
||||
query: queryToExport || exportDefaultQuery,
|
||||
query,
|
||||
panelType: PANEL_TYPES.TIME_SERIES,
|
||||
dashboardId: dashboard.id,
|
||||
widgetId,
|
||||
@@ -84,7 +147,7 @@ function Explorer(): JSX.Element {
|
||||
|
||||
safeNavigate(dashboardEditView);
|
||||
},
|
||||
[exportDefaultQuery, safeNavigate],
|
||||
[exportDefaultQuery, safeNavigate, yAxisUnit],
|
||||
);
|
||||
|
||||
const splitedQueries = useMemo(
|
||||
@@ -107,11 +170,17 @@ function Explorer(): JSX.Element {
|
||||
<div className="explore-header">
|
||||
<div className="explore-header-left-actions">
|
||||
<span>1 chart/query</span>
|
||||
<Switch
|
||||
checked={showOneChartPerQuery}
|
||||
onChange={handleToggleShowOneChartPerQuery}
|
||||
size="small"
|
||||
/>
|
||||
<Tooltip
|
||||
open={disableOneChartPerQuery ? undefined : false}
|
||||
title="One chart per query cannot be disabled for multiple queries with different units."
|
||||
>
|
||||
<Switch
|
||||
checked={showOneChartPerQuery}
|
||||
onChange={handleToggleShowOneChartPerQuery}
|
||||
disabled={disableOneChartPerQuery}
|
||||
size="small"
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="explore-header-right-actions">
|
||||
<DateTimeSelector showAutoRefresh />
|
||||
@@ -142,7 +211,18 @@ function Explorer(): JSX.Element {
|
||||
</Button.Group> */}
|
||||
<div className="explore-content">
|
||||
{selectedTab === ExplorerTabs.TIME_SERIES && (
|
||||
<TimeSeries showOneChartPerQuery={showOneChartPerQuery} />
|
||||
<TimeSeries
|
||||
showOneChartPerQuery={showOneChartPerQuery}
|
||||
areAllMetricUnitsSame={areAllMetricUnitsSame}
|
||||
isMetricUnitsLoading={isMetricUnitsLoading}
|
||||
isMetricUnitsError={isMetricUnitsError}
|
||||
metricUnits={units}
|
||||
metricNames={metricNames}
|
||||
metrics={metrics}
|
||||
setIsMetricDetailsOpen={setIsMetricDetailsOpen}
|
||||
yAxisUnit={yAxisUnit}
|
||||
setYAxisUnit={setYAxisUnit}
|
||||
/>
|
||||
)}
|
||||
{/* TODO: Enable once we have resolved all related metrics issues */}
|
||||
{/* {selectedTab === ExplorerTabs.RELATED_METRICS && (
|
||||
@@ -158,6 +238,14 @@ function Explorer(): JSX.Element {
|
||||
isOneChartPerQuery={showOneChartPerQuery}
|
||||
splitedQueries={splitedQueries}
|
||||
/>
|
||||
{isMetricDetailsOpen && (
|
||||
<MetricDetails
|
||||
metricName={metricNames[0]}
|
||||
isOpen={isMetricDetailsOpen}
|
||||
onClose={(): void => setIsMetricDetailsOpen(false)}
|
||||
isModalTimeSelection={false}
|
||||
/>
|
||||
)}
|
||||
</Sentry.ErrorBoundary>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Button, Tooltip, Typography } from 'antd';
|
||||
import { MetricType } from 'api/metricsExplorer/getMetricsList';
|
||||
import classNames from 'classnames';
|
||||
import YAxisUnitSelector from 'components/YAxisUnitSelector';
|
||||
import { ENTITY_VERSION_V4 } from 'constants/app';
|
||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
import { BuilderUnitsFilter } from 'container/QueryBuilder/filters/BuilderUnitsFilter/BuilderUnits';
|
||||
import TimeSeriesView from 'container/TimeSeriesView/TimeSeriesView';
|
||||
import { convertDataValueToMs } from 'container/TimeSeriesView/utils';
|
||||
import { useUpdateMetricMetadata } from 'hooks/metricsExplorer/useUpdateMetricMetadata';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import { GetMetricQueryRange } from 'lib/dashboard/getQueryResults';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useQueries } from 'react-query';
|
||||
import { AlertTriangle } from 'lucide-react';
|
||||
import { useMemo } from 'react';
|
||||
import { useQueries, useQueryClient } from 'react-query';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
@@ -19,8 +25,21 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { TimeSeriesProps } from './types';
|
||||
import { splitQueryIntoOneChartPerQuery } from './utils';
|
||||
|
||||
function TimeSeries({ showOneChartPerQuery }: TimeSeriesProps): JSX.Element {
|
||||
function TimeSeries({
|
||||
showOneChartPerQuery,
|
||||
areAllMetricUnitsSame,
|
||||
isMetricUnitsLoading,
|
||||
isMetricUnitsError,
|
||||
metricUnits,
|
||||
metricNames,
|
||||
metrics,
|
||||
setIsMetricDetailsOpen,
|
||||
yAxisUnit,
|
||||
setYAxisUnit,
|
||||
}: TimeSeriesProps): JSX.Element {
|
||||
const { stagedQuery, currentQuery } = useQueryBuilder();
|
||||
const { notifications } = useNotifications();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const { selectedTime: globalSelectedTime, maxTime, minTime } = useSelector<
|
||||
AppState,
|
||||
@@ -56,8 +75,6 @@ function TimeSeries({ showOneChartPerQuery }: TimeSeriesProps): JSX.Element {
|
||||
[showOneChartPerQuery, stagedQuery],
|
||||
);
|
||||
|
||||
const [yAxisUnit, setYAxisUnit] = useState<string>('');
|
||||
|
||||
const queries = useQueries(
|
||||
queryPayloads.map((payload, index) => ({
|
||||
queryKey: [
|
||||
@@ -105,30 +122,137 @@ function TimeSeries({ showOneChartPerQuery }: TimeSeriesProps): JSX.Element {
|
||||
setYAxisUnit(value);
|
||||
};
|
||||
|
||||
const goToMetricDetails = (): void => {
|
||||
setIsMetricDetailsOpen(true);
|
||||
};
|
||||
|
||||
const showYAxisUnitSelector = useMemo(() => {
|
||||
if (metricUnits.length <= 1) {
|
||||
return true;
|
||||
}
|
||||
if (areAllMetricUnitsSame) {
|
||||
return metricUnits[0] !== '';
|
||||
}
|
||||
return false;
|
||||
}, [metricUnits, areAllMetricUnitsSame]);
|
||||
|
||||
const showSaveUnitButton = useMemo(
|
||||
() =>
|
||||
metricUnits.length === 1 &&
|
||||
Boolean(metrics?.[0]) &&
|
||||
metricUnits[0] === '' &&
|
||||
yAxisUnit !== '',
|
||||
[metricUnits, metrics, yAxisUnit],
|
||||
);
|
||||
|
||||
const {
|
||||
mutate: updateMetricMetadata,
|
||||
isLoading: isUpdatingMetricMetadata,
|
||||
} = useUpdateMetricMetadata();
|
||||
|
||||
const handleSaveUnit = (): void => {
|
||||
updateMetricMetadata(
|
||||
{
|
||||
metricName: metricNames[0],
|
||||
payload: {
|
||||
unit: yAxisUnit,
|
||||
description: metrics[0]?.metadata?.description ?? '',
|
||||
metricType: metrics[0]?.metadata?.metric_type as MetricType,
|
||||
},
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
notifications.success({
|
||||
message: 'Unit saved successfully',
|
||||
});
|
||||
queryClient.invalidateQueries([
|
||||
REACT_QUERY_KEY.GET_METRIC_DETAILS,
|
||||
metricNames[0],
|
||||
]);
|
||||
},
|
||||
onError: () => {
|
||||
notifications.error({
|
||||
message: 'Failed to save unit',
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<BuilderUnitsFilter onChange={onUnitChangeHandler} yAxisUnit={yAxisUnit} />
|
||||
<div className="y-axis-unit-selector-container">
|
||||
{showYAxisUnitSelector && (
|
||||
<>
|
||||
<YAxisUnitSelector
|
||||
value={yAxisUnit}
|
||||
onChange={onUnitChangeHandler}
|
||||
loading={isMetricUnitsLoading}
|
||||
disabled={isMetricUnitsLoading || isMetricUnitsError}
|
||||
/>
|
||||
{showSaveUnitButton && (
|
||||
<div className="save-unit-container">
|
||||
<Typography.Text>
|
||||
Save the selected unit for this metric?
|
||||
</Typography.Text>
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
loading={isUpdatingMetricMetadata}
|
||||
onClick={handleSaveUnit}
|
||||
>
|
||||
Yes
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={classNames({
|
||||
'time-series-container': changeLayoutForOneChartPerQuery,
|
||||
})}
|
||||
>
|
||||
{responseData.map((datapoint, index) => (
|
||||
<div
|
||||
className="time-series-view"
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
key={index}
|
||||
>
|
||||
<TimeSeriesView
|
||||
isFilterApplied={false}
|
||||
isError={queries[index].isError}
|
||||
isLoading={queries[index].isLoading}
|
||||
data={datapoint}
|
||||
yAxisUnit={yAxisUnit}
|
||||
dataSource={DataSource.METRICS}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
{responseData.map((datapoint, index) => {
|
||||
const isMetricUnitEmpty =
|
||||
!queries[index].isLoading &&
|
||||
!isMetricUnitsLoading &&
|
||||
metricUnits.length > 1 &&
|
||||
metricUnits[index] === '';
|
||||
|
||||
return (
|
||||
<div
|
||||
className="time-series-view"
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
key={index}
|
||||
>
|
||||
{isMetricUnitEmpty && (
|
||||
<Tooltip
|
||||
className="no-unit-warning"
|
||||
title={
|
||||
<Typography.Text>
|
||||
This metric does not have a unit. Please set one for it in the{' '}
|
||||
<Typography.Link onClick={goToMetricDetails}>
|
||||
metric details
|
||||
</Typography.Link>{' '}
|
||||
drawer.
|
||||
</Typography.Text>
|
||||
}
|
||||
>
|
||||
<AlertTriangle size={16} color={Color.BG_AMBER_400} />
|
||||
</Tooltip>
|
||||
)}
|
||||
<TimeSeriesView
|
||||
isFilterApplied={false}
|
||||
isError={queries[index].isError}
|
||||
isLoading={queries[index].isLoading}
|
||||
data={datapoint}
|
||||
yAxisUnit={yAxisUnit}
|
||||
dataSource={DataSource.METRICS}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { MetricDetails } from 'api/metricsExplorer/getMetricDetails';
|
||||
import { RelatedMetric } from 'api/metricsExplorer/getRelatedMetrics';
|
||||
import { UseQueryResult } from 'react-query';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
@@ -10,6 +11,15 @@ export enum ExplorerTabs {
|
||||
|
||||
export interface TimeSeriesProps {
|
||||
showOneChartPerQuery: boolean;
|
||||
areAllMetricUnitsSame: boolean;
|
||||
isMetricUnitsLoading: boolean;
|
||||
isMetricUnitsError: boolean;
|
||||
metricUnits: string[];
|
||||
metricNames: string[];
|
||||
metrics: (MetricDetails | undefined)[];
|
||||
setIsMetricDetailsOpen: (isOpen: boolean) => void;
|
||||
yAxisUnit: string;
|
||||
setYAxisUnit: (unit: string) => void;
|
||||
}
|
||||
|
||||
export interface RelatedMetricsProps {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { MetricDetails } from 'api/metricsExplorer/getMetricDetails';
|
||||
import { useGetMultipleMetrics } from 'hooks/metricsExplorer/useGetMultipleMetrics';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
@@ -35,3 +37,25 @@ export const splitQueryIntoOneChartPerQuery = (query: Query): Query[] => {
|
||||
|
||||
return queries;
|
||||
};
|
||||
|
||||
export function useGetMetricUnits(
|
||||
metricNames: string[],
|
||||
isEnabled = true,
|
||||
): {
|
||||
isLoading: boolean;
|
||||
units: string[];
|
||||
isError: boolean;
|
||||
metrics: (MetricDetails | undefined)[];
|
||||
} {
|
||||
const metricsData = useGetMultipleMetrics(metricNames, {
|
||||
enabled: metricNames.length > 0 && isEnabled,
|
||||
});
|
||||
return {
|
||||
isLoading: metricsData.some((metric) => metric.isLoading),
|
||||
units: metricsData.map(
|
||||
(metric) => metric.data?.payload?.data?.metadata?.unit ?? '',
|
||||
),
|
||||
metrics: metricsData.map((metric) => metric.data?.payload?.data),
|
||||
isError: metricsData.some((metric) => metric.isError),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import { Temporality } from 'api/metricsExplorer/getMetricDetails';
|
||||
import { MetricType } from 'api/metricsExplorer/getMetricsList';
|
||||
import { UpdateMetricMetadataProps } from 'api/metricsExplorer/updateMetricMetadata';
|
||||
import { ResizeTable } from 'components/ResizeTable';
|
||||
import YAxisUnitSelector from 'components/YAxisUnitSelector';
|
||||
import { getUniversalNameFromMetricUnit } from 'components/YAxisUnitSelector/utils';
|
||||
import FieldRenderer from 'container/LogDetailedView/FieldRenderer';
|
||||
import { DataType } from 'container/LogDetailedView/TableView';
|
||||
import { useUpdateMetricMetadata } from 'hooks/metricsExplorer/useUpdateMetricMetadata';
|
||||
@@ -35,6 +37,7 @@ function Metadata({
|
||||
metricType: metadata?.metric_type || MetricType.SUM,
|
||||
description: metadata?.description || '',
|
||||
temporality: metadata?.temporality,
|
||||
unit: metadata?.unit,
|
||||
});
|
||||
const { notifications } = useNotifications();
|
||||
const {
|
||||
@@ -66,6 +69,7 @@ function Metadata({
|
||||
);
|
||||
|
||||
const columns: ColumnsType<DataType> = useMemo(
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
() => [
|
||||
{
|
||||
title: 'Key',
|
||||
@@ -91,7 +95,10 @@ function Metadata({
|
||||
ellipsis: true,
|
||||
className: 'metric-metadata-value',
|
||||
render: (field: { value: string; key: string }): JSX.Element => {
|
||||
if (!isEditing || field.key === 'unit') {
|
||||
// Don't allow editing of unit if it's already set
|
||||
const disableEditingForMetricsWithUnits =
|
||||
field.key === 'unit' && Boolean(metadata?.unit);
|
||||
if (!isEditing || disableEditingForMetricsWithUnits) {
|
||||
if (field.key === 'metric_type') {
|
||||
return (
|
||||
<div>
|
||||
@@ -99,7 +106,11 @@ function Metadata({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return <FieldRenderer field={field.value || '-'} />;
|
||||
let fieldValue = field.value;
|
||||
if (field.key === 'unit') {
|
||||
fieldValue = getUniversalNameFromMetricUnit(field.value);
|
||||
}
|
||||
return <FieldRenderer field={fieldValue || '-'} />;
|
||||
}
|
||||
if (field.key === 'metric_type') {
|
||||
return (
|
||||
@@ -118,6 +129,16 @@ function Metadata({
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (field.key === 'unit') {
|
||||
return (
|
||||
<YAxisUnitSelector
|
||||
value={metricMetadata.unit}
|
||||
onChange={(value): void => {
|
||||
setMetricMetadata((prev) => ({ ...prev, unit: value }));
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (field.key === 'temporality') {
|
||||
return (
|
||||
<Select
|
||||
@@ -154,7 +175,7 @@ function Metadata({
|
||||
},
|
||||
},
|
||||
],
|
||||
[isEditing, metricMetadata, setMetricMetadata],
|
||||
[isEditing, metadata?.unit, metricMetadata],
|
||||
);
|
||||
|
||||
const handleSave = useCallback(() => {
|
||||
|
||||
@@ -123,16 +123,18 @@ function MetricDetails({
|
||||
<Typography.Text>{metric?.name}</Typography.Text>
|
||||
</div>
|
||||
<div className="metric-details-header-buttons">
|
||||
<Button
|
||||
onClick={goToMetricsExplorerwithSelectedMetric}
|
||||
icon={<Compass size={16} />}
|
||||
disabled={!metricName}
|
||||
data-testid="open-in-explorer-button"
|
||||
>
|
||||
Open in Explorer
|
||||
</Button>
|
||||
{openInspectModal && (
|
||||
<Button
|
||||
onClick={goToMetricsExplorerwithSelectedMetric}
|
||||
icon={<Compass size={16} />}
|
||||
disabled={!metricName}
|
||||
data-testid="open-in-explorer-button"
|
||||
>
|
||||
Open in Explorer
|
||||
</Button>
|
||||
)}
|
||||
{/* Show the based on the feature flag. Will remove before releasing the feature */}
|
||||
{showInspectFeature && (
|
||||
{showInspectFeature && openInspectModal && (
|
||||
<Button
|
||||
className="inspect-metrics-button"
|
||||
aria-label="Inspect Metric"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { MetricDetails } from 'api/metricsExplorer/getMetricDetails';
|
||||
import { MetricType } from 'api/metricsExplorer/getMetricsList';
|
||||
import { getUniversalNameFromMetricUnit } from 'components/YAxisUnitSelector/utils';
|
||||
import ROUTES from 'constants/routes';
|
||||
import * as useGetMetricDetails from 'hooks/metricsExplorer/useGetMetricDetails';
|
||||
import * as useUpdateMetricMetadata from 'hooks/metricsExplorer/useUpdateMetricMetadata';
|
||||
@@ -95,7 +96,9 @@ describe('MetricDetails', () => {
|
||||
|
||||
expect(screen.getByText(mockMetricName)).toBeInTheDocument();
|
||||
expect(screen.getByText(mockMetricDescription)).toBeInTheDocument();
|
||||
expect(screen.getByText(`${mockMetricData.unit}`)).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(`${getUniversalNameFromMetricUnit(mockMetricData.unit)}`),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders the "open in explorer" and "inspect" buttons', () => {
|
||||
|
||||
@@ -11,7 +11,7 @@ export interface MetricDetailsProps {
|
||||
isOpen: boolean;
|
||||
metricName: string | null;
|
||||
isModalTimeSelection: boolean;
|
||||
openInspectModal: (metricName: string) => void;
|
||||
openInspectModal?: (metricName: string) => void;
|
||||
}
|
||||
|
||||
export interface DashboardsAndAlertsPopoverProps {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { render } from '@testing-library/react';
|
||||
import { MetricType } from 'api/metricsExplorer/getMetricsList';
|
||||
import { getUniversalNameFromMetricUnit } from 'components/YAxisUnitSelector/utils';
|
||||
import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
import { TreemapViewType } from '../types';
|
||||
@@ -144,7 +145,9 @@ describe('formatDataForMetricsTable', () => {
|
||||
// Verify unit rendering
|
||||
const unitElement = result[0].unit as JSX.Element;
|
||||
const { container: unitWrapper } = render(unitElement);
|
||||
expect(unitWrapper.textContent).toBe('bytes');
|
||||
expect(unitWrapper.textContent).toBe(
|
||||
getUniversalNameFromMetricUnit(mockData[0].unit),
|
||||
);
|
||||
|
||||
// Verify samples rendering
|
||||
const samplesElement = result[0][TreemapViewType.SAMPLES] as JSX.Element;
|
||||
@@ -187,7 +190,9 @@ describe('formatDataForMetricsTable', () => {
|
||||
// Verify null unit rendering
|
||||
const unitElement = result[0].unit as JSX.Element;
|
||||
const { container: unitWrapper } = render(unitElement);
|
||||
expect(unitWrapper.textContent).toBe('ms');
|
||||
expect(unitWrapper.textContent).toBe(
|
||||
getUniversalNameFromMetricUnit(mockData[0].unit),
|
||||
);
|
||||
|
||||
// Verify zero samples rendering
|
||||
const samplesElement = result[0][TreemapViewType.SAMPLES] as JSX.Element;
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
SamplesData,
|
||||
TimeseriesData,
|
||||
} from 'api/metricsExplorer/getMetricsTreeMap';
|
||||
import { getUniversalNameFromMetricUnit } from 'components/YAxisUnitSelector/utils';
|
||||
import {
|
||||
BarChart,
|
||||
BarChart2,
|
||||
@@ -200,7 +201,7 @@ export const formatDataForMetricsTable = (
|
||||
metric_type: <MetricTypeRenderer type={metric.type} />,
|
||||
unit: (
|
||||
<ValidateRowValueWrapper value={metric.unit}>
|
||||
{metric.unit}
|
||||
{getUniversalNameFromMetricUnit(metric.unit)}
|
||||
</ValidateRowValueWrapper>
|
||||
),
|
||||
[TreemapViewType.SAMPLES]: (
|
||||
|
||||
@@ -190,7 +190,8 @@
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.y-axis-unit-selector {
|
||||
.y-axis-unit-selector,
|
||||
.y-axis-unit-selector-v2 {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -223,6 +224,21 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.y-axis-unit-selector-v2 {
|
||||
.y-axis-unit-selector-component {
|
||||
.ant-select {
|
||||
width: 100%;
|
||||
border-radius: 2px;
|
||||
background: var(--bg-ink-300);
|
||||
|
||||
.ant-select-selector {
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.soft-min-max {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Typography } from 'antd';
|
||||
import YAxisUnitSelectorComponent from 'components/YAxisUnitSelector';
|
||||
import { Dispatch, SetStateAction } from 'react';
|
||||
|
||||
type OnSelectType = Dispatch<SetStateAction<string>> | ((val: string) => void);
|
||||
function YAxisUnitSelectorV2({
|
||||
defaultValue,
|
||||
onSelect,
|
||||
fieldLabel,
|
||||
}: {
|
||||
defaultValue: string;
|
||||
onSelect: OnSelectType;
|
||||
fieldLabel: string;
|
||||
}): JSX.Element {
|
||||
return (
|
||||
<div className="y-axis-unit-selector-v2">
|
||||
<Typography.Text className="heading">{fieldLabel}</Typography.Text>
|
||||
<YAxisUnitSelectorComponent value={defaultValue} onChange={onSelect} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default YAxisUnitSelectorV2;
|
||||
@@ -0,0 +1,72 @@
|
||||
import { Y_AXIS_CATEGORIES } from 'components/YAxisUnitSelector/constants';
|
||||
|
||||
import {
|
||||
alertsCategory,
|
||||
getCategoryByOptionId,
|
||||
getCategorySelectOptionByName,
|
||||
} from '../alertFomatCategories';
|
||||
import { CategoryNames, DataRateFormats, MiscellaneousFormats } from '../types';
|
||||
|
||||
describe('getCategorySelectOptionByName', () => {
|
||||
it('should return empty array for undefined category name', () => {
|
||||
const result = getCategorySelectOptionByName(undefined);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return empty array for invalid category name', () => {
|
||||
const result = getCategorySelectOptionByName('invalid_category');
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return correct options for legacy Data Rate category', () => {
|
||||
const result = getCategorySelectOptionByName(CategoryNames.DataRate);
|
||||
expect(result).toEqual(
|
||||
alertsCategory
|
||||
.find((category) => category.name === CategoryNames.DataRate)
|
||||
?.formats.map((format) => ({
|
||||
label: format.name,
|
||||
value: format.id,
|
||||
})),
|
||||
);
|
||||
});
|
||||
|
||||
it('should return correct options for legacy Miscellaneous category', () => {
|
||||
const result = getCategorySelectOptionByName(CategoryNames.Miscellaneous);
|
||||
expect(result).toContainEqual({
|
||||
label: 'Percent (0 - 100)',
|
||||
value: MiscellaneousFormats.Percent,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getCategoryByOptionId', () => {
|
||||
it('should return undefined for invalid option id', () => {
|
||||
const result = getCategoryByOptionId('invalid_id');
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return correct category for legacy Data Rate format id', () => {
|
||||
const result = getCategoryByOptionId(DataRateFormats.BytesPerSecSI);
|
||||
expect(result?.name).toBe(CategoryNames.DataRate);
|
||||
});
|
||||
|
||||
it('should return correct category for legacy Miscellaneous format id', () => {
|
||||
const result = getCategoryByOptionId(MiscellaneousFormats.Percent);
|
||||
expect(result?.name).toBe(CategoryNames.Miscellaneous);
|
||||
});
|
||||
|
||||
it('should return correct category for new Y axis unit id', () => {
|
||||
const testCategory = Y_AXIS_CATEGORIES[0];
|
||||
const testUnit = testCategory.units[0];
|
||||
const result = getCategoryByOptionId(testUnit.id);
|
||||
expect(result).toEqual({
|
||||
name: testCategory.name,
|
||||
formats: Y_AXIS_CATEGORIES.find(
|
||||
(category) => category.name === testCategory.name,
|
||||
)?.units.map((format) => ({
|
||||
name: format.name,
|
||||
id: format.id,
|
||||
})),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,5 @@
|
||||
import { DefaultOptionType } from 'antd/es/select';
|
||||
import { Y_AXIS_CATEGORIES } from 'components/YAxisUnitSelector/constants';
|
||||
|
||||
import {
|
||||
BooleanFormats,
|
||||
@@ -107,18 +108,53 @@ export const alertsCategory = [
|
||||
|
||||
export const getCategorySelectOptionByName = (
|
||||
name?: CategoryNames | string,
|
||||
): DefaultOptionType[] =>
|
||||
alertsCategory
|
||||
): DefaultOptionType[] => {
|
||||
const newAlertsCategory = Y_AXIS_CATEGORIES.find(
|
||||
(category) => category.name === name,
|
||||
);
|
||||
if (newAlertsCategory) {
|
||||
return newAlertsCategory.units.map((unit) => ({
|
||||
label: unit.name,
|
||||
value: unit.id,
|
||||
}));
|
||||
}
|
||||
|
||||
const oldAlertsCategory = alertsCategory
|
||||
.find((category) => category.name === name)
|
||||
?.formats.map((format) => ({
|
||||
label: format.name,
|
||||
value: format.id,
|
||||
})) || [];
|
||||
}));
|
||||
if (oldAlertsCategory) {
|
||||
return oldAlertsCategory;
|
||||
}
|
||||
|
||||
export const getCategoryByOptionId = (id: string): Category | undefined =>
|
||||
alertsCategory.find((category) =>
|
||||
return [];
|
||||
};
|
||||
|
||||
export const getCategoryByOptionId = (id: string): Category | undefined => {
|
||||
const newAlertsCategory = Y_AXIS_CATEGORIES.find((category) =>
|
||||
category.units.some((unit) => unit.id === id),
|
||||
);
|
||||
if (newAlertsCategory) {
|
||||
return {
|
||||
name: newAlertsCategory.name,
|
||||
formats: newAlertsCategory.units.map((unit) => ({
|
||||
name: unit.name,
|
||||
id: unit.id,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
const oldAlertsCategory = alertsCategory.find((category) =>
|
||||
category.formats.some((format) => format.id === id),
|
||||
);
|
||||
if (oldAlertsCategory) {
|
||||
return oldAlertsCategory;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const isCategoryName = (name: string): name is CategoryNames =>
|
||||
alertsCategory.some((category) => category.name === name);
|
||||
|
||||
@@ -60,7 +60,7 @@ import LegendColors from './LegendColors/LegendColors';
|
||||
import ThresholdSelector from './Threshold/ThresholdSelector';
|
||||
import { ThresholdProps } from './Threshold/types';
|
||||
import { timePreferance } from './timeItems';
|
||||
import YAxisUnitSelector from './YAxisUnitSelector';
|
||||
import YAxisUnitSelectorV2 from './YAxisUnitSelectorV2';
|
||||
|
||||
const { TextArea } = Input;
|
||||
const { Option } = Select;
|
||||
@@ -332,7 +332,7 @@ function RightContainer({
|
||||
)}
|
||||
|
||||
{allowYAxisUnit && (
|
||||
<YAxisUnitSelector
|
||||
<YAxisUnitSelectorV2
|
||||
defaultValue={yAxisUnit}
|
||||
onSelect={setYAxisUnit}
|
||||
fieldLabel={
|
||||
|
||||
@@ -46,6 +46,7 @@ import {
|
||||
import { Props } from 'types/api/dashboard/update';
|
||||
import { IField } from 'types/api/logs/fields';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
@@ -238,6 +239,19 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
||||
selectedWidget?.columnUnits || {},
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const compositeQuery = query.get('compositeQuery');
|
||||
if (compositeQuery) {
|
||||
try {
|
||||
const decoded = decodeURIComponent(compositeQuery);
|
||||
const parsedQuery = JSON.parse(decoded) as Query;
|
||||
setYAxisUnit(parsedQuery.unit || 'none');
|
||||
} catch (error) {
|
||||
setYAxisUnit('none');
|
||||
}
|
||||
}
|
||||
}, [query]);
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedWidget((prev) => {
|
||||
if (!prev) {
|
||||
|
||||
35
frontend/src/hooks/metricsExplorer/useGetMultipleMetrics.ts
Normal file
35
frontend/src/hooks/metricsExplorer/useGetMultipleMetrics.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import {
|
||||
getMetricDetails,
|
||||
MetricDetailsResponse,
|
||||
} from 'api/metricsExplorer/getMetricDetails';
|
||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
import { useQueries, UseQueryOptions, UseQueryResult } from 'react-query';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
|
||||
type QueryData = SuccessResponse<MetricDetailsResponse> | ErrorResponse;
|
||||
type QueryResult = UseQueryResult<QueryData, Error>;
|
||||
|
||||
type UseGetMultipleMetrics = (
|
||||
metricNames: string[],
|
||||
options?: UseQueryOptions<QueryData, Error>,
|
||||
headers?: Record<string, string>,
|
||||
) => QueryResult[];
|
||||
|
||||
export const useGetMultipleMetrics: UseGetMultipleMetrics = (
|
||||
metricNames,
|
||||
options,
|
||||
headers,
|
||||
) => {
|
||||
const queries = useQueries(
|
||||
metricNames.map(
|
||||
(metricName) =>
|
||||
({
|
||||
queryKey: [REACT_QUERY_KEY.GET_METRIC_DETAILS, metricName],
|
||||
queryFn: ({ signal }) => getMetricDetails(metricName, signal, headers),
|
||||
...options,
|
||||
} as UseQueryOptions<QueryData, Error>),
|
||||
),
|
||||
);
|
||||
|
||||
return queries as QueryResult[];
|
||||
};
|
||||
@@ -1,3 +1,6 @@
|
||||
import { Y_AXIS_UNIT_NAMES } from 'components/YAxisUnitSelector/constants';
|
||||
import { UniversalYAxisUnit } from 'components/YAxisUnitSelector/types';
|
||||
|
||||
const unitsMapping = [
|
||||
{
|
||||
label: 'Data',
|
||||
@@ -72,6 +75,68 @@ const unitsMapping = [
|
||||
value: 'decpbytes',
|
||||
factor: 1000 * 1000 * 1000 * 1000 * 1000,
|
||||
},
|
||||
// Universal units
|
||||
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.BYTES],
|
||||
value: UniversalYAxisUnit.BYTES,
|
||||
factor: 1,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.KILOBYTES],
|
||||
value: UniversalYAxisUnit.KILOBYTES,
|
||||
factor: 1024,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.MEGABYTES],
|
||||
value: UniversalYAxisUnit.MEGABYTES,
|
||||
factor: 1024 * 1024,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.GIGABYTES],
|
||||
value: UniversalYAxisUnit.GIGABYTES,
|
||||
factor: 1024 * 1024 * 1024,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.TERABYTES],
|
||||
value: UniversalYAxisUnit.TERABYTES,
|
||||
factor: 1024 * 1024 * 1024 * 1024,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.PETABYTES],
|
||||
value: UniversalYAxisUnit.PETABYTES,
|
||||
factor: 1024 * 1024 * 1024 * 1024 * 1024,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.BITS],
|
||||
value: UniversalYAxisUnit.BITS,
|
||||
factor: 8,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.KILOBITS],
|
||||
value: UniversalYAxisUnit.KILOBITS,
|
||||
factor: 8 * 1024,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.MEGABITS],
|
||||
value: UniversalYAxisUnit.MEGABITS,
|
||||
factor: 8 * 1024 * 1024,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.GIGABITS],
|
||||
value: UniversalYAxisUnit.GIGABITS,
|
||||
factor: 8 * 1024 * 1024 * 1024,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.TERABITS],
|
||||
value: UniversalYAxisUnit.TERABITS,
|
||||
factor: 8 * 1024 * 1024 * 1024 * 1024,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.PETABITS],
|
||||
value: UniversalYAxisUnit.PETABITS,
|
||||
factor: 8 * 1024 * 1024 * 1024 * 1024 * 1024,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -130,6 +195,72 @@ const unitsMapping = [
|
||||
// ... (other options)
|
||||
],
|
||||
},
|
||||
// Universal units for data rate
|
||||
{
|
||||
label: 'Data Rate',
|
||||
options: [
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.BYTES_SECOND],
|
||||
value: UniversalYAxisUnit.BYTES_SECOND,
|
||||
factor: 1,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.KILOBYTES_SECOND],
|
||||
value: UniversalYAxisUnit.KILOBYTES_SECOND,
|
||||
factor: 1024,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.MEGABYTES_SECOND],
|
||||
value: UniversalYAxisUnit.MEGABYTES_SECOND,
|
||||
factor: 1024 * 1024,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.GIGABYTES_SECOND],
|
||||
value: UniversalYAxisUnit.GIGABYTES_SECOND,
|
||||
factor: 1024 * 1024 * 1024,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.TERABYTES_SECOND],
|
||||
value: UniversalYAxisUnit.TERABYTES_SECOND,
|
||||
factor: 1024 * 1024 * 1024 * 1024,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.PETABYTES_SECOND],
|
||||
value: UniversalYAxisUnit.PETABYTES_SECOND,
|
||||
factor: 1024,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.BITS_SECOND],
|
||||
value: UniversalYAxisUnit.BITS_SECOND,
|
||||
factor: 8,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.KILOBITS_SECOND],
|
||||
value: UniversalYAxisUnit.KILOBITS_SECOND,
|
||||
factor: 8 * 1024,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.MEGABITS_SECOND],
|
||||
value: UniversalYAxisUnit.MEGABITS_SECOND,
|
||||
factor: 8 * 1024 * 1024,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.GIGABITS_SECOND],
|
||||
value: UniversalYAxisUnit.GIGABITS_SECOND,
|
||||
factor: 8 * 1024 * 1024 * 1024,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.TERABITS_SECOND],
|
||||
value: UniversalYAxisUnit.TERABITS_SECOND,
|
||||
factor: 8 * 1024 * 1024 * 1024 * 1024,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.PETABITS_SECOND],
|
||||
value: UniversalYAxisUnit.PETABITS_SECOND,
|
||||
factor: 8 * 1024 * 1024 * 1024 * 1024 * 1024,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Time',
|
||||
options: [
|
||||
@@ -168,6 +299,12 @@ const unitsMapping = [
|
||||
value: 'd',
|
||||
factor: 24 * 60 * 60 * 1000 * 1000 * 1000, // 1 d = 24 h
|
||||
},
|
||||
// Universal units for time
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.MINUTES],
|
||||
value: UniversalYAxisUnit.MINUTES,
|
||||
factor: 60 * 1000 * 1000 * 1000, // 1 m = 60 s
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -223,7 +360,63 @@ const unitsMapping = [
|
||||
value: 'wpm',
|
||||
factor: 60, // 1 wpm = 60 wps
|
||||
},
|
||||
// ... (other options)
|
||||
// Universal units for throughput
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.COUNT_SECOND],
|
||||
value: UniversalYAxisUnit.COUNT_SECOND,
|
||||
factor: 1,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.COUNT_MINUTE],
|
||||
value: UniversalYAxisUnit.COUNT_MINUTE,
|
||||
factor: 60,
|
||||
},
|
||||
],
|
||||
},
|
||||
// Universal units for operations
|
||||
{
|
||||
label: 'Operations',
|
||||
options: [
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.OPS_SECOND],
|
||||
value: UniversalYAxisUnit.OPS_SECOND,
|
||||
factor: 1,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.OPS_MINUTE],
|
||||
value: UniversalYAxisUnit.OPS_MINUTE,
|
||||
factor: 60,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.REQUESTS_SECOND],
|
||||
value: UniversalYAxisUnit.REQUESTS_SECOND,
|
||||
factor: 1,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.READS_SECOND],
|
||||
value: UniversalYAxisUnit.READS_SECOND,
|
||||
factor: 1,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.WRITES_SECOND],
|
||||
value: UniversalYAxisUnit.WRITES_SECOND,
|
||||
factor: 1,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.READS_MINUTE],
|
||||
value: UniversalYAxisUnit.READS_MINUTE,
|
||||
factor: 60,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.WRITES_MINUTE],
|
||||
value: UniversalYAxisUnit.WRITES_MINUTE,
|
||||
factor: 60,
|
||||
},
|
||||
{
|
||||
label: Y_AXIS_UNIT_NAMES[UniversalYAxisUnit.IOOPS_SECOND],
|
||||
value: UniversalYAxisUnit.IOOPS_SECOND,
|
||||
factor: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -654,6 +654,11 @@ export const getUPlotChartOptions = ({
|
||||
isDarkMode,
|
||||
colorMapping,
|
||||
}),
|
||||
axes: getAxes({ isDarkMode, yAxisUnit, panelType, isLogScale }),
|
||||
axes: getAxes({
|
||||
isDarkMode,
|
||||
yAxisUnit,
|
||||
panelType,
|
||||
isLogScale,
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -43,17 +43,17 @@ var (
|
||||
// FromUnit returns a converter for the given unit
|
||||
func FromUnit(u Unit) Converter {
|
||||
switch u {
|
||||
case "ns", "us", "µs", "ms", "s", "m", "h", "d":
|
||||
case "ns", "us", "µs", "ms", "s", "m", "h", "d", "min":
|
||||
return DurationConverter
|
||||
case "bytes", "decbytes", "bits", "decbits", "kbytes", "decKbytes", "deckbytes", "mbytes", "decMbytes", "decmbytes", "gbytes", "decGbytes", "decgbytes", "tbytes", "decTbytes", "dectbytes", "pbytes", "decPbytes", "decpbytes":
|
||||
case "bytes", "decbytes", "bits", "decbits", "kbytes", "decKbytes", "deckbytes", "mbytes", "decMbytes", "decmbytes", "gbytes", "decGbytes", "decgbytes", "tbytes", "decTbytes", "dectbytes", "pbytes", "decPbytes", "decpbytes", "By", "kBy", "MBy", "GBy", "TBy", "PBy":
|
||||
return DataConverter
|
||||
case "binBps", "Bps", "binbps", "bps", "KiBs", "Kibits", "KBs", "Kbits", "MiBs", "Mibits", "MBs", "Mbits", "GiBs", "Gibits", "GBs", "Gbits", "TiBs", "Tibits", "TBs", "Tbits", "PiBs", "Pibits", "PBs", "Pbits":
|
||||
case "binBps", "Bps", "binbps", "bps", "KiBs", "Kibits", "KBs", "Kbits", "MiBs", "Mibits", "MBs", "Mbits", "GiBs", "Gibits", "GBs", "Gbits", "TiBs", "Tibits", "TBs", "Tbits", "PiBs", "Pibits", "PBs", "Pbits", "By/s", "kBy/s", "MBy/s", "GBy/s", "TBy/s", "PBy/s", "bit/s", "kbit/s", "Mbit/s", "Gbit/s", "Tbit/s", "Pbit/s":
|
||||
return DataRateConverter
|
||||
case "percent", "percentunit":
|
||||
case "percent", "percentunit", "%":
|
||||
return PercentConverter
|
||||
case "bool", "bool_yes_no", "bool_true_false", "bool_1_0":
|
||||
return BoolConverter
|
||||
case "cps", "ops", "reqps", "rps", "wps", "iops", "cpm", "opm", "rpm", "wpm":
|
||||
case "cps", "ops", "reqps", "rps", "wps", "iops", "cpm", "opm", "rpm", "wpm", "{count}/s", "{ops}/s", "{req}/s", "{read}/s", "{write}/s", "{iops}/s", "{count}/min", "{ops}/min", "{read}/min", "{write}/min":
|
||||
return ThroughputConverter
|
||||
default:
|
||||
return NoneConverter
|
||||
|
||||
@@ -60,7 +60,7 @@ func (*dataConverter) Name() string {
|
||||
|
||||
func FromDataUnit(u Unit) float64 {
|
||||
switch u {
|
||||
case "bytes": // base 2
|
||||
case "bytes", "By": // base 2
|
||||
return Byte
|
||||
case "decbytes": // base 10
|
||||
return Byte
|
||||
@@ -68,23 +68,23 @@ func FromDataUnit(u Unit) float64 {
|
||||
return Bit
|
||||
case "decbits": // base 10
|
||||
return Bit
|
||||
case "kbytes": // base 2
|
||||
case "kbytes", "kBy": // base 2
|
||||
return Kibibyte
|
||||
case "decKbytes", "deckbytes": // base 10
|
||||
return Kilobyte
|
||||
case "mbytes": // base 2
|
||||
case "mbytes", "MBy": // base 2
|
||||
return Mebibyte
|
||||
case "decMbytes", "decmbytes": // base 10
|
||||
return Megabyte
|
||||
case "gbytes": // base 2
|
||||
case "gbytes", "GBy": // base 2
|
||||
return Gibibyte
|
||||
case "decGbytes", "decgbytes": // base 10
|
||||
return Gigabyte
|
||||
case "tbytes": // base 2
|
||||
case "tbytes", "TBy": // base 2
|
||||
return Tebibyte
|
||||
case "decTbytes", "dectbytes": // base 10
|
||||
return Terabyte
|
||||
case "pbytes": // base 2
|
||||
case "pbytes", "PBy": // base 2
|
||||
return Pebibyte
|
||||
case "decPbytes", "decpbytes": // base 10
|
||||
return Petabyte
|
||||
|
||||
@@ -59,51 +59,51 @@ func FromDataRateUnit(u Unit) float64 {
|
||||
switch u {
|
||||
case "binBps": // bytes/sec(IEC)
|
||||
return BytePerSecond
|
||||
case "Bps": // bytes/sec(SI)
|
||||
case "Bps", "By/s": // bytes/sec(SI)
|
||||
return BytePerSecond
|
||||
case "binbps": // bits/sec(IEC)
|
||||
return BitPerSecond
|
||||
case "bps": // bits/sec(SI)
|
||||
case "bps", "bit/s": // bits/sec(SI)
|
||||
return BitPerSecond
|
||||
case "KiBs": // kibibytes/sec
|
||||
return KibibytePerSecond
|
||||
case "Kibits": // kibibits/sec
|
||||
return KibibitPerSecond
|
||||
case "KBs": // kilobytes/sec
|
||||
case "KBs", "kBy/s": // kilobytes/sec
|
||||
return KilobytePerSecond
|
||||
case "Kbits": // kilobits/sec
|
||||
case "Kbits", "kbit/s": // kilobits/sec
|
||||
return KilobitPerSecond
|
||||
case "MiBs": // mebibytes/sec
|
||||
return MebibytePerSecond
|
||||
case "Mibits": // mebibits/sec
|
||||
return MebibitPerSecond
|
||||
case "MBs": // megabytes/sec
|
||||
case "MBs", "MBy/s": // megabytes/sec
|
||||
return MegabytePerSecond
|
||||
case "Mbits": // megabits/sec
|
||||
case "Mbits", "Mbit/s": // megabits/sec
|
||||
return MegabitPerSecond
|
||||
case "GiBs": // gibibytes/sec
|
||||
return GibibytePerSecond
|
||||
case "Gibits": // gibibits/sec
|
||||
return GibibitPerSecond
|
||||
case "GBs": // gigabytes/sec
|
||||
case "GBs", "GBy/s": // gigabytes/sec
|
||||
return GigabytePerSecond
|
||||
case "Gbits": // gigabits/sec
|
||||
case "Gbits", "Gbit/s": // gigabits/sec
|
||||
return GigabitPerSecond
|
||||
case "TiBs": // tebibytes/sec
|
||||
return TebibytePerSecond
|
||||
case "Tibits": // tebibits/sec
|
||||
return TebibitPerSecond
|
||||
case "TBs": // terabytes/sec
|
||||
case "TBs", "TBy/s": // terabytes/sec
|
||||
return TerabytePerSecond
|
||||
case "Tbits": // terabits/sec
|
||||
case "Tbits", "Tbit/s": // terabits/sec
|
||||
return TerabitPerSecond
|
||||
case "PiBs": // pebibytes/sec
|
||||
return PebibytePerSecond
|
||||
case "Pibits": // pebibits/sec
|
||||
return PebibitPerSecond
|
||||
case "PBs": // petabytes/sec
|
||||
case "PBs", "PBy/s": // petabytes/sec
|
||||
return PetabytePerSecond
|
||||
case "Pbits": // petabits/sec
|
||||
case "Pbits", "Pbit/s": // petabits/sec
|
||||
return PetabitPerSecond
|
||||
default:
|
||||
return 1
|
||||
|
||||
@@ -36,10 +36,16 @@ func TestDataRate(t *testing.T) {
|
||||
|
||||
// 8 bits = 1 byte
|
||||
assert.Equal(t, Value{F: 1, U: "binBps"}, dataRateConverter.Convert(Value{F: 8, U: "binbps"}, "binBps"))
|
||||
// 8 bits = 1 byte
|
||||
assert.Equal(t, Value{F: 1, U: "Bps"}, dataRateConverter.Convert(Value{F: 8, U: "bps"}, "Bps"))
|
||||
// 8 bits = 1 byte
|
||||
assert.Equal(t, Value{F: 1, U: "By/s"}, dataRateConverter.Convert(Value{F: 8, U: "bit/s"}, "By/s"))
|
||||
// 1024 bytes = 1 kbytes
|
||||
assert.Equal(t, Value{F: 1, U: "KiBs"}, dataRateConverter.Convert(Value{F: 1024, U: "binBps"}, "KiBs"))
|
||||
// 1 byte = 8 bits
|
||||
assert.Equal(t, Value{F: 8, U: "binbps"}, dataRateConverter.Convert(Value{F: 1, U: "binBps"}, "binbps"))
|
||||
// 1 byte = 8 bits
|
||||
assert.Equal(t, Value{F: 8, U: "bit/s"}, dataRateConverter.Convert(Value{F: 1, U: "Bps"}, "bit/s"))
|
||||
// 1 mbytes = 1024 kbytes
|
||||
assert.Equal(t, Value{F: 1, U: "MiBs"}, dataRateConverter.Convert(Value{F: 1024, U: "KiBs"}, "MiBs"))
|
||||
// 1 kbytes = 1024 bytes
|
||||
@@ -57,6 +63,10 @@ func TestDataRate(t *testing.T) {
|
||||
// 1 gbytes = 1024 * 1024 kbytes
|
||||
assert.Equal(t, Value{F: 1024 * 1024, U: "KiBs"}, dataRateConverter.Convert(Value{F: 1, U: "GiBs"}, "KiBs"))
|
||||
// 1 gbytes = 1024 * 1024 * 1024 bytes
|
||||
assert.Equal(t, Value{F: (1024 * 1024 * 1024 * 8) / 1024, U: "Kibits"}, dataRateConverter.Convert(Value{F: 1, U: "GiBs"}, "Kibits"))
|
||||
// 1 gbytes = 1024 * 1024 * 1024 bytes
|
||||
assert.Equal(t, Value{F: float64(1024*1024*1024) / 1000.0, U: "kBy/s"}, dataRateConverter.Convert(Value{F: 1, U: "GiBs"}, "kBy/s"))
|
||||
// 1 gbytes = 1024 * 1024 * 1024 bytes
|
||||
assert.Equal(t, Value{F: 1024 * 1024 * 1024, U: "binBps"}, dataRateConverter.Convert(Value{F: 1, U: "GiBs"}, "binBps"))
|
||||
// 1024 * 1024 bytes = 1 mbytes
|
||||
assert.Equal(t, Value{F: 1, U: "MiBs"}, dataRateConverter.Convert(Value{F: 1024 * 1024, U: "binBps"}, "MiBs"))
|
||||
|
||||
@@ -10,8 +10,10 @@ func TestData(t *testing.T) {
|
||||
dataConverter := NewDataConverter()
|
||||
// 8 bits = 1 byte
|
||||
assert.Equal(t, Value{F: 1, U: "bytes"}, dataConverter.Convert(Value{F: 8, U: "bits"}, "bytes"))
|
||||
assert.Equal(t, Value{F: 1, U: "By"}, dataConverter.Convert(Value{F: 8, U: "bits"}, "By"))
|
||||
// 1024 bytes = 1 kbytes
|
||||
assert.Equal(t, Value{F: 1, U: "kbytes"}, dataConverter.Convert(Value{F: 1024, U: "bytes"}, "kbytes"))
|
||||
assert.Equal(t, Value{F: 1, U: "kBy"}, dataConverter.Convert(Value{F: 1024, U: "bytes"}, "kBy"))
|
||||
// 1 byte = 8 bits
|
||||
assert.Equal(t, Value{F: 8, U: "bits"}, dataConverter.Convert(Value{F: 1, U: "bytes"}, "bits"))
|
||||
// 1 mbytes = 1024 kbytes
|
||||
@@ -20,6 +22,7 @@ func TestData(t *testing.T) {
|
||||
assert.Equal(t, Value{F: 1024, U: "bytes"}, dataConverter.Convert(Value{F: 1, U: "kbytes"}, "bytes"))
|
||||
// 1024 kbytes = 1 mbytes
|
||||
assert.Equal(t, Value{F: 1, U: "mbytes"}, dataConverter.Convert(Value{F: 1024, U: "kbytes"}, "mbytes"))
|
||||
assert.Equal(t, Value{F: 1, U: "MBy"}, dataConverter.Convert(Value{F: 1024, U: "kbytes"}, "MBy"))
|
||||
// 1 mbytes = 1024 * 1024 bytes
|
||||
assert.Equal(t, Value{F: 1024 * 1024, U: "bytes"}, dataConverter.Convert(Value{F: 1, U: "mbytes"}, "bytes"))
|
||||
// 1024 mbytes = 1 gbytes
|
||||
@@ -42,6 +45,10 @@ func TestData(t *testing.T) {
|
||||
assert.Equal(t, Value{F: 1024 * 1024 * 1024 * 1024, U: "bytes"}, dataConverter.Convert(Value{F: 1, U: "tbytes"}, "bytes"))
|
||||
// 1024 tbytes = 1 pbytes
|
||||
assert.Equal(t, Value{F: 1, U: "pbytes"}, dataConverter.Convert(Value{F: 1024, U: "tbytes"}, "pbytes"))
|
||||
// 1024 tbytes = 1 pbytes
|
||||
assert.Equal(t, Value{F: 1, U: "PBy"}, dataConverter.Convert(Value{F: 1024, U: "tbytes"}, "PBy"))
|
||||
// 1 pbytes = 1024 tbytes
|
||||
assert.Equal(t, Value{F: 1024, U: "tbytes"}, dataConverter.Convert(Value{F: 1, U: "pbytes"}, "tbytes"))
|
||||
// 1024 pbytes = 1 tbytes
|
||||
assert.Equal(t, Value{F: 1024, U: "TBy"}, dataConverter.Convert(Value{F: 1, U: "pbytes"}, "TBy"))
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ func (*percentConverter) Name() string {
|
||||
|
||||
func FromPercentUnit(u Unit) float64 {
|
||||
switch u {
|
||||
case "percent":
|
||||
case "percent", "%":
|
||||
return 1
|
||||
case "percentunit":
|
||||
return 100
|
||||
|
||||
@@ -13,4 +13,5 @@ func TestPercentConverter(t *testing.T) {
|
||||
assert.Equal(t, Value{F: 100, U: "percent"}, percentConverter.Convert(Value{F: 1, U: "percentunit"}, "percent"))
|
||||
assert.Equal(t, Value{F: 1, U: "percentunit"}, percentConverter.Convert(Value{F: 100, U: "percent"}, "percentunit"))
|
||||
assert.Equal(t, Value{F: 0.01, U: "percentunit"}, percentConverter.Convert(Value{F: 1, U: "percent"}, "percentunit"))
|
||||
assert.Equal(t, Value{F: 1, U: "percent"}, percentConverter.Convert(Value{F: 1, U: "%"}, "percent"))
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ func FromTimeUnit(u Unit) Duration {
|
||||
return Decisecond
|
||||
case "s":
|
||||
return Second
|
||||
case "m":
|
||||
case "m", "min":
|
||||
return Minute
|
||||
case "h":
|
||||
return Hour
|
||||
|
||||
@@ -24,6 +24,8 @@ func TestDurationConvert(t *testing.T) {
|
||||
assert.Equal(t, Value{F: 60, U: "s"}, timeConverter.Convert(Value{F: 1, U: "m"}, "s"))
|
||||
// 60 m = 1 h
|
||||
assert.Equal(t, Value{F: 1, U: "h"}, timeConverter.Convert(Value{F: 60, U: "m"}, "h"))
|
||||
// 60 min = 1 h
|
||||
assert.Equal(t, Value{F: 1, U: "h"}, timeConverter.Convert(Value{F: 60, U: "min"}, "h"))
|
||||
// 168 h = 1 w
|
||||
assert.Equal(t, Value{F: 1, U: "w"}, timeConverter.Convert(Value{F: 168, U: "h"}, "w"))
|
||||
// 1 h = 60 m
|
||||
|
||||
@@ -20,7 +20,7 @@ func (*dataFormatter) Name() string {
|
||||
|
||||
func (f *dataFormatter) Format(value float64, unit string) string {
|
||||
switch unit {
|
||||
case "bytes":
|
||||
case "bytes", "By":
|
||||
return humanize.IBytes(uint64(value))
|
||||
case "decbytes":
|
||||
return humanize.Bytes(uint64(value))
|
||||
@@ -28,23 +28,23 @@ func (f *dataFormatter) Format(value float64, unit string) string {
|
||||
return humanize.IBytes(uint64(value * converter.Bit))
|
||||
case "decbits":
|
||||
return humanize.Bytes(uint64(value * converter.Bit))
|
||||
case "kbytes":
|
||||
case "kbytes", "kBy":
|
||||
return humanize.IBytes(uint64(value * converter.Kibibit))
|
||||
case "decKbytes", "deckbytes":
|
||||
return humanize.IBytes(uint64(value * converter.Kilobit))
|
||||
case "mbytes":
|
||||
case "mbytes", "MBy":
|
||||
return humanize.IBytes(uint64(value * converter.Mebibit))
|
||||
case "decMbytes", "decmbytes":
|
||||
return humanize.Bytes(uint64(value * converter.Megabit))
|
||||
case "gbytes":
|
||||
case "gbytes", "GBy":
|
||||
return humanize.IBytes(uint64(value * converter.Gibibit))
|
||||
case "decGbytes", "decgbytes":
|
||||
return humanize.Bytes(uint64(value * converter.Gigabit))
|
||||
case "tbytes":
|
||||
case "tbytes", "TBy":
|
||||
return humanize.IBytes(uint64(value * converter.Tebibit))
|
||||
case "decTbytes", "dectbytes":
|
||||
return humanize.Bytes(uint64(value * converter.Terabit))
|
||||
case "pbytes":
|
||||
case "pbytes", "PBy":
|
||||
return humanize.IBytes(uint64(value * converter.Pebibit))
|
||||
case "decPbytes", "decpbytes":
|
||||
return humanize.Bytes(uint64(value * converter.Petabit))
|
||||
|
||||
@@ -22,51 +22,51 @@ func (f *dataRateFormatter) Format(value float64, unit string) string {
|
||||
switch unit {
|
||||
case "binBps":
|
||||
return humanize.IBytes(uint64(value)) + "/s"
|
||||
case "Bps":
|
||||
case "Bps", "By/s":
|
||||
return humanize.Bytes(uint64(value)) + "/s"
|
||||
case "binbps":
|
||||
return humanize.IBytes(uint64(value*converter.BitPerSecond)) + "/s"
|
||||
case "bps":
|
||||
case "bps", "bit/s":
|
||||
return humanize.Bytes(uint64(value*converter.BitPerSecond)) + "/s"
|
||||
case "KiBs":
|
||||
return humanize.IBytes(uint64(value*converter.KibibitPerSecond)) + "/s"
|
||||
case "Kibits":
|
||||
return humanize.IBytes(uint64(value*converter.KibibytePerSecond)) + "/s"
|
||||
case "KBs":
|
||||
case "KBs", "kBy/s":
|
||||
return humanize.IBytes(uint64(value*converter.KilobitPerSecond)) + "/s"
|
||||
case "Kbits":
|
||||
case "Kbits", "kbit/s":
|
||||
return humanize.IBytes(uint64(value*converter.KilobytePerSecond)) + "/s"
|
||||
case "MiBs":
|
||||
return humanize.IBytes(uint64(value*converter.MebibitPerSecond)) + "/s"
|
||||
case "Mibits":
|
||||
return humanize.IBytes(uint64(value*converter.MebibytePerSecond)) + "/s"
|
||||
case "MBs":
|
||||
case "MBs", "MBy/s":
|
||||
return humanize.IBytes(uint64(value*converter.MegabitPerSecond)) + "/s"
|
||||
case "Mbits":
|
||||
case "Mbits", "Mbit/s":
|
||||
return humanize.IBytes(uint64(value*converter.MegabytePerSecond)) + "/s"
|
||||
case "GiBs":
|
||||
return humanize.IBytes(uint64(value*converter.GibibitPerSecond)) + "/s"
|
||||
case "Gibits":
|
||||
return humanize.IBytes(uint64(value*converter.GibibytePerSecond)) + "/s"
|
||||
case "GBs":
|
||||
case "GBs", "GBy/s":
|
||||
return humanize.IBytes(uint64(value*converter.GigabitPerSecond)) + "/s"
|
||||
case "Gbits":
|
||||
case "Gbits", "Gbit/s":
|
||||
return humanize.IBytes(uint64(value*converter.GigabytePerSecond)) + "/s"
|
||||
case "TiBs":
|
||||
return humanize.IBytes(uint64(value*converter.TebibitPerSecond)) + "/s"
|
||||
case "Tibits":
|
||||
return humanize.IBytes(uint64(value*converter.TebibytePerSecond)) + "/s"
|
||||
case "TBs":
|
||||
case "TBs", "TBy/s":
|
||||
return humanize.IBytes(uint64(value*converter.TerabitPerSecond)) + "/s"
|
||||
case "Tbits":
|
||||
case "Tbits", "Tbit/s":
|
||||
return humanize.IBytes(uint64(value*converter.TerabytePerSecond)) + "/s"
|
||||
case "PiBs":
|
||||
return humanize.IBytes(uint64(value*converter.PebibitPerSecond)) + "/s"
|
||||
case "Pibits":
|
||||
return humanize.IBytes(uint64(value*converter.PebibytePerSecond)) + "/s"
|
||||
case "PBs":
|
||||
case "PBs", "PBy/s":
|
||||
return humanize.IBytes(uint64(value*converter.PetabitPerSecond)) + "/s"
|
||||
case "Pbits":
|
||||
case "Pbits", "Pbit/s":
|
||||
return humanize.IBytes(uint64(value*converter.PetabytePerSecond)) + "/s"
|
||||
}
|
||||
// When unit is not matched, return the value as it is.
|
||||
|
||||
@@ -10,14 +10,25 @@ func TestData(t *testing.T) {
|
||||
dataFormatter := NewDataFormatter()
|
||||
|
||||
assert.Equal(t, "1 B", dataFormatter.Format(1, "bytes"))
|
||||
assert.Equal(t, "1 B", dataFormatter.Format(1, "By"))
|
||||
assert.Equal(t, "1.0 KiB", dataFormatter.Format(1024, "bytes"))
|
||||
assert.Equal(t, "1.0 KiB", dataFormatter.Format(1024, "By"))
|
||||
assert.Equal(t, "2.3 GiB", dataFormatter.Format(2.3*1024, "mbytes"))
|
||||
assert.Equal(t, "2.3 GiB", dataFormatter.Format(2.3*1024, "MBy"))
|
||||
assert.Equal(t, "1.0 MiB", dataFormatter.Format(1024*1024, "bytes"))
|
||||
assert.Equal(t, "1.0 MiB", dataFormatter.Format(1024*1024, "By"))
|
||||
assert.Equal(t, "69 TiB", dataFormatter.Format(69*1024*1024, "mbytes"))
|
||||
assert.Equal(t, "69 TiB", dataFormatter.Format(69*1024*1024, "MBy"))
|
||||
assert.Equal(t, "102 KiB", dataFormatter.Format(102*1024, "bytes"))
|
||||
assert.Equal(t, "102 KiB", dataFormatter.Format(102*1024, "By"))
|
||||
assert.Equal(t, "240 MiB", dataFormatter.Format(240*1024, "kbytes"))
|
||||
assert.Equal(t, "240 MiB", dataFormatter.Format(240*1024, "kBy"))
|
||||
assert.Equal(t, "1.0 GiB", dataFormatter.Format(1024*1024, "kbytes"))
|
||||
assert.Equal(t, "1.0 GiB", dataFormatter.Format(1024*1024, "kBy"))
|
||||
assert.Equal(t, "23 GiB", dataFormatter.Format(23*1024*1024, "kbytes"))
|
||||
assert.Equal(t, "23 GiB", dataFormatter.Format(23*1024*1024, "kBy"))
|
||||
assert.Equal(t, "32 TiB", dataFormatter.Format(32*1024*1024*1024, "kbytes"))
|
||||
assert.Equal(t, "32 TiB", dataFormatter.Format(32*1024*1024*1024, "kBy"))
|
||||
assert.Equal(t, "24 MiB", dataFormatter.Format(24, "mbytes"))
|
||||
assert.Equal(t, "24 MiB", dataFormatter.Format(24, "MBy"))
|
||||
}
|
||||
|
||||
@@ -18,17 +18,17 @@ var (
|
||||
|
||||
func FromUnit(u string) Formatter {
|
||||
switch u {
|
||||
case "ns", "us", "µs", "ms", "s", "m", "h", "d":
|
||||
case "ns", "us", "µs", "ms", "s", "m", "h", "d", "min":
|
||||
return DurationFormatter
|
||||
case "bytes", "decbytes", "bits", "decbits", "kbytes", "decKbytes", "deckbytes", "mbytes", "decMbytes", "decmbytes", "gbytes", "decGbytes", "decgbytes", "tbytes", "decTbytes", "dectbytes", "pbytes", "decPbytes", "decpbytes":
|
||||
case "bytes", "decbytes", "bits", "decbits", "kbytes", "decKbytes", "deckbytes", "mbytes", "decMbytes", "decmbytes", "gbytes", "decGbytes", "decgbytes", "tbytes", "decTbytes", "dectbytes", "pbytes", "decPbytes", "decpbytes", "By", "kBy", "MBy", "GBy", "TBy", "PBy":
|
||||
return DataFormatter
|
||||
case "binBps", "Bps", "binbps", "bps", "KiBs", "Kibits", "KBs", "Kbits", "MiBs", "Mibits", "MBs", "Mbits", "GiBs", "Gibits", "GBs", "Gbits", "TiBs", "Tibits", "TBs", "Tbits", "PiBs", "Pibits", "PBs", "Pbits":
|
||||
case "binBps", "Bps", "binbps", "bps", "KiBs", "Kibits", "KBs", "Kbits", "MiBs", "Mibits", "MBs", "Mbits", "GiBs", "Gibits", "GBs", "Gbits", "TiBs", "Tibits", "TBs", "Tbits", "PiBs", "Pibits", "PBs", "Pbits", "By/s", "kBy/s", "MBy/s", "GBy/s", "TBy/s", "PBy/s", "bit/s", "kbit/s", "Mbit/s", "Gbit/s", "Tbit/s", "Pbit/s":
|
||||
return DataRateFormatter
|
||||
case "percent", "percentunit":
|
||||
case "percent", "percentunit", "%":
|
||||
return PercentFormatter
|
||||
case "bool", "bool_yes_no", "bool_true_false", "bool_1_0":
|
||||
return BoolFormatter
|
||||
case "cps", "ops", "reqps", "rps", "wps", "iops", "cpm", "opm", "rpm", "wpm":
|
||||
case "cps", "ops", "reqps", "rps", "wps", "iops", "cpm", "opm", "rpm", "wpm", "{count}/s", "{ops}/s", "{req}/s", "{read}/s", "{write}/s", "{iops}/s", "{count}/min", "{ops}/min", "{read}/min", "{write}/min":
|
||||
return ThroughputFormatter
|
||||
default:
|
||||
return NoneFormatter
|
||||
|
||||
@@ -22,7 +22,7 @@ func toPercentUnit(value float64, decimals DecimalCount) string {
|
||||
|
||||
func (f *percentFormatter) Format(value float64, unit string) string {
|
||||
switch unit {
|
||||
case "percent":
|
||||
case "percent", "%":
|
||||
return toPercent(value, nil)
|
||||
case "percentunit":
|
||||
return toPercentUnit(value, nil)
|
||||
|
||||
@@ -22,25 +22,25 @@ func simpleCountUnit(value float64, decimals *int, symbol string) string {
|
||||
|
||||
func (f *throughputFormatter) Format(value float64, unit string) string {
|
||||
switch unit {
|
||||
case "cps":
|
||||
case "cps", "{count}/s":
|
||||
return simpleCountUnit(value, nil, "c/s")
|
||||
case "ops":
|
||||
case "ops", "{ops}/s":
|
||||
return simpleCountUnit(value, nil, "op/s")
|
||||
case "reqps":
|
||||
case "reqps", "{req}/s":
|
||||
return simpleCountUnit(value, nil, "req/s")
|
||||
case "rps":
|
||||
case "rps", "{read}/s":
|
||||
return simpleCountUnit(value, nil, "r/s")
|
||||
case "wps":
|
||||
case "wps", "{write}/s":
|
||||
return simpleCountUnit(value, nil, "w/s")
|
||||
case "iops":
|
||||
case "iops", "{iops}/s":
|
||||
return simpleCountUnit(value, nil, "iops")
|
||||
case "cpm":
|
||||
case "cpm", "{count}/min":
|
||||
return simpleCountUnit(value, nil, "c/m")
|
||||
case "opm":
|
||||
case "opm", "{ops}/min":
|
||||
return simpleCountUnit(value, nil, "op/m")
|
||||
case "rpm":
|
||||
case "rpm", "{read}/min":
|
||||
return simpleCountUnit(value, nil, "r/m")
|
||||
case "wpm":
|
||||
case "wpm", "{write}/min":
|
||||
return simpleCountUnit(value, nil, "w/m")
|
||||
}
|
||||
// When unit is not matched, return the value as it is.
|
||||
|
||||
@@ -10,6 +10,11 @@ func TestThroughput(t *testing.T) {
|
||||
throughputFormatter := NewThroughputFormatter()
|
||||
|
||||
assert.Equal(t, "10 req/s", throughputFormatter.Format(10, "reqps"))
|
||||
assert.Equal(t, "10 req/s", throughputFormatter.Format(10, "{req}/s"))
|
||||
assert.Equal(t, "1K req/s", throughputFormatter.Format(1000, "reqps"))
|
||||
assert.Equal(t, "1K req/s", throughputFormatter.Format(1000, "{req}/s"))
|
||||
assert.Equal(t, "1M req/s", throughputFormatter.Format(1000000, "reqps"))
|
||||
assert.Equal(t, "1M req/s", throughputFormatter.Format(1000000, "{req}/s"))
|
||||
assert.Equal(t, "10 c/m", throughputFormatter.Format(10, "cpm"))
|
||||
assert.Equal(t, "10 c/m", throughputFormatter.Format(10, "{count}/min"))
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ func (f *durationFormatter) Format(value float64, unit string) string {
|
||||
return toMilliSeconds(value)
|
||||
case "s":
|
||||
return toSeconds(value)
|
||||
case "m":
|
||||
case "m", "min":
|
||||
return toMinutes(value)
|
||||
case "h":
|
||||
return toHours(value)
|
||||
|
||||
@@ -26,4 +26,5 @@ func TestDuration(t *testing.T) {
|
||||
assert.Equal(t, "1.82 min", durationFormatter.Format(109200000000, "ns"))
|
||||
assert.Equal(t, "1.27 day", durationFormatter.Format(109800000000000, "ns"))
|
||||
assert.Equal(t, "2 day", durationFormatter.Format(172800000, "ms"))
|
||||
assert.Equal(t, "1 hour", durationFormatter.Format(60, "min"))
|
||||
}
|
||||
|
||||
@@ -1219,6 +1219,35 @@ func TestThresholdRuleUnitCombinations(t *testing.T) {
|
||||
matchType: "1", // Once
|
||||
target: 200, // 200 GB
|
||||
},
|
||||
{
|
||||
targetUnit: "decgbytes",
|
||||
yAxisUnit: "By",
|
||||
values: [][]interface{}{
|
||||
{float64(2863284053), "attr", time.Now()}, // 2.86 GB
|
||||
{float64(2863388842), "attr", time.Now().Add(1 * time.Second)}, // 2.86 GB
|
||||
{float64(300947400), "attr", time.Now().Add(2 * time.Second)}, // 0.3 GB
|
||||
{float64(299316000), "attr", time.Now().Add(3 * time.Second)}, // 0.3 GB
|
||||
{float64(66640400.00000001), "attr", time.Now().Add(4 * time.Second)}, // 66.64 MB
|
||||
},
|
||||
expectAlerts: 0,
|
||||
compareOp: "1", // Above
|
||||
matchType: "1", // Once
|
||||
target: 200, // 200 GB
|
||||
},
|
||||
{
|
||||
targetUnit: "h",
|
||||
yAxisUnit: "min",
|
||||
values: [][]interface{}{
|
||||
{float64(55), "attr", time.Now()}, // 55 minutes
|
||||
{float64(57), "attr", time.Now().Add(1 * time.Minute)}, // 57 minutes
|
||||
{float64(30), "attr", time.Now().Add(2 * time.Minute)}, // 30 minutes
|
||||
{float64(29), "attr", time.Now().Add(3 * time.Minute)}, // 29 minutes
|
||||
},
|
||||
expectAlerts: 0,
|
||||
compareOp: "1", // Above
|
||||
matchType: "1", // Once
|
||||
target: 1, // 1 hour
|
||||
},
|
||||
}
|
||||
|
||||
for idx, c := range cases {
|
||||
|
||||
@@ -86,5 +86,26 @@ var (
|
||||
matchType: "1", // Once
|
||||
target: 200, // 200 GB
|
||||
},
|
||||
{
|
||||
targetUnit: "decgbytes",
|
||||
yAxisUnit: "By",
|
||||
values: [][]interface{}{
|
||||
{float64(2863284053), "attr", time.Now()}, // 2.86 GB
|
||||
{float64(2863388842), "attr", time.Now().Add(1 * time.Second)}, // 2.86 GB
|
||||
{float64(300947400), "attr", time.Now().Add(2 * time.Second)}, // 0.3 GB
|
||||
{float64(299316000), "attr", time.Now().Add(3 * time.Second)}, // 0.3 GB
|
||||
{float64(66640400.00000001), "attr", time.Now().Add(4 * time.Second)}, // 66.64 MB
|
||||
},
|
||||
metaValues: [][]interface{}{},
|
||||
createTableValues: [][]interface{}{
|
||||
{"statement"},
|
||||
},
|
||||
attrMetaValues: [][]interface{}{},
|
||||
resourceMetaValues: [][]interface{}{},
|
||||
expectAlerts: 0,
|
||||
compareOp: "1", // Above
|
||||
matchType: "1", // Once
|
||||
target: 200, // 200 GB
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user