From 0f3ec4e98abd740762f07d5c6fbb518f4da5abe9 Mon Sep 17 00:00:00 2001 From: tada5hi Date: Fri, 21 Oct 2022 19:36:29 +0200 Subject: [PATCH] fix: changed structure of filters-parse-output-element --- docs/guide/filters-api-reference.md | 4 +- docs/guide/parse-api-reference.md | 2 +- src/parameter/filters/build.ts | 18 ++-- src/parameter/filters/constants.ts | 21 ++-- src/parameter/filters/parse.ts | 27 +++-- src/parameter/filters/type.ts | 8 +- src/parameter/filters/utils/operator.ts | 130 +++++++++++++++--------- src/parameter/filters/utils/value.ts | 31 +++--- test/unit/build.spec.ts | 20 ++-- test/unit/filters.spec.ts | 86 +++++++--------- test/unit/parse.spec.ts | 3 +- 11 files changed, 188 insertions(+), 162 deletions(-) diff --git a/docs/guide/filters-api-reference.md b/docs/guide/filters-api-reference.md index 0fc845cb..cd527899 100644 --- a/docs/guide/filters-api-reference.md +++ b/docs/guide/filters-api-reference.md @@ -171,9 +171,7 @@ type FiltersParseOptions< ```typescript type FiltersParseOutputElement = { - operator?: { - [K in FilterOperatorLabel]?: boolean - }, + operator: `${FilterComparisonOperator}`, value: FilterValueSimple, key: string, path?: string diff --git a/docs/guide/parse-api-reference.md b/docs/guide/parse-api-reference.md index 5c9e5dea..15dd9552 100644 --- a/docs/guide/parse-api-reference.md +++ b/docs/guide/parse-api-reference.md @@ -31,7 +31,7 @@ console.log(output); // {key: 'age', operator: FieldOperator.INCLUDE} // ], // filters: [ -// {key: 'name', value: 'pe', operator: FilterOperator.LIKE} +// {key: 'name', value: 'pe', operator: FilterComparisonOperator.LIKE} // ] //} ``` diff --git a/src/parameter/filters/build.ts b/src/parameter/filters/build.ts index b15ec825..ff8f44be 100644 --- a/src/parameter/filters/build.ts +++ b/src/parameter/filters/build.ts @@ -8,18 +8,18 @@ import { merge } from 'smob'; import { ObjectLiteral } from '../../type'; import { FiltersBuildInput } from './type'; -import { FilterOperator } from './constants'; +import { FilterInputOperatorValue } from './constants'; import { isFilterValueConfig } from './utils'; import { flattenNestedObject } from '../../utils'; const OperatorWeight = { - [FilterOperator.NEGATION]: 0, - [FilterOperator.LIKE]: 50, - [FilterOperator.LESS_THAN_EQUAL]: 150, - [FilterOperator.LESS_THAN]: 450, - [FilterOperator.MORE_THAN_EQUAL]: 1350, - [FilterOperator.MORE_THAN]: 4050, - [FilterOperator.IN]: 13105, + [FilterInputOperatorValue.NEGATION]: 0, + [FilterInputOperatorValue.LIKE]: 50, + [FilterInputOperatorValue.LESS_THAN_EQUAL]: 150, + [FilterInputOperatorValue.LESS_THAN]: 450, + [FilterInputOperatorValue.MORE_THAN_EQUAL]: 1350, + [FilterInputOperatorValue.MORE_THAN]: 4050, + [FilterInputOperatorValue.IN]: 13105, }; export function buildQueryFilters( @@ -58,7 +58,7 @@ export function buildQueryFilters( // merge operators input.operator = input.operator .sort((a, b) => OperatorWeight[a] - OperatorWeight[b]) - .join('') as FilterOperator; + .join('') as FilterInputOperatorValue; } output[key] = `${input.operator}${input.value}`; diff --git a/src/parameter/filters/constants.ts b/src/parameter/filters/constants.ts index 2dcebb6b..43269738 100644 --- a/src/parameter/filters/constants.ts +++ b/src/parameter/filters/constants.ts @@ -5,17 +5,20 @@ * view the LICENSE file that was distributed with this source code. */ -export enum FilterOperatorLabel { - NEGATION = 'negation', - LIKE = 'like', - LESS_THAN_EQUAL = 'lessThanEqual', - LESS_THAN = 'lessThan', - MORE_THAN_EQUAL = 'moreThanEqual', - MORE_THAN = 'moreThan', - IN = 'in', +export enum FilterComparisonOperator { + EQUAL = '$eq', + NOT_EQUAL = '$ne', + LIKE = '$l', + NOT_LIKE = '$nl', + LESS_THAN_EQUAL = '$lte', + LESS_THAN = '$lt', + GREATER_THAN_EQUAL = '$gte', + GREATER_THAN = '$gt', + IN = '$in', + NOT_IN = '$nin', } -export enum FilterOperator { +export enum FilterInputOperatorValue { NEGATION = '!', LIKE = '~', LESS_THAN_EQUAL = '<=', diff --git a/src/parameter/filters/parse.ts b/src/parameter/filters/parse.ts index 2d964294..842327bf 100644 --- a/src/parameter/filters/parse.ts +++ b/src/parameter/filters/parse.ts @@ -15,32 +15,31 @@ import { hasOwnProperty, isFieldNonRelational, isFieldPathAllowedByRelations, } from '../../utils'; import { isPathCoveredByParseAllowed } from '../utils'; +import { FilterComparisonOperator } from './constants'; import { FiltersParseOptions, FiltersParseOutput, FiltersParseOutputElement } from './type'; -import { determineFilterOperatorLabelsByValue, transformFilterValue } from './utils'; +import { parseFilterValue, transformFilterValue } from './utils'; // -------------------------------------------------- function transformFiltersParseOutputElement(element: FiltersParseOutputElement) : FiltersParseOutputElement { if ( hasOwnProperty(element, 'path') && - ( - typeof element.path === 'undefined' || - element.path === null - ) + (typeof element.path === 'undefined' || element.path === null) ) { delete element.path; } - if (typeof element.value === 'string') { - const { value, operators } = determineFilterOperatorLabelsByValue(element.value); - if (operators.length > 0) { - element.value = value; - element.operator = {}; + if (element.operator) { + return element; + } - for (let i = 0; i < operators.length; i++) { - element.operator[operators[i]] = true; - } - } + if (typeof element.value === 'string') { + element = { + ...element, + ...parseFilterValue(element.value), + }; + } else { + element.operator = FilterComparisonOperator.EQUAL; } element.value = transformFilterValue(element.value); diff --git a/src/parameter/filters/type.ts b/src/parameter/filters/type.ts index 82cc7b25..1ba9f542 100644 --- a/src/parameter/filters/type.ts +++ b/src/parameter/filters/type.ts @@ -13,7 +13,7 @@ import { RelationsParseOutput } from '../relations'; import { ParseAllowedKeys, } from '../type'; -import { FilterOperator, FilterOperatorLabel } from './constants'; +import { FilterComparisonOperator, FilterInputOperatorValue } from './constants'; // ----------------------------------------------------------- @@ -30,7 +30,7 @@ export type FilterValue = V exten V; export type FilterValueConfig = { - operator: `${FilterOperator}` | (`${FilterOperator}`)[]; + operator: `${FilterInputOperatorValue}` | (`${FilterInputOperatorValue}`)[]; value: FilterValueSimple }; @@ -77,9 +77,7 @@ export type FiltersParseOptions< }; export type FiltersParseOutputElement = { - operator?: { - [K in FilterOperatorLabel]?: boolean - }, + operator?: `${FilterComparisonOperator}`, value: FilterValueSimple, key: string, path?: string diff --git a/src/parameter/filters/utils/operator.ts b/src/parameter/filters/utils/operator.ts index 158e79ea..4316cce3 100644 --- a/src/parameter/filters/utils/operator.ts +++ b/src/parameter/filters/utils/operator.ts @@ -5,65 +5,97 @@ * view the LICENSE file that was distributed with this source code. */ -import { - FilterValueConfig, -} from '../type'; import { hasOwnProperty, isSimpleValue } from '../../../utils'; -import { FilterOperator, FilterOperatorLabel } from '../constants'; - -const config : { - sign: FilterOperator | `${FilterOperator}`, - label: FilterOperatorLabel | `${FilterOperatorLabel}` -}[] = []; - -const operatorKeys = Object.keys(FilterOperator) as (keyof typeof FilterOperator)[]; -for (let i = 0; i < operatorKeys.length; i++) { - config.push({ - sign: FilterOperator[operatorKeys[i]], - label: FilterOperatorLabel[operatorKeys[i]], - }); +import { FilterComparisonOperator, FilterInputOperatorValue } from '../constants'; +import { FilterValueConfig } from '../type'; + +function matchOperator(key: string, value: string, position: 'start' | 'end' | 'global') : string | undefined { + switch (position) { + case 'start': { + if (value.substring(0, key.length) === key) { + return value.substring(key.length); + } + break; + } + case 'end': { + if (value.substring(0 - key.length) === key) { + return value.substring(0, value.length - key.length - 1); + } + break; + } + } + + return undefined; } -export function determineFilterOperatorLabelsByValue(input: string) : { - operators: (`${FilterOperatorLabel}`)[], +export function parseFilterValue(input: string) : { + operator: `${FilterComparisonOperator}`, value: string | string[] } { - let value : string[] | string = input; + let negation = false; - const operators : (`${FilterOperatorLabel}`)[] = []; + let value = matchOperator(FilterInputOperatorValue.NEGATION, input, 'start'); + if (typeof value !== 'undefined') { + negation = true; + input = value; + } - for (let i = 0; i < config.length; i++) { - if (typeof value !== 'string') { - // eslint-disable-next-line no-continue - continue; - } + value = matchOperator(FilterInputOperatorValue.LIKE, input, 'start'); + if (typeof value !== 'undefined') { + return { + value, + operator: negation ? + FilterComparisonOperator.NOT_LIKE : + FilterComparisonOperator.LIKE, + }; + } - switch (config[i].sign) { - case FilterOperator.IN: - if ( - value.includes(config[i].sign) - ) { - operators.push(config[i].label); - value = value.split(config[i].sign); - } - break; - default: - if ( - value.slice(0, config[i].sign.length) === config[i].sign - ) { - operators.push(config[i].label); - value = value.slice(config[i].sign.length); - if (value.toLowerCase() === 'null') { - value = null; - } - } - break; - } + value = matchOperator(FilterInputOperatorValue.LESS_THAN_EQUAL, input, 'start'); + if (typeof value !== 'undefined') { + return { + value, + operator: FilterComparisonOperator.LESS_THAN_EQUAL, + }; + } + + value = matchOperator(FilterInputOperatorValue.LESS_THAN, input, 'start'); + if (typeof value !== 'undefined') { + return { + value, + operator: FilterComparisonOperator.LESS_THAN, + }; + } + + value = matchOperator(FilterInputOperatorValue.MORE_THAN_EQUAL, input, 'start'); + if (typeof value !== 'undefined') { + return { + value, + operator: FilterComparisonOperator.GREATER_THAN_EQUAL, + }; + } + + value = matchOperator(FilterInputOperatorValue.MORE_THAN, input, 'start'); + if (typeof value !== 'undefined') { + return { + value, + operator: FilterComparisonOperator.GREATER_THAN, + }; + } + + if (input.includes(FilterInputOperatorValue.IN)) { + return { + value: input.split(FilterInputOperatorValue.IN), + operator: negation ? + FilterComparisonOperator.NOT_IN : + FilterComparisonOperator.IN, + }; } return { - operators, - value, + value: input, + operator: negation ? + FilterComparisonOperator.NOT_EQUAL : + FilterComparisonOperator.EQUAL, }; } @@ -73,7 +105,7 @@ export function isFilterValueConfig(data: unknown) : data is FilterValueConfig n === 0 || !!n) as FilterValue; - } - - if (typeof input === 'undefined' || input === null) { - return null; - } - if (typeof input === 'string') { const lower = input.trim().toLowerCase(); @@ -40,6 +27,24 @@ export function transformFilterValue(input: FilterValue) : FilterValue { if (!Number.isNaN(num)) { return num; } + + const parts = input.split(','); + if (parts.length > 1) { + return transformFilterValue(parts); + } + } + + if (Array.isArray(input)) { + for (let i = 0; i < input.length; i++) { + input[i] = transformFilterValue(input[i]) as string | number; + } + + return (input as unknown[]) + .filter((n) => n === 0 || !!n) as FilterValue; + } + + if (typeof input === 'undefined' || input === null) { + return null; } return input; diff --git a/test/unit/build.spec.ts b/test/unit/build.spec.ts index f1c28d0c..14375954 100644 --- a/test/unit/build.spec.ts +++ b/test/unit/build.spec.ts @@ -6,7 +6,7 @@ */ import { - FilterOperator, Parameter, SortDirection, URLParameter, buildQuery, DEFAULT_ID, + FilterInputOperatorValue, Parameter, SortDirection, URLParameter, buildQuery, DEFAULT_ID, } from '../../src'; import { buildURLQueryString } from '../../src/utils'; @@ -98,7 +98,7 @@ describe('src/build.ts', () => { record = buildQuery({ filter: { id: { - operator: FilterOperator.LIKE, + operator: FilterInputOperatorValue.LIKE, value: 1, }, }, @@ -109,7 +109,7 @@ describe('src/build.ts', () => { record = buildQuery({ filter: { id: { - operator: FilterOperator.LESS_THAN, + operator: FilterInputOperatorValue.LESS_THAN, value: 1, }, }, @@ -120,7 +120,7 @@ describe('src/build.ts', () => { record = buildQuery({ filter: { id: { - operator: FilterOperator.LESS_THAN_EQUAL, + operator: FilterInputOperatorValue.LESS_THAN_EQUAL, value: 1, }, }, @@ -131,7 +131,7 @@ describe('src/build.ts', () => { record = buildQuery({ filter: { id: { - operator: FilterOperator.MORE_THAN, + operator: FilterInputOperatorValue.MORE_THAN, value: 1, }, }, @@ -142,7 +142,7 @@ describe('src/build.ts', () => { record = buildQuery({ filter: { id: { - operator: FilterOperator.MORE_THAN_EQUAL, + operator: FilterInputOperatorValue.MORE_THAN_EQUAL, value: 1, }, }, @@ -162,8 +162,8 @@ describe('src/build.ts', () => { filter: { id: { operator: [ - FilterOperator.NEGATION, - FilterOperator.LIKE, + FilterInputOperatorValue.NEGATION, + FilterInputOperatorValue.LIKE, ], value: [1, 2, 3], }, @@ -176,8 +176,8 @@ describe('src/build.ts', () => { filter: { id: { operator: [ - FilterOperator.LIKE, - FilterOperator.NEGATION, + FilterInputOperatorValue.LIKE, + FilterInputOperatorValue.NEGATION, ], value: [1, 2, 3], }, diff --git a/test/unit/filters.spec.ts b/test/unit/filters.spec.ts index 1d8a250f..8bd9083e 100644 --- a/test/unit/filters.spec.ts +++ b/test/unit/filters.spec.ts @@ -7,11 +7,10 @@ import { isFilterValueConfig, - FilterOperatorLabel, FiltersParseOptions, FiltersParseOutput, parseQueryFilters, - parseQueryRelations, + parseQueryRelations, FilterComparisonOperator, } from '../../src'; describe('src/filter/index.ts', () => { @@ -21,6 +20,7 @@ describe('src/filter/index.ts', () => { expect(allowedFilter).toEqual([{ key: 'id', value: 1, + operator: FilterComparisonOperator.EQUAL, }] as FiltersParseOutput); // filter none @@ -32,6 +32,7 @@ describe('src/filter/index.ts', () => { expect(allowedFilter).toEqual([{ key: 'id', value: 1, + operator: FilterComparisonOperator.EQUAL, }] as FiltersParseOutput); // filter with query alias @@ -39,6 +40,7 @@ describe('src/filter/index.ts', () => { expect(allowedFilter).toEqual([{ key: 'id', value: 1, + operator: FilterComparisonOperator.EQUAL, }] as FiltersParseOutput); // filter allowed @@ -46,6 +48,7 @@ describe('src/filter/index.ts', () => { expect(allowedFilter).toEqual([{ key: 'name', value: 'tada5hi', + operator: FilterComparisonOperator.EQUAL, }] as FiltersParseOutput); // filter data with el empty value @@ -57,12 +60,14 @@ describe('src/filter/index.ts', () => { expect(allowedFilter).toEqual([{ key: 'name', value: null, + operator: FilterComparisonOperator.EQUAL, }] as FiltersParseOutput); allowedFilter = parseQueryFilters({ name: 'null' }, { allowed: ['name'] }); expect(allowedFilter).toEqual([{ key: 'name', value: null, + operator: FilterComparisonOperator.EQUAL, }] as FiltersParseOutput); // filter wrong allowed @@ -86,7 +91,8 @@ describe('src/filter/index.ts', () => { { key: 'id', path: 'user', - value: 1 + value: 1, + operator: FilterComparisonOperator.EQUAL, } ] as FiltersParseOutput) }) @@ -103,7 +109,8 @@ describe('src/filter/index.ts', () => { expect(data).toEqual([ { key: 'id', - value: 1 + value: 1, + operator: FilterComparisonOperator.EQUAL, } ] as FiltersParseOutput); @@ -112,20 +119,16 @@ describe('src/filter/index.ts', () => { { key: 'age', value: 18, - operator: { - lessThan: true - } + operator: FilterComparisonOperator.LESS_THAN } ] as FiltersParseOutput); - data = parseQueryFilters({name: 'Peter'}, options); + data = parseQueryFilters({age: 20}, options); expect(data).toEqual([ { key: 'age', - value: 18, - operator: { - lessThan: true - } + value: 20, + operator: FilterComparisonOperator.EQUAL, } ] as FiltersParseOutput) }); @@ -145,13 +148,12 @@ describe('src/filter/index.ts', () => { { key: 'id', value: 18, + operator: FilterComparisonOperator.EQUAL, }, { key: 'age', value: 18, - operator: { - lessThan: true - } + operator: FilterComparisonOperator.LESS_THAN, } ] as FiltersParseOutput); @@ -161,14 +163,13 @@ describe('src/filter/index.ts', () => { key: 'id', path: 'user', value: 5, + operator: FilterComparisonOperator.EQUAL, }, { key: 'age', path: 'user', value: 18, - operator: { - lessThan: true - } + operator: FilterComparisonOperator.LESS_THAN, } ] as FiltersParseOutput); @@ -178,6 +179,7 @@ describe('src/filter/index.ts', () => { key: 'id', path: 'user', value: 5, + operator: FilterComparisonOperator.EQUAL, } ] as FiltersParseOutput); }); @@ -200,6 +202,7 @@ describe('src/filter/index.ts', () => { { key: 'id', value: 1, + operator: FilterComparisonOperator.EQUAL, }, ] as FiltersParseOutput); @@ -218,7 +221,7 @@ describe('src/filter/index.ts', () => { } ); expect(data).toEqual([ - { key: 'id', value: [2, 3], operator: { in: true }}, + { key: 'id', value: [2, 3], operator: FilterComparisonOperator.IN, }, ] as FiltersParseOutput); }) @@ -229,6 +232,7 @@ describe('src/filter/index.ts', () => { { key: 'id', value: 1, + operator: FilterComparisonOperator.EQUAL, }, ] as FiltersParseOutput); @@ -237,9 +241,7 @@ describe('src/filter/index.ts', () => { expect(data).toEqual([ { key: 'id', - operator: { - [FilterOperatorLabel.NEGATION]: true, - }, + operator: FilterComparisonOperator.NOT_EQUAL, value: 1, }, ] as FiltersParseOutput); @@ -249,9 +251,7 @@ describe('src/filter/index.ts', () => { expect(data).toEqual([ { key: 'id', - operator: { - [FilterOperatorLabel.IN]: true, - }, + operator: FilterComparisonOperator.IN, value: [0, 1, 2, 3], }, ] as FiltersParseOutput); @@ -261,10 +261,7 @@ describe('src/filter/index.ts', () => { expect(data).toEqual([ { key: 'id', - operator: { - [FilterOperatorLabel.IN]: true, - [FilterOperatorLabel.NEGATION]: true, - }, + operator: FilterComparisonOperator.NOT_IN, value: [1, 2, 3], }, ] as FiltersParseOutput); @@ -274,9 +271,7 @@ describe('src/filter/index.ts', () => { expect(data).toEqual([ { key: 'name', - operator: { - [FilterOperatorLabel.LIKE]: true, - }, + operator: FilterComparisonOperator.LIKE, value: 'name', }, ] as FiltersParseOutput); @@ -286,9 +281,7 @@ describe('src/filter/index.ts', () => { expect(data).toEqual([ { key: 'id', - operator: { - [FilterOperatorLabel.LESS_THAN]: true, - }, + operator: FilterComparisonOperator.LESS_THAN, value: 10, }, ] as FiltersParseOutput); @@ -298,9 +291,7 @@ describe('src/filter/index.ts', () => { expect(data).toEqual([ { key: 'id', - operator: { - [FilterOperatorLabel.LESS_THAN_EQUAL]: true, - }, + operator: FilterComparisonOperator.LESS_THAN_EQUAL, value: 10, }, ] as FiltersParseOutput); @@ -310,9 +301,7 @@ describe('src/filter/index.ts', () => { expect(data).toEqual([ { key: 'id', - operator: { - [FilterOperatorLabel.MORE_THAN]: true, - }, + operator: FilterComparisonOperator.GREATER_THAN, value: 10, }, ] as FiltersParseOutput); @@ -322,9 +311,7 @@ describe('src/filter/index.ts', () => { expect(data).toEqual([ { key: 'id', - operator: { - [FilterOperatorLabel.MORE_THAN_EQUAL]: true, - }, + operator: FilterComparisonOperator.GREATER_THAN_EQUAL, value: 10, }, ] as FiltersParseOutput); @@ -334,10 +321,7 @@ describe('src/filter/index.ts', () => { expect(data).toEqual([ { key: 'name', - operator: { - [FilterOperatorLabel.LIKE]: true, - [FilterOperatorLabel.NEGATION]: true, - }, + operator: FilterComparisonOperator.NOT_LIKE, value: 'name', }, ] as FiltersParseOutput); @@ -359,11 +343,13 @@ describe('src/filter/index.ts', () => { { key: 'id', value: 1, + operator: FilterComparisonOperator.EQUAL, }, { path: 'profile', key: 'id', value: 2, + operator: FilterComparisonOperator.EQUAL, }, ] as FiltersParseOutput); @@ -373,11 +359,13 @@ describe('src/filter/index.ts', () => { { key: 'id', value: 1, + operator: FilterComparisonOperator.EQUAL, }, { path: 'profile', key: 'id', value: 2, + operator: FilterComparisonOperator.EQUAL, }, ] as FiltersParseOutput); @@ -387,11 +375,13 @@ describe('src/filter/index.ts', () => { { key: 'id', value: 1, + operator: FilterComparisonOperator.EQUAL, }, { path: 'user_roles.role', key: 'id', value: 2, + operator: FilterComparisonOperator.EQUAL, }, ] as FiltersParseOutput); }); diff --git a/test/unit/parse.spec.ts b/test/unit/parse.spec.ts index d916e1a7..8157fd8b 100644 --- a/test/unit/parse.spec.ts +++ b/test/unit/parse.spec.ts @@ -6,7 +6,7 @@ */ import { - FieldsParseOutput, + FieldsParseOutput, FilterComparisonOperator, FiltersParseOutput, PaginationParseOutput, Parameter, @@ -68,6 +68,7 @@ describe('src/parse.ts', () => { expect(value).toEqual([{ key: 'name', value: 'tada5hi', + operator: FilterComparisonOperator.EQUAL }] as FiltersParseOutput); });