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:
111
test/hooks/collections/Data/index.ts
Normal file
111
test/hooks/collections/Data/index.ts
Normal 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',
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -1,14 +1,17 @@
|
||||
import type { SanitizedConfig } from '../../packages/payload/src/config/types'
|
||||
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults'
|
||||
import AfterOperation from './collections/AfterOperation'
|
||||
import ChainingHooks from './collections/ChainingHooks'
|
||||
import ContextHooks from './collections/ContextHooks'
|
||||
import { DataHooks } from './collections/Data'
|
||||
import Hooks, { hooksSlug } from './collections/Hook'
|
||||
import NestedAfterReadHooks from './collections/NestedAfterReadHooks'
|
||||
import Relations from './collections/Relations'
|
||||
import TransformHooks from './collections/Transform'
|
||||
import Users, { seedHooksUsers } from './collections/Users'
|
||||
|
||||
export default buildConfigWithDefaults({
|
||||
import { DataHooksGlobal } from './globals/Data'
|
||||
export const HooksConfig: Promise<SanitizedConfig> = buildConfigWithDefaults({
|
||||
collections: [
|
||||
AfterOperation,
|
||||
ContextHooks,
|
||||
@@ -18,7 +21,9 @@ export default buildConfigWithDefaults({
|
||||
ChainingHooks,
|
||||
Relations,
|
||||
Users,
|
||||
DataHooks,
|
||||
],
|
||||
globals: [DataHooksGlobal],
|
||||
onInit: async (payload) => {
|
||||
await seedHooksUsers(payload)
|
||||
await payload.create({
|
||||
@@ -38,3 +43,5 @@ export default buildConfigWithDefaults({
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
export default HooksConfig
|
||||
|
||||
97
test/hooks/globals/Data/index.ts
Normal file
97
test/hooks/globals/Data/index.ts
Normal 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',
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import { RESTClient } from '../helpers/rest'
|
||||
import { afterOperationSlug } from './collections/AfterOperation'
|
||||
import { chainingHooksSlug } from './collections/ChainingHooks'
|
||||
import { contextHooksSlug } from './collections/ContextHooks'
|
||||
import { dataHooksSlug } from './collections/Data'
|
||||
import { hooksSlug } from './collections/Hook'
|
||||
import {
|
||||
generatedAfterReadText,
|
||||
@@ -16,7 +17,8 @@ import {
|
||||
import { relationsSlug } from './collections/Relations'
|
||||
import { transformSlug } from './collections/Transform'
|
||||
import { hooksUsersSlug } from './collections/Users'
|
||||
import configPromise from './config'
|
||||
import configPromise, { HooksConfig } from './config'
|
||||
import { dataHooksGlobalSlug } from './globals/Data'
|
||||
|
||||
let client: RESTClient
|
||||
let apiUrl
|
||||
@@ -293,4 +295,111 @@ describe('Hooks', () => {
|
||||
).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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user