mirror of
https://github.com/strapi/strapi.git
synced 2026-01-04 03:03:38 +00:00
360 lines
9.5 KiB
JavaScript
360 lines
9.5 KiB
JavaScript
import {
|
|
get,
|
|
isBoolean,
|
|
isNumber,
|
|
isNull,
|
|
isObject,
|
|
isArray,
|
|
isEmpty,
|
|
isNaN,
|
|
toNumber,
|
|
} from 'lodash';
|
|
import moment from 'moment';
|
|
import * as yup from 'yup';
|
|
import { translatedErrors as errorsTrads } from 'strapi-helper-plugin';
|
|
|
|
yup.addMethod(yup.mixed, 'defined', function() {
|
|
return this.test('defined', errorsTrads.required, value => value !== undefined);
|
|
});
|
|
|
|
yup.addMethod(yup.array, 'notEmptyMin', function(min) {
|
|
return this.test('notEmptyMin', errorsTrads.min, value => {
|
|
if (isEmpty(value)) {
|
|
return true;
|
|
}
|
|
|
|
return value.length >= min;
|
|
});
|
|
});
|
|
|
|
yup.addMethod(yup.string, 'isInferior', function(message, max) {
|
|
return this.test('isInferior', message, function(value) {
|
|
if (!value) {
|
|
return true;
|
|
}
|
|
|
|
if (Number.isNaN(toNumber(value))) {
|
|
return true;
|
|
}
|
|
|
|
return toNumber(max) >= toNumber(value);
|
|
});
|
|
});
|
|
|
|
yup.addMethod(yup.string, 'isSuperior', function(message, min) {
|
|
return this.test('isSuperior', message, function(value) {
|
|
if (!value) {
|
|
return true;
|
|
}
|
|
|
|
if (Number.isNaN(toNumber(value))) {
|
|
return true;
|
|
}
|
|
|
|
return toNumber(value) >= toNumber(min);
|
|
});
|
|
});
|
|
|
|
const getAttributes = data => get(data, ['schema', 'attributes'], {});
|
|
|
|
const createYupSchema = (model, { components }, isCreatingEntry = true) => {
|
|
const attributes = getAttributes(model);
|
|
|
|
return yup.object().shape(
|
|
Object.keys(attributes).reduce((acc, current) => {
|
|
const attribute = attributes[current];
|
|
|
|
if (
|
|
attribute.type !== 'relation' &&
|
|
attribute.type !== 'component' &&
|
|
attribute.type !== 'dynamiczone'
|
|
) {
|
|
const formatted = createYupSchemaAttribute(attribute.type, attribute, isCreatingEntry);
|
|
acc[current] = formatted;
|
|
}
|
|
|
|
if (attribute.type === 'relation') {
|
|
acc[current] = [
|
|
'oneWay',
|
|
'oneToOne',
|
|
'manyToOne',
|
|
'oneToManyMorph',
|
|
'oneToOneMorph',
|
|
].includes(attribute.relationType)
|
|
? yup.object().nullable()
|
|
: yup.array().nullable();
|
|
}
|
|
|
|
if (attribute.type === 'component') {
|
|
const componentFieldSchema = createYupSchema(components[attribute.component], {
|
|
components,
|
|
});
|
|
|
|
if (attribute.repeatable === true) {
|
|
const { min, max, required } = attribute;
|
|
let componentSchema = yup.lazy(value => {
|
|
let baseSchema = yup.array().of(componentFieldSchema);
|
|
|
|
if (min) {
|
|
if (required) {
|
|
baseSchema = baseSchema.min(min, errorsTrads.min);
|
|
} else if (required !== true && isEmpty(value)) {
|
|
baseSchema = baseSchema.nullable();
|
|
} else {
|
|
baseSchema = baseSchema.min(min, errorsTrads.min);
|
|
}
|
|
}
|
|
|
|
if (max) {
|
|
baseSchema = baseSchema.max(max, errorsTrads.max);
|
|
}
|
|
|
|
return baseSchema;
|
|
});
|
|
|
|
acc[current] = componentSchema;
|
|
|
|
return acc;
|
|
}
|
|
const componentSchema = yup.lazy(obj => {
|
|
if (obj !== undefined) {
|
|
return attribute.required === true
|
|
? componentFieldSchema.defined()
|
|
: componentFieldSchema.nullable();
|
|
}
|
|
|
|
return attribute.required === true ? yup.object().defined() : yup.object().nullable();
|
|
});
|
|
|
|
acc[current] = componentSchema;
|
|
|
|
return acc;
|
|
}
|
|
|
|
if (attribute.type === 'dynamiczone') {
|
|
let dynamicZoneSchema = yup.array().of(
|
|
yup.lazy(({ __component }) => {
|
|
return createYupSchema(components[__component], { components });
|
|
})
|
|
);
|
|
|
|
const { max, min } = attribute;
|
|
|
|
if (attribute.required) {
|
|
// dynamicZoneSchema = dynamicZoneSchema.required();
|
|
dynamicZoneSchema = dynamicZoneSchema.test('required', errorsTrads.required, value => {
|
|
if (isCreatingEntry) {
|
|
return value !== null || value !== undefined;
|
|
}
|
|
|
|
if (value === undefined) {
|
|
return true;
|
|
}
|
|
|
|
return value !== null;
|
|
});
|
|
|
|
if (min) {
|
|
dynamicZoneSchema = dynamicZoneSchema
|
|
.test('min', errorsTrads.min, value => {
|
|
if (isCreatingEntry) {
|
|
return value && value.length > 0;
|
|
}
|
|
|
|
if (value === undefined) {
|
|
return true;
|
|
}
|
|
|
|
return value !== null && value.length > 0;
|
|
})
|
|
.test('required', errorsTrads.required, value => {
|
|
if (isCreatingEntry) {
|
|
return value !== null || value !== undefined;
|
|
}
|
|
|
|
if (value === undefined) {
|
|
return true;
|
|
}
|
|
|
|
return value !== null;
|
|
});
|
|
}
|
|
} else {
|
|
// eslint-disable-next-line no-lonely-if
|
|
if (min) {
|
|
dynamicZoneSchema = dynamicZoneSchema.notEmptyMin(min);
|
|
}
|
|
}
|
|
|
|
if (max) {
|
|
dynamicZoneSchema = dynamicZoneSchema.max(max, errorsTrads.max);
|
|
}
|
|
|
|
acc[current] = dynamicZoneSchema;
|
|
}
|
|
|
|
return acc;
|
|
}, {})
|
|
);
|
|
};
|
|
|
|
const createYupSchemaAttribute = (type, validations, isCreatingEntry) => {
|
|
let schema = yup.mixed();
|
|
|
|
let regex = get(validations, 'regex', null);
|
|
delete validations.regex;
|
|
|
|
if (regex) {
|
|
validations.regex = new RegExp(regex);
|
|
}
|
|
|
|
if (['string', 'uid', 'text', 'richtext', 'email', 'password', 'enumeration'].includes(type)) {
|
|
schema = yup.string();
|
|
}
|
|
|
|
if (type === 'json') {
|
|
schema = yup
|
|
.mixed(errorsTrads.json)
|
|
.test('isJSON', errorsTrads.json, value => {
|
|
if (value === undefined) {
|
|
return true;
|
|
}
|
|
|
|
if (isNumber(value) || isNull(value) || isObject(value) || isArray(value)) {
|
|
return true;
|
|
}
|
|
|
|
try {
|
|
JSON.parse(value);
|
|
|
|
return true;
|
|
} catch (err) {
|
|
return false;
|
|
}
|
|
})
|
|
.nullable();
|
|
}
|
|
|
|
if (type === 'email') {
|
|
schema = schema.email(errorsTrads.email);
|
|
}
|
|
|
|
if (['number', 'integer', 'biginteger', 'float', 'decimal'].includes(type)) {
|
|
schema = yup
|
|
.number()
|
|
.transform(cv => (isNaN(cv) ? undefined : cv))
|
|
.typeError();
|
|
}
|
|
|
|
if (['date', 'datetime'].includes(type)) {
|
|
schema = yup.date();
|
|
}
|
|
|
|
if (type === 'biginteger') {
|
|
schema = yup.string().matches(/^\d*$/);
|
|
}
|
|
|
|
Object.keys(validations).forEach(validation => {
|
|
const validationValue = validations[validation];
|
|
|
|
if (
|
|
!!validationValue ||
|
|
(!isBoolean(validationValue) && Number.isInteger(Math.floor(validationValue))) ||
|
|
validationValue === 0
|
|
) {
|
|
switch (validation) {
|
|
case 'required': {
|
|
if (type === 'password' && isCreatingEntry) {
|
|
schema = schema.required(errorsTrads.required);
|
|
}
|
|
|
|
if (type !== 'password') {
|
|
if (isCreatingEntry) {
|
|
schema = schema.required(errorsTrads.required);
|
|
} else {
|
|
schema = schema.test('required', errorsTrads.required, value => {
|
|
// Field is not touched and the user is editing the entry
|
|
if (value === undefined) {
|
|
return true;
|
|
}
|
|
|
|
if (['number', 'integer', 'biginteger', 'float', 'decimal'].includes(type)) {
|
|
if (value === 0) {
|
|
return true;
|
|
}
|
|
|
|
return !!value;
|
|
}
|
|
|
|
if (['date', 'datetime'].includes(type)) {
|
|
return moment(value)._isValid === true;
|
|
}
|
|
|
|
if (type === 'boolean') {
|
|
return value !== undefined;
|
|
}
|
|
|
|
return !isEmpty(value);
|
|
});
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case 'max': {
|
|
if (type === 'biginteger') {
|
|
schema = schema.isInferior(errorsTrads.max, validationValue);
|
|
} else {
|
|
schema = schema.max(validationValue, errorsTrads.max);
|
|
}
|
|
break;
|
|
}
|
|
case 'maxLength':
|
|
schema = schema.max(validationValue, errorsTrads.maxLength);
|
|
break;
|
|
case 'min': {
|
|
if (type === 'biginteger') {
|
|
schema = schema.isSuperior(errorsTrads.min, validationValue);
|
|
} else {
|
|
schema = schema.min(validationValue, errorsTrads.min);
|
|
}
|
|
break;
|
|
}
|
|
case 'minLength':
|
|
schema = schema.min(validationValue, errorsTrads.minLength);
|
|
break;
|
|
case 'regex':
|
|
schema = schema.matches(validationValue, errorsTrads.regex);
|
|
break;
|
|
case 'lowercase':
|
|
if (['text', 'textarea', 'email', 'string'].includes(type)) {
|
|
schema = schema.strict().lowercase();
|
|
}
|
|
break;
|
|
case 'uppercase':
|
|
if (['text', 'textarea', 'email', 'string'].includes(type)) {
|
|
schema = schema.strict().uppercase();
|
|
}
|
|
break;
|
|
case 'positive':
|
|
if (['number', 'integer', 'bigint', 'float', 'decimal'].includes(type)) {
|
|
schema = schema.positive();
|
|
}
|
|
break;
|
|
case 'negative':
|
|
if (['number', 'integer', 'bigint', 'float', 'decimal'].includes(type)) {
|
|
schema = schema.negative();
|
|
}
|
|
break;
|
|
default:
|
|
schema = schema.nullable();
|
|
}
|
|
}
|
|
});
|
|
|
|
return schema;
|
|
};
|
|
|
|
export default createYupSchema;
|