skip unique validation if the attribute is unchanged

This commit is contained in:
Dieter Stinglhamber 2021-11-26 09:30:28 +01:00
parent d3de1ca7be
commit 2b625210d7
3 changed files with 45 additions and 25 deletions

View File

@ -202,10 +202,14 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
const isDraft = contentTypesUtils.isDraft(entityToUpdate, model);
const validData = await entityValidator.validateEntityUpdate(model, data, {
const validData = await entityValidator.validateEntityUpdate(
model,
data,
{
isDraft,
entityId,
});
},
entityToUpdate
);
const query = transformParamsToQuery(uid, pickSelectionParams(wrappedParams));

View File

@ -120,12 +120,13 @@ const createRelationValidator = createOrUpdate => (attr, data, { isDraft }) => {
const createScalarAttributeValidator = createOrUpdate => (
attr,
{ isDraft, uid, attributeName, entityId }
data,
{ isDraft, model, attributeName, entity }
) => {
let validator;
if (has(attr.type, validators)) {
validator = validators[attr.type](attr, { isDraft, uid, attributeName, entityId });
validator = validators[attr.type](attr, { isDraft, model, attributeName, entity, data });
} else {
// No validators specified - fall back to mixed
validator = yup.mixed();
@ -139,18 +140,18 @@ const createScalarAttributeValidator = createOrUpdate => (
const createAttributeValidator = createOrUpdate => (
attr,
data,
{ isDraft, uid, attributeName, entityId }
{ isDraft, model, attributeName, entity }
) => {
let validator;
if (isMediaAttribute(attr)) {
validator = yup.mixed();
} else if (isScalarAttribute(attr)) {
validator = createScalarAttributeValidator(createOrUpdate)(attr, {
validator = createScalarAttributeValidator(createOrUpdate)(attr, data, {
isDraft,
uid,
model,
attributeName,
entityId,
entity,
});
} else {
if (attr.type === 'component') {
@ -169,14 +170,14 @@ const createAttributeValidator = createOrUpdate => (
return validator;
};
const createModelValidator = createOrUpdate => (model, data, { isDraft, entityId }) => {
const createModelValidator = createOrUpdate => (model, data, { isDraft }, entity) => {
const writableAttributes = model ? getWritableAttributes(model) : [];
const schema = writableAttributes.reduce((validators, attributeName) => {
const validator = createAttributeValidator(createOrUpdate)(
model.attributes[attributeName],
prop(attributeName, data),
{ isDraft, uid: model.uid, attributeName, entityId }
{ isDraft, model, attributeName, entity }
);
return assoc(attributeName, validator)(validators);
@ -188,7 +189,8 @@ const createModelValidator = createOrUpdate => (model, data, { isDraft, entityId
const createValidateEntity = createOrUpdate => async (
model,
data,
{ isDraft = false, entityId } = {}
{ isDraft = false } = {},
entity = null
) => {
if (!isObject(data)) {
const { displayName } = model.info;
@ -198,10 +200,14 @@ const createValidateEntity = createOrUpdate => async (
);
}
const validator = createModelValidator(createOrUpdate)(model, data, {
const validator = createModelValidator(createOrUpdate)(
model,
data,
{
isDraft,
entityId,
}).required();
},
entity
).required();
return validateYupSchema(validator, { strict: false, abortEarly: false })(data);
};

View File

@ -7,9 +7,9 @@ const { yup } = require('@strapi/utils');
/**
* Utility function to compose validators
*/
const composeValidators = (...fns) => (attr, { isDraft, uid, attributeName, entityId }) => {
const composeValidators = (...fns) => (attr, { isDraft, model, attributeName, entity, data }) => {
return fns.reduce((validator, fn) => {
return fn(attr, validator, { isDraft, uid, attributeName, entityId });
return fn(attr, validator, { isDraft, model, attributeName, entity, data });
}, yup.mixed());
};
@ -71,14 +71,24 @@ const addMaxFloatValidator = ({ max }, validator) =>
const addStringRegexValidator = ({ regex }, validator) =>
_.isUndefined(regex) ? validator : validator.matches(new RegExp(regex));
const addUniqueValidator = (attr, validator, { uid, attributeName, entityId }) => {
if (attr.unique) {
const addUniqueValidator = (attr, validator, { model, attributeName, entity, data }) => {
/**
* If the attribute is unchanged we skip the unique verification. This will
* prevent the validator to be triggered in case the user activated the
* unique constraint after already creating multiple entries with
* the same attribute value for that field.
*/
if (entity && data === entity[attributeName]) {
return validator;
}
if (attr.unique || attr.type === 'uid') {
return validator.test('unique', 'This attribute must be unique', async value => {
let whereParams = entityId
? { $and: [{ [attributeName]: value }, { $not: { id: entityId } }] }
let whereParams = entity
? { $and: [{ [attributeName]: value }, { $not: { id: entity.id } }] }
: { [attributeName]: value };
const record = await strapi.db.query(uid).findOne({
const record = await strapi.db.query(model.uid).findOne({
select: ['id'],
where: whereParams,
});
@ -131,7 +141,7 @@ module.exports = {
password: stringValidator,
email: emailValidator,
enumeration: enumerationValidator,
boolean: () => () => yup.mixed(),
boolean: () => yup.boolean(),
uid: uidValidator,
json: () => yup.mixed(),
integer: integerValidator,