datoit
Version:
An open source headless CMS solution to create and manage your own API. It provides a powerful dashboard and features to make your life easier. Databases supported: MongoDB, MySQL, MariaDB, PostgreSQL, SQLite
187 lines (154 loc) • 5.55 kB
JavaScript
/**
* Entity validator
* Module that will validate input data for entity creation or edition
*/
;
const { has, assoc, prop } = require('lodash/fp');
const strapiUtils = require('datoit-utils');
const validators = require('./validators');
const { yup, formatYupErrors } = strapiUtils;
const { isMediaAttribute, isScalarAttribute, getWritableAttributes } = strapiUtils.contentTypes;
const addMinMax = (attr, validator, data) => {
if (Number.isInteger(attr.min) && (attr.required || (Array.isArray(data) && data.length > 0))) {
validator = validator.min(attr.min);
}
if (Number.isInteger(attr.max)) {
validator = validator.max(attr.max);
}
return validator;
};
const addRequiredValidation = createOrUpdate => (required, validator) => {
if (required) {
if (createOrUpdate === 'creation') {
validator = validator.notNil();
} else if (createOrUpdate === 'update') {
validator = validator.notNull();
}
} else {
validator = validator.nullable();
}
return validator;
};
const addDefault = createOrUpdate => (attr, validator) => {
if (createOrUpdate === 'creation') {
if (
((attr.type === 'component' && attr.repeatable) || attr.type === 'dynamiczone') &&
!attr.required
) {
validator = validator.default([]);
} else {
validator = validator.default(attr.default);
}
} else {
validator = validator.default(undefined);
}
return validator;
};
const preventCast = validator => validator.transform((val, originalVal) => originalVal);
const createComponentValidator = createOrUpdate => (attr, data, { isDraft }) => {
let validator;
const [model] = strapi.db.getModelsByAttribute(attr);
if (!model) {
throw new Error('Validation failed: Model not found');
}
if (prop('repeatable', attr) === true) {
validator = yup
.array()
.of(
yup.lazy(item => createModelValidator(createOrUpdate)(model, item, { isDraft }).notNull())
);
validator = addRequiredValidation(createOrUpdate)(true, validator);
validator = addMinMax(attr, validator, data);
} else {
validator = createModelValidator(createOrUpdate)(model, data, { isDraft });
validator = addRequiredValidation(createOrUpdate)(!isDraft && attr.required, validator);
}
return validator;
};
const createDzValidator = createOrUpdate => (attr, data, { isDraft }) => {
let validator;
validator = yup.array().of(
yup.lazy(item => {
const model = strapi.getModel(prop('__component', item));
const schema = yup
.object()
.shape({
__component: yup
.string()
.required()
.oneOf(Object.keys(strapi.components)),
})
.notNull();
return model
? schema.concat(createModelValidator(createOrUpdate)(model, item, { isDraft }))
: schema;
})
);
validator = addRequiredValidation(createOrUpdate)(true, validator);
validator = addMinMax(attr, validator, data);
return validator;
};
const createRelationValidator = createOrUpdate => (attr, data, { isDraft }) => {
let validator;
if (Array.isArray(data)) {
validator = yup.array().of(yup.mixed());
} else {
validator = yup.mixed();
}
validator = addRequiredValidation(createOrUpdate)(!isDraft && attr.required, validator);
return validator;
};
const createScalarAttributeValidator = createOrUpdate => (attr, { isDraft }) => {
let validator;
if (has(attr.type, validators)) {
validator = validators[attr.type](attr, { isDraft });
} else {
// No validators specified - fall back to mixed
validator = yup.mixed();
}
validator = addRequiredValidation(createOrUpdate)(!isDraft && attr.required, validator);
return validator;
};
const createAttributeValidator = createOrUpdate => (attr, data, { isDraft }) => {
let validator;
if (isMediaAttribute(attr)) {
validator = yup.mixed();
} else if (isScalarAttribute(attr)) {
validator = createScalarAttributeValidator(createOrUpdate)(attr, { isDraft });
} else {
if (attr.type === 'component') {
validator = createComponentValidator(createOrUpdate)(attr, data, { isDraft });
} else if (attr.type === 'dynamiczone') {
validator = createDzValidator(createOrUpdate)(attr, data, { isDraft });
} else {
validator = createRelationValidator(createOrUpdate)(attr, data, { isDraft });
}
validator = preventCast(validator);
}
validator = addDefault(createOrUpdate)(attr, validator);
return validator;
};
const createModelValidator = createOrUpdate => (model, data, { isDraft }) => {
const writableAttributes = model ? getWritableAttributes(model) : [];
const schema = writableAttributes.reduce((validators, attributeName) => {
const validator = createAttributeValidator(createOrUpdate)(
model.attributes[attributeName],
prop(attributeName, data),
{ isDraft }
);
return assoc(attributeName, validator)(validators);
}, {});
return yup.object().shape(schema);
};
const createValidateEntity = createOrUpdate => async (model, data, { isDraft = false } = {}) => {
try {
const validator = createModelValidator(createOrUpdate)(model, data, { isDraft }).required();
return await validator.validate(data, { abortEarly: false });
} catch (e) {
throw strapi.errors.badRequest('ValidationError', { errors: formatYupErrors(e) });
}
};
module.exports = {
validateEntityCreation: createValidateEntity('creation'),
validateEntityUpdate: createValidateEntity('update'),
};