diff --git a/.changeset/big-jeans-itch.md b/.changeset/big-jeans-itch.md new file mode 100644 index 00000000000..8c208836405 --- /dev/null +++ b/.changeset/big-jeans-itch.md @@ -0,0 +1,5 @@ +--- +'@keystonejs/keystone': minor +--- + +Added `listKey` as an argument to all hooks. diff --git a/docs/api/hooks.md b/docs/api/hooks.md index 690696343f9..bfbe65871f3 100644 --- a/docs/api/hooks.md +++ b/docs/api/hooks.md @@ -157,6 +157,7 @@ The result is passed to [the next function in the execution order](/docs/guides/ | `originalInput` | `Object` | The data received by the GraphQL mutation | | `resolvedData` | `Object` | The data received by the GraphQL mutation plus defaults values | | `context` | `Apollo Context` | The [Apollo `context` object](https://www.apollographql.com/docs/apollo-server/data/data/#context-argument) for this request | +| `listKey` | `String` | The key for the list being operated on | #### Usage @@ -169,6 +170,7 @@ const resolveInput = ({ originalInput, resolvedData, context, + listKey, }) => { // Input resolution logic. Object returned is used in place of `resolvedData`. return resolvedData; @@ -195,6 +197,7 @@ Return values are ignored. | `resolvedData` | `Object` | The data received by the GraphQL mutation plus defaults values | | `context` | `Apollo Context` | The [Apollo `context` object](https://www.apollographql.com/docs/apollo-server/data/data/#context-argument) for this request | | `addFieldValidationError` | `Function` | Used to set a field validation error; accepts a `String` | +| `listKey` | `String` | The key for the list being operated on | #### Usage @@ -208,6 +211,7 @@ const validateInput = ({ resolvedData, context, addFieldValidationError, + listKey, }) => { // Throw error objects or register validation errors with addFieldValidationError() // Return values ignored @@ -233,6 +237,7 @@ Return values are ignored. | `originalInput` | `Object` | The data received by the GraphQL mutation | | `resolvedData` | `Object` | The data received by the GraphQL mutation plus defaults values | | `context` | `Apollo Context` | The [Apollo `context` object](https://www.apollographql.com/docs/apollo-server/data/data/#context-argument) for this request | +| `listKey` | `String` | The key for the list being operated on | #### Usage @@ -245,6 +250,7 @@ const beforeChange = ({ originalInput, resolvedData, context, + listKey, }) => { // Perform side effects // Return values ignored @@ -274,6 +280,7 @@ Return values are ignored. | `originalInput` | `Object` | The data received by the GraphQL mutation | | `updatedItem` | `Object` | The new/currently stored item | | `context` | `Apollo Context` | The [Apollo `context` object](https://www.apollographql.com/docs/apollo-server/data/data/#context-argument) for this request | +| `listKey` | `String` | The key for the list being operated on | #### Usage @@ -286,6 +293,7 @@ const afterChange = ({ originalInput, updatedItem, context, + listKey, }) => { // Perform side effects // Return values ignored @@ -309,6 +317,7 @@ Should throw or register errors with `addFieldValidationError()` if the | `existingItem` | `Object` | The current stored item | | `context` | `Apollo Context` | The [Apollo `context` object](https://www.apollographql.com/docs/apollo-server/data/data/#context-argument) for this request | | `addFieldValidationError` | `Function` | Used to set a field validation error; accepts a `String` | +| `listKey` | `String` | The key for the list being operated on | #### Usage @@ -320,6 +329,7 @@ const validateDelete = ({ existingItem, context, addFieldValidationError, + listKey, }) => { // Throw error objects or register validation errors with addFieldValidationError() // Return values ignored @@ -343,6 +353,7 @@ Return values are ignored. | `operation` | `String` | The operation being performed (`delete` in this case) | | `existingItem` | `Object` | The current stored item | | `context` | `Apollo Context` | The [Apollo `context` object](https://www.apollographql.com/docs/apollo-server/data/data/#context-argument) for this request | +| `listKey` | `String` | The key for the list being operated on | #### Usage @@ -353,6 +364,7 @@ const beforeDelete = ({ operation, existingItem, context, + listKey, }) => { // Perform side effects // Return values ignored @@ -378,6 +390,7 @@ Return values are ignored. | `operation` | `String` | The operation being performed (`delete` in this case) | | `existingItem` | `Object` | The previously stored item, now deleted | | `context` | `Apollo Context` | The [Apollo `context` object](https://www.apollographql.com/docs/apollo-server/data/data/#context-argument) for this request | +| `listKey` | `String` | The key for the list being operated on | #### Usage @@ -388,6 +401,7 @@ const afterDelete = ({ operation, existingItem, context, + listKey, }) => { // Perform side effects // Return values ignored @@ -412,6 +426,7 @@ The result is passed to [the next function in the execution order](/docs/guides/ | `operation` | `String` | The operation being performed (`authenticate` in this case) | | `originalInput` | `Object` | The data received by the GraphQL mutation | | `context` | `Apollo Context` | The [Apollo `context` object](https://www.apollographql.com/docs/apollo-server/essentials/data.html#context) for this request | +| `listKey` | `String` | The key for the list being operated on | #### Usage @@ -422,6 +437,7 @@ const resolveAuthInput = ({ operation, originalInput, context, + listKey, }) => { // Input resolution logic // Object returned is used in place of resolvedData @@ -448,6 +464,7 @@ Return values are ignored. | `resolvedData` | `Object` | The data received by the GraphQL mutation or returned by `resolveAuthInput`, if defined | | `context` | `Apollo Context` | The [Apollo `context` object](https://www.apollographql.com/docs/apollo-server/essentials/data.html#context) for this request | | `addValidationError` | `Function` | Used to set a validation error; accepts a message `String` | +| `listKey` | `String` | The key for the list being operated on | #### Usage @@ -460,6 +477,7 @@ const validateAuthInput = ({ resolvedData, context, addFieldValidationError, + listKey, }) => { // Throw error objects or register validation errors with addValidationError() // Return values ignored @@ -484,6 +502,7 @@ Return values are ignored. | `originalInput` | `Object` | The data received by the GraphQL mutation | | `resolvedData` | `Object` | The data received by the GraphQL mutation or returned by `resolveAuthInput`, if defined | | `context` | `Apollo Context` | The [Apollo `context` object](https://www.apollographql.com/docs/apollo-server/essentials/data.html#context) for this request | +| `listKey` | `String` | The key for the list being operated on | #### Usage @@ -495,6 +514,7 @@ const beforeAuth = ({ originalInput, resolvedData, context, + listKey, }) => { // Perform side effects // Return values ignored @@ -525,6 +545,7 @@ Return values are ignored. | `originalInput` | `Object` | The data received by the GraphQL mutation | | `resolvedData` | `Object` | The data received by the GraphQL mutation or returned by `resolveAuthInput`, if defined | | `context` | `Apollo Context` | The [Apollo `context` object](https://www.apollographql.com/docs/apollo-server/essentials/data.html#context) for this request | +| `listKey` | `String` | The key for the list being operated on | #### Usage @@ -540,6 +561,7 @@ const afterAuth = ({ originalInput, resolvedData, context, + listKey, }) => { // Perform side effects // Return values ignored @@ -562,6 +584,7 @@ Return values are ignored. | :---------- | :--------------- | :---------------------------------------------------------------------------------------------------------------------------- | | `operation` | `String` | The operation being performed (`authenticate` in this case) | | `context` | `Apollo Context` | The [Apollo `context` object](https://www.apollographql.com/docs/apollo-server/essentials/data.html#context) for this request | +| `listKey` | `String` | The key for the list being operated on | #### Usage @@ -571,6 +594,7 @@ Return values are ignored. const beforeUnauth = ({ operation, context, + listKey, }) => { // Perform side effects // Return values ignored @@ -598,6 +622,7 @@ Return values are ignored. | `listKey` | `String` | The list key of the unauthenticated user (if there was one) | | `itemid` | `String` | The item ID of the unauthenticated user (if there was one) | | `context` | `Apollo Context` | The [Apollo `context` object](https://www.apollographql.com/docs/apollo-server/essentials/data.html#context) for this request | +| `listKey` | `String` | The key for the list being operated on | #### Usage @@ -613,6 +638,7 @@ const afterAuth = ({ originalInput, resolvedData, context, + listKey, }) => { // Perform side effects // Return values ignored diff --git a/packages/keystone/lib/ListTypes/hooks.js b/packages/keystone/lib/ListTypes/hooks.js index 2122e187057..eb573b91614 100644 --- a/packages/keystone/lib/ListTypes/hooks.js +++ b/packages/keystone/lib/ListTypes/hooks.js @@ -29,7 +29,8 @@ class HookManager { } async resolveInput({ resolvedData, existingItem, context, operation, originalInput }) { - const args = { resolvedData, existingItem, context, originalInput, operation }; + const { listKey } = this; + const args = { resolvedData, existingItem, context, originalInput, operation, listKey }; // First we run the field type hooks // NOTE: resolveInput is run on _every_ field, regardless if it has a value @@ -70,7 +71,8 @@ class HookManager { } async validateInput({ resolvedData, existingItem, context, operation, originalInput }) { - const args = { resolvedData, existingItem, context, originalInput, operation }; + const { listKey } = this; + const args = { resolvedData, existingItem, context, originalInput, operation, listKey }; // Check for isRequired const fieldValidationErrors = this.fields .filter( @@ -97,7 +99,8 @@ class HookManager { } async validateDelete({ existingItem, context, operation }) { - const args = { existingItem, context, operation }; + const { listKey } = this; + const args = { existingItem, context, operation, listKey }; const fields = this.fields; await this._validateHook({ args, fields, operation, hookName: 'validateDelete' }); } @@ -131,22 +134,26 @@ class HookManager { } async beforeChange({ resolvedData, existingItem, context, operation, originalInput }) { - const args = { resolvedData, existingItem, context, originalInput, operation }; + const { listKey } = this; + const args = { resolvedData, existingItem, context, originalInput, operation, listKey }; await this._runHook({ args, fieldObject: resolvedData, hookName: 'beforeChange' }); } async beforeDelete({ existingItem, context, operation }) { - const args = { existingItem, context, operation }; + const { listKey } = this; + const args = { existingItem, context, operation, listKey }; await this._runHook({ args, fieldObject: existingItem, hookName: 'beforeDelete' }); } async afterChange({ updatedItem, existingItem, context, operation, originalInput }) { - const args = { updatedItem, originalInput, existingItem, context, operation }; + const { listKey } = this; + const args = { updatedItem, originalInput, existingItem, context, operation, listKey }; await this._runHook({ args, fieldObject: updatedItem, hookName: 'afterChange' }); } async afterDelete({ existingItem, context, operation }) { - const args = { existingItem, context, operation }; + const { listKey } = this; + const args = { existingItem, context, operation, listKey }; await this._runHook({ args, fieldObject: existingItem, hookName: 'afterDelete' }); } diff --git a/packages/keystone/lib/providers/listAuth.js b/packages/keystone/lib/providers/listAuth.js index 8d30f4164e7..d2920006892 100644 --- a/packages/keystone/lib/providers/listAuth.js +++ b/packages/keystone/lib/providers/listAuth.js @@ -5,17 +5,20 @@ const { throwAccessDenied, ValidationFailureError } = require('../ListTypes/grap const graphqlLogger = logger('graphql'); class HookManager { - constructor({ name, hooks = {} }) { + constructor({ name, listKey, hooks = {} }) { this.name = name; this.hooks = hooks; + this.listKey = listKey; } async resolveAuthInput({ context, operation, originalInput }) { + const { listKey } = this; let resolvedData = originalInput; if (this.hooks.resolveAuthInput) { + const args = { context, originalInput, operation, listKey }; // And run any list-level hook - resolvedData = await this.hooks.resolveAuthInput({ context, originalInput, operation }); + resolvedData = await this.hooks.resolveAuthInput(args); if (typeof resolvedData !== 'object') { const method = `${this.name}.hooks.resolveAuthInput()`; throw new Error( @@ -29,7 +32,8 @@ class HookManager { } async validateAuthInput({ resolvedData, context, operation, originalInput }) { - const args = { resolvedData, context, originalInput, operation }; + const { listKey } = this; + const args = { resolvedData, context, originalInput, operation, listKey }; if (this.hooks['validateAuthInput']) { const listValidationErrors = []; @@ -55,7 +59,8 @@ class HookManager { } async beforeAuth({ resolvedData, context, operation, originalInput }) { - const args = { resolvedData, context, originalInput, operation }; + const { listKey } = this; + const args = { resolvedData, context, originalInput, operation, listKey }; if (this.hooks.beforeAuth) await this.hooks.beforeAuth(args); } @@ -69,12 +74,24 @@ class HookManager { resolvedData, context, }) { - const args = { resolvedData, context, operation, originalInput, item, success, message, token }; + const { listKey } = this; + const args = { + resolvedData, + context, + operation, + originalInput, + item, + success, + message, + token, + listKey, + }; if (this.hooks.afterAuth) await this.hooks.afterAuth(args); } async beforeUnauth({ operation, context }) { - const args = { context, operation }; + const { listKey } = this; + const args = { context, operation, listKey }; if (this.hooks.beforeUnauth) await this.hooks.beforeUnauth(args); } @@ -99,7 +116,11 @@ class ListAuthProvider { unauthenticateOutputName: `unauthenticate${itemQueryName}Output`, updateAuthenticatedMutationName: `updateAuthenticated${itemQueryName}`, }; - this.hookManager = new HookManager({ name: authStrategy.constructor.name, hooks }); + this.hookManager = new HookManager({ + name: authStrategy.constructor.name, + hooks, + listKey: list.key, + }); // Record GQL names in the strategy authStrategy.gqlNames = this.gqlNames; }