291 lines
11 KiB
JavaScript
291 lines
11 KiB
JavaScript
'use strict';
|
|
|
|
var strapiAdmin = require('@strapi/admin/strapi-admin');
|
|
var pipe = require('lodash/fp/pipe');
|
|
var yup = require('yup');
|
|
var attributes = require('../constants/attributes.js');
|
|
|
|
function _interopNamespaceDefault(e) {
|
|
var n = Object.create(null);
|
|
if (e) {
|
|
Object.keys(e).forEach(function (k) {
|
|
if (k !== 'default') {
|
|
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
Object.defineProperty(n, k, d.get ? d : {
|
|
enumerable: true,
|
|
get: function () { return e[k]; }
|
|
});
|
|
}
|
|
});
|
|
}
|
|
n.default = e;
|
|
return Object.freeze(n);
|
|
}
|
|
|
|
var yup__namespace = /*#__PURE__*/_interopNamespaceDefault(yup);
|
|
|
|
const arrayValidator = (attribute, options)=>({
|
|
message: strapiAdmin.translatedErrors.required,
|
|
test (value) {
|
|
if (options.status === 'draft') {
|
|
return true;
|
|
}
|
|
if (!attribute.required) {
|
|
return true;
|
|
}
|
|
if (!value) {
|
|
return false;
|
|
}
|
|
if (Array.isArray(value) && value.length === 0) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
});
|
|
/**
|
|
* TODO: should we create a Map to store these based on the hash of the schema?
|
|
*/ const createYupSchema = (attributes$1 = {}, components = {}, options = {
|
|
status: null
|
|
})=>{
|
|
const createModelSchema = (attributes$1)=>yup__namespace.object().shape(Object.entries(attributes$1).reduce((acc, [name, attribute])=>{
|
|
if (attributes.DOCUMENT_META_FIELDS.includes(name)) {
|
|
return acc;
|
|
}
|
|
/**
|
|
* These validations won't apply to every attribute
|
|
* and that's okay, in that case we just return the
|
|
* schema as it was passed.
|
|
*/ const validations = [
|
|
addNullableValidation,
|
|
addRequiredValidation,
|
|
addMinLengthValidation,
|
|
addMaxLengthValidation,
|
|
addMinValidation,
|
|
addMaxValidation,
|
|
addRegexValidation
|
|
].map((fn)=>fn(attribute, options));
|
|
const transformSchema = pipe(...validations);
|
|
switch(attribute.type){
|
|
case 'component':
|
|
{
|
|
const { attributes } = components[attribute.component];
|
|
if (attribute.repeatable) {
|
|
return {
|
|
...acc,
|
|
[name]: transformSchema(yup__namespace.array().of(createModelSchema(attributes).nullable(false))).test(arrayValidator(attribute, options))
|
|
};
|
|
} else {
|
|
return {
|
|
...acc,
|
|
[name]: transformSchema(createModelSchema(attributes).nullable())
|
|
};
|
|
}
|
|
}
|
|
case 'dynamiczone':
|
|
return {
|
|
...acc,
|
|
[name]: transformSchema(yup__namespace.array().of(yup__namespace.lazy((data)=>{
|
|
const attributes = components?.[data?.__component]?.attributes;
|
|
const validation = yup__namespace.object().shape({
|
|
__component: yup__namespace.string().required().oneOf(Object.keys(components))
|
|
}).nullable(false);
|
|
if (!attributes) {
|
|
return validation;
|
|
}
|
|
return validation.concat(createModelSchema(attributes));
|
|
}))).test(arrayValidator(attribute, options))
|
|
};
|
|
case 'relation':
|
|
return {
|
|
...acc,
|
|
[name]: transformSchema(yup__namespace.lazy((value)=>{
|
|
if (!value) {
|
|
return yup__namespace.mixed().nullable(true);
|
|
} else if (Array.isArray(value)) {
|
|
// If a relation value is an array, we expect
|
|
// an array of objects with {id} properties, representing the related entities.
|
|
return yup__namespace.array().of(yup__namespace.object().shape({
|
|
id: yup__namespace.number().required()
|
|
}));
|
|
} else if (typeof value === 'object') {
|
|
// A realtion value can also be an object. Some API
|
|
// repsonses return the number of entities in the relation
|
|
// as { count: x }
|
|
return yup__namespace.object();
|
|
} else {
|
|
return yup__namespace.mixed().test('type-error', 'Relation values must be either null, an array of objects with {id} or an object.', ()=>false);
|
|
}
|
|
}))
|
|
};
|
|
default:
|
|
return {
|
|
...acc,
|
|
[name]: transformSchema(createAttributeSchema(attribute))
|
|
};
|
|
}
|
|
}, {}))/**
|
|
* TODO: investigate why an undefined object fails a check of `nullable`.
|
|
*/ .default(null);
|
|
return createModelSchema(attributes$1);
|
|
};
|
|
const createAttributeSchema = (attribute)=>{
|
|
switch(attribute.type){
|
|
case 'biginteger':
|
|
return yup__namespace.string().matches(/^-?\d*$/);
|
|
case 'boolean':
|
|
return yup__namespace.boolean();
|
|
case 'blocks':
|
|
return yup__namespace.mixed().test('isBlocks', strapiAdmin.translatedErrors.json, (value)=>{
|
|
if (!value || Array.isArray(value)) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
});
|
|
case 'decimal':
|
|
case 'float':
|
|
case 'integer':
|
|
return yup__namespace.number();
|
|
case 'email':
|
|
return yup__namespace.string().email(strapiAdmin.translatedErrors.email);
|
|
case 'enumeration':
|
|
return yup__namespace.string().oneOf([
|
|
...attribute.enum,
|
|
null
|
|
]);
|
|
case 'json':
|
|
return yup__namespace.mixed().test('isJSON', strapiAdmin.translatedErrors.json, (value)=>{
|
|
/**
|
|
* We don't want to validate the JSON field if it's empty.
|
|
*/ if (!value || typeof value === 'string' && value.length === 0) {
|
|
return true;
|
|
}
|
|
// If the value was created via content API and wasn't changed, then it's still an object
|
|
if (typeof value === 'object') {
|
|
try {
|
|
JSON.stringify(value);
|
|
return true;
|
|
} catch (err) {
|
|
return false;
|
|
}
|
|
}
|
|
try {
|
|
JSON.parse(value);
|
|
return true;
|
|
} catch (err) {
|
|
return false;
|
|
}
|
|
});
|
|
case 'password':
|
|
case 'richtext':
|
|
case 'string':
|
|
case 'text':
|
|
return yup__namespace.string();
|
|
case 'uid':
|
|
return yup__namespace.string().matches(/^[A-Za-z0-9-_.~]*$/);
|
|
default:
|
|
/**
|
|
* This allows any value.
|
|
*/ return yup__namespace.mixed();
|
|
}
|
|
};
|
|
// Helper function to return schema.nullable() if it exists, otherwise return schema
|
|
const nullableSchema = (schema)=>{
|
|
return schema?.nullable ? schema.nullable() : // e.g. when the schema has been built using yup.lazy (e.g. for relations).
|
|
// In these cases we should just return the schema as it is.
|
|
schema;
|
|
};
|
|
const addNullableValidation = ()=>(schema)=>{
|
|
return nullableSchema(schema);
|
|
};
|
|
const addRequiredValidation = (attribute, options)=>(schema)=>{
|
|
if (options.status === 'draft' || !attribute.required) {
|
|
return schema;
|
|
}
|
|
if (attribute.required && 'required' in schema) {
|
|
return schema.required(strapiAdmin.translatedErrors.required);
|
|
}
|
|
return schema;
|
|
};
|
|
const addMinLengthValidation = (attribute, options)=>(schema)=>{
|
|
// Skip minLength validation for draft
|
|
if (options.status === 'draft') {
|
|
return schema;
|
|
}
|
|
if ('minLength' in attribute && attribute.minLength && Number.isInteger(attribute.minLength) && 'min' in schema) {
|
|
return schema.min(attribute.minLength, {
|
|
...strapiAdmin.translatedErrors.minLength,
|
|
values: {
|
|
min: attribute.minLength
|
|
}
|
|
});
|
|
}
|
|
return schema;
|
|
};
|
|
const addMaxLengthValidation = (attribute)=>(schema)=>{
|
|
if ('maxLength' in attribute && attribute.maxLength && Number.isInteger(attribute.maxLength) && 'max' in schema) {
|
|
return schema.max(attribute.maxLength, {
|
|
...strapiAdmin.translatedErrors.maxLength,
|
|
values: {
|
|
max: attribute.maxLength
|
|
}
|
|
});
|
|
}
|
|
return schema;
|
|
};
|
|
const addMinValidation = (attribute, options)=>(schema)=>{
|
|
// do not validate min for draft
|
|
if (options.status === 'draft') {
|
|
return schema;
|
|
}
|
|
if ('min' in attribute && 'min' in schema) {
|
|
const min = toInteger(attribute.min);
|
|
if (min) {
|
|
return schema.min(min, {
|
|
...strapiAdmin.translatedErrors.min,
|
|
values: {
|
|
min
|
|
}
|
|
});
|
|
}
|
|
}
|
|
return schema;
|
|
};
|
|
const addMaxValidation = (attribute)=>(schema)=>{
|
|
if ('max' in attribute) {
|
|
const max = toInteger(attribute.max);
|
|
if ('max' in schema && max) {
|
|
return schema.max(max, {
|
|
...strapiAdmin.translatedErrors.max,
|
|
values: {
|
|
max
|
|
}
|
|
});
|
|
}
|
|
}
|
|
return schema;
|
|
};
|
|
const toInteger = (val)=>{
|
|
if (typeof val === 'number' || val === undefined) {
|
|
return val;
|
|
} else {
|
|
const num = Number(val);
|
|
return isNaN(num) ? undefined : num;
|
|
}
|
|
};
|
|
const addRegexValidation = (attribute)=>(schema)=>{
|
|
if ('regex' in attribute && attribute.regex && 'matches' in schema) {
|
|
return schema.matches(new RegExp(attribute.regex), {
|
|
message: {
|
|
id: strapiAdmin.translatedErrors.regex.id,
|
|
defaultMessage: 'The value does not match the defined pattern.'
|
|
},
|
|
excludeEmptyString: !attribute.required
|
|
});
|
|
}
|
|
return schema;
|
|
};
|
|
|
|
exports.createYupSchema = createYupSchema;
|
|
//# sourceMappingURL=validation.js.map
|