diff --git a/src/parameter/fields/parse.ts b/src/parameter/fields/parse.ts index a1cc208d..7c6f3a6a 100644 --- a/src/parameter/fields/parse.ts +++ b/src/parameter/fields/parse.ts @@ -55,15 +55,18 @@ export function parseQueryFields( allowedDomainFields, ); - let domainKeys : string[] = Object.keys(domainFields); + let keys : string[] = Object.keys(domainFields); // If it is an empty array nothing is allowed - if (domainKeys.length === 0) { + if ( + ( + typeof options.default !== 'undefined' || + typeof options.allowed !== 'undefined' + ) && keys.length === 0 + ) { return []; } - domainKeys = Object.keys(domainFields); - const prototype: string = Object.prototype.toString.call(data); if ( prototype !== '[object Object]' && @@ -84,14 +87,18 @@ export function parseQueryFields( options.mapping ??= {}; const reverseMapping = buildReverseRecord(options.mapping); + if (keys.length === 0) { + keys = Object.keys(data); + } + const output : FieldsParseOutput = []; - for (let i = 0; i < domainKeys.length; i++) { - const domainKey = domainKeys[i]; + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; if ( - !isFieldPathAllowedByRelations({ path: domainKey }, options.relations) && - domainKey !== DEFAULT_ID + !isFieldPathAllowedByRelations({ path: key }, options.relations) && + key !== DEFAULT_ID ) { continue; } @@ -99,14 +106,14 @@ export function parseQueryFields( let fields : string[] = []; if ( - hasOwnProperty(data, domainKey) + hasOwnProperty(data, key) ) { - fields = parseFieldsInput(data[domainKey]); + fields = parseFieldsInput(data[key]); } else if ( - hasOwnProperty(reverseMapping, domainKey) + hasOwnProperty(reverseMapping, key) ) { - if (hasOwnProperty(data, reverseMapping[domainKey])) { - fields = parseFieldsInput(data[reverseMapping[domainKey]]); + if (hasOwnProperty(data, reverseMapping[key])) { + fields = parseFieldsInput(data[reverseMapping[key]]); } } @@ -119,16 +126,18 @@ export function parseQueryFields( if (fields.length > 0) { for (let j = 0; j < fields.length; j++) { fields[j] = applyMapping( - buildFieldWithPath({ name: fields[j], path: domainKey }), + buildFieldWithPath({ name: fields[j], path: key }), options.mapping, true, ); } - fields = fields - .filter((field) => domainFields[domainKey].indexOf( - removeFieldInputOperator(field), - ) !== -1); + if (hasOwnProperty(domainFields, key)) { + fields = fields + .filter((field) => domainFields[key].indexOf( + removeFieldInputOperator(field), + ) !== -1); + } transformed = transformFieldsInput( fields, @@ -137,17 +146,17 @@ export function parseQueryFields( if ( transformed.default.length === 0 && - hasOwnProperty(defaultDomainFields, domainKey) + hasOwnProperty(defaultDomainFields, key) ) { - transformed.default = defaultDomainFields[domainKey]; + transformed.default = defaultDomainFields[key]; } if ( transformed.included.length === 0 && transformed.default.length === 0 && - hasOwnProperty(allowedDomainFields, domainKey) + hasOwnProperty(allowedDomainFields, key) ) { - transformed.default = allowedDomainFields[domainKey]; + transformed.default = allowedDomainFields[key]; } transformed.default = Array.from(new Set([ @@ -165,8 +174,8 @@ export function parseQueryFields( if (transformed.default.length > 0) { for (let j = 0; j < transformed.default.length; j++) { let path : string | undefined; - if (domainKey !== DEFAULT_ID) { - path = domainKey; + if (key !== DEFAULT_ID) { + path = key; } else if (options.defaultPath) { path = options.defaultPath; } diff --git a/src/parameter/filters/parse.ts b/src/parameter/filters/parse.ts index cfa1bd93..bb64fb89 100644 --- a/src/parameter/filters/parse.ts +++ b/src/parameter/filters/parse.ts @@ -14,7 +14,8 @@ import { getFieldDetails, hasOwnProperty, isFieldNonRelational, isFieldPathAllowedByRelations, } from '../../utils'; -import { isPathCoveredByParseAllowedOption } from '../utils'; +import { ParseAllowedOption } from '../type'; +import { flattenParseAllowedOption, isPathCoveredByParseAllowedOption } from '../utils'; import { FilterComparisonOperator } from './constants'; import { FiltersParseOptions, FiltersParseOutput, FiltersParseOutputElement } from './type'; import { parseFilterValue, transformFilterValue } from './utils'; @@ -47,14 +48,6 @@ function transformFiltersParseOutputElement(element: FiltersParseOutputElement) return element; } -function transformFiltersParseOutput(output: FiltersParseOutput) { - for (let i = 0; i < output.length; i++) { - output[i] = transformFiltersParseOutputElement(output[i]); - } - - return output; -} - function buildDefaultFiltersParseOutput( options: FiltersParseOptions, input?: Record, @@ -118,25 +111,29 @@ export function parseQueryFilters( options.relations = options.relations || []; // If it is an empty array nothing is allowed - if ( - typeof options.allowed === 'undefined' || - options.allowed.length === 0 - ) { - return []; + if (typeof options.allowed !== 'undefined') { + options.allowed = flattenParseAllowedOption(options.allowed) as ParseAllowedOption; + if (options.allowed.length === 0) { + return buildDefaultFiltersParseOutput(options); + } } /* istanbul ignore next */ if (typeof data !== 'object' || data === null) { - return transformFiltersParseOutput( - buildDefaultFiltersParseOutput(options), - ); + return buildDefaultFiltersParseOutput(options); } const { length } = Object.keys(data); if (length === 0) { - return transformFiltersParseOutput( - buildDefaultFiltersParseOutput(options), - ); + return buildDefaultFiltersParseOutput(options); + } + + if ( + (typeof options.allowed === 'undefined' || options.allowed.length === 0) && + options.default + ) { + const flatten = flattenNestedObject(options.default); + options.allowed = Object.keys(flatten) as ParseAllowedOption; } const items : Record = {}; diff --git a/src/parameter/relations/parse.ts b/src/parameter/relations/parse.ts index 9528d5f8..791df13e 100644 --- a/src/parameter/relations/parse.ts +++ b/src/parameter/relations/parse.ts @@ -23,7 +23,7 @@ export function parseQueryRelations( // If it is an empty array nothing is allowed if ( - typeof options.allowed === 'undefined' || + Array.isArray(options.allowed) && options.allowed.length === 0 ) { return []; diff --git a/src/parameter/sort/parse.ts b/src/parameter/sort/parse.ts index b16384f8..0243fb0d 100644 --- a/src/parameter/sort/parse.ts +++ b/src/parameter/sort/parse.ts @@ -13,6 +13,7 @@ import { hasOwnProperty, isFieldNonRelational, isFieldPathAllowedByRelations, } from '../../utils'; +import { ParseAllowedOption } from '../type'; import { flattenParseAllowedOption, isPathCoveredByParseAllowedOption } from '../utils'; import { @@ -76,11 +77,11 @@ export function parseQuerySort( options = options ?? {}; // If it is an empty array nothing is allowed - if ( - Array.isArray(options.allowed) && - options.allowed.length === 0 - ) { - return []; + if (typeof options.allowed !== 'undefined') { + const allowed = flattenParseAllowedOption(options.allowed) as ParseAllowedOption; + if (allowed.length === 0) { + return buildDefaultSortParseOutput(options); + } } options.mapping = options.mapping || {}; @@ -96,6 +97,14 @@ export function parseQuerySort( return buildDefaultSortParseOutput(options); } + if ( + typeof options.allowed === 'undefined' && + options.default + ) { + const flatten = flattenNestedObject(options.default); + options.allowed = Object.keys(flatten) as ParseAllowedOption; + } + let parts : string[] = []; if (typeof data === 'string') { diff --git a/src/parameter/type.ts b/src/parameter/type.ts index 82a46dc7..f82c4610 100644 --- a/src/parameter/type.ts +++ b/src/parameter/type.ts @@ -19,7 +19,8 @@ type ParseAllowedObjectOption = { export type ParseAllowedOption = T extends ObjectLiteral ? ( - ParseAllowedObjectOption | + ParseAllowedObjectOption + | ( SimpleKeys[] | ParseAllowedObjectOption diff --git a/test/unit/fields.spec.ts b/test/unit/fields.spec.ts index 063a342a..8db82125 100644 --- a/test/unit/fields.spec.ts +++ b/test/unit/fields.spec.ts @@ -132,6 +132,9 @@ describe('src/fields/index.ts', () => { let data = parseQueryFields(undefined, options); expect(data).toEqual([{key: 'id'}, {key: 'name'}]); + data = parseQueryFields('name', { default: ['id']}); + expect(data).toEqual([{ key: 'id' }]); + // fields undefined with default data = parseQueryFields(undefined, {...options, default: ['id']}); expect(data).toEqual([{ key: 'id' }]); @@ -140,6 +143,18 @@ describe('src/fields/index.ts', () => { data = parseQueryFields(['id'], options); expect(data).toEqual([{ key: 'id' }] as FieldsParseOutput); + // no options + data = parseQueryFields(['id']); + expect(data).toEqual([{ key: 'id' }] as FieldsParseOutput); + + // empty allowed -> allows nothing + data = parseQueryFields(['id'], {allowed: []}); + expect(data).toEqual([] as FieldsParseOutput); + + // empty default -> allows nothing + data = parseQueryFields(['id'], {default: []}); + expect(data).toEqual([] as FieldsParseOutput); + // fields as string data = parseQueryFields('id', options); expect(data).toEqual([{ key: 'id' }] as FieldsParseOutput); @@ -166,9 +181,9 @@ describe('src/fields/index.ts', () => { data = parseQueryFields('id', { ...options, allowed: [] }); expect(data).toEqual([] as FieldsParseOutput); - // undefined allowed -> allows nothing + // undefined allowed -> allows everything data = parseQueryFields('id', { ...options, allowed: undefined }); - expect(data).toEqual([] as FieldsParseOutput); + expect(data).toEqual([{ key: 'id' }] as FieldsParseOutput); // field not allowed data = parseQueryFields('avatar', options); diff --git a/test/unit/filters.spec.ts b/test/unit/filters.spec.ts index 1a16fa4f..81ca79f6 100644 --- a/test/unit/filters.spec.ts +++ b/test/unit/filters.spec.ts @@ -26,6 +26,28 @@ describe('src/filter/index.ts', () => { allowedFilter = parseQueryFilters({ id: 1 }, { allowed: [] }); expect(allowedFilter).toEqual([] as FiltersParseOutput); + // filter + allowedFilter = parseQueryFilters({ id: 1 }, { allowed: undefined }); + expect(allowedFilter).toEqual([{ + key: 'id', + value: 1, + operator: FilterComparisonOperator.EQUAL, + }] as FiltersParseOutput); + + allowedFilter = parseQueryFilters({ id: 1 }, { default: { name: 'admin' } }); + expect(allowedFilter).toEqual([{ + key: 'name', + value: 'admin', + operator: FilterComparisonOperator.EQUAL, + }] as FiltersParseOutput); + + allowedFilter = parseQueryFilters({ name: 'tada5hi' }, { default: { name: 'admin' } }); + expect(allowedFilter).toEqual([{ + key: 'name', + value: 'tada5hi', + operator: FilterComparisonOperator.EQUAL, + }] as FiltersParseOutput); + // filter with alias allowedFilter = parseQueryFilters({ aliasId: 1 }, { mapping: { aliasId: 'id' }, allowed: ['id'] }); expect(allowedFilter).toEqual([{ diff --git a/test/unit/parse.spec.ts b/test/unit/parse.spec.ts index 8157fd8b..ee10406d 100644 --- a/test/unit/parse.spec.ts +++ b/test/unit/parse.spec.ts @@ -33,6 +33,20 @@ describe('src/parse.ts', () => { value = parseQuery({ [Parameter.FIELDS]: ['id', 'name'], }); + expect(value).toEqual({ + fields: [ + {key: 'id'}, + {key: 'name'} + ] + } as ParseOutput); + + value = parseQuery({ + [Parameter.FIELDS]: ['id', 'name'], + }, { + fields: { + allowed: [] + } + }); expect(value).toEqual({ fields: [] } as ParseOutput); diff --git a/test/unit/relations.spec.ts b/test/unit/relations.spec.ts index 3fe51fa7..adc99740 100644 --- a/test/unit/relations.spec.ts +++ b/test/unit/relations.spec.ts @@ -64,9 +64,9 @@ describe('src/relations/index.ts', () => { allowed = parseQueryRelations(['profile'], { allowed: [] }); expect(allowed).toEqual([] as RelationsParseOutput); - // no allowed + // non array, permit everything allowed = parseQueryRelations(['profile'], { allowed: undefined }); - expect(allowed).toEqual([] as RelationsParseOutput); + expect(allowed).toEqual([{key: 'profile', value: 'profile'}] as RelationsParseOutput); // nested data with alias allowed = parseQueryRelations(['profile.photos', 'profile.photos.abc', 'profile.abc'], { allowed: ['profile.photos'] }); diff --git a/test/unit/sort.spec.ts b/test/unit/sort.spec.ts index a808c5de..dc6b922b 100644 --- a/test/unit/sort.spec.ts +++ b/test/unit/sort.spec.ts @@ -32,6 +32,14 @@ describe('src/sort/index.ts', () => { transformed = parseQuerySort('-id', { allowed: undefined }); expect(transformed).toEqual([{ key: 'id', value: SortDirection.DESC }] as SortParseOutput); + // only default + transformed = parseQuerySort('name', { default: { name: 'DESC' } }); + expect(transformed).toEqual([{ key: 'name', value: SortDirection.ASC }] as SortParseOutput); + + // only default with no match + transformed = parseQuerySort('-id', { default: { name: 'DESC' } }); + expect(transformed).toEqual([{ key: 'name', value: SortDirection.DESC }] as SortParseOutput); + // wrong allowed transformed = parseQuerySort('-id', { allowed: ['a'] }); expect(transformed).toEqual([] as SortParseOutput);