Skip to content

Commit 0da0da8

Browse files
fix: adds read and readDistinct to hook mapping (#14853)
Fixes #14847
1 parent f7cc078 commit 0da0da8

File tree

6 files changed

+361
-46
lines changed

6 files changed

+361
-46
lines changed

packages/payload/src/collections/operations/utilities/buildBeforeOperation.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,9 @@ export async function buildBeforeOperation<TOperationGeneric extends CollectionS
8585
let newArgs = args
8686

8787
if (args.collection.config.hooks?.beforeOperation?.length) {
88+
// TODO: v4 should not need this mapping
8889
// Map the operation to the hook operation type for backward compatibility
89-
const hookOperation =
90-
operationToHookOperation[operation as keyof typeof operationToHookOperation]
90+
const hookOperation = operationToHookOperation[operation]
9191

9292
for (const hook of args.collection.config.hooks.beforeOperation) {
9393
const hookResult = await hook({

packages/payload/src/collections/operations/utilities/types.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ export type AfterOperationArg<TOperationGeneric extends CollectionSlug> = {
8484
args: Parameters<OperationMap<TOperationGeneric>['find']>[0]
8585
/**
8686
* @deprecated Use 'find' or 'findByID' operation instead
87+
*
88+
* TODO: v4 - remove this union option
8789
*/
8890
operation: 'read'
8991
result: Awaited<ReturnType<OperationMap<TOperationGeneric>['find']>>
@@ -179,6 +181,8 @@ export const operationToHookOperation = {
179181
findVersions: 'read',
180182
forgotPassword: 'forgotPassword',
181183
login: 'login',
184+
read: 'read',
185+
readDistinct: 'readDistinct',
182186
refresh: 'refresh',
183187
resetPassword: 'resetPassword',
184188
restoreVersion: 'restoreVersion',
@@ -199,6 +203,8 @@ export type BeforeOperationArg<TOperationGeneric extends CollectionSlug> = {
199203
| Parameters<OperationMap<TOperationGeneric>['findByID']>[0]
200204
/**
201205
* @deprecated Use 'find' or 'findByID' operation instead
206+
*
207+
* TODO: v4 - remove this union option
202208
*/
203209
operation: 'read'
204210
}
@@ -232,11 +238,16 @@ export type BeforeOperationArg<TOperationGeneric extends CollectionSlug> = {
232238
}
233239
| {
234240
args: Parameters<OperationMap<TOperationGeneric>['findDistinct']>[0]
235-
operation: 'findDistinct'
241+
/**
242+
* @deprecated Use 'findDistinct' operation instead
243+
*
244+
* TODO: v4 - remove this union option
245+
*/
246+
operation: 'readDistinct'
236247
}
237248
| {
238249
args: Parameters<OperationMap<TOperationGeneric>['findDistinct']>[0]
239-
operation: 'readDistinct'
250+
operation: 'findDistinct'
240251
}
241252
| {
242253
args: Parameters<OperationMap<TOperationGeneric>['findVersionByID']>[0]
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import type { CollectionConfig } from 'payload'
2+
3+
export const beforeOperationSlug = 'beforeOperation'
4+
5+
// Store the last operation for testing
6+
let lastOperation: string | undefined
7+
8+
export const BeforeOperationCollection: CollectionConfig = {
9+
slug: beforeOperationSlug,
10+
versions: {
11+
drafts: true,
12+
},
13+
hooks: {
14+
beforeOperation: [
15+
({ operation }) => {
16+
// Store operation for testing
17+
lastOperation = operation
18+
},
19+
],
20+
},
21+
fields: [
22+
{
23+
name: 'category',
24+
type: 'text',
25+
},
26+
],
27+
}
28+
29+
export const getLastOperation = () => lastOperation
30+
export const clearLastOperation = () => {
31+
lastOperation = undefined
32+
}

test/hooks/config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
BeforeDelete2Collection,
1414
BeforeDeleteCollection,
1515
} from './collections/BeforeDelete/index.js'
16+
import { BeforeOperationCollection } from './collections/BeforeOperation/index.js'
1617
import { BeforeValidateCollection } from './collections/BeforeValidate/index.js'
1718
import ChainingHooks from './collections/ChainingHooks/index.js'
1819
import ContextHooks from './collections/ContextHooks/index.js'
@@ -34,6 +35,7 @@ export const HooksConfig: Promise<SanitizedConfig> = buildConfigWithDefaults({
3435
},
3536
},
3637
collections: [
38+
BeforeOperationCollection,
3739
BeforeChangeHooks,
3840
BeforeValidateCollection,
3941
AfterOperationCollection,

test/hooks/int.spec.ts

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ import { devUser, regularUser } from '../credentials.js'
1010
import { initPayloadInt } from '../helpers/initPayloadInt.js'
1111
import { isMongoose } from '../helpers/isMongoose.js'
1212
import { afterOperationSlug } from './collections/AfterOperation/index.js'
13+
import {
14+
beforeOperationSlug,
15+
clearLastOperation,
16+
getLastOperation,
17+
} from './collections/BeforeOperation/index.js'
1318
import { chainingHooksSlug } from './collections/ChainingHooks/index.js'
1419
import { contextHooksSlug } from './collections/ContextHooks/index.js'
1520
import { dataHooksSlug } from './collections/Data/index.js'
@@ -743,4 +748,241 @@ describe('Hooks', () => {
743748
expect(updateResult).toBeDefined()
744749
})
745750
})
751+
752+
describe('beforeOperation', () => {
753+
afterEach(() => {
754+
clearLastOperation()
755+
})
756+
757+
it('should pass correct operation arg on create', async () => {
758+
await payload.create({
759+
collection: beforeOperationSlug,
760+
data: {},
761+
})
762+
763+
expect(getLastOperation()).toEqual('create')
764+
})
765+
766+
it('should pass correct operation arg on update', async () => {
767+
const doc = await payload.create({
768+
collection: beforeOperationSlug,
769+
data: {},
770+
})
771+
772+
await payload.update({
773+
id: doc.id,
774+
collection: beforeOperationSlug,
775+
data: {},
776+
})
777+
778+
expect(getLastOperation()).toEqual('update')
779+
})
780+
781+
it('should pass correct operation arg on updateByID', async () => {
782+
const doc = await payload.create({
783+
collection: beforeOperationSlug,
784+
data: {},
785+
})
786+
787+
await payload.update({
788+
id: doc.id,
789+
collection: beforeOperationSlug,
790+
data: {},
791+
})
792+
793+
expect(getLastOperation()).toEqual('update')
794+
})
795+
796+
it('should pass correct operation arg on read (findByID)', async () => {
797+
const doc = await payload.create({
798+
collection: beforeOperationSlug,
799+
data: {},
800+
})
801+
802+
await payload.findByID({
803+
id: doc.id,
804+
collection: beforeOperationSlug,
805+
})
806+
807+
expect(getLastOperation()).toEqual('read')
808+
})
809+
810+
it('should pass correct operation arg on read (find)', async () => {
811+
await payload.create({
812+
collection: beforeOperationSlug,
813+
data: {},
814+
})
815+
816+
clearLastOperation()
817+
818+
await payload.find({
819+
collection: beforeOperationSlug,
820+
})
821+
822+
expect(getLastOperation()).toEqual('read')
823+
})
824+
825+
it('should pass correct operation arg on readDistinct (findDistinct)', async () => {
826+
await payload.create({
827+
collection: beforeOperationSlug,
828+
data: { category: 'test1' },
829+
})
830+
await payload.create({
831+
collection: beforeOperationSlug,
832+
data: { category: 'test2' },
833+
})
834+
await payload.create({
835+
collection: beforeOperationSlug,
836+
data: { category: 'test1' },
837+
})
838+
839+
await payload.findDistinct({
840+
collection: beforeOperationSlug,
841+
field: 'category',
842+
})
843+
844+
expect(getLastOperation()).toEqual('readDistinct')
845+
})
846+
847+
it('should pass correct operation arg on delete', async () => {
848+
const doc = await payload.create({
849+
collection: beforeOperationSlug,
850+
data: {},
851+
})
852+
853+
await payload.delete({
854+
id: doc.id,
855+
collection: beforeOperationSlug,
856+
})
857+
858+
expect(getLastOperation()).toEqual('delete')
859+
})
860+
861+
it('should pass correct operation arg on deleteByID', async () => {
862+
const doc = await payload.create({
863+
collection: beforeOperationSlug,
864+
data: {},
865+
})
866+
867+
await payload.delete({
868+
id: doc.id,
869+
collection: beforeOperationSlug,
870+
})
871+
872+
expect(getLastOperation()).toEqual('delete')
873+
})
874+
875+
it('should pass correct operation arg on count', async () => {
876+
await payload.create({
877+
collection: beforeOperationSlug,
878+
data: {},
879+
})
880+
881+
await payload.count({
882+
collection: beforeOperationSlug,
883+
})
884+
885+
expect(getLastOperation()).toEqual('count')
886+
})
887+
888+
it('should pass correct operation arg on countVersions', async () => {
889+
const doc = await payload.create({
890+
collection: beforeOperationSlug,
891+
data: {},
892+
})
893+
894+
await payload.countVersions({
895+
collection: beforeOperationSlug,
896+
where: {
897+
parent: {
898+
equals: doc.id,
899+
},
900+
},
901+
})
902+
903+
expect(getLastOperation()).toEqual('countVersions')
904+
})
905+
906+
it('should pass correct operation arg on findVersions', async () => {
907+
const doc = await payload.create({
908+
collection: beforeOperationSlug,
909+
data: {},
910+
})
911+
912+
await payload.findVersions({
913+
collection: beforeOperationSlug,
914+
where: {
915+
parent: {
916+
equals: doc.id,
917+
},
918+
},
919+
})
920+
921+
expect(getLastOperation()).toEqual('read')
922+
})
923+
924+
it('should pass correct operation arg on findVersionByID', async () => {
925+
const doc = await payload.create({
926+
collection: beforeOperationSlug,
927+
data: { category: 'v1' },
928+
})
929+
930+
// Update to create a version
931+
await payload.update({
932+
id: doc.id,
933+
collection: beforeOperationSlug,
934+
data: { category: 'v2' },
935+
})
936+
937+
const versions = await payload.findVersions({
938+
collection: beforeOperationSlug,
939+
where: {
940+
parent: {
941+
equals: doc.id,
942+
},
943+
},
944+
})
945+
946+
expect(versions.docs.length).toBeGreaterThan(0)
947+
948+
await payload.findVersionByID({
949+
collection: beforeOperationSlug,
950+
id: versions.docs[0]!.id,
951+
})
952+
953+
expect(getLastOperation()).toEqual('read')
954+
})
955+
956+
it('should pass correct operation arg on restoreVersion', async () => {
957+
const doc = await payload.create({
958+
collection: beforeOperationSlug,
959+
data: { category: 'v1' },
960+
})
961+
962+
// Update to create a version
963+
await payload.update({
964+
id: doc.id,
965+
collection: beforeOperationSlug,
966+
data: { category: 'v2' },
967+
})
968+
969+
const versions = await payload.findVersions({
970+
collection: beforeOperationSlug,
971+
where: {
972+
parent: {
973+
equals: doc.id,
974+
},
975+
},
976+
})
977+
978+
expect(versions.docs.length).toBeGreaterThan(0)
979+
980+
await payload.restoreVersion({
981+
collection: beforeOperationSlug,
982+
id: versions.docs[0]!.id,
983+
})
984+
985+
expect(getLastOperation()).toEqual('restoreVersion')
986+
})
987+
})
746988
})

0 commit comments

Comments
 (0)