Skip to content

Commit 9031f3b

Browse files
feat: add hooks to restoreVersion collection operation (#13333)
Adds missing hooks to the restoreVersion operation. - beforeOperation - beforeValidate - Fields - beforeValidate - Collection - beforeChange - Collection - beforeChange - Fields - afterOperation
1 parent df91321 commit 9031f3b

File tree

7 files changed

+172
-17
lines changed

7 files changed

+172
-17
lines changed

packages/payload/src/collections/config/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export type HookOperationType =
8585
| 'readDistinct'
8686
| 'refresh'
8787
| 'resetPassword'
88+
| 'restoreVersion'
8889
| 'update'
8990

9091
type CreateOrUpdateOperation = Extract<HookOperationType, 'create' | 'update'>

packages/payload/src/collections/operations/create.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ export const createOperation = async <
291291
autosave,
292292
collection: collectionConfig,
293293
docWithLocales: result,
294+
operation: 'create',
294295
payload,
295296
publishSpecificLocale,
296297
req,

packages/payload/src/collections/operations/restoreVersion.ts

Lines changed: 158 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,23 @@ import { combineQueries } from '../../database/combineQueries.js'
1010
import { APIError, Forbidden, NotFound } from '../../errors/index.js'
1111
import { afterChange } from '../../fields/hooks/afterChange/index.js'
1212
import { afterRead } from '../../fields/hooks/afterRead/index.js'
13+
import { beforeChange } from '../../fields/hooks/beforeChange/index.js'
14+
import { beforeValidate } from '../../fields/hooks/beforeValidate/index.js'
15+
import { commitTransaction } from '../../utilities/commitTransaction.js'
16+
import { deepCopyObjectSimple } from '../../utilities/deepCopyObject.js'
17+
import { initTransaction } from '../../utilities/initTransaction.js'
1318
import { killTransaction } from '../../utilities/killTransaction.js'
1419
import { sanitizeSelect } from '../../utilities/sanitizeSelect.js'
1520
import { getLatestCollectionVersion } from '../../versions/getLatestCollectionVersion.js'
21+
import { saveVersion } from '../../versions/saveVersion.js'
22+
import { buildAfterOperation } from './utils.js'
1623

1724
export type Arguments = {
1825
collection: Collection
1926
currentDepth?: number
2027
depth?: number
2128
disableErrors?: boolean
29+
disableTransaction?: boolean
2230
draft?: boolean
2331
id: number | string
2432
overrideAccess?: boolean
@@ -35,7 +43,7 @@ export const restoreVersionOperation = async <TData extends TypeWithID = any>(
3543
id,
3644
collection: { config: collectionConfig },
3745
depth,
38-
draft,
46+
draft: draftArg = false,
3947
overrideAccess = false,
4048
populate,
4149
req,
@@ -45,6 +53,25 @@ export const restoreVersionOperation = async <TData extends TypeWithID = any>(
4553
} = args
4654

4755
try {
56+
const shouldCommit = !args.disableTransaction && (await initTransaction(args.req))
57+
58+
// /////////////////////////////////////
59+
// beforeOperation - Collection
60+
// /////////////////////////////////////
61+
62+
if (args.collection.config.hooks?.beforeOperation?.length) {
63+
for (const hook of args.collection.config.hooks.beforeOperation) {
64+
args =
65+
(await hook({
66+
args,
67+
collection: args.collection.config,
68+
context: args.req.context,
69+
operation: 'restoreVersion',
70+
req: args.req,
71+
})) || args
72+
}
73+
}
74+
4875
if (!id) {
4976
throw new APIError('Missing ID of version to restore.', httpStatus.BAD_REQUEST)
5077
}
@@ -68,7 +95,7 @@ export const restoreVersionOperation = async <TData extends TypeWithID = any>(
6895
throw new NotFound(req.t)
6996
}
7097

71-
const parentDocID = rawVersion.parent
98+
const { parent: parentDocID, version: versionToRestoreWithLocales } = rawVersion
7299

73100
// /////////////////////////////////////
74101
// Access
@@ -90,6 +117,7 @@ export const restoreVersionOperation = async <TData extends TypeWithID = any>(
90117
where: combineQueries({ id: { equals: parentDocID } }, accessResults),
91118
}
92119

120+
// Get the document from the non versioned collection
93121
const doc = await req.payload.db.findOne(findOneArgs)
94122

95123
if (!doc && !hasWherePolicy) {
@@ -109,7 +137,6 @@ export const restoreVersionOperation = async <TData extends TypeWithID = any>(
109137
// /////////////////////////////////////
110138
// fetch previousDoc
111139
// /////////////////////////////////////
112-
113140
const prevDocWithLocales = await getLatestCollectionVersion({
114141
id: parentDocID,
115142
config: collectionConfig,
@@ -118,6 +145,109 @@ export const restoreVersionOperation = async <TData extends TypeWithID = any>(
118145
req,
119146
})
120147

148+
// originalDoc with hoisted localized data
149+
const originalDoc = await afterRead({
150+
collection: collectionConfig,
151+
context: req.context,
152+
depth: 0,
153+
doc: deepCopyObjectSimple(prevDocWithLocales),
154+
draft: draftArg,
155+
fallbackLocale: null,
156+
global: null,
157+
locale: locale!,
158+
overrideAccess: true,
159+
req,
160+
showHiddenFields: true,
161+
})
162+
163+
// version data with hoisted localized data
164+
const prevVersionDoc = await afterRead({
165+
collection: collectionConfig,
166+
context: req.context,
167+
depth: 0,
168+
doc: deepCopyObjectSimple(versionToRestoreWithLocales),
169+
draft: draftArg,
170+
fallbackLocale: null,
171+
global: null,
172+
locale: locale!,
173+
overrideAccess: true,
174+
req,
175+
showHiddenFields: true,
176+
})
177+
178+
let data = deepCopyObjectSimple(prevVersionDoc)
179+
180+
// /////////////////////////////////////
181+
// beforeValidate - Fields
182+
// /////////////////////////////////////
183+
184+
data = await beforeValidate({
185+
id: parentDocID,
186+
collection: collectionConfig,
187+
context: req.context,
188+
data,
189+
doc: originalDoc,
190+
global: null,
191+
operation: 'update',
192+
overrideAccess,
193+
req,
194+
})
195+
196+
// /////////////////////////////////////
197+
// beforeValidate - Collection
198+
// /////////////////////////////////////
199+
200+
if (collectionConfig.hooks?.beforeValidate?.length) {
201+
for (const hook of collectionConfig.hooks.beforeValidate) {
202+
data =
203+
(await hook({
204+
collection: collectionConfig,
205+
context: req.context,
206+
data,
207+
operation: 'update',
208+
originalDoc,
209+
req,
210+
})) || data
211+
}
212+
}
213+
214+
// /////////////////////////////////////
215+
// beforeChange - Collection
216+
// /////////////////////////////////////
217+
218+
if (collectionConfig.hooks?.beforeChange?.length) {
219+
for (const hook of collectionConfig.hooks.beforeChange) {
220+
data =
221+
(await hook({
222+
collection: collectionConfig,
223+
context: req.context,
224+
data,
225+
operation: 'update',
226+
originalDoc,
227+
req,
228+
})) || data
229+
}
230+
}
231+
232+
// /////////////////////////////////////
233+
// beforeChange - Fields
234+
// /////////////////////////////////////
235+
236+
let result = await beforeChange({
237+
id: parentDocID,
238+
collection: collectionConfig,
239+
context: req.context,
240+
data: { ...data, id: parentDocID },
241+
doc: originalDoc,
242+
docWithLocales: versionToRestoreWithLocales,
243+
global: null,
244+
operation: 'update',
245+
overrideAccess,
246+
req,
247+
skipValidation:
248+
draftArg && collectionConfig.versions.drafts && !collectionConfig.versions.drafts.validate,
249+
})
250+
121251
// /////////////////////////////////////
122252
// Update
123253
// /////////////////////////////////////
@@ -128,10 +258,10 @@ export const restoreVersionOperation = async <TData extends TypeWithID = any>(
128258
select: incomingSelect,
129259
})
130260

131-
let result = await req.payload.db.updateOne({
261+
result = await req.payload.db.updateOne({
132262
id: parentDocID,
133263
collection: collectionConfig.slug,
134-
data: rawVersion.version,
264+
data: result,
135265
req,
136266
select,
137267
})
@@ -140,18 +270,16 @@ export const restoreVersionOperation = async <TData extends TypeWithID = any>(
140270
// Save `previousDoc` as a version after restoring
141271
// /////////////////////////////////////
142272

143-
const prevVersion = { ...prevDocWithLocales }
144-
145-
delete prevVersion.id
146-
147-
await payload.db.createVersion({
273+
result = await saveVersion({
274+
id: parentDocID,
148275
autosave: false,
149-
collectionSlug: collectionConfig.slug,
150-
createdAt: prevVersion.createdAt,
151-
parent: parentDocID,
276+
collection: collectionConfig,
277+
docWithLocales: result,
278+
draft: draftArg,
279+
operation: 'restoreVersion',
280+
payload,
152281
req,
153-
updatedAt: new Date().toISOString(),
154-
versionData: draft ? { ...rawVersion.version, _status: 'draft' } : rawVersion.version,
282+
select,
155283
})
156284

157285
// /////////////////////////////////////
@@ -225,6 +353,21 @@ export const restoreVersionOperation = async <TData extends TypeWithID = any>(
225353
}
226354
}
227355

356+
// /////////////////////////////////////
357+
// afterOperation - Collection
358+
// /////////////////////////////////////
359+
360+
result = await buildAfterOperation({
361+
args,
362+
collection: collectionConfig,
363+
operation: 'restoreVersion',
364+
result,
365+
})
366+
367+
if (shouldCommit) {
368+
await commitTransaction(req)
369+
}
370+
228371
return result
229372
} catch (error: unknown) {
230373
await killTransaction(req)

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ export const updateDocument = async <
314314
collection: collectionConfig,
315315
docWithLocales: result,
316316
draft: shouldSaveDraft,
317+
operation: 'update',
317318
payload,
318319
publishSpecificLocale,
319320
req,

packages/payload/src/collections/operations/utils.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { forgotPasswordOperation } from '../../auth/operations/forgotPasswo
22
import type { loginOperation } from '../../auth/operations/login.js'
33
import type { refreshOperation } from '../../auth/operations/refresh.js'
44
import type { resetPasswordOperation } from '../../auth/operations/resetPassword.js'
5-
import type { CollectionSlug } from '../../index.js'
5+
import type { CollectionSlug, restoreVersionOperation } from '../../index.js'
66
import type { PayloadRequest } from '../../types/index.js'
77
import type { SanitizedCollectionConfig, SelectFromCollectionSlug } from '../config/types.js'
88
import type { countOperation } from './count.js'
@@ -36,6 +36,7 @@ export type AfterOperationMap<TOperationGeneric extends CollectionSlug> = {
3636
login: typeof loginOperation<TOperationGeneric>
3737
refresh: typeof refreshOperation
3838
resetPassword: typeof resetPasswordOperation<TOperationGeneric>
39+
restoreVersion: typeof restoreVersionOperation
3940
update: typeof updateOperation<TOperationGeneric, SelectFromCollectionSlug<TOperationGeneric>>
4041
updateByID: typeof updateByIDOperation<
4142
TOperationGeneric,
@@ -108,6 +109,11 @@ export type AfterOperationArg<TOperationGeneric extends CollectionSlug> = {
108109
operation: 'resetPassword'
109110
result: Awaited<ReturnType<AfterOperationMap<TOperationGeneric>['resetPassword']>>
110111
}
112+
| {
113+
args: Parameters<AfterOperationMap<TOperationGeneric>['restoreVersion']>[0]
114+
operation: 'restoreVersion'
115+
result: Awaited<ReturnType<AfterOperationMap<TOperationGeneric>['restoreVersion']>>
116+
}
111117
| {
112118
args: Parameters<AfterOperationMap<TOperationGeneric>['update']>[0]
113119
operation: 'update'

packages/payload/src/globals/operations/update.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ export const updateOperation = async <
282282
docWithLocales: result,
283283
draft: shouldSaveDraft,
284284
global: globalConfig,
285+
operation: 'update',
285286
payload,
286287
publishSpecificLocale,
287288
req,

packages/payload/src/versions/saveVersion.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ type Args = {
1616
draft?: boolean
1717
global?: SanitizedGlobalConfig
1818
id?: number | string
19+
operation?: 'create' | 'restoreVersion' | 'update'
1920
payload: Payload
2021
publishSpecificLocale?: string
2122
req?: PayloadRequest
@@ -30,6 +31,7 @@ export const saveVersion = async ({
3031
docWithLocales: doc,
3132
draft,
3233
global,
34+
operation,
3335
payload,
3436
publishSpecificLocale,
3537
req,
@@ -126,7 +128,7 @@ export const saveVersion = async ({
126128
const createVersionArgs = {
127129
autosave: Boolean(autosave),
128130
collectionSlug: undefined as string | undefined,
129-
createdAt: now,
131+
createdAt: operation === 'restoreVersion' ? versionData.createdAt : now,
130132
globalSlug: undefined as string | undefined,
131133
parent: collection ? id : undefined,
132134
publishedLocale: publishSpecificLocale || undefined,

0 commit comments

Comments
 (0)