From 16b9aab78582705801db5772d9c45aec7b7264bd Mon Sep 17 00:00:00 2001 From: James Date: Wed, 29 Jul 2020 19:19:15 -0400 Subject: [PATCH] modifies create and update hooks to 'change' instead --- demo/collections/Hooks.js | 28 ++----- src/auth/operations/register.js | 55 ++++++++----- src/auth/operations/update.js | 45 ++++++---- src/collections/operations/create.js | 56 ++++++++----- src/collections/operations/delete.js | 2 +- src/collections/operations/find.js | 2 +- src/collections/operations/findByID.js | 2 +- src/collections/operations/update.js | 45 ++++++---- .../requestHandlers/collections.spec.js | 82 ++++--------------- src/collections/sanitize.js | 19 +++-- src/fields/performFieldOperations.js | 22 ++--- src/globals/operations/findOne.js | 2 +- src/globals/operations/update.js | 66 +++++++++------ src/globals/sanitize.js | 5 +- 14 files changed, 223 insertions(+), 208 deletions(-) diff --git a/demo/collections/Hooks.js b/demo/collections/Hooks.js index 71b0f2984..404c24210 100644 --- a/demo/collections/Hooks.js +++ b/demo/collections/Hooks.js @@ -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; }, diff --git a/src/auth/operations/register.js b/src/auth/operations/register.js index a67bda466..e08487d41 100644 --- a/src/auth/operations/register.js +++ b/src/auth/operations/register.js @@ -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; diff --git a/src/auth/operations/update.js b/src/auth/operations/update.js index 49e39e6bf..79651717c 100644 --- a/src/auth/operations/update.js +++ b/src/auth/operations/update.js @@ -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; diff --git a/src/collections/operations/create.js b/src/collections/operations/create.js index b181d1fd7..bd151ebf9 100644 --- a/src/collections/operations/create.js +++ b/src/collections/operations/create.js @@ -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; diff --git a/src/collections/operations/delete.js b/src/collections/operations/delete.js index 784e6964e..0787cebbd 100644 --- a/src/collections/operations/delete.js +++ b/src/collections/operations/delete.js @@ -97,7 +97,7 @@ async function deleteQuery(args) { result = await this.performFieldOperations(collectionConfig, { data: result, hook: 'afterRead', - operationName: 'read', + operation: 'read', req, depth, }); diff --git a/src/collections/operations/find.js b/src/collections/operations/find.js index 41b71f236..5127fc74b 100644 --- a/src/collections/operations/find.js +++ b/src/collections/operations/find.js @@ -99,7 +99,7 @@ async function find(args) { data, req, hook: 'afterRead', - operationName: 'read', + operation: 'read', overrideAccess, }, find, diff --git a/src/collections/operations/findByID.js b/src/collections/operations/findByID.js index 92bc080a5..bc7e37bab 100644 --- a/src/collections/operations/findByID.js +++ b/src/collections/operations/findByID.js @@ -81,7 +81,7 @@ async function findByID(args) { req, data: result, hook: 'afterRead', - operationName: 'read', + operation: 'read', currentDepth, }); diff --git a/src/collections/operations/update.js b/src/collections/operations/update.js index e1800c419..0300664c0 100644 --- a/src/collections/operations/update.js +++ b/src/collections/operations/update.js @@ -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; diff --git a/src/collections/requestHandlers/collections.spec.js b/src/collections/requestHandlers/collections.spec.js index 03855c390..113282ae3 100644 --- a/src/collections/requestHandlers/collections.spec.js +++ b/src/collections/requestHandlers/collections.spec.js @@ -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); }); }); }); diff --git a/src/collections/sanitize.js b/src/collections/sanitize.js index 81eefb6f1..92e82aeaa 100644 --- a/src/collections/sanitize.js +++ b/src/collections/sanitize.js @@ -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; }, ], }, diff --git a/src/fields/performFieldOperations.js b/src/fields/performFieldOperations.js index 1e341b6e4..1ece97f54 100644 --- a/src/fields/performFieldOperations.js +++ b/src/fields/performFieldOperations.js @@ -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; diff --git a/src/globals/operations/findOne.js b/src/globals/operations/findOne.js index bd8f9f622..1c7145238 100644 --- a/src/globals/operations/findOne.js +++ b/src/globals/operations/findOne.js @@ -50,7 +50,7 @@ async function findOne(args) { doc = this.performFieldOperations(globalConfig, { data: doc, hook: 'afterRead', - operationName: 'read', + operation: 'read', req, depth, }); diff --git a/src/globals/operations/update.js b/src/globals/operations/update.js index 0e0f5a74e..91e839102 100644 --- a/src/globals/operations/update.js +++ b/src/globals/operations/update.js @@ -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; diff --git a/src/globals/sanitize.js b/src/globals/sanitize.js index 6990a4333..3792610b8 100644 --- a/src/globals/sanitize.js +++ b/src/globals/sanitize.js @@ -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 = [];