add util for throwing standardized validationerror

This commit is contained in:
Ben Irvin 2023-08-22 12:19:30 +02:00
parent 9a4028d6e3
commit f1015c3094
11 changed files with 52 additions and 47 deletions

View File

@ -45,7 +45,7 @@ const COMPONENT_FIELDS = ['__component'];
const STATIC_FIELDS = [ID_ATTRIBUTE];
const throwInvalidParam = ({ key }) => {
throw new ValidationError(`Invalid param ${key}`);
throw new ValidationError(`Invalid parameter ${key}`);
};
module.exports = ({ action, ability, model }) => {

View File

@ -39,12 +39,12 @@ const createContentAPIValidators = () => {
const nonWritableAttributes = getNonWritableAttributes(schema);
const transforms = [
// Remove non writable attributes
// non writable attributes
traverseEntity(visitors.throwRestrictedFields(nonWritableAttributes), { schema }),
];
if (auth) {
// Remove restricted relations
// restricted relations
transforms.push(traverseEntity(visitors.throwRestrictedRelations(auth), { schema }));
}

View File

@ -0,0 +1,5 @@
import { ValidationError } from '../errors';
export const throwInvalidParam = ({ key }: { key: string }) => {
throw new ValidationError(`Invalid parameter ${key}`);
};

View File

@ -10,7 +10,7 @@ import { throwPassword, throwPrivate, throwDynamicZones, throwMorphToRelations }
import { isOperator } from '../operators';
import type { Model } from '../types';
import { ValidationError } from '../errors';
import { throwInvalidParam } from './utils';
const { traverseQueryFilters, traverseQuerySort, traverseQueryFields } = traversals;
@ -20,30 +20,30 @@ const throwPasswords = (schema: Model) => async (entity: Data) => {
const defaultValidateFilters = curry((schema: Model, filters: unknown) => {
return pipeAsync(
// Remove keys that are not attributes or valid operators
// keys that are not attributes or valid operators
traverseQueryFilters(
({ key, attribute }) => {
const isAttribute = !!attribute;
if (!isAttribute && !isOperator(key) && key !== 'id') {
throw new ValidationError(`invalid key ${key}`);
throwInvalidParam({ key });
}
},
{ schema }
),
// Remove dynamic zones from filters
// dynamic zones from filters
traverseQueryFilters(throwDynamicZones, { schema }),
// Remove morpTo relations from filters
// morpTo relations from filters
traverseQueryFilters(throwMorphToRelations, { schema }),
// Remove passwords from filters
// passwords from filters
traverseQueryFilters(throwPassword, { schema }),
// Remove private from filters
// private from filters
traverseQueryFilters(throwPrivate, { schema }),
// Remove empty objects
// empty objects
traverseQueryFilters(
({ key, value }) => {
if (isObject(value) && isEmpty(value)) {
throw new ValidationError(`invalid key ${key}`);
throwInvalidParam({ key });
}
},
{ schema }
@ -53,7 +53,7 @@ const defaultValidateFilters = curry((schema: Model, filters: unknown) => {
const defaultValidateSort = curry((schema: Model, sort: unknown) => {
return pipeAsync(
// Remove non attribute keys
// non attribute keys
traverseQuerySort(
({ key, attribute }) => {
// ID is not an attribute per se, so we need to make
@ -63,24 +63,24 @@ const defaultValidateSort = curry((schema: Model, sort: unknown) => {
}
if (!attribute) {
throw new ValidationError(`invalid key ${key}`);
throwInvalidParam({ key });
}
},
{ schema }
),
// Remove dynamic zones from sort
// dynamic zones from sort
traverseQuerySort(throwDynamicZones, { schema }),
// Remove morpTo relations from sort
// morpTo relations from sort
traverseQuerySort(throwMorphToRelations, { schema }),
// Remove private from sort
// private from sort
traverseQuerySort(throwPrivate, { schema }),
// Remove passwords from filters
// passwords from filters
traverseQuerySort(throwPassword, { schema }),
// Remove keys for empty non-scalar values
// keys for empty non-scalar values
traverseQuerySort(
({ key, attribute, value }) => {
if (!isScalarAttribute(attribute) && isEmpty(value)) {
throw new ValidationError(`invalid key ${key}`);
throwInvalidParam({ key });
}
},
{ schema }
@ -90,21 +90,21 @@ const defaultValidateSort = curry((schema: Model, sort: unknown) => {
const defaultValidateFields = curry((schema: Model, fields: unknown) => {
return pipeAsync(
// Only keep scalar attributes
// Only allow scalar attributes
traverseQueryFields(
({ key, attribute }) => {
if (key === 'id') {
return;
}
if (isNil(attribute) || !isScalarAttribute(attribute)) {
throw new ValidationError(`invalid key ${key}`);
throwInvalidParam({ key });
}
},
{ schema }
),
// Remove private fields
// private fields
traverseQueryFields(throwPrivate, { schema }),
// Remove password fields
// password fields
traverseQueryFields(throwPassword, { schema })
)(fields);
});

View File

@ -1,6 +1,6 @@
import { isArray, isNil, toPath } from 'lodash/fp';
import type { Visitor } from '../../traverse/factory';
import { ValidationError } from '../../errors';
import { throwInvalidParam } from '../utils';
export default (allowedFields: string[] | null = null): Visitor =>
({ key, path: { attribute: path } }) => {
@ -66,8 +66,8 @@ export default (allowedFields: string[] | null = null): Visitor =>
return;
}
// Remove otherwise
throw new ValidationError(`Invalid parameter ${key}`);
// throw otherwise
throwInvalidParam({ key });
};
/**

View File

@ -1,10 +1,10 @@
import { isDynamicZoneAttribute } from '../../content-types';
import { ValidationError } from '../../errors';
import { throwInvalidParam } from '../utils';
import type { Visitor } from '../../traverse/factory';
const visitor: Visitor = ({ key, attribute }) => {
if (isDynamicZoneAttribute(attribute)) {
throw new ValidationError(`Invalid parameter ${key}`);
throwInvalidParam({ key });
}
};

View File

@ -1,10 +1,10 @@
import { isMorphToRelationalAttribute } from '../../content-types';
import { ValidationError } from '../../errors';
import { throwInvalidParam } from '../utils';
import type { Visitor } from '../../traverse/factory';
const visitor: Visitor = ({ key, attribute }) => {
if (isMorphToRelationalAttribute(attribute)) {
throw new ValidationError(`Invalid parameter ${key}`);
throwInvalidParam({ key });
}
};

View File

@ -1,9 +1,9 @@
import { ValidationError } from '../../errors';
import { throwInvalidParam } from '../utils';
import type { Visitor } from '../../traverse/factory';
const visitor: Visitor = ({ key, attribute }) => {
if (attribute?.type === 'password') {
throw new ValidationError(`Invalid parameter ${key}`);
throwInvalidParam({ key });
}
};

View File

@ -1,5 +1,5 @@
import { isPrivateAttribute } from '../../content-types';
import { ValidationError } from '../../errors';
import { throwInvalidParam } from '../utils';
import type { Visitor } from '../../traverse/factory';
const visitor: Visitor = ({ schema, key, attribute }) => {
@ -10,7 +10,7 @@ const visitor: Visitor = ({ schema, key, attribute }) => {
const isPrivate = attribute.private === true || isPrivateAttribute(schema, key);
if (isPrivate) {
throw new ValidationError(`Invalid parameter ${key}`);
throwInvalidParam({ key });
}
};

View File

@ -1,12 +1,12 @@
import { isArray } from 'lodash/fp';
import type { Visitor } from '../../traverse/factory';
import { ValidationError } from '../../errors';
import { throwInvalidParam } from '../utils';
export default (restrictedFields: string[] | null = null): Visitor =>
({ key, path: { attribute: path } }) => {
// Remove all fields
// all fields
if (restrictedFields === null) {
throw new ValidationError(`Invalid parameter ${key}`);
throwInvalidParam({ key });
}
// Ignore invalid formats
@ -14,16 +14,16 @@ export default (restrictedFields: string[] | null = null): Visitor =>
return;
}
// Remove if an exact match was found
// if an exact match was found
if (restrictedFields.includes(path as string)) {
throw new ValidationError(`Invalid parameter ${key}`);
throwInvalidParam({ key });
}
// Remove nested matches
// nested matches
const isRestrictedNested = restrictedFields.some((allowedPath) =>
path?.toString().startsWith(`${allowedPath}.`)
);
if (isRestrictedNested) {
throw new ValidationError(`Invalid parameter ${key}`);
throwInvalidParam({ key });
}
};

View File

@ -1,5 +1,5 @@
import * as contentTypeUtils from '../../content-types';
import { ValidationError } from '../../errors';
import { throwInvalidParam } from '../utils';
import type { Visitor } from '../../traverse/factory';
const ACTIONS_TO_VERIFY = ['find'];
@ -31,9 +31,9 @@ export default (auth: unknown): Visitor =>
}
}
// If the new value is empty, remove the relation completely
// If the new value is empty
if (newMorphValue.length === 0) {
throw new ValidationError(`Invalid parameter ${key}`);
throwInvalidParam({ key });
} else {
set(key, newMorphValue);
}
@ -44,9 +44,9 @@ export default (auth: unknown): Visitor =>
const isAllowed = await hasAccessToSomeScopes(scopes, auth);
// If the authenticated user don't have access to any of the scopes, then remove the field
// If the authenticated user don't have access to any of the scopes
if (!isAllowed) {
throw new ValidationError(`Invalid parameter ${key}`);
throwInvalidParam({ key });
}
};