Skip to content

Commit

Permalink
feat: parse everything, if allowed- & default- option are not defined
Browse files Browse the repository at this point in the history
  • Loading branch information
tada5hi committed Oct 28, 2022
1 parent d31e51f commit 9bda36a
Show file tree
Hide file tree
Showing 10 changed files with 130 additions and 55 deletions.
57 changes: 33 additions & 24 deletions src/parameter/fields/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,18 @@ export function parseQueryFields<T extends ObjectLiteral = ObjectLiteral>(
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]' &&
Expand All @@ -84,29 +87,33 @@ export function parseQueryFields<T extends ObjectLiteral = ObjectLiteral>(
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;
}

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]]);
}
}

Expand All @@ -119,16 +126,18 @@ export function parseQueryFields<T extends ObjectLiteral = ObjectLiteral>(
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,
Expand All @@ -137,17 +146,17 @@ export function parseQueryFields<T extends ObjectLiteral = ObjectLiteral>(

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([
Expand All @@ -165,8 +174,8 @@ export function parseQueryFields<T extends ObjectLiteral = ObjectLiteral>(
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;
}
Expand Down
37 changes: 17 additions & 20 deletions src/parameter/filters/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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<T extends ObjectLiteral = ObjectLiteral>(
options: FiltersParseOptions<T>,
input?: Record<string, FiltersParseOutputElement>,
Expand Down Expand Up @@ -118,25 +111,29 @@ export function parseQueryFilters<T extends ObjectLiteral = ObjectLiteral>(
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<T>;
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<T>;
}

const items : Record<string, FiltersParseOutputElement> = {};
Expand Down
2 changes: 1 addition & 1 deletion src/parameter/relations/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function parseQueryRelations<T extends ObjectLiteral = ObjectLiteral>(

// If it is an empty array nothing is allowed
if (
typeof options.allowed === 'undefined' ||
Array.isArray(options.allowed) &&
options.allowed.length === 0
) {
return [];
Expand Down
19 changes: 14 additions & 5 deletions src/parameter/sort/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
hasOwnProperty, isFieldNonRelational,
isFieldPathAllowedByRelations,
} from '../../utils';
import { ParseAllowedOption } from '../type';
import { flattenParseAllowedOption, isPathCoveredByParseAllowedOption } from '../utils';

import {
Expand Down Expand Up @@ -76,11 +77,11 @@ export function parseQuerySort<T extends ObjectLiteral = ObjectLiteral>(
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<T>;
if (allowed.length === 0) {
return buildDefaultSortParseOutput(options);
}
}

options.mapping = options.mapping || {};
Expand All @@ -96,6 +97,14 @@ export function parseQuerySort<T extends ObjectLiteral = ObjectLiteral>(
return buildDefaultSortParseOutput(options);
}

if (
typeof options.allowed === 'undefined' &&
options.default
) {
const flatten = flattenNestedObject(options.default);
options.allowed = Object.keys(flatten) as ParseAllowedOption<T>;
}

let parts : string[] = [];

if (typeof data === 'string') {
Expand Down
3 changes: 2 additions & 1 deletion src/parameter/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ type ParseAllowedObjectOption<T extends ObjectLiteral = ObjectLiteral> = {

export type ParseAllowedOption<T extends ObjectLiteral = ObjectLiteral> = T extends ObjectLiteral ?
(
ParseAllowedObjectOption<T> |
ParseAllowedObjectOption<T>
|
(
SimpleKeys<T>[] |
ParseAllowedObjectOption<T>
Expand Down
19 changes: 17 additions & 2 deletions test/unit/fields.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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' }]);
Expand All @@ -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);
Expand All @@ -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);
Expand Down
22 changes: 22 additions & 0 deletions test/unit/filters.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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([{
Expand Down
14 changes: 14 additions & 0 deletions test/unit/parse.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions test/unit/relations.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'] });
Expand Down
8 changes: 8 additions & 0 deletions test/unit/sort.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 9bda36a

Please sign in to comment.