From 6d97cf3a16a86fe406b6aa8397b3ae05a4d4b6f7 Mon Sep 17 00:00:00 2001 From: cauemarcondes Date: Mon, 20 Apr 2020 14:05:56 +0200 Subject: [PATCH 01/12] validating java settings --- .../runtime_types/duration_rt.test.ts | 78 ++++++++++++++++--- .../runtime_types/duration_rt.ts | 30 +++++-- .../__snapshots__/index.test.ts.snap | 4 +- .../setting_definitions/java_settings.ts | 21 ++++- 4 files changed, 113 insertions(+), 20 deletions(-) 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 98d0cb5f028c3..643a7203b980b 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 @@ -45,21 +45,77 @@ describe('durationRt', () => { }); describe('getDurationRt', () => { - const customDurationRt = getDurationRt({ min: -1 }); - describe('it should not accept', () => { - [undefined, null, '', 0, 'foo', true, false, '100', 's', 'm', '-2ms'].map( - input => { + describe('min/max amount validation', () => { + const customDurationRt = getDurationRt({ min: -1, max: 10 }); + describe('it should not accept', () => { + ['-2ms', '-3s', '11m'].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(customDurationRt.decode(input))).toBeFalsy(); + }); + }); + }); + describe('it should accept', () => { + ['-1ms', '0s', '1m', '10ms'].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(customDurationRt.decode(input))).toBeTruthy(); + }); + }); + }); + }); + describe('unit validation', () => { + const customDurationRt = getDurationRt({ unit: 'ms' }); + describe('it should not accept', () => { + ['-2s', '-3s', '11m'].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(customDurationRt.decode(input))).toBeFalsy(); + }); + }); + }); + describe('it should accept', () => { + ['-1ms', '0ms', '1ms', '10ms'].map(input => { it(`${JSON.stringify(input)}`, () => { - expect(isRight(customDurationRt.decode(input))).toBe(false); + expect(isRight(customDurationRt.decode(input))).toBeTruthy(); }); - } - ); + }); + }); }); - describe('it should accept', () => { - ['1000ms', '2s', '3m', '1s', '-1s', '0ms'].map(input => { - it(`${JSON.stringify(input)}`, () => { - expect(isRight(customDurationRt.decode(input))).toBe(true); + describe('must be at least 1m', () => { + const customDurationRt = getDurationRt({ min: 1, unit: 'm' }); + describe('it should not accept', () => { + ['0m', '-1m', '1ms', '1s'].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(customDurationRt.decode(input))).toBeFalsy(); + }); + }); + }); + describe('it should accept', () => { + ['1m', '2m', '1000m'].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(customDurationRt.decode(input))).toBeTruthy(); + }); + }); + }); + }); + + describe('must be between 1ms(1ms - 1000ms) and 1s', () => { + const customDurationRt = getDurationRt([ + { min: 1, max: 1, unit: 's' }, + { min: 1, max: 1000, unit: 'ms' } + ]); + + describe('it should not accept', () => { + ['-1s', '0s', '2s', '1001ms', '0ms', '-1ms', '0m', '1m'].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(customDurationRt.decode(input))).toBeFalsy(); + }); + }); + }); + describe('it should accept', () => { + ['1s', '1ms', '50ms', '1000ms'].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(customDurationRt.decode(input))).toBeTruthy(); + }); }); }); }); 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 b691276854fb0..0a77ebf82cf09 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 @@ -8,18 +8,38 @@ import * as t from 'io-ts'; import { either } from 'fp-ts/lib/Either'; import { amountAndUnitToObject } from '../amount_and_unit'; +type DurationUnit = 'ms' | 's' | 'm'; export const DURATION_UNITS = ['ms', 's', 'm']; -export function getDurationRt({ min }: { min: number }) { +interface Criteria { + min?: number; + max?: number; + unit?: DurationUnit; +} + +function validateDuration(inputAsString: string, { min, max, unit }: Criteria) { + const { amount, unit: inputUnit } = amountAndUnitToObject(inputAsString); + const amountAsInt = parseInt(amount, 10); + const isValidUnit = + DURATION_UNITS.includes(inputUnit) && (unit ? unit === inputUnit : true); + + const isValidAmount = + (min ? amountAsInt >= min : true) && (max ? amountAsInt <= max : true); + + return isValidUnit && isValidAmount; +} + +export function getDurationRt(criteria: Criteria | Criteria[]) { 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 amountAsInt = parseInt(amount, 10); - const isValidUnit = DURATION_UNITS.includes(unit); - const isValid = amountAsInt >= min && isValidUnit; + const isValid = Array.isArray(criteria) + ? criteria + .map(_criteria => validateDuration(inputAsString, _criteria)) + .some(result => result) + : validateDuration(inputAsString, criteria); return isValid ? t.success(inputAsString) diff --git a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap index ea706be9f584a..d62fa58ce8ee2 100644 --- a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap +++ b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap @@ -101,7 +101,7 @@ Array [ "s", "m", ], - "validationError": "Please specify an integer and a unit", + "validationError": "Must be between '1ms' and '1s'", "validationName": "durationRt", }, Object { @@ -146,7 +146,7 @@ Array [ "s", "m", ], - "validationError": "Please specify an integer and a unit", + "validationError": "Must be at least '1m'", "validationName": "durationRt", }, Object { diff --git a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/java_settings.ts b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/java_settings.ts index 2e10c74378549..355f755f6b9cd 100644 --- a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/java_settings.ts +++ b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/java_settings.ts @@ -6,6 +6,7 @@ import { i18n } from '@kbn/i18n'; import { RawSettingDefinition } from './types'; +import { getDurationRt } from '../runtime_types/duration_rt'; export const javaSettings: RawSettingDefinition[] = [ // ENABLE_LOG_CORRELATION @@ -99,7 +100,14 @@ export const javaSettings: RawSettingDefinition[] = [ 'The minimal time required in order to determine whether the system is either currently under stress, or that the stress detected previously has been relieved. All measurements during this time must be consistent in comparison to the relevant threshold in order to detect a change of stress state. Must be at least `1m`.' } ), - includeAgents: ['java'] + includeAgents: ['java'], + validation: + // must be at least 1m + getDurationRt({ min: 1, unit: 'm' }), + validationError: i18n.translate( + 'xpack.apm.agentConfig.stressMonitorCpuDurationThreshold.errorText', + { defaultMessage: "Must be at least '1m'" } + ) }, { key: 'stress_monitor_system_cpu_stress_threshold', @@ -176,7 +184,16 @@ export const javaSettings: RawSettingDefinition[] = [ 'The frequency at which stack traces are gathered within a profiling session. The lower you set it, the more accurate the durations will be. This comes at the expense of higher overhead and more spans for potentially irrelevant operations. The minimal duration of a profiling-inferred span is the same as the value of this setting.' } ), - includeAgents: ['java'] + includeAgents: ['java'], + validation: getDurationRt([ + // must be between 1ms(1ms - 1000ms) and 1s + { min: 1, max: 1, unit: 's' }, + { min: 1, max: 1000, unit: 'ms' } + ]), + validationError: i18n.translate( + 'xpack.apm.agentConfig.profilingInferredSpansSamplingInterval.errorText', + { defaultMessage: "Must be between '1ms' and '1s'" } + ) }, { key: 'profiling_inferred_spans_min_duration', From b3e99154aa88e2c6645d96fd9acf0fd0db51f8ce Mon Sep 17 00:00:00 2001 From: cauemarcondes Date: Thu, 23 Apr 2020 11:19:47 +0200 Subject: [PATCH 02/12] adding min max support to duration --- .../runtime_types/duration_rt.test.ts | 84 +++++++++++-------- .../runtime_types/duration_rt.ts | 45 +++++----- .../setting_definitions/general_settings.ts | 2 +- .../setting_definitions/java_settings.ts | 10 +-- 4 files changed, 69 insertions(+), 72 deletions(-) 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 643a7203b980b..e861984da592f 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 @@ -45,34 +45,29 @@ describe('durationRt', () => { }); describe('getDurationRt', () => { - describe('min/max amount validation', () => { - const customDurationRt = getDurationRt({ min: -1, max: 10 }); - describe('it should not accept', () => { - ['-2ms', '-3s', '11m'].map(input => { - it(`${JSON.stringify(input)}`, () => { - expect(isRight(customDurationRt.decode(input))).toBeFalsy(); - }); - }); - }); - describe('it should accept', () => { - ['-1ms', '0s', '1m', '10ms'].map(input => { - it(`${JSON.stringify(input)}`, () => { - expect(isRight(customDurationRt.decode(input))).toBeTruthy(); - }); - }); - }); - }); - describe('unit validation', () => { - const customDurationRt = getDurationRt({ unit: 'ms' }); + describe('must be at least 1m', () => { + const customDurationRt = getDurationRt({ min: '1m' }); describe('it should not accept', () => { - ['-2s', '-3s', '11m'].map(input => { + [ + undefined, + null, + '', + 0, + 'foo', + true, + false, + '0m', + '-1m', + '1ms', + '1s' + ].map(input => { it(`${JSON.stringify(input)}`, () => { expect(isRight(customDurationRt.decode(input))).toBeFalsy(); }); }); }); describe('it should accept', () => { - ['-1ms', '0ms', '1ms', '10ms'].map(input => { + ['1m', '2m', '1000m'].map(input => { it(`${JSON.stringify(input)}`, () => { expect(isRight(customDurationRt.decode(input))).toBeTruthy(); }); @@ -80,39 +75,54 @@ describe('getDurationRt', () => { }); }); - describe('must be at least 1m', () => { - const customDurationRt = getDurationRt({ min: 1, unit: 'm' }); + describe('must be between 1ms and 1s', () => { + const customDurationRt = getDurationRt({ min: '1ms', max: '1s' }); + describe('it should not accept', () => { - ['0m', '-1m', '1ms', '1s'].map(input => { + [ + undefined, + null, + '', + 0, + 'foo', + true, + false, + '-1s', + '0s', + '2s', + '1001ms', + '0ms', + '-1ms', + '0m', + '1m' + ].map(input => { it(`${JSON.stringify(input)}`, () => { expect(isRight(customDurationRt.decode(input))).toBeFalsy(); }); }); }); describe('it should accept', () => { - ['1m', '2m', '1000m'].map(input => { + ['1s', '1ms', '50ms', '1000ms'].map(input => { it(`${JSON.stringify(input)}`, () => { expect(isRight(customDurationRt.decode(input))).toBeTruthy(); }); }); }); }); - - describe('must be between 1ms(1ms - 1000ms) and 1s', () => { - const customDurationRt = getDurationRt([ - { min: 1, max: 1, unit: 's' }, - { min: 1, max: 1000, unit: 'ms' } - ]); + describe('must be max 1m', () => { + const customDurationRt = getDurationRt({ max: '1m' }); describe('it should not accept', () => { - ['-1s', '0s', '2s', '1001ms', '0ms', '-1ms', '0m', '1m'].map(input => { - it(`${JSON.stringify(input)}`, () => { - expect(isRight(customDurationRt.decode(input))).toBeFalsy(); - }); - }); + [undefined, null, '', 0, 'foo', true, false, '2m', '61s', '60001ms'].map( + input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(customDurationRt.decode(input))).toBeFalsy(); + }); + } + ); }); describe('it should accept', () => { - ['1s', '1ms', '50ms', '1000ms'].map(input => { + ['1m', '0m', '-1m', '60s', '6000ms', '1ms', '1s'].map(input => { it(`${JSON.stringify(input)}`, () => { expect(isRight(customDurationRt.decode(input))).toBeTruthy(); }); 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 0a77ebf82cf09..cdfc18eb0ea72 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 @@ -6,42 +6,35 @@ import * as t from 'io-ts'; import { either } from 'fp-ts/lib/Either'; +import moment, { unitOfTime } from 'moment'; import { amountAndUnitToObject } from '../amount_and_unit'; -type DurationUnit = 'ms' | 's' | 'm'; export const DURATION_UNITS = ['ms', 's', 'm']; -interface Criteria { - min?: number; - max?: number; - unit?: DurationUnit; +function getDuration({ amount, unit }: { amount: string; unit: string }) { + return moment.duration(parseInt(amount, 10), unit as unitOfTime.Base); } -function validateDuration(inputAsString: string, { min, max, unit }: Criteria) { - const { amount, unit: inputUnit } = amountAndUnitToObject(inputAsString); - const amountAsInt = parseInt(amount, 10); - const isValidUnit = - DURATION_UNITS.includes(inputUnit) && (unit ? unit === inputUnit : true); - - const isValidAmount = - (min ? amountAsInt >= min : true) && (max ? amountAsInt <= max : true); - - return isValidUnit && isValidAmount; -} - -export function getDurationRt(criteria: Criteria | Criteria[]) { +export function getDurationRt({ min, max }: { min?: string; max?: string }) { return new t.Type( 'durationRt', t.string.is, (input, context) => { return either.chain(t.string.validate(input, context), inputAsString => { - const isValid = Array.isArray(criteria) - ? criteria - .map(_criteria => validateDuration(inputAsString, _criteria)) - .some(result => result) - : validateDuration(inputAsString, criteria); - - return isValid + const { amount, unit } = amountAndUnitToObject(inputAsString); + const inputDuration = getDuration({ amount, unit }); + const minDuration = min + ? getDuration(amountAndUnitToObject(min)) + : inputDuration; + const maxDuration = max + ? getDuration(amountAndUnitToObject(max)) + : inputDuration; + + const isValidUnit = DURATION_UNITS.includes(unit); + const isValidAmount = + inputDuration >= minDuration && inputDuration <= maxDuration; + + return isValidUnit && isValidAmount ? t.success(inputAsString) : t.failure( input, @@ -54,4 +47,4 @@ export function getDurationRt(criteria: Criteria | Criteria[]) { ); } -export const durationRt = getDurationRt({ min: 1 }); +export const durationRt = getDurationRt({ min: '1ms' }); diff --git a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts index 7477238ba79ae..1693a627ec7b5 100644 --- a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts +++ b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts @@ -144,7 +144,7 @@ export const generalSettings: RawSettingDefinition[] = [ { key: 'span_frames_min_duration', type: 'duration', - validation: getDurationRt({ min: -1 }), + validation: getDurationRt({ min: '-1ms' }), defaultValue: '5ms', label: i18n.translate('xpack.apm.agentConfig.spanFramesMinDuration.label', { defaultMessage: 'Span frames minimum duration' diff --git a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/java_settings.ts b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/java_settings.ts index 355f755f6b9cd..fbb2fb1ea4779 100644 --- a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/java_settings.ts +++ b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/java_settings.ts @@ -101,9 +101,7 @@ export const javaSettings: RawSettingDefinition[] = [ } ), includeAgents: ['java'], - validation: - // must be at least 1m - getDurationRt({ min: 1, unit: 'm' }), + validation: getDurationRt({ min: '1m' }), validationError: i18n.translate( 'xpack.apm.agentConfig.stressMonitorCpuDurationThreshold.errorText', { defaultMessage: "Must be at least '1m'" } @@ -185,11 +183,7 @@ export const javaSettings: RawSettingDefinition[] = [ } ), includeAgents: ['java'], - validation: getDurationRt([ - // must be between 1ms(1ms - 1000ms) and 1s - { min: 1, max: 1, unit: 's' }, - { min: 1, max: 1000, unit: 'ms' } - ]), + validation: getDurationRt({ min: '1ms', max: '1s' }), validationError: i18n.translate( 'xpack.apm.agentConfig.profilingInferredSpansSamplingInterval.errorText', { defaultMessage: "Must be between '1ms' and '1s'" } From df150b759d12b6389597a05e3d9d6bd9938ce9b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Mon, 20 Apr 2020 22:40:02 +0200 Subject: [PATCH 03/12] Agent config cleanup --- .../SettingsPage/SettingFormRow.tsx | 14 +- .../SettingsPage/SettingsPage.tsx | 4 +- .../agent_configuration_intake_rt.ts | 4 +- .../runtime_types/amount_and_unit_rt.test.ts | 156 ++++++++++++++++++ .../runtime_types/amount_and_unit_rt.ts | 54 ++++++ .../runtime_types/bytes_rt.test.ts | 39 ----- .../runtime_types/bytes_rt.ts | 33 ---- .../runtime_types/duration_rt.test.ts | 132 --------------- .../runtime_types/float_rt.test.ts | 36 ++++ .../runtime_types/float_rt.ts | 28 ++++ .../runtime_types/get_range_type.ts | 17 ++ .../runtime_types/integer_rt.test.ts | 60 ++++--- .../runtime_types/integer_rt.ts | 28 +++- .../runtime_types/number_float_rt.test.ts | 36 ---- .../runtime_types/number_float_rt.ts | 36 ---- .../setting_definitions/general_settings.ts | 19 +-- .../setting_definitions/index.ts | 109 ++++++------ .../setting_definitions/types.d.ts | 47 +++--- 18 files changed, 441 insertions(+), 411 deletions(-) create mode 100644 x-pack/plugins/apm/common/agent_configuration/runtime_types/amount_and_unit_rt.test.ts create mode 100644 x-pack/plugins/apm/common/agent_configuration/runtime_types/amount_and_unit_rt.ts delete mode 100644 x-pack/plugins/apm/common/agent_configuration/runtime_types/bytes_rt.test.ts delete mode 100644 x-pack/plugins/apm/common/agent_configuration/runtime_types/bytes_rt.ts delete mode 100644 x-pack/plugins/apm/common/agent_configuration/runtime_types/duration_rt.test.ts create mode 100644 x-pack/plugins/apm/common/agent_configuration/runtime_types/float_rt.test.ts create mode 100644 x-pack/plugins/apm/common/agent_configuration/runtime_types/float_rt.ts create mode 100644 x-pack/plugins/apm/common/agent_configuration/runtime_types/get_range_type.ts delete mode 100644 x-pack/plugins/apm/common/agent_configuration/runtime_types/number_float_rt.test.ts delete mode 100644 x-pack/plugins/apm/common/agent_configuration/runtime_types/number_float_rt.ts diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingFormRow.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingFormRow.tsx index baab600145b81..b2e9be4383f88 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingFormRow.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingFormRow.tsx @@ -18,7 +18,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { SettingDefinition } from '../../../../../../../../../../plugins/apm/common/agent_configuration/setting_definitions/types'; -import { isValid } from '../../../../../../../../../../plugins/apm/common/agent_configuration/setting_definitions'; +import { validateSetting } from '../../../../../../../../../../plugins/apm/common/agent_configuration/setting_definitions'; import { amountAndUnitToString, amountAndUnitToObject @@ -93,7 +93,8 @@ function FormRow({ onChange( setting.key, @@ -137,7 +138,8 @@ export function SettingFormRow({ value?: string; onChange: (key: string, value: string) => void; }) { - const isInvalid = value != null && value !== '' && !isValid(setting, value); + const { isValid, message } = validateSetting(setting, value); + const isInvalid = value != null && value !== '' && !isValid; return ( } > - + diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingsPage.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingsPage.tsx index 6d76b69600333..62ab2ef410b67 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingsPage.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingsPage.tsx @@ -29,7 +29,7 @@ import { AgentConfigurationIntake } from '../../../../../../../../../../plugins/ import { filterByAgent, settingDefinitions, - isValid + validateSetting } from '../../../../../../../../../../plugins/apm/common/agent_configuration/setting_definitions'; import { saveConfig } from './saveConfig'; import { useApmPluginContext } from '../../../../../../hooks/useApmPluginContext'; @@ -79,7 +79,7 @@ export function SettingsPage({ // every setting must be valid for the form to be valid .every(def => { const value = newConfig.settings[def.key]; - return isValid(def, value); + return validateSetting(def, value).isValid; }) ); }, [newConfig.settings]); diff --git a/x-pack/plugins/apm/common/agent_configuration/runtime_types/agent_configuration_intake_rt.ts b/x-pack/plugins/apm/common/agent_configuration/runtime_types/agent_configuration_intake_rt.ts index a0b1d5015b9ef..e903a56486b6e 100644 --- a/x-pack/plugins/apm/common/agent_configuration/runtime_types/agent_configuration_intake_rt.ts +++ b/x-pack/plugins/apm/common/agent_configuration/runtime_types/agent_configuration_intake_rt.ts @@ -6,11 +6,11 @@ import * as t from 'io-ts'; import { settingDefinitions } from '../setting_definitions'; +import { SettingValidation } from '../setting_definitions/types'; // retrieve validation from config definitions settings and validate on the server const knownSettings = settingDefinitions.reduce< - // TODO: is it possible to get rid of any? - Record> + Record >((acc, { key, validation }) => { acc[key] = validation; return acc; diff --git a/x-pack/plugins/apm/common/agent_configuration/runtime_types/amount_and_unit_rt.test.ts b/x-pack/plugins/apm/common/agent_configuration/runtime_types/amount_and_unit_rt.test.ts new file mode 100644 index 0000000000000..a8c16074d324c --- /dev/null +++ b/x-pack/plugins/apm/common/agent_configuration/runtime_types/amount_and_unit_rt.test.ts @@ -0,0 +1,156 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { getAmountAndUnitRt } from './amount_and_unit_rt'; +import { isRight } from 'fp-ts/lib/Either'; + +describe('durationRt', () => { + const durationRt = getAmountAndUnitRt({ + min: 0, + units: ['ms', 's', 'm'] + }); + + describe('it should not accept', () => { + [undefined, null, '', 0, 'foo', true, false, '100', 's', 'm'].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(durationRt.decode(input))).toBe(false); + }); + }); + }); + + describe('It should accept', () => { + ['0h', '1000ms', '2s', '3m'].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(durationRt.decode(input))).toBe(true); + }); + }); + }); +}); + +// describe('getDurationRt', () => { +// describe('must be at least 1m', () => { +// const customDurationRt = getDurationRt({ min: '1m' }); +// describe('it should not accept', () => { +// [ +// undefined, +// null, +// '', +// 0, +// 'foo', +// true, +// false, +// '0m', +// '-1m', +// '1ms', +// '1s' +// ].map(input => { +// it(`${JSON.stringify(input)}`, () => { +// expect(isRight(customDurationRt.decode(input))).toBeFalsy(); +// }); +// }); +// }); +// describe('it should accept', () => { +// ['1m', '2m', '1000m'].map(input => { +// it(`${JSON.stringify(input)}`, () => { +// expect(isRight(customDurationRt.decode(input))).toBeTruthy(); +// }); +// }); +// }); + +// describe('must be between 1ms and 1s', () => { +// const customDurationRt = getDurationRt({ min: '1ms', max: '1s' }); + +// describe('it should not accept', () => { +// [ +// undefined, +// null, +// '', +// 0, +// 'foo', +// true, +// false, +// '-1s', +// '0s', +// '2s', +// '1001ms', +// '0ms', +// '-1ms', +// '0m', +// '1m' +// ].map(input => { +// it(`${JSON.stringify(input)}`, () => { +// expect(isRight(customDurationRt.decode(input))).toBeFalsy(); +// }); +// }); +// }); +// describe('it should accept', () => { +// ['1s', '1ms', '50ms', '1000ms'].map(input => { +// it(`${JSON.stringify(input)}`, () => { +// expect(isRight(customDurationRt.decode(input))).toBeTruthy(); +// }); +// }); +// }); +// }); +// describe('must be max 1m', () => { +// const customDurationRt = getDurationRt({ max: '1m' }); + +// describe('it should not accept', () => { +// [ +// undefined, +// null, +// '', +// 0, +// 'foo', +// true, +// false, +// '2m', +// '61s', +// '60001ms' +// ].map(input => { +// it(`${JSON.stringify(input)}`, () => { +// expect(isRight(customDurationRt.decode(input))).toBeFalsy(); +// }); +// }); +// }); +// describe('it should accept', () => { +// ['1m', '0m', '-1m', '60s', '6000ms', '1ms', '1s'].map(input => { +// it(`${JSON.stringify(input)}`, () => { +// expect(isRight(customDurationRt.decode(input))).toBeTruthy(); +// }); +// }); +// }); +// }); +// }); +// }); + +describe('bytesRt', () => { + const bytesRt = getAmountAndUnitRt({ + min: 0, + units: ['b', 'mb', 'kb'] + }); + + describe('it should not accept', () => { + ['mb', '-1kb', '5gb', '6tb'].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(bytesRt.decode(input))).toBe(false); + }); + }); + }); + + describe('it should accept', () => { + ['1b', '2kb', '3mb'].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(bytesRt.decode(input))).toBe(true); + }); + }); + }); +}); diff --git a/x-pack/plugins/apm/common/agent_configuration/runtime_types/amount_and_unit_rt.ts b/x-pack/plugins/apm/common/agent_configuration/runtime_types/amount_and_unit_rt.ts new file mode 100644 index 0000000000000..31e53f449144d --- /dev/null +++ b/x-pack/plugins/apm/common/agent_configuration/runtime_types/amount_and_unit_rt.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +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'; + +export function getAmountAndUnitRt({ + min = -Infinity, + max = Infinity, + units +}: { + min?: number; + max?: number; + units: string[]; +}) { + const message = i18n.translate('xpack.apm.agentConfig.amount.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(min, max) + } + }); + + return new t.Type( + 'amountAndUnitRt', + t.string.is, + (input, context) => { + return either.chain(t.string.validate(input, context), inputAsString => { + const { amount, unit } = amountAndUnitToObject(inputAsString); + const amountAsInt = parseInt(amount, 10); + const isValidUnit = units.includes(unit); + const isValid = amountAsInt >= min && amountAsInt <= max && isValidUnit; + + return isValid + ? t.success(inputAsString) + : t.failure(input, context, message); + }); + }, + t.identity + ); +} 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 deleted file mode 100644 index 596037645c002..0000000000000 --- a/x-pack/plugins/apm/common/agent_configuration/runtime_types/bytes_rt.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { bytesRt } from './bytes_rt'; -import { isRight } from 'fp-ts/lib/Either'; - -describe('bytesRt', () => { - describe('it should not accept', () => { - [ - undefined, - null, - '', - 0, - 'foo', - true, - false, - '100', - 'mb', - '0kb', - '5gb', - '6tb' - ].map(input => { - it(`${JSON.stringify(input)}`, () => { - expect(isRight(bytesRt.decode(input))).toBe(false); - }); - }); - }); - - describe('it should accept', () => { - ['1b', '2kb', '3mb'].map(input => { - it(`${JSON.stringify(input)}`, () => { - expect(isRight(bytesRt.decode(input))).toBe(true); - }); - }); - }); -}); 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 deleted file mode 100644 index d189fab89ae5d..0000000000000 --- a/x-pack/plugins/apm/common/agent_configuration/runtime_types/bytes_rt.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import * as t from 'io-ts'; -import { either } from 'fp-ts/lib/Either'; -import { amountAndUnitToObject } from '../amount_and_unit'; - -export const BYTE_UNITS = ['b', 'kb', 'mb']; - -export const bytesRt = new t.Type( - 'bytesRt', - t.string.is, - (input, context) => { - return either.chain(t.string.validate(input, context), inputAsString => { - const { amount, unit } = amountAndUnitToObject(inputAsString); - const amountAsInt = parseInt(amount, 10); - const isValidUnit = BYTE_UNITS.includes(unit); - const isValid = amountAsInt > 0 && isValidUnit; - - return isValid - ? t.success(inputAsString) - : t.failure( - input, - context, - `Must have numeric amount and a valid unit (${BYTE_UNITS})` - ); - }); - }, - t.identity -); 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 deleted file mode 100644 index e861984da592f..0000000000000 --- a/x-pack/plugins/apm/common/agent_configuration/runtime_types/duration_rt.test.ts +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { durationRt, getDurationRt } from './duration_rt'; -import { isRight } from 'fp-ts/lib/Either'; - -describe('durationRt', () => { - describe('it should not accept', () => { - [ - undefined, - null, - '', - 0, - 'foo', - true, - false, - '100', - 's', - 'm', - '0ms', - '-1ms' - ].map(input => { - it(`${JSON.stringify(input)}`, () => { - expect(isRight(durationRt.decode(input))).toBe(false); - }); - }); - }); - - describe('it should accept', () => { - ['1000ms', '2s', '3m', '1s'].map(input => { - it(`${JSON.stringify(input)}`, () => { - expect(isRight(durationRt.decode(input))).toBe(true); - }); - }); - }); -}); - -describe('getDurationRt', () => { - describe('must be at least 1m', () => { - const customDurationRt = getDurationRt({ min: '1m' }); - describe('it should not accept', () => { - [ - undefined, - null, - '', - 0, - 'foo', - true, - false, - '0m', - '-1m', - '1ms', - '1s' - ].map(input => { - it(`${JSON.stringify(input)}`, () => { - expect(isRight(customDurationRt.decode(input))).toBeFalsy(); - }); - }); - }); - describe('it should accept', () => { - ['1m', '2m', '1000m'].map(input => { - it(`${JSON.stringify(input)}`, () => { - expect(isRight(customDurationRt.decode(input))).toBeTruthy(); - }); - }); - }); - }); - - describe('must be between 1ms and 1s', () => { - const customDurationRt = getDurationRt({ min: '1ms', max: '1s' }); - - describe('it should not accept', () => { - [ - undefined, - null, - '', - 0, - 'foo', - true, - false, - '-1s', - '0s', - '2s', - '1001ms', - '0ms', - '-1ms', - '0m', - '1m' - ].map(input => { - it(`${JSON.stringify(input)}`, () => { - expect(isRight(customDurationRt.decode(input))).toBeFalsy(); - }); - }); - }); - describe('it should accept', () => { - ['1s', '1ms', '50ms', '1000ms'].map(input => { - it(`${JSON.stringify(input)}`, () => { - expect(isRight(customDurationRt.decode(input))).toBeTruthy(); - }); - }); - }); - }); - describe('must be max 1m', () => { - const customDurationRt = getDurationRt({ max: '1m' }); - - describe('it should not accept', () => { - [undefined, null, '', 0, 'foo', true, false, '2m', '61s', '60001ms'].map( - input => { - it(`${JSON.stringify(input)}`, () => { - expect(isRight(customDurationRt.decode(input))).toBeFalsy(); - }); - } - ); - }); - describe('it should accept', () => { - ['1m', '0m', '-1m', '60s', '6000ms', '1ms', '1s'].map(input => { - it(`${JSON.stringify(input)}`, () => { - expect(isRight(customDurationRt.decode(input))).toBeTruthy(); - }); - }); - }); - }); -}); diff --git a/x-pack/plugins/apm/common/agent_configuration/runtime_types/float_rt.test.ts b/x-pack/plugins/apm/common/agent_configuration/runtime_types/float_rt.test.ts new file mode 100644 index 0000000000000..82fb8ee068b30 --- /dev/null +++ b/x-pack/plugins/apm/common/agent_configuration/runtime_types/float_rt.test.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { floatRt } from './float_rt'; +import { isRight } from 'fp-ts/lib/Either'; + +describe('floatRt', () => { + it('does not accept empty values', () => { + expect(isRight(floatRt.decode(undefined))).toBe(false); + expect(isRight(floatRt.decode(null))).toBe(false); + expect(isRight(floatRt.decode(''))).toBe(false); + }); + + it('should only accept stringified numbers', () => { + expect(isRight(floatRt.decode('0.5'))).toBe(true); + expect(isRight(floatRt.decode(0.5))).toBe(false); + }); + + it('checks if the number falls within 0, 1', () => { + expect(isRight(floatRt.decode('0'))).toBe(true); + expect(isRight(floatRt.decode('0.5'))).toBe(true); + expect(isRight(floatRt.decode('-0.1'))).toBe(false); + expect(isRight(floatRt.decode('1.1'))).toBe(false); + expect(isRight(floatRt.decode(NaN))).toBe(false); + }); + + it('checks whether the number of decimals is 3', () => { + expect(isRight(floatRt.decode('1'))).toBe(true); + expect(isRight(floatRt.decode('0.9'))).toBe(true); + expect(isRight(floatRt.decode('0.99'))).toBe(true); + expect(isRight(floatRt.decode('0.999'))).toBe(true); + expect(isRight(floatRt.decode('0.9999'))).toBe(false); + }); +}); diff --git a/x-pack/plugins/apm/common/agent_configuration/runtime_types/float_rt.ts b/x-pack/plugins/apm/common/agent_configuration/runtime_types/float_rt.ts new file mode 100644 index 0000000000000..4aa166f84bfe9 --- /dev/null +++ b/x-pack/plugins/apm/common/agent_configuration/runtime_types/float_rt.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as t from 'io-ts'; +import { either } from 'fp-ts/lib/Either'; + +export const floatRt = new t.Type( + 'floatRt', + t.string.is, + (input, context) => { + return either.chain(t.string.validate(input, context), inputAsString => { + const inputAsFloat = parseFloat(inputAsString); + const maxThreeDecimals = + parseFloat(inputAsFloat.toFixed(3)) === inputAsFloat; + + const isValid = + inputAsFloat >= 0 && inputAsFloat <= 1 && maxThreeDecimals; + + return isValid + ? t.success(inputAsString) + : t.failure(input, context, 'Must be a number between 0.000 and 1'); + }); + }, + t.identity +); 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 new file mode 100644 index 0000000000000..83239a5490295 --- /dev/null +++ b/x-pack/plugins/apm/common/agent_configuration/runtime_types/get_range_type.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { isFinite } from 'lodash'; + +export function getRangeType(min?: number, max?: number) { + if (isFinite(min) && isFinite(max)) { + return 'between'; + } else if (isFinite(min)) { + return 'gt'; // greater than + } else if (isFinite(max)) { + return 'lt'; // less than + } +} 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 ef7fbeed4331e..3286998131179 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 @@ -4,43 +4,51 @@ * you may not use this file except in compliance with the Elastic License. */ -import { integerRt, getIntegerRt } from './integer_rt'; +import { getIntegerRt } from './integer_rt'; import { isRight } from 'fp-ts/lib/Either'; -describe('integerRt', () => { - describe('it should not accept', () => { - [undefined, null, '', 'foo', 0, 55, NaN].map(input => { - it(`${JSON.stringify(input)}`, () => { - expect(isRight(integerRt.decode(input))).toBe(false); - }); +describe('getIntegerRt', () => { + describe('with range', () => { + const integerRt = getIntegerRt({ + min: 0, + max: 32000 + }); + + describe('it should not accept', () => { + [NaN, undefined, null, '', 'foo', 0, 55, '-1', '-55', '33000'].map( + input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(integerRt.decode(input))).toBe(false); + }); + } + ); }); - }); - describe('it should accept', () => { - ['-1234', '-1', '0', '1000', '32000', '100000'].map(input => { - it(`${JSON.stringify(input)}`, () => { - expect(isRight(integerRt.decode(input))).toBe(true); + describe('it should accept number between 0 and 32000', () => { + ['0', '1000', '32000'].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(integerRt.decode(input))).toBe(true); + }); }); }); }); -}); -describe('getIntegerRt', () => { - const customIntegerRt = getIntegerRt({ min: 0, max: 32000 }); - describe('it should not accept', () => { - [undefined, null, '', 'foo', 0, 55, '-1', '-55', '33000', NaN].map( - input => { + describe('without range', () => { + const integerRt = getIntegerRt(); + + describe('it should not accept', () => { + [NaN, undefined, null, '', 'foo', 0, 55].map(input => { it(`${JSON.stringify(input)}`, () => { - expect(isRight(customIntegerRt.decode(input))).toBe(false); + expect(isRight(integerRt.decode(input))).toBe(false); }); - } - ); - }); + }); + }); - describe('it should accept', () => { - ['0', '1000', '32000'].map(input => { - it(`${JSON.stringify(input)}`, () => { - expect(isRight(customIntegerRt.decode(input))).toBe(true); + describe('it should accept any number', () => { + ['-100', '-1', '0', '1000', '32000', '100000'].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(integerRt.decode(input))).toBe(true); + }); }); }); }); 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 6dbf175c8b4ce..6cae5e6d9cb15 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,26 @@ import * as t from 'io-ts'; import { either } from 'fp-ts/lib/Either'; +import { i18n } from '@kbn/i18n'; +import { getRangeType } from './get_range_type'; + +export function getIntegerRt({ + min = -Infinity, + max = Infinity +}: { + 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) } + }); -export function getIntegerRt({ min, max }: { min: number; max: number }) { return new t.Type( 'integerRt', t.string.is, @@ -17,15 +35,9 @@ export function getIntegerRt({ min, max }: { min: number; max: number }) { const isValid = inputAsInt >= min && inputAsInt <= max; return isValid ? t.success(inputAsString) - : t.failure( - input, - context, - `Number must be a valid number between ${min} and ${max}` - ); + : t.failure(input, context, message); }); }, t.identity ); } - -export const integerRt = getIntegerRt({ min: -Infinity, max: Infinity }); diff --git a/x-pack/plugins/apm/common/agent_configuration/runtime_types/number_float_rt.test.ts b/x-pack/plugins/apm/common/agent_configuration/runtime_types/number_float_rt.test.ts deleted file mode 100644 index ece229ca162fb..0000000000000 --- a/x-pack/plugins/apm/common/agent_configuration/runtime_types/number_float_rt.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import { numberFloatRt } from './number_float_rt'; -import { isRight } from 'fp-ts/lib/Either'; - -describe('numberFloatRt', () => { - it('does not accept empty values', () => { - expect(isRight(numberFloatRt.decode(undefined))).toBe(false); - expect(isRight(numberFloatRt.decode(null))).toBe(false); - expect(isRight(numberFloatRt.decode(''))).toBe(false); - }); - - it('should only accept stringified numbers', () => { - expect(isRight(numberFloatRt.decode('0.5'))).toBe(true); - expect(isRight(numberFloatRt.decode(0.5))).toBe(false); - }); - - it('checks if the number falls within 0, 1', () => { - expect(isRight(numberFloatRt.decode('0'))).toBe(true); - expect(isRight(numberFloatRt.decode('0.5'))).toBe(true); - expect(isRight(numberFloatRt.decode('-0.1'))).toBe(false); - expect(isRight(numberFloatRt.decode('1.1'))).toBe(false); - expect(isRight(numberFloatRt.decode(NaN))).toBe(false); - }); - - it('checks whether the number of decimals is 3', () => { - expect(isRight(numberFloatRt.decode('1'))).toBe(true); - expect(isRight(numberFloatRt.decode('0.9'))).toBe(true); - expect(isRight(numberFloatRt.decode('0.99'))).toBe(true); - expect(isRight(numberFloatRt.decode('0.999'))).toBe(true); - expect(isRight(numberFloatRt.decode('0.9999'))).toBe(false); - }); -}); diff --git a/x-pack/plugins/apm/common/agent_configuration/runtime_types/number_float_rt.ts b/x-pack/plugins/apm/common/agent_configuration/runtime_types/number_float_rt.ts deleted file mode 100644 index f1890c9851a3d..0000000000000 --- a/x-pack/plugins/apm/common/agent_configuration/runtime_types/number_float_rt.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import * as t from 'io-ts'; -import { either } from 'fp-ts/lib/Either'; - -export function getNumberFloatRt({ min, max }: { min: number; max: number }) { - return new t.Type( - 'numberFloatRt', - t.string.is, - (input, context) => { - return either.chain(t.string.validate(input, context), inputAsString => { - const inputAsFloat = parseFloat(inputAsString); - const maxThreeDecimals = - parseFloat(inputAsFloat.toFixed(3)) === inputAsFloat; - - const isValid = - inputAsFloat >= min && inputAsFloat <= max && maxThreeDecimals; - - return isValid - ? t.success(inputAsString) - : t.failure( - input, - context, - `Number must be between ${min} and ${max}` - ); - }); - }, - t.identity - ); -} - -export const numberFloatRt = getNumberFloatRt({ min: 0, max: 1 }); diff --git a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts index 1693a627ec7b5..0b7fd71319c58 100644 --- a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts +++ b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts @@ -5,14 +5,9 @@ */ import { i18n } from '@kbn/i18n'; -import { getIntegerRt } from '../runtime_types/integer_rt'; import { captureBodyRt } from '../runtime_types/capture_body_rt'; import { RawSettingDefinition } from './types'; -import { getDurationRt } from '../runtime_types/duration_rt'; -/* - * Settings added here will show up in the UI and will be validated on the client and server - */ export const generalSettings: RawSettingDefinition[] = [ // API Request Size { @@ -144,7 +139,7 @@ export const generalSettings: RawSettingDefinition[] = [ { key: 'span_frames_min_duration', type: 'duration', - validation: getDurationRt({ min: '-1ms' }), + // min: '-1ms', defaultValue: '5ms', label: i18n.translate('xpack.apm.agentConfig.spanFramesMinDuration.label', { defaultMessage: 'Span frames minimum duration' @@ -156,8 +151,7 @@ export const generalSettings: RawSettingDefinition[] = [ 'In its default settings, the APM agent will collect a stack trace with every recorded span.\nWhile this is very helpful to find the exact place in your code that causes the span, collecting this stack trace does have some overhead. \nWhen setting this option to a negative value, like `-1ms`, stack traces will be collected for all spans. Setting it to a positive value, e.g. `5ms`, will limit stack trace collection to spans with durations equal to or longer than the given value, e.g. 5 milliseconds.\n\nTo disable stack trace collection for spans completely, set the value to `0ms`.' } ), - excludeAgents: ['js-base', 'rum-js', 'nodejs'], - min: -1 + excludeAgents: ['js-base', 'rum-js', 'nodejs'] }, // STACK_TRACE_LIMIT @@ -182,11 +176,8 @@ export const generalSettings: RawSettingDefinition[] = [ { key: 'transaction_max_spans', type: 'integer', - validation: getIntegerRt({ min: 0, max: 32000 }), - validationError: i18n.translate( - 'xpack.apm.agentConfig.transactionMaxSpans.errorText', - { defaultMessage: 'Must be between 0 and 32000' } - ), + min: 0, + max: 32000, defaultValue: '500', label: i18n.translate('xpack.apm.agentConfig.transactionMaxSpans.label', { defaultMessage: 'Transaction max spans' @@ -198,8 +189,6 @@ export const generalSettings: RawSettingDefinition[] = [ 'Limits the amount of spans that are recorded per transaction.' } ), - min: 0, - max: 32000, excludeAgents: ['js-base', 'rum-js'] }, 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 8786a94be096d..aceb5d053f5a4 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 @@ -7,58 +7,74 @@ import * as t from 'io-ts'; import { sortBy } from 'lodash'; import { isRight } from 'fp-ts/lib/Either'; -import { i18n } from '@kbn/i18n'; +import { PathReporter } from 'io-ts/lib/PathReporter'; import { AgentName } from '../../../typings/es_schemas/ui/fields/agent'; import { booleanRt } from '../runtime_types/boolean_rt'; -import { integerRt } from '../runtime_types/integer_rt'; +import { getIntegerRt } from '../runtime_types/integer_rt'; import { isRumAgentName } from '../../agent_name'; -import { numberFloatRt } from '../runtime_types/number_float_rt'; -import { bytesRt, BYTE_UNITS } from '../runtime_types/bytes_rt'; -import { durationRt, DURATION_UNITS } from '../runtime_types/duration_rt'; +import { floatRt } from '../runtime_types/float_rt'; +import { getAmountAndUnitRt } from '../runtime_types/amount_and_unit_rt'; import { RawSettingDefinition, SettingDefinition } from './types'; import { generalSettings } from './general_settings'; import { javaSettings } from './java_settings'; -function getDefaultsByType(settingDefinition: RawSettingDefinition) { - switch (settingDefinition.type) { +function getSettingDefaults(setting: RawSettingDefinition): SettingDefinition { + switch (setting.type) { + case 'select': + return { validation: t.string, ...setting }; + case 'boolean': - return { validation: booleanRt }; + return { validation: booleanRt, ...setting }; + case 'text': - return { validation: t.string }; - case 'integer': + return { validation: t.string, ...setting }; + + case 'integer': { + const { min, max } = setting; + return { - validation: integerRt, - validationError: i18n.translate( - 'xpack.apm.agentConfig.integer.errorText', - { defaultMessage: 'Must be an integer' } - ) + validation: getIntegerRt({ min, max }), + min, + max, + ...setting }; - case 'float': + } + + case 'float': { return { - validation: numberFloatRt, - validationError: i18n.translate( - 'xpack.apm.agentConfig.float.errorText', - { defaultMessage: 'Must be a number between 0.000 and 1' } - ) + validation: floatRt, + ...setting }; - case 'bytes': + } + + case 'bytes': { + const units = setting.units ?? ['b', 'kb', 'mb']; + const min = setting.min ?? 0; + const max = setting.max; + return { - validation: bytesRt, - units: BYTE_UNITS, - validationError: i18n.translate( - 'xpack.apm.agentConfig.bytes.errorText', - { defaultMessage: 'Please specify an integer and a unit' } - ) + validation: getAmountAndUnitRt({ min, max, units }), + units, + min, + ...setting }; - case 'duration': + } + + case 'duration': { + const units = setting.units ?? ['ms', 's', 'm']; + const min = setting.min ?? 0; + const max = setting.max; + return { - validation: durationRt, - units: DURATION_UNITS, - validationError: i18n.translate( - 'xpack.apm.agentConfig.bytes.errorText', - { defaultMessage: 'Please specify an integer and a unit' } - ) + validation: getAmountAndUnitRt({ min, max, units }), + units, + min, + ...setting }; + } + + default: + return setting; } } @@ -91,23 +107,14 @@ export function filterByAgent(agentName?: AgentName) { }; } -export function isValid(setting: SettingDefinition, value: unknown) { - return isRight(setting.validation.decode(value)); +export function validateSetting(setting: SettingDefinition, value: unknown) { + const result = setting.validation.decode(value); + const message = PathReporter.report(result)[0]; + const isValid = isRight(result); + return { isValid, message }; } -export const settingDefinitions = sortBy( - [...generalSettings, ...javaSettings].map(def => { - const defWithDefaults = { - ...getDefaultsByType(def), - ...def - }; - - // ensure every option has validation - if (!defWithDefaults.validation) { - throw new Error(`Missing validation for ${def.key}`); - } - - return defWithDefaults as SettingDefinition; - }), +export const settingDefinitions: SettingDefinition[] = sortBy( + [...generalSettings, ...javaSettings].map(getSettingDefaults), 'key' ); diff --git a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/types.d.ts b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/types.d.ts index 815b8cb3d4e83..f8a62f9713c92 100644 --- a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/types.d.ts +++ b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/types.d.ts @@ -7,6 +7,9 @@ import * as t from 'io-ts'; import { AgentName } from '../../../typings/es_schemas/ui/fields/agent'; +// TODO: is it possible to get rid of `any`? +export type SettingValidation = t.Type; + interface BaseSetting { /** * UI: unique key to identify setting @@ -25,7 +28,7 @@ interface BaseSetting { category?: string; /** - * UI: + * UI: Default value set by agent */ defaultValue?: string; @@ -39,16 +42,6 @@ interface BaseSetting { */ placeholder?: string; - /** - * runtime validation of the input - */ - validation?: t.Type; - - /** - * UI: error shown when the runtime validation fails - */ - validationError?: string; - /** * Limits the setting to no agents, except those specified in `includeAgents` */ @@ -62,36 +55,41 @@ interface BaseSetting { interface TextSetting extends BaseSetting { type: 'text'; -} - -interface IntegerSetting extends BaseSetting { - type: 'integer'; - min?: number; - max?: number; -} - -interface FloatSetting extends BaseSetting { - type: 'float'; + validation?: SettingValidation; } interface SelectSetting extends BaseSetting { type: 'select'; options: Array<{ text: string; value: string }>; + validation?: SettingValidation; } interface BooleanSetting extends BaseSetting { type: 'boolean'; } +interface FloatSetting extends BaseSetting { + type: 'float'; +} + +interface IntegerSetting extends BaseSetting { + type: 'integer'; + min?: number; + max?: number; +} + interface BytesSetting extends BaseSetting { type: 'bytes'; + min?: number; + max?: number; units?: string[]; } interface DurationSetting extends BaseSetting { type: 'duration'; - units?: string[]; min?: number; + max?: number; + units?: string[]; } export type RawSettingDefinition = @@ -104,5 +102,8 @@ export type RawSettingDefinition = | DurationSetting; export type SettingDefinition = RawSettingDefinition & { - validation: NonNullable; + /** + * runtime validation of input + */ + validation: SettingValidation; }; From 2f78f1852debd1d863c3499bb384d2b607f61891 Mon Sep 17 00:00:00 2001 From: cauemarcondes Date: Wed, 29 Apr 2020 10:49:42 +0200 Subject: [PATCH 04/12] refactoring --- .../SettingsPage/SettingFormRow.tsx | 6 +- .../runtime_types/amount_and_unit_rt.test.ts | 156 ------------------ .../runtime_types/bytes_rt.test.ts | 31 ++++ .../{amount_and_unit_rt.ts => bytes_rt.ts} | 6 +- .../runtime_types/duration_rt.test.ts | 96 +++++++++++ .../runtime_types/duration_rt.ts | 53 ++++-- .../__snapshots__/index.test.ts.snap | 35 ++-- .../setting_definitions/index.ts | 9 +- .../setting_definitions/java_settings.ts | 14 +- .../setting_definitions/types.d.ts | 4 +- 10 files changed, 198 insertions(+), 212 deletions(-) delete mode 100644 x-pack/plugins/apm/common/agent_configuration/runtime_types/amount_and_unit_rt.test.ts create mode 100644 x-pack/plugins/apm/common/agent_configuration/runtime_types/bytes_rt.test.ts rename x-pack/plugins/apm/common/agent_configuration/runtime_types/{amount_and_unit_rt.ts => bytes_rt.ts} (94%) create mode 100644 x-pack/plugins/apm/common/agent_configuration/runtime_types/duration_rt.test.ts diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingFormRow.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingFormRow.tsx index b2e9be4383f88..75caed4ae480f 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingFormRow.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingFormRow.tsx @@ -93,8 +93,10 @@ function FormRow({ onChange( setting.key, diff --git a/x-pack/plugins/apm/common/agent_configuration/runtime_types/amount_and_unit_rt.test.ts b/x-pack/plugins/apm/common/agent_configuration/runtime_types/amount_and_unit_rt.test.ts deleted file mode 100644 index a8c16074d324c..0000000000000 --- a/x-pack/plugins/apm/common/agent_configuration/runtime_types/amount_and_unit_rt.test.ts +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { getAmountAndUnitRt } from './amount_and_unit_rt'; -import { isRight } from 'fp-ts/lib/Either'; - -describe('durationRt', () => { - const durationRt = getAmountAndUnitRt({ - min: 0, - units: ['ms', 's', 'm'] - }); - - describe('it should not accept', () => { - [undefined, null, '', 0, 'foo', true, false, '100', 's', 'm'].map(input => { - it(`${JSON.stringify(input)}`, () => { - expect(isRight(durationRt.decode(input))).toBe(false); - }); - }); - }); - - describe('It should accept', () => { - ['0h', '1000ms', '2s', '3m'].map(input => { - it(`${JSON.stringify(input)}`, () => { - expect(isRight(durationRt.decode(input))).toBe(true); - }); - }); - }); -}); - -// describe('getDurationRt', () => { -// describe('must be at least 1m', () => { -// const customDurationRt = getDurationRt({ min: '1m' }); -// describe('it should not accept', () => { -// [ -// undefined, -// null, -// '', -// 0, -// 'foo', -// true, -// false, -// '0m', -// '-1m', -// '1ms', -// '1s' -// ].map(input => { -// it(`${JSON.stringify(input)}`, () => { -// expect(isRight(customDurationRt.decode(input))).toBeFalsy(); -// }); -// }); -// }); -// describe('it should accept', () => { -// ['1m', '2m', '1000m'].map(input => { -// it(`${JSON.stringify(input)}`, () => { -// expect(isRight(customDurationRt.decode(input))).toBeTruthy(); -// }); -// }); -// }); - -// describe('must be between 1ms and 1s', () => { -// const customDurationRt = getDurationRt({ min: '1ms', max: '1s' }); - -// describe('it should not accept', () => { -// [ -// undefined, -// null, -// '', -// 0, -// 'foo', -// true, -// false, -// '-1s', -// '0s', -// '2s', -// '1001ms', -// '0ms', -// '-1ms', -// '0m', -// '1m' -// ].map(input => { -// it(`${JSON.stringify(input)}`, () => { -// expect(isRight(customDurationRt.decode(input))).toBeFalsy(); -// }); -// }); -// }); -// describe('it should accept', () => { -// ['1s', '1ms', '50ms', '1000ms'].map(input => { -// it(`${JSON.stringify(input)}`, () => { -// expect(isRight(customDurationRt.decode(input))).toBeTruthy(); -// }); -// }); -// }); -// }); -// describe('must be max 1m', () => { -// const customDurationRt = getDurationRt({ max: '1m' }); - -// describe('it should not accept', () => { -// [ -// undefined, -// null, -// '', -// 0, -// 'foo', -// true, -// false, -// '2m', -// '61s', -// '60001ms' -// ].map(input => { -// it(`${JSON.stringify(input)}`, () => { -// expect(isRight(customDurationRt.decode(input))).toBeFalsy(); -// }); -// }); -// }); -// describe('it should accept', () => { -// ['1m', '0m', '-1m', '60s', '6000ms', '1ms', '1s'].map(input => { -// it(`${JSON.stringify(input)}`, () => { -// expect(isRight(customDurationRt.decode(input))).toBeTruthy(); -// }); -// }); -// }); -// }); -// }); -// }); - -describe('bytesRt', () => { - const bytesRt = getAmountAndUnitRt({ - min: 0, - units: ['b', 'mb', 'kb'] - }); - - describe('it should not accept', () => { - ['mb', '-1kb', '5gb', '6tb'].map(input => { - it(`${JSON.stringify(input)}`, () => { - expect(isRight(bytesRt.decode(input))).toBe(false); - }); - }); - }); - - describe('it should accept', () => { - ['1b', '2kb', '3mb'].map(input => { - it(`${JSON.stringify(input)}`, () => { - expect(isRight(bytesRt.decode(input))).toBe(true); - }); - }); - }); -}); 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 new file mode 100644 index 0000000000000..4df6128f248ac --- /dev/null +++ b/x-pack/plugins/apm/common/agent_configuration/runtime_types/bytes_rt.test.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { getBytesRt } from './bytes_rt'; +import { isRight } from 'fp-ts/lib/Either'; + +describe('bytesRt', () => { + const bytesRt = getBytesRt({ + min: 0, + units: ['b', 'mb', 'kb'] + }); + + describe('it should not accept', () => { + ['mb', '-1kb', '5gb', '6tb'].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(bytesRt.decode(input))).toBe(false); + }); + }); + }); + + describe('it should accept', () => { + ['1b', '2kb', '3mb'].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(bytesRt.decode(input))).toBe(true); + }); + }); + }); +}); diff --git a/x-pack/plugins/apm/common/agent_configuration/runtime_types/amount_and_unit_rt.ts b/x-pack/plugins/apm/common/agent_configuration/runtime_types/bytes_rt.ts similarity index 94% rename from x-pack/plugins/apm/common/agent_configuration/runtime_types/amount_and_unit_rt.ts rename to x-pack/plugins/apm/common/agent_configuration/runtime_types/bytes_rt.ts index 31e53f449144d..b623e854e41a7 100644 --- a/x-pack/plugins/apm/common/agent_configuration/runtime_types/amount_and_unit_rt.ts +++ b/x-pack/plugins/apm/common/agent_configuration/runtime_types/bytes_rt.ts @@ -10,7 +10,9 @@ import { i18n } from '@kbn/i18n'; import { amountAndUnitToObject } from '../amount_and_unit'; import { getRangeType } from './get_range_type'; -export function getAmountAndUnitRt({ +export const BYTE_UNITS = ['b', 'kb', 'mb']; + +export function getBytesRt({ min = -Infinity, max = Infinity, units @@ -35,7 +37,7 @@ export function getAmountAndUnitRt({ }); return new t.Type( - 'amountAndUnitRt', + 'bytesRt', t.string.is, (input, context) => { return either.chain(t.string.validate(input, context), inputAsString => { 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 new file mode 100644 index 0000000000000..fff1052cf6da2 --- /dev/null +++ b/x-pack/plugins/apm/common/agent_configuration/runtime_types/duration_rt.test.ts @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { getDurationRt } from './duration_rt'; +import { isRight } from 'fp-ts/lib/Either'; + +describe('getDurationRt', () => { + const units = ['ms', 's', 'm']; + describe('must be at least 1m', () => { + const customDurationRt = getDurationRt({ min: '1m', units }); + describe('it should not accept', () => { + [ + undefined, + null, + '', + 0, + 'foo', + true, + false, + '0m', + '-1m', + '1ms', + '1s' + ].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(customDurationRt.decode(input))).toBeFalsy(); + }); + }); + }); + describe('it should accept', () => { + ['1m', '2m', '1000m'].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(customDurationRt.decode(input))).toBeTruthy(); + }); + }); + }); + }); + + describe('must be between 1ms and 1s', () => { + const customDurationRt = getDurationRt({ min: '1ms', max: '1s', units }); + + describe('it should not accept', () => { + [ + undefined, + null, + '', + 0, + 'foo', + true, + false, + '-1s', + '0s', + '2s', + '1001ms', + '0ms', + '-1ms', + '0m', + '1m' + ].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(customDurationRt.decode(input))).toBeFalsy(); + }); + }); + }); + describe('it should accept', () => { + ['1s', '1ms', '50ms', '1000ms'].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(customDurationRt.decode(input))).toBeTruthy(); + }); + }); + }); + }); + describe('must be max 1m', () => { + const customDurationRt = getDurationRt({ max: '1m', units }); + + describe('it should not accept', () => { + [undefined, null, '', 0, 'foo', true, false, '2m', '61s', '60001ms'].map( + input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(customDurationRt.decode(input))).toBeFalsy(); + }); + } + ); + }); + describe('it should accept', () => { + ['1m', '0m', '-1m', '60s', '6000ms', '1ms', '1s'].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(customDurationRt.decode(input))).toBeTruthy(); + }); + }); + }); + }); +}); 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 cdfc18eb0ea72..32093e1834fda 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,15 +7,42 @@ 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 } from '../amount_and_unit'; - -export const DURATION_UNITS = ['ms', 's', 'm']; +import { getRangeType } from './get_range_type'; function getDuration({ amount, unit }: { amount: string; unit: string }) { return moment.duration(parseInt(amount, 10), unit as unitOfTime.Base); } -export function getDurationRt({ min, max }: { min?: string; max?: string }) { +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.amount.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 ? parseInt(minAmountAndUnit.amount, 10) : undefined, + maxAmountAndUnit ? parseInt(maxAmountAndUnit.amount, 10) : undefined + ) + } + }); return new t.Type( 'durationRt', t.string.is, @@ -23,28 +50,24 @@ export function getDurationRt({ min, max }: { min?: string; max?: string }) { return either.chain(t.string.validate(input, context), inputAsString => { const { amount, unit } = amountAndUnitToObject(inputAsString); const inputDuration = getDuration({ amount, unit }); - const minDuration = min - ? getDuration(amountAndUnitToObject(min)) + + const minDuration = minAmountAndUnit + ? getDuration(minAmountAndUnit) : inputDuration; - const maxDuration = max - ? getDuration(amountAndUnitToObject(max)) + + const maxDuration = maxAmountAndUnit + ? getDuration(maxAmountAndUnit) : inputDuration; - const isValidUnit = DURATION_UNITS.includes(unit); + const isValidUnit = units.includes(unit); const isValidAmount = inputDuration >= minDuration && inputDuration <= maxDuration; return isValidUnit && isValidAmount ? t.success(inputAsString) - : t.failure( - input, - context, - `Must have numeric amount and a valid unit (${DURATION_UNITS})` - ); + : t.failure(input, context, message); }); }, t.identity ); } - -export const durationRt = getDurationRt({ min: '1ms' }); diff --git a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap index d62fa58ce8ee2..536682dc3a53c 100644 --- a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap +++ b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap @@ -4,24 +4,24 @@ exports[`settingDefinitions should have correct default values 1`] = ` Array [ Object { "key": "api_request_size", + "min": 0, "type": "bytes", "units": Array [ "b", "kb", "mb", ], - "validationError": "Please specify an integer and a unit", "validationName": "bytesRt", }, Object { "key": "api_request_time", + "min": "1ms", "type": "duration", "units": Array [ "ms", "s", "m", ], - "validationError": "Please specify an integer and a unit", "validationName": "durationRt", }, Object { @@ -84,24 +84,25 @@ Array [ }, Object { "key": "profiling_inferred_spans_min_duration", + "min": "1ms", "type": "duration", "units": Array [ "ms", "s", "m", ], - "validationError": "Please specify an integer and a unit", "validationName": "durationRt", }, Object { "key": "profiling_inferred_spans_sampling_interval", + "max": "1s", + "min": "1ms", "type": "duration", "units": Array [ "ms", "s", "m", ], - "validationError": "Must be between '1ms' and '1s'", "validationName": "durationRt", }, Object { @@ -111,81 +112,75 @@ Array [ }, Object { "key": "server_timeout", + "min": "1ms", "type": "duration", "units": Array [ "ms", "s", "m", ], - "validationError": "Please specify an integer and a unit", "validationName": "durationRt", }, Object { "key": "span_frames_min_duration", - "min": -1, + "min": "1ms", "type": "duration", "units": Array [ "ms", "s", "m", ], - "validationError": "Please specify an integer and a unit", "validationName": "durationRt", }, Object { "key": "stack_trace_limit", + "max": undefined, + "min": undefined, "type": "integer", - "validationError": "Must be an integer", "validationName": "integerRt", }, Object { "key": "stress_monitor_cpu_duration_threshold", + "min": "1m", "type": "duration", "units": Array [ "ms", "s", "m", ], - "validationError": "Must be at least '1m'", "validationName": "durationRt", }, Object { "key": "stress_monitor_gc_relief_threshold", "type": "float", - "validationError": "Must be a number between 0.000 and 1", - "validationName": "numberFloatRt", + "validationName": "floatRt", }, Object { "key": "stress_monitor_gc_stress_threshold", "type": "float", - "validationError": "Must be a number between 0.000 and 1", - "validationName": "numberFloatRt", + "validationName": "floatRt", }, Object { "key": "stress_monitor_system_cpu_relief_threshold", "type": "float", - "validationError": "Must be a number between 0.000 and 1", - "validationName": "numberFloatRt", + "validationName": "floatRt", }, Object { "key": "stress_monitor_system_cpu_stress_threshold", "type": "float", - "validationError": "Must be a number between 0.000 and 1", - "validationName": "numberFloatRt", + "validationName": "floatRt", }, Object { "key": "transaction_max_spans", "max": 32000, "min": 0, "type": "integer", - "validationError": "Must be between 0 and 32000", "validationName": "integerRt", }, Object { "key": "transaction_sample_rate", "type": "float", - "validationError": "Must be a number between 0.000 and 1", - "validationName": "numberFloatRt", + "validationName": "floatRt", }, ] `; 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 aceb5d053f5a4..f7760aaafc0f2 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 @@ -13,10 +13,11 @@ import { booleanRt } from '../runtime_types/boolean_rt'; import { getIntegerRt } from '../runtime_types/integer_rt'; import { isRumAgentName } from '../../agent_name'; import { floatRt } from '../runtime_types/float_rt'; -import { getAmountAndUnitRt } from '../runtime_types/amount_and_unit_rt'; import { RawSettingDefinition, SettingDefinition } from './types'; import { generalSettings } from './general_settings'; import { javaSettings } from './java_settings'; +import { getDurationRt } from '../runtime_types/duration_rt'; +import { getBytesRt } from '../runtime_types/bytes_rt'; function getSettingDefaults(setting: RawSettingDefinition): SettingDefinition { switch (setting.type) { @@ -53,7 +54,7 @@ function getSettingDefaults(setting: RawSettingDefinition): SettingDefinition { const max = setting.max; return { - validation: getAmountAndUnitRt({ min, max, units }), + validation: getBytesRt({ min, max, units }), units, min, ...setting @@ -62,11 +63,11 @@ function getSettingDefaults(setting: RawSettingDefinition): SettingDefinition { case 'duration': { const units = setting.units ?? ['ms', 's', 'm']; - const min = setting.min ?? 0; + const min = setting.min ?? '1ms'; const max = setting.max; return { - validation: getAmountAndUnitRt({ min, max, units }), + validation: getDurationRt({ min, max, units }), units, min, ...setting diff --git a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/java_settings.ts b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/java_settings.ts index fbb2fb1ea4779..bc8f19becf053 100644 --- a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/java_settings.ts +++ b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/java_settings.ts @@ -6,7 +6,6 @@ import { i18n } from '@kbn/i18n'; import { RawSettingDefinition } from './types'; -import { getDurationRt } from '../runtime_types/duration_rt'; export const javaSettings: RawSettingDefinition[] = [ // ENABLE_LOG_CORRELATION @@ -101,11 +100,7 @@ export const javaSettings: RawSettingDefinition[] = [ } ), includeAgents: ['java'], - validation: getDurationRt({ min: '1m' }), - validationError: i18n.translate( - 'xpack.apm.agentConfig.stressMonitorCpuDurationThreshold.errorText', - { defaultMessage: "Must be at least '1m'" } - ) + min: '1m' }, { key: 'stress_monitor_system_cpu_stress_threshold', @@ -183,11 +178,8 @@ export const javaSettings: RawSettingDefinition[] = [ } ), includeAgents: ['java'], - validation: getDurationRt({ min: '1ms', max: '1s' }), - validationError: i18n.translate( - 'xpack.apm.agentConfig.profilingInferredSpansSamplingInterval.errorText', - { defaultMessage: "Must be between '1ms' and '1s'" } - ) + min: '1ms', + max: '1s' }, { key: 'profiling_inferred_spans_min_duration', diff --git a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/types.d.ts b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/types.d.ts index f8a62f9713c92..29125193edfb6 100644 --- a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/types.d.ts +++ b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/types.d.ts @@ -87,8 +87,8 @@ interface BytesSetting extends BaseSetting { interface DurationSetting extends BaseSetting { type: 'duration'; - min?: number; - max?: number; + min?: string; + max?: string; units?: string[]; } From 8065477dabad62919265e39cbc65642b1e08bde7 Mon Sep 17 00:00:00 2001 From: cauemarcondes Date: Wed, 29 Apr 2020 11:10:13 +0200 Subject: [PATCH 05/12] refactoring --- .../agent_configuration/setting_definitions/general_settings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts index 0b7fd71319c58..4ade59d489040 100644 --- a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts +++ b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts @@ -139,7 +139,7 @@ export const generalSettings: RawSettingDefinition[] = [ { key: 'span_frames_min_duration', type: 'duration', - // min: '-1ms', + min: '-1ms', defaultValue: '5ms', label: i18n.translate('xpack.apm.agentConfig.spanFramesMinDuration.label', { defaultMessage: 'Span frames minimum duration' From c0b073f2559dabc0e6d181509753746a59f9ba93 Mon Sep 17 00:00:00 2001 From: cauemarcondes Date: Wed, 29 Apr 2020 11:17:52 +0200 Subject: [PATCH 06/12] refactoring --- .../setting_definitions/__snapshots__/index.test.ts.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap index 536682dc3a53c..92aa0e438d97b 100644 --- a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap +++ b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap @@ -123,7 +123,7 @@ Array [ }, Object { "key": "span_frames_min_duration", - "min": "1ms", + "min": "-1ms", "type": "duration", "units": Array [ "ms", From dd2a462d7e5aa55bc74e9c7e4e540fd41b48a53b Mon Sep 17 00:00:00 2001 From: cauemarcondes Date: Wed, 29 Apr 2020 13:15:23 +0200 Subject: [PATCH 07/12] fixing i18n --- .../apm/common/agent_configuration/runtime_types/bytes_rt.ts | 2 +- .../common/agent_configuration/runtime_types/duration_rt.ts | 2 +- x-pack/plugins/translations/translations/ja-JP.json | 4 ---- x-pack/plugins/translations/translations/zh-CN.json | 4 ---- 4 files changed, 2 insertions(+), 10 deletions(-) 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 b623e854e41a7..99c8e2dfbe412 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 @@ -21,7 +21,7 @@ export function getBytesRt({ max?: number; units: string[]; }) { - const message = i18n.translate('xpack.apm.agentConfig.amount.errorText', { + 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}} 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 32093e1834fda..39ba4c28cdf53 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 @@ -27,7 +27,7 @@ export function getDurationRt({ const minAmountAndUnit = min && amountAndUnitToObject(min); const maxAmountAndUnit = max && amountAndUnitToObject(max); - const message = i18n.translate('xpack.apm.agentConfig.amount.errorText', { + 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}} diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 5bb0891516517..4f690ee398af9 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -4097,7 +4097,6 @@ "xpack.apm.agentConfig.apiRequestSize.label": "API リクエストサイズ", "xpack.apm.agentConfig.apiRequestTime.description": "APM Server への HTTP リクエストを開いておく最大時間。\n\n注:この値は、APM Server の「read_timeout」設定よりも低くする必要があります。", "xpack.apm.agentConfig.apiRequestTime.label": "API リクエスト時間", - "xpack.apm.agentConfig.bytes.errorText": "整数と単位を指定してください", "xpack.apm.agentConfig.captureBody.description": "HTTP リクエストのトランザクションの場合、エージェントはオプションとしてリクエスト本文 (POST 変数など) をキャプチャすることができます。デフォルトは「off」です。", "xpack.apm.agentConfig.captureBody.label": "本文をキャプチャ", "xpack.apm.agentConfig.captureHeaders.description": "「true」に設定すると、エージェントは Cookie を含むリクエストヘッダーとレスポンスヘッダーをキャプチャします。\n\n注:これを「false」に設定すると、ネットワーク帯域幅、ディスク容量、およびオブジェクト割り当てが減少します。", @@ -4131,8 +4130,6 @@ "xpack.apm.agentConfig.editConfigTitle": "構成の編集", "xpack.apm.agentConfig.enableLogCorrelation.description": "エージェントが SLF4J のhttps://www.slf4j.org/api/org/slf4j/MDC.html[MDC] と融合してトレースログ相関を有効にすべきかどうかを指定するブール値。\n「true」に設定した場合、エージェントは現在アクティブなスパンとトランザクションの「trace.id」と「transaction.id」を MDC に設定します。\n詳細は <> をご覧ください。\n\n注:実行時にこの設定を有効にできますが、再起動しないと無効にはできません。", "xpack.apm.agentConfig.enableLogCorrelation.label": "ログ相関を有効にする", - "xpack.apm.agentConfig.float.errorText": "0.000 から 1 までの数字でなければなりません", - "xpack.apm.agentConfig.integer.errorText": "整数でなければなりません", "xpack.apm.agentConfig.logLevel.description": "エージェントのログ記録レベルを設定します", "xpack.apm.agentConfig.logLevel.label": "ログレベル", "xpack.apm.agentConfig.newConfig.description": "これで Kibana でエージェント構成を直接的に微調整できます。\n しかも、変更は APM エージェントに自動的に伝達されるので、再デプロイする必要はありません。", @@ -4182,7 +4179,6 @@ "xpack.apm.agentConfig.stressMonitorSystemCpuStressThreshold.description": "システム CPU 監視でシステム CPU ストレスの検出に使用するしきい値。\nシステム CPU が少なくとも「stress_monitor_cpu_duration_threshold」と同じ長さ以上の期間にわたってこのしきい値を超えると、監視機能はこれをストレス状態と見なします。", "xpack.apm.agentConfig.stressMonitorSystemCpuStressThreshold.label": "ストレス監視システム CPU ストレスしきい値", "xpack.apm.agentConfig.transactionMaxSpans.description": "トランザクションごとに記録される範囲を制限します。デフォルトは 500 です。", - "xpack.apm.agentConfig.transactionMaxSpans.errorText": "0 と 32000 の間でなければなりません", "xpack.apm.agentConfig.transactionMaxSpans.label": "トランザクションの最大範囲", "xpack.apm.agentConfig.transactionSampleRate.description": "デフォルトでは、エージェントはすべてのトランザクション (例えば、サービスへのリクエストなど) をサンプリングします。オーバーヘッドやストレージ要件を減らすには、サンプルレートの値を 0.0〜1.0 に設定します。全体的な時間とサンプリングされないトランザクションの結果は記録されますが、コンテキスト情報、ラベル、スパンは記録されません。", "xpack.apm.agentConfig.transactionSampleRate.label": "トランザクションのサンプルレート", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index a4f4705364737..98292d5bc9160 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -4098,7 +4098,6 @@ "xpack.apm.agentConfig.apiRequestSize.label": "API 请求大小", "xpack.apm.agentConfig.apiRequestTime.description": "使 APM Server 的 HTTP 请求保持开放的最大时间。\n\n注意:此值必须小于 APM Server 的 `read_timeout` 设置。", "xpack.apm.agentConfig.apiRequestTime.label": "API 请求时间", - "xpack.apm.agentConfig.bytes.errorText": "请指定整数和单位", "xpack.apm.agentConfig.captureBody.description": "有关属于 HTTP 请求的事务,代理可以选择性地捕获请求正文(例如 POST 变量)。默认为“off”。", "xpack.apm.agentConfig.captureBody.label": "捕获正文", "xpack.apm.agentConfig.captureHeaders.description": "如果设置为 `true`,代理将捕获请求和响应标头,包括 cookie。\n\n注意:将其设置为 `false` 可减少网络带宽、磁盘空间和对象分配。", @@ -4132,8 +4131,6 @@ "xpack.apm.agentConfig.editConfigTitle": "编辑配置", "xpack.apm.agentConfig.enableLogCorrelation.description": "指定是否应在 SLF4J 的 https://www.slf4j.org/api/org/slf4j/MDC.html[MDC] 中集成代理以启用跟踪-日志关联的布尔值。\n如果设置为 `true`,代理会将当前活动跨度和事务的 `trace.id` 和 `transaction.id` 设置为 MDC。\n请参阅 <> 以了解更多详情。\n\n注意:尽管允许在运行时启用此设置,但不重新启动将无法禁用。", "xpack.apm.agentConfig.enableLogCorrelation.label": "启用日志关联", - "xpack.apm.agentConfig.float.errorText": "必须是介于 0.000 和 1 之间的数字", - "xpack.apm.agentConfig.integer.errorText": "必须为整数", "xpack.apm.agentConfig.logLevel.description": "设置代理的日志记录级别", "xpack.apm.agentConfig.logLevel.label": "日志级别", "xpack.apm.agentConfig.newConfig.description": "这允许您直接在 Kibana 中微调\n 代理配置。最重要是,更改自动传播给您的 APM\n 代理,从而无需重新部署。", @@ -4183,7 +4180,6 @@ "xpack.apm.agentConfig.stressMonitorSystemCpuStressThreshold.description": "系统 CPU 监测用于检测系统 CPU 压力的阈值。\n如果系统 CPU 超过此阈值的持续时间至少有 `stress_monitor_cpu_duration_threshold`,\n监测会将其视为压力状态。", "xpack.apm.agentConfig.stressMonitorSystemCpuStressThreshold.label": "压力监测系统 cpu 压力阈值", "xpack.apm.agentConfig.transactionMaxSpans.description": "限制每个事务记录的跨度数量。默认值为 500。", - "xpack.apm.agentConfig.transactionMaxSpans.errorText": "必须介于 0 和 32000 之间", "xpack.apm.agentConfig.transactionMaxSpans.label": "事务最大跨度数", "xpack.apm.agentConfig.transactionSampleRate.description": "默认情况下,代理将采样每个事务(例如对服务的请求)。要减少开销和存储需要,可以将采样率设置介于 0.0 和 1.0 之间的值。我们仍记录整体时间和未采样事务的结果,但不记录上下文信息、标签和跨度。", "xpack.apm.agentConfig.transactionSampleRate.label": "事务采样率", From 603d6a81366759e94701b35fa92639205c5e82c4 Mon Sep 17 00:00:00 2001 From: cauemarcondes Date: Thu, 30 Apr 2020 13:43:28 +0200 Subject: [PATCH 08/12] validating min and max bytes --- .../SettingsPage/SettingFormRow.tsx | 11 ++- .../agent_configuration/amount_and_unit.ts | 6 +- .../runtime_types/bytes_rt.test.ts | 67 +++++++++++++++---- .../runtime_types/bytes_rt.ts | 41 +++++++++--- .../runtime_types/duration_rt.ts | 10 +-- .../__snapshots__/index.test.ts.snap | 2 +- .../setting_definitions/index.ts | 2 +- .../setting_definitions/types.d.ts | 4 +- 8 files changed, 104 insertions(+), 39 deletions(-) diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingFormRow.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingFormRow.tsx index 75caed4ae480f..8b1590245f681 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingFormRow.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingFormRow.tsx @@ -92,15 +92,14 @@ function FormRow({ onChange( setting.key, - amountAndUnitToString({ amount: e.target.value, unit }) + amountAndUnitToString({ + amount: parseInt(e.target.value, 10), + unit + }) ) } /> diff --git a/x-pack/plugins/apm/common/agent_configuration/amount_and_unit.ts b/x-pack/plugins/apm/common/agent_configuration/amount_and_unit.ts index d6520ae150539..53d6ea49af8e0 100644 --- a/x-pack/plugins/apm/common/agent_configuration/amount_and_unit.ts +++ b/x-pack/plugins/apm/common/agent_configuration/amount_and_unit.ts @@ -4,15 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -interface AmountAndUnit { - amount: string; +export interface AmountAndUnit { + amount: number; unit: string; } export function amountAndUnitToObject(value: string): AmountAndUnit { // matches any postive and negative number and its unit. const [, amount = '', unit = ''] = value.match(/(^-?\d+)?(\w+)?/) || []; - return { amount, unit }; + return { amount: parseInt(amount, 10), unit }; } export function amountAndUnitToString({ amount, unit }: AmountAndUnit) { 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 4df6128f248ac..180637f08a84a 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 @@ -8,23 +8,66 @@ import { getBytesRt } from './bytes_rt'; import { isRight } from 'fp-ts/lib/Either'; describe('bytesRt', () => { - const bytesRt = getBytesRt({ - min: 0, - units: ['b', 'mb', 'kb'] - }); + describe('must accept any amount and unit', () => { + const bytesRt = getBytesRt({ + units: ['b', 'mb', 'kb'] + }); + describe('it should not accept', () => { + ['mb', 1, '1', '5gb', '6tb'].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(bytesRt.decode(input))).toBe(false); + }); + }); + }); - describe('it should not accept', () => { - ['mb', '-1kb', '5gb', '6tb'].map(input => { - it(`${JSON.stringify(input)}`, () => { - expect(isRight(bytesRt.decode(input))).toBe(false); + describe('it should accept', () => { + ['-1b', '0mb', '1b', '2kb', '3mb', '1000mb'].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(bytesRt.decode(input))).toBe(true); + }); }); }); }); + describe('must be at least 0b', () => { + const bytesRt = getBytesRt({ + min: '0b', + units: ['b', 'mb', 'kb'] + }); + + describe('it should not accept', () => { + ['mb', '-1kb', '5gb', '6tb'].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(bytesRt.decode(input))).toBe(false); + }); + }); + }); - describe('it should accept', () => { - ['1b', '2kb', '3mb'].map(input => { - it(`${JSON.stringify(input)}`, () => { - expect(isRight(bytesRt.decode(input))).toBe(true); + describe('it should accept', () => { + ['1b', '2kb', '3mb'].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(bytesRt.decode(input))).toBe(true); + }); + }); + }); + }); + describe('must be between 500b and 1mb', () => { + const bytesRt = getBytesRt({ + min: '500b', + max: '1kb', + units: ['b', 'mb', 'kb'] + }); + describe('it should not accept', () => { + ['mb', '-1b', '1b', '499b', '1025b', '2kb', '1mb'].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(bytesRt.decode(input))).toBe(false); + }); + }); + }); + describe('it should accept', () => { + ['500b', '1024b', '1kb'].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(bytesRt.decode(input))).toBe(true); + }); }); }); }); 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 99c8e2dfbe412..01a4afdcebb5b 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 @@ -10,17 +10,35 @@ import { i18n } from '@kbn/i18n'; import { amountAndUnitToObject } from '../amount_and_unit'; import { getRangeType } from './get_range_type'; -export const BYTE_UNITS = ['b', 'kb', 'mb']; +function toBytes(amount: number, unit: string) { + switch (unit) { + case 'kb': + return amount * 2 ** 10; + case 'mb': + return amount * 2 ** 20; + case 'b': + default: + return amount; + } +} export function getBytesRt({ - min = -Infinity, - max = Infinity, + min, + max, units }: { - min?: number; - max?: number; + 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: 'b' }; + const message = i18n.translate('xpack.apm.agentConfig.bytes.errorText', { defaultMessage: `{rangeType, select, between {Must be between {min} and {max} with unit: {units}} @@ -32,7 +50,7 @@ export function getBytesRt({ min, max, units: units.join(', '), - rangeType: getRangeType(min, max) + rangeType: getRangeType(minAmount, maxAmount) } }); @@ -42,11 +60,16 @@ export function getBytesRt({ (input, context) => { return either.chain(t.string.validate(input, context), inputAsString => { const { amount, unit } = amountAndUnitToObject(inputAsString); - const amountAsInt = parseInt(amount, 10); const isValidUnit = units.includes(unit); - const isValid = amountAsInt >= min && amountAsInt <= max && isValidUnit; - return isValid + const inputAsBytes = toBytes(amount, unit); + const minAsBytes = toBytes(minAmount, minUnit); + const maxAsBytes = toBytes(maxAmount, maxUnit); + + const isValidAmount = + inputAsBytes >= minAsBytes && inputAsBytes <= maxAsBytes; + + return isValidUnit && isValidAmount ? t.success(inputAsString) : t.failure(input, context, message); }); 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 39ba4c28cdf53..86e1f2d77e75d 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 @@ -8,11 +8,11 @@ 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 } from '../amount_and_unit'; +import { amountAndUnitToObject, AmountAndUnit } from '../amount_and_unit'; import { getRangeType } from './get_range_type'; -function getDuration({ amount, unit }: { amount: string; unit: string }) { - return moment.duration(parseInt(amount, 10), unit as unitOfTime.Base); +function getDuration({ amount, unit }: AmountAndUnit) { + return moment.duration(amount, unit as unitOfTime.Base); } export function getDurationRt({ @@ -38,8 +38,8 @@ export function getDurationRt({ min, max, rangeType: getRangeType( - minAmountAndUnit ? parseInt(minAmountAndUnit.amount, 10) : undefined, - maxAmountAndUnit ? parseInt(maxAmountAndUnit.amount, 10) : undefined + minAmountAndUnit ? minAmountAndUnit.amount : undefined, + maxAmountAndUnit ? maxAmountAndUnit.amount : undefined ) } }); diff --git a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap index 92aa0e438d97b..4f5763dcde582 100644 --- a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap +++ b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap @@ -4,7 +4,7 @@ exports[`settingDefinitions should have correct default values 1`] = ` Array [ Object { "key": "api_request_size", - "min": 0, + "min": "0b", "type": "bytes", "units": Array [ "b", 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 f7760aaafc0f2..8a8f726c61f76 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 @@ -50,7 +50,7 @@ function getSettingDefaults(setting: RawSettingDefinition): SettingDefinition { case 'bytes': { const units = setting.units ?? ['b', 'kb', 'mb']; - const min = setting.min ?? 0; + const min = setting.min ?? '0b'; const max = setting.max; return { diff --git a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/types.d.ts b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/types.d.ts index 29125193edfb6..85a454b5f256a 100644 --- a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/types.d.ts +++ b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/types.d.ts @@ -80,8 +80,8 @@ interface IntegerSetting extends BaseSetting { interface BytesSetting extends BaseSetting { type: 'bytes'; - min?: number; - max?: number; + min?: string; + max?: string; units?: string[]; } From 8ed25ce61c1e6c25f9ac77350d20a8bf3791bb75 Mon Sep 17 00:00:00 2001 From: cauemarcondes Date: Thu, 30 Apr 2020 13:45:22 +0200 Subject: [PATCH 09/12] refactoring --- .../apm/common/agent_configuration/runtime_types/bytes_rt.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 01a4afdcebb5b..01da9598d5d33 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 @@ -37,7 +37,7 @@ export function getBytesRt({ const { amount: maxAmount, unit: maxUnit } = max ? amountAndUnitToObject(max) - : { amount: Infinity, unit: 'b' }; + : { amount: Infinity, unit: 'mb' }; const message = i18n.translate('xpack.apm.agentConfig.bytes.errorText', { defaultMessage: `{rangeType, select, From 7718f2c11d46b32441ecff10441a2a46903871d9 Mon Sep 17 00:00:00 2001 From: cauemarcondes Date: Thu, 30 Apr 2020 20:03:00 +0200 Subject: [PATCH 10/12] refactoring --- .../runtime_types/bytes_rt.test.ts | 34 +++++++--- .../runtime_types/bytes_rt.ts | 63 ++++++------------- .../runtime_types/duration_rt.test.ts | 38 +++++++++-- .../runtime_types/duration_rt.ts | 63 ++++++------------- .../runtime_types/get_range_type.ts | 26 +++++++- .../runtime_types/integer_rt.test.ts | 12 ++++ .../runtime_types/integer_rt.ts | 13 +--- .../setting_definitions/index.ts | 4 +- 8 files changed, 141 insertions(+), 112 deletions(-) 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 180637f08a84a..4d786605b00c7 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 01da9598d5d33..1b18b65852ba6 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 fff1052cf6da2..ebfd9d9a72704 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 86e1f2d77e75d..601d0e6266349 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 83239a5490295..5bd0fcb80c4dd 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 3286998131179..a0395a4a140d9 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 6cae5e6d9cb15..fcfd18e1994a4 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 8a8f726c61f76..7869cd7d79e17 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 From 0af3733c2f98dc2b1d9f5c4e31c5160415077bd6 Mon Sep 17 00:00:00 2001 From: cauemarcondes Date: Sat, 2 May 2020 11:27:17 +0200 Subject: [PATCH 11/12] refactoring --- .../apm/common/agent_configuration/runtime_types/bytes_rt.ts | 4 ++-- .../common/agent_configuration/runtime_types/duration_rt.ts | 4 ++-- .../{get_range_type.ts => get_range_type_message.ts} | 0 .../common/agent_configuration/runtime_types/integer_rt.ts | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) rename x-pack/plugins/apm/common/agent_configuration/runtime_types/{get_range_type.ts => get_range_type_message.ts} (100%) 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 1b18b65852ba6..9f49527438b49 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 @@ -7,7 +7,7 @@ import * as t from 'io-ts'; import { either } from 'fp-ts/lib/Either'; import { amountAndUnitToObject } from '../amount_and_unit'; -import { getRangeTypeMessage } from './get_range_type'; +import { getRangeTypeMessage } from './get_range_type_message'; function toBytes(amount: number, unit: string) { switch (unit) { @@ -42,7 +42,7 @@ export function getBytesRt({ min, max }: { min?: string; max?: string }) { const inputAsBytes = amountAndUnitToBytes(inputAsString); const isValidAmount = - typeof inputAsBytes !== 'undefined' && + inputAsBytes !== undefined && inputAsBytes >= minAsBytes && inputAsBytes <= maxAsBytes; 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 601d0e6266349..cede5ed262558 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 @@ -8,7 +8,7 @@ import * as t from 'io-ts'; import { either } from 'fp-ts/lib/Either'; import moment, { unitOfTime } from 'moment'; import { amountAndUnitToObject, AmountAndUnit } from '../amount_and_unit'; -import { getRangeTypeMessage } from './get_range_type'; +import { getRangeTypeMessage } from './get_range_type_message'; function toMilliseconds({ amount, unit }: AmountAndUnit) { return moment.duration(amount, unit as unitOfTime.Base); @@ -36,7 +36,7 @@ export function getDurationRt({ min, max }: { min?: string; max?: string }) { const inputAsMilliseconds = amountAndUnitToMilliseconds(inputAsString); const isValidAmount = - typeof inputAsMilliseconds !== 'undefined' && + inputAsMilliseconds !== undefined && inputAsMilliseconds >= minAsMilliseconds && inputAsMilliseconds <= maxAsMilliseconds; 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_message.ts similarity index 100% rename from x-pack/plugins/apm/common/agent_configuration/runtime_types/get_range_type.ts rename to x-pack/plugins/apm/common/agent_configuration/runtime_types/get_range_type_message.ts 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 fcfd18e1994a4..adb91992f756a 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,7 +6,7 @@ import * as t from 'io-ts'; import { either } from 'fp-ts/lib/Either'; -import { getRangeTypeMessage } from './get_range_type'; +import { getRangeTypeMessage } from './get_range_type_message'; export function getIntegerRt({ min = -Infinity, From fb7e285611403d56edb9d49374a92abda976c56f Mon Sep 17 00:00:00 2001 From: cauemarcondes Date: Mon, 4 May 2020 13:13:28 +0200 Subject: [PATCH 12/12] accept number and string on amountAndUnitToString --- .../apm/common/agent_configuration/amount_and_unit.ts | 5 ++++- .../SettingsPage/SettingFormRow.tsx | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/apm/common/agent_configuration/amount_and_unit.ts b/x-pack/plugins/apm/common/agent_configuration/amount_and_unit.ts index 53d6ea49af8e0..cd64b3025a65b 100644 --- a/x-pack/plugins/apm/common/agent_configuration/amount_and_unit.ts +++ b/x-pack/plugins/apm/common/agent_configuration/amount_and_unit.ts @@ -15,6 +15,9 @@ export function amountAndUnitToObject(value: string): AmountAndUnit { return { amount: parseInt(amount, 10), unit }; } -export function amountAndUnitToString({ amount, unit }: AmountAndUnit) { +export function amountAndUnitToString({ + amount, + unit +}: Omit & { amount: string | number }) { return `${amount}${unit}`; } diff --git a/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingFormRow.tsx b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingFormRow.tsx index ff756fea3b3ce..6711fecc2376c 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingFormRow.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingFormRow.tsx @@ -97,7 +97,7 @@ function FormRow({ onChange( setting.key, amountAndUnitToString({ - amount: parseInt(e.target.value, 10), + amount: e.target.value, unit }) )