diff --git a/demo/collections/Page/index.js b/demo/collections/Page/index.js index 1a5b7d7650..768843fa89 100644 --- a/demo/collections/Page/index.js +++ b/demo/collections/Page/index.js @@ -101,7 +101,7 @@ module.exports = { return (siblings.cardNumber && siblings.cardNumber.value); }, hooks: { - afterRead: value => `hooked value - ${value}`, + afterRead: operation => operation.value, }, }, ], @@ -139,7 +139,7 @@ module.exports = { width: 50, required: true, hooks: { - afterRead: value => `hooked value - ${value}`, + afterRead: operation => operation.value, }, }, { diff --git a/demo/collections/Post/index.js b/demo/collections/Post/index.js index c5d304ddd0..871eb22f1f 100644 --- a/demo/collections/Post/index.js +++ b/demo/collections/Post/index.js @@ -31,7 +31,11 @@ module.exports = { return operation; }, afterCreate: (operation, value) => value, - afterRead: (operation, value) => value, + afterRead: (operation) => { + const { json } = operation; + json.title += ' and this'; + json.extra = 'afterRead Hook data'; + }, afterUpdate: (operation, value) => value, afterDelete: (operation, value) => { console.log(`Deleted ${operation.query._id}`); @@ -49,9 +53,9 @@ module.exports = { unique: true, localized: true, hooks: { - beforeCreate: value => value, - beforeUpdate: value => value, - afterRead: value => value, + beforeCreate: operation => operation.value, + beforeUpdate: operation => operation.value, + afterRead: operation => operation.value, }, }, { diff --git a/demo/globals/Footer.js b/demo/globals/Footer.js index a47a594fe9..4c26b9bb8a 100644 --- a/demo/globals/Footer.js +++ b/demo/globals/Footer.js @@ -4,7 +4,7 @@ module.exports = { slug: 'footer', label: 'Footer', policies: { - upsert: ({ req: { user } }) => checkRole(['admin', 'user'], user), + update: ({ req: { user } }) => checkRole(['admin', 'user'], user), read: () => true, }, fields: [ diff --git a/demo/globals/Header.js b/demo/globals/Header.js index 0e36297f82..45ebbb1f41 100644 --- a/demo/globals/Header.js +++ b/demo/globals/Header.js @@ -6,7 +6,7 @@ module.exports = { slug: 'header', label: 'Header', policies: { - upsert: ({ req: { user } }) => checkRole(['admin', 'user'], user), + update: ({ req: { user } }) => checkRole(['admin', 'user'], user), read: () => true, }, fields: [ diff --git a/package.json b/package.json index 99c7b1df1c..1882836926 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "cov": "npm run core:build && node ./node_modules/jest/bin/jest.js src/tests --coverage", "dev": "nodemon demo/server.js", "lint": "eslint **/*.js", + "debug": "nodemon --inspect demo/server.js", "debug:test:int": "node --inspect-brk node_modules/.bin/jest --runInBand" }, "bin": { diff --git a/src/collections/operations/create.js b/src/collections/operations/create.js index 98e0bbc08e..b5a01a5836 100644 --- a/src/collections/operations/create.js +++ b/src/collections/operations/create.js @@ -22,7 +22,7 @@ const create = async (args) => { // 3. Execute before create field-level hooks // ///////////////////////////////////// - options.data = await executeFieldHooks(args.config.fields, args.data, 'beforeCreate'); + options.data = await executeFieldHooks(options, args.config.fields, args.data, 'beforeCreate', args.data); // ///////////////////////////////////// // 4. Execute before collection hook diff --git a/src/collections/operations/delete.js b/src/collections/operations/delete.js index 90a588c1a5..73076561f1 100644 --- a/src/collections/operations/delete.js +++ b/src/collections/operations/delete.js @@ -56,7 +56,7 @@ const deleteQuery = async (args) => { const { afterDelete } = args.config.hooks; if (typeof afterDelete === 'function') { - result = await afterDelete(options, result); + result = await afterDelete(options, result) || result; } // ///////////////////////////////////// diff --git a/src/collections/operations/find.js b/src/collections/operations/find.js index 1b24ca9588..e61da38e19 100644 --- a/src/collections/operations/find.js +++ b/src/collections/operations/find.js @@ -77,8 +77,7 @@ const find = async (args) => { if (locale && doc.setLocale) { doc.setLocale(locale, fallbackLocale); } - const hookedDoc = await executeFieldHooks(args.config.fields, doc, 'afterRead'); - return hookedDoc; + return executeFieldHooks(options, args.config.fields, doc, 'afterRead', doc); })), }; @@ -87,21 +86,30 @@ const find = async (args) => { // ///////////////////////////////////// const { afterRead } = args.config.hooks; + let afterReadResult = null; if (typeof afterRead === 'function') { - result = await afterRead(options, result); + afterReadResult = { + ...result, + docs: await Promise.all(result.docs.map(async (doc) => { + const json = doc.toJSON({ virtuals: true }); + return afterRead({ + options, + doc, + json, + }) || json; + })), + }; } // ///////////////////////////////////// // 6. Return results // ///////////////////////////////////// - result = { + return afterReadResult || { ...result, - docs: await Promise.all(result.docs.map(async doc => doc.toJSON({ virtuals: true }))), + docs: result.docs.map(doc => doc.toJSON({ virtuals: true })), }; - - return result; } catch (err) { throw err; } diff --git a/src/collections/operations/findByID.js b/src/collections/operations/findByID.js index f0698c5425..86cc1cec1a 100644 --- a/src/collections/operations/findByID.js +++ b/src/collections/operations/findByID.js @@ -58,7 +58,6 @@ const findByID = async (args) => { } } - let result = await Model.findOne(query, {}, queryOptionsToExecute); if (!result) throw new NotFound(); @@ -67,13 +66,13 @@ const findByID = async (args) => { result.setLocale(locale, fallbackLocale); } - result = result.toJSON({ virtuals: true }); + let json = result.toJSON({ virtuals: true }); // ///////////////////////////////////// // 4. Execute after collection field-level hooks // ///////////////////////////////////// - result = await executeFieldHooks(args.config.fields, result, 'afterRead'); + result = await executeFieldHooks(options, options.config.fields, result, 'afterRead', result); // ///////////////////////////////////// // 5. Execute after collection hook @@ -82,14 +81,18 @@ const findByID = async (args) => { const { afterRead } = args.config.hooks; if (typeof afterRead === 'function') { - result = await afterRead(options, result); + json = await afterRead({ + ...options, + result, + json, + }) || json; } // ///////////////////////////////////// // 6. Return results // ///////////////////////////////////// - return result; + return json; } catch (err) { throw err; } diff --git a/src/collections/operations/update.js b/src/collections/operations/update.js index 4c534e612d..bd3b8b4e5e 100644 --- a/src/collections/operations/update.js +++ b/src/collections/operations/update.js @@ -23,7 +23,7 @@ const update = async (args) => { // 3. Execute before update field-level hooks // ///////////////////////////////////// - options.data = await executeFieldHooks(args.config.fields, args.data, 'beforeUpdate'); + options.data = await executeFieldHooks(options, args.config.fields, args.data, 'beforeUpdate', args.data); // ///////////////////////////////////// // 4. Execute before collection hook diff --git a/src/fields/executeHooks.js b/src/fields/executeHooks.js index 1d9a8c2136..d21bbe931e 100644 --- a/src/fields/executeHooks.js +++ b/src/fields/executeHooks.js @@ -1,20 +1,21 @@ -const executeFieldHooks = async (fields, data, hookName) => { +const executeFieldHooks = async (operation, fields, value, hookName, data = null) => { + const fullData = value || data; if (Array.isArray(data)) { - const postHookData = await Promise.all(data.map(async (row) => { - const rowData = await executeFieldHooks(fields, row, hookName); - return rowData; + return Promise.all(data.map(async (row) => { + return executeFieldHooks(operation, fields, fullData, row, hookName); })); - - return postHookData; } - const postHookData = Object.create(data); const hookPromises = []; fields.forEach((field) => { - if (typeof field.hooks[hookName] === 'function' && data[field.name]) { + if (typeof field.hooks[hookName] === 'function' && fullData[field.name]) { const hookPromise = async () => { - postHookData[field.name] = await field.hooks[hookName](data[field.name]); + fullData[field.name] = await field.hooks[hookName]({ + ...operation, + data: fullData, + value: data[field.name], + }); }; hookPromises.push(hookPromise()); @@ -22,7 +23,7 @@ const executeFieldHooks = async (fields, data, hookName) => { if (field.fields && data[field.name]) { const hookPromise = async () => { - postHookData[field.name] = await executeFieldHooks(field.fields, data[field.name], hookName); + fullData[field.name] = await executeFieldHooks(operation, field.fields, fullData[field.name], hookName, data[field.name]); }; hookPromises.push(hookPromise()); @@ -31,7 +32,7 @@ const executeFieldHooks = async (fields, data, hookName) => { await Promise.all(hookPromises); - return postHookData; + return fullData; }; module.exports = executeFieldHooks; diff --git a/src/fields/schemaMap.js b/src/fields/schemaMap.js index f383b10e62..3480dfb918 100644 --- a/src/fields/schemaMap.js +++ b/src/fields/schemaMap.js @@ -3,6 +3,7 @@ const { Schema } = require('mongoose'); const formatBaseSchema = (field) => { return { hidden: field.hidden || false, + hide: field.hidden === 'api' || field.hidden === true, localized: field.localized || false, unique: field.unique || false, required: (field.required && !field.localized) || false, diff --git a/src/globals/graphql/register.js b/src/globals/graphql/register.js index f3ecfae011..64617c98cd 100644 --- a/src/globals/graphql/register.js +++ b/src/globals/graphql/register.js @@ -2,7 +2,7 @@ const { GraphQLNonNull } = require('graphql'); const formatName = require('../../graphql/utilities/formatName'); const { - findOne, upsert, + findOne, update, } = require('./resolvers'); function registerGlobals() { @@ -38,12 +38,12 @@ function registerGlobals() { resolve: findOne(this.globals.Model, global), }; - this.Mutation.fields[`upsert${formattedLabel}`] = { + this.Mutation.fields[`update${formattedLabel}`] = { type: global.graphQL.type, args: { data: { type: global.graphQL.mutationInputType }, }, - resolve: upsert(this.globals.Model, global), + resolve: update(this.globals.Model, global), }; }); } diff --git a/src/globals/graphql/resolvers/index.js b/src/globals/graphql/resolvers/index.js index 66cd182d20..511bbcb223 100644 --- a/src/globals/graphql/resolvers/index.js +++ b/src/globals/graphql/resolvers/index.js @@ -1,7 +1,7 @@ const findOne = require('./findOne'); -const upsert = require('./upsert'); +const update = require('./update'); module.exports = { findOne, - upsert, + update, }; diff --git a/src/globals/graphql/resolvers/upsert.js b/src/globals/graphql/resolvers/update.js similarity index 64% rename from src/globals/graphql/resolvers/upsert.js rename to src/globals/graphql/resolvers/update.js index 5195c7d41e..31f2512238 100644 --- a/src/globals/graphql/resolvers/upsert.js +++ b/src/globals/graphql/resolvers/update.js @@ -1,7 +1,7 @@ /* eslint-disable no-param-reassign */ -const { upsert } = require('../../operations'); +const { update } = require('../../operations'); -const upsertResolver = (Model, config) => async (_, args, context) => { +const updateResolver = (Model, config) => async (_, args, context) => { if (args.locale) context.locale = args.locale; if (args.fallbackLocale) context.fallbackLocale = args.fallbackLocale; @@ -16,9 +16,9 @@ const upsertResolver = (Model, config) => async (_, args, context) => { req: context, }; - const result = await upsert(options); + const result = await update(options); return result; }; -module.exports = upsertResolver; +module.exports = updateResolver; diff --git a/src/globals/operations/findOne.js b/src/globals/operations/findOne.js index 3ba78da2bf..06ec225814 100644 --- a/src/globals/operations/findOne.js +++ b/src/globals/operations/findOne.js @@ -62,13 +62,13 @@ const findOne = async (args) => { result.setLocale(locale, fallbackLocale); } - result = result.toJSON({ virtuals: true }); + let json = result.toJSON({ virtuals: true }); // ///////////////////////////////////// // 4. Execute after collection field-level hooks // ///////////////////////////////////// - result = await executeFieldHooks(args.config.fields, result, 'afterRead'); + result = await executeFieldHooks(options, args.config.fields, result, 'afterRead', result); // ///////////////////////////////////// // 5. Execute after collection hook @@ -77,14 +77,14 @@ const findOne = async (args) => { const { afterRead } = args.config.hooks; if (typeof afterRead === 'function') { - result = await afterRead(options, result); + json = await afterRead(options, result, json) || json; } // ///////////////////////////////////// // 6. Return results // ///////////////////////////////////// - return result; + return json; } catch (error) { throw error; } diff --git a/src/globals/operations/index.js b/src/globals/operations/index.js index 72f1041cc2..3a7c971df0 100644 --- a/src/globals/operations/index.js +++ b/src/globals/operations/index.js @@ -1,7 +1,7 @@ -const upsert = require('./upsert'); +const update = require('./update'); const findOne = require('./findOne'); module.exports = { findOne, - upsert, + update, }; diff --git a/src/globals/operations/upsert.js b/src/globals/operations/update.js similarity index 74% rename from src/globals/operations/upsert.js rename to src/globals/operations/update.js index 597a3ba1e7..0eeb8ad198 100644 --- a/src/globals/operations/upsert.js +++ b/src/globals/operations/update.js @@ -1,13 +1,13 @@ const executePolicy = require('../../users/executePolicy'); const executeFieldHooks = require('../../fields/executeHooks'); -const upsert = async (args) => { +const update = async (args) => { try { // ///////////////////////////////////// // 1. Retrieve and execute policy // ///////////////////////////////////// - await executePolicy(args, args.config.policies.upsert); + await executePolicy(args, args.config.policies.update); let options = { ...args }; @@ -17,16 +17,16 @@ const upsert = async (args) => { // 2. Execute before update field-level hooks // ///////////////////////////////////// - options.data = await executeFieldHooks(args.config.fields, args.data, 'beforeUpdate'); + options.data = await executeFieldHooks(options, args.config.fields, args.data, 'beforeUpdate', args.data); // ///////////////////////////////////// // 2. Execute before global hook // ///////////////////////////////////// - const { beforeUpsert } = args.config.hooks; + const { beforeUpdate } = args.config.hooks; - if (typeof beforeUpsert === 'function') { - options = await beforeUpsert(options); + if (typeof beforeUpdate === 'function') { + options = await beforeUpdate(options); } // ///////////////////////////////////// @@ -64,10 +64,10 @@ const upsert = async (args) => { // 4. Execute after global hook // ///////////////////////////////////// - const { afterUpsert } = args.config.hooks; + const { afterUpdate } = args.config.hooks; - if (typeof afterUpsert === 'function') { - result = await afterUpsert(options, result); + if (typeof afterUpdate === 'function') { + result = await afterUpdate(options, result); } // ///////////////////////////////////// @@ -80,4 +80,4 @@ const upsert = async (args) => { } }; -module.exports = upsert; +module.exports = update; diff --git a/src/globals/requestHandlers/index.js b/src/globals/requestHandlers/index.js index 66cd182d20..511bbcb223 100644 --- a/src/globals/requestHandlers/index.js +++ b/src/globals/requestHandlers/index.js @@ -1,7 +1,7 @@ const findOne = require('./findOne'); -const upsert = require('./upsert'); +const update = require('./update'); module.exports = { findOne, - upsert, + update, }; diff --git a/src/globals/requestHandlers/upsert.js b/src/globals/requestHandlers/update.js similarity index 73% rename from src/globals/requestHandlers/upsert.js rename to src/globals/requestHandlers/update.js index c2cf12f1fc..675a7b67e1 100644 --- a/src/globals/requestHandlers/upsert.js +++ b/src/globals/requestHandlers/update.js @@ -1,12 +1,12 @@ const httpStatus = require('http-status'); const formatErrorResponse = require('../../express/responses/formatError'); -const { upsert } = require('../operations'); +const { update } = require('../operations'); -const upsertHandler = (Model, config) => async (req, res) => { +const updateHandler = (Model, config) => async (req, res) => { try { const { slug } = config; - const result = await upsert({ + const result = await update({ req, Model, config, @@ -20,4 +20,4 @@ const upsertHandler = (Model, config) => async (req, res) => { } }; -module.exports = upsertHandler; +module.exports = updateHandler; diff --git a/src/globals/routes.js b/src/globals/routes.js index 3575cefbf3..c242f8c807 100644 --- a/src/globals/routes.js +++ b/src/globals/routes.js @@ -1,7 +1,7 @@ const express = require('express'); const requestHandlers = require('./requestHandlers'); -const { upsert, findOne } = requestHandlers; +const { update, findOne } = requestHandlers; const router = express.Router(); @@ -10,8 +10,8 @@ const registerGlobals = (globalConfigs, Globals) => { router .route(`/globals/${global.slug}`) .get(findOne(Globals, global)) - .post(upsert(Globals, global)) - .put(upsert(Globals, global)); + .post(update(Globals, global)) + .put(update(Globals, global)); }); return router; diff --git a/src/localization/plugin.js b/src/localization/plugin.js index 0c97c0fad6..e0ca4e87f6 100644 --- a/src/localization/plugin.js +++ b/src/localization/plugin.js @@ -77,7 +77,7 @@ module.exports = function localizationPlugin(schema, options) { .set(function (value) { // multiple locales are set as an object if (value && typeof value === 'object' && !Array.isArray(value)) { - const { locales } = this.schema.options.localization; + const { locales } = options; locales.forEach((locale) => { if (!value[locale]) { return; diff --git a/src/users/operations/register.js b/src/users/operations/register.js index 9131c996de..394e6cbdc9 100644 --- a/src/users/operations/register.js +++ b/src/users/operations/register.js @@ -25,7 +25,7 @@ const register = async (args) => { // 3. Execute before register field-level hooks // ///////////////////////////////////// - options.data = await executeFieldHooks(args.config.fields, args.data, 'beforeCreate'); + options.data = await executeFieldHooks(options, args.config.fields, args.data, 'beforeCreate'); // ///////////////////////////////////// // 4. Execute before register hook @@ -62,7 +62,6 @@ const register = async (args) => { Object.assign(user, modelData); let result = await Model.register(user, data.password); - result = result.toJSON({ virtuals: true }); await passport.authenticate('local'); @@ -79,8 +78,7 @@ const register = async (args) => { // ///////////////////////////////////// // 7. Return user // ///////////////////////////////////// - - return result; + return result.toJSON({ virtuals: true }); } catch (error) { throw error; }