modifies create and update hooks to 'change' instead
This commit is contained in:
@@ -15,14 +15,6 @@ module.exports = {
|
||||
delete: () => true,
|
||||
},
|
||||
hooks: {
|
||||
beforeCreate: [
|
||||
(operation) => {
|
||||
if (operation.req.headers.hook === 'beforeCreate') {
|
||||
operation.req.body.description += '-beforeCreateSuffix';
|
||||
}
|
||||
return operation.data;
|
||||
},
|
||||
],
|
||||
beforeRead: [
|
||||
(operation) => {
|
||||
if (operation.req.headers.hook === 'beforeRead') {
|
||||
@@ -30,10 +22,10 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
],
|
||||
beforeUpdate: [
|
||||
beforeChange: [
|
||||
(operation) => {
|
||||
if (operation.req.headers.hook === 'beforeUpdate') {
|
||||
operation.req.body.description += '-beforeUpdateSuffix';
|
||||
if (operation.req.headers.hook === 'beforeChange') {
|
||||
operation.req.body.description += '-beforeChangeSuffix';
|
||||
}
|
||||
return operation.data;
|
||||
},
|
||||
@@ -46,14 +38,6 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
],
|
||||
afterCreate: [
|
||||
(operation) => {
|
||||
if (operation.req.headers.hook === 'afterCreate') {
|
||||
operation.doc.afterCreateHook = true;
|
||||
}
|
||||
return operation.doc;
|
||||
},
|
||||
],
|
||||
afterRead: [
|
||||
(operation) => {
|
||||
const { doc } = operation;
|
||||
@@ -62,10 +46,10 @@ module.exports = {
|
||||
return doc;
|
||||
},
|
||||
],
|
||||
afterUpdate: [
|
||||
afterChange: [
|
||||
(operation) => {
|
||||
if (operation.req.headers.hook === 'afterUpdate') {
|
||||
operation.doc.afterUpdateHook = true;
|
||||
if (operation.req.headers.hook === 'afterChange') {
|
||||
operation.doc.afterChangeHook = true;
|
||||
}
|
||||
return operation.doc;
|
||||
},
|
||||
|
||||
@@ -27,31 +27,47 @@ async function register(args) {
|
||||
}
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 2. Execute field-level hooks, access, and validation
|
||||
// 2. Execute before validate collection hooks
|
||||
// /////////////////////////////////////
|
||||
|
||||
data = await this.performFieldOperations(collectionConfig, {
|
||||
data,
|
||||
hook: 'beforeCreate',
|
||||
operationName: 'create',
|
||||
req,
|
||||
});
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 3. Execute before create hook
|
||||
// /////////////////////////////////////
|
||||
|
||||
await collectionConfig.hooks.beforeCreate.reduce(async (priorHook, hook) => {
|
||||
await collectionConfig.hooks.beforeValidate.reduce(async (priorHook, hook) => {
|
||||
await priorHook;
|
||||
|
||||
data = (await hook({
|
||||
data,
|
||||
req,
|
||||
operation: 'create',
|
||||
})) || data;
|
||||
}, Promise.resolve());
|
||||
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 3. Execute field-level hooks, access, and validation
|
||||
// /////////////////////////////////////
|
||||
|
||||
data = await this.performFieldOperations(collectionConfig, {
|
||||
data,
|
||||
hook: 'beforeChange',
|
||||
operation: 'create',
|
||||
req,
|
||||
});
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 4. Execute before create hook
|
||||
// /////////////////////////////////////
|
||||
|
||||
await collectionConfig.hooks.beforeChange.reduce(async (priorHook, hook) => {
|
||||
await priorHook;
|
||||
|
||||
data = (await hook({
|
||||
data,
|
||||
req,
|
||||
operation: 'create',
|
||||
})) || data;
|
||||
}, Promise.resolve());
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 6. Perform register
|
||||
// 5. Perform register
|
||||
// /////////////////////////////////////
|
||||
|
||||
const modelData = { ...data };
|
||||
@@ -72,32 +88,33 @@ async function register(args) {
|
||||
result = result.toJSON({ virtuals: true });
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 7. Execute field-level hooks and access
|
||||
// 6. Execute field-level hooks and access
|
||||
// /////////////////////////////////////
|
||||
|
||||
result = await this.performFieldOperations(collectionConfig, {
|
||||
data: result,
|
||||
hook: 'afterRead',
|
||||
operationName: 'read',
|
||||
operation: 'read',
|
||||
req,
|
||||
depth,
|
||||
});
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 8. Execute after create hook
|
||||
// 7. Execute after create hook
|
||||
// /////////////////////////////////////
|
||||
|
||||
await collectionConfig.hooks.afterCreate.reduce(async (priorHook, hook) => {
|
||||
await collectionConfig.hooks.afterChange.reduce(async (priorHook, hook) => {
|
||||
await priorHook;
|
||||
|
||||
result = await hook({
|
||||
doc: result,
|
||||
req: args.req,
|
||||
operation: 'create',
|
||||
}) || result;
|
||||
}, Promise.resolve());
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 9. Return user
|
||||
// 8. Return user
|
||||
// /////////////////////////////////////
|
||||
|
||||
return result;
|
||||
|
||||
@@ -4,8 +4,6 @@ const { NotFound, Forbidden } = require('../../errors');
|
||||
const executeAccess = require('../executeAccess');
|
||||
|
||||
async function update(args) {
|
||||
const { config } = this;
|
||||
|
||||
const {
|
||||
depth,
|
||||
collection: {
|
||||
@@ -54,39 +52,55 @@ async function update(args) {
|
||||
let { data } = args;
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 2. Execute field-level hooks, access, and validation
|
||||
// 3. Execute before validate collection hooks
|
||||
// /////////////////////////////////////
|
||||
|
||||
await collectionConfig.hooks.beforeValidate.reduce(async (priorHook, hook) => {
|
||||
await priorHook;
|
||||
|
||||
data = (await hook({
|
||||
data,
|
||||
req,
|
||||
operation: 'update',
|
||||
originalDoc,
|
||||
})) || data;
|
||||
}, Promise.resolve());
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 4. Execute field-level hooks, access, and validation
|
||||
// /////////////////////////////////////
|
||||
|
||||
data = await this.performFieldOperations(collectionConfig, {
|
||||
data,
|
||||
req,
|
||||
hook: 'beforeUpdate',
|
||||
operationName: 'update',
|
||||
hook: 'beforeChange',
|
||||
operation: 'update',
|
||||
originalDoc,
|
||||
});
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 3. Execute before update hook
|
||||
// 5. Execute before update hook
|
||||
// /////////////////////////////////////
|
||||
|
||||
await collectionConfig.hooks.beforeUpdate.reduce(async (priorHook, hook) => {
|
||||
await collectionConfig.hooks.beforeChange.reduce(async (priorHook, hook) => {
|
||||
await priorHook;
|
||||
|
||||
data = (await hook({
|
||||
data,
|
||||
req,
|
||||
originalDoc,
|
||||
operation: 'update',
|
||||
})) || data;
|
||||
}, Promise.resolve());
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 4. Merge updates into existing data
|
||||
// 6. Merge updates into existing data
|
||||
// /////////////////////////////////////
|
||||
|
||||
data = deepmerge(originalDoc, data, { arrayMerge: overwriteMerge });
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 5. Handle password update
|
||||
// 7. Handle password update
|
||||
// /////////////////////////////////////
|
||||
|
||||
const dataToUpdate = { ...data };
|
||||
@@ -98,7 +112,7 @@ async function update(args) {
|
||||
}
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 6. Perform database operation
|
||||
// 8. Perform database operation
|
||||
// /////////////////////////////////////
|
||||
|
||||
Object.assign(user, dataToUpdate);
|
||||
@@ -108,32 +122,33 @@ async function update(args) {
|
||||
user = user.toJSON({ virtuals: true });
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 7. Execute field-level hooks and access
|
||||
// 9. Execute field-level hooks and access
|
||||
// /////////////////////////////////////
|
||||
|
||||
user = this.performFieldOperations(collectionConfig, {
|
||||
data: user,
|
||||
hook: 'afterRead',
|
||||
operationName: 'read',
|
||||
operation: 'read',
|
||||
req,
|
||||
depth,
|
||||
});
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 8. Execute after update hook
|
||||
// 10. Execute after update hook
|
||||
// /////////////////////////////////////
|
||||
|
||||
await collectionConfig.hooks.afterUpdate.reduce(async (priorHook, hook) => {
|
||||
await collectionConfig.hooks.afterChange.reduce(async (priorHook, hook) => {
|
||||
await priorHook;
|
||||
|
||||
user = await hook({
|
||||
doc: user,
|
||||
req,
|
||||
operation: 'update',
|
||||
}) || user;
|
||||
}, Promise.resolve());
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 9. Return user
|
||||
// 11. Return user
|
||||
// /////////////////////////////////////
|
||||
|
||||
return user;
|
||||
|
||||
@@ -33,31 +33,46 @@ async function create(args) {
|
||||
await executeAccess({ req }, collectionConfig.access.create);
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 2. Execute field-level access, hooks, and validation
|
||||
// 2. Execute before validate collection hooks
|
||||
// /////////////////////////////////////
|
||||
|
||||
data = await performFieldOperations(collectionConfig, {
|
||||
data,
|
||||
hook: 'beforeCreate',
|
||||
operationName: 'create',
|
||||
req,
|
||||
});
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 3. Execute before collection hook
|
||||
// /////////////////////////////////////
|
||||
|
||||
await collectionConfig.hooks.beforeCreate.reduce(async (priorHook, hook) => {
|
||||
await collectionConfig.hooks.beforeValidate.reduce(async (priorHook, hook) => {
|
||||
await priorHook;
|
||||
|
||||
data = (await hook({
|
||||
data,
|
||||
req,
|
||||
operation: 'create',
|
||||
})) || data;
|
||||
}, Promise.resolve());
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 4. Upload and resize any files that may be present
|
||||
// 3. Execute field-level access, beforeChange hooks, and validation
|
||||
// /////////////////////////////////////
|
||||
|
||||
data = await performFieldOperations(collectionConfig, {
|
||||
data,
|
||||
hook: 'beforeChange',
|
||||
operation: 'create',
|
||||
req,
|
||||
});
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 4. Execute before change hooks
|
||||
// /////////////////////////////////////
|
||||
|
||||
await collectionConfig.hooks.beforeChange.reduce(async (priorHook, hook) => {
|
||||
await priorHook;
|
||||
|
||||
data = (await hook({
|
||||
data,
|
||||
req,
|
||||
operation: 'create',
|
||||
})) || data;
|
||||
}, Promise.resolve());
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 5. Upload and resize any files that may be present
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.upload) {
|
||||
@@ -98,7 +113,7 @@ async function create(args) {
|
||||
}
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 5. Perform database operation
|
||||
// 6. Perform database operation
|
||||
// /////////////////////////////////////
|
||||
|
||||
let result = new Model();
|
||||
@@ -113,32 +128,33 @@ async function create(args) {
|
||||
result = result.toJSON({ virtuals: true });
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 6. Execute field-level hooks and access
|
||||
// 7. Execute field-level hooks and access
|
||||
// /////////////////////////////////////
|
||||
|
||||
result = await performFieldOperations(collectionConfig, {
|
||||
data: result,
|
||||
hook: 'afterRead',
|
||||
operationName: 'read',
|
||||
operation: 'read',
|
||||
req,
|
||||
depth,
|
||||
});
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 7. Execute after collection hook
|
||||
// 8. Execute after collection hook
|
||||
// /////////////////////////////////////
|
||||
|
||||
await collectionConfig.hooks.afterCreate.reduce(async (priorHook, hook) => {
|
||||
await collectionConfig.hooks.afterChange.reduce(async (priorHook, hook) => {
|
||||
await priorHook;
|
||||
|
||||
result = await hook({
|
||||
doc: result,
|
||||
req: args.req,
|
||||
operation: 'create',
|
||||
}) || result;
|
||||
}, Promise.resolve());
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 8. Return results
|
||||
// 9. Return results
|
||||
// /////////////////////////////////////
|
||||
|
||||
return result;
|
||||
|
||||
@@ -97,7 +97,7 @@ async function deleteQuery(args) {
|
||||
result = await this.performFieldOperations(collectionConfig, {
|
||||
data: result,
|
||||
hook: 'afterRead',
|
||||
operationName: 'read',
|
||||
operation: 'read',
|
||||
req,
|
||||
depth,
|
||||
});
|
||||
|
||||
@@ -99,7 +99,7 @@ async function find(args) {
|
||||
data,
|
||||
req,
|
||||
hook: 'afterRead',
|
||||
operationName: 'read',
|
||||
operation: 'read',
|
||||
overrideAccess,
|
||||
},
|
||||
find,
|
||||
|
||||
@@ -81,7 +81,7 @@ async function findByID(args) {
|
||||
req,
|
||||
data: result,
|
||||
hook: 'afterRead',
|
||||
operationName: 'read',
|
||||
operation: 'read',
|
||||
currentDepth,
|
||||
});
|
||||
|
||||
|
||||
@@ -63,42 +63,58 @@ async function update(args) {
|
||||
|
||||
const originalDoc = doc.toJSON({ virtuals: true });
|
||||
|
||||
let { data } = args;
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 2. Execute field-level hooks, access, and validation
|
||||
// 3. Execute before validate collection hooks
|
||||
// /////////////////////////////////////
|
||||
|
||||
let { data } = args;
|
||||
await collectionConfig.hooks.beforeValidate.reduce(async (priorHook, hook) => {
|
||||
await priorHook;
|
||||
|
||||
data = (await hook({
|
||||
data,
|
||||
req,
|
||||
operation: 'update',
|
||||
originalDoc,
|
||||
})) || data;
|
||||
}, Promise.resolve());
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 4. Execute field-level hooks, access, and validation
|
||||
// /////////////////////////////////////
|
||||
|
||||
data = await this.performFieldOperations(collectionConfig, {
|
||||
data,
|
||||
req,
|
||||
originalDoc,
|
||||
hook: 'beforeUpdate',
|
||||
operationName: 'update',
|
||||
hook: 'beforeChange',
|
||||
operation: 'update',
|
||||
});
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 3. Execute before update hook
|
||||
// 5. Execute before update hook
|
||||
// /////////////////////////////////////
|
||||
|
||||
await collectionConfig.hooks.beforeUpdate.reduce(async (priorHook, hook) => {
|
||||
await collectionConfig.hooks.beforeChange.reduce(async (priorHook, hook) => {
|
||||
await priorHook;
|
||||
|
||||
data = (await hook({
|
||||
data,
|
||||
req,
|
||||
originalDoc,
|
||||
operation: 'update',
|
||||
})) || data;
|
||||
}, Promise.resolve());
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 4. Merge updates into existing data
|
||||
// 6. Merge updates into existing data
|
||||
// /////////////////////////////////////
|
||||
|
||||
data = deepmerge(originalDoc, data, { arrayMerge: overwriteMerge });
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 5. Upload and resize any files that may be present
|
||||
// 7. Upload and resize any files that may be present
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.upload) {
|
||||
@@ -141,7 +157,7 @@ async function update(args) {
|
||||
}
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 6. Perform database operation
|
||||
// 8. Perform database operation
|
||||
// /////////////////////////////////////
|
||||
|
||||
Object.assign(doc, data);
|
||||
@@ -151,32 +167,33 @@ async function update(args) {
|
||||
doc = doc.toJSON({ virtuals: true });
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 7. Execute field-level hooks and access
|
||||
// 9. Execute field-level hooks and access
|
||||
// /////////////////////////////////////
|
||||
|
||||
doc = await this.performFieldOperations(collectionConfig, {
|
||||
data: doc,
|
||||
hook: 'afterRead',
|
||||
operationName: 'read',
|
||||
operation: 'read',
|
||||
req,
|
||||
depth,
|
||||
});
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 8. Execute after collection hook
|
||||
// 10. Execute after collection hook
|
||||
// /////////////////////////////////////
|
||||
|
||||
await collectionConfig.hooks.afterUpdate.reduce(async (priorHook, hook) => {
|
||||
await collectionConfig.hooks.afterChange.reduce(async (priorHook, hook) => {
|
||||
await priorHook;
|
||||
|
||||
doc = await hook({
|
||||
doc,
|
||||
req,
|
||||
operation: 'update',
|
||||
}) || doc;
|
||||
}, Promise.resolve());
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 9. Return updated document
|
||||
// 11. Return updated document
|
||||
// /////////////////////////////////////
|
||||
|
||||
return doc;
|
||||
|
||||
@@ -406,8 +406,8 @@ describe('Collections - REST', () => {
|
||||
|
||||
expect(getResponse.status).toBe(200);
|
||||
expect(data.docs).toHaveLength(2);
|
||||
expect(data.docs[0].id).toEqual(id1);
|
||||
expect(data.docs[1].id).toEqual(id2);
|
||||
expect(data.docs[0].id).toStrictEqual(id1);
|
||||
expect(data.docs[1].id).toStrictEqual(id2);
|
||||
|
||||
// Query on shared desc and sort descending
|
||||
const getResponseSorted = await fetch(`${url}/api/localized-posts?where[description][equals]=${desc}&sort=-title`);
|
||||
@@ -416,14 +416,14 @@ describe('Collections - REST', () => {
|
||||
expect(getResponse.status).toBe(200);
|
||||
expect(sortedData.docs).toHaveLength(2);
|
||||
// Opposite order from first request
|
||||
expect(sortedData.docs[0].id).toEqual(id2);
|
||||
expect(sortedData.docs[1].id).toEqual(id1);
|
||||
expect(sortedData.docs[0].id).toStrictEqual(id2);
|
||||
expect(sortedData.docs[1].id).toStrictEqual(id1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Hooks', () => {
|
||||
describe('Before', () => {
|
||||
it('beforeCreate', async () => {
|
||||
it('beforeChange', async () => {
|
||||
const response = await fetch(`${url}/api/hooks`, {
|
||||
body: JSON.stringify({
|
||||
title: faker.name.firstName(),
|
||||
@@ -433,48 +433,13 @@ describe('Collections - REST', () => {
|
||||
headers: {
|
||||
Authorization: `JWT ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
hook: 'beforeCreate', // Used by hook
|
||||
hook: 'beforeChange', // Used by hook
|
||||
},
|
||||
method: 'post',
|
||||
});
|
||||
const data = await response.json();
|
||||
expect(response.status).toBe(201);
|
||||
expect(data.doc.description).toEqual('Original-beforeCreateSuffix');
|
||||
});
|
||||
|
||||
it('beforeUpdate', async () => {
|
||||
const createResponse = await fetch(`${url}/api/hooks`, {
|
||||
body: JSON.stringify({
|
||||
title: faker.name.firstName(),
|
||||
description: 'Original',
|
||||
priority: 1,
|
||||
}),
|
||||
headers: {
|
||||
Authorization: `JWT ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method: 'post',
|
||||
});
|
||||
const createData = await createResponse.json();
|
||||
expect(createResponse.status).toBe(201);
|
||||
const { id } = createData.doc;
|
||||
|
||||
const response = await fetch(`${url}/api/hooks/${id}`, {
|
||||
body: JSON.stringify({
|
||||
description: 'Updated',
|
||||
}),
|
||||
headers: {
|
||||
Authorization: `JWT ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
hook: 'beforeUpdate', // Used by hook
|
||||
|
||||
},
|
||||
method: 'put',
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
expect(response.status).toBe(200);
|
||||
expect(data.doc.description).toEqual('Updated-beforeUpdateSuffix');
|
||||
expect(data.doc.description).toStrictEqual('Original-beforeChangeSuffix');
|
||||
});
|
||||
|
||||
it('beforeDelete', async () => {
|
||||
@@ -505,30 +470,11 @@ describe('Collections - REST', () => {
|
||||
const data = await response.json();
|
||||
expect(response.status).toBe(200);
|
||||
// Intentionally afterDeleteHook - beforeDelete hook is setting header in order to trigger afterDelete hook
|
||||
expect(data.afterDeleteHook).toEqual(true);
|
||||
expect(data.afterDeleteHook).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('After', () => {
|
||||
it('afterCreate', async () => {
|
||||
const response = await fetch(`${url}/api/hooks`, {
|
||||
body: JSON.stringify({
|
||||
title: faker.name.firstName(),
|
||||
description: 'Original',
|
||||
priority: 1,
|
||||
}),
|
||||
headers: {
|
||||
Authorization: `JWT ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
hook: 'afterCreate', // Used by hook
|
||||
},
|
||||
method: 'post',
|
||||
});
|
||||
const data = await response.json();
|
||||
expect(response.status).toBe(201);
|
||||
expect(data.doc.afterCreateHook).toEqual(true);
|
||||
});
|
||||
|
||||
it('afterRead', async () => {
|
||||
const response = await fetch(`${url}/api/hooks`, {
|
||||
body: JSON.stringify({
|
||||
@@ -547,10 +493,10 @@ describe('Collections - REST', () => {
|
||||
const getResponse = await fetch(`${url}/api/hooks/${data.doc.id}`);
|
||||
const getResponseData = await getResponse.json();
|
||||
expect(getResponse.status).toBe(200);
|
||||
expect(getResponseData.afterReadHook).toEqual(true);
|
||||
expect(getResponseData.afterReadHook).toStrictEqual(true);
|
||||
});
|
||||
|
||||
it('afterUpdate', async () => {
|
||||
it('afterChange', async () => {
|
||||
const createResponse = await fetch(`${url}/api/hooks`, {
|
||||
body: JSON.stringify({
|
||||
title: faker.name.firstName(),
|
||||
@@ -568,19 +514,19 @@ describe('Collections - REST', () => {
|
||||
|
||||
const response = await fetch(`${url}/api/hooks/${id}`, {
|
||||
body: JSON.stringify({
|
||||
description: 'afterUpdate',
|
||||
description: 'afterChange',
|
||||
}),
|
||||
headers: {
|
||||
Authorization: `JWT ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
hook: 'afterUpdate', // Used by hook
|
||||
hook: 'afterChange', // Used by hook
|
||||
},
|
||||
method: 'put',
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
expect(response.status).toBe(200);
|
||||
expect(data.doc.afterUpdateHook).toEqual(true);
|
||||
expect(data.doc.afterChangeHook).toStrictEqual(true);
|
||||
});
|
||||
|
||||
it('afterDelete', async () => {
|
||||
@@ -610,7 +556,7 @@ describe('Collections - REST', () => {
|
||||
|
||||
const data = await response.json();
|
||||
expect(response.status).toBe(200);
|
||||
expect(data.afterDeleteHook).toEqual(true);
|
||||
expect(data.afterDeleteHook).toStrictEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -77,10 +77,9 @@ const sanitizeCollection = (collections, collection) => {
|
||||
if (!sanitized.access) sanitized.access = {};
|
||||
if (!sanitized.admin) sanitized.admin = {};
|
||||
|
||||
if (!sanitized.hooks.beforeCreate) sanitized.hooks.beforeCreate = [];
|
||||
if (!sanitized.hooks.afterCreate) sanitized.hooks.afterCreate = [];
|
||||
if (!sanitized.hooks.beforeUpdate) sanitized.hooks.beforeUpdate = [];
|
||||
if (!sanitized.hooks.afterUpdate) sanitized.hooks.afterUpdate = [];
|
||||
if (!sanitized.hooks.beforeValidate) sanitized.hooks.beforeValidate = [];
|
||||
if (!sanitized.hooks.beforeChange) sanitized.hooks.beforeChange = [];
|
||||
if (!sanitized.hooks.afterChange) sanitized.hooks.afterChange = [];
|
||||
if (!sanitized.hooks.beforeRead) sanitized.hooks.beforeRead = [];
|
||||
if (!sanitized.hooks.afterRead) sanitized.hooks.afterRead = [];
|
||||
if (!sanitized.hooks.beforeDelete) sanitized.hooks.beforeDelete = [];
|
||||
@@ -103,10 +102,14 @@ const sanitizeCollection = (collections, collection) => {
|
||||
name: 'filename',
|
||||
label: 'Filename',
|
||||
hooks: {
|
||||
beforeCreate: [
|
||||
({ req }) => {
|
||||
const file = (req.files && req.files.file) ? req.files.file : req.file;
|
||||
return file.name;
|
||||
beforeChange: [
|
||||
({ req, operation, value }) => {
|
||||
if (operation === 'create') {
|
||||
const file = (req.files && req.files.file) ? req.files.file : req.file;
|
||||
return file.name;
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
const { ValidationError } = require('../errors');
|
||||
const executeAccess = require('../auth/executeAccess');
|
||||
|
||||
async function performFieldOperations(entityConfig, operation) {
|
||||
async function performFieldOperations(entityConfig, args) {
|
||||
const {
|
||||
data: fullData,
|
||||
originalDoc: fullOriginalDoc,
|
||||
operationName,
|
||||
operation,
|
||||
hook,
|
||||
req,
|
||||
req: {
|
||||
payloadAPI,
|
||||
},
|
||||
overrideAccess,
|
||||
} = operation;
|
||||
} = args;
|
||||
|
||||
const recursivePerformFieldOperations = performFieldOperations.bind(this);
|
||||
|
||||
let depth = 0;
|
||||
|
||||
if (payloadAPI === 'REST') {
|
||||
depth = (operation.depth || operation.depth === 0) ? parseInt(operation.depth, 10) : this.config.defaultDepth;
|
||||
depth = (args.depth || args.depth === 0) ? parseInt(args.depth, 10) : this.config.defaultDepth;
|
||||
}
|
||||
|
||||
const currentDepth = operation.currentDepth || 1;
|
||||
const currentDepth = args.currentDepth || 1;
|
||||
|
||||
const populateRelationship = async (dataReference, data, field, i) => {
|
||||
const dataToUpdate = dataReference;
|
||||
@@ -116,10 +116,10 @@ async function performFieldOperations(entityConfig, operation) {
|
||||
const createAccessPromise = async (data, originalDoc, field) => {
|
||||
const resultingData = data;
|
||||
|
||||
if (field.access && field.access[operationName]) {
|
||||
const result = await field.access[operationName]({ req });
|
||||
if (field.access && field.access[operation]) {
|
||||
const result = await field.access[operation]({ req });
|
||||
|
||||
if (!result && operationName === 'update' && originalDoc[field.name] !== undefined) {
|
||||
if (!result && operation === 'update' && originalDoc[field.name] !== undefined) {
|
||||
resultingData[field.name] = originalDoc[field.name];
|
||||
} else if (!result) {
|
||||
delete resultingData[field.name];
|
||||
@@ -175,7 +175,7 @@ async function performFieldOperations(entityConfig, operation) {
|
||||
req,
|
||||
data: relatedDocumentData,
|
||||
hook: 'afterRead',
|
||||
operationName: 'read',
|
||||
operation: 'read',
|
||||
});
|
||||
|
||||
await relatedCollection.hooks.afterRead.reduce(async (priorHook, currentHook) => {
|
||||
@@ -221,7 +221,7 @@ async function performFieldOperations(entityConfig, operation) {
|
||||
req,
|
||||
data: relatedDocumentData,
|
||||
hook: 'afterRead',
|
||||
operationName: 'read',
|
||||
operation: 'read',
|
||||
});
|
||||
|
||||
await relatedCollection.hooks.afterRead.reduce(async (priorHook, currentHook) => {
|
||||
@@ -285,7 +285,7 @@ async function performFieldOperations(entityConfig, operation) {
|
||||
}
|
||||
}
|
||||
|
||||
if (operationName === 'create' || operationName === 'update') {
|
||||
if (operation === 'create' || operation === 'update') {
|
||||
if (field.type === 'array' || field.type === 'blocks') {
|
||||
const hasRowsOfNewData = Array.isArray(data[field.name]);
|
||||
const newRowCount = hasRowsOfNewData ? data[field.name].length : 0;
|
||||
|
||||
@@ -50,7 +50,7 @@ async function findOne(args) {
|
||||
doc = this.performFieldOperations(globalConfig, {
|
||||
data: doc,
|
||||
hook: 'afterRead',
|
||||
operationName: 'read',
|
||||
operation: 'read',
|
||||
req,
|
||||
depth,
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@ const overwriteMerge = require('../../utilities/overwriteMerge');
|
||||
const executeAccess = require('../../auth/executeAccess');
|
||||
|
||||
async function update(args) {
|
||||
const { config, globals: { Model } } = this;
|
||||
const { globals: { Model } } = this;
|
||||
|
||||
const {
|
||||
globalConfig,
|
||||
@@ -38,42 +38,57 @@ async function update(args) {
|
||||
|
||||
const globalJSON = global.toJSON({ virtuals: true });
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 3. Execute before global hook
|
||||
// /////////////////////////////////////
|
||||
|
||||
let { data } = args;
|
||||
|
||||
await globalConfig.hooks.beforeUpdate.reduce(async (priorHook, hook) => {
|
||||
// /////////////////////////////////////
|
||||
// 3. Execute before validate collection hooks
|
||||
// /////////////////////////////////////
|
||||
|
||||
await globalConfig.hooks.beforeValidate.reduce(async (priorHook, hook) => {
|
||||
await priorHook;
|
||||
|
||||
data = (await hook({
|
||||
data,
|
||||
req,
|
||||
operation: 'update',
|
||||
})) || data;
|
||||
}, Promise.resolve());
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 4. Execute field-level hooks, access, and validation
|
||||
// /////////////////////////////////////
|
||||
|
||||
data = await this.performFieldOperations(globalConfig, {
|
||||
data,
|
||||
req,
|
||||
hook: 'beforeChange',
|
||||
operation: 'update',
|
||||
originalDoc: global,
|
||||
});
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 5. Execute before global hook
|
||||
// /////////////////////////////////////
|
||||
|
||||
await globalConfig.hooks.beforeChange.reduce(async (priorHook, hook) => {
|
||||
await priorHook;
|
||||
|
||||
data = (await hook({
|
||||
data,
|
||||
req,
|
||||
originalDoc: global,
|
||||
operation: 'update',
|
||||
})) || data;
|
||||
}, Promise.resolve());
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 4. Merge updates into existing data
|
||||
// 6. Merge updates into existing data
|
||||
// /////////////////////////////////////
|
||||
|
||||
data = deepmerge(globalJSON, data, { arrayMerge: overwriteMerge });
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 5. Execute field-level hooks, access, and validation
|
||||
// /////////////////////////////////////
|
||||
|
||||
data = await this.performFieldOperations(globalConfig, {
|
||||
data,
|
||||
req,
|
||||
hook: 'beforeUpdate',
|
||||
operationName: 'update',
|
||||
originalDoc: globalJSON,
|
||||
});
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 6. Perform database operation
|
||||
// 7. Perform database operation
|
||||
// /////////////////////////////////////
|
||||
|
||||
Object.assign(global, data);
|
||||
@@ -83,32 +98,33 @@ async function update(args) {
|
||||
global = global.toJSON({ virtuals: true });
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 7. Execute field-level hooks and access
|
||||
// 8. Execute field-level hooks and access
|
||||
// /////////////////////////////////////
|
||||
|
||||
global = await this.performFieldOperations(globalConfig, {
|
||||
data: global,
|
||||
hook: 'afterRead',
|
||||
operationName: 'read',
|
||||
operation: 'read',
|
||||
req,
|
||||
depth,
|
||||
});
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 8. Execute after global hook
|
||||
// 9. Execute after global hook
|
||||
// /////////////////////////////////////
|
||||
|
||||
await globalConfig.hooks.afterUpdate.reduce(async (priorHook, hook) => {
|
||||
await globalConfig.hooks.afterChange.reduce(async (priorHook, hook) => {
|
||||
await priorHook;
|
||||
|
||||
global = await hook({
|
||||
doc: global,
|
||||
req,
|
||||
operation: 'update',
|
||||
}) || global;
|
||||
}, Promise.resolve());
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 9. Return global
|
||||
// 10. Return global
|
||||
// /////////////////////////////////////
|
||||
|
||||
return global;
|
||||
|
||||
@@ -23,8 +23,9 @@ const sanitizeGlobals = (globals) => {
|
||||
if (!sanitizedGlobal.access) sanitizedGlobal.access = {};
|
||||
if (!sanitizedGlobal.admin) sanitizedGlobal.admin = {};
|
||||
|
||||
if (!sanitizedGlobal.hooks.beforeUpdate) sanitizedGlobal.hooks.beforeUpdate = [];
|
||||
if (!sanitizedGlobal.hooks.afterUpdate) sanitizedGlobal.hooks.afterUpdate = [];
|
||||
if (!sanitizedGlobal.hooks.beforeValidate) sanitizedGlobal.hooks.beforeValidate = [];
|
||||
if (!sanitizedGlobal.hooks.beforeChange) sanitizedGlobal.hooks.beforeChange = [];
|
||||
if (!sanitizedGlobal.hooks.afterChange) sanitizedGlobal.hooks.afterChange = [];
|
||||
if (!sanitizedGlobal.hooks.beforeRead) sanitizedGlobal.hooks.beforeRead = [];
|
||||
if (!sanitizedGlobal.hooks.afterRead) sanitizedGlobal.hooks.afterRead = [];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user