perf: operations performance optimizations (#10609)
- reduces unnecessary shallow copying within operations by removing unnecessary spreads or .map()'s - removes unnecessary `deleteMany` call in `deleteUserPreferences` for auth-enabled collections - replaces all instances of `validOperators.includes` with `validOperatorMap[]`. O(n) => O(1) - optimizes the `sanitizeInternalFields` function. Previously, it was doing a **lot** of shallow copying
This commit is contained in:
@@ -2,7 +2,7 @@ import type { FlattenedField, Operator, PathToQuery, Payload } from 'payload'
|
|||||||
|
|
||||||
import { Types } from 'mongoose'
|
import { Types } from 'mongoose'
|
||||||
import { getLocalizedPaths } from 'payload'
|
import { getLocalizedPaths } from 'payload'
|
||||||
import { validOperators } from 'payload/shared'
|
import { validOperatorSet } from 'payload/shared'
|
||||||
|
|
||||||
import type { MongooseAdapter } from '../index.js'
|
import type { MongooseAdapter } from '../index.js'
|
||||||
|
|
||||||
@@ -187,7 +187,7 @@ export async function buildSearchParam({
|
|||||||
return relationshipQuery
|
return relationshipQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
if (formattedOperator && validOperators.includes(formattedOperator as Operator)) {
|
if (formattedOperator && validOperatorSet.has(formattedOperator as Operator)) {
|
||||||
const operatorKey = operatorMap[formattedOperator]
|
const operatorKey = operatorMap[formattedOperator]
|
||||||
|
|
||||||
if (field.type === 'relationship' || field.type === 'upload') {
|
if (field.type === 'relationship' || field.type === 'upload') {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { FilterQuery } from 'mongoose'
|
|||||||
import type { FlattenedField, Operator, Payload, Where } from 'payload'
|
import type { FlattenedField, Operator, Payload, Where } from 'payload'
|
||||||
|
|
||||||
import { deepMergeWithCombinedArrays } from 'payload'
|
import { deepMergeWithCombinedArrays } from 'payload'
|
||||||
import { validOperators } from 'payload/shared'
|
import { validOperatorSet } from 'payload/shared'
|
||||||
|
|
||||||
import { buildAndOrConditions } from './buildAndOrConditions.js'
|
import { buildAndOrConditions } from './buildAndOrConditions.js'
|
||||||
import { buildSearchParam } from './buildSearchParams.js'
|
import { buildSearchParam } from './buildSearchParams.js'
|
||||||
@@ -53,7 +53,7 @@ export async function parseParams({
|
|||||||
const pathOperators = where[relationOrPath]
|
const pathOperators = where[relationOrPath]
|
||||||
if (typeof pathOperators === 'object') {
|
if (typeof pathOperators === 'object') {
|
||||||
for (const operator of Object.keys(pathOperators)) {
|
for (const operator of Object.keys(pathOperators)) {
|
||||||
if (validOperators.includes(operator as Operator)) {
|
if (validOperatorSet.has(operator as Operator)) {
|
||||||
const searchParam = await buildSearchParam({
|
const searchParam = await buildSearchParam({
|
||||||
collectionSlug,
|
collectionSlug,
|
||||||
fields,
|
fields,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type { FlattenedField, Operator, Where } from 'payload'
|
|||||||
import { and, isNotNull, isNull, ne, notInArray, or, sql } from 'drizzle-orm'
|
import { and, isNotNull, isNull, ne, notInArray, or, sql } from 'drizzle-orm'
|
||||||
import { PgUUID } from 'drizzle-orm/pg-core'
|
import { PgUUID } from 'drizzle-orm/pg-core'
|
||||||
import { QueryError } from 'payload'
|
import { QueryError } from 'payload'
|
||||||
import { validOperators } from 'payload/shared'
|
import { validOperatorSet } from 'payload/shared'
|
||||||
|
|
||||||
import type { DrizzleAdapter, GenericColumn } from '../types.js'
|
import type { DrizzleAdapter, GenericColumn } from '../types.js'
|
||||||
import type { BuildQueryJoinAliases } from './buildQuery.js'
|
import type { BuildQueryJoinAliases } from './buildQuery.js'
|
||||||
@@ -73,7 +73,7 @@ export function parseParams({
|
|||||||
const pathOperators = where[relationOrPath]
|
const pathOperators = where[relationOrPath]
|
||||||
if (typeof pathOperators === 'object') {
|
if (typeof pathOperators === 'object') {
|
||||||
for (let operator of Object.keys(pathOperators)) {
|
for (let operator of Object.keys(pathOperators)) {
|
||||||
if (validOperators.includes(operator as Operator)) {
|
if (validOperatorSet.has(operator as Operator)) {
|
||||||
const val = where[relationOrPath][operator]
|
const val = where[relationOrPath][operator]
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ type GetAccessResultsArgs = {
|
|||||||
export async function getAccessResults({
|
export async function getAccessResults({
|
||||||
req,
|
req,
|
||||||
}: GetAccessResultsArgs): Promise<SanitizedPermissions> {
|
}: GetAccessResultsArgs): Promise<SanitizedPermissions> {
|
||||||
const results = {} as Permissions
|
const results = {
|
||||||
|
collections: {},
|
||||||
|
globals: {},
|
||||||
|
} as Permissions
|
||||||
const { payload, user } = req
|
const { payload, user } = req
|
||||||
|
|
||||||
const isLoggedIn = !!user
|
const isLoggedIn = !!user
|
||||||
@@ -49,10 +52,7 @@ export async function getAccessResults({
|
|||||||
operations: collectionOperations,
|
operations: collectionOperations,
|
||||||
req,
|
req,
|
||||||
})
|
})
|
||||||
results.collections = {
|
results.collections[collection.slug] = collectionPolicy
|
||||||
...results.collections,
|
|
||||||
[collection.slug]: collectionPolicy,
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -70,10 +70,7 @@ export async function getAccessResults({
|
|||||||
operations: globalOperations,
|
operations: globalOperations,
|
||||||
req,
|
req,
|
||||||
})
|
})
|
||||||
results.globals = {
|
results.globals[global.slug] = globalPolicy
|
||||||
...results.globals,
|
|
||||||
[global.slug]: globalPolicy,
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ export type Arguments = {
|
|||||||
where?: Where
|
where?: Where
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const lockDurationDefault = 300 // Default 5 minutes in seconds
|
||||||
|
|
||||||
export const findOperation = async <
|
export const findOperation = async <
|
||||||
TSlug extends CollectionSlug,
|
TSlug extends CollectionSlug,
|
||||||
TSelect extends SelectFromCollectionSlug<TSlug>,
|
TSelect extends SelectFromCollectionSlug<TSlug>,
|
||||||
@@ -189,11 +191,12 @@ export const findOperation = async <
|
|||||||
try {
|
try {
|
||||||
const lockDocumentsProp = collectionConfig?.lockDocuments
|
const lockDocumentsProp = collectionConfig?.lockDocuments
|
||||||
|
|
||||||
const lockDurationDefault = 300 // Default 5 minutes in seconds
|
|
||||||
const lockDuration =
|
const lockDuration =
|
||||||
typeof lockDocumentsProp === 'object' ? lockDocumentsProp.duration : lockDurationDefault
|
typeof lockDocumentsProp === 'object' ? lockDocumentsProp.duration : lockDurationDefault
|
||||||
const lockDurationInMilliseconds = lockDuration * 1000
|
const lockDurationInMilliseconds = lockDuration * 1000
|
||||||
|
|
||||||
|
const now = new Date().getTime()
|
||||||
|
|
||||||
const lockedDocuments = await payload.find({
|
const lockedDocuments = await payload.find({
|
||||||
collection: 'payload-locked-documents',
|
collection: 'payload-locked-documents',
|
||||||
depth: 1,
|
depth: 1,
|
||||||
@@ -216,14 +219,13 @@ export const findOperation = async <
|
|||||||
// Query where the lock is newer than the current time minus lock time
|
// Query where the lock is newer than the current time minus lock time
|
||||||
{
|
{
|
||||||
updatedAt: {
|
updatedAt: {
|
||||||
greater_than: new Date(new Date().getTime() - lockDurationInMilliseconds),
|
greater_than: new Date(now - lockDurationInMilliseconds),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const now = new Date().getTime()
|
|
||||||
const lockedDocs = Array.isArray(lockedDocuments?.docs) ? lockedDocuments.docs : []
|
const lockedDocs = Array.isArray(lockedDocuments?.docs) ? lockedDocuments.docs : []
|
||||||
|
|
||||||
// Filter out stale locks
|
// Filter out stale locks
|
||||||
@@ -232,20 +234,16 @@ export const findOperation = async <
|
|||||||
return lastEditedAt + lockDurationInMilliseconds > now
|
return lastEditedAt + lockDurationInMilliseconds > now
|
||||||
})
|
})
|
||||||
|
|
||||||
result.docs = result.docs.map((doc) => {
|
for (const doc of result.docs) {
|
||||||
const lockedDoc = validLockedDocs.find((lock) => lock?.document?.value === doc.id)
|
const lockedDoc = validLockedDocs.find((lock) => lock?.document?.value === doc.id)
|
||||||
return {
|
doc._isLocked = !!lockedDoc
|
||||||
...doc,
|
doc._userEditing = lockedDoc ? lockedDoc?.user?.value : null
|
||||||
_isLocked: !!lockedDoc,
|
}
|
||||||
_userEditing: lockedDoc ? lockedDoc?.user?.value : null,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
result.docs = result.docs.map((doc) => ({
|
for (const doc of result.docs) {
|
||||||
...doc,
|
doc._isLocked = false
|
||||||
_isLocked: false,
|
doc._userEditing = null
|
||||||
_userEditing: null,
|
}
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,9 +251,8 @@ export const findOperation = async <
|
|||||||
// beforeRead - Collection
|
// beforeRead - Collection
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|
||||||
result = {
|
if (collectionConfig?.hooks?.beforeRead?.length) {
|
||||||
...result,
|
result.docs = await Promise.all(
|
||||||
docs: await Promise.all(
|
|
||||||
result.docs.map(async (doc) => {
|
result.docs.map(async (doc) => {
|
||||||
let docRef = doc
|
let docRef = doc
|
||||||
|
|
||||||
@@ -274,45 +271,41 @@ export const findOperation = async <
|
|||||||
|
|
||||||
return docRef
|
return docRef
|
||||||
}),
|
}),
|
||||||
),
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
// afterRead - Fields
|
// afterRead - Fields
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|
||||||
result = {
|
result.docs = await Promise.all(
|
||||||
...result,
|
result.docs.map(async (doc) =>
|
||||||
docs: await Promise.all(
|
afterRead<DataFromCollectionSlug<TSlug>>({
|
||||||
result.docs.map(async (doc) =>
|
collection: collectionConfig,
|
||||||
afterRead<DataFromCollectionSlug<TSlug>>({
|
context: req.context,
|
||||||
collection: collectionConfig,
|
currentDepth,
|
||||||
context: req.context,
|
depth,
|
||||||
currentDepth,
|
doc,
|
||||||
depth,
|
draft: draftsEnabled,
|
||||||
doc,
|
fallbackLocale,
|
||||||
draft: draftsEnabled,
|
findMany: true,
|
||||||
fallbackLocale,
|
global: null,
|
||||||
findMany: true,
|
locale,
|
||||||
global: null,
|
overrideAccess,
|
||||||
locale,
|
populate,
|
||||||
overrideAccess,
|
req,
|
||||||
populate,
|
select,
|
||||||
req,
|
showHiddenFields,
|
||||||
select,
|
}),
|
||||||
showHiddenFields,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
}
|
)
|
||||||
|
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
// afterRead - Collection
|
// afterRead - Collection
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|
||||||
result = {
|
if (collectionConfig?.hooks?.afterRead?.length) {
|
||||||
...result,
|
result.docs = await Promise.all(
|
||||||
docs: await Promise.all(
|
|
||||||
result.docs.map(async (doc) => {
|
result.docs.map(async (doc) => {
|
||||||
let docRef = doc
|
let docRef = doc
|
||||||
|
|
||||||
@@ -332,7 +325,7 @@ export const findOperation = async <
|
|||||||
|
|
||||||
return docRef
|
return docRef
|
||||||
}),
|
}),
|
||||||
),
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|||||||
@@ -87,70 +87,62 @@ export const findVersionsOperation = async <TData extends TypeWithVersion<TData>
|
|||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
// beforeRead - Collection
|
// beforeRead - Collection
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
const result: PaginatedDocs<TData> = paginatedDocs as unknown as PaginatedDocs<TData>
|
||||||
|
result.docs = (await Promise.all(
|
||||||
|
paginatedDocs.docs.map(async (doc) => {
|
||||||
|
const docRef = doc
|
||||||
|
// Fallback if not selected
|
||||||
|
if (!docRef.version) {
|
||||||
|
;(docRef as any).version = {}
|
||||||
|
}
|
||||||
|
await collectionConfig.hooks.beforeRead.reduce(async (priorHook, hook) => {
|
||||||
|
await priorHook
|
||||||
|
|
||||||
let result = {
|
docRef.version =
|
||||||
...paginatedDocs,
|
(await hook({
|
||||||
docs: await Promise.all(
|
collection: collectionConfig,
|
||||||
paginatedDocs.docs.map(async (doc) => {
|
context: req.context,
|
||||||
const docRef = doc
|
doc: docRef.version,
|
||||||
// Fallback if not selected
|
query: fullWhere,
|
||||||
if (!docRef.version) {
|
req,
|
||||||
;(docRef as any).version = {}
|
})) || docRef.version
|
||||||
}
|
}, Promise.resolve())
|
||||||
await collectionConfig.hooks.beforeRead.reduce(async (priorHook, hook) => {
|
|
||||||
await priorHook
|
|
||||||
|
|
||||||
docRef.version =
|
|
||||||
(await hook({
|
|
||||||
collection: collectionConfig,
|
|
||||||
context: req.context,
|
|
||||||
doc: docRef.version,
|
|
||||||
query: fullWhere,
|
|
||||||
req,
|
|
||||||
})) || docRef.version
|
|
||||||
}, Promise.resolve())
|
|
||||||
|
|
||||||
return docRef
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
} as PaginatedDocs<TData>
|
|
||||||
|
|
||||||
|
return docRef
|
||||||
|
}),
|
||||||
|
)) as TData[]
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
// afterRead - Fields
|
// afterRead - Fields
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|
||||||
result = {
|
result.docs = await Promise.all(
|
||||||
...result,
|
result.docs.map(async (data) => {
|
||||||
docs: await Promise.all(
|
data.version = await afterRead({
|
||||||
result.docs.map(async (data) => ({
|
collection: collectionConfig,
|
||||||
...data,
|
context: req.context,
|
||||||
version: await afterRead({
|
depth,
|
||||||
collection: collectionConfig,
|
doc: data.version,
|
||||||
context: req.context,
|
draft: undefined,
|
||||||
depth,
|
fallbackLocale,
|
||||||
doc: data.version,
|
findMany: true,
|
||||||
draft: undefined,
|
global: null,
|
||||||
fallbackLocale,
|
locale,
|
||||||
findMany: true,
|
overrideAccess,
|
||||||
global: null,
|
populate,
|
||||||
locale,
|
req,
|
||||||
overrideAccess,
|
select: typeof select?.version === 'object' ? select.version : undefined,
|
||||||
populate,
|
showHiddenFields,
|
||||||
req,
|
})
|
||||||
select: typeof select?.version === 'object' ? select.version : undefined,
|
return data
|
||||||
showHiddenFields,
|
}),
|
||||||
}),
|
)
|
||||||
})),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
// afterRead - Collection
|
// afterRead - Collection
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|
||||||
result = {
|
if (collectionConfig.hooks.afterRead?.length) {
|
||||||
...result,
|
result.docs = await Promise.all(
|
||||||
docs: await Promise.all(
|
|
||||||
result.docs.map(async (doc) => {
|
result.docs.map(async (doc) => {
|
||||||
const docRef = doc
|
const docRef = doc
|
||||||
|
|
||||||
@@ -170,17 +162,13 @@ export const findVersionsOperation = async <TData extends TypeWithVersion<TData>
|
|||||||
|
|
||||||
return docRef
|
return docRef
|
||||||
}),
|
}),
|
||||||
),
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
// Return results
|
// Return results
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
result.docs = result.docs.map((doc) => sanitizeInternalFields<TData>(doc))
|
||||||
result = {
|
|
||||||
...result,
|
|
||||||
docs: result.docs.map((doc) => sanitizeInternalFields<TData>(doc)),
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import type { Operator, PayloadRequest, Where, WhereField } from '../../types/in
|
|||||||
import type { EntityPolicies } from './types.js'
|
import type { EntityPolicies } from './types.js'
|
||||||
|
|
||||||
import { QueryError } from '../../errors/QueryError.js'
|
import { QueryError } from '../../errors/QueryError.js'
|
||||||
import { validOperators } from '../../types/constants.js'
|
import { validOperatorSet } from '../../types/constants.js'
|
||||||
import { validateSearchParam } from './validateSearchParams.js'
|
import { validateSearchParam } from './validateSearchParams.js'
|
||||||
|
|
||||||
type Args = {
|
type Args = {
|
||||||
@@ -58,10 +58,11 @@ export async function validateQueryPaths({
|
|||||||
const whereFields = flattenWhere(where)
|
const whereFields = flattenWhere(where)
|
||||||
// We need to determine if the whereKey is an AND, OR, or a schema path
|
// We need to determine if the whereKey is an AND, OR, or a schema path
|
||||||
const promises = []
|
const promises = []
|
||||||
void whereFields.map((constraint) => {
|
for (const constraint of whereFields) {
|
||||||
void Object.keys(constraint).map((path) => {
|
for (const path in constraint) {
|
||||||
void Object.entries(constraint[path]).map(([operator, val]) => {
|
for (const operator in constraint[path]) {
|
||||||
if (validOperators.includes(operator as Operator)) {
|
const val = constraint[path][operator]
|
||||||
|
if (validOperatorSet.has(operator as Operator)) {
|
||||||
promises.push(
|
promises.push(
|
||||||
validateSearchParam({
|
validateSearchParam({
|
||||||
collectionConfig,
|
collectionConfig,
|
||||||
@@ -78,9 +79,10 @@ export async function validateQueryPaths({
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
})
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
await Promise.all(promises)
|
await Promise.all(promises)
|
||||||
if (errors.length > 0) {
|
if (errors.length > 0) {
|
||||||
throw new QueryError(errors)
|
throw new QueryError(errors)
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export { getFieldPaths } from '../fields/getFieldPaths.js'
|
|||||||
|
|
||||||
export * from '../fields/validations.js'
|
export * from '../fields/validations.js'
|
||||||
|
|
||||||
export { validOperators } from '../types/constants.js'
|
export { validOperators, validOperatorSet } from '../types/constants.js'
|
||||||
|
|
||||||
export { formatFilesize } from '../uploads/formatFilesize.js'
|
export { formatFilesize } from '../uploads/formatFilesize.js'
|
||||||
|
|
||||||
|
|||||||
@@ -17,22 +17,30 @@ export const deleteUserPreferences = async ({ collectionConfig, ids, payload, re
|
|||||||
collection: 'payload-preferences',
|
collection: 'payload-preferences',
|
||||||
req,
|
req,
|
||||||
where: {
|
where: {
|
||||||
and: [
|
or: [
|
||||||
{
|
{
|
||||||
'user.value': { in: ids },
|
and: [
|
||||||
|
{
|
||||||
|
'user.value': { in: ids },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'user.relationTo': { equals: collectionConfig.slug },
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'user.relationTo': { equals: collectionConfig.slug },
|
key: { in: ids.map((id) => `collection-${collectionConfig.slug}-${id}`) },
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
await payload.db.deleteMany({
|
||||||
|
collection: 'payload-preferences',
|
||||||
|
req,
|
||||||
|
where: {
|
||||||
|
key: { in: ids.map((id) => `collection-${collectionConfig.slug}-${id}`) },
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
await payload.db.deleteMany({
|
|
||||||
collection: 'payload-preferences',
|
|
||||||
req,
|
|
||||||
where: {
|
|
||||||
key: { in: ids.map((id) => `collection-${collectionConfig.slug}-${id}`) },
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,3 +15,7 @@ export const validOperators = [
|
|||||||
'intersects',
|
'intersects',
|
||||||
'near',
|
'near',
|
||||||
] as const
|
] as const
|
||||||
|
|
||||||
|
export type Operator = (typeof validOperators)[number]
|
||||||
|
|
||||||
|
export const validOperatorSet = new Set<Operator>(validOperators)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import type {
|
|||||||
TypedLocale,
|
TypedLocale,
|
||||||
TypedUser,
|
TypedUser,
|
||||||
} from '../index.js'
|
} from '../index.js'
|
||||||
import type { validOperators } from './constants.js'
|
import type { Operator } from './constants.js'
|
||||||
export type { Payload as Payload } from '../index.js'
|
export type { Payload as Payload } from '../index.js'
|
||||||
|
|
||||||
export type CustomPayloadRequestProperties = {
|
export type CustomPayloadRequestProperties = {
|
||||||
@@ -101,7 +101,7 @@ export type PayloadRequest = CustomPayloadRequestProperties &
|
|||||||
PayloadRequestData &
|
PayloadRequestData &
|
||||||
Required<Pick<Request, 'headers'>>
|
Required<Pick<Request, 'headers'>>
|
||||||
|
|
||||||
export type Operator = (typeof validOperators)[number]
|
export type { Operator }
|
||||||
|
|
||||||
// Makes it so things like passing new Date() will error
|
// Makes it so things like passing new Date() will error
|
||||||
export type JsonValue = JsonArray | JsonObject | unknown //Date | JsonArray | JsonObject | boolean | null | number | string // TODO: Evaluate proper, strong type for this
|
export type JsonValue = JsonArray | JsonObject | unknown //Date | JsonArray | JsonObject | boolean | null | number | string // TODO: Evaluate proper, strong type for this
|
||||||
|
|||||||
@@ -1,22 +1,17 @@
|
|||||||
const internalFields = ['__v']
|
const sanitizeInternalFields = <T extends Record<string, unknown>>(incomingDoc: T): T => {
|
||||||
|
// Create a new object to hold the sanitized fields
|
||||||
|
const newDoc: Record<string, unknown> = {}
|
||||||
|
|
||||||
const sanitizeInternalFields = <T extends Record<string, unknown>>(incomingDoc: T): T =>
|
for (const key in incomingDoc) {
|
||||||
Object.entries(incomingDoc).reduce((newDoc, [key, val]): T => {
|
const val = incomingDoc[key]
|
||||||
if (key === '_id') {
|
if (key === '_id') {
|
||||||
return {
|
newDoc['id'] = val
|
||||||
...newDoc,
|
} else if (key !== '__v') {
|
||||||
id: val,
|
newDoc[key] = val
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (internalFields.indexOf(key) > -1) {
|
return newDoc as T
|
||||||
return newDoc
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...newDoc,
|
|
||||||
[key]: val,
|
|
||||||
}
|
|
||||||
}, {} as T)
|
|
||||||
|
|
||||||
export default sanitizeInternalFields
|
export default sanitizeInternalFields
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type { Operator, Where } from 'payload'
|
import type { Operator, Where } from 'payload'
|
||||||
|
|
||||||
import { validOperators } from 'payload/shared'
|
import { validOperatorSet } from 'payload/shared'
|
||||||
|
|
||||||
const validateWhereQuery = (whereQuery): whereQuery is Where => {
|
const validateWhereQuery = (whereQuery): whereQuery is Where => {
|
||||||
if (
|
if (
|
||||||
@@ -27,7 +27,7 @@ const validateWhereQuery = (whereQuery): whereQuery is Where => {
|
|||||||
for (const key of andKeys) {
|
for (const key of andKeys) {
|
||||||
const operator = Object.keys(andQuery[key])[0]
|
const operator = Object.keys(andQuery[key])[0]
|
||||||
// Check if the key is a valid Operator.
|
// Check if the key is a valid Operator.
|
||||||
if (!operator || !validOperators.includes(operator as Operator)) {
|
if (!operator || !validOperatorSet.has(operator as Operator)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user