diff --git a/changelog/unreleased/issue-24982.toml b/changelog/unreleased/issue-24982.toml new file mode 100644 index 0000000000..f93cbf50fc --- /dev/null +++ b/changelog/unreleased/issue-24982.toml @@ -0,0 +1,5 @@ +type = "f" +message = "Use GiB units for defining traffic limits in the chart. Add a label indicating that limits are calculated in UTC." + +issues = ["24982"] +pulls = ["24983"] diff --git a/graylog2-web-interface/src/components/common/Graph/TrafficGraph.tsx b/graylog2-web-interface/src/components/common/Graph/TrafficGraph.tsx index bf7d4c88ed..067eecdfb7 100644 --- a/graylog2-web-interface/src/components/common/Graph/TrafficGraph.tsx +++ b/graylog2-web-interface/src/components/common/Graph/TrafficGraph.tsx @@ -63,7 +63,7 @@ const TrafficGraph = ({ width, traffic, trafficLimit = undefined }: Props) => { y: yValues, ...getHoverTemplateSettings({ convertedValues: yValues, - unit: FieldUnit.fromJSON({ abbrev: 'b', unit_type: 'size' }), + unit: FieldUnit.fromJSON({ abbrev: 'b', unit_type: 'binary_size' }), }), }, ], @@ -122,14 +122,14 @@ const TrafficGraph = ({ width, traffic, trafficLimit = undefined }: Props) => { const notZoomedLayout = useMemo( () => ({ rangemode: 'tozero', - ...(getFormatSettingsByData('size', valuesToGetFormatSettings) as GeneratedLayout), + ...(getFormatSettingsByData('binary_size', valuesToGetFormatSettings) as GeneratedLayout), }), [valuesToGetFormatSettings], ); const zoomedLayout = useMemo( () => ({ rangemode: 'tozero', - ...(getFormatSettingsByData('size', yValues) as GeneratedLayout), + ...(getFormatSettingsByData('binary_size', yValues) as GeneratedLayout), }), [yValues], ); @@ -143,7 +143,7 @@ const TrafficGraph = ({ width, traffic, trafficLimit = undefined }: Props) => { xaxis: { type: 'date', title: { - text: 'Time', + text: 'Time shown in UTC', }, }, hovermode: 'x', diff --git a/graylog2-web-interface/src/components/common/Graph/TrafficGraphWithDaySelect.tsx b/graylog2-web-interface/src/components/common/Graph/TrafficGraphWithDaySelect.tsx index e6acdb8c21..9cf38f377d 100644 --- a/graylog2-web-interface/src/components/common/Graph/TrafficGraphWithDaySelect.tsx +++ b/graylog2-web-interface/src/components/common/Graph/TrafficGraphWithDaySelect.tsx @@ -89,7 +89,7 @@ const TrafficGraphWithDaySelect = ({ traffic, trafficLimit = undefined, title = const unixTraffic = useMemo(() => (traffic ? formatTrafficData(traffic) : null), [traffic]); const formattedTotalTraffic = useMemo(() => { - const prettified = getPrettifiedValue(bytesOut, { abbrev: 'b', unitType: 'size' }); + const prettified = getPrettifiedValue(bytesOut, { abbrev: 'b', unitType: 'binary_size' }); return formatValueWithUnitLabel(prettified?.value, prettified.unit.abbrev); }, [bytesOut]); diff --git a/graylog2-web-interface/src/views/components/aggregationwizard/units/FieldUnitPopover.tsx b/graylog2-web-interface/src/views/components/aggregationwizard/units/FieldUnitPopover.tsx index 04d4fac0f6..68f43b3d0e 100644 --- a/graylog2-web-interface/src/views/components/aggregationwizard/units/FieldUnitPopover.tsx +++ b/graylog2-web-interface/src/views/components/aggregationwizard/units/FieldUnitPopover.tsx @@ -24,7 +24,7 @@ import Popover from 'components/common/Popover'; import { HoverForHelp, ModalButtonToolbar } from 'components/common'; import { Alert, Button, Input } from 'components/bootstrap'; import type { Unit } from 'views/components/visualizations/utils/unitConverters'; -import { mappedUnitsFromJSON as units } from 'views/components/visualizations/utils/unitConverters'; +import { mappedUnitsFromJSONForAggregation as units } from 'views/components/visualizations/utils/unitConverters'; import type { FieldUnitsFormValues } from 'views/types'; import type FieldUnit from 'views/logic/aggregationbuilder/FieldUnit'; import getUnitTextLabel from 'views/components/visualizations/utils/getUnitTextLabel'; diff --git a/graylog2-web-interface/src/views/components/visualizations/utils/__tests__/unitConverters.test.ts b/graylog2-web-interface/src/views/components/visualizations/utils/__tests__/unitConverters.test.ts index 8d5d2ac348..0c0be14de4 100644 --- a/graylog2-web-interface/src/views/components/visualizations/utils/__tests__/unitConverters.test.ts +++ b/graylog2-web-interface/src/views/components/visualizations/utils/__tests__/unitConverters.test.ts @@ -71,6 +71,22 @@ describe('Unit converter functions', () => { }); }); + it('for binary_size should convert bigger unit to bytes', () => { + const result = convertValueToBaseUnit(1, { abbrev: 'KiB', unitType: 'binary_size' }); + + expect(result).toEqual({ + value: 1024, + unit: { + type: 'base', + abbrev: 'b', + name: 'byte', + unitType: 'binary_size', + useInPrettier: true, + conversion: undefined, + }, + }); + }); + it('for percent should convert bigger unit to decimal percent', () => { const result = convertValueToBaseUnit(50, { abbrev: '%', unitType: 'percent' }); @@ -201,6 +217,75 @@ describe('Unit converter functions', () => { }); }); + it('for binary_size should convert smaller unit (KiB) to bigger (GiB)', () => { + const result = convertValueToUnit( + 1048576, + { abbrev: 'KiB', unitType: 'binary_size' }, + { abbrev: 'GiB', unitType: 'binary_size' }, + ); + + expect(result).toEqual({ + value: 1, + unit: { + type: 'derived', + abbrev: 'GiB', + name: 'gibibyte', + unitType: 'binary_size', + useInPrettier: true, + conversion: { + value: 1073741824, + action: 'MULTIPLY', + }, + }, + }); + }); + + it('for binary_size should convert bytes to GiB', () => { + const result = convertValueToUnit( + 3221225472, + { abbrev: 'b', unitType: 'binary_size' }, + { abbrev: 'GiB', unitType: 'binary_size' }, + ); + + expect(result).toEqual({ + value: 3, + unit: { + type: 'derived', + abbrev: 'GiB', + name: 'gibibyte', + unitType: 'binary_size', + useInPrettier: true, + conversion: { + value: 1073741824, + action: 'MULTIPLY', + }, + }, + }); + }); + + it('for binary_size should convert bytes to MiB', () => { + const result = convertValueToUnit( + 1572864, + { abbrev: 'b', unitType: 'binary_size' }, + { abbrev: 'MiB', unitType: 'binary_size' }, + ); + + expect(result).toEqual({ + value: 1.5, + unit: { + type: 'derived', + abbrev: 'MiB', + name: 'mebibyte', + unitType: 'binary_size', + useInPrettier: true, + conversion: { + value: 1048576, + action: 'MULTIPLY', + }, + }, + }); + }); + it('return nulls when some params where missed', () => { const result1 = convertValueToUnit(50, { abbrev: 'Gb', unitType: 'size' }, undefined); const result2 = convertValueToUnit(50, undefined, { abbrev: 'Gb', unitType: 'size' }); @@ -301,6 +386,82 @@ describe('Unit converter functions', () => { }); }); + it('for binary_size should convert smaller then 1 value to the value with lower unit', () => { + const result = getPrettifiedValue(0.5, { abbrev: 'GiB', unitType: 'binary_size' }); + + expect(result).toEqual({ + value: 512, + unit: { + type: 'derived', + abbrev: 'MiB', + name: 'mebibyte', + unitType: 'binary_size', + useInPrettier: true, + conversion: { + value: 1048576, + action: 'MULTIPLY', + }, + }, + }); + }); + + it('for binary_size should convert bigger then 1 value to the value with higher unit', () => { + const result = getPrettifiedValue(2048, { abbrev: 'MiB', unitType: 'binary_size' }); + + expect(result).toEqual({ + value: 2, + unit: { + type: 'derived', + abbrev: 'GiB', + name: 'gibibyte', + unitType: 'binary_size', + useInPrettier: true, + conversion: { + value: 1073741824, + action: 'MULTIPLY', + }, + }, + }); + }); + + it('for binary_size should prettify larger bytes to GiB', () => { + const result = getPrettifiedValue(3221225472, { abbrev: 'b', unitType: 'binary_size' }); + + expect(result).toEqual({ + value: 3, + unit: { + type: 'derived', + abbrev: 'GiB', + name: 'gibibyte', + unitType: 'binary_size', + useInPrettier: true, + conversion: { + value: 1073741824, + action: 'MULTIPLY', + }, + }, + }); + }); + + it('for binary_size should prettify larger bytes to MiB', () => { + const result = getPrettifiedValue(1572864, { abbrev: 'b', unitType: 'binary_size' }); + + expect(result).toEqual({ + value: 1.5, + unit: { + type: 'derived', + abbrev: 'MiB', + name: 'mebibyte', + unitType: 'binary_size', + useInPrettier: true, + conversion: { + value: 1048576, + action: 'MULTIPLY', + }, + }, + }); + }); + it('for percent should always convert to integer % unit', () => { const result1 = getPrettifiedValue(100, { abbrev: 'd%', unitType: 'percent' }); const result2 = getPrettifiedValue(10000, { abbrev: '%', unitType: 'percent' }); diff --git a/graylog2-web-interface/src/views/components/visualizations/utils/chartLayoutGenerators.ts b/graylog2-web-interface/src/views/components/visualizations/utils/chartLayoutGenerators.ts index c27bfd6fc1..dca56c2c12 100644 --- a/graylog2-web-interface/src/views/components/visualizations/utils/chartLayoutGenerators.ts +++ b/graylog2-web-interface/src/views/components/visualizations/utils/chartLayoutGenerators.ts @@ -117,6 +117,8 @@ export const getFormatSettingsByData = (unitTypeKey: FieldUnitType | DefaultAxis }; case 'size': return getFormatSettingsWithCustomTickVals(values, 'size'); + case 'binary_size': + return getFormatSettingsWithCustomTickVals(values, 'binary_size'); case 'time': return getFormatSettingsWithCustomTickVals(values, 'time'); default: @@ -311,7 +313,11 @@ export const getHoverTemplateSettings = ({ unit: FieldUnit; name?: string; }): { text: Array; hovertemplate: string; meta: string } | {} => { - if (unit?.unitType === 'time' || unit?.unitType === 'size') { + if ( + unit?.unitType === 'time' || + unit?.unitType === 'size' || + unit?.unitType === 'binary_size' + ) { return { text: getHoverTexts({ convertedValues, unit }), hovertemplate: `%{text}
${name ? '%{meta}' : ''}`, diff --git a/graylog2-web-interface/src/views/components/visualizations/utils/unitConverters.ts b/graylog2-web-interface/src/views/components/visualizations/utils/unitConverters.ts index 845a7c1be7..aa19f87f77 100644 --- a/graylog2-web-interface/src/views/components/visualizations/utils/unitConverters.ts +++ b/graylog2-web-interface/src/views/components/visualizations/utils/unitConverters.ts @@ -28,6 +28,45 @@ import supportedUnits from '../../../../../../graylog2-server/src/main/resources type UnitConversionAction = 'MULTIPLY' | 'DIVIDE'; +const binarySize = [ + { + 'type': 'base', + 'abbrev': 'b', + 'name': 'byte', + 'unit_type': 'binary_size', + }, + { + 'type': 'derived', + 'abbrev': 'KiB', + 'name': 'kibibyte', + 'unit_type': 'binary_size', + 'conversion': { + 'value': 1024, + 'action': 'MULTIPLY', + }, + }, + { + 'type': 'derived', + 'abbrev': 'MiB', + 'name': 'mebibyte', + 'unit_type': 'binary_size', + 'conversion': { + 'value': 1048576, + 'action': 'MULTIPLY', + }, + }, + { + 'type': 'derived', + 'abbrev': 'GiB', + 'name': 'gibibyte', + 'unit_type': 'binary_size', + 'conversion': { + 'value': 1073741824, + 'action': 'MULTIPLY', + }, + }, +]; + const sourceUnits = supportedUnits.units as FieldUnitTypesJson; export type UnitJson = { type: 'base' | 'derived'; @@ -70,13 +109,21 @@ const unitFromJson = (unitJson: UnitJson): Unit => ({ conversion: unitJson.conversion, useInPrettier: isUnitUsableInPrettier(unitJson), }); -export const mappedUnitsFromJSON: FieldUnitTypes = ( + +export const mappedUnitsFromJSONForAggregation: FieldUnitTypes = ( mapValues( sourceUnits, (unitsJson: Array): Array => unitsJson.map((unitJson: UnitJson): Unit => unitFromJson(unitJson)), ) ); +export const mappedUnitsFromJSON: FieldUnitTypes = ( + mapValues( + { ...sourceUnits, binary_size: binarySize }, + (unitsJson: Array): Array => unitsJson.map((unitJson: UnitJson): Unit => unitFromJson(unitJson)), + ) +); + export const _getBaseUnit = (units: FieldUnitTypes, unitType: FieldUnitType): Unit => units[unitType].find(({ type }) => type === 'base'); diff --git a/graylog2-web-interface/src/views/types.ts b/graylog2-web-interface/src/views/types.ts index 8c02349444..632561344b 100644 --- a/graylog2-web-interface/src/views/types.ts +++ b/graylog2-web-interface/src/views/types.ts @@ -569,7 +569,7 @@ export interface WidgetCreator { icon: React.ComponentType<{}>; } -export type FieldUnitType = 'size' | 'time' | 'percent'; +export type FieldUnitType = 'size' | 'time' | 'percent' | 'binary_size'; export type DefaultAxisKey = typeof DEFAULT_AXIS_KEY;