Files
payloadcms/packages/payload/src/globals/operations/restoreVersion.ts
Sasha a22c0e62fa feat: add populate property to Local / REST API (#8969)
### What?
Adds `populate` property to Local API and REST API operations that can
be used to specify `select` for a specific collection when it's
populated
```ts
const result = await payload.findByID({
  populate: {
   // type safe if you have generated types
    posts: {
      text: true,
    },
  },
  collection: 'pages',
  depth: 1,
  id: aboutPage.id,
})

result.relatedPost // only has text and id properties
``` 

```ts
fetch('https://localhost:3000/api/pages?populate[posts][text]=true') // highlight-line
  .then((res) => res.json())
  .then((data) => console.log(data))
```

It also overrides
[`defaultPopulate`](https://github.com/payloadcms/payload/pull/8934)

Ensures `defaultPopulate` doesn't affect GraphQL.

### How?
Implements the property for all operations that have the `depth`
argument.
2024-11-06 13:50:19 -05:00

199 lines
4.9 KiB
TypeScript

import type { PayloadRequest, PopulateType } from '../../types/index.js'
import type { TypeWithVersion } from '../../versions/types.js'
import type { SanitizedGlobalConfig } from '../config/types.js'
import executeAccess from '../../auth/executeAccess.js'
import { NotFound } from '../../errors/index.js'
import { afterChange } from '../../fields/hooks/afterChange/index.js'
import { afterRead } from '../../fields/hooks/afterRead/index.js'
import { commitTransaction } from '../../utilities/commitTransaction.js'
import { initTransaction } from '../../utilities/initTransaction.js'
import { killTransaction } from '../../utilities/killTransaction.js'
export type Arguments = {
depth?: number
draft?: boolean
globalConfig: SanitizedGlobalConfig
id: number | string
overrideAccess?: boolean
populate?: PopulateType
req?: PayloadRequest
showHiddenFields?: boolean
}
export const restoreVersionOperation = async <T extends TypeWithVersion<T> = any>(
args: Arguments,
): Promise<T> => {
const {
id,
depth,
draft,
globalConfig,
overrideAccess,
populate,
req: { fallbackLocale, locale, payload },
req,
showHiddenFields,
} = args
try {
const shouldCommit = await initTransaction(req)
// /////////////////////////////////////
// Access
// /////////////////////////////////////
if (!overrideAccess) {
await executeAccess({ req }, globalConfig.access.update)
}
// /////////////////////////////////////
// Retrieve original raw version
// /////////////////////////////////////
const { docs: versionDocs } = await payload.db.findGlobalVersions<any>({
global: globalConfig.slug,
limit: 1,
req,
where: { id: { equals: id } },
})
if (!versionDocs || versionDocs.length === 0) {
throw new NotFound(req.t)
}
const rawVersion = versionDocs[0]
// Patch globalType onto version doc
rawVersion.version.globalType = globalConfig.slug
// Overwrite draft status if draft is true
if (draft) {
rawVersion.version._status = 'draft'
}
// /////////////////////////////////////
// fetch previousDoc
// /////////////////////////////////////
const previousDoc = await payload.findGlobal({
slug: globalConfig.slug,
depth,
req,
})
// /////////////////////////////////////
// Update global
// /////////////////////////////////////
const global = await payload.db.findGlobal({
slug: globalConfig.slug,
req,
})
let result = rawVersion.version
if (global) {
result = await payload.db.updateGlobal({
slug: globalConfig.slug,
data: result,
req,
})
const now = new Date().toISOString()
result = await payload.db.createGlobalVersion({
autosave: false,
createdAt: result.createdAt ? new Date(result.createdAt).toISOString() : now,
globalSlug: globalConfig.slug,
parent: id,
req,
updatedAt: draft ? now : new Date(result.updatedAt).toISOString(),
versionData: result,
})
} else {
result = await payload.db.createGlobal({
slug: globalConfig.slug,
data: result,
req,
})
}
// /////////////////////////////////////
// afterRead - Fields
// /////////////////////////////////////
result = await afterRead({
collection: null,
context: req.context,
depth,
doc: result,
draft: undefined,
fallbackLocale,
global: globalConfig,
locale,
overrideAccess,
populate,
req,
showHiddenFields,
})
// /////////////////////////////////////
// afterRead - Global
// /////////////////////////////////////
await globalConfig.hooks.afterRead.reduce(async (priorHook, hook) => {
await priorHook
result =
(await hook({
context: req.context,
doc: result,
global: globalConfig,
req,
})) || result
}, Promise.resolve())
// /////////////////////////////////////
// afterChange - Fields
// /////////////////////////////////////
result = await afterChange({
collection: null,
context: req.context,
data: result,
doc: result,
global: globalConfig,
operation: 'update',
previousDoc,
req,
})
// /////////////////////////////////////
// afterChange - Global
// /////////////////////////////////////
await globalConfig.hooks.afterChange.reduce(async (priorHook, hook) => {
await priorHook
result =
(await hook({
context: req.context,
doc: result,
global: globalConfig,
previousDoc,
req,
})) || result
}, Promise.resolve())
if (shouldCommit) {
await commitTransaction(req)
}
return result
} catch (error: unknown) {
await killTransaction(req)
throw error
}
}