throw validation error when passing wrong relations

This commit is contained in:
Marc-Roig 2022-12-12 15:48:45 +01:00
parent b01fc82f52
commit 32bb3a0645
5 changed files with 66 additions and 48 deletions

View File

@ -1,7 +1,7 @@
'use strict';
const _ = require('lodash/fp');
const { InvalidRelationError } = require('../errors');
/**
* When connecting relations, the order you connect them matters.
*
@ -72,7 +72,7 @@ const sortConnectArray = (connectArr, initialArr = []) => {
} else {
// If we reach this point, it means that the adjacent relation is not in the connect array
// and it is not in the database. This should not happen.
throw new Error(
throw new InvalidRelationError(
`There was a problem connecting relation with id ${rel.id} at position ${JSON.stringify(
rel.position
)}. The relation with id ${adjacentRelId} needs to be connected first.`

View File

@ -5,6 +5,7 @@ const NotNullError = require('./not-null');
const InvalidTimeError = require('./invalid-time');
const InvalidDateError = require('./invalid-date');
const InvalidDateTimeError = require('./invalid-datetime');
const InvalidRelationError = require('./invalid-relation');
module.exports = {
DatabaseError,
@ -12,4 +13,5 @@ module.exports = {
InvalidTimeError,
InvalidDateError,
InvalidDateTimeError,
InvalidRelationError,
};

View File

@ -0,0 +1,14 @@
'use strict';
const DatabaseError = require('./database');
class InvalidRelationError extends DatabaseError {
constructor(message) {
super();
this.name = 'InvalidRelationFormat';
this.message = message || 'Invalid relation format';
this.details = {};
}
}
module.exports = InvalidRelationError;

View File

@ -2,7 +2,7 @@
const _ = require('lodash');
const delegate = require('delegates');
const { InvalidTimeError, InvalidDateError, InvalidDateTimeError } =
const { InvalidTimeError, InvalidDateError, InvalidDateTimeError, InvalidRelationError } =
require('@strapi/database').errors;
const {
webhook: webhookUtils,
@ -34,7 +34,12 @@ const transformLoadParamsToQuery = (uid, field, params = {}, pagination = {}) =>
// TODO: those should be strapi events used by the webhooks not the other way arround
const { ENTRY_CREATE, ENTRY_UPDATE, ENTRY_DELETE } = webhookUtils.webhookEvents;
const databaseErrorsToTransform = [InvalidTimeError, InvalidDateTimeError, InvalidDateError];
const databaseErrorsToTransform = [
InvalidTimeError,
InvalidDateTimeError,
InvalidDateError,
InvalidRelationError,
];
const creationPipeline = (data, context) => {
return applyTransforms(data, context);

View File

@ -120,7 +120,7 @@ const createEntry = async (pluralName, data, populate) => {
body: { data },
qs: { populate },
});
return body.data;
return body;
};
const updateEntry = async (pluralName, id, data, populate) => {
@ -130,7 +130,7 @@ const updateEntry = async (pluralName, id, data, populate) => {
body: { data },
qs: { populate },
});
return body.data;
return body;
};
const createShop = async ({
@ -228,9 +228,9 @@ describe('Relations', () => {
const createdProduct2 = await createEntry('products', { name: 'Candle' });
const createdProduct3 = await createEntry('products', { name: 'Mug' });
data.products.push(createdProduct1);
data.products.push(createdProduct2);
data.products.push(createdProduct3);
data.products.push(createdProduct1.data);
data.products.push(createdProduct2.data);
data.products.push(createdProduct3.data);
id1 = data.products[0].id;
id2 = data.products[1].id;
@ -271,7 +271,7 @@ describe('Relations', () => {
populateShop
);
expect(shop).toMatchObject({
expect(shop.data).toMatchObject({
attributes: {
myCompo: {
compo_products_mw: { data: [{ id: id1 }, { id: id2 }] },
@ -310,7 +310,7 @@ describe('Relations', () => {
populateShop
);
expect(shop).toMatchObject({
expect(shop.data).toMatchObject({
attributes: {
myCompo: {
compo_products_mw: { data: [{ id: id2 }, { id: id1 }] },
@ -357,7 +357,7 @@ describe('Relations', () => {
const updatedShop = await updateEntry(
'shops',
createdShop.id,
createdShop.data.id,
{
name: 'Cazotte Shop',
products_ow: { connect: relationToAdd },
@ -367,7 +367,7 @@ describe('Relations', () => {
products_mm: { connect: relationToAdd },
products_mw: { connect: relationToAdd },
myCompo: {
id: createdShop.attributes.myCompo.id,
id: createdShop.data.attributes.myCompo.id,
compo_products_ow: { connect: relationToAdd },
compo_products_mw: { connect: relationToAdd },
},
@ -375,7 +375,7 @@ describe('Relations', () => {
populateShop
);
expect(updatedShop).toMatchObject({
expect(updatedShop.data).toMatchObject({
attributes: {
myCompo: {
compo_products_mw: { data: [{ id: id1 }, { id: id2 }, { id: id3 }] },
@ -415,7 +415,7 @@ describe('Relations', () => {
const updatedShop = await updateEntry(
'shops',
createdShop.id,
createdShop.data.id,
{
name: 'Cazotte Shop',
products_ow: { connect: relationToAdd, disconnect: relationToRemove },
@ -425,7 +425,7 @@ describe('Relations', () => {
products_mm: { connect: relationToAdd, disconnect: relationToRemove },
products_mw: { connect: relationToAdd, disconnect: relationToRemove },
myCompo: {
id: createdShop.attributes.myCompo.id,
id: createdShop.data.attributes.myCompo.id,
compo_products_ow: { connect: relationToAdd, disconnect: relationToRemove },
compo_products_mw: { connect: relationToAdd, disconnect: relationToRemove },
},
@ -433,7 +433,7 @@ describe('Relations', () => {
populateShop
);
expect(updatedShop).toMatchObject({
expect(updatedShop.data).toMatchObject({
attributes: {
myCompo: {
compo_products_mw: { data: [{ id: id2 }, { id: id3 }] },
@ -473,7 +473,7 @@ describe('Relations', () => {
const updatedShop = await updateEntry(
'shops',
createdShop.id,
createdShop.data.id,
{
name: 'Cazotte Shop',
products_ow: { connect: relationToAdd, disconnect: relationToRemove },
@ -483,7 +483,7 @@ describe('Relations', () => {
products_mm: { connect: relationToAdd, disconnect: relationToRemove },
products_mw: { connect: relationToAdd, disconnect: relationToRemove },
myCompo: {
id: createdShop.attributes.myCompo.id,
id: createdShop.data.attributes.myCompo.id,
compo_products_ow: { connect: relationToAdd, disconnect: relationToRemove },
compo_products_mw: { connect: relationToAdd, disconnect: relationToRemove },
},
@ -491,7 +491,7 @@ describe('Relations', () => {
populateShop
);
expect(updatedShop).toMatchObject({
expect(updatedShop.data).toMatchObject({
attributes: {
myCompo: {
compo_products_mw: { data: [{ id: id2 }, { id: id3 }] },
@ -527,21 +527,21 @@ describe('Relations', () => {
const updatedShop = await updateEntry(
'shops',
createdShop.id,
createdShop.data.id,
{
name: 'Cazotte Shop',
products_om: { connect: relationToChange },
products_mm: { connect: relationToChange },
products_mw: { connect: relationToChange },
myCompo: {
id: createdShop.attributes.myCompo.id,
id: createdShop.data.attributes.myCompo.id,
compo_products_mw: { connect: relationToChange },
},
},
populateShop
);
expect(updatedShop).toMatchObject({
expect(updatedShop.data).toMatchObject({
attributes: {
myCompo: {
compo_products_mw: { data: [{ id: id3 }, { id: id2 }, { id: id1 }] },
@ -572,21 +572,21 @@ describe('Relations', () => {
const updatedShop = await updateEntry(
'shops',
createdShop.id,
createdShop.data.id,
{
name: 'Cazotte Shop',
products_om: { connect: relationToChange },
products_mm: { connect: relationToChange },
products_mw: { connect: relationToChange },
myCompo: {
id: createdShop.attributes.myCompo.id,
id: createdShop.data.attributes.myCompo.id,
compo_products_mw: { connect: relationToChange },
},
},
populateShop
);
expect(updatedShop).toMatchObject({
expect(updatedShop.data).toMatchObject({
attributes: {
myCompo: {
compo_products_mw: { data: [{ id: id1 }, { id: id3 }, { id: id2 }] },
@ -617,21 +617,21 @@ describe('Relations', () => {
const updatedShop = await updateEntry(
'shops',
createdShop.id,
createdShop.data.id,
{
name: 'Cazotte Shop',
products_om: { connect: relationToChange },
products_mm: { connect: relationToChange },
products_mw: { connect: relationToChange },
myCompo: {
id: createdShop.attributes.myCompo.id,
id: createdShop.data.attributes.myCompo.id,
compo_products_mw: { connect: relationToChange },
},
},
populateShop
);
expect(updatedShop).toMatchObject({
expect(updatedShop.data).toMatchObject({
attributes: {
myCompo: {
compo_products_mw: { data: [{ id: id3 }, { id: id2 }, { id: id1 }] },
@ -675,7 +675,7 @@ describe('Relations', () => {
const updatedShop = await updateEntry(
'shops',
createdShop.id,
createdShop.data.id,
{
name: 'Cazotte Shop',
products_ow: { disconnect: relationsToDisconnectOne },
@ -685,7 +685,7 @@ describe('Relations', () => {
products_mm: { disconnect: relationsToDisconnectMany },
products_mw: { disconnect: relationsToDisconnectMany },
myCompo: {
id: createdShop.attributes.myCompo.id,
id: createdShop.data.attributes.myCompo.id,
compo_products_ow: { disconnect: relationsToDisconnectOne },
compo_products_mw: { disconnect: relationsToDisconnectMany },
},
@ -693,7 +693,7 @@ describe('Relations', () => {
populateShop
);
expect(updatedShop).toMatchObject({
expect(updatedShop.data).toMatchObject({
attributes: {
myCompo: {
compo_products_ow: { data: null },
@ -733,7 +733,7 @@ describe('Relations', () => {
const updatedShop = await updateEntry(
'shops',
createdShop.id,
createdShop.data.id,
{
name: 'Cazotte Shop',
products_ow: { disconnect: relationsToDisconnectMany },
@ -743,7 +743,7 @@ describe('Relations', () => {
products_mm: { disconnect: relationsToDisconnectMany },
products_mw: { disconnect: relationsToDisconnectMany },
myCompo: {
id: createdShop.attributes.myCompo.id,
id: createdShop.data.attributes.myCompo.id,
compo_products_ow: { disconnect: relationsToDisconnectMany },
compo_products_mw: { disconnect: relationsToDisconnectMany },
},
@ -751,7 +751,7 @@ describe('Relations', () => {
populateShop
);
expect(updatedShop).toMatchObject({
expect(updatedShop.data).toMatchObject({
attributes: {
myCompo: {
compo_products_ow: { data: { id: id1 } },
@ -779,7 +779,7 @@ describe('Relations', () => {
});
const expectedCreatedShop = shopFactory({ anyToManyRel: [{ id: id2 }, { id: id1 }] });
expect(createdShop).toMatchObject(expectedCreatedShop);
expect(createdShop.data).toMatchObject(expectedCreatedShop);
});
test('Connect new relation at the end', async () => {
@ -791,7 +791,7 @@ describe('Relations', () => {
});
const expectedCreatedShop = shopFactory({ anyToManyRel: [{ id: id1 }, { id: id2 }] });
expect(createdShop).toMatchObject(expectedCreatedShop);
expect(createdShop.data).toMatchObject(expectedCreatedShop);
});
test('Create relations using before and after', async () => {
@ -807,7 +807,7 @@ describe('Relations', () => {
const expectedShop = shopFactory({
anyToManyRel: [{ id: id2 }, { id: id1 }, { id: id3 }],
});
expect(createdShop).toMatchObject(expectedShop);
expect(createdShop.data).toMatchObject(expectedShop);
});
test('Update relations using before and after', async () => {
@ -818,7 +818,7 @@ describe('Relations', () => {
],
});
const updatedShop = await updateShop(shop, {
const updatedShop = await updateShop(shop.data, {
anyToManyRel: [
{ id: id1, position: { before: id2 } },
{ id: id2, position: { start: true } },
@ -829,7 +829,7 @@ describe('Relations', () => {
const expectedShop = shopFactory({
anyToManyRel: [{ id: id2 }, { id: id1 }, { id: id3 }],
});
expect(updatedShop).toMatchObject(expectedShop);
expect(updatedShop.data).toMatchObject(expectedShop);
});
});
@ -841,7 +841,7 @@ describe('Relations', () => {
],
});
const updatedShop = await updateShop(shop, {
const updatedShop = await updateShop(shop.data, {
anyToManyRel: [
{ id: id1, position: { end: true } },
{ id: id1, position: { start: true } },
@ -852,22 +852,19 @@ describe('Relations', () => {
const expectedShop = shopFactory({
anyToManyRel: [{ id: id2 }, { id: id1 }],
});
expect(updatedShop).toMatchObject(expectedShop);
expect(updatedShop.data).toMatchObject(expectedShop);
});
test.only('Update relations with invalid connect array', async () => {
test('Update relations with invalid connect array in strict mode', async () => {
const shop = await createShop({
anyToManyRel: [{ id: id1, position: { end: true } }],
});
// Connect relation before id2, but id2 is not in the DB or in the connect array
const updatedShop = await updateShop(shop, {
const updatedShop = await updateShop(shop.data, {
anyToManyRel: [{ id: id1, position: { after: id2 } }],
});
const expectedShop = shopFactory({
anyToManyRel: [{ id: id1 }],
});
expect(updatedShop).toMatchObject(expectedShop);
expect(updatedShop.error).toMatchObject({ status: 400, name: 'ValidationError' });
});
});