diff --git a/x-pack/plugins/apm/common/agent_configuration/runtime_types/bytes_rt.test.ts b/x-pack/plugins/apm/common/agent_configuration/runtime_types/bytes_rt.test.ts index 180637f08a84af..4d786605b00c72 100644 --- a/x-pack/plugins/apm/common/agent_configuration/runtime_types/bytes_rt.test.ts +++ b/x-pack/plugins/apm/common/agent_configuration/runtime_types/bytes_rt.test.ts @@ -6,12 +6,11 @@ import { getBytesRt } from './bytes_rt'; import { isRight } from 'fp-ts/lib/Either'; +import { PathReporter } from 'io-ts/lib/PathReporter'; describe('bytesRt', () => { describe('must accept any amount and unit', () => { - const bytesRt = getBytesRt({ - units: ['b', 'mb', 'kb'] - }); + const bytesRt = getBytesRt({}); describe('it should not accept', () => { ['mb', 1, '1', '5gb', '6tb'].map(input => { it(`${JSON.stringify(input)}`, () => { @@ -30,8 +29,7 @@ describe('bytesRt', () => { }); describe('must be at least 0b', () => { const bytesRt = getBytesRt({ - min: '0b', - units: ['b', 'mb', 'kb'] + min: '0b' }); describe('it should not accept', () => { @@ -42,6 +40,17 @@ describe('bytesRt', () => { }); }); + describe('it should return correct error message', () => { + ['-1kb', '5gb', '6tb'].map(input => { + it(`${JSON.stringify(input)}`, () => { + const result = bytesRt.decode(input); + const message = PathReporter.report(result)[0]; + expect(message).toEqual('Must be greater than 0b'); + expect(isRight(result)).toBeFalsy(); + }); + }); + }); + describe('it should accept', () => { ['1b', '2kb', '3mb'].map(input => { it(`${JSON.stringify(input)}`, () => { @@ -50,11 +59,10 @@ describe('bytesRt', () => { }); }); }); - describe('must be between 500b and 1mb', () => { + describe('must be between 500b and 1kb', () => { const bytesRt = getBytesRt({ min: '500b', - max: '1kb', - units: ['b', 'mb', 'kb'] + max: '1kb' }); describe('it should not accept', () => { ['mb', '-1b', '1b', '499b', '1025b', '2kb', '1mb'].map(input => { @@ -63,6 +71,16 @@ describe('bytesRt', () => { }); }); }); + describe('it should return correct error message', () => { + ['-1b', '1b', '499b', '1025b', '2kb', '1mb'].map(input => { + it(`${JSON.stringify(input)}`, () => { + const result = bytesRt.decode(input); + const message = PathReporter.report(result)[0]; + expect(message).toEqual('Must be between 500b and 1kb'); + expect(isRight(result)).toBeFalsy(); + }); + }); + }); describe('it should accept', () => { ['500b', '1024b', '1kb'].map(input => { it(`${JSON.stringify(input)}`, () => { diff --git a/x-pack/plugins/apm/common/agent_configuration/runtime_types/bytes_rt.ts b/x-pack/plugins/apm/common/agent_configuration/runtime_types/bytes_rt.ts index 01da9598d5d33f..1b18b65852ba61 100644 --- a/x-pack/plugins/apm/common/agent_configuration/runtime_types/bytes_rt.ts +++ b/x-pack/plugins/apm/common/agent_configuration/runtime_types/bytes_rt.ts @@ -6,70 +6,47 @@ import * as t from 'io-ts'; import { either } from 'fp-ts/lib/Either'; -import { i18n } from '@kbn/i18n'; import { amountAndUnitToObject } from '../amount_and_unit'; -import { getRangeType } from './get_range_type'; +import { getRangeTypeMessage } from './get_range_type'; function toBytes(amount: number, unit: string) { switch (unit) { + case 'b': + return amount; case 'kb': return amount * 2 ** 10; case 'mb': return amount * 2 ** 20; - case 'b': - default: - return amount; } } -export function getBytesRt({ - min, - max, - units -}: { - min?: string; - max?: string; - units: string[]; -}) { - const { amount: minAmount, unit: minUnit } = min - ? amountAndUnitToObject(min) - : { amount: -Infinity, unit: 'b' }; - - const { amount: maxAmount, unit: maxUnit } = max - ? amountAndUnitToObject(max) - : { amount: Infinity, unit: 'mb' }; - - const message = i18n.translate('xpack.apm.agentConfig.bytes.errorText', { - defaultMessage: `{rangeType, select, - between {Must be between {min} and {max} with unit: {units}} - gt {Must be greater than {min} with unit: {units}} - lt {Must be less than {max} with unit: {units}} - other {Must be an integer with unit: {units}} - }`, - values: { - min, - max, - units: units.join(', '), - rangeType: getRangeType(minAmount, maxAmount) +function amountAndUnitToBytes(value?: string): number | undefined { + if (value) { + const { amount, unit } = amountAndUnitToObject(value); + if (isFinite(amount) && unit) { + return toBytes(amount, unit); } - }); + } +} + +export function getBytesRt({ min, max }: { min?: string; max?: string }) { + const minAsBytes = amountAndUnitToBytes(min) ?? -Infinity; + const maxAsBytes = amountAndUnitToBytes(max) ?? Infinity; + const message = getRangeTypeMessage(min, max); return new t.Type( 'bytesRt', t.string.is, (input, context) => { return either.chain(t.string.validate(input, context), inputAsString => { - const { amount, unit } = amountAndUnitToObject(inputAsString); - const isValidUnit = units.includes(unit); - - const inputAsBytes = toBytes(amount, unit); - const minAsBytes = toBytes(minAmount, minUnit); - const maxAsBytes = toBytes(maxAmount, maxUnit); + const inputAsBytes = amountAndUnitToBytes(inputAsString); const isValidAmount = - inputAsBytes >= minAsBytes && inputAsBytes <= maxAsBytes; + typeof inputAsBytes !== 'undefined' && + inputAsBytes >= minAsBytes && + inputAsBytes <= maxAsBytes; - return isValidUnit && isValidAmount + return isValidAmount ? t.success(inputAsString) : t.failure(input, context, message); }); diff --git a/x-pack/plugins/apm/common/agent_configuration/runtime_types/duration_rt.test.ts b/x-pack/plugins/apm/common/agent_configuration/runtime_types/duration_rt.test.ts index fff1052cf6da28..ebfd9d9a727044 100644 --- a/x-pack/plugins/apm/common/agent_configuration/runtime_types/duration_rt.test.ts +++ b/x-pack/plugins/apm/common/agent_configuration/runtime_types/duration_rt.test.ts @@ -6,11 +6,11 @@ import { getDurationRt } from './duration_rt'; import { isRight } from 'fp-ts/lib/Either'; +import { PathReporter } from 'io-ts/lib/PathReporter'; describe('getDurationRt', () => { - const units = ['ms', 's', 'm']; describe('must be at least 1m', () => { - const customDurationRt = getDurationRt({ min: '1m', units }); + const customDurationRt = getDurationRt({ min: '1m' }); describe('it should not accept', () => { [ undefined, @@ -30,6 +30,16 @@ describe('getDurationRt', () => { }); }); }); + describe('it should return correct error message', () => { + ['0m', '-1m', '1ms', '1s'].map(input => { + it(`${JSON.stringify(input)}`, () => { + const result = customDurationRt.decode(input); + const message = PathReporter.report(result)[0]; + expect(message).toEqual('Must be greater than 1m'); + expect(isRight(result)).toBeFalsy(); + }); + }); + }); describe('it should accept', () => { ['1m', '2m', '1000m'].map(input => { it(`${JSON.stringify(input)}`, () => { @@ -40,7 +50,7 @@ describe('getDurationRt', () => { }); describe('must be between 1ms and 1s', () => { - const customDurationRt = getDurationRt({ min: '1ms', max: '1s', units }); + const customDurationRt = getDurationRt({ min: '1ms', max: '1s' }); describe('it should not accept', () => { [ @@ -65,6 +75,16 @@ describe('getDurationRt', () => { }); }); }); + describe('it should return correct error message', () => { + ['-1s', '0s', '2s', '1001ms', '0ms', '-1ms', '0m', '1m'].map(input => { + it(`${JSON.stringify(input)}`, () => { + const result = customDurationRt.decode(input); + const message = PathReporter.report(result)[0]; + expect(message).toEqual('Must be between 1ms and 1s'); + expect(isRight(result)).toBeFalsy(); + }); + }); + }); describe('it should accept', () => { ['1s', '1ms', '50ms', '1000ms'].map(input => { it(`${JSON.stringify(input)}`, () => { @@ -74,7 +94,7 @@ describe('getDurationRt', () => { }); }); describe('must be max 1m', () => { - const customDurationRt = getDurationRt({ max: '1m', units }); + const customDurationRt = getDurationRt({ max: '1m' }); describe('it should not accept', () => { [undefined, null, '', 0, 'foo', true, false, '2m', '61s', '60001ms'].map( @@ -85,6 +105,16 @@ describe('getDurationRt', () => { } ); }); + describe('it should return correct error message', () => { + ['2m', '61s', '60001ms'].map(input => { + it(`${JSON.stringify(input)}`, () => { + const result = customDurationRt.decode(input); + const message = PathReporter.report(result)[0]; + expect(message).toEqual('Must be less than 1m'); + expect(isRight(result)).toBeFalsy(); + }); + }); + }); describe('it should accept', () => { ['1m', '0m', '-1m', '60s', '6000ms', '1ms', '1s'].map(input => { it(`${JSON.stringify(input)}`, () => { diff --git a/x-pack/plugins/apm/common/agent_configuration/runtime_types/duration_rt.ts b/x-pack/plugins/apm/common/agent_configuration/runtime_types/duration_rt.ts index 86e1f2d77e75dc..601d0e62663490 100644 --- a/x-pack/plugins/apm/common/agent_configuration/runtime_types/duration_rt.ts +++ b/x-pack/plugins/apm/common/agent_configuration/runtime_types/duration_rt.ts @@ -7,63 +7,40 @@ import * as t from 'io-ts'; import { either } from 'fp-ts/lib/Either'; import moment, { unitOfTime } from 'moment'; -import { i18n } from '@kbn/i18n'; import { amountAndUnitToObject, AmountAndUnit } from '../amount_and_unit'; -import { getRangeType } from './get_range_type'; +import { getRangeTypeMessage } from './get_range_type'; -function getDuration({ amount, unit }: AmountAndUnit) { +function toMilliseconds({ amount, unit }: AmountAndUnit) { return moment.duration(amount, unit as unitOfTime.Base); } -export function getDurationRt({ - min, - max, - units -}: { - min?: string; - max?: string; - units: string[]; -}) { - const minAmountAndUnit = min && amountAndUnitToObject(min); - const maxAmountAndUnit = max && amountAndUnitToObject(max); - - const message = i18n.translate('xpack.apm.agentConfig.duration.errorText', { - defaultMessage: `{rangeType, select, - between {Must be between {min} and {max}} - gt {Must be greater than {min}} - lt {Must be less than {max}} - other {Must be an integer} - }`, - values: { - min, - max, - rangeType: getRangeType( - minAmountAndUnit ? minAmountAndUnit.amount : undefined, - maxAmountAndUnit ? maxAmountAndUnit.amount : undefined - ) +function amountAndUnitToMilliseconds(value?: string) { + if (value) { + const { amount, unit } = amountAndUnitToObject(value); + if (isFinite(amount) && unit) { + return toMilliseconds({ amount, unit }); } - }); + } +} + +export function getDurationRt({ min, max }: { min?: string; max?: string }) { + const minAsMilliseconds = amountAndUnitToMilliseconds(min) ?? -Infinity; + const maxAsMilliseconds = amountAndUnitToMilliseconds(max) ?? Infinity; + const message = getRangeTypeMessage(min, max); + return new t.Type( 'durationRt', t.string.is, (input, context) => { return either.chain(t.string.validate(input, context), inputAsString => { - const { amount, unit } = amountAndUnitToObject(inputAsString); - const inputDuration = getDuration({ amount, unit }); - - const minDuration = minAmountAndUnit - ? getDuration(minAmountAndUnit) - : inputDuration; - - const maxDuration = maxAmountAndUnit - ? getDuration(maxAmountAndUnit) - : inputDuration; + const inputAsMilliseconds = amountAndUnitToMilliseconds(inputAsString); - const isValidUnit = units.includes(unit); const isValidAmount = - inputDuration >= minDuration && inputDuration <= maxDuration; + typeof inputAsMilliseconds !== 'undefined' && + inputAsMilliseconds >= minAsMilliseconds && + inputAsMilliseconds <= maxAsMilliseconds; - return isValidUnit && isValidAmount + return isValidAmount ? t.success(inputAsString) : t.failure(input, context, message); }); diff --git a/x-pack/plugins/apm/common/agent_configuration/runtime_types/get_range_type.ts b/x-pack/plugins/apm/common/agent_configuration/runtime_types/get_range_type.ts index 83239a54902958..5bd0fcb80c4dde 100644 --- a/x-pack/plugins/apm/common/agent_configuration/runtime_types/get_range_type.ts +++ b/x-pack/plugins/apm/common/agent_configuration/runtime_types/get_range_type.ts @@ -5,8 +5,10 @@ */ import { isFinite } from 'lodash'; +import { i18n } from '@kbn/i18n'; +import { amountAndUnitToObject } from '../amount_and_unit'; -export function getRangeType(min?: number, max?: number) { +function getRangeType(min?: number, max?: number) { if (isFinite(min) && isFinite(max)) { return 'between'; } else if (isFinite(min)) { @@ -15,3 +17,25 @@ export function getRangeType(min?: number, max?: number) { return 'lt'; // less than } } + +export function getRangeTypeMessage( + min?: number | string, + max?: number | string +) { + return i18n.translate('xpack.apm.agentConfig.range.errorText', { + defaultMessage: `{rangeType, select, + between {Must be between {min} and {max}} + gt {Must be greater than {min}} + lt {Must be less than {max}} + other {Must be an integer} + }`, + values: { + min, + max, + rangeType: getRangeType( + typeof min === 'string' ? amountAndUnitToObject(min).amount : min, + typeof max === 'string' ? amountAndUnitToObject(max).amount : max + ) + } + }); +} diff --git a/x-pack/plugins/apm/common/agent_configuration/runtime_types/integer_rt.test.ts b/x-pack/plugins/apm/common/agent_configuration/runtime_types/integer_rt.test.ts index 32869981311791..a0395a4a140d97 100644 --- a/x-pack/plugins/apm/common/agent_configuration/runtime_types/integer_rt.test.ts +++ b/x-pack/plugins/apm/common/agent_configuration/runtime_types/integer_rt.test.ts @@ -6,6 +6,7 @@ import { getIntegerRt } from './integer_rt'; import { isRight } from 'fp-ts/lib/Either'; +import { PathReporter } from 'io-ts/lib/PathReporter'; describe('getIntegerRt', () => { describe('with range', () => { @@ -24,6 +25,17 @@ describe('getIntegerRt', () => { ); }); + describe('it should return correct error message', () => { + ['-1', '-55', '33000'].map(input => { + it(`${JSON.stringify(input)}`, () => { + const result = integerRt.decode(input); + const message = PathReporter.report(result)[0]; + expect(message).toEqual('Must be between 0 and 32000'); + expect(isRight(result)).toBeFalsy(); + }); + }); + }); + describe('it should accept number between 0 and 32000', () => { ['0', '1000', '32000'].map(input => { it(`${JSON.stringify(input)}`, () => { diff --git a/x-pack/plugins/apm/common/agent_configuration/runtime_types/integer_rt.ts b/x-pack/plugins/apm/common/agent_configuration/runtime_types/integer_rt.ts index 6cae5e6d9cb159..fcfd18e1994a4c 100644 --- a/x-pack/plugins/apm/common/agent_configuration/runtime_types/integer_rt.ts +++ b/x-pack/plugins/apm/common/agent_configuration/runtime_types/integer_rt.ts @@ -6,8 +6,7 @@ import * as t from 'io-ts'; import { either } from 'fp-ts/lib/Either'; -import { i18n } from '@kbn/i18n'; -import { getRangeType } from './get_range_type'; +import { getRangeTypeMessage } from './get_range_type'; export function getIntegerRt({ min = -Infinity, @@ -16,15 +15,7 @@ export function getIntegerRt({ min?: number; max?: number; } = {}) { - const message = i18n.translate('xpack.apm.agentConfig.integer.errorText', { - defaultMessage: `{rangeType, select, - between {Must be between {min} and {max}} - gt {Must be greater than {min}} - lt {Must be less than {max}} - other {Must be an integer} - }`, - values: { min, max, rangeType: getRangeType(min, max) } - }); + const message = getRangeTypeMessage(min, max); return new t.Type( 'integerRt', diff --git a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/index.ts b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/index.ts index 8a8f726c61f76d..7869cd7d79e17a 100644 --- a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/index.ts +++ b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/index.ts @@ -54,7 +54,7 @@ function getSettingDefaults(setting: RawSettingDefinition): SettingDefinition { const max = setting.max; return { - validation: getBytesRt({ min, max, units }), + validation: getBytesRt({ min, max }), units, min, ...setting @@ -67,7 +67,7 @@ function getSettingDefaults(setting: RawSettingDefinition): SettingDefinition { const max = setting.max; return { - validation: getDurationRt({ min, max, units }), + validation: getDurationRt({ min, max }), units, min, ...setting