feat: export sanitizeSelectParam, sanitizePopulateParam, senitizeJoinParams utils (#9777)

### What?
* Exposes to `payload` these functions: `sanitizeSelectParam`,
`sanitizePopulateParam`, `senitizeJoinParams`.
* Refactors `sanitizeSelect` and `sanitizePopulate` to
`sanitizeSelectParam` and `sanitizePopulateParam` for clarity.
* Moves them from `@payloadcms/next` to `payload` as they aren't related
to next.

### Why?
To use these functions externally, for example in custom endpoints.
This commit is contained in:
Sasha
2024-12-06 20:30:04 +02:00
committed by GitHub
parent 62fc2f552f
commit afd0b54d1b
22 changed files with 83 additions and 100 deletions

View File

@@ -1,13 +1,11 @@
import { getTranslation } from '@payloadcms/translations'
import httpStatus from 'http-status'
import { createOperation } from 'payload'
import { createOperation, sanitizePopulateParam, sanitizeSelectParam } from 'payload'
import { isNumber } from 'payload/shared'
import type { CollectionRouteHandler } from '../types.js'
import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
export const create: CollectionRouteHandler = async ({ collection, req }) => {
const { searchParams } = req
@@ -21,9 +19,9 @@ export const create: CollectionRouteHandler = async ({ collection, req }) => {
data: req.data,
depth: isNumber(depth) ? depth : undefined,
draft,
populate: sanitizePopulate(req.query.populate),
populate: sanitizePopulateParam(req.query.populate),
req,
select: sanitizeSelect(req.query.select),
select: sanitizeSelectParam(req.query.select),
})
return Response.json(

View File

@@ -2,14 +2,12 @@ import type { Where } from 'payload'
import { getTranslation } from '@payloadcms/translations'
import httpStatus from 'http-status'
import { deleteOperation } from 'payload'
import { deleteOperation, sanitizePopulateParam, sanitizeSelectParam } from 'payload'
import { isNumber } from 'payload/shared'
import type { CollectionRouteHandler } from '../types.js'
import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
export const deleteDoc: CollectionRouteHandler = async ({ collection, req }) => {
const { depth, overrideLock, populate, select, where } = req.query as {
@@ -24,9 +22,9 @@ export const deleteDoc: CollectionRouteHandler = async ({ collection, req }) =>
collection,
depth: isNumber(depth) ? Number(depth) : undefined,
overrideLock: Boolean(overrideLock === 'true'),
populate: sanitizePopulate(populate),
populate: sanitizePopulateParam(populate),
req,
select: sanitizeSelect(select),
select: sanitizeSelectParam(select),
where,
})

View File

@@ -1,13 +1,11 @@
import httpStatus from 'http-status'
import { deleteByIDOperation } from 'payload'
import { deleteByIDOperation, sanitizePopulateParam, sanitizeSelectParam } from 'payload'
import { isNumber } from 'payload/shared'
import type { CollectionRouteHandlerWithID } from '../types.js'
import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizeCollectionID } from '../utilities/sanitizeCollectionID.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
export const deleteByID: CollectionRouteHandlerWithID = async ({
id: incomingID,
@@ -29,9 +27,9 @@ export const deleteByID: CollectionRouteHandlerWithID = async ({
collection,
depth: isNumber(depth) ? depth : undefined,
overrideLock: Boolean(overrideLock === 'true'),
populate: sanitizePopulate(req.query.populate),
populate: sanitizePopulateParam(req.query.populate),
req,
select: sanitizeSelect(req.query.select),
select: sanitizeSelectParam(req.query.select),
})
const headers = headersWithCors({

View File

@@ -1,14 +1,12 @@
import { getTranslation } from '@payloadcms/translations'
import httpStatus from 'http-status'
import { duplicateOperation } from 'payload'
import { duplicateOperation, sanitizePopulateParam, sanitizeSelectParam } from 'payload'
import { isNumber } from 'payload/shared'
import type { CollectionRouteHandlerWithID } from '../types.js'
import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizeCollectionID } from '../utilities/sanitizeCollectionID.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
export const duplicate: CollectionRouteHandlerWithID = async ({
id: incomingID,
@@ -31,9 +29,9 @@ export const duplicate: CollectionRouteHandlerWithID = async ({
collection,
depth: isNumber(depth) ? Number(depth) : undefined,
draft,
populate: sanitizePopulate(req.query.populate),
populate: sanitizePopulateParam(req.query.populate),
req,
select: sanitizeSelect(req.query.select),
select: sanitizeSelectParam(req.query.select),
})
const message = req.t('general:successfullyDuplicated', {

View File

@@ -1,16 +1,17 @@
import type { JoinQuery, Where } from 'payload'
import httpStatus from 'http-status'
import { findOperation } from 'payload'
import {
findOperation,
sanitizeJoinParams,
sanitizePopulateParam,
sanitizeSelectParam,
} from 'payload'
import { isNumber } from 'payload/shared'
import type { CollectionRouteHandler } from '../types.js'
import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizeJoinParams } from '../utilities/sanitizeJoinParams.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
export const find: CollectionRouteHandler = async ({ collection, req }) => {
const { depth, draft, joins, limit, page, populate, select, sort, where } = req.query as {
depth?: string
@@ -31,9 +32,9 @@ export const find: CollectionRouteHandler = async ({ collection, req }) => {
joins: sanitizeJoinParams(joins),
limit: isNumber(limit) ? Number(limit) : undefined,
page: isNumber(page) ? Number(page) : undefined,
populate: sanitizePopulate(populate),
populate: sanitizePopulateParam(populate),
req,
select: sanitizeSelect(select),
select: sanitizeSelectParam(select),
sort: typeof sort === 'string' ? sort.split(',') : undefined,
where,
})

View File

@@ -1,16 +1,18 @@
import type { JoinQuery } from 'payload'
import httpStatus from 'http-status'
import { findByIDOperation } from 'payload'
import {
findByIDOperation,
sanitizeJoinParams,
sanitizePopulateParam,
sanitizeSelectParam,
} from 'payload'
import { isNumber } from 'payload/shared'
import type { CollectionRouteHandlerWithID } from '../types.js'
import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizeCollectionID } from '../utilities/sanitizeCollectionID.js'
import { sanitizeJoinParams } from '../utilities/sanitizeJoinParams.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
export const findByID: CollectionRouteHandlerWithID = async ({
id: incomingID,
@@ -32,9 +34,9 @@ export const findByID: CollectionRouteHandlerWithID = async ({
depth: isNumber(depth) ? Number(depth) : undefined,
draft: searchParams.get('draft') === 'true',
joins: sanitizeJoinParams(req.query.joins as JoinQuery),
populate: sanitizePopulate(req.query.populate),
populate: sanitizePopulateParam(req.query.populate),
req,
select: sanitizeSelect(req.query.select),
select: sanitizeSelectParam(req.query.select),
})
return Response.json(result, {

View File

@@ -1,13 +1,11 @@
import httpStatus from 'http-status'
import { findVersionByIDOperation } from 'payload'
import { findVersionByIDOperation, sanitizePopulateParam, sanitizeSelectParam } from 'payload'
import { isNumber } from 'payload/shared'
import type { CollectionRouteHandlerWithID } from '../types.js'
import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizeCollectionID } from '../utilities/sanitizeCollectionID.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
export const findVersionByID: CollectionRouteHandlerWithID = async ({
id: incomingID,
@@ -27,9 +25,9 @@ export const findVersionByID: CollectionRouteHandlerWithID = async ({
id,
collection,
depth: isNumber(depth) ? Number(depth) : undefined,
populate: sanitizePopulate(req.query.populate),
populate: sanitizePopulateParam(req.query.populate),
req,
select: sanitizeSelect(req.query.select),
select: sanitizeSelectParam(req.query.select),
})
return Response.json(result, {

View File

@@ -1,14 +1,12 @@
import type { Where } from 'payload'
import httpStatus from 'http-status'
import { findVersionsOperation } from 'payload'
import { findVersionsOperation, sanitizePopulateParam, sanitizeSelectParam } from 'payload'
import { isNumber } from 'payload/shared'
import type { CollectionRouteHandler } from '../types.js'
import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
export const findVersions: CollectionRouteHandler = async ({ collection, req }) => {
const { depth, limit, page, populate, select, sort, where } = req.query as {
@@ -26,9 +24,9 @@ export const findVersions: CollectionRouteHandler = async ({ collection, req })
depth: isNumber(depth) ? Number(depth) : undefined,
limit: isNumber(limit) ? Number(limit) : undefined,
page: isNumber(page) ? Number(page) : undefined,
populate: sanitizePopulate(populate),
populate: sanitizePopulateParam(populate),
req,
select: sanitizeSelect(select),
select: sanitizeSelectParam(select),
sort: typeof sort === 'string' ? sort.split(',') : undefined,
where,
})

View File

@@ -1,12 +1,11 @@
import httpStatus from 'http-status'
import { restoreVersionOperation } from 'payload'
import { restoreVersionOperation, sanitizePopulateParam } from 'payload'
import { isNumber } from 'payload/shared'
import type { CollectionRouteHandlerWithID } from '../types.js'
import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizeCollectionID } from '../utilities/sanitizeCollectionID.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
export const restoreVersion: CollectionRouteHandlerWithID = async ({
id: incomingID,
@@ -28,7 +27,7 @@ export const restoreVersion: CollectionRouteHandlerWithID = async ({
collection,
depth: isNumber(depth) ? Number(depth) : undefined,
draft: draft === 'true' ? true : undefined,
populate: sanitizePopulate(req.query.populate),
populate: sanitizePopulateParam(req.query.populate),
req,
})

View File

@@ -2,14 +2,12 @@ import type { Where } from 'payload'
import { getTranslation } from '@payloadcms/translations'
import httpStatus from 'http-status'
import { updateOperation } from 'payload'
import { sanitizePopulateParam, sanitizeSelectParam, updateOperation } from 'payload'
import { isNumber } from 'payload/shared'
import type { CollectionRouteHandler } from '../types.js'
import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
export const update: CollectionRouteHandler = async ({ collection, req }) => {
const { depth, draft, limit, overrideLock, populate, select, where } = req.query as {
@@ -29,9 +27,9 @@ export const update: CollectionRouteHandler = async ({ collection, req }) => {
draft: draft === 'true',
limit: isNumber(limit) ? Number(limit) : undefined,
overrideLock: Boolean(overrideLock === 'true'),
populate: sanitizePopulate(populate),
populate: sanitizePopulateParam(populate),
req,
select: sanitizeSelect(select),
select: sanitizeSelectParam(select),
where,
})

View File

@@ -1,13 +1,11 @@
import httpStatus from 'http-status'
import { updateByIDOperation } from 'payload'
import { sanitizePopulateParam, sanitizeSelectParam, updateByIDOperation } from 'payload'
import { isNumber } from 'payload/shared'
import type { CollectionRouteHandlerWithID } from '../types.js'
import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizeCollectionID } from '../utilities/sanitizeCollectionID.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
export const updateByID: CollectionRouteHandlerWithID = async ({
id: incomingID,
@@ -35,10 +33,10 @@ export const updateByID: CollectionRouteHandlerWithID = async ({
depth: isNumber(depth) ? Number(depth) : undefined,
draft,
overrideLock: Boolean(overrideLock === 'true'),
populate: sanitizePopulate(req.query.populate),
populate: sanitizePopulateParam(req.query.populate),
publishSpecificLocale,
req,
select: sanitizeSelect(req.query.select),
select: sanitizeSelectParam(req.query.select),
})
let message = req.t('general:updatedSuccessfully')

View File

@@ -1,12 +1,10 @@
import httpStatus from 'http-status'
import { findOneOperation } from 'payload'
import { findOneOperation, sanitizePopulateParam, sanitizeSelectParam } from 'payload'
import { isNumber } from 'payload/shared'
import type { GlobalRouteHandler } from '../types.js'
import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
export const findOne: GlobalRouteHandler = async ({ globalConfig, req }) => {
const { searchParams } = req
@@ -17,9 +15,9 @@ export const findOne: GlobalRouteHandler = async ({ globalConfig, req }) => {
depth: isNumber(depth) ? Number(depth) : undefined,
draft: searchParams.get('draft') === 'true',
globalConfig,
populate: sanitizePopulate(req.query.populate),
populate: sanitizePopulateParam(req.query.populate),
req,
select: sanitizeSelect(req.query.select),
select: sanitizeSelectParam(req.query.select),
})
return Response.json(result, {

View File

@@ -1,12 +1,10 @@
import httpStatus from 'http-status'
import { findVersionByIDOperationGlobal } from 'payload'
import { findVersionByIDOperationGlobal, sanitizePopulateParam, sanitizeSelectParam } from 'payload'
import { isNumber } from 'payload/shared'
import type { GlobalRouteHandlerWithID } from '../types.js'
import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
export const findVersionByID: GlobalRouteHandlerWithID = async ({ id, globalConfig, req }) => {
const { searchParams } = req
@@ -16,9 +14,9 @@ export const findVersionByID: GlobalRouteHandlerWithID = async ({ id, globalConf
id,
depth: isNumber(depth) ? Number(depth) : undefined,
globalConfig,
populate: sanitizePopulate(req.query.populate),
populate: sanitizePopulateParam(req.query.populate),
req,
select: sanitizeSelect(req.query.select),
select: sanitizeSelectParam(req.query.select),
})
return Response.json(result, {

View File

@@ -1,14 +1,12 @@
import type { Where } from 'payload'
import httpStatus from 'http-status'
import { findVersionsOperationGlobal } from 'payload'
import { findVersionsOperationGlobal, sanitizePopulateParam, sanitizeSelectParam } from 'payload'
import { isNumber } from 'payload/shared'
import type { GlobalRouteHandler } from '../types.js'
import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
export const findVersions: GlobalRouteHandler = async ({ globalConfig, req }) => {
const { depth, limit, page, populate, select, sort, where } = req.query as {
@@ -26,9 +24,9 @@ export const findVersions: GlobalRouteHandler = async ({ globalConfig, req }) =>
globalConfig,
limit: isNumber(limit) ? Number(limit) : undefined,
page: isNumber(page) ? Number(page) : undefined,
populate: sanitizePopulate(populate),
populate: sanitizePopulateParam(populate),
req,
select: sanitizeSelect(select),
select: sanitizeSelectParam(select),
sort: typeof sort === 'string' ? sort.split(',') : undefined,
where,
})

View File

@@ -1,11 +1,10 @@
import httpStatus from 'http-status'
import { restoreVersionOperationGlobal } from 'payload'
import { restoreVersionOperationGlobal, sanitizePopulateParam } from 'payload'
import { isNumber } from 'payload/shared'
import type { GlobalRouteHandlerWithID } from '../types.js'
import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
export const restoreVersion: GlobalRouteHandlerWithID = async ({ id, globalConfig, req }) => {
const { searchParams } = req
@@ -17,7 +16,7 @@ export const restoreVersion: GlobalRouteHandlerWithID = async ({ id, globalConfi
depth: isNumber(depth) ? Number(depth) : undefined,
draft: draft === 'true' ? true : undefined,
globalConfig,
populate: sanitizePopulate(req.query.populate),
populate: sanitizePopulateParam(req.query.populate),
req,
})

View File

@@ -1,12 +1,10 @@
import httpStatus from 'http-status'
import { updateOperationGlobal } from 'payload'
import { sanitizePopulateParam, sanitizeSelectParam, updateOperationGlobal } from 'payload'
import { isNumber } from 'payload/shared'
import type { GlobalRouteHandler } from '../types.js'
import { headersWithCors } from '../../../utilities/headersWithCors.js'
import { sanitizePopulate } from '../utilities/sanitizePopulate.js'
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
export const update: GlobalRouteHandler = async ({ globalConfig, req }) => {
const { searchParams } = req
@@ -22,10 +20,10 @@ export const update: GlobalRouteHandler = async ({ globalConfig, req }) => {
depth: isNumber(depth) ? Number(depth) : undefined,
draft,
globalConfig,
populate: sanitizePopulate(req.query.populate),
populate: sanitizePopulateParam(req.query.populate),
publishSpecificLocale,
req,
select: sanitizeSelect(req.query.select),
select: sanitizeSelectParam(req.query.select),
})
let message = req.t('general:updatedSuccessfully')

View File

@@ -1,15 +0,0 @@
import type { PopulateType } from 'payload'
import { sanitizeSelect } from './sanitizeSelect.js'
export const sanitizePopulate = (unsanitizedPopulate: unknown): PopulateType | undefined => {
if (!unsanitizedPopulate || typeof unsanitizedPopulate !== 'object') {
return
}
for (const k in unsanitizedPopulate) {
unsanitizedPopulate[k] = sanitizeSelect(unsanitizedPopulate[k])
}
return unsanitizedPopulate as PopulateType
}

View File

@@ -1355,6 +1355,9 @@ export { isValidID } from './utilities/isValidID.js'
export { killTransaction } from './utilities/killTransaction.js'
export { mapAsync } from './utilities/mapAsync.js'
export { sanitizeFallbackLocale } from './utilities/sanitizeFallbackLocale.js'
export { sanitizeJoinParams } from './utilities/sanitizeJoinParams.js'
export { sanitizePopulateParam } from './utilities/sanitizePopulateParam.js'
export { sanitizeSelectParam } from './utilities/sanitizeSelectParam.js'
export { traverseFields } from './utilities/traverseFields.js'
export type { TraverseFieldsCallback } from './utilities/traverseFields.js'
export { buildVersionCollectionFields } from './versions/buildCollectionFields.js'

View File

@@ -1,6 +1,6 @@
import type { JoinQuery } from 'payload'
import type { JoinQuery } from '../types/index.js'
import { isNumber } from 'payload/shared'
import { isNumber } from './isNumber.js'
/**
* Convert request JoinQuery object from strings to numbers

View File

@@ -0,0 +1,18 @@
import type { PopulateType } from '../types/index.js'
import { sanitizeSelectParam } from './sanitizeSelectParam.js'
/**
* Sanitizes REST populate query to PopulateType
*/
export const sanitizePopulateParam = (unsanitizedPopulate: unknown): PopulateType | undefined => {
if (!unsanitizedPopulate || typeof unsanitizedPopulate !== 'object') {
return
}
for (const k in unsanitizedPopulate) {
unsanitizedPopulate[k] = sanitizeSelectParam(unsanitizedPopulate[k])
}
return unsanitizedPopulate as PopulateType
}

View File

@@ -1,9 +1,9 @@
import type { SelectType } from 'payload'
import type { SelectType } from '../types/index.js'
/**
* Sanitizes REST select query to SelectType
*/
export const sanitizeSelect = (unsanitizedSelect: unknown): SelectType | undefined => {
export const sanitizeSelectParam = (unsanitizedSelect: unknown): SelectType | undefined => {
if (unsanitizedSelect && typeof unsanitizedSelect === 'object') {
for (const k in unsanitizedSelect) {
if (unsanitizedSelect[k] === 'true') {
@@ -11,7 +11,7 @@ export const sanitizeSelect = (unsanitizedSelect: unknown): SelectType | undefin
} else if (unsanitizedSelect[k] === 'false') {
unsanitizedSelect[k] = false
} else if (typeof unsanitizedSelect[k] === 'object') {
sanitizeSelect(unsanitizedSelect[k])
sanitizeSelectParam(unsanitizedSelect[k])
}
}
}