From cb4cafb9c9a92c1ccd2cc40d4a0f5bcd3ea06c3a Mon Sep 17 00:00:00 2001 From: tada5hi Date: Sat, 15 Oct 2022 14:29:11 +0200 Subject: [PATCH] fix: transform filter output value + enhanced typing for filter(s) --- src/parameter/filters/parse.ts | 9 +--- src/parameter/filters/type.ts | 32 ++++++++----- src/parameter/filters/utils/index.ts | 3 +- .../filters/utils/{opertor.ts => operator.ts} | 0 src/parameter/filters/utils/value.ts | 46 +++++++++++++++++++ test/unit/filters.spec.ts | 26 +++++------ 6 files changed, 83 insertions(+), 33 deletions(-) rename src/parameter/filters/utils/{opertor.ts => operator.ts} (100%) create mode 100644 src/parameter/filters/utils/value.ts diff --git a/src/parameter/filters/parse.ts b/src/parameter/filters/parse.ts index 1aca9e67..9e3f41d4 100644 --- a/src/parameter/filters/parse.ts +++ b/src/parameter/filters/parse.ts @@ -16,7 +16,7 @@ import { isAllowedByRelations, } from '../../utils'; import { FiltersParseOptions, FiltersParseOutput, FiltersParseOutputElement } from './type'; -import { determineFilterOperatorLabelsByValue } from './utils'; +import { determineFilterOperatorLabelsByValue, transformFilterValue } from './utils'; // -------------------------------------------------- @@ -57,7 +57,7 @@ function transformFiltersParseOutputElement(element: FiltersParseOutputElement) } } - // todo: cast unknown/unknown[] to number ( Number(value) <- isNan? ) or boolean ('false', 'FALSE', 'true', ... ) + element.value = transformFilterValue(element.value); return element; } @@ -172,15 +172,10 @@ export function parseQueryFilters( if (typeof value === 'string') { value = value.trim(); const stripped : string = (value as string).replace('/,/g', ''); - if (stripped.length === 0) { // eslint-disable-next-line no-continue continue; } - - if ((value as string).toLowerCase() === 'null') { - value = null; - } } keys[i] = getNameByAliasMapping(keys[i], options.aliasMapping); diff --git a/src/parameter/filters/type.ts b/src/parameter/filters/type.ts index 03b4da67..eb63a859 100644 --- a/src/parameter/filters/type.ts +++ b/src/parameter/filters/type.ts @@ -12,22 +12,30 @@ import { import { FilterOperator, FilterOperatorLabel } from './constants'; // ----------------------------------------------------------- -// Build -// ----------------------------------------------------------- -export type FilterOperatorConfig = { +type FilterValueInputPrimitive = boolean | number | string; +type FilterValueInput = FilterValueInputPrimitive | null | undefined; + +export type FilterValueSimple = V extends FilterValueInputPrimitive ? (V | V[]) : V; +export type FilterValueWithOperator = V extends string | number ? + `!${V}` | `!~${V}` | `~${V}` | `<${V}` | `<=${V}` | `>${V}` | `>=${V}` : + never; + +export type FilterValue = V extends FilterValueInputPrimitive ? + (FilterValueSimple | FilterValueWithOperator | Array>) : + V; + +export type FilterOperatorConfig = { operator: `${FilterOperator}` | (`${FilterOperator}`)[]; - value: V | V[] + value: FilterValueSimple }; -type FilterValue = V extends string | number | boolean ? (V | V[]) : never; -type FilterValueOperator = `!${V}` | `!~${V}` | `~${V}` | `<${V}` | `<=${V}` | `>${V}` | `>=${V}`; -type FilterValueWithOperator = V extends string | number | boolean ? - (FilterValue | FilterValueOperator | Array>) : - never; +// ----------------------------------------------------------- +// Build +// ----------------------------------------------------------- export type FiltersBuildInputValue = T extends OnlyScalar ? - T | FilterValueWithOperator | FilterOperatorConfig : + T | FilterValue | FilterOperatorConfig : T extends OnlyObject ? FiltersBuildInput> : never; export type FiltersBuildInput = { @@ -39,11 +47,11 @@ export type FiltersBuildInput = { // ----------------------------------------------------------- export type FiltersParseOptions = ParseOptionsBase & { - default?: Record>, + default?: Record>, defaultByElement?: boolean }; -export type FiltersParseOutputElement = ParseOutputElementBase> & { +export type FiltersParseOutputElement = ParseOutputElementBase & { operator?: { [K in FilterOperatorLabel]?: boolean } diff --git a/src/parameter/filters/utils/index.ts b/src/parameter/filters/utils/index.ts index f851c937..0d749f23 100644 --- a/src/parameter/filters/utils/index.ts +++ b/src/parameter/filters/utils/index.ts @@ -5,4 +5,5 @@ * view the LICENSE file that was distributed with this source code. */ -export * from './opertor'; +export * from './value'; +export * from './operator'; diff --git a/src/parameter/filters/utils/opertor.ts b/src/parameter/filters/utils/operator.ts similarity index 100% rename from src/parameter/filters/utils/opertor.ts rename to src/parameter/filters/utils/operator.ts diff --git a/src/parameter/filters/utils/value.ts b/src/parameter/filters/utils/value.ts new file mode 100644 index 00000000..acf87683 --- /dev/null +++ b/src/parameter/filters/utils/value.ts @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022. + * Author Peter Placzek (tada5hi) + * For the full copyright and license information, + * view the LICENSE file that was distributed with this source code. + */ + +import { FilterValueSimple } from '../type'; + +export function transformFilterValue(input: FilterValueSimple) : FilterValueSimple { + if (Array.isArray(input)) { + for (let i = 0; i < input.length; i++) { + input[i] = transformFilterValue(input[i]) as string | number | boolean; + } + + return (input as unknown[]) + .filter((n) => n === 0 || !!n) as FilterValueSimple; + } + + if (typeof input === 'undefined' || input === null) { + return null; + } + + if (typeof input === 'string') { + const lower = input.trim().toLowerCase(); + + if (lower === 'true') { + return true; + } + + if (lower === 'false') { + return false; + } + + if (lower === 'null') { + return null; + } + + const num = Number(input); + if (!Number.isNaN(num)) { + return num; + } + } + + return input; +} diff --git a/test/unit/filters.spec.ts b/test/unit/filters.spec.ts index f3b054a6..ce42258d 100644 --- a/test/unit/filters.spec.ts +++ b/test/unit/filters.spec.ts @@ -113,7 +113,7 @@ describe('src/filter/index.ts', () => { expect(data).toEqual([ { key: 'age', - value: '18', + value: 18, alias: 'user', operator: { lessThan: true @@ -125,7 +125,7 @@ describe('src/filter/index.ts', () => { expect(data).toEqual([ { key: 'age', - value: '18', + value: 18, alias: 'user', operator: { lessThan: true @@ -152,7 +152,7 @@ describe('src/filter/index.ts', () => { }, { key: 'age', - value: '18', + value: 18, operator: { lessThan: true } @@ -167,7 +167,7 @@ describe('src/filter/index.ts', () => { }, { key: 'age', - value: '18', + value: 18, operator: { lessThan: true } @@ -189,7 +189,7 @@ describe('src/filter/index.ts', () => { expect(data).toEqual([ { key: 'id', - value: '1', + value: 1, }, ] as FiltersParseOutput); @@ -201,19 +201,19 @@ describe('src/filter/index.ts', () => { operator: { [FilterOperatorLabel.NEGATION]: true, }, - value: '1', + value: 1, }, ] as FiltersParseOutput); // in operator - data = parseQueryFilters({ id: '1,2,3' }, { allowed: ['id'] }); + data = parseQueryFilters({ id: 'null,0,1,2,3' }, { allowed: ['id'] }); expect(data).toEqual([ { key: 'id', operator: { [FilterOperatorLabel.IN]: true, }, - value: ['1', '2', '3'], + value: [0, 1, 2, 3], }, ] as FiltersParseOutput); @@ -226,7 +226,7 @@ describe('src/filter/index.ts', () => { [FilterOperatorLabel.IN]: true, [FilterOperatorLabel.NEGATION]: true, }, - value: ['1', '2', '3'], + value: [1, 2, 3], }, ] as FiltersParseOutput); @@ -250,7 +250,7 @@ describe('src/filter/index.ts', () => { operator: { [FilterOperatorLabel.LESS_THAN]: true, }, - value: '10', + value: 10, }, ] as FiltersParseOutput); @@ -262,7 +262,7 @@ describe('src/filter/index.ts', () => { operator: { [FilterOperatorLabel.LESS_THAN_EQUAL]: true, }, - value: '10', + value: 10, }, ] as FiltersParseOutput); @@ -274,7 +274,7 @@ describe('src/filter/index.ts', () => { operator: { [FilterOperatorLabel.MORE_THAN]: true, }, - value: '10', + value: 10, }, ] as FiltersParseOutput); @@ -286,7 +286,7 @@ describe('src/filter/index.ts', () => { operator: { [FilterOperatorLabel.MORE_THAN_EQUAL]: true, }, - value: '10', + value: 10, }, ] as FiltersParseOutput);