feat: collection, global and field props for hooks, fix request context initialization, add context to global hooks (#3780)

* feat: pass collection, global and field props to collection, global and field hooks - where applicable

* fix: initial request context not set for all operations

* chore: add tests which check the collection prop for collection hooks

* feat: add context to props of global hooks

* chore: add global tests for global and field props

* chore: int tests: use JSON instead of object hashes
This commit is contained in:
Alessio Gravili
2023-10-21 11:40:57 +02:00
committed by GitHub
parent 67d61df563
commit f6adbae0c7
52 changed files with 752 additions and 71 deletions

View File

@@ -62,7 +62,7 @@ All field-level hooks are formatted to accept the same arguments, although some
Field Hooks receive one `args` argument that contains the following properties: Field Hooks receive one `args` argument that contains the following properties:
| Option | Description | | Option | Description |
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | |--------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **`data`** | The data passed to update the document within `create` and `update` operations, and the full document itself in the `afterRead` hook. | | **`data`** | The data passed to update the document within `create` and `update` operations, and the full document itself in the `afterRead` hook. |
| **`siblingData`** | The sibling data passed to a field that the hook is running against. | | **`siblingData`** | The sibling data passed to a field that the hook is running against. |
| **`findMany`** | Boolean to denote if this hook is running against finding one, or finding many within the `afterRead` hook. | | **`findMany`** | Boolean to denote if this hook is running against finding one, or finding many within the `afterRead` hook. |
@@ -73,6 +73,10 @@ Field Hooks receive one `args` argument that contains the following properties:
| **`req`** | The Express `request` object. It is mocked for Local API operations. | | **`req`** | The Express `request` object. It is mocked for Local API operations. |
| **`value`** | The value of the field. | | **`value`** | The value of the field. |
| **`previousValue`** | The previous value of the field, before changes were applied, only in `afterChange` hooks. | | **`previousValue`** | The previous value of the field, before changes were applied, only in `afterChange` hooks. |
| **`context`** | Context passed to this hook. More info can be found under [Context](/docs/hooks/context) |
| **`field`** | The field which the hook is running against. |
| **`collection`** | The collection which the field belongs to. If the field belongs to a global, this will be null. |
| **`global`** | The global which the field belongs to. If the field belongs to a collection, this will be null. |
#### Return value #### Return value

View File

@@ -62,6 +62,7 @@ const Duplicate: React.FC<Props> = ({ id, collection, slug }) => {
if (typeof collection.admin.hooks?.beforeDuplicate === 'function') { if (typeof collection.admin.hooks?.beforeDuplicate === 'function') {
data = await collection.admin.hooks.beforeDuplicate({ data = await collection.admin.hooks.beforeDuplicate({
collection,
data, data,
locale, locale,
}) })
@@ -108,6 +109,7 @@ const Duplicate: React.FC<Props> = ({ id, collection, slug }) => {
if (typeof collection.admin.hooks?.beforeDuplicate === 'function') { if (typeof collection.admin.hooks?.beforeDuplicate === 'function') {
localizedDoc = await collection.admin.hooks.beforeDuplicate({ localizedDoc = await collection.admin.hooks.beforeDuplicate({
collection,
data: localizedDoc, data: localizedDoc,
locale, locale,
}) })

View File

@@ -38,6 +38,7 @@ async function forgotPassword(incomingArgs: Arguments): Promise<null | string> {
args = args =
(await hook({ (await hook({
args, args,
collection: args.collection?.config,
context: args.req.context, context: args.req.context,
operation: 'forgotPassword', operation: 'forgotPassword',
})) || args })) || args
@@ -139,7 +140,7 @@ async function forgotPassword(incomingArgs: Arguments): Promise<null | string> {
await collectionConfig.hooks.afterForgotPassword.reduce(async (priorHook, hook) => { await collectionConfig.hooks.afterForgotPassword.reduce(async (priorHook, hook) => {
await priorHook await priorHook
await hook({ args, context: req.context }) await hook({ args, collection: args.collection?.config, context: req.context })
}, Promise.resolve()) }, Promise.resolve())
// ///////////////////////////////////// // /////////////////////////////////////
@@ -148,6 +149,7 @@ async function forgotPassword(incomingArgs: Arguments): Promise<null | string> {
token = await buildAfterOperation({ token = await buildAfterOperation({
args, args,
collection: args.collection?.config,
operation: 'forgotPassword', operation: 'forgotPassword',
result: token, result: token,
}) })

View File

@@ -30,7 +30,7 @@ async function localForgotPassword<T extends keyof GeneratedTypes['collections']
expiration, expiration,
req = {} as PayloadRequest, req = {} as PayloadRequest,
} = options } = options
setRequestContext(options.req) setRequestContext(req)
const collection = payload.collections[collectionSlug] const collection = payload.collections[collectionSlug]

View File

@@ -41,7 +41,7 @@ async function localLogin<TSlug extends keyof GeneratedTypes['collections']>(
res, res,
showHiddenFields, showHiddenFields,
} = options } = options
setRequestContext(options.req) setRequestContext(req)
const collection = payload.collections[collectionSlug] const collection = payload.collections[collectionSlug]

View File

@@ -24,7 +24,8 @@ async function localResetPassword<T extends keyof GeneratedTypes['collections']>
options: Options<T>, options: Options<T>,
): Promise<Result> { ): Promise<Result> {
const { collection: collectionSlug, data, overrideAccess, req = {} as PayloadRequest } = options const { collection: collectionSlug, data, overrideAccess, req = {} as PayloadRequest } = options
setRequestContext(options.req)
setRequestContext(req)
const collection = payload.collections[collectionSlug] const collection = payload.collections[collectionSlug]

View File

@@ -27,7 +27,7 @@ async function localUnlock<T extends keyof GeneratedTypes['collections']>(
overrideAccess = true, overrideAccess = true,
req = {} as PayloadRequest, req = {} as PayloadRequest,
} = options } = options
setRequestContext(options.req) setRequestContext(req)
const collection = payload.collections[collectionSlug] const collection = payload.collections[collectionSlug]

View File

@@ -18,7 +18,7 @@ async function localVerifyEmail<T extends keyof GeneratedTypes['collections']>(
options: Options<T>, options: Options<T>,
): Promise<boolean> { ): Promise<boolean> {
const { collection: collectionSlug, req = {} as PayloadRequest, token } = options const { collection: collectionSlug, req = {} as PayloadRequest, token } = options
setRequestContext(options.req) setRequestContext(req)
const collection = payload.collections[collectionSlug] const collection = payload.collections[collectionSlug]

View File

@@ -54,6 +54,7 @@ async function login<TSlug extends keyof GeneratedTypes['collections']>(
args = args =
(await hook({ (await hook({
args, args,
collection: args.collection?.config,
context: args.req.context, context: args.req.context,
operation: 'login', operation: 'login',
})) || args })) || args
@@ -138,6 +139,7 @@ async function login<TSlug extends keyof GeneratedTypes['collections']>(
user = user =
(await hook({ (await hook({
collection: args.collection?.config,
context: args.req.context, context: args.req.context,
req: args.req, req: args.req,
user, user,
@@ -175,6 +177,7 @@ async function login<TSlug extends keyof GeneratedTypes['collections']>(
user = user =
(await hook({ (await hook({
collection: args.collection?.config,
context: args.req.context, context: args.req.context,
req: args.req, req: args.req,
token, token,
@@ -187,10 +190,11 @@ async function login<TSlug extends keyof GeneratedTypes['collections']>(
// ///////////////////////////////////// // /////////////////////////////////////
user = await afterRead({ user = await afterRead({
collection: collectionConfig,
context: req.context, context: req.context,
depth, depth,
doc: user, doc: user,
entityConfig: collectionConfig, global: null,
overrideAccess, overrideAccess,
req, req,
showHiddenFields, showHiddenFields,
@@ -205,6 +209,7 @@ async function login<TSlug extends keyof GeneratedTypes['collections']>(
user = user =
(await hook({ (await hook({
collection: args.collection?.config,
context: req.context, context: req.context,
doc: user, doc: user,
req, req,
@@ -220,6 +225,7 @@ async function login<TSlug extends keyof GeneratedTypes['collections']>(
user = user =
(await hook({ (await hook({
collection: args.collection?.config,
context: req.context, context: req.context,
doc: user, doc: user,
req, req,
@@ -238,6 +244,7 @@ async function login<TSlug extends keyof GeneratedTypes['collections']>(
result = await buildAfterOperation<GeneratedTypes['collections'][TSlug]>({ result = await buildAfterOperation<GeneratedTypes['collections'][TSlug]>({
args, args,
collection: args.collection?.config,
operation: 'login', operation: 'login',
result, result,
}) })

View File

@@ -46,6 +46,7 @@ async function logout(incomingArgs: Arguments): Promise<string> {
args = args =
(await hook({ (await hook({
collection: args.collection?.config,
context: req.context, context: req.context,
req, req,
res, res,

View File

@@ -69,6 +69,7 @@ async function me({ collection, req }: Arguments): Promise<Result> {
response = response =
(await hook({ (await hook({
collection: collection?.config,
context: req.context, context: req.context,
req, req,
response, response,

View File

@@ -39,6 +39,7 @@ async function refresh(incomingArgs: Arguments): Promise<Result> {
args = args =
(await hook({ (await hook({
args, args,
collection: args.collection?.config,
context: args.req.context, context: args.req.context,
operation: 'refresh', operation: 'refresh',
})) || args })) || args
@@ -112,6 +113,7 @@ async function refresh(incomingArgs: Arguments): Promise<Result> {
result = result =
(await hook({ (await hook({
collection: args.collection?.config,
context: args.req.context, context: args.req.context,
exp, exp,
req: args.req, req: args.req,
@@ -126,6 +128,7 @@ async function refresh(incomingArgs: Arguments): Promise<Result> {
result = await buildAfterOperation({ result = await buildAfterOperation({
args, args,
collection: args.collection?.config,
operation: 'refresh', operation: 'refresh',
result, result,
}) })

View File

@@ -41,6 +41,8 @@ type CreateOrUpdateOperation = Extract<HookOperationType, 'create' | 'update'>
export type BeforeOperationHook = (args: { export type BeforeOperationHook = (args: {
args?: any args?: any
/** The collection which this hook is being run on */
collection: SanitizedCollectionConfig
context: RequestContext context: RequestContext
/** /**
* Hook operation being performed * Hook operation being performed
@@ -49,6 +51,8 @@ export type BeforeOperationHook = (args: {
}) => any }) => any
export type BeforeValidateHook<T extends TypeWithID = any> = (args: { export type BeforeValidateHook<T extends TypeWithID = any> = (args: {
/** The collection which this hook is being run on */
collection: SanitizedCollectionConfig
context: RequestContext context: RequestContext
data?: Partial<T> data?: Partial<T>
/** /**
@@ -65,6 +69,8 @@ export type BeforeValidateHook<T extends TypeWithID = any> = (args: {
}) => any }) => any
export type BeforeChangeHook<T extends TypeWithID = any> = (args: { export type BeforeChangeHook<T extends TypeWithID = any> = (args: {
/** The collection which this hook is being run on */
collection: SanitizedCollectionConfig
context: RequestContext context: RequestContext
data: Partial<T> data: Partial<T>
/** /**
@@ -81,6 +87,8 @@ export type BeforeChangeHook<T extends TypeWithID = any> = (args: {
}) => any }) => any
export type AfterChangeHook<T extends TypeWithID = any> = (args: { export type AfterChangeHook<T extends TypeWithID = any> = (args: {
/** The collection which this hook is being run on */
collection: SanitizedCollectionConfig
context: RequestContext context: RequestContext
doc: T doc: T
/** /**
@@ -92,6 +100,8 @@ export type AfterChangeHook<T extends TypeWithID = any> = (args: {
}) => any }) => any
export type BeforeReadHook<T extends TypeWithID = any> = (args: { export type BeforeReadHook<T extends TypeWithID = any> = (args: {
/** The collection which this hook is being run on */
collection: SanitizedCollectionConfig
context: RequestContext context: RequestContext
doc: T doc: T
query: { [key: string]: any } query: { [key: string]: any }
@@ -99,6 +109,8 @@ export type BeforeReadHook<T extends TypeWithID = any> = (args: {
}) => any }) => any
export type AfterReadHook<T extends TypeWithID = any> = (args: { export type AfterReadHook<T extends TypeWithID = any> = (args: {
/** The collection which this hook is being run on */
collection: SanitizedCollectionConfig
context: RequestContext context: RequestContext
doc: T doc: T
findMany?: boolean findMany?: boolean
@@ -107,12 +119,16 @@ export type AfterReadHook<T extends TypeWithID = any> = (args: {
}) => any }) => any
export type BeforeDeleteHook = (args: { export type BeforeDeleteHook = (args: {
/** The collection which this hook is being run on */
collection: SanitizedCollectionConfig
context: RequestContext context: RequestContext
id: number | string id: number | string
req: PayloadRequest req: PayloadRequest
}) => any }) => any
export type AfterDeleteHook<T extends TypeWithID = any> = (args: { export type AfterDeleteHook<T extends TypeWithID = any> = (args: {
/** The collection which this hook is being run on */
collection: SanitizedCollectionConfig
context: RequestContext context: RequestContext
doc: T doc: T
id: number | string id: number | string
@@ -127,15 +143,21 @@ export type AfterErrorHook = (
err: Error, err: Error,
res: unknown, res: unknown,
context: RequestContext, context: RequestContext,
/** The collection which this hook is being run on. This is null if the AfterError hook was be added to the payload-wide config */
collection: SanitizedCollectionConfig | null,
) => { response: any; status: number } | void ) => { response: any; status: number } | void
export type BeforeLoginHook<T extends TypeWithID = any> = (args: { export type BeforeLoginHook<T extends TypeWithID = any> = (args: {
/** The collection which this hook is being run on */
collection: SanitizedCollectionConfig
context: RequestContext context: RequestContext
req: PayloadRequest req: PayloadRequest
user: T user: T
}) => any }) => any
export type AfterLoginHook<T extends TypeWithID = any> = (args: { export type AfterLoginHook<T extends TypeWithID = any> = (args: {
/** The collection which this hook is being run on */
collection: SanitizedCollectionConfig
context: RequestContext context: RequestContext
req: PayloadRequest req: PayloadRequest
token: string token: string
@@ -143,18 +165,24 @@ export type AfterLoginHook<T extends TypeWithID = any> = (args: {
}) => any }) => any
export type AfterLogoutHook<T extends TypeWithID = any> = (args: { export type AfterLogoutHook<T extends TypeWithID = any> = (args: {
/** The collection which this hook is being run on */
collection: SanitizedCollectionConfig
context: RequestContext context: RequestContext
req: PayloadRequest req: PayloadRequest
res: Response res: Response
}) => any }) => any
export type AfterMeHook<T extends TypeWithID = any> = (args: { export type AfterMeHook<T extends TypeWithID = any> = (args: {
/** The collection which this hook is being run on */
collection: SanitizedCollectionConfig
context: RequestContext context: RequestContext
req: PayloadRequest req: PayloadRequest
response: unknown response: unknown
}) => any }) => any
export type AfterRefreshHook<T extends TypeWithID = any> = (args: { export type AfterRefreshHook<T extends TypeWithID = any> = (args: {
/** The collection which this hook is being run on */
collection: SanitizedCollectionConfig
context: RequestContext context: RequestContext
exp: number exp: number
req: PayloadRequest req: PayloadRequest
@@ -162,9 +190,16 @@ export type AfterRefreshHook<T extends TypeWithID = any> = (args: {
token: string token: string
}) => any }) => any
export type AfterForgotPasswordHook = (args: { args?: any; context: RequestContext }) => any export type AfterForgotPasswordHook = (args: {
args?: any
/** The collection which this hook is being run on */
collection: SanitizedCollectionConfig
context: RequestContext
}) => any
type BeforeDuplicateArgs<T> = { type BeforeDuplicateArgs<T> = {
/** The collection which this hook is being run on */
collection: SanitizedCollectionConfig
data: T data: T
locale?: string locale?: string
} }

View File

@@ -65,6 +65,7 @@ async function create<TSlug extends keyof GeneratedTypes['collections']>(
args = args =
(await hook({ (await hook({
args, args,
collection: args.collection.config,
context: args.req.context, context: args.req.context,
operation: 'create', operation: 'create',
})) || args })) || args
@@ -139,10 +140,11 @@ async function create<TSlug extends keyof GeneratedTypes['collections']>(
// ///////////////////////////////////// // /////////////////////////////////////
data = await beforeValidate({ data = await beforeValidate({
collection: collectionConfig,
context: req.context, context: req.context,
data, data,
doc: {}, doc: {},
entityConfig: collectionConfig, global: null,
operation: 'create', operation: 'create',
overrideAccess, overrideAccess,
req, req,
@@ -158,6 +160,7 @@ async function create<TSlug extends keyof GeneratedTypes['collections']>(
data = data =
(await hook({ (await hook({
collection: collectionConfig,
context: req.context, context: req.context,
data, data,
operation: 'create', operation: 'create',
@@ -184,6 +187,7 @@ async function create<TSlug extends keyof GeneratedTypes['collections']>(
data = data =
(await hook({ (await hook({
collection: collectionConfig,
context: req.context, context: req.context,
data, data,
operation: 'create', operation: 'create',
@@ -196,11 +200,12 @@ async function create<TSlug extends keyof GeneratedTypes['collections']>(
// ///////////////////////////////////// // /////////////////////////////////////
const resultWithLocales = await beforeChange<Record<string, unknown>>({ const resultWithLocales = await beforeChange<Record<string, unknown>>({
collection: collectionConfig,
context: req.context, context: req.context,
data, data,
doc: {}, doc: {},
docWithLocales: {}, docWithLocales: {},
entityConfig: collectionConfig, global: null,
operation: 'create', operation: 'create',
req, req,
skipValidation: shouldSaveDraft, skipValidation: shouldSaveDraft,
@@ -293,10 +298,11 @@ async function create<TSlug extends keyof GeneratedTypes['collections']>(
// ///////////////////////////////////// // /////////////////////////////////////
result = await afterRead({ result = await afterRead({
collection: collectionConfig,
context: req.context, context: req.context,
depth, depth,
doc: result, doc: result,
entityConfig: collectionConfig, global: null,
overrideAccess, overrideAccess,
req, req,
showHiddenFields, showHiddenFields,
@@ -311,6 +317,7 @@ async function create<TSlug extends keyof GeneratedTypes['collections']>(
result = result =
(await hook({ (await hook({
collection: collectionConfig,
context: req.context, context: req.context,
doc: result, doc: result,
req, req,
@@ -322,10 +329,11 @@ async function create<TSlug extends keyof GeneratedTypes['collections']>(
// ///////////////////////////////////// // /////////////////////////////////////
result = await afterChange({ result = await afterChange({
collection: collectionConfig,
context: req.context, context: req.context,
data, data,
doc: result, doc: result,
entityConfig: collectionConfig, global: null,
operation: 'create', operation: 'create',
previousDoc: {}, previousDoc: {},
req, req,
@@ -353,6 +361,7 @@ async function create<TSlug extends keyof GeneratedTypes['collections']>(
result = result =
(await hook({ (await hook({
collection: collectionConfig,
context: req.context, context: req.context,
doc: result, doc: result,
operation: 'create', operation: 'create',
@@ -369,6 +378,7 @@ async function create<TSlug extends keyof GeneratedTypes['collections']>(
result = await buildAfterOperation<GeneratedTypes['collections'][TSlug]>({ result = await buildAfterOperation<GeneratedTypes['collections'][TSlug]>({
args, args,
collection: collectionConfig,
operation: 'create', operation: 'create',
result, result,
}) })

View File

@@ -49,6 +49,7 @@ async function deleteOperation<TSlug extends keyof GeneratedTypes['collections']
args = args =
(await hook({ (await hook({
args, args,
collection: args.collection.config,
context: args.req.context, context: args.req.context,
operation: 'delete', operation: 'delete',
})) || args })) || args
@@ -126,6 +127,7 @@ async function deleteOperation<TSlug extends keyof GeneratedTypes['collections']
return hook({ return hook({
id, id,
collection: collectionConfig,
context: req.context, context: req.context,
req, req,
}) })
@@ -171,10 +173,11 @@ async function deleteOperation<TSlug extends keyof GeneratedTypes['collections']
// ///////////////////////////////////// // /////////////////////////////////////
result = await afterRead({ result = await afterRead({
collection: collectionConfig,
context: req.context, context: req.context,
depth, depth,
doc: result || doc, doc: result || doc,
entityConfig: collectionConfig, global: null,
overrideAccess, overrideAccess,
req, req,
showHiddenFields, showHiddenFields,
@@ -189,6 +192,7 @@ async function deleteOperation<TSlug extends keyof GeneratedTypes['collections']
result = result =
(await hook({ (await hook({
collection: collectionConfig,
context: req.context, context: req.context,
doc: result || doc, doc: result || doc,
req, req,
@@ -205,6 +209,7 @@ async function deleteOperation<TSlug extends keyof GeneratedTypes['collections']
result = result =
(await hook({ (await hook({
id, id,
collection: collectionConfig,
context: req.context, context: req.context,
doc: result, doc: result,
req, req,
@@ -249,6 +254,7 @@ async function deleteOperation<TSlug extends keyof GeneratedTypes['collections']
result = await buildAfterOperation<GeneratedTypes['collections'][TSlug]>({ result = await buildAfterOperation<GeneratedTypes['collections'][TSlug]>({
args, args,
collection: collectionConfig,
operation: 'delete', operation: 'delete',
result, result,
}) })

View File

@@ -40,6 +40,7 @@ async function deleteByID<TSlug extends keyof GeneratedTypes['collections']>(
args = args =
(await hook({ (await hook({
args, args,
collection: args.collection.config,
context: args.req.context, context: args.req.context,
operation: 'delete', operation: 'delete',
})) || args })) || args
@@ -82,6 +83,7 @@ async function deleteByID<TSlug extends keyof GeneratedTypes['collections']>(
return hook({ return hook({
id, id,
collection: collectionConfig,
context: req.context, context: req.context,
req, req,
}) })
@@ -148,10 +150,11 @@ async function deleteByID<TSlug extends keyof GeneratedTypes['collections']>(
// ///////////////////////////////////// // /////////////////////////////////////
result = await afterRead({ result = await afterRead({
collection: collectionConfig,
context: req.context, context: req.context,
depth, depth,
doc: result, doc: result,
entityConfig: collectionConfig, global: null,
overrideAccess, overrideAccess,
req, req,
showHiddenFields, showHiddenFields,
@@ -166,6 +169,7 @@ async function deleteByID<TSlug extends keyof GeneratedTypes['collections']>(
result = result =
(await hook({ (await hook({
collection: collectionConfig,
context: req.context, context: req.context,
doc: result, doc: result,
req, req,
@@ -182,6 +186,7 @@ async function deleteByID<TSlug extends keyof GeneratedTypes['collections']>(
result = result =
(await hook({ (await hook({
id, id,
collection: collectionConfig,
context: req.context, context: req.context,
doc: result, doc: result,
req, req,
@@ -194,6 +199,7 @@ async function deleteByID<TSlug extends keyof GeneratedTypes['collections']>(
result = await buildAfterOperation<GeneratedTypes['collections'][TSlug]>({ result = await buildAfterOperation<GeneratedTypes['collections'][TSlug]>({
args, args,
collection: collectionConfig,
operation: 'deleteByID', operation: 'deleteByID',
result, result,
}) })

View File

@@ -46,6 +46,7 @@ async function find<T extends TypeWithID & Record<string, unknown>>(
args = args =
(await hook({ (await hook({
args, args,
collection: args.collection.config,
context: args.req.context, context: args.req.context,
operation: 'read', operation: 'read',
})) || args })) || args
@@ -166,6 +167,7 @@ async function find<T extends TypeWithID & Record<string, unknown>>(
docRef = docRef =
(await hook({ (await hook({
collection: collectionConfig,
context: req.context, context: req.context,
doc: docRef, doc: docRef,
query: fullWhere, query: fullWhere,
@@ -187,12 +189,13 @@ async function find<T extends TypeWithID & Record<string, unknown>>(
docs: await Promise.all( docs: await Promise.all(
result.docs.map(async (doc) => result.docs.map(async (doc) =>
afterRead<T>({ afterRead<T>({
collection: collectionConfig,
context: req.context, context: req.context,
currentDepth, currentDepth,
depth, depth,
doc, doc,
entityConfig: collectionConfig,
findMany: true, findMany: true,
global: null,
overrideAccess, overrideAccess,
req, req,
showHiddenFields, showHiddenFields,
@@ -216,6 +219,7 @@ async function find<T extends TypeWithID & Record<string, unknown>>(
docRef = docRef =
(await hook({ (await hook({
collection: collectionConfig,
context: req.context, context: req.context,
doc: docRef, doc: docRef,
findMany: true, findMany: true,
@@ -235,6 +239,7 @@ async function find<T extends TypeWithID & Record<string, unknown>>(
result = await buildAfterOperation<T>({ result = await buildAfterOperation<T>({
args, args,
collection: collectionConfig,
operation: 'find', operation: 'find',
result, result,
}) })

View File

@@ -39,6 +39,7 @@ async function findByID<T extends TypeWithID>(incomingArgs: Arguments): Promise<
args = args =
(await hook({ (await hook({
args, args,
collection: args.collection.config,
context: args.req.context, context: args.req.context,
operation: 'read', operation: 'read',
})) || args })) || args
@@ -138,6 +139,7 @@ async function findByID<T extends TypeWithID>(incomingArgs: Arguments): Promise<
result = result =
(await hook({ (await hook({
collection: collectionConfig,
context: req.context, context: req.context,
doc: result, doc: result,
query: findOneArgs.where, query: findOneArgs.where,
@@ -150,11 +152,12 @@ async function findByID<T extends TypeWithID>(incomingArgs: Arguments): Promise<
// ///////////////////////////////////// // /////////////////////////////////////
result = await afterRead({ result = await afterRead({
collection: collectionConfig,
context: req.context, context: req.context,
currentDepth, currentDepth,
depth, depth,
doc: result, doc: result,
entityConfig: collectionConfig, global: null,
overrideAccess, overrideAccess,
req, req,
showHiddenFields, showHiddenFields,
@@ -169,6 +172,7 @@ async function findByID<T extends TypeWithID>(incomingArgs: Arguments): Promise<
result = result =
(await hook({ (await hook({
collection: collectionConfig,
context: req.context, context: req.context,
doc: result, doc: result,
query: findOneArgs.where, query: findOneArgs.where,
@@ -182,6 +186,7 @@ async function findByID<T extends TypeWithID>(incomingArgs: Arguments): Promise<
result = await buildAfterOperation<T>({ result = await buildAfterOperation<T>({
args, args,
collection: collectionConfig,
operation: 'findByID', operation: 'findByID',
result: result as any, result: result as any,
}) // TODO: fix this typing }) // TODO: fix this typing

View File

@@ -93,6 +93,7 @@ async function findVersionByID<T extends TypeWithID = any>(
result.version = result.version =
(await hook({ (await hook({
collection: collectionConfig,
context: req.context, context: req.context,
doc: result.version, doc: result.version,
query: fullWhere, query: fullWhere,
@@ -105,11 +106,12 @@ async function findVersionByID<T extends TypeWithID = any>(
// ///////////////////////////////////// // /////////////////////////////////////
result.version = await afterRead({ result.version = await afterRead({
collection: collectionConfig,
context: req.context, context: req.context,
currentDepth, currentDepth,
depth, depth,
doc: result.version, doc: result.version,
entityConfig: collectionConfig, global: null,
overrideAccess, overrideAccess,
req, req,
showHiddenFields, showHiddenFields,
@@ -124,6 +126,7 @@ async function findVersionByID<T extends TypeWithID = any>(
result.version = result.version =
(await hook({ (await hook({
collection: collectionConfig,
context: req.context, context: req.context,
doc: result.version, doc: result.version,
query: fullWhere, query: fullWhere,

View File

@@ -94,6 +94,7 @@ async function findVersions<T extends TypeWithVersion<T>>(
docRef.version = docRef.version =
(await hook({ (await hook({
collection: collectionConfig,
context: req.context, context: req.context,
doc: docRef.version, doc: docRef.version,
query: fullWhere, query: fullWhere,
@@ -116,11 +117,12 @@ async function findVersions<T extends TypeWithVersion<T>>(
result.docs.map(async (data) => ({ result.docs.map(async (data) => ({
...data, ...data,
version: await afterRead({ version: await afterRead({
collection: collectionConfig,
context: req.context, context: req.context,
depth, depth,
doc: data.version, doc: data.version,
entityConfig: collectionConfig,
findMany: true, findMany: true,
global: null,
overrideAccess, overrideAccess,
req, req,
showHiddenFields, showHiddenFields,
@@ -144,6 +146,7 @@ async function findVersions<T extends TypeWithVersion<T>>(
docRef.version = docRef.version =
(await hook({ (await hook({
collection: collectionConfig,
context: req.context, context: req.context,
doc: doc.version, doc: doc.version,
findMany: true, findMany: true,

View File

@@ -56,7 +56,7 @@ export default async function findLocal<T extends keyof GeneratedTypes['collecti
user, user,
where, where,
} = options } = options
setRequestContext(options.req, context) setRequestContext(req, context)
const collection = payload.collections[collectionSlug] const collection = payload.collections[collectionSlug]
const defaultLocale = payload?.config?.localization const defaultLocale = payload?.config?.localization

View File

@@ -47,7 +47,7 @@ export default async function findByIDLocal<T extends keyof GeneratedTypes['coll
showHiddenFields, showHiddenFields,
user, user,
} = options } = options
setRequestContext(options.req, context) setRequestContext(req, context)
const collection = payload.collections[collectionSlug] const collection = payload.collections[collectionSlug]
const defaultLocale = payload?.config?.localization const defaultLocale = payload?.config?.localization

View File

@@ -44,7 +44,7 @@ export default async function findVersionByIDLocal<T extends keyof GeneratedType
req = {} as PayloadRequest, req = {} as PayloadRequest,
showHiddenFields, showHiddenFields,
} = options } = options
setRequestContext(options.req, context) setRequestContext(req, context)
const collection = payload.collections[collectionSlug] const collection = payload.collections[collectionSlug]
const defaultLocale = payload?.config?.localization const defaultLocale = payload?.config?.localization

View File

@@ -135,10 +135,11 @@ async function restoreVersion<T extends TypeWithID = any>(args: Arguments): Prom
// ///////////////////////////////////// // /////////////////////////////////////
result = await afterRead({ result = await afterRead({
collection: collectionConfig,
context: req.context, context: req.context,
depth, depth,
doc: result, doc: result,
entityConfig: collectionConfig, global: null,
overrideAccess, overrideAccess,
req, req,
showHiddenFields, showHiddenFields,
@@ -153,6 +154,7 @@ async function restoreVersion<T extends TypeWithID = any>(args: Arguments): Prom
result = result =
(await hook({ (await hook({
collection: collectionConfig,
context: req.context, context: req.context,
doc: result, doc: result,
req, req,
@@ -164,10 +166,11 @@ async function restoreVersion<T extends TypeWithID = any>(args: Arguments): Prom
// ///////////////////////////////////// // /////////////////////////////////////
result = await afterChange({ result = await afterChange({
collection: collectionConfig,
context: req.context, context: req.context,
data: result, data: result,
doc: result, doc: result,
entityConfig: collectionConfig, global: null,
operation: 'update', operation: 'update',
previousDoc: prevDocWithLocales, previousDoc: prevDocWithLocales,
req, req,
@@ -182,6 +185,7 @@ async function restoreVersion<T extends TypeWithID = any>(args: Arguments): Prom
result = result =
(await hook({ (await hook({
collection: collectionConfig,
context: req.context, context: req.context,
doc: result, doc: result,
operation: 'update', operation: 'update',

View File

@@ -56,6 +56,7 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
args = args =
(await hook({ (await hook({
args, args,
collection: args.collection.config,
context: args.req.context, context: args.req.context,
operation: 'update', operation: 'update',
})) || args })) || args
@@ -169,10 +170,11 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
try { try {
const originalDoc = await afterRead({ const originalDoc = await afterRead({
collection: collectionConfig,
context: req.context, context: req.context,
depth: 0, depth: 0,
doc, doc,
entityConfig: collectionConfig, global: null,
overrideAccess: true, overrideAccess: true,
req, req,
showHiddenFields: true, showHiddenFields: true,
@@ -193,10 +195,11 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
data = await beforeValidate<DeepPartial<GeneratedTypes['collections'][TSlug]>>({ data = await beforeValidate<DeepPartial<GeneratedTypes['collections'][TSlug]>>({
id, id,
collection: collectionConfig,
context: req.context, context: req.context,
data, data,
doc: originalDoc, doc: originalDoc,
entityConfig: collectionConfig, global: null,
operation: 'update', operation: 'update',
overrideAccess, overrideAccess,
req, req,
@@ -211,6 +214,7 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
data = data =
(await hook({ (await hook({
collection: collectionConfig,
context: req.context, context: req.context,
data, data,
operation: 'update', operation: 'update',
@@ -236,6 +240,7 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
data = data =
(await hook({ (await hook({
collection: collectionConfig,
context: req.context, context: req.context,
data, data,
operation: 'update', operation: 'update',
@@ -250,11 +255,12 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
let result = await beforeChange<GeneratedTypes['collections'][TSlug]>({ let result = await beforeChange<GeneratedTypes['collections'][TSlug]>({
id, id,
collection: collectionConfig,
context: req.context, context: req.context,
data, data,
doc: originalDoc, doc: originalDoc,
docWithLocales: doc, docWithLocales: doc,
entityConfig: collectionConfig, global: null,
operation: 'update', operation: 'update',
req, req,
skipValidation: shouldSaveDraft || data._status === 'draft', skipValidation: shouldSaveDraft || data._status === 'draft',
@@ -297,10 +303,11 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
// ///////////////////////////////////// // /////////////////////////////////////
result = await afterRead({ result = await afterRead({
collection: collectionConfig,
context: req.context, context: req.context,
depth, depth,
doc: result, doc: result,
entityConfig: collectionConfig, global: null,
overrideAccess, overrideAccess,
req, req,
showHiddenFields, showHiddenFields,
@@ -315,6 +322,7 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
result = result =
(await hook({ (await hook({
collection: collectionConfig,
context: req.context, context: req.context,
doc: result, doc: result,
req, req,
@@ -326,10 +334,11 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
// ///////////////////////////////////// // /////////////////////////////////////
result = await afterChange<GeneratedTypes['collections'][TSlug]>({ result = await afterChange<GeneratedTypes['collections'][TSlug]>({
collection: collectionConfig,
context: req.context, context: req.context,
data, data,
doc: result, doc: result,
entityConfig: collectionConfig, global: null,
operation: 'update', operation: 'update',
previousDoc: originalDoc, previousDoc: originalDoc,
req, req,
@@ -344,6 +353,7 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
result = result =
(await hook({ (await hook({
collection: collectionConfig,
context: req.context, context: req.context,
doc: result, doc: result,
operation: 'update', operation: 'update',
@@ -385,6 +395,7 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
result = await buildAfterOperation<GeneratedTypes['collections'][TSlug]>({ result = await buildAfterOperation<GeneratedTypes['collections'][TSlug]>({
args, args,
collection: collectionConfig,
operation: 'update', operation: 'update',
result, result,
}) })

View File

@@ -55,6 +55,7 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
args = args =
(await hook({ (await hook({
args, args,
collection: args.collection.config,
context: args.req.context, context: args.req.context,
operation: 'update', operation: 'update',
})) || args })) || args
@@ -123,10 +124,11 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
if (!docWithLocales && hasWherePolicy) throw new Forbidden(t) if (!docWithLocales && hasWherePolicy) throw new Forbidden(t)
const originalDoc = await afterRead({ const originalDoc = await afterRead({
collection: collectionConfig,
context: req.context, context: req.context,
depth: 0, depth: 0,
doc: docWithLocales, doc: docWithLocales,
entityConfig: collectionConfig, global: null,
overrideAccess: true, overrideAccess: true,
req, req,
showHiddenFields: true, showHiddenFields: true,
@@ -166,10 +168,11 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
data = await beforeValidate<DeepPartial<GeneratedTypes['collections'][TSlug]>>({ data = await beforeValidate<DeepPartial<GeneratedTypes['collections'][TSlug]>>({
id, id,
collection: collectionConfig,
context: req.context, context: req.context,
data, data,
doc: originalDoc, doc: originalDoc,
entityConfig: collectionConfig, global: null,
operation: 'update', operation: 'update',
overrideAccess, overrideAccess,
req, req,
@@ -184,6 +187,7 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
data = data =
(await hook({ (await hook({
collection: collectionConfig,
context: req.context, context: req.context,
data, data,
operation: 'update', operation: 'update',
@@ -209,6 +213,7 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
data = data =
(await hook({ (await hook({
collection: collectionConfig,
context: req.context, context: req.context,
data, data,
operation: 'update', operation: 'update',
@@ -223,11 +228,12 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
let result = await beforeChange<GeneratedTypes['collections'][TSlug]>({ let result = await beforeChange<GeneratedTypes['collections'][TSlug]>({
id, id,
collection: collectionConfig,
context: req.context, context: req.context,
data, data,
doc: originalDoc, doc: originalDoc,
docWithLocales, docWithLocales,
entityConfig: collectionConfig, global: null,
operation: 'update', operation: 'update',
req, req,
skipValidation: shouldSaveDraft || data._status === 'draft', skipValidation: shouldSaveDraft || data._status === 'draft',
@@ -285,10 +291,11 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
// ///////////////////////////////////// // /////////////////////////////////////
result = await afterRead({ result = await afterRead({
collection: collectionConfig,
context: req.context, context: req.context,
depth, depth,
doc: result, doc: result,
entityConfig: collectionConfig, global: null,
overrideAccess, overrideAccess,
req, req,
showHiddenFields, showHiddenFields,
@@ -303,6 +310,7 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
result = result =
(await hook({ (await hook({
collection: collectionConfig,
context: req.context, context: req.context,
doc: result, doc: result,
req, req,
@@ -314,10 +322,11 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
// ///////////////////////////////////// // /////////////////////////////////////
result = await afterChange<GeneratedTypes['collections'][TSlug]>({ result = await afterChange<GeneratedTypes['collections'][TSlug]>({
collection: collectionConfig,
context: req.context, context: req.context,
data, data,
doc: result, doc: result,
entityConfig: collectionConfig, global: null,
operation: 'update', operation: 'update',
previousDoc: originalDoc, previousDoc: originalDoc,
req, req,
@@ -332,6 +341,7 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
result = result =
(await hook({ (await hook({
collection: collectionConfig,
context: req.context, context: req.context,
doc: result, doc: result,
operation: 'update', operation: 'update',
@@ -346,6 +356,7 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
result = await buildAfterOperation<GeneratedTypes['collections'][TSlug]>({ result = await buildAfterOperation<GeneratedTypes['collections'][TSlug]>({
args, args,
collection: collectionConfig,
operation: 'updateByID', operation: 'updateByID',
result, result,
}) })

View File

@@ -1,7 +1,7 @@
import type forgotPassword from '../../auth/operations/forgotPassword' import type forgotPassword from '../../auth/operations/forgotPassword'
import type login from '../../auth/operations/login' import type login from '../../auth/operations/login'
import type refresh from '../../auth/operations/refresh' import type refresh from '../../auth/operations/refresh'
import type { AfterOperationHook, TypeWithID } from '../config/types' import type { AfterOperationHook, SanitizedCollectionConfig, TypeWithID } from '../config/types'
import type create from './create' import type create from './create'
import type deleteOperation from './delete' import type deleteOperation from './delete'
import type deleteByID from './deleteByID' import type deleteByID from './deleteByID'
@@ -25,51 +25,71 @@ export type AfterOperationMap<T extends TypeWithID> = {
export type AfterOperationArg<T extends TypeWithID> = export type AfterOperationArg<T extends TypeWithID> =
| { | {
args: Parameters<AfterOperationMap<T>['create']>[0] args: Parameters<AfterOperationMap<T>['create']>[0]
/** The collection which this hook is being run on */
collection: SanitizedCollectionConfig
operation: 'create' operation: 'create'
result: Awaited<ReturnType<AfterOperationMap<T>['create']>> result: Awaited<ReturnType<AfterOperationMap<T>['create']>>
} }
| { | {
args: Parameters<AfterOperationMap<T>['delete']>[0] args: Parameters<AfterOperationMap<T>['delete']>[0]
/** The collection which this hook is being run on */
collection: SanitizedCollectionConfig
operation: 'delete' operation: 'delete'
result: Awaited<ReturnType<AfterOperationMap<T>['delete']>> result: Awaited<ReturnType<AfterOperationMap<T>['delete']>>
} }
| { | {
args: Parameters<AfterOperationMap<T>['deleteByID']>[0] args: Parameters<AfterOperationMap<T>['deleteByID']>[0]
/** The collection which this hook is being run on */
collection: SanitizedCollectionConfig
operation: 'deleteByID' operation: 'deleteByID'
result: Awaited<ReturnType<AfterOperationMap<T>['deleteByID']>> result: Awaited<ReturnType<AfterOperationMap<T>['deleteByID']>>
} }
| { | {
args: Parameters<AfterOperationMap<T>['find']>[0] args: Parameters<AfterOperationMap<T>['find']>[0]
/** The collection which this hook is being run on */
collection: SanitizedCollectionConfig
operation: 'find' operation: 'find'
result: Awaited<ReturnType<AfterOperationMap<T>['find']>> result: Awaited<ReturnType<AfterOperationMap<T>['find']>>
} }
| { | {
args: Parameters<AfterOperationMap<T>['findByID']>[0] args: Parameters<AfterOperationMap<T>['findByID']>[0]
/** The collection which this hook is being run on */
collection: SanitizedCollectionConfig
operation: 'findByID' operation: 'findByID'
result: Awaited<ReturnType<AfterOperationMap<T>['findByID']>> result: Awaited<ReturnType<AfterOperationMap<T>['findByID']>>
} }
| { | {
args: Parameters<AfterOperationMap<T>['forgotPassword']>[0] args: Parameters<AfterOperationMap<T>['forgotPassword']>[0]
/** The collection which this hook is being run on */
collection: SanitizedCollectionConfig
operation: 'forgotPassword' operation: 'forgotPassword'
result: Awaited<ReturnType<AfterOperationMap<T>['forgotPassword']>> result: Awaited<ReturnType<AfterOperationMap<T>['forgotPassword']>>
} }
| { | {
args: Parameters<AfterOperationMap<T>['login']>[0] args: Parameters<AfterOperationMap<T>['login']>[0]
/** The collection which this hook is being run on */
collection: SanitizedCollectionConfig
operation: 'login' operation: 'login'
result: Awaited<ReturnType<AfterOperationMap<T>['login']>> result: Awaited<ReturnType<AfterOperationMap<T>['login']>>
} }
| { | {
args: Parameters<AfterOperationMap<T>['refresh']>[0] args: Parameters<AfterOperationMap<T>['refresh']>[0]
/** The collection which this hook is being run on */
collection: SanitizedCollectionConfig
operation: 'refresh' operation: 'refresh'
result: Awaited<ReturnType<AfterOperationMap<T>['refresh']>> result: Awaited<ReturnType<AfterOperationMap<T>['refresh']>>
} }
| { | {
args: Parameters<AfterOperationMap<T>['update']>[0] args: Parameters<AfterOperationMap<T>['update']>[0]
/** The collection which this hook is being run on */
collection: SanitizedCollectionConfig
operation: 'update' operation: 'update'
result: Awaited<ReturnType<AfterOperationMap<T>['update']>> result: Awaited<ReturnType<AfterOperationMap<T>['update']>>
} }
| { | {
args: Parameters<AfterOperationMap<T>['updateByID']>[0] args: Parameters<AfterOperationMap<T>['updateByID']>[0]
/** The collection which this hook is being run on */
collection: SanitizedCollectionConfig
operation: 'updateByID' operation: 'updateByID'
result: Awaited<ReturnType<AfterOperationMap<T>['updateByID']>> result: Awaited<ReturnType<AfterOperationMap<T>['updateByID']>>
} }
@@ -82,7 +102,7 @@ export const buildAfterOperation = async <
>( >(
operationArgs: AfterOperationArg<T> & { operation: O }, operationArgs: AfterOperationArg<T> & { operation: O },
): Promise<Awaited<ReturnType<AfterOperationMap<T>[O]>>> => { ): Promise<Awaited<ReturnType<AfterOperationMap<T>[O]>>> => {
const { args, operation, result } = operationArgs const { args, collection, operation, result } = operationArgs
let newResult = result let newResult = result
@@ -92,6 +112,7 @@ export const buildAfterOperation = async <
const hookResult = await hook({ const hookResult = await hook({
args, args,
collection,
operation, operation,
result: newResult, result: newResult,
} as AfterOperationArg<T>) } as AfterOperationArg<T>)

View File

@@ -47,11 +47,17 @@ const errorHandler =
err, err,
response, response,
req.context, req.context,
req.collection.config,
)) || { response, status }) )) || { response, status })
} }
if (typeof config.hooks.afterError === 'function') { if (typeof config.hooks.afterError === 'function') {
;({ response, status } = (await config.hooks.afterError(err, response, req.context)) || { ;({ response, status } = (await config.hooks.afterError(
err,
response,
req.context,
req.collection.config,
)) || {
response, response,
status, status,
}) })

View File

@@ -9,18 +9,25 @@ import type { Description } from '../../admin/components/forms/FieldDescription/
import type { RowLabel } from '../../admin/components/forms/RowLabel/types' import type { RowLabel } from '../../admin/components/forms/RowLabel/types'
import type { RichTextAdapter } from '../../admin/components/forms/field-types/RichText/types' import type { RichTextAdapter } from '../../admin/components/forms/field-types/RichText/types'
import type { User } from '../../auth' import type { User } from '../../auth'
import type { TypeWithID } from '../../collections/config/types' import type { SanitizedCollectionConfig, TypeWithID } from '../../collections/config/types'
import type { SanitizedConfig } from '../../config/types' import type { SanitizedConfig } from '../../config/types'
import type { PayloadRequest, RequestContext } from '../../express/types' import type { PayloadRequest, RequestContext } from '../../express/types'
import type { SanitizedGlobalConfig } from '../../globals/config/types'
import type { Payload } from '../../payload' import type { Payload } from '../../payload'
import type { Operation, Where } from '../../types' import type { Operation, Where } from '../../types'
export type FieldHookArgs<T extends TypeWithID = any, P = any, S = any> = { export type FieldHookArgs<T extends TypeWithID = any, P = any, S = any> = {
/** The collection which the field belongs to. If the field belongs to a global, this will be null. */
collection: SanitizedCollectionConfig | null
context: RequestContext context: RequestContext
/** The data passed to update the document within create and update operations, and the full document itself in the afterRead hook. */ /** The data passed to update the document within create and update operations, and the full document itself in the afterRead hook. */
data?: Partial<T> data?: Partial<T>
/** The field which the hook is running against. */
field: FieldAffectingData
/** Boolean to denote if this hook is running against finding one, or finding many within the afterRead hook. */ /** Boolean to denote if this hook is running against finding one, or finding many within the afterRead hook. */
findMany?: boolean findMany?: boolean
/** The global which the field belongs to. If the field belongs to a collection, this will be null. */
global: SanitizedGlobalConfig | null
/** A string relating to which operation the field type is currently executing within. Useful within beforeValidate, beforeChange, and afterChange hooks to differentiate between create and update operations. */ /** A string relating to which operation the field type is currently executing within. Useful within beforeValidate, beforeChange, and afterChange hooks to differentiate between create and update operations. */
operation?: 'create' | 'delete' | 'read' | 'update' operation?: 'create' | 'delete' | 'read' | 'update'
/** The full original document in `update` operations. In the `afterChange` hook, this is the resulting document of the operation. */ /** The full original document in `update` operations. In the `afterChange` hook, this is the resulting document of the operation. */

View File

@@ -6,20 +6,23 @@ import { deepCopyObject } from '../../../utilities/deepCopyObject'
import { traverseFields } from './traverseFields' import { traverseFields } from './traverseFields'
type Args<T> = { type Args<T> = {
collection: SanitizedCollectionConfig | null
context: RequestContext context: RequestContext
data: Record<string, unknown> | T data: Record<string, unknown> | T
doc: Record<string, unknown> | T doc: Record<string, unknown> | T
entityConfig: SanitizedCollectionConfig | SanitizedGlobalConfig global: SanitizedGlobalConfig | null
operation: 'create' | 'update' operation: 'create' | 'update'
previousDoc: Record<string, unknown> | T previousDoc: Record<string, unknown> | T
req: PayloadRequest req: PayloadRequest
} }
export const afterChange = async <T extends Record<string, unknown>>({ export const afterChange = async <T extends Record<string, unknown>>({
collection,
context, context,
data, data,
doc: incomingDoc, doc: incomingDoc,
entityConfig, global,
operation, operation,
previousDoc, previousDoc,
req, req,
@@ -27,10 +30,12 @@ export const afterChange = async <T extends Record<string, unknown>>({
const doc = deepCopyObject(incomingDoc) const doc = deepCopyObject(incomingDoc)
await traverseFields({ await traverseFields({
collection,
context, context,
data, data,
doc, doc,
fields: entityConfig.fields, fields: collection?.fields || global?.fields,
global,
operation, operation,
previousDoc, previousDoc,
previousSiblingDoc: previousDoc, previousSiblingDoc: previousDoc,

View File

@@ -1,15 +1,19 @@
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
import type { SanitizedCollectionConfig } from '../../../collections/config/types'
import type { PayloadRequest, RequestContext } from '../../../express/types' import type { PayloadRequest, RequestContext } from '../../../express/types'
import type { SanitizedGlobalConfig } from '../../../globals/config/types'
import type { Field, TabAsField } from '../../config/types' import type { Field, TabAsField } from '../../config/types'
import { fieldAffectsData, tabHasName } from '../../config/types' import { fieldAffectsData, tabHasName } from '../../config/types'
import { traverseFields } from './traverseFields' import { traverseFields } from './traverseFields'
type Args = { type Args = {
collection: SanitizedCollectionConfig | null
context: RequestContext context: RequestContext
data: Record<string, unknown> data: Record<string, unknown>
doc: Record<string, unknown> doc: Record<string, unknown>
field: Field | TabAsField field: Field | TabAsField
global: SanitizedGlobalConfig | null
operation: 'create' | 'update' operation: 'create' | 'update'
previousDoc: Record<string, unknown> previousDoc: Record<string, unknown>
previousSiblingDoc: Record<string, unknown> previousSiblingDoc: Record<string, unknown>
@@ -22,10 +26,12 @@ type Args = {
// - Execute field hooks // - Execute field hooks
export const promise = async ({ export const promise = async ({
collection,
context, context,
data, data,
doc, doc,
field, field,
global,
operation, operation,
previousDoc, previousDoc,
previousSiblingDoc, previousSiblingDoc,
@@ -40,8 +46,11 @@ export const promise = async ({
await priorHook await priorHook
const hookedValue = await currentHook({ const hookedValue = await currentHook({
collection,
context, context,
data, data,
field,
global,
operation, operation,
originalDoc: doc, originalDoc: doc,
previousDoc, previousDoc,
@@ -63,10 +72,12 @@ export const promise = async ({
switch (field.type) { switch (field.type) {
case 'group': { case 'group': {
await traverseFields({ await traverseFields({
collection,
context, context,
data, data,
doc, doc,
fields: field.fields, fields: field.fields,
global,
operation, operation,
previousDoc, previousDoc,
previousSiblingDoc: previousDoc[field.name] as Record<string, unknown>, previousSiblingDoc: previousDoc[field.name] as Record<string, unknown>,
@@ -86,10 +97,12 @@ export const promise = async ({
rows.forEach((row, i) => { rows.forEach((row, i) => {
promises.push( promises.push(
traverseFields({ traverseFields({
collection,
context, context,
data, data,
doc, doc,
fields: field.fields, fields: field.fields,
global,
operation, operation,
previousDoc, previousDoc,
previousSiblingDoc: previousDoc?.[field.name]?.[i] || ({} as Record<string, unknown>), previousSiblingDoc: previousDoc?.[field.name]?.[i] || ({} as Record<string, unknown>),
@@ -115,10 +128,12 @@ export const promise = async ({
if (block) { if (block) {
promises.push( promises.push(
traverseFields({ traverseFields({
collection,
context, context,
data, data,
doc, doc,
fields: block.fields, fields: block.fields,
global,
operation, operation,
previousDoc, previousDoc,
previousSiblingDoc: previousSiblingDoc:
@@ -139,10 +154,12 @@ export const promise = async ({
case 'row': case 'row':
case 'collapsible': { case 'collapsible': {
await traverseFields({ await traverseFields({
collection,
context, context,
data, data,
doc, doc,
fields: field.fields, fields: field.fields,
global,
operation, operation,
previousDoc, previousDoc,
previousSiblingDoc: { ...previousSiblingDoc }, previousSiblingDoc: { ...previousSiblingDoc },
@@ -166,10 +183,12 @@ export const promise = async ({
} }
await traverseFields({ await traverseFields({
collection,
context, context,
data, data,
doc, doc,
fields: field.fields, fields: field.fields,
global,
operation, operation,
previousDoc, previousDoc,
previousSiblingDoc: tabPreviousSiblingDoc, previousSiblingDoc: tabPreviousSiblingDoc,
@@ -183,10 +202,12 @@ export const promise = async ({
case 'tabs': { case 'tabs': {
await traverseFields({ await traverseFields({
collection,
context, context,
data, data,
doc, doc,
fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })), fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })),
global,
operation, operation,
previousDoc, previousDoc,
previousSiblingDoc: { ...previousSiblingDoc }, previousSiblingDoc: { ...previousSiblingDoc },

View File

@@ -1,13 +1,17 @@
import type { SanitizedCollectionConfig } from '../../../collections/config/types'
import type { PayloadRequest, RequestContext } from '../../../express/types' import type { PayloadRequest, RequestContext } from '../../../express/types'
import type { SanitizedGlobalConfig } from '../../../globals/config/types'
import type { Field, TabAsField } from '../../config/types' import type { Field, TabAsField } from '../../config/types'
import { promise } from './promise' import { promise } from './promise'
type Args = { type Args = {
collection: SanitizedCollectionConfig | null
context: RequestContext context: RequestContext
data: Record<string, unknown> data: Record<string, unknown>
doc: Record<string, unknown> doc: Record<string, unknown>
fields: (Field | TabAsField)[] fields: (Field | TabAsField)[]
global: SanitizedGlobalConfig | null
operation: 'create' | 'update' operation: 'create' | 'update'
previousDoc: Record<string, unknown> previousDoc: Record<string, unknown>
previousSiblingDoc: Record<string, unknown> previousSiblingDoc: Record<string, unknown>
@@ -17,10 +21,12 @@ type Args = {
} }
export const traverseFields = async ({ export const traverseFields = async ({
collection,
context, context,
data, data,
doc, doc,
fields, fields,
global,
operation, operation,
previousDoc, previousDoc,
previousSiblingDoc, previousSiblingDoc,
@@ -33,10 +39,12 @@ export const traverseFields = async ({
fields.forEach((field) => { fields.forEach((field) => {
promises.push( promises.push(
promise({ promise({
collection,
context, context,
data, data,
doc, doc,
field, field,
global,
operation, operation,
previousDoc, previousDoc,
previousSiblingDoc, previousSiblingDoc,

View File

@@ -6,13 +6,14 @@ import { deepCopyObject } from '../../../utilities/deepCopyObject'
import { traverseFields } from './traverseFields' import { traverseFields } from './traverseFields'
type Args = { type Args = {
collection: SanitizedCollectionConfig | null
context: RequestContext context: RequestContext
currentDepth?: number currentDepth?: number
depth: number depth: number
doc: Record<string, unknown> doc: Record<string, unknown>
entityConfig: SanitizedCollectionConfig | SanitizedGlobalConfig
findMany?: boolean findMany?: boolean
flattenLocales?: boolean flattenLocales?: boolean
global: SanitizedGlobalConfig | null
overrideAccess: boolean overrideAccess: boolean
req: PayloadRequest req: PayloadRequest
showHiddenFields: boolean showHiddenFields: boolean
@@ -20,13 +21,14 @@ type Args = {
export async function afterRead<T = any>(args: Args): Promise<T> { export async function afterRead<T = any>(args: Args): Promise<T> {
const { const {
collection,
context, context,
currentDepth: incomingCurrentDepth, currentDepth: incomingCurrentDepth,
depth: incomingDepth, depth: incomingDepth,
doc: incomingDoc, doc: incomingDoc,
entityConfig,
findMany, findMany,
flattenLocales = true, flattenLocales = true,
global,
overrideAccess, overrideAccess,
req, req,
showHiddenFields, showHiddenFields,
@@ -45,14 +47,16 @@ export async function afterRead<T = any>(args: Args): Promise<T> {
const currentDepth = incomingCurrentDepth || 1 const currentDepth = incomingCurrentDepth || 1
traverseFields({ traverseFields({
collection,
context, context,
currentDepth, currentDepth,
depth, depth,
doc, doc,
fieldPromises, fieldPromises,
fields: entityConfig.fields, fields: collection?.fields || global?.fields,
findMany, findMany,
flattenLocales, flattenLocales,
global,
overrideAccess, overrideAccess,
populationPromises, populationPromises,
req, req,

View File

@@ -1,6 +1,8 @@
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
import type { RichTextAdapter } from '../../../admin/components/forms/field-types/RichText/types' import type { RichTextAdapter } from '../../../admin/components/forms/field-types/RichText/types'
import type { SanitizedCollectionConfig } from '../../../collections/config/types'
import type { PayloadRequest, RequestContext } from '../../../express/types' import type { PayloadRequest, RequestContext } from '../../../express/types'
import type { SanitizedGlobalConfig } from '../../../globals/config/types'
import type { Field, TabAsField } from '../../config/types' import type { Field, TabAsField } from '../../config/types'
import { fieldAffectsData, tabHasName } from '../../config/types' import { fieldAffectsData, tabHasName } from '../../config/types'
@@ -8,6 +10,7 @@ import relationshipPopulationPromise from './relationshipPopulationPromise'
import { traverseFields } from './traverseFields' import { traverseFields } from './traverseFields'
type Args = { type Args = {
collection: SanitizedCollectionConfig | null
context: RequestContext context: RequestContext
currentDepth: number currentDepth: number
depth: number depth: number
@@ -16,6 +19,7 @@ type Args = {
fieldPromises: Promise<void>[] fieldPromises: Promise<void>[]
findMany: boolean findMany: boolean
flattenLocales: boolean flattenLocales: boolean
global: SanitizedGlobalConfig | null
overrideAccess: boolean overrideAccess: boolean
populationPromises: Promise<void>[] populationPromises: Promise<void>[]
req: PayloadRequest req: PayloadRequest
@@ -32,6 +36,7 @@ type Args = {
// - Populate relationships // - Populate relationships
export const promise = async ({ export const promise = async ({
collection,
context, context,
currentDepth, currentDepth,
depth, depth,
@@ -40,6 +45,7 @@ export const promise = async ({
fieldPromises, fieldPromises,
findMany, findMany,
flattenLocales, flattenLocales,
global,
overrideAccess, overrideAccess,
populationPromises, populationPromises,
req, req,
@@ -179,8 +185,11 @@ export const promise = async ({
const hookPromises = Object.entries(siblingDoc[field.name]).map(([locale, value]) => const hookPromises = Object.entries(siblingDoc[field.name]).map(([locale, value]) =>
(async () => { (async () => {
const hookedValue = await currentHook({ const hookedValue = await currentHook({
collection,
context, context,
data: doc, data: doc,
field,
global,
operation: 'read', operation: 'read',
originalDoc: doc, originalDoc: doc,
req, req,
@@ -197,9 +206,12 @@ export const promise = async ({
await Promise.all(hookPromises) await Promise.all(hookPromises)
} else { } else {
const hookedValue = await currentHook({ const hookedValue = await currentHook({
collection,
context, context,
data: doc, data: doc,
field,
findMany, findMany,
global,
operation: 'read', operation: 'read',
originalDoc: doc, originalDoc: doc,
req, req,
@@ -252,6 +264,7 @@ export const promise = async ({
if (typeof siblingDoc[field.name] !== 'object') groupDoc = {} if (typeof siblingDoc[field.name] !== 'object') groupDoc = {}
traverseFields({ traverseFields({
collection,
context, context,
currentDepth, currentDepth,
depth, depth,
@@ -260,6 +273,7 @@ export const promise = async ({
fields: field.fields, fields: field.fields,
findMany, findMany,
flattenLocales, flattenLocales,
global,
overrideAccess, overrideAccess,
populationPromises, populationPromises,
req, req,
@@ -276,6 +290,7 @@ export const promise = async ({
if (Array.isArray(rows)) { if (Array.isArray(rows)) {
rows.forEach((row) => { rows.forEach((row) => {
traverseFields({ traverseFields({
collection,
context, context,
currentDepth, currentDepth,
depth, depth,
@@ -284,6 +299,7 @@ export const promise = async ({
fields: field.fields, fields: field.fields,
findMany, findMany,
flattenLocales, flattenLocales,
global,
overrideAccess, overrideAccess,
populationPromises, populationPromises,
req, req,
@@ -296,6 +312,7 @@ export const promise = async ({
if (Array.isArray(localeRows)) { if (Array.isArray(localeRows)) {
localeRows.forEach((row) => { localeRows.forEach((row) => {
traverseFields({ traverseFields({
collection,
context, context,
currentDepth, currentDepth,
depth, depth,
@@ -304,6 +321,7 @@ export const promise = async ({
fields: field.fields, fields: field.fields,
findMany, findMany,
flattenLocales, flattenLocales,
global,
overrideAccess, overrideAccess,
populationPromises, populationPromises,
req, req,
@@ -328,6 +346,7 @@ export const promise = async ({
if (block) { if (block) {
traverseFields({ traverseFields({
collection,
context, context,
currentDepth, currentDepth,
depth, depth,
@@ -336,6 +355,7 @@ export const promise = async ({
fields: block.fields, fields: block.fields,
findMany, findMany,
flattenLocales, flattenLocales,
global,
overrideAccess, overrideAccess,
populationPromises, populationPromises,
req, req,
@@ -352,6 +372,7 @@ export const promise = async ({
if (block) { if (block) {
traverseFields({ traverseFields({
collection,
context, context,
currentDepth, currentDepth,
depth, depth,
@@ -360,6 +381,7 @@ export const promise = async ({
fields: block.fields, fields: block.fields,
findMany, findMany,
flattenLocales, flattenLocales,
global,
overrideAccess, overrideAccess,
populationPromises, populationPromises,
req, req,
@@ -380,6 +402,7 @@ export const promise = async ({
case 'row': case 'row':
case 'collapsible': { case 'collapsible': {
traverseFields({ traverseFields({
collection,
context, context,
currentDepth, currentDepth,
depth, depth,
@@ -388,6 +411,7 @@ export const promise = async ({
fields: field.fields, fields: field.fields,
findMany, findMany,
flattenLocales, flattenLocales,
global,
overrideAccess, overrideAccess,
populationPromises, populationPromises,
req, req,
@@ -406,6 +430,7 @@ export const promise = async ({
} }
await traverseFields({ await traverseFields({
collection,
context, context,
currentDepth, currentDepth,
depth, depth,
@@ -414,6 +439,7 @@ export const promise = async ({
fields: field.fields, fields: field.fields,
findMany, findMany,
flattenLocales, flattenLocales,
global,
overrideAccess, overrideAccess,
populationPromises, populationPromises,
req, req,
@@ -426,6 +452,7 @@ export const promise = async ({
case 'tabs': { case 'tabs': {
traverseFields({ traverseFields({
collection,
context, context,
currentDepth, currentDepth,
depth, depth,
@@ -434,6 +461,7 @@ export const promise = async ({
fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })), fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })),
findMany, findMany,
flattenLocales, flattenLocales,
global,
overrideAccess, overrideAccess,
populationPromises, populationPromises,
req, req,

View File

@@ -1,9 +1,12 @@
import type { SanitizedCollectionConfig } from '../../../collections/config/types'
import type { PayloadRequest, RequestContext } from '../../../express/types' import type { PayloadRequest, RequestContext } from '../../../express/types'
import type { SanitizedGlobalConfig } from '../../../globals/config/types'
import type { Field, TabAsField } from '../../config/types' import type { Field, TabAsField } from '../../config/types'
import { promise } from './promise' import { promise } from './promise'
type Args = { type Args = {
collection: SanitizedCollectionConfig | null
context: RequestContext context: RequestContext
currentDepth: number currentDepth: number
depth: number depth: number
@@ -12,6 +15,7 @@ type Args = {
fields: (Field | TabAsField)[] fields: (Field | TabAsField)[]
findMany: boolean findMany: boolean
flattenLocales: boolean flattenLocales: boolean
global: SanitizedGlobalConfig | null
overrideAccess: boolean overrideAccess: boolean
populationPromises: Promise<void>[] populationPromises: Promise<void>[]
req: PayloadRequest req: PayloadRequest
@@ -20,6 +24,7 @@ type Args = {
} }
export const traverseFields = ({ export const traverseFields = ({
collection,
context, context,
currentDepth, currentDepth,
depth, depth,
@@ -28,6 +33,7 @@ export const traverseFields = ({
fields, fields,
findMany, findMany,
flattenLocales, flattenLocales,
global,
overrideAccess, overrideAccess,
populationPromises, populationPromises,
req, req,
@@ -37,6 +43,7 @@ export const traverseFields = ({
fields.forEach((field) => { fields.forEach((field) => {
fieldPromises.push( fieldPromises.push(
promise({ promise({
collection,
context, context,
currentDepth, currentDepth,
depth, depth,
@@ -45,6 +52,7 @@ export const traverseFields = ({
fieldPromises, fieldPromises,
findMany, findMany,
flattenLocales, flattenLocales,
global,
overrideAccess, overrideAccess,
populationPromises, populationPromises,
req, req,

View File

@@ -8,11 +8,12 @@ import { deepCopyObject } from '../../../utilities/deepCopyObject'
import { traverseFields } from './traverseFields' import { traverseFields } from './traverseFields'
type Args<T> = { type Args<T> = {
collection: SanitizedCollectionConfig | null
context: RequestContext context: RequestContext
data: Record<string, unknown> | T data: Record<string, unknown> | T
doc: Record<string, unknown> | T doc: Record<string, unknown> | T
docWithLocales: Record<string, unknown> docWithLocales: Record<string, unknown>
entityConfig: SanitizedCollectionConfig | SanitizedGlobalConfig global: SanitizedGlobalConfig | null
id?: number | string id?: number | string
operation: Operation operation: Operation
req: PayloadRequest req: PayloadRequest
@@ -21,11 +22,12 @@ type Args<T> = {
export const beforeChange = async <T extends Record<string, unknown>>({ export const beforeChange = async <T extends Record<string, unknown>>({
id, id,
collection,
context, context,
data: incomingData, data: incomingData,
doc, doc,
docWithLocales, docWithLocales,
entityConfig, global,
operation, operation,
req, req,
skipValidation, skipValidation,
@@ -36,12 +38,14 @@ export const beforeChange = async <T extends Record<string, unknown>>({
await traverseFields({ await traverseFields({
id, id,
collection,
context, context,
data, data,
doc, doc,
docWithLocales, docWithLocales,
errors, errors,
fields: entityConfig.fields, fields: collection?.fields || global?.fields,
global,
mergeLocaleActions, mergeLocaleActions,
operation, operation,
path: '', path: '',

View File

@@ -1,7 +1,9 @@
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
import merge from 'deepmerge' import merge from 'deepmerge'
import type { SanitizedCollectionConfig } from '../../../collections/config/types'
import type { PayloadRequest, RequestContext } from '../../../express/types' import type { PayloadRequest, RequestContext } from '../../../express/types'
import type { SanitizedGlobalConfig } from '../../../globals/config/types'
import type { Operation } from '../../../types' import type { Operation } from '../../../types'
import type { Field, TabAsField } from '../../config/types' import type { Field, TabAsField } from '../../config/types'
@@ -10,12 +12,14 @@ import { getExistingRowDoc } from './getExistingRowDoc'
import { traverseFields } from './traverseFields' import { traverseFields } from './traverseFields'
type Args = { type Args = {
collection: SanitizedCollectionConfig | null
context: RequestContext context: RequestContext
data: Record<string, unknown> data: Record<string, unknown>
doc: Record<string, unknown> doc: Record<string, unknown>
docWithLocales: Record<string, unknown> docWithLocales: Record<string, unknown>
errors: { field: string; message: string }[] errors: { field: string; message: string }[]
field: Field | TabAsField field: Field | TabAsField
global: SanitizedGlobalConfig | null
id?: number | string id?: number | string
mergeLocaleActions: (() => void)[] mergeLocaleActions: (() => void)[]
operation: Operation operation: Operation
@@ -36,12 +40,14 @@ type Args = {
export const promise = async ({ export const promise = async ({
id, id,
collection,
context, context,
data, data,
doc, doc,
docWithLocales, docWithLocales,
errors, errors,
field, field,
global,
mergeLocaleActions, mergeLocaleActions,
operation, operation,
path, path,
@@ -75,8 +81,11 @@ export const promise = async ({
await priorHook await priorHook
const hookedValue = await currentHook({ const hookedValue = await currentHook({
collection,
context, context,
data, data,
field,
global,
operation, operation,
originalDoc: doc, originalDoc: doc,
req, req,
@@ -183,12 +192,14 @@ export const promise = async ({
await traverseFields({ await traverseFields({
id, id,
collection,
context, context,
data, data,
doc, doc,
docWithLocales, docWithLocales,
errors, errors,
fields: field.fields, fields: field.fields,
global,
mergeLocaleActions, mergeLocaleActions,
operation, operation,
path: `${path}${field.name}.`, path: `${path}${field.name}.`,
@@ -211,12 +222,14 @@ export const promise = async ({
promises.push( promises.push(
traverseFields({ traverseFields({
id, id,
collection,
context, context,
data, data,
doc, doc,
docWithLocales, docWithLocales,
errors, errors,
fields: field.fields, fields: field.fields,
global,
mergeLocaleActions, mergeLocaleActions,
operation, operation,
path: `${path}${field.name}.${i}.`, path: `${path}${field.name}.${i}.`,
@@ -251,12 +264,14 @@ export const promise = async ({
promises.push( promises.push(
traverseFields({ traverseFields({
id, id,
collection,
context, context,
data, data,
doc, doc,
docWithLocales, docWithLocales,
errors, errors,
fields: block.fields, fields: block.fields,
global,
mergeLocaleActions, mergeLocaleActions,
operation, operation,
path: `${path}${field.name}.${i}.`, path: `${path}${field.name}.${i}.`,
@@ -280,12 +295,14 @@ export const promise = async ({
case 'collapsible': { case 'collapsible': {
await traverseFields({ await traverseFields({
id, id,
collection,
context, context,
data, data,
doc, doc,
docWithLocales, docWithLocales,
errors, errors,
fields: field.fields, fields: field.fields,
global,
mergeLocaleActions, mergeLocaleActions,
operation, operation,
path, path,
@@ -319,12 +336,14 @@ export const promise = async ({
await traverseFields({ await traverseFields({
id, id,
collection,
context, context,
data, data,
doc, doc,
docWithLocales, docWithLocales,
errors, errors,
fields: field.fields, fields: field.fields,
global,
mergeLocaleActions, mergeLocaleActions,
operation, operation,
path: tabPath, path: tabPath,
@@ -341,12 +360,14 @@ export const promise = async ({
case 'tabs': { case 'tabs': {
await traverseFields({ await traverseFields({
id, id,
collection,
context, context,
data, data,
doc, doc,
docWithLocales, docWithLocales,
errors, errors,
fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })), fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })),
global,
mergeLocaleActions, mergeLocaleActions,
operation, operation,
path, path,

View File

@@ -1,16 +1,20 @@
import type { SanitizedCollectionConfig } from '../../../collections/config/types'
import type { PayloadRequest, RequestContext } from '../../../express/types' import type { PayloadRequest, RequestContext } from '../../../express/types'
import type { SanitizedGlobalConfig } from '../../../globals/config/types'
import type { Operation } from '../../../types' import type { Operation } from '../../../types'
import type { Field, TabAsField } from '../../config/types' import type { Field, TabAsField } from '../../config/types'
import { promise } from './promise' import { promise } from './promise'
type Args = { type Args = {
collection: SanitizedCollectionConfig | null
context: RequestContext context: RequestContext
data: Record<string, unknown> data: Record<string, unknown>
doc: Record<string, unknown> doc: Record<string, unknown>
docWithLocales: Record<string, unknown> docWithLocales: Record<string, unknown>
errors: { field: string; message: string }[] errors: { field: string; message: string }[]
fields: (Field | TabAsField)[] fields: (Field | TabAsField)[]
global: SanitizedGlobalConfig | null
id?: number | string id?: number | string
mergeLocaleActions: (() => void)[] mergeLocaleActions: (() => void)[]
operation: Operation operation: Operation
@@ -24,12 +28,14 @@ type Args = {
export const traverseFields = async ({ export const traverseFields = async ({
id, id,
collection,
context, context,
data, data,
doc, doc,
docWithLocales, docWithLocales,
errors, errors,
fields, fields,
global,
mergeLocaleActions, mergeLocaleActions,
operation, operation,
path, path,
@@ -45,12 +51,14 @@ export const traverseFields = async ({
promises.push( promises.push(
promise({ promise({
id, id,
collection,
context, context,
data, data,
doc, doc,
docWithLocales, docWithLocales,
errors, errors,
field, field,
global,
mergeLocaleActions, mergeLocaleActions,
operation, operation,
path, path,

View File

@@ -6,10 +6,11 @@ import { deepCopyObject } from '../../../utilities/deepCopyObject'
import { traverseFields } from './traverseFields' import { traverseFields } from './traverseFields'
type Args<T> = { type Args<T> = {
collection: SanitizedCollectionConfig | null
context: RequestContext context: RequestContext
data: Record<string, unknown> | T data: Record<string, unknown> | T
doc?: Record<string, unknown> | T doc?: Record<string, unknown> | T
entityConfig: SanitizedCollectionConfig | SanitizedGlobalConfig global: SanitizedGlobalConfig | null
id?: number | string id?: number | string
operation: 'create' | 'update' operation: 'create' | 'update'
overrideAccess: boolean overrideAccess: boolean
@@ -18,10 +19,11 @@ type Args<T> = {
export const beforeValidate = async <T extends Record<string, unknown>>({ export const beforeValidate = async <T extends Record<string, unknown>>({
id, id,
collection,
context, context,
data: incomingData, data: incomingData,
doc, doc,
entityConfig, global,
operation, operation,
overrideAccess, overrideAccess,
req, req,
@@ -30,10 +32,12 @@ export const beforeValidate = async <T extends Record<string, unknown>>({
await traverseFields({ await traverseFields({
id, id,
collection,
context, context,
data, data,
doc, doc,
fields: entityConfig.fields, fields: collection?.fields || global?.fields,
global,
operation, operation,
overrideAccess, overrideAccess,
req, req,

View File

@@ -1,5 +1,7 @@
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
import type { SanitizedCollectionConfig } from '../../../collections/config/types'
import type { PayloadRequest, RequestContext } from '../../../express/types' import type { PayloadRequest, RequestContext } from '../../../express/types'
import type { SanitizedGlobalConfig } from '../../../globals/config/types'
import type { Field, TabAsField } from '../../config/types' import type { Field, TabAsField } from '../../config/types'
import { fieldAffectsData, tabHasName, valueIsValueWithRelation } from '../../config/types' import { fieldAffectsData, tabHasName, valueIsValueWithRelation } from '../../config/types'
@@ -9,10 +11,12 @@ import { getExistingRowDoc } from '../beforeChange/getExistingRowDoc'
import { traverseFields } from './traverseFields' import { traverseFields } from './traverseFields'
type Args<T> = { type Args<T> = {
collection: SanitizedCollectionConfig | null
context: RequestContext context: RequestContext
data: T data: T
doc: T doc: T
field: Field | TabAsField field: Field | TabAsField
global: SanitizedGlobalConfig | null
id?: number | string id?: number | string
operation: 'create' | 'update' operation: 'create' | 'update'
overrideAccess: boolean overrideAccess: boolean
@@ -30,10 +34,12 @@ type Args<T> = {
export const promise = async <T>({ export const promise = async <T>({
id, id,
collection,
context, context,
data, data,
doc, doc,
field, field,
global,
operation, operation,
overrideAccess, overrideAccess,
req, req,
@@ -209,8 +215,11 @@ export const promise = async <T>({
await priorHook await priorHook
const hookedValue = await currentHook({ const hookedValue = await currentHook({
collection,
context, context,
data, data,
field,
global,
operation, operation,
originalDoc: doc, originalDoc: doc,
req, req,
@@ -263,10 +272,12 @@ export const promise = async <T>({
await traverseFields({ await traverseFields({
id, id,
collection,
context, context,
data, data,
doc, doc,
fields: field.fields, fields: field.fields,
global,
operation, operation,
overrideAccess, overrideAccess,
req, req,
@@ -282,14 +293,16 @@ export const promise = async <T>({
if (Array.isArray(rows)) { if (Array.isArray(rows)) {
const promises = [] const promises = []
rows.forEach((row, i) => { rows.forEach((row) => {
promises.push( promises.push(
traverseFields({ traverseFields({
id, id,
collection,
context, context,
data, data,
doc, doc,
fields: field.fields, fields: field.fields,
global,
operation, operation,
overrideAccess, overrideAccess,
req, req,
@@ -308,7 +321,7 @@ export const promise = async <T>({
if (Array.isArray(rows)) { if (Array.isArray(rows)) {
const promises = [] const promises = []
rows.forEach((row, i) => { rows.forEach((row) => {
const rowSiblingDoc = getExistingRowDoc(row, siblingDoc[field.name]) const rowSiblingDoc = getExistingRowDoc(row, siblingDoc[field.name])
const blockTypeToMatch = row.blockType || rowSiblingDoc.blockType const blockTypeToMatch = row.blockType || rowSiblingDoc.blockType
const block = field.blocks.find((blockType) => blockType.slug === blockTypeToMatch) const block = field.blocks.find((blockType) => blockType.slug === blockTypeToMatch)
@@ -319,10 +332,12 @@ export const promise = async <T>({
promises.push( promises.push(
traverseFields({ traverseFields({
id, id,
collection,
context, context,
data, data,
doc, doc,
fields: block.fields, fields: block.fields,
global,
operation, operation,
overrideAccess, overrideAccess,
req, req,
@@ -342,10 +357,12 @@ export const promise = async <T>({
case 'collapsible': { case 'collapsible': {
await traverseFields({ await traverseFields({
id, id,
collection,
context, context,
data, data,
doc, doc,
fields: field.fields, fields: field.fields,
global,
operation, operation,
overrideAccess, overrideAccess,
req, req,
@@ -372,10 +389,12 @@ export const promise = async <T>({
await traverseFields({ await traverseFields({
id, id,
collection,
context, context,
data, data,
doc, doc,
fields: field.fields, fields: field.fields,
global,
operation, operation,
overrideAccess, overrideAccess,
req, req,
@@ -389,10 +408,12 @@ export const promise = async <T>({
case 'tabs': { case 'tabs': {
await traverseFields({ await traverseFields({
id, id,
collection,
context, context,
data, data,
doc, doc,
fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })), fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })),
global,
operation, operation,
overrideAccess, overrideAccess,
req, req,

View File

@@ -1,13 +1,17 @@
import type { SanitizedCollectionConfig } from '../../../collections/config/types'
import type { PayloadRequest, RequestContext } from '../../../express/types' import type { PayloadRequest, RequestContext } from '../../../express/types'
import type { SanitizedGlobalConfig } from '../../../globals/config/types'
import type { Field, TabAsField } from '../../config/types' import type { Field, TabAsField } from '../../config/types'
import { promise } from './promise' import { promise } from './promise'
type Args<T> = { type Args<T> = {
collection: SanitizedCollectionConfig | null
context: RequestContext context: RequestContext
data: T data: T
doc: T doc: T
fields: (Field | TabAsField)[] fields: (Field | TabAsField)[]
global: SanitizedGlobalConfig | null
id?: number | string id?: number | string
operation: 'create' | 'update' operation: 'create' | 'update'
overrideAccess: boolean overrideAccess: boolean
@@ -18,10 +22,12 @@ type Args<T> = {
export const traverseFields = async <T>({ export const traverseFields = async <T>({
id, id,
collection,
context, context,
data, data,
doc, doc,
fields, fields,
global,
operation, operation,
overrideAccess, overrideAccess,
req, req,
@@ -33,10 +39,12 @@ export const traverseFields = async <T>({
promises.push( promises.push(
promise({ promise({
id, id,
collection,
context, context,
data, data,
doc, doc,
field, field,
global,
operation, operation,
overrideAccess, overrideAccess,
req, req,

View File

@@ -18,6 +18,7 @@ import type {
LivePreviewConfig, LivePreviewConfig,
} from '../../config/types' } from '../../config/types'
import type { PayloadRequest } from '../../express/types' import type { PayloadRequest } from '../../express/types'
import type { RequestContext } from '../../express/types'
import type { Field } from '../../fields/config/types' import type { Field } from '../../fields/config/types'
import type { Where } from '../../types' import type { Where } from '../../types'
import type { IncomingGlobalVersions, SanitizedGlobalVersions } from '../../versions/types' import type { IncomingGlobalVersions, SanitizedGlobalVersions } from '../../versions/types'
@@ -27,20 +28,46 @@ export type TypeWithID = {
} }
export type BeforeValidateHook = (args: { export type BeforeValidateHook = (args: {
context: RequestContext
data?: any data?: any
/** The global which this hook is being run on */
global: SanitizedGlobalConfig
originalDoc?: any originalDoc?: any
req?: PayloadRequest req?: PayloadRequest
}) => any }) => any
export type BeforeChangeHook = (args: { data: any; originalDoc?: any; req: PayloadRequest }) => any export type BeforeChangeHook = (args: {
context: RequestContext
data: any
/** The global which this hook is being run on */
global: SanitizedGlobalConfig
originalDoc?: any
req: PayloadRequest
}) => any
export type AfterChangeHook = (args: { doc: any; previousDoc: any; req: PayloadRequest }) => any export type AfterChangeHook = (args: {
context: RequestContext
doc: any
/** The global which this hook is being run on */
global: SanitizedGlobalConfig
previousDoc: any
req: PayloadRequest
}) => any
export type BeforeReadHook = (args: { doc: any; req: PayloadRequest }) => any export type BeforeReadHook = (args: {
context: RequestContext
doc: any
/** The global which this hook is being run on */
global: SanitizedGlobalConfig
req: PayloadRequest
}) => any
export type AfterReadHook = (args: { export type AfterReadHook = (args: {
context: RequestContext
doc: any doc: any
findMany?: boolean findMany?: boolean
/** The global which this hook is being run on */
global: SanitizedGlobalConfig
query?: Where query?: Where
req: PayloadRequest req: PayloadRequest
}) => any }) => any

View File

@@ -83,7 +83,9 @@ async function findOne<T extends Record<string, unknown>>(args: Args): Promise<T
doc = doc =
(await hook({ (await hook({
context: req.context,
doc, doc,
global: globalConfig,
req, req,
})) || doc })) || doc
}, Promise.resolve()) }, Promise.resolve())
@@ -93,10 +95,11 @@ async function findOne<T extends Record<string, unknown>>(args: Args): Promise<T
// ///////////////////////////////////// // /////////////////////////////////////
doc = await afterRead({ doc = await afterRead({
collection: null,
context: req.context, context: req.context,
depth, depth,
doc, doc,
entityConfig: globalConfig, global: globalConfig,
overrideAccess, overrideAccess,
req, req,
showHiddenFields, showHiddenFields,
@@ -111,7 +114,9 @@ async function findOne<T extends Record<string, unknown>>(args: Args): Promise<T
doc = doc =
(await hook({ (await hook({
context: req.context,
doc, doc,
global: globalConfig,
req, req,
})) || doc })) || doc
}, Promise.resolve()) }, Promise.resolve())

View File

@@ -87,7 +87,9 @@ async function findVersionByID<T extends TypeWithVersion<T> = any>(args: Argumen
result = result =
(await hook({ (await hook({
context: req.context,
doc: result.version, doc: result.version,
global: globalConfig,
req, req,
})) || result.version })) || result.version
}, Promise.resolve()) }, Promise.resolve())
@@ -97,11 +99,12 @@ async function findVersionByID<T extends TypeWithVersion<T> = any>(args: Argumen
// ///////////////////////////////////// // /////////////////////////////////////
result.version = await afterRead({ result.version = await afterRead({
collection: null,
context: req.context, context: req.context,
currentDepth, currentDepth,
depth, depth,
doc: result.version, doc: result.version,
entityConfig: globalConfig, global: globalConfig,
overrideAccess, overrideAccess,
req, req,
showHiddenFields, showHiddenFields,
@@ -116,7 +119,9 @@ async function findVersionByID<T extends TypeWithVersion<T> = any>(args: Argumen
result.version = result.version =
(await hook({ (await hook({
context: req.context,
doc: result.version, doc: result.version,
global: globalConfig,
query: findGlobalVersionsArgs.where, query: findGlobalVersionsArgs.where,
req, req,
})) || result.version })) || result.version

View File

@@ -88,11 +88,12 @@ async function findVersions<T extends TypeWithVersion<T>>(
paginatedDocs.docs.map(async (data) => ({ paginatedDocs.docs.map(async (data) => ({
...data, ...data,
version: await afterRead({ version: await afterRead({
collection: null,
context: req.context, context: req.context,
depth, depth,
doc: data.version, doc: data.version,
entityConfig: globalConfig,
findMany: true, findMany: true,
global: globalConfig,
overrideAccess, overrideAccess,
req, req,
showHiddenFields, showHiddenFields,
@@ -115,8 +116,14 @@ async function findVersions<T extends TypeWithVersion<T>>(
await priorHook await priorHook
docRef.version = docRef.version =
(await hook({ doc: doc.version, findMany: true, query: fullWhere, req })) || (await hook({
doc.version context: req.context,
doc: doc.version,
findMany: true,
global: globalConfig,
query: fullWhere,
req,
})) || doc.version
}, Promise.resolve()) }, Promise.resolve())
return docRef return docRef

View File

@@ -97,10 +97,11 @@ async function restoreVersion<T extends TypeWithVersion<T> = any>(args: Argument
// ///////////////////////////////////// // /////////////////////////////////////
result = await afterRead({ result = await afterRead({
collection: null,
context: req.context, context: req.context,
depth, depth,
doc: result, doc: result,
entityConfig: globalConfig, global: globalConfig,
overrideAccess, overrideAccess,
req, req,
showHiddenFields, showHiddenFields,
@@ -115,7 +116,9 @@ async function restoreVersion<T extends TypeWithVersion<T> = any>(args: Argument
result = result =
(await hook({ (await hook({
context: req.context,
doc: result, doc: result,
global: globalConfig,
req, req,
})) || result })) || result
}, Promise.resolve()) }, Promise.resolve())
@@ -125,10 +128,11 @@ async function restoreVersion<T extends TypeWithVersion<T> = any>(args: Argument
// ///////////////////////////////////// // /////////////////////////////////////
result = await afterChange({ result = await afterChange({
collection: null,
context: req.context, context: req.context,
data: result, data: result,
doc: result, doc: result,
entityConfig: globalConfig, global: globalConfig,
operation: 'update', operation: 'update',
previousDoc, previousDoc,
req, req,
@@ -143,7 +147,9 @@ async function restoreVersion<T extends TypeWithVersion<T> = any>(args: Argument
result = result =
(await hook({ (await hook({
context: req.context,
doc: result, doc: result,
global: globalConfig,
previousDoc, previousDoc,
req, req,
})) || result })) || result

View File

@@ -92,10 +92,11 @@ async function update<TSlug extends keyof GeneratedTypes['globals']>(
} }
const originalDoc = await afterRead({ const originalDoc = await afterRead({
collection: null,
context: req.context, context: req.context,
depth: 0, depth: 0,
doc: globalJSON, doc: globalJSON,
entityConfig: globalConfig, global: globalConfig,
overrideAccess: true, overrideAccess: true,
req, req,
showHiddenFields, showHiddenFields,
@@ -106,10 +107,11 @@ async function update<TSlug extends keyof GeneratedTypes['globals']>(
// ///////////////////////////////////// // /////////////////////////////////////
data = await beforeValidate({ data = await beforeValidate({
collection: null,
context: req.context, context: req.context,
data, data,
doc: originalDoc, doc: originalDoc,
entityConfig: globalConfig, global: globalConfig,
operation: 'update', operation: 'update',
overrideAccess, overrideAccess,
req, req,
@@ -124,7 +126,9 @@ async function update<TSlug extends keyof GeneratedTypes['globals']>(
data = data =
(await hook({ (await hook({
context: req.context,
data, data,
global: globalConfig,
originalDoc, originalDoc,
req, req,
})) || data })) || data
@@ -139,7 +143,9 @@ async function update<TSlug extends keyof GeneratedTypes['globals']>(
data = data =
(await hook({ (await hook({
context: req.context,
data, data,
global: globalConfig,
originalDoc, originalDoc,
req, req,
})) || data })) || data
@@ -150,11 +156,12 @@ async function update<TSlug extends keyof GeneratedTypes['globals']>(
// ///////////////////////////////////// // /////////////////////////////////////
let result = await beforeChange({ let result = await beforeChange({
collection: null,
context: req.context, context: req.context,
data, data,
doc: originalDoc, doc: originalDoc,
docWithLocales: globalJSON, docWithLocales: globalJSON,
entityConfig: globalConfig, global: globalConfig,
operation: 'update', operation: 'update',
req, req,
skipValidation: shouldSaveDraft, skipValidation: shouldSaveDraft,
@@ -204,10 +211,11 @@ async function update<TSlug extends keyof GeneratedTypes['globals']>(
// ///////////////////////////////////// // /////////////////////////////////////
result = await afterRead({ result = await afterRead({
collection: null,
context: req.context, context: req.context,
depth, depth,
doc: result, doc: result,
entityConfig: globalConfig, global: globalConfig,
overrideAccess, overrideAccess,
req, req,
showHiddenFields, showHiddenFields,
@@ -222,7 +230,9 @@ async function update<TSlug extends keyof GeneratedTypes['globals']>(
result = result =
(await hook({ (await hook({
context: req.context,
doc: result, doc: result,
global: globalConfig,
req, req,
})) || result })) || result
}, Promise.resolve()) }, Promise.resolve())
@@ -232,10 +242,11 @@ async function update<TSlug extends keyof GeneratedTypes['globals']>(
// ///////////////////////////////////// // /////////////////////////////////////
result = await afterChange({ result = await afterChange({
collection: null,
context: req.context, context: req.context,
data, data,
doc: result, doc: result,
entityConfig: globalConfig, global: globalConfig,
operation: 'update', operation: 'update',
previousDoc: originalDoc, previousDoc: originalDoc,
req, req,
@@ -250,7 +261,9 @@ async function update<TSlug extends keyof GeneratedTypes['globals']>(
result = result =
(await hook({ (await hook({
context: req.context,
doc: result, doc: result,
global: globalConfig,
previousDoc: originalDoc, previousDoc: originalDoc,
req, req,
})) || result })) || result

View File

@@ -35,7 +35,7 @@ const errorHandler = async (
} }
if (afterErrorHook) { if (afterErrorHook) {
;({ response } = (await afterErrorHook(err, response, null)) || { response }) ;({ response } = (await afterErrorHook(err, response, null, null)) || { response })
} }
return response return response

View File

@@ -0,0 +1,111 @@
/* eslint-disable no-param-reassign */
import type { CollectionConfig } from '../../../../packages/payload/src/collections/config/types'
export const dataHooksSlug = 'data-hooks'
export const DataHooks: CollectionConfig = {
slug: dataHooksSlug,
access: {
read: () => true,
create: () => true,
delete: () => true,
update: () => true,
},
hooks: {
beforeOperation: [
async ({ context, collection, args }) => {
context['collection_beforeOperation_collection'] = JSON.stringify(collection)
return args
},
],
beforeChange: [
({ context, data, collection }) => {
context['collection_beforeChange_collection'] = JSON.stringify(collection)
return data
},
],
afterChange: [
async ({ context, collection }) => {
context['collection_afterChange_collection'] = JSON.stringify(collection)
},
],
beforeRead: [
async ({ context, collection }) => {
context['collection_beforeRead_collection'] = JSON.stringify(collection)
},
],
afterRead: [
({ context, collection, doc }) => {
context['collection_afterRead_collection'] = JSON.stringify(collection)
return doc
},
],
afterOperation: [
({ args, result, collection }) => {
args.req.context['collection_afterOperation_collection'] = JSON.stringify(collection)
for (const contextKey in args.req.context) {
if (contextKey.startsWith('collection_')) {
result[contextKey] = args.req.context[contextKey]
}
}
return result
},
],
},
fields: [
{
name: 'field_collectionAndField',
type: 'text',
hooks: {
beforeChange: [
({ collection, field, context, value }) => {
context['field_beforeChange_CollectionAndField'] =
JSON.stringify(collection) + JSON.stringify(field)
return value
},
],
afterRead: [
({ collection, field, context }) => {
return (
(context['field_beforeChange_CollectionAndField'] as string) +
JSON.stringify(collection) +
JSON.stringify(field)
)
},
],
},
},
{
name: 'collection_beforeOperation_collection',
type: 'text',
},
{
name: 'collection_beforeChange_collection',
type: 'text',
},
{
name: 'collection_afterChange_collection',
type: 'text',
},
{
name: 'collection_beforeRead_collection',
type: 'text',
},
{
name: 'collection_afterRead_collection',
type: 'text',
},
{
name: 'collection_afterOperation_collection',
type: 'text',
},
],
}

View File

@@ -1,14 +1,17 @@
import type { SanitizedConfig } from '../../packages/payload/src/config/types'
import { buildConfigWithDefaults } from '../buildConfigWithDefaults' import { buildConfigWithDefaults } from '../buildConfigWithDefaults'
import AfterOperation from './collections/AfterOperation' import AfterOperation from './collections/AfterOperation'
import ChainingHooks from './collections/ChainingHooks' import ChainingHooks from './collections/ChainingHooks'
import ContextHooks from './collections/ContextHooks' import ContextHooks from './collections/ContextHooks'
import { DataHooks } from './collections/Data'
import Hooks, { hooksSlug } from './collections/Hook' import Hooks, { hooksSlug } from './collections/Hook'
import NestedAfterReadHooks from './collections/NestedAfterReadHooks' import NestedAfterReadHooks from './collections/NestedAfterReadHooks'
import Relations from './collections/Relations' import Relations from './collections/Relations'
import TransformHooks from './collections/Transform' import TransformHooks from './collections/Transform'
import Users, { seedHooksUsers } from './collections/Users' import Users, { seedHooksUsers } from './collections/Users'
import { DataHooksGlobal } from './globals/Data'
export default buildConfigWithDefaults({ export const HooksConfig: Promise<SanitizedConfig> = buildConfigWithDefaults({
collections: [ collections: [
AfterOperation, AfterOperation,
ContextHooks, ContextHooks,
@@ -18,7 +21,9 @@ export default buildConfigWithDefaults({
ChainingHooks, ChainingHooks,
Relations, Relations,
Users, Users,
DataHooks,
], ],
globals: [DataHooksGlobal],
onInit: async (payload) => { onInit: async (payload) => {
await seedHooksUsers(payload) await seedHooksUsers(payload)
await payload.create({ await payload.create({
@@ -38,3 +43,5 @@ export default buildConfigWithDefaults({
}) })
}, },
}) })
export default HooksConfig

View File

@@ -0,0 +1,97 @@
/* eslint-disable no-param-reassign */
import type { GlobalConfig } from '../../../../packages/payload/src/globals/config/types'
export const dataHooksGlobalSlug = 'data-hooks-global'
export const DataHooksGlobal: GlobalConfig = {
slug: dataHooksGlobalSlug,
access: {
read: () => true,
update: () => true,
},
hooks: {
beforeChange: [
({ data, global, context }) => {
context['global_beforeChange_global'] = JSON.stringify(global)
return data
},
],
beforeRead: [
async ({ context, global }) => {
context['global_beforeRead_global'] = JSON.stringify(global)
},
],
afterRead: [
({ context, global, doc }) => {
context['global_afterRead_global'] = JSON.stringify(global)
// Needs to be done for both afterRead (for findOne test) and afterChange (for update test)
for (const contextKey in context) {
if (contextKey.startsWith('global_')) {
doc[contextKey] = context[contextKey]
}
}
return doc
},
],
afterChange: [
async ({ context, global, doc }) => {
context['global_afterChange_global'] = JSON.stringify(global)
// Needs to be done for both afterRead (for findOne test) and afterChange (for update test), as afterChange is called after afterRead
for (const contextKey in context) {
if (contextKey.startsWith('global_')) {
doc[contextKey] = context[contextKey]
}
}
return doc
},
],
},
fields: [
{
name: 'field_globalAndField',
type: 'text',
hooks: {
beforeChange: [
({ global, field, context, value }) => {
context['field_beforeChange_GlobalAndField'] =
JSON.stringify(global) + JSON.stringify(field)
return value
},
],
afterRead: [
({ global, field, context }) => {
return (
(context['field_beforeChange_GlobalAndField'] as string) +
JSON.stringify(global) +
JSON.stringify(field)
)
},
],
},
},
{
name: 'global_beforeChange_global',
type: 'text',
},
{
name: 'global_afterChange_global',
type: 'text',
},
{
name: 'global_beforeRead_global',
type: 'text',
},
{
name: 'global_afterRead_global',
type: 'text',
},
],
}

View File

@@ -8,6 +8,7 @@ import { RESTClient } from '../helpers/rest'
import { afterOperationSlug } from './collections/AfterOperation' import { afterOperationSlug } from './collections/AfterOperation'
import { chainingHooksSlug } from './collections/ChainingHooks' import { chainingHooksSlug } from './collections/ChainingHooks'
import { contextHooksSlug } from './collections/ContextHooks' import { contextHooksSlug } from './collections/ContextHooks'
import { dataHooksSlug } from './collections/Data'
import { hooksSlug } from './collections/Hook' import { hooksSlug } from './collections/Hook'
import { import {
generatedAfterReadText, generatedAfterReadText,
@@ -16,7 +17,8 @@ import {
import { relationsSlug } from './collections/Relations' import { relationsSlug } from './collections/Relations'
import { transformSlug } from './collections/Transform' import { transformSlug } from './collections/Transform'
import { hooksUsersSlug } from './collections/Users' import { hooksUsersSlug } from './collections/Users'
import configPromise from './config' import configPromise, { HooksConfig } from './config'
import { dataHooksGlobalSlug } from './globals/Data'
let client: RESTClient let client: RESTClient
let apiUrl let apiUrl
@@ -293,4 +295,111 @@ describe('Hooks', () => {
).rejects.toThrow(AuthenticationError) ).rejects.toThrow(AuthenticationError)
}) })
}) })
describe('hook parameter data', () => {
it('should pass collection prop to collection hooks', async () => {
const sanitizedConfig = await HooksConfig
const sanitizedHooksCollection = JSON.parse(
JSON.stringify(sanitizedConfig.collections.find(({ slug }) => slug === dataHooksSlug)),
)
const doc = await payload.create({
collection: dataHooksSlug,
data: {},
})
expect(JSON.parse(doc.collection_beforeOperation_collection)).toStrictEqual(
sanitizedHooksCollection,
)
expect(JSON.parse(doc.collection_beforeChange_collection)).toStrictEqual(
sanitizedHooksCollection,
)
expect(JSON.parse(doc.collection_afterChange_collection)).toStrictEqual(
sanitizedHooksCollection,
)
expect(JSON.parse(doc.collection_afterRead_collection)).toStrictEqual(
sanitizedHooksCollection,
)
expect(JSON.parse(doc.collection_afterOperation_collection)).toStrictEqual(
sanitizedHooksCollection,
)
// BeforeRead is only run for find operations
const foundDoc = await payload.findByID({
collection: dataHooksSlug,
id: doc.id,
})
expect(JSON.parse(foundDoc.collection_beforeRead_collection)).toStrictEqual(
sanitizedHooksCollection,
)
})
it('should pass collection and field props to field hooks', async () => {
const sanitizedConfig = await HooksConfig
const sanitizedHooksCollection = sanitizedConfig.collections.find(
({ slug }) => slug === dataHooksSlug,
)
const field = sanitizedHooksCollection.fields.find(
(field) => 'name' in field && field.name === 'field_collectionAndField',
)
const doc = await payload.create({
collection: dataHooksSlug,
data: {},
})
const collectionAndField = JSON.stringify(sanitizedHooksCollection) + JSON.stringify(field)
expect(doc.field_collectionAndField).toStrictEqual(collectionAndField + collectionAndField)
})
it('should pass global prop to global hooks', async () => {
const sanitizedConfig = await HooksConfig
const sanitizedHooksGlobal = JSON.parse(
JSON.stringify(sanitizedConfig.globals.find(({ slug }) => slug === dataHooksGlobalSlug)),
)
const doc = await payload.updateGlobal({
slug: dataHooksGlobalSlug,
data: {},
})
expect(JSON.parse(doc.global_beforeChange_global)).toStrictEqual(sanitizedHooksGlobal)
expect(JSON.parse(doc.global_afterRead_global)).toStrictEqual(sanitizedHooksGlobal)
expect(JSON.parse(doc.global_afterChange_global)).toStrictEqual(sanitizedHooksGlobal)
// beforeRead is only run for findOne operations
const foundDoc = await payload.findGlobal({
slug: dataHooksGlobalSlug,
})
expect(JSON.parse(foundDoc.global_beforeRead_global)).toStrictEqual(sanitizedHooksGlobal)
})
it('should pass global and field props to global hooks', async () => {
const sanitizedConfig = await HooksConfig
const sanitizedHooksGlobal = sanitizedConfig.globals.find(
({ slug }) => slug === dataHooksGlobalSlug,
)
const globalString = JSON.stringify(sanitizedHooksGlobal)
const fieldString = JSON.stringify(
sanitizedHooksGlobal.fields.find(
(field) => 'name' in field && field.name === 'field_globalAndField',
),
)
const doc = await payload.updateGlobal({
slug: dataHooksGlobalSlug,
data: {},
})
const globalAndFieldString = globalString + fieldString
expect(doc.field_globalAndField).toStrictEqual(globalAndFieldString + globalAndFieldString)
})
})
}) })