chore(db-mongodb): tsconfig uses strict: true and noUncheckedIndexedAccess: true (#11444)
Migrates the `db-mongodb` package to use `strict: true` and `noUncheckedIndexedAccess: true` TSConfig properties. This greatly improves code quality and prevents some runtime errors or gives better error messages.
This commit is contained in:
@@ -53,6 +53,9 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@types/mongoose-aggregate-paginate-v2": "1.0.12",
|
||||
"@types/prompts": "^2.4.5",
|
||||
"@types/uuid": "10.0.0",
|
||||
"mongodb": "6.12.0",
|
||||
"mongodb-memory-server": "^10",
|
||||
"payload": "workspace:*"
|
||||
|
||||
@@ -70,9 +70,15 @@ export const connect: Connect = async function connect(
|
||||
await this.migrate({ migrations: this.prodMigrations })
|
||||
}
|
||||
} catch (err) {
|
||||
let msg = `Error: cannot connect to MongoDB.`
|
||||
|
||||
if (typeof err === 'object' && err && 'message' in err && typeof err.message === 'string') {
|
||||
msg = `${msg} Details: ${err.message}`
|
||||
}
|
||||
|
||||
this.payload.logger.error({
|
||||
err,
|
||||
msg: `Error: cannot connect to MongoDB. Details: ${err.message}`,
|
||||
msg,
|
||||
})
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
@@ -6,13 +6,15 @@ import { flattenWhereToOperators } from 'payload'
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { buildQuery } from './queries/buildQuery.js'
|
||||
import { getCollection } from './utilities/getEntity.js'
|
||||
import { getSession } from './utilities/getSession.js'
|
||||
|
||||
export const count: Count = async function count(
|
||||
this: MongooseAdapter,
|
||||
{ collection, locale, req, where },
|
||||
{ collection: collectionSlug, locale, req, where = {} },
|
||||
) {
|
||||
const Model = this.collections[collection]
|
||||
const { collectionConfig, Model } = getCollection({ adapter: this, collectionSlug })
|
||||
|
||||
const options: CountOptions = {
|
||||
session: await getSession(this, req),
|
||||
}
|
||||
@@ -26,8 +28,8 @@ export const count: Count = async function count(
|
||||
|
||||
const query = await buildQuery({
|
||||
adapter: this,
|
||||
collectionSlug: collection,
|
||||
fields: this.payload.collections[collection].config.flattenedFields,
|
||||
collectionSlug,
|
||||
fields: collectionConfig.flattenedFields,
|
||||
locale,
|
||||
where,
|
||||
})
|
||||
|
||||
@@ -6,13 +6,15 @@ import { buildVersionGlobalFields, flattenWhereToOperators } from 'payload'
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { buildQuery } from './queries/buildQuery.js'
|
||||
import { getGlobal } from './utilities/getEntity.js'
|
||||
import { getSession } from './utilities/getSession.js'
|
||||
|
||||
export const countGlobalVersions: CountGlobalVersions = async function countGlobalVersions(
|
||||
this: MongooseAdapter,
|
||||
{ global, locale, req, where },
|
||||
{ global: globalSlug, locale, req, where = {} },
|
||||
) {
|
||||
const Model = this.versions[global]
|
||||
const { globalConfig, Model } = getGlobal({ adapter: this, globalSlug, versions: true })
|
||||
|
||||
const options: CountOptions = {
|
||||
session: await getSession(this, req),
|
||||
}
|
||||
@@ -26,11 +28,7 @@ export const countGlobalVersions: CountGlobalVersions = async function countGlob
|
||||
|
||||
const query = await buildQuery({
|
||||
adapter: this,
|
||||
fields: buildVersionGlobalFields(
|
||||
this.payload.config,
|
||||
this.payload.globals.config.find((each) => each.slug === global),
|
||||
true,
|
||||
),
|
||||
fields: buildVersionGlobalFields(this.payload.config, globalConfig, true),
|
||||
locale,
|
||||
where,
|
||||
})
|
||||
|
||||
@@ -6,13 +6,19 @@ import { buildVersionCollectionFields, flattenWhereToOperators } from 'payload'
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { buildQuery } from './queries/buildQuery.js'
|
||||
import { getCollection } from './utilities/getEntity.js'
|
||||
import { getSession } from './utilities/getSession.js'
|
||||
|
||||
export const countVersions: CountVersions = async function countVersions(
|
||||
this: MongooseAdapter,
|
||||
{ collection, locale, req, where },
|
||||
{ collection: collectionSlug, locale, req, where = {} },
|
||||
) {
|
||||
const Model = this.versions[collection]
|
||||
const { collectionConfig, Model } = getCollection({
|
||||
adapter: this,
|
||||
collectionSlug,
|
||||
versions: true,
|
||||
})
|
||||
|
||||
const options: CountOptions = {
|
||||
session: await getSession(this, req),
|
||||
}
|
||||
@@ -26,11 +32,7 @@ export const countVersions: CountVersions = async function countVersions(
|
||||
|
||||
const query = await buildQuery({
|
||||
adapter: this,
|
||||
fields: buildVersionCollectionFields(
|
||||
this.payload.config,
|
||||
this.payload.collections[collection].config,
|
||||
true,
|
||||
),
|
||||
fields: buildVersionCollectionFields(this.payload.config, collectionConfig, true),
|
||||
locale,
|
||||
where,
|
||||
})
|
||||
|
||||
@@ -3,15 +3,17 @@ import type { Create } from 'payload'
|
||||
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { getCollection } from './utilities/getEntity.js'
|
||||
import { getSession } from './utilities/getSession.js'
|
||||
import { handleError } from './utilities/handleError.js'
|
||||
import { transform } from './utilities/transform.js'
|
||||
|
||||
export const create: Create = async function create(
|
||||
this: MongooseAdapter,
|
||||
{ collection, data, req, returning },
|
||||
{ collection: collectionSlug, data, req, returning },
|
||||
) {
|
||||
const Model = this.collections[collection]
|
||||
const { collectionConfig, customIDType, Model } = getCollection({ adapter: this, collectionSlug })
|
||||
|
||||
const options: CreateOptions = {
|
||||
session: await getSession(this, req),
|
||||
}
|
||||
@@ -21,18 +23,18 @@ export const create: Create = async function create(
|
||||
transform({
|
||||
adapter: this,
|
||||
data,
|
||||
fields: this.payload.collections[collection].config.fields,
|
||||
fields: collectionConfig.fields,
|
||||
operation: 'write',
|
||||
})
|
||||
|
||||
if (this.payload.collections[collection].customIDType) {
|
||||
if (customIDType) {
|
||||
data._id = data.id
|
||||
}
|
||||
|
||||
try {
|
||||
;[doc] = await Model.create([data], options)
|
||||
} catch (error) {
|
||||
handleError({ collection, error, req })
|
||||
handleError({ collection: collectionSlug, error, req })
|
||||
}
|
||||
if (returning === false) {
|
||||
return null
|
||||
@@ -43,7 +45,7 @@ export const create: Create = async function create(
|
||||
transform({
|
||||
adapter: this,
|
||||
data: doc,
|
||||
fields: this.payload.collections[collection].config.fields,
|
||||
fields: collectionConfig.fields,
|
||||
operation: 'read',
|
||||
})
|
||||
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
import type { CreateOptions } from 'mongoose'
|
||||
import type { CreateGlobal } from 'payload'
|
||||
|
||||
import { type CreateGlobal } from 'payload'
|
||||
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { getGlobal } from './utilities/getEntity.js'
|
||||
import { getSession } from './utilities/getSession.js'
|
||||
import { transform } from './utilities/transform.js'
|
||||
|
||||
export const createGlobal: CreateGlobal = async function createGlobal(
|
||||
this: MongooseAdapter,
|
||||
{ slug, data, req, returning },
|
||||
{ slug: globalSlug, data, req, returning },
|
||||
) {
|
||||
const Model = this.globals
|
||||
const { globalConfig, Model } = getGlobal({ adapter: this, globalSlug })
|
||||
|
||||
transform({
|
||||
adapter: this,
|
||||
data,
|
||||
fields: this.payload.config.globals.find((globalConfig) => globalConfig.slug === slug).fields,
|
||||
globalSlug: slug,
|
||||
fields: globalConfig.fields,
|
||||
globalSlug,
|
||||
operation: 'write',
|
||||
})
|
||||
|
||||
@@ -34,7 +36,7 @@ export const createGlobal: CreateGlobal = async function createGlobal(
|
||||
transform({
|
||||
adapter: this,
|
||||
data: result,
|
||||
fields: this.payload.config.globals.find((globalConfig) => globalConfig.slug === slug).fields,
|
||||
fields: globalConfig.fields,
|
||||
operation: 'read',
|
||||
})
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import type { CreateOptions } from 'mongoose'
|
||||
|
||||
import { buildVersionGlobalFields, type CreateGlobalVersion } from 'payload'
|
||||
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { getGlobal } from './utilities/getEntity.js'
|
||||
import { getSession } from './utilities/getSession.js'
|
||||
import { transform } from './utilities/transform.js'
|
||||
|
||||
@@ -22,8 +21,9 @@ export const createGlobalVersion: CreateGlobalVersion = async function createGlo
|
||||
versionData,
|
||||
},
|
||||
) {
|
||||
const VersionModel = this.versions[globalSlug]
|
||||
const options: CreateOptions = {
|
||||
const { globalConfig, Model } = getGlobal({ adapter: this, globalSlug, versions: true })
|
||||
|
||||
const options = {
|
||||
session: await getSession(this, req),
|
||||
}
|
||||
|
||||
@@ -38,10 +38,7 @@ export const createGlobalVersion: CreateGlobalVersion = async function createGlo
|
||||
version: versionData,
|
||||
}
|
||||
|
||||
const fields = buildVersionGlobalFields(
|
||||
this.payload.config,
|
||||
this.payload.config.globals.find((global) => global.slug === globalSlug),
|
||||
)
|
||||
const fields = buildVersionGlobalFields(this.payload.config, globalConfig)
|
||||
|
||||
transform({
|
||||
adapter: this,
|
||||
@@ -50,9 +47,9 @@ export const createGlobalVersion: CreateGlobalVersion = async function createGlo
|
||||
operation: 'write',
|
||||
})
|
||||
|
||||
let [doc] = await VersionModel.create([data], options, req)
|
||||
let [doc] = await Model.create([data], options, req)
|
||||
|
||||
await VersionModel.updateMany(
|
||||
await Model.updateMany(
|
||||
{
|
||||
$and: [
|
||||
{
|
||||
|
||||
@@ -42,8 +42,9 @@ export const createMigration: CreateMigration = async function createMigration({
|
||||
const migrationFileContent = migrationTemplate(predefinedMigration)
|
||||
|
||||
const [yyymmdd, hhmmss] = new Date().toISOString().split('T')
|
||||
const formattedDate = yyymmdd.replace(/\D/g, '')
|
||||
const formattedTime = hhmmss.split('.')[0].replace(/\D/g, '')
|
||||
|
||||
const formattedDate = yyymmdd!.replace(/\D/g, '')
|
||||
const formattedTime = hhmmss!.split('.')[0]!.replace(/\D/g, '')
|
||||
|
||||
const timestamp = `${formattedDate}_${formattedTime}`
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import type { CreateOptions } from 'mongoose'
|
||||
|
||||
import { buildVersionCollectionFields, type CreateVersion } from 'payload'
|
||||
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { getCollection } from './utilities/getEntity.js'
|
||||
import { getSession } from './utilities/getSession.js'
|
||||
import { transform } from './utilities/transform.js'
|
||||
|
||||
@@ -22,8 +21,13 @@ export const createVersion: CreateVersion = async function createVersion(
|
||||
versionData,
|
||||
},
|
||||
) {
|
||||
const VersionModel = this.versions[collectionSlug]
|
||||
const options: CreateOptions = {
|
||||
const { collectionConfig, Model } = getCollection({
|
||||
adapter: this,
|
||||
collectionSlug,
|
||||
versions: true,
|
||||
})
|
||||
|
||||
const options = {
|
||||
session: await getSession(this, req),
|
||||
}
|
||||
|
||||
@@ -38,10 +42,7 @@ export const createVersion: CreateVersion = async function createVersion(
|
||||
version: versionData,
|
||||
}
|
||||
|
||||
const fields = buildVersionCollectionFields(
|
||||
this.payload.config,
|
||||
this.payload.collections[collectionSlug].config,
|
||||
)
|
||||
const fields = buildVersionCollectionFields(this.payload.config, collectionConfig)
|
||||
|
||||
transform({
|
||||
adapter: this,
|
||||
@@ -50,7 +51,7 @@ export const createVersion: CreateVersion = async function createVersion(
|
||||
operation: 'write',
|
||||
})
|
||||
|
||||
let [doc] = await VersionModel.create([data], options, req)
|
||||
let [doc] = await Model.create([data], options, req)
|
||||
|
||||
const parentQuery = {
|
||||
$or: [
|
||||
@@ -62,7 +63,7 @@ export const createVersion: CreateVersion = async function createVersion(
|
||||
],
|
||||
}
|
||||
|
||||
await VersionModel.updateMany(
|
||||
await Model.updateMany(
|
||||
{
|
||||
$and: [
|
||||
{
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
import type { DeleteOptions } from 'mongodb'
|
||||
import type { DeleteMany } from 'payload'
|
||||
|
||||
import { type DeleteMany } from 'payload'
|
||||
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { buildQuery } from './queries/buildQuery.js'
|
||||
import { getCollection } from './utilities/getEntity.js'
|
||||
import { getSession } from './utilities/getSession.js'
|
||||
|
||||
export const deleteMany: DeleteMany = async function deleteMany(
|
||||
this: MongooseAdapter,
|
||||
{ collection, req, where },
|
||||
{ collection: collectionSlug, req, where },
|
||||
) {
|
||||
const Model = this.collections[collection]
|
||||
const { collectionConfig, Model } = getCollection({ adapter: this, collectionSlug })
|
||||
|
||||
const options: DeleteOptions = {
|
||||
session: await getSession(this, req),
|
||||
}
|
||||
|
||||
const query = await buildQuery({
|
||||
adapter: this,
|
||||
collectionSlug: collection,
|
||||
fields: this.payload.collections[collection].config.flattenedFields,
|
||||
collectionSlug,
|
||||
fields: collectionConfig.flattenedFields,
|
||||
where,
|
||||
})
|
||||
|
||||
|
||||
@@ -5,18 +5,20 @@ import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { buildQuery } from './queries/buildQuery.js'
|
||||
import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js'
|
||||
import { getCollection } from './utilities/getEntity.js'
|
||||
import { getSession } from './utilities/getSession.js'
|
||||
import { transform } from './utilities/transform.js'
|
||||
|
||||
export const deleteOne: DeleteOne = async function deleteOne(
|
||||
this: MongooseAdapter,
|
||||
{ collection, req, returning, select, where },
|
||||
{ collection: collectionSlug, req, returning, select, where },
|
||||
) {
|
||||
const Model = this.collections[collection]
|
||||
const { collectionConfig, Model } = getCollection({ adapter: this, collectionSlug })
|
||||
|
||||
const options: MongooseUpdateQueryOptions = {
|
||||
projection: buildProjectionFromSelect({
|
||||
adapter: this,
|
||||
fields: this.payload.collections[collection].config.flattenedFields,
|
||||
fields: collectionConfig.flattenedFields,
|
||||
select,
|
||||
}),
|
||||
session: await getSession(this, req),
|
||||
@@ -24,8 +26,8 @@ export const deleteOne: DeleteOne = async function deleteOne(
|
||||
|
||||
const query = await buildQuery({
|
||||
adapter: this,
|
||||
collectionSlug: collection,
|
||||
fields: this.payload.collections[collection].config.flattenedFields,
|
||||
collectionSlug,
|
||||
fields: collectionConfig.flattenedFields,
|
||||
where,
|
||||
})
|
||||
|
||||
@@ -43,7 +45,7 @@ export const deleteOne: DeleteOne = async function deleteOne(
|
||||
transform({
|
||||
adapter: this,
|
||||
data: doc,
|
||||
fields: this.payload.collections[collection].config.fields,
|
||||
fields: collectionConfig.fields,
|
||||
operation: 'read',
|
||||
})
|
||||
|
||||
|
||||
@@ -3,26 +3,27 @@ import { buildVersionCollectionFields, type DeleteVersions } from 'payload'
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { buildQuery } from './queries/buildQuery.js'
|
||||
import { getCollection } from './utilities/getEntity.js'
|
||||
import { getSession } from './utilities/getSession.js'
|
||||
|
||||
export const deleteVersions: DeleteVersions = async function deleteVersions(
|
||||
this: MongooseAdapter,
|
||||
{ collection, locale, req, where },
|
||||
{ collection: collectionSlug, locale, req, where },
|
||||
) {
|
||||
const VersionsModel = this.versions[collection]
|
||||
const { collectionConfig, Model } = getCollection({
|
||||
adapter: this,
|
||||
collectionSlug,
|
||||
versions: true,
|
||||
})
|
||||
|
||||
const session = await getSession(this, req)
|
||||
|
||||
const query = await buildQuery({
|
||||
adapter: this,
|
||||
fields: buildVersionCollectionFields(
|
||||
this.payload.config,
|
||||
this.payload.collections[collection].config,
|
||||
true,
|
||||
),
|
||||
fields: buildVersionCollectionFields(this.payload.config, collectionConfig, true),
|
||||
locale,
|
||||
where,
|
||||
})
|
||||
|
||||
await VersionsModel.deleteMany(query, { session })
|
||||
await Model.deleteMany(query, { session })
|
||||
}
|
||||
|
||||
@@ -10,13 +10,14 @@ import { buildSortParam } from './queries/buildSortParam.js'
|
||||
import { aggregatePaginate } from './utilities/aggregatePaginate.js'
|
||||
import { buildJoinAggregation } from './utilities/buildJoinAggregation.js'
|
||||
import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js'
|
||||
import { getCollection } from './utilities/getEntity.js'
|
||||
import { getSession } from './utilities/getSession.js'
|
||||
import { transform } from './utilities/transform.js'
|
||||
|
||||
export const find: Find = async function find(
|
||||
this: MongooseAdapter,
|
||||
{
|
||||
collection,
|
||||
collection: collectionSlug,
|
||||
joins = {},
|
||||
limit = 0,
|
||||
locale,
|
||||
@@ -26,11 +27,10 @@ export const find: Find = async function find(
|
||||
req,
|
||||
select,
|
||||
sort: sortArg,
|
||||
where,
|
||||
where = {},
|
||||
},
|
||||
) {
|
||||
const Model = this.collections[collection]
|
||||
const collectionConfig = this.payload.collections[collection].config
|
||||
const { collectionConfig, Model } = getCollection({ adapter: this, collectionSlug })
|
||||
|
||||
const session = await getSession(this, req)
|
||||
|
||||
@@ -54,8 +54,8 @@ export const find: Find = async function find(
|
||||
|
||||
const query = await buildQuery({
|
||||
adapter: this,
|
||||
collectionSlug: collection,
|
||||
fields: this.payload.collections[collection].config.flattenedFields,
|
||||
collectionSlug,
|
||||
fields: collectionConfig.flattenedFields,
|
||||
locale,
|
||||
where,
|
||||
})
|
||||
@@ -109,7 +109,8 @@ export const find: Find = async function find(
|
||||
if (limit >= 0) {
|
||||
paginationOptions.limit = limit
|
||||
// limit must also be set here, it's ignored when pagination is false
|
||||
paginationOptions.options.limit = limit
|
||||
|
||||
paginationOptions.options!.limit = limit
|
||||
|
||||
// Disable pagination if limit is 0
|
||||
if (limit === 0) {
|
||||
@@ -121,7 +122,7 @@ export const find: Find = async function find(
|
||||
|
||||
const aggregate = await buildJoinAggregation({
|
||||
adapter: this,
|
||||
collection,
|
||||
collection: collectionSlug,
|
||||
collectionConfig,
|
||||
joins,
|
||||
locale,
|
||||
@@ -139,7 +140,7 @@ export const find: Find = async function find(
|
||||
pagination: paginationOptions.pagination,
|
||||
projection: paginationOptions.projection,
|
||||
query,
|
||||
session: paginationOptions.options?.session,
|
||||
session: paginationOptions.options?.session ?? undefined,
|
||||
sort: paginationOptions.sort as object,
|
||||
useEstimatedCount: paginationOptions.useEstimatedCount,
|
||||
})
|
||||
@@ -150,7 +151,7 @@ export const find: Find = async function find(
|
||||
transform({
|
||||
adapter: this,
|
||||
data: result.docs,
|
||||
fields: this.payload.collections[collection].config.fields,
|
||||
fields: collectionConfig.fields,
|
||||
operation: 'read',
|
||||
})
|
||||
|
||||
|
||||
@@ -7,15 +7,16 @@ import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { buildQuery } from './queries/buildQuery.js'
|
||||
import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js'
|
||||
import { getGlobal } from './utilities/getEntity.js'
|
||||
import { getSession } from './utilities/getSession.js'
|
||||
import { transform } from './utilities/transform.js'
|
||||
|
||||
export const findGlobal: FindGlobal = async function findGlobal(
|
||||
this: MongooseAdapter,
|
||||
{ slug, locale, req, select, where },
|
||||
{ slug: globalSlug, locale, req, select, where = {} },
|
||||
) {
|
||||
const Model = this.globals
|
||||
const globalConfig = this.payload.globals.config.find((each) => each.slug === slug)
|
||||
const { globalConfig, Model } = getGlobal({ adapter: this, globalSlug })
|
||||
|
||||
const fields = globalConfig.flattenedFields
|
||||
const options: QueryOptions = {
|
||||
lean: true,
|
||||
@@ -30,12 +31,12 @@ export const findGlobal: FindGlobal = async function findGlobal(
|
||||
const query = await buildQuery({
|
||||
adapter: this,
|
||||
fields,
|
||||
globalSlug: slug,
|
||||
globalSlug,
|
||||
locale,
|
||||
where: combineQueries({ globalType: { equals: slug } }, where),
|
||||
where: combineQueries({ globalType: { equals: globalSlug } }, where),
|
||||
})
|
||||
|
||||
const doc = (await Model.findOne(query, {}, options)) as any
|
||||
const doc: any = await Model.findOne(query, {}, options)
|
||||
|
||||
if (!doc) {
|
||||
return null
|
||||
|
||||
@@ -1,22 +1,34 @@
|
||||
import type { PaginateOptions, QueryOptions } from 'mongoose'
|
||||
import type { FindGlobalVersions } from 'payload'
|
||||
|
||||
import { buildVersionGlobalFields, flattenWhereToOperators } from 'payload'
|
||||
import { APIError, buildVersionGlobalFields, flattenWhereToOperators } from 'payload'
|
||||
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { buildQuery } from './queries/buildQuery.js'
|
||||
import { buildSortParam } from './queries/buildSortParam.js'
|
||||
import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js'
|
||||
import { getGlobal } from './utilities/getEntity.js'
|
||||
import { getSession } from './utilities/getSession.js'
|
||||
import { transform } from './utilities/transform.js'
|
||||
|
||||
export const findGlobalVersions: FindGlobalVersions = async function findGlobalVersions(
|
||||
this: MongooseAdapter,
|
||||
{ global, limit, locale, page, pagination, req, select, skip, sort: sortArg, where },
|
||||
{
|
||||
global: globalSlug,
|
||||
limit,
|
||||
locale,
|
||||
page,
|
||||
pagination,
|
||||
req,
|
||||
select,
|
||||
skip,
|
||||
sort: sortArg,
|
||||
where = {},
|
||||
},
|
||||
) {
|
||||
const globalConfig = this.payload.globals.config.find(({ slug }) => slug === global)
|
||||
const Model = this.versions[global]
|
||||
const { globalConfig, Model } = getGlobal({ adapter: this, globalSlug, versions: true })
|
||||
|
||||
const versionFields = buildVersionGlobalFields(this.payload.config, globalConfig, true)
|
||||
|
||||
const session = await getSession(this, req)
|
||||
@@ -88,10 +100,11 @@ export const findGlobalVersions: FindGlobalVersions = async function findGlobalV
|
||||
}
|
||||
}
|
||||
|
||||
if (limit >= 0) {
|
||||
if (limit && limit >= 0) {
|
||||
paginationOptions.limit = limit
|
||||
// limit must also be set here, it's ignored when pagination is false
|
||||
paginationOptions.options.limit = limit
|
||||
|
||||
paginationOptions.options!.limit = limit
|
||||
|
||||
// Disable pagination if limit is 0
|
||||
if (limit === 0) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { AggregateOptions, QueryOptions } from 'mongoose'
|
||||
import type { FindOne } from 'payload'
|
||||
|
||||
import { type FindOne } from 'payload'
|
||||
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
@@ -7,15 +8,16 @@ import { buildQuery } from './queries/buildQuery.js'
|
||||
import { aggregatePaginate } from './utilities/aggregatePaginate.js'
|
||||
import { buildJoinAggregation } from './utilities/buildJoinAggregation.js'
|
||||
import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js'
|
||||
import { getCollection } from './utilities/getEntity.js'
|
||||
import { getSession } from './utilities/getSession.js'
|
||||
import { transform } from './utilities/transform.js'
|
||||
|
||||
export const findOne: FindOne = async function findOne(
|
||||
this: MongooseAdapter,
|
||||
{ collection, joins, locale, req, select, where },
|
||||
{ collection: collectionSlug, joins, locale, req, select, where = {} },
|
||||
) {
|
||||
const Model = this.collections[collection]
|
||||
const collectionConfig = this.payload.collections[collection].config
|
||||
const { collectionConfig, Model } = getCollection({ adapter: this, collectionSlug })
|
||||
|
||||
const session = await getSession(this, req)
|
||||
const options: AggregateOptions & QueryOptions = {
|
||||
lean: true,
|
||||
@@ -24,7 +26,7 @@ export const findOne: FindOne = async function findOne(
|
||||
|
||||
const query = await buildQuery({
|
||||
adapter: this,
|
||||
collectionSlug: collection,
|
||||
collectionSlug,
|
||||
fields: collectionConfig.flattenedFields,
|
||||
locale,
|
||||
where,
|
||||
@@ -38,7 +40,7 @@ export const findOne: FindOne = async function findOne(
|
||||
|
||||
const aggregate = await buildJoinAggregation({
|
||||
adapter: this,
|
||||
collection,
|
||||
collection: collectionSlug,
|
||||
collectionConfig,
|
||||
joins,
|
||||
locale,
|
||||
|
||||
@@ -8,15 +8,31 @@ import type { MongooseAdapter } from './index.js'
|
||||
import { buildQuery } from './queries/buildQuery.js'
|
||||
import { buildSortParam } from './queries/buildSortParam.js'
|
||||
import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js'
|
||||
import { getCollection } from './utilities/getEntity.js'
|
||||
import { getSession } from './utilities/getSession.js'
|
||||
import { transform } from './utilities/transform.js'
|
||||
|
||||
export const findVersions: FindVersions = async function findVersions(
|
||||
this: MongooseAdapter,
|
||||
{ collection, limit, locale, page, pagination, req = {}, select, skip, sort: sortArg, where },
|
||||
{
|
||||
collection: collectionSlug,
|
||||
limit,
|
||||
locale,
|
||||
page,
|
||||
pagination,
|
||||
req = {},
|
||||
select,
|
||||
skip,
|
||||
sort: sortArg,
|
||||
where = {},
|
||||
},
|
||||
) {
|
||||
const Model = this.versions[collection]
|
||||
const collectionConfig = this.payload.collections[collection].config
|
||||
const { collectionConfig, Model } = getCollection({
|
||||
adapter: this,
|
||||
collectionSlug,
|
||||
versions: true,
|
||||
})
|
||||
|
||||
const session = await getSession(this, req)
|
||||
const options: QueryOptions = {
|
||||
limit,
|
||||
@@ -92,10 +108,11 @@ export const findVersions: FindVersions = async function findVersions(
|
||||
}
|
||||
}
|
||||
|
||||
if (limit >= 0) {
|
||||
if (limit && limit >= 0) {
|
||||
paginationOptions.limit = limit
|
||||
// limit must also be set here, it's ignored when pagination is false
|
||||
paginationOptions.options.limit = limit
|
||||
|
||||
paginationOptions.options!.limit = limit
|
||||
|
||||
// Disable pagination if limit is 0
|
||||
if (limit === 0) {
|
||||
|
||||
@@ -11,6 +11,7 @@ import type {
|
||||
BaseDatabaseAdapter,
|
||||
CollectionSlug,
|
||||
DatabaseAdapterObj,
|
||||
Migration,
|
||||
Payload,
|
||||
TypeWithID,
|
||||
TypeWithVersion,
|
||||
@@ -110,11 +111,7 @@ export interface Args {
|
||||
* typed as any to avoid dependency
|
||||
*/
|
||||
mongoMemoryServer?: MongoMemoryReplSet
|
||||
prodMigrations?: {
|
||||
down: (args: MigrateDownArgs) => Promise<void>
|
||||
name: string
|
||||
up: (args: MigrateUpArgs) => Promise<void>
|
||||
}[]
|
||||
prodMigrations?: Migration[]
|
||||
transactionOptions?: false | TransactionOptions
|
||||
|
||||
/** The URL to connect to MongoDB or false to start payload and prevent connecting */
|
||||
@@ -181,7 +178,7 @@ export function mongooseAdapter({
|
||||
collectionsSchemaOptions = {},
|
||||
connectOptions,
|
||||
disableIndexHints = false,
|
||||
ensureIndexes,
|
||||
ensureIndexes = false,
|
||||
migrationDir: migrationDirArg,
|
||||
mongoMemoryServer,
|
||||
prodMigrations,
|
||||
@@ -198,11 +195,14 @@ export function mongooseAdapter({
|
||||
// Mongoose-specific
|
||||
autoPluralization,
|
||||
collections: {},
|
||||
// @ts-expect-error initialize without a connection
|
||||
connection: undefined,
|
||||
connectOptions: connectOptions || {},
|
||||
disableIndexHints,
|
||||
ensureIndexes,
|
||||
// @ts-expect-error don't have globals model yet
|
||||
globals: undefined,
|
||||
// @ts-expect-error Should not be required
|
||||
mongoMemoryServer,
|
||||
sessions: {},
|
||||
transactionOptions: transactionOptions === false ? undefined : transactionOptions,
|
||||
|
||||
@@ -6,7 +6,7 @@ import paginate from 'mongoose-paginate-v2'
|
||||
import { buildVersionCollectionFields, buildVersionGlobalFields } from 'payload'
|
||||
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
import type { CollectionModel } from './types.js'
|
||||
import type { CollectionModel, GlobalModel } from './types.js'
|
||||
|
||||
import { buildCollectionSchema } from './models/buildCollectionSchema.js'
|
||||
import { buildGlobalModel } from './models/buildGlobalModel.js'
|
||||
@@ -16,7 +16,7 @@ import { getDBName } from './utilities/getDBName.js'
|
||||
|
||||
export const init: Init = function init(this: MongooseAdapter) {
|
||||
this.payload.config.collections.forEach((collection: SanitizedCollectionConfig) => {
|
||||
const schemaOptions = this.collectionsSchemaOptions[collection.slug]
|
||||
const schemaOptions = this.collectionsSchemaOptions?.[collection.slug]
|
||||
|
||||
const schema = buildCollectionSchema(collection, this.payload, schemaOptions)
|
||||
|
||||
@@ -68,7 +68,7 @@ export const init: Init = function init(this: MongooseAdapter) {
|
||||
) as CollectionModel
|
||||
})
|
||||
|
||||
this.globals = buildGlobalModel(this.payload)
|
||||
this.globals = buildGlobalModel(this.payload) as GlobalModel
|
||||
|
||||
this.payload.config.globals.forEach((global) => {
|
||||
if (global.versions) {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import type { PayloadRequest } from 'payload'
|
||||
|
||||
import { commitTransaction, initTransaction, killTransaction, readMigrationFiles } from 'payload'
|
||||
import prompts from 'prompts'
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ export const buildCollectionSchema = (
|
||||
})
|
||||
|
||||
if (Array.isArray(collection.upload.filenameCompoundIndex)) {
|
||||
const indexDefinition: Record<string, 1> = collection.upload.filenameCompoundIndex.reduce(
|
||||
const indexDefinition = collection.upload.filenameCompoundIndex.reduce<Record<string, 1>>(
|
||||
(acc, index) => {
|
||||
acc[index] = 1
|
||||
return acc
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +1,16 @@
|
||||
import type { ClientSession, Model } from 'mongoose'
|
||||
import type { Field, PayloadRequest, SanitizedConfig } from 'payload'
|
||||
import type { Field, PayloadRequest } from 'payload'
|
||||
|
||||
import { buildVersionCollectionFields, buildVersionGlobalFields } from 'payload'
|
||||
|
||||
import type { MongooseAdapter } from '../index.js'
|
||||
|
||||
import { getCollection, getGlobal } from '../utilities/getEntity.js'
|
||||
import { getSession } from '../utilities/getSession.js'
|
||||
import { transform } from '../utilities/transform.js'
|
||||
|
||||
const migrateModelWithBatching = async ({
|
||||
batchSize,
|
||||
config,
|
||||
db,
|
||||
fields,
|
||||
Model,
|
||||
@@ -18,12 +18,11 @@ const migrateModelWithBatching = async ({
|
||||
session,
|
||||
}: {
|
||||
batchSize: number
|
||||
config: SanitizedConfig
|
||||
db: MongooseAdapter
|
||||
fields: Field[]
|
||||
Model: Model<any>
|
||||
parentIsLocalized: boolean
|
||||
session: ClientSession
|
||||
session?: ClientSession
|
||||
}): Promise<void> => {
|
||||
let hasNext = true
|
||||
let skip = 0
|
||||
@@ -55,6 +54,7 @@ const migrateModelWithBatching = async ({
|
||||
}
|
||||
|
||||
await Model.collection.bulkWrite(
|
||||
// @ts-expect-error bulkWrite has a weird type, insertOne, updateMany etc are required here as well.
|
||||
docs.map((doc) => ({
|
||||
updateOne: {
|
||||
filter: { _id: doc._id },
|
||||
@@ -123,12 +123,13 @@ export async function migrateRelationshipsV2_V3({
|
||||
if (hasRelationshipOrUploadField(collection)) {
|
||||
payload.logger.info(`Migrating collection "${collection.slug}"`)
|
||||
|
||||
const { Model } = getCollection({ adapter: db, collectionSlug: collection.slug })
|
||||
|
||||
await migrateModelWithBatching({
|
||||
batchSize,
|
||||
config,
|
||||
db,
|
||||
fields: collection.fields,
|
||||
Model: db.collections[collection.slug],
|
||||
Model,
|
||||
parentIsLocalized: false,
|
||||
session,
|
||||
})
|
||||
@@ -139,12 +140,17 @@ export async function migrateRelationshipsV2_V3({
|
||||
if (collection.versions) {
|
||||
payload.logger.info(`Migrating collection versions "${collection.slug}"`)
|
||||
|
||||
const { Model } = getCollection({
|
||||
adapter: db,
|
||||
collectionSlug: collection.slug,
|
||||
versions: true,
|
||||
})
|
||||
|
||||
await migrateModelWithBatching({
|
||||
batchSize,
|
||||
config,
|
||||
db,
|
||||
fields: buildVersionCollectionFields(config, collection),
|
||||
Model: db.versions[collection.slug],
|
||||
Model,
|
||||
parentIsLocalized: false,
|
||||
session,
|
||||
})
|
||||
@@ -193,12 +199,13 @@ export async function migrateRelationshipsV2_V3({
|
||||
if (global.versions) {
|
||||
payload.logger.info(`Migrating global versions "${global.slug}"`)
|
||||
|
||||
const { Model } = getGlobal({ adapter: db, globalSlug: global.slug, versions: true })
|
||||
|
||||
await migrateModelWithBatching({
|
||||
batchSize,
|
||||
config,
|
||||
db,
|
||||
fields: buildVersionGlobalFields(config, global),
|
||||
Model: db.versions[global.slug],
|
||||
Model,
|
||||
parentIsLocalized: false,
|
||||
session,
|
||||
})
|
||||
|
||||
@@ -3,18 +3,20 @@ import type { Payload, PayloadRequest } from 'payload'
|
||||
|
||||
import type { MongooseAdapter } from '../index.js'
|
||||
|
||||
import { getCollection, getGlobal } from '../utilities/getEntity.js'
|
||||
import { getSession } from '../utilities/getSession.js'
|
||||
|
||||
export async function migrateVersionsV1_V2({ req }: { req: PayloadRequest }) {
|
||||
const { payload } = req
|
||||
|
||||
const session = await getSession(payload.db as MongooseAdapter, req)
|
||||
const adapter = payload.db as MongooseAdapter
|
||||
const session = await getSession(adapter, req)
|
||||
|
||||
// For each collection
|
||||
|
||||
for (const { slug, versions } of payload.config.collections) {
|
||||
if (versions?.drafts) {
|
||||
await migrateCollectionDocs({ slug, payload, session })
|
||||
await migrateCollectionDocs({ slug, adapter, payload, session })
|
||||
|
||||
payload.logger.info(`Migrated the "${slug}" collection.`)
|
||||
}
|
||||
@@ -23,9 +25,13 @@ export async function migrateVersionsV1_V2({ req }: { req: PayloadRequest }) {
|
||||
// For each global
|
||||
for (const { slug, versions } of payload.config.globals) {
|
||||
if (versions) {
|
||||
const VersionsModel = payload.db.versions[slug]
|
||||
const { Model } = getGlobal({
|
||||
adapter,
|
||||
globalSlug: slug,
|
||||
versions: true,
|
||||
})
|
||||
|
||||
await VersionsModel.findOneAndUpdate(
|
||||
await Model.findOneAndUpdate(
|
||||
{},
|
||||
{ latest: true },
|
||||
{
|
||||
@@ -41,17 +47,23 @@ export async function migrateVersionsV1_V2({ req }: { req: PayloadRequest }) {
|
||||
|
||||
async function migrateCollectionDocs({
|
||||
slug,
|
||||
adapter,
|
||||
docsAtATime = 100,
|
||||
payload,
|
||||
session,
|
||||
}: {
|
||||
adapter: MongooseAdapter
|
||||
docsAtATime?: number
|
||||
payload: Payload
|
||||
session: ClientSession
|
||||
session?: ClientSession
|
||||
slug: string
|
||||
}) {
|
||||
const VersionsModel = payload.db.versions[slug]
|
||||
const remainingDocs = await VersionsModel.aggregate(
|
||||
const { Model } = getCollection({
|
||||
adapter,
|
||||
collectionSlug: slug,
|
||||
versions: true,
|
||||
})
|
||||
const remainingDocs = await Model.aggregate(
|
||||
[
|
||||
// Sort so that newest are first
|
||||
{
|
||||
@@ -87,7 +99,7 @@ async function migrateCollectionDocs({
|
||||
).exec()
|
||||
|
||||
if (!remainingDocs || remainingDocs.length === 0) {
|
||||
const newVersions = await VersionsModel.find(
|
||||
const newVersions = await Model.find(
|
||||
{
|
||||
latest: {
|
||||
$eq: true,
|
||||
@@ -108,7 +120,7 @@ async function migrateCollectionDocs({
|
||||
|
||||
const remainingDocIDs = remainingDocs.map((doc) => doc._versionID)
|
||||
|
||||
await VersionsModel.updateMany(
|
||||
await Model.updateMany(
|
||||
{
|
||||
_id: {
|
||||
$in: remainingDocIDs,
|
||||
@@ -122,5 +134,5 @@ async function migrateCollectionDocs({
|
||||
},
|
||||
)
|
||||
|
||||
await migrateCollectionDocs({ slug, payload, session })
|
||||
await migrateCollectionDocs({ slug, adapter, payload, session })
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import type { FilterQuery } from 'mongoose'
|
||||
import type { FlattenedField, Operator, PathToQuery, Payload } from 'payload'
|
||||
|
||||
import { Types } from 'mongoose'
|
||||
import { getLocalizedPaths } from 'payload'
|
||||
import { APIError, getLocalizedPaths } from 'payload'
|
||||
import { validOperatorSet } from 'payload/shared'
|
||||
|
||||
import type { MongooseAdapter } from '../index.js'
|
||||
import type { OperatorMapKey } from './operatorMap.js'
|
||||
|
||||
import { getCollection } from '../utilities/getEntity.js'
|
||||
import { operatorMap } from './operatorMap.js'
|
||||
import { sanitizeQueryValue } from './sanitizeQueryValue.js'
|
||||
|
||||
@@ -43,7 +46,7 @@ export async function buildSearchParam({
|
||||
parentIsLocalized: boolean
|
||||
payload: Payload
|
||||
val: unknown
|
||||
}): Promise<SearchParam> {
|
||||
}): Promise<SearchParam | undefined> {
|
||||
// Replace GraphQL nested field double underscore formatting
|
||||
let sanitizedPath = incomingPath.replace(/__/g, '.')
|
||||
if (sanitizedPath === 'id') {
|
||||
@@ -55,7 +58,9 @@ export async function buildSearchParam({
|
||||
let hasCustomID = false
|
||||
|
||||
if (sanitizedPath === '_id') {
|
||||
const customIDFieldType = payload.collections[collectionSlug]?.customIDType
|
||||
const customIDFieldType = collectionSlug
|
||||
? payload.collections[collectionSlug]?.customIDType
|
||||
: undefined
|
||||
|
||||
let idFieldType: 'number' | 'text' = 'text'
|
||||
|
||||
@@ -71,7 +76,7 @@ export async function buildSearchParam({
|
||||
name: 'id',
|
||||
type: idFieldType,
|
||||
} as FlattenedField,
|
||||
parentIsLocalized,
|
||||
parentIsLocalized: parentIsLocalized ?? false,
|
||||
path: '_id',
|
||||
})
|
||||
} else {
|
||||
@@ -86,6 +91,10 @@ export async function buildSearchParam({
|
||||
})
|
||||
}
|
||||
|
||||
if (!paths[0]) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const [{ field, path }] = paths
|
||||
if (path) {
|
||||
const sanitizedQueryValue = sanitizeQueryValue({
|
||||
@@ -109,6 +118,10 @@ export async function buildSearchParam({
|
||||
return { value: rawQuery }
|
||||
}
|
||||
|
||||
if (!formattedOperator) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
// If there are multiple collections to search through,
|
||||
// Recursively build up a list of query constraints
|
||||
if (paths.length > 1) {
|
||||
@@ -116,84 +129,86 @@ export async function buildSearchParam({
|
||||
// to work backwards from top
|
||||
const pathsToQuery = paths.slice(1).reverse()
|
||||
|
||||
const initialRelationshipQuery = {
|
||||
let relationshipQuery: SearchParam = {
|
||||
value: {},
|
||||
} as SearchParam
|
||||
}
|
||||
|
||||
const relationshipQuery = await pathsToQuery.reduce(
|
||||
async (priorQuery, { collectionSlug: slug, path: subPath }, i) => {
|
||||
const priorQueryResult = await priorQuery
|
||||
for (const [i, { collectionSlug, path: subPath }] of pathsToQuery.entries()) {
|
||||
if (!collectionSlug) {
|
||||
throw new APIError(`Collection with the slug ${collectionSlug} was not found.`)
|
||||
}
|
||||
|
||||
const SubModel = (payload.db as MongooseAdapter).collections[slug]
|
||||
const { Model: SubModel } = getCollection({
|
||||
adapter: payload.db as MongooseAdapter,
|
||||
collectionSlug,
|
||||
})
|
||||
|
||||
// On the "deepest" collection,
|
||||
// Search on the value passed through the query
|
||||
if (i === 0) {
|
||||
const subQuery = await SubModel.buildQuery({
|
||||
locale,
|
||||
payload,
|
||||
where: {
|
||||
[subPath]: {
|
||||
[formattedOperator]: val,
|
||||
},
|
||||
if (i === 0) {
|
||||
const subQuery = await SubModel.buildQuery({
|
||||
locale,
|
||||
payload,
|
||||
where: {
|
||||
[subPath]: {
|
||||
[formattedOperator]: val,
|
||||
},
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
const result = await SubModel.find(subQuery, subQueryOptions)
|
||||
|
||||
const $in: unknown[] = []
|
||||
|
||||
result.forEach((doc) => {
|
||||
const stringID = doc._id.toString()
|
||||
$in.push(stringID)
|
||||
|
||||
if (Types.ObjectId.isValid(stringID)) {
|
||||
$in.push(doc._id)
|
||||
}
|
||||
})
|
||||
|
||||
if (pathsToQuery.length === 1) {
|
||||
return {
|
||||
path,
|
||||
value: { $in },
|
||||
}
|
||||
}
|
||||
|
||||
const nextSubPath = pathsToQuery[i + 1].path
|
||||
|
||||
return {
|
||||
value: { [nextSubPath]: { $in } },
|
||||
}
|
||||
}
|
||||
|
||||
const subQuery = priorQueryResult.value
|
||||
const result = await SubModel.find(subQuery, subQueryOptions)
|
||||
|
||||
const $in = result.map((doc) => doc._id)
|
||||
const $in: unknown[] = []
|
||||
|
||||
// If it is the last recursion
|
||||
// then pass through the search param
|
||||
if (i + 1 === pathsToQuery.length) {
|
||||
result.forEach((doc) => {
|
||||
const stringID = doc._id.toString()
|
||||
$in.push(stringID)
|
||||
|
||||
if (Types.ObjectId.isValid(stringID)) {
|
||||
$in.push(doc._id)
|
||||
}
|
||||
})
|
||||
|
||||
if (pathsToQuery.length === 1) {
|
||||
return {
|
||||
path,
|
||||
value: { $in },
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
const nextSubPath = pathsToQuery[i + 1]?.path
|
||||
|
||||
if (nextSubPath) {
|
||||
relationshipQuery = { value: { [nextSubPath]: $in } }
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
const subQuery = relationshipQuery.value as FilterQuery<any>
|
||||
const result = await SubModel.find(subQuery, subQueryOptions)
|
||||
|
||||
const $in = result.map((doc) => doc._id)
|
||||
|
||||
// If it is the last recursion
|
||||
// then pass through the search param
|
||||
if (i + 1 === pathsToQuery.length) {
|
||||
relationshipQuery = {
|
||||
path,
|
||||
value: { $in },
|
||||
}
|
||||
} else {
|
||||
relationshipQuery = {
|
||||
value: {
|
||||
_id: { $in },
|
||||
},
|
||||
}
|
||||
},
|
||||
Promise.resolve(initialRelationshipQuery),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return relationshipQuery
|
||||
}
|
||||
|
||||
if (formattedOperator && validOperatorSet.has(formattedOperator as Operator)) {
|
||||
const operatorKey = operatorMap[formattedOperator]
|
||||
const operatorKey = operatorMap[formattedOperator as OperatorMapKey]
|
||||
|
||||
if (field.type === 'relationship' || field.type === 'upload') {
|
||||
let hasNumberIDRelation
|
||||
@@ -210,7 +225,7 @@ export async function buildSearchParam({
|
||||
|
||||
if (typeof formattedValue === 'string') {
|
||||
if (Types.ObjectId.isValid(formattedValue)) {
|
||||
result.value[multiIDCondition].push({
|
||||
result.value[multiIDCondition]?.push({
|
||||
[path]: { [operatorKey]: new Types.ObjectId(formattedValue) },
|
||||
})
|
||||
} else {
|
||||
@@ -226,14 +241,16 @@ export async function buildSearchParam({
|
||||
)
|
||||
|
||||
if (hasNumberIDRelation) {
|
||||
result.value[multiIDCondition].push({
|
||||
result.value[multiIDCondition]?.push({
|
||||
[path]: { [operatorKey]: parseFloat(formattedValue) },
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result.value[multiIDCondition].length > 1) {
|
||||
const length = result.value[multiIDCondition]?.length
|
||||
|
||||
if (typeof length === 'number' && length > 1) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { PaginateOptions } from 'mongoose'
|
||||
import type { FlattenedField, SanitizedConfig, Sort } from 'payload'
|
||||
|
||||
import { getLocalizedSortProperty } from './getLocalizedSortProperty.js'
|
||||
@@ -6,7 +5,7 @@ import { getLocalizedSortProperty } from './getLocalizedSortProperty.js'
|
||||
type Args = {
|
||||
config: SanitizedConfig
|
||||
fields: FlattenedField[]
|
||||
locale: string
|
||||
locale?: string
|
||||
parentIsLocalized?: boolean
|
||||
sort: Sort
|
||||
timestamps: boolean
|
||||
@@ -23,10 +22,10 @@ export const buildSortParam = ({
|
||||
config,
|
||||
fields,
|
||||
locale,
|
||||
parentIsLocalized,
|
||||
parentIsLocalized = false,
|
||||
sort,
|
||||
timestamps,
|
||||
}: Args): PaginateOptions['sort'] => {
|
||||
}: Args): Record<string, string> => {
|
||||
if (!sort) {
|
||||
if (timestamps) {
|
||||
sort = '-createdAt'
|
||||
@@ -39,7 +38,7 @@ export const buildSortParam = ({
|
||||
sort = [sort]
|
||||
}
|
||||
|
||||
const sorting = sort.reduce<PaginateOptions['sort']>((acc, item) => {
|
||||
const sorting = sort.reduce<Record<string, string>>((acc, item) => {
|
||||
let sortProperty: string
|
||||
let sortDirection: SortDirection
|
||||
if (item.indexOf('-') === 0) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { FlattenedField, Payload, Where } from 'payload'
|
||||
|
||||
import { QueryError } from 'payload'
|
||||
import { APIError } from 'payload'
|
||||
|
||||
import { parseParams } from './parseParams.js'
|
||||
|
||||
@@ -23,7 +23,7 @@ export const getBuildQueryPlugin = ({
|
||||
collectionSlug,
|
||||
versionsFields,
|
||||
}: GetBuildQueryPluginArgs = {}) => {
|
||||
return function buildQueryPlugin(schema) {
|
||||
return function buildQueryPlugin(schema: any) {
|
||||
const modifiedSchema = schema
|
||||
async function schemaBuildQuery({
|
||||
globalSlug,
|
||||
@@ -31,19 +31,35 @@ export const getBuildQueryPlugin = ({
|
||||
payload,
|
||||
where,
|
||||
}: BuildQueryArgs): Promise<Record<string, unknown>> {
|
||||
let fields = versionsFields
|
||||
if (!fields) {
|
||||
let fields: FlattenedField[] | null = null
|
||||
|
||||
if (versionsFields) {
|
||||
fields = versionsFields
|
||||
} else {
|
||||
if (globalSlug) {
|
||||
const globalConfig = payload.globals.config.find(({ slug }) => slug === globalSlug)
|
||||
|
||||
if (!globalConfig) {
|
||||
throw new APIError(`Global with the slug ${globalSlug} was not found`)
|
||||
}
|
||||
|
||||
fields = globalConfig.flattenedFields
|
||||
}
|
||||
if (collectionSlug) {
|
||||
const collectionConfig = payload.collections[collectionSlug].config
|
||||
const collectionConfig = payload.collections[collectionSlug]?.config
|
||||
|
||||
if (!collectionConfig) {
|
||||
throw new APIError(`Collection with the slug ${globalSlug} was not found`)
|
||||
}
|
||||
|
||||
fields = collectionConfig.flattenedFields
|
||||
}
|
||||
}
|
||||
|
||||
const errors = []
|
||||
if (fields === null) {
|
||||
throw new APIError('Fields are not initialized.')
|
||||
}
|
||||
|
||||
const result = await parseParams({
|
||||
collectionSlug,
|
||||
fields,
|
||||
@@ -54,10 +70,6 @@ export const getBuildQueryPlugin = ({
|
||||
where,
|
||||
})
|
||||
|
||||
if (errors.length > 0) {
|
||||
throw new QueryError(errors)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
modifiedSchema.statics.buildQuery = schemaBuildQuery
|
||||
|
||||
@@ -19,6 +19,7 @@ describe('get localized sort property', () => {
|
||||
it('passes through a non-localized sort property', () => {
|
||||
const result = getLocalizedSortProperty({
|
||||
config,
|
||||
parentIsLocalized: false,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
@@ -35,6 +36,7 @@ describe('get localized sort property', () => {
|
||||
it('properly localizes an un-localized sort property', () => {
|
||||
const result = getLocalizedSortProperty({
|
||||
config,
|
||||
parentIsLocalized: false,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
@@ -52,6 +54,7 @@ describe('get localized sort property', () => {
|
||||
it('keeps specifically asked-for localized sort properties', () => {
|
||||
const result = getLocalizedSortProperty({
|
||||
config,
|
||||
parentIsLocalized: false,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
@@ -69,6 +72,7 @@ describe('get localized sort property', () => {
|
||||
it('properly localizes nested sort properties', () => {
|
||||
const result = getLocalizedSortProperty({
|
||||
config,
|
||||
parentIsLocalized: false,
|
||||
fields: flattenAllFields({
|
||||
fields: [
|
||||
{
|
||||
@@ -94,6 +98,7 @@ describe('get localized sort property', () => {
|
||||
it('keeps requested locale with nested sort properties', () => {
|
||||
const result = getLocalizedSortProperty({
|
||||
config,
|
||||
parentIsLocalized: false,
|
||||
fields: flattenAllFields({
|
||||
fields: [
|
||||
{
|
||||
@@ -119,6 +124,7 @@ describe('get localized sort property', () => {
|
||||
it('properly localizes field within row', () => {
|
||||
const result = getLocalizedSortProperty({
|
||||
config,
|
||||
parentIsLocalized: false,
|
||||
fields: flattenAllFields({
|
||||
fields: [
|
||||
{
|
||||
@@ -143,6 +149,7 @@ describe('get localized sort property', () => {
|
||||
it('properly localizes field within named tab', () => {
|
||||
const result = getLocalizedSortProperty({
|
||||
config,
|
||||
parentIsLocalized: false,
|
||||
fields: flattenAllFields({
|
||||
fields: [
|
||||
{
|
||||
@@ -172,6 +179,7 @@ describe('get localized sort property', () => {
|
||||
it('properly localizes field within unnamed tab', () => {
|
||||
const result = getLocalizedSortProperty({
|
||||
config,
|
||||
parentIsLocalized: false,
|
||||
fields: flattenAllFields({
|
||||
fields: [
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ import { fieldAffectsData, fieldIsPresentationalOnly, fieldShouldBeLocalized } f
|
||||
type Args = {
|
||||
config: SanitizedConfig
|
||||
fields: FlattenedField[]
|
||||
locale: string
|
||||
locale?: string
|
||||
parentIsLocalized: boolean
|
||||
result?: string
|
||||
segments: string[]
|
||||
@@ -36,14 +36,16 @@ export const getLocalizedSortProperty = ({
|
||||
)
|
||||
|
||||
if (matchedField && !fieldIsPresentationalOnly(matchedField)) {
|
||||
let nextFields: FlattenedField[]
|
||||
let nextFields: FlattenedField[] | null = null
|
||||
let nextParentIsLocalized = parentIsLocalized
|
||||
const remainingSegments = [...segments]
|
||||
let localizedSegment = matchedField.name
|
||||
|
||||
if (fieldShouldBeLocalized({ field: matchedField, parentIsLocalized })) {
|
||||
if (
|
||||
fieldShouldBeLocalized({ field: matchedField, parentIsLocalized: parentIsLocalized ?? false })
|
||||
) {
|
||||
// Check to see if next segment is a locale
|
||||
if (segments.length > 0) {
|
||||
if (segments.length > 0 && remainingSegments[0]) {
|
||||
const nextSegmentIsLocale = config.localization.localeCodes.includes(remainingSegments[0])
|
||||
|
||||
// If next segment is locale, remove it from remaining segments
|
||||
@@ -66,16 +68,21 @@ export const getLocalizedSortProperty = ({
|
||||
) {
|
||||
nextFields = matchedField.flattenedFields
|
||||
if (!nextParentIsLocalized) {
|
||||
nextParentIsLocalized = matchedField.localized
|
||||
nextParentIsLocalized = matchedField.localized ?? false
|
||||
}
|
||||
}
|
||||
|
||||
if (matchedField.type === 'blocks') {
|
||||
nextFields = (matchedField.blockReferences ?? matchedField.blocks).reduce(
|
||||
nextFields = (matchedField.blockReferences ?? matchedField.blocks).reduce<FlattenedField[]>(
|
||||
(flattenedBlockFields, _block) => {
|
||||
// TODO: iterate over blocks mapped to block slug in v4, or pass through payload.blocks
|
||||
const block =
|
||||
typeof _block === 'string' ? config.blocks.find((b) => b.slug === _block) : _block
|
||||
typeof _block === 'string' ? config.blocks?.find((b) => b.slug === _block) : _block
|
||||
|
||||
if (!block) {
|
||||
return [...flattenedBlockFields]
|
||||
}
|
||||
|
||||
return [
|
||||
...flattenedBlockFields,
|
||||
...block.flattenedFields.filter(
|
||||
@@ -93,7 +100,7 @@ export const getLocalizedSortProperty = ({
|
||||
|
||||
const result = incomingResult ? `${incomingResult}.${localizedSegment}` : localizedSegment
|
||||
|
||||
if (nextFields) {
|
||||
if (nextFields !== null) {
|
||||
return getLocalizedSortProperty({
|
||||
config,
|
||||
fields: nextFields,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
export type OperatorMapKey = keyof typeof operatorMap
|
||||
|
||||
export const operatorMap = {
|
||||
all: '$all',
|
||||
equals: '$eq',
|
||||
|
||||
@@ -19,7 +19,7 @@ export async function parseParams({
|
||||
collectionSlug?: string
|
||||
fields: FlattenedField[]
|
||||
globalSlug?: string
|
||||
locale: string
|
||||
locale?: string
|
||||
parentIsLocalized: boolean
|
||||
payload: Payload
|
||||
where: Where
|
||||
@@ -30,7 +30,7 @@ export async function parseParams({
|
||||
// We need to determine if the whereKey is an AND, OR, or a schema path
|
||||
for (const relationOrPath of Object.keys(where)) {
|
||||
const condition = where[relationOrPath]
|
||||
let conditionOperator: '$and' | '$or'
|
||||
let conditionOperator: '$and' | '$or' | null = null
|
||||
if (relationOrPath.toLowerCase() === 'and') {
|
||||
conditionOperator = '$and'
|
||||
} else if (relationOrPath.toLowerCase() === 'or') {
|
||||
@@ -46,7 +46,7 @@ export async function parseParams({
|
||||
payload,
|
||||
where: condition,
|
||||
})
|
||||
if (builtConditions.length > 0) {
|
||||
if (builtConditions.length > 0 && conditionOperator !== null) {
|
||||
result[conditionOperator] = builtConditions
|
||||
}
|
||||
} else {
|
||||
@@ -58,6 +58,7 @@ export async function parseParams({
|
||||
const validOperators = Object.keys(pathOperators).filter((operator) =>
|
||||
validOperatorSet.has(operator as Operator),
|
||||
)
|
||||
|
||||
for (const operator of validOperators) {
|
||||
const searchParam = await buildSearchParam({
|
||||
collectionSlug,
|
||||
@@ -68,7 +69,7 @@ export async function parseParams({
|
||||
operator,
|
||||
parentIsLocalized,
|
||||
payload,
|
||||
val: pathOperators[operator],
|
||||
val: (pathOperators as Record<string, Where>)[operator],
|
||||
})
|
||||
|
||||
if (searchParam?.value && searchParam?.path) {
|
||||
@@ -83,7 +84,7 @@ export async function parseParams({
|
||||
result[searchParam.path] = searchParam.value
|
||||
}
|
||||
} else if (typeof searchParam?.value === 'object') {
|
||||
result = deepMergeWithCombinedArrays(result, searchParam.value, {
|
||||
result = deepMergeWithCombinedArrays(result, searchParam.value ?? {}, {
|
||||
// dont clone Types.ObjectIDs
|
||||
clone: false,
|
||||
})
|
||||
|
||||
@@ -21,7 +21,7 @@ type SanitizeQueryValueArgs = {
|
||||
val: any
|
||||
}
|
||||
|
||||
const buildExistsQuery = (formattedValue, path, treatEmptyString = true) => {
|
||||
const buildExistsQuery = (formattedValue: unknown, path: string, treatEmptyString = true) => {
|
||||
if (formattedValue) {
|
||||
return {
|
||||
rawQuery: {
|
||||
@@ -54,14 +54,17 @@ const getFieldFromSegments = ({
|
||||
field: FlattenedBlock | FlattenedField
|
||||
payload: Payload
|
||||
segments: string[]
|
||||
}) => {
|
||||
}): FlattenedField | undefined => {
|
||||
if ('blocks' in field || 'blockReferences' in field) {
|
||||
const _field: FlattenedBlocksField = field as FlattenedBlocksField
|
||||
for (const _block of _field.blockReferences ?? _field.blocks) {
|
||||
const block: FlattenedBlock = typeof _block === 'string' ? payload.blocks[_block] : _block
|
||||
const field = getFieldFromSegments({ field: block, payload, segments })
|
||||
if (field) {
|
||||
return field
|
||||
const block: FlattenedBlock | undefined =
|
||||
typeof _block === 'string' ? payload.blocks[_block] : _block
|
||||
if (block) {
|
||||
const field = getFieldFromSegments({ field: block, payload, segments })
|
||||
if (field) {
|
||||
return field
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,11 +96,13 @@ export const sanitizeQueryValue = ({
|
||||
path,
|
||||
payload,
|
||||
val,
|
||||
}: SanitizeQueryValueArgs): {
|
||||
operator?: string
|
||||
rawQuery?: unknown
|
||||
val?: unknown
|
||||
} => {
|
||||
}: SanitizeQueryValueArgs):
|
||||
| {
|
||||
operator?: string
|
||||
rawQuery?: unknown
|
||||
val?: unknown
|
||||
}
|
||||
| undefined => {
|
||||
let formattedValue = val
|
||||
let formattedOperator = operator
|
||||
if (['array', 'blocks', 'group', 'tab'].includes(field.type) && path.includes('.')) {
|
||||
@@ -141,24 +146,26 @@ export const sanitizeQueryValue = ({
|
||||
formattedValue = createArrayFromCommaDelineated(val)
|
||||
}
|
||||
|
||||
formattedValue = formattedValue.reduce((formattedValues, inVal) => {
|
||||
if (!hasCustomID) {
|
||||
if (Types.ObjectId.isValid(inVal)) {
|
||||
formattedValues.push(new Types.ObjectId(inVal))
|
||||
if (Array.isArray(formattedValue)) {
|
||||
formattedValue = formattedValue.reduce<unknown[]>((formattedValues, inVal) => {
|
||||
if (!hasCustomID) {
|
||||
if (Types.ObjectId.isValid(inVal)) {
|
||||
formattedValues.push(new Types.ObjectId(inVal))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (field.type === 'number') {
|
||||
const parsedNumber = parseFloat(inVal)
|
||||
if (!Number.isNaN(parsedNumber)) {
|
||||
formattedValues.push(parsedNumber)
|
||||
if (field.type === 'number') {
|
||||
const parsedNumber = parseFloat(inVal)
|
||||
if (!Number.isNaN(parsedNumber)) {
|
||||
formattedValues.push(parsedNumber)
|
||||
}
|
||||
} else {
|
||||
formattedValues.push(inVal)
|
||||
}
|
||||
} else {
|
||||
formattedValues.push(inVal)
|
||||
}
|
||||
|
||||
return formattedValues
|
||||
}, [])
|
||||
return formattedValues
|
||||
}, [])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +182,7 @@ export const sanitizeQueryValue = ({
|
||||
if (['all', 'in', 'not_in'].includes(operator) && typeof formattedValue === 'string') {
|
||||
formattedValue = createArrayFromCommaDelineated(formattedValue)
|
||||
|
||||
if (field.type === 'number') {
|
||||
if (field.type === 'number' && Array.isArray(formattedValue)) {
|
||||
formattedValue = formattedValue.map((arrayVal) => parseFloat(arrayVal))
|
||||
}
|
||||
}
|
||||
@@ -264,7 +271,7 @@ export const sanitizeQueryValue = ({
|
||||
return formattedValues
|
||||
}
|
||||
|
||||
if (typeof relationTo === 'string' && payload.collections[relationTo].customIDType) {
|
||||
if (typeof relationTo === 'string' && payload.collections[relationTo]?.customIDType) {
|
||||
if (payload.collections[relationTo].customIDType === 'number') {
|
||||
const parsedNumber = parseFloat(inVal)
|
||||
if (!Number.isNaN(parsedNumber)) {
|
||||
@@ -279,7 +286,7 @@ export const sanitizeQueryValue = ({
|
||||
|
||||
if (
|
||||
Array.isArray(relationTo) &&
|
||||
relationTo.some((relationTo) => !!payload.collections[relationTo].customIDType)
|
||||
relationTo.some((relationTo) => !!payload.collections[relationTo]?.customIDType)
|
||||
) {
|
||||
if (Types.ObjectId.isValid(inVal.toString())) {
|
||||
formattedValues.push(new Types.ObjectId(inVal))
|
||||
@@ -302,7 +309,7 @@ export const sanitizeQueryValue = ({
|
||||
(!Array.isArray(relationTo) || !path.endsWith('.relationTo'))
|
||||
) {
|
||||
if (typeof relationTo === 'string') {
|
||||
const customIDType = payload.collections[relationTo].customIDType
|
||||
const customIDType = payload.collections[relationTo]?.customIDType
|
||||
|
||||
if (customIDType) {
|
||||
if (customIDType === 'number') {
|
||||
@@ -320,7 +327,7 @@ export const sanitizeQueryValue = ({
|
||||
}
|
||||
} else {
|
||||
const hasCustomIDType = relationTo.some(
|
||||
(relationTo) => !!payload.collections[relationTo].customIDType,
|
||||
(relationTo) => !!payload.collections[relationTo]?.customIDType,
|
||||
)
|
||||
|
||||
if (hasCustomIDType) {
|
||||
|
||||
@@ -10,15 +10,31 @@ import { buildSortParam } from './queries/buildSortParam.js'
|
||||
import { aggregatePaginate } from './utilities/aggregatePaginate.js'
|
||||
import { buildJoinAggregation } from './utilities/buildJoinAggregation.js'
|
||||
import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js'
|
||||
import { getCollection } from './utilities/getEntity.js'
|
||||
import { getSession } from './utilities/getSession.js'
|
||||
import { transform } from './utilities/transform.js'
|
||||
|
||||
export const queryDrafts: QueryDrafts = async function queryDrafts(
|
||||
this: MongooseAdapter,
|
||||
{ collection, joins, limit, locale, page, pagination, req, select, sort: sortArg, where },
|
||||
{
|
||||
collection: collectionSlug,
|
||||
joins,
|
||||
limit,
|
||||
locale,
|
||||
page,
|
||||
pagination,
|
||||
req,
|
||||
select,
|
||||
sort: sortArg,
|
||||
where = {},
|
||||
},
|
||||
) {
|
||||
const VersionModel = this.versions[collection]
|
||||
const collectionConfig = this.payload.collections[collection].config
|
||||
const { collectionConfig, Model } = getCollection({
|
||||
adapter: this,
|
||||
collectionSlug,
|
||||
versions: true,
|
||||
})
|
||||
|
||||
const options: QueryOptions = {
|
||||
session: await getSession(this, req),
|
||||
}
|
||||
@@ -89,24 +105,25 @@ export const queryDrafts: QueryDrafts = async function queryDrafts(
|
||||
// the correct indexed field
|
||||
paginationOptions.useCustomCountFn = () => {
|
||||
return Promise.resolve(
|
||||
VersionModel.countDocuments(versionQuery, {
|
||||
Model.countDocuments(versionQuery, {
|
||||
hint: { _id: 1 },
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (limit > 0) {
|
||||
if (limit && limit > 0) {
|
||||
paginationOptions.limit = limit
|
||||
// limit must also be set here, it's ignored when pagination is false
|
||||
paginationOptions.options.limit = limit
|
||||
|
||||
paginationOptions.options!.limit = limit
|
||||
}
|
||||
|
||||
let result
|
||||
|
||||
const aggregate = await buildJoinAggregation({
|
||||
adapter: this,
|
||||
collection,
|
||||
collection: collectionSlug,
|
||||
collectionConfig,
|
||||
joins,
|
||||
locale,
|
||||
@@ -122,17 +139,17 @@ export const queryDrafts: QueryDrafts = async function queryDrafts(
|
||||
collation: paginationOptions.collation,
|
||||
joinAggregation: aggregate,
|
||||
limit: paginationOptions.limit,
|
||||
Model: VersionModel,
|
||||
Model,
|
||||
page: paginationOptions.page,
|
||||
pagination: paginationOptions.pagination,
|
||||
projection: paginationOptions.projection,
|
||||
query: versionQuery,
|
||||
session: paginationOptions.options?.session,
|
||||
session: paginationOptions.options?.session ?? undefined,
|
||||
sort: paginationOptions.sort as object,
|
||||
useEstimatedCount: paginationOptions.useEstimatedCount,
|
||||
})
|
||||
} else {
|
||||
result = await VersionModel.paginate(versionQuery, paginationOptions)
|
||||
result = await Model.paginate(versionQuery, paginationOptions)
|
||||
}
|
||||
|
||||
transform({
|
||||
|
||||
@@ -7,6 +7,7 @@ import { v4 as uuid } from 'uuid'
|
||||
import type { MongooseAdapter } from '../index.js'
|
||||
|
||||
// Needs await to fulfill the interface
|
||||
// @ts-expect-error TransactionOptions isn't compatible with BeginTransaction of the DatabaseAdapter interface.
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
export const beginTransaction: BeginTransaction = async function beginTransaction(
|
||||
this: MongooseAdapter,
|
||||
@@ -20,12 +21,13 @@ export const beginTransaction: BeginTransaction = async function beginTransactio
|
||||
const id = uuid()
|
||||
|
||||
if (!this.sessions[id]) {
|
||||
// @ts-expect-error BaseDatabaseAdapter and MongoosAdapter (that extends Base) sessions aren't compatible.
|
||||
this.sessions[id] = client.startSession()
|
||||
}
|
||||
if (this.sessions[id].inTransaction()) {
|
||||
if (this.sessions[id]?.inTransaction()) {
|
||||
this.payload.logger.warn('beginTransaction called while transaction already exists')
|
||||
} else {
|
||||
this.sessions[id].startTransaction(options || (this.transactionOptions as TransactionOptions))
|
||||
this.sessions[id]?.startTransaction(options || (this.transactionOptions as TransactionOptions))
|
||||
}
|
||||
|
||||
return id
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import type { CommitTransaction } from 'payload'
|
||||
|
||||
export const commitTransaction: CommitTransaction = async function commitTransaction(id) {
|
||||
import type { MongooseAdapter } from '../index.js'
|
||||
|
||||
export const commitTransaction: CommitTransaction = async function commitTransaction(
|
||||
this: MongooseAdapter,
|
||||
id,
|
||||
) {
|
||||
if (id instanceof Promise) {
|
||||
return
|
||||
}
|
||||
@@ -12,7 +17,7 @@ export const commitTransaction: CommitTransaction = async function commitTransac
|
||||
await this.sessions[id].commitTransaction()
|
||||
try {
|
||||
await this.sessions[id].endSession()
|
||||
} catch (error) {
|
||||
} catch (_) {
|
||||
// ending sessions is only best effort and won't impact anything if it fails since the transaction was committed
|
||||
}
|
||||
delete this.sessions[id]
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import type { RollbackTransaction } from 'payload'
|
||||
|
||||
import type { MongooseAdapter } from '../index.js'
|
||||
|
||||
export const rollbackTransaction: RollbackTransaction = async function rollbackTransaction(
|
||||
this: MongooseAdapter,
|
||||
incomingID = '',
|
||||
) {
|
||||
let transactionID: number | string
|
||||
@@ -18,7 +21,7 @@ export const rollbackTransaction: RollbackTransaction = async function rollbackT
|
||||
}
|
||||
|
||||
// when session exists but is not inTransaction something unexpected is happening to the session
|
||||
if (!this.sessions[transactionID].inTransaction()) {
|
||||
if (!this.sessions[transactionID]?.inTransaction()) {
|
||||
this.payload.logger.warn('rollbackTransaction called when no transaction exists')
|
||||
delete this.sessions[transactionID]
|
||||
return
|
||||
@@ -26,8 +29,8 @@ export const rollbackTransaction: RollbackTransaction = async function rollbackT
|
||||
|
||||
// the first call for rollback should be aborted and deleted causing any other operations with the same transaction to fail
|
||||
try {
|
||||
await this.sessions[transactionID].abortTransaction()
|
||||
await this.sessions[transactionID].endSession()
|
||||
await this.sessions[transactionID]?.abortTransaction()
|
||||
await this.sessions[transactionID]?.endSession()
|
||||
} catch (error) {
|
||||
// ignore the error as it is likely a race condition from multiple errors
|
||||
}
|
||||
|
||||
@@ -4,15 +4,17 @@ import type { UpdateGlobal } from 'payload'
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js'
|
||||
import { getGlobal } from './utilities/getEntity.js'
|
||||
import { getSession } from './utilities/getSession.js'
|
||||
import { transform } from './utilities/transform.js'
|
||||
|
||||
export const updateGlobal: UpdateGlobal = async function updateGlobal(
|
||||
this: MongooseAdapter,
|
||||
{ slug, data, options: optionsArgs = {}, req, returning, select },
|
||||
{ slug: globalSlug, data, options: optionsArgs = {}, req, returning, select },
|
||||
) {
|
||||
const Model = this.globals
|
||||
const fields = this.payload.config.globals.find((global) => global.slug === slug).fields
|
||||
const { globalConfig, Model } = getGlobal({ adapter: this, globalSlug })
|
||||
|
||||
const fields = globalConfig.fields
|
||||
|
||||
const options: MongooseUpdateQueryOptions = {
|
||||
...optionsArgs,
|
||||
@@ -20,22 +22,22 @@ export const updateGlobal: UpdateGlobal = async function updateGlobal(
|
||||
new: true,
|
||||
projection: buildProjectionFromSelect({
|
||||
adapter: this,
|
||||
fields: this.payload.config.globals.find((global) => global.slug === slug).flattenedFields,
|
||||
fields: globalConfig.flattenedFields,
|
||||
select,
|
||||
}),
|
||||
session: await getSession(this, req),
|
||||
}
|
||||
|
||||
transform({ adapter: this, data, fields, globalSlug: slug, operation: 'write' })
|
||||
transform({ adapter: this, data, fields, globalSlug, operation: 'write' })
|
||||
|
||||
if (returning === false) {
|
||||
await Model.updateOne({ globalType: slug }, data, options)
|
||||
await Model.updateOne({ globalType: globalSlug }, data, options)
|
||||
return null
|
||||
}
|
||||
|
||||
const result: any = await Model.findOneAndUpdate({ globalType: slug }, data, options)
|
||||
const result: any = await Model.findOneAndUpdate({ globalType: globalSlug }, data, options)
|
||||
|
||||
transform({ adapter: this, data: result, fields, globalSlug: slug, operation: 'read' })
|
||||
transform({ adapter: this, data: result, fields, globalSlug, operation: 'read' })
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { buildQuery } from './queries/buildQuery.js'
|
||||
import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js'
|
||||
import { getGlobal } from './utilities/getEntity.js'
|
||||
import { getSession } from './utilities/getSession.js'
|
||||
import { transform } from './utilities/transform.js'
|
||||
|
||||
@@ -23,12 +24,11 @@ export async function updateGlobalVersion<T extends TypeWithID>(
|
||||
where,
|
||||
}: UpdateGlobalVersionArgs<T>,
|
||||
) {
|
||||
const VersionModel = this.versions[globalSlug]
|
||||
const { globalConfig, Model } = getGlobal({ adapter: this, globalSlug, versions: true })
|
||||
const whereToUse = where || { id: { equals: id } }
|
||||
|
||||
const currentGlobal = this.payload.config.globals.find((global) => global.slug === globalSlug)
|
||||
const fields = buildVersionGlobalFields(this.payload.config, currentGlobal)
|
||||
const flattenedFields = buildVersionGlobalFields(this.payload.config, currentGlobal, true)
|
||||
const fields = buildVersionGlobalFields(this.payload.config, globalConfig)
|
||||
const flattenedFields = buildVersionGlobalFields(this.payload.config, globalConfig, true)
|
||||
const options: MongooseUpdateQueryOptions = {
|
||||
...optionsArgs,
|
||||
lean: true,
|
||||
@@ -51,11 +51,11 @@ export async function updateGlobalVersion<T extends TypeWithID>(
|
||||
transform({ adapter: this, data: versionData, fields, operation: 'write' })
|
||||
|
||||
if (returning === false) {
|
||||
await VersionModel.updateOne(query, versionData, options)
|
||||
await Model.updateOne(query, versionData, options)
|
||||
return null
|
||||
}
|
||||
|
||||
const doc = await VersionModel.findOneAndUpdate(query, versionData, options)
|
||||
const doc = await Model.findOneAndUpdate(query, versionData, options)
|
||||
|
||||
if (!doc) {
|
||||
return null
|
||||
|
||||
@@ -5,16 +5,25 @@ import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { buildQuery } from './queries/buildQuery.js'
|
||||
import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js'
|
||||
import { getCollection } from './utilities/getEntity.js'
|
||||
import { getSession } from './utilities/getSession.js'
|
||||
import { handleError } from './utilities/handleError.js'
|
||||
import { transform } from './utilities/transform.js'
|
||||
|
||||
export const updateMany: UpdateMany = async function updateMany(
|
||||
this: MongooseAdapter,
|
||||
{ collection, data, locale, options: optionsArgs = {}, req, returning, select, where },
|
||||
{
|
||||
collection: collectionSlug,
|
||||
data,
|
||||
locale,
|
||||
options: optionsArgs = {},
|
||||
req,
|
||||
returning,
|
||||
select,
|
||||
where,
|
||||
},
|
||||
) {
|
||||
const Model = this.collections[collection]
|
||||
const fields = this.payload.collections[collection].config.fields
|
||||
const { collectionConfig, Model } = getCollection({ adapter: this, collectionSlug })
|
||||
|
||||
const options: MongooseUpdateQueryOptions = {
|
||||
...optionsArgs,
|
||||
@@ -22,7 +31,7 @@ export const updateMany: UpdateMany = async function updateMany(
|
||||
new: true,
|
||||
projection: buildProjectionFromSelect({
|
||||
adapter: this,
|
||||
fields: this.payload.collections[collection].config.flattenedFields,
|
||||
fields: collectionConfig.flattenedFields,
|
||||
select,
|
||||
}),
|
||||
session: await getSession(this, req),
|
||||
@@ -30,18 +39,18 @@ export const updateMany: UpdateMany = async function updateMany(
|
||||
|
||||
const query = await buildQuery({
|
||||
adapter: this,
|
||||
collectionSlug: collection,
|
||||
fields: this.payload.collections[collection].config.flattenedFields,
|
||||
collectionSlug,
|
||||
fields: collectionConfig.flattenedFields,
|
||||
locale,
|
||||
where,
|
||||
})
|
||||
|
||||
transform({ adapter: this, data, fields, operation: 'write' })
|
||||
transform({ adapter: this, data, fields: collectionConfig.fields, operation: 'write' })
|
||||
|
||||
try {
|
||||
await Model.updateMany(query, data, options)
|
||||
} catch (error) {
|
||||
handleError({ collection, error, req })
|
||||
handleError({ collection: collectionSlug, error, req })
|
||||
}
|
||||
|
||||
if (returning === false) {
|
||||
@@ -53,7 +62,7 @@ export const updateMany: UpdateMany = async function updateMany(
|
||||
transform({
|
||||
adapter: this,
|
||||
data: result,
|
||||
fields,
|
||||
fields: collectionConfig.fields,
|
||||
operation: 'read',
|
||||
})
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { buildQuery } from './queries/buildQuery.js'
|
||||
import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js'
|
||||
import { getCollection } from './utilities/getEntity.js'
|
||||
import { getSession } from './utilities/getSession.js'
|
||||
import { handleError } from './utilities/handleError.js'
|
||||
import { transform } from './utilities/transform.js'
|
||||
@@ -13,26 +14,27 @@ export const updateOne: UpdateOne = async function updateOne(
|
||||
this: MongooseAdapter,
|
||||
{
|
||||
id,
|
||||
collection,
|
||||
collection: collectionSlug,
|
||||
data,
|
||||
locale,
|
||||
options: optionsArgs = {},
|
||||
req,
|
||||
returning,
|
||||
select,
|
||||
where: whereArg,
|
||||
where: whereArg = {},
|
||||
},
|
||||
) {
|
||||
const { collectionConfig, Model } = getCollection({ adapter: this, collectionSlug })
|
||||
const where = id ? { id: { equals: id } } : whereArg
|
||||
const Model = this.collections[collection]
|
||||
const fields = this.payload.collections[collection].config.fields
|
||||
const fields = collectionConfig.fields
|
||||
|
||||
const options: MongooseUpdateQueryOptions = {
|
||||
...optionsArgs,
|
||||
lean: true,
|
||||
new: true,
|
||||
projection: buildProjectionFromSelect({
|
||||
adapter: this,
|
||||
fields: this.payload.collections[collection].config.flattenedFields,
|
||||
fields: collectionConfig.flattenedFields,
|
||||
select,
|
||||
}),
|
||||
session: await getSession(this, req),
|
||||
@@ -40,8 +42,8 @@ export const updateOne: UpdateOne = async function updateOne(
|
||||
|
||||
const query = await buildQuery({
|
||||
adapter: this,
|
||||
collectionSlug: collection,
|
||||
fields: this.payload.collections[collection].config.flattenedFields,
|
||||
collectionSlug,
|
||||
fields: collectionConfig.flattenedFields,
|
||||
locale,
|
||||
where,
|
||||
})
|
||||
@@ -58,7 +60,7 @@ export const updateOne: UpdateOne = async function updateOne(
|
||||
result = await Model.findOneAndUpdate(query, data, options)
|
||||
}
|
||||
} catch (error) {
|
||||
handleError({ collection, error, req })
|
||||
handleError({ collection: collectionSlug, error, req })
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
|
||||
@@ -6,25 +6,34 @@ import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { buildQuery } from './queries/buildQuery.js'
|
||||
import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js'
|
||||
import { getCollection } from './utilities/getEntity.js'
|
||||
import { getSession } from './utilities/getSession.js'
|
||||
import { transform } from './utilities/transform.js'
|
||||
|
||||
export const updateVersion: UpdateVersion = async function updateVersion(
|
||||
this: MongooseAdapter,
|
||||
{ id, collection, locale, options: optionsArgs = {}, req, returning, select, versionData, where },
|
||||
{
|
||||
id,
|
||||
collection: collectionSlug,
|
||||
locale,
|
||||
options: optionsArgs = {},
|
||||
req,
|
||||
returning,
|
||||
select,
|
||||
versionData,
|
||||
where,
|
||||
},
|
||||
) {
|
||||
const VersionModel = this.versions[collection]
|
||||
const whereToUse = where || { id: { equals: id } }
|
||||
const fields = buildVersionCollectionFields(
|
||||
this.payload.config,
|
||||
this.payload.collections[collection].config,
|
||||
)
|
||||
const { collectionConfig, Model } = getCollection({
|
||||
adapter: this,
|
||||
collectionSlug,
|
||||
versions: true,
|
||||
})
|
||||
|
||||
const flattenedFields = buildVersionCollectionFields(
|
||||
this.payload.config,
|
||||
this.payload.collections[collection].config,
|
||||
true,
|
||||
)
|
||||
const whereToUse = where || { id: { equals: id } }
|
||||
const fields = buildVersionCollectionFields(this.payload.config, collectionConfig)
|
||||
|
||||
const flattenedFields = buildVersionCollectionFields(this.payload.config, collectionConfig, true)
|
||||
|
||||
const options: MongooseUpdateQueryOptions = {
|
||||
...optionsArgs,
|
||||
@@ -48,11 +57,11 @@ export const updateVersion: UpdateVersion = async function updateVersion(
|
||||
transform({ adapter: this, data: versionData, fields, operation: 'write' })
|
||||
|
||||
if (returning === false) {
|
||||
await VersionModel.updateOne(query, versionData, options)
|
||||
await Model.updateOne(query, versionData, options)
|
||||
return null
|
||||
}
|
||||
|
||||
const doc = await VersionModel.findOneAndUpdate(query, versionData, options)
|
||||
const doc = await Model.findOneAndUpdate(query, versionData, options)
|
||||
|
||||
if (!doc) {
|
||||
return null
|
||||
|
||||
@@ -82,16 +82,18 @@ export const aggregatePaginate = async ({
|
||||
const totalPages =
|
||||
pagination !== false && typeof limit === 'number' && limit !== 0 ? Math.ceil(count / limit) : 1
|
||||
|
||||
const hasPrevPage = pagination !== false && page > 1
|
||||
const hasNextPage = pagination !== false && totalPages > page
|
||||
const hasPrevPage = typeof page === 'number' && pagination !== false && page > 1
|
||||
const hasNextPage = typeof page === 'number' && pagination !== false && totalPages > page
|
||||
const pagingCounter =
|
||||
pagination !== false && typeof limit === 'number' ? (page - 1) * limit + 1 : 1
|
||||
typeof page === 'number' && pagination !== false && typeof limit === 'number'
|
||||
? (page - 1) * limit + 1
|
||||
: 1
|
||||
|
||||
const result: PaginatedDocs = {
|
||||
docs,
|
||||
hasNextPage,
|
||||
hasPrevPage,
|
||||
limit,
|
||||
limit: limit ?? 0,
|
||||
nextPage: hasNextPage ? page + 1 : null,
|
||||
page,
|
||||
pagingCounter,
|
||||
|
||||
@@ -1,28 +1,29 @@
|
||||
import type { PipelineStage } from 'mongoose'
|
||||
import type {
|
||||
CollectionSlug,
|
||||
FlattenedField,
|
||||
JoinQuery,
|
||||
SanitizedCollectionConfig,
|
||||
Where,
|
||||
} from 'payload'
|
||||
|
||||
import {
|
||||
APIError,
|
||||
type CollectionSlug,
|
||||
type FlattenedField,
|
||||
type JoinQuery,
|
||||
type SanitizedCollectionConfig,
|
||||
} from 'payload'
|
||||
import { fieldShouldBeLocalized } from 'payload/shared'
|
||||
|
||||
import type { MongooseAdapter } from '../index.js'
|
||||
|
||||
import { buildQuery } from '../queries/buildQuery.js'
|
||||
import { buildSortParam } from '../queries/buildSortParam.js'
|
||||
import { getCollection } from './getEntity.js'
|
||||
|
||||
type BuildJoinAggregationArgs = {
|
||||
adapter: MongooseAdapter
|
||||
collection: CollectionSlug
|
||||
collectionConfig: SanitizedCollectionConfig
|
||||
joins: JoinQuery
|
||||
locale: string
|
||||
joins?: JoinQuery
|
||||
locale?: string
|
||||
projection?: Record<string, true>
|
||||
// the where clause for the top collection
|
||||
query?: Where
|
||||
query?: Record<string, unknown>
|
||||
/** whether the query is from drafts */
|
||||
versions?: boolean
|
||||
}
|
||||
@@ -44,9 +45,18 @@ export const buildJoinAggregation = async ({
|
||||
return
|
||||
}
|
||||
|
||||
const joinConfig = adapter.payload.collections[collection].config.joins
|
||||
const joinConfig = adapter.payload.collections[collection]?.config?.joins
|
||||
|
||||
if (!joinConfig) {
|
||||
throw new APIError(`Could not retrieve sanitized join config for ${collection}.`)
|
||||
}
|
||||
|
||||
const aggregate: PipelineStage[] = []
|
||||
const polymorphicJoinsConfig = adapter.payload.collections[collection].config.polymorphicJoins
|
||||
const polymorphicJoinsConfig = adapter.payload.collections[collection]?.config?.polymorphicJoins
|
||||
|
||||
if (!polymorphicJoinsConfig) {
|
||||
throw new APIError(`Could not retrieve sanitized polymorphic joins config for ${collection}.`)
|
||||
}
|
||||
|
||||
for (const join of polymorphicJoinsConfig) {
|
||||
if (projection && !projection[join.joinPath]) {
|
||||
@@ -62,12 +72,14 @@ export const buildJoinAggregation = async ({
|
||||
limit: limitJoin = join.field.defaultLimit ?? 10,
|
||||
page,
|
||||
sort: sortJoin = join.field.defaultSort || collectionConfig.defaultSort,
|
||||
where: whereJoin,
|
||||
where: whereJoin = {},
|
||||
} = joins?.[join.joinPath] || {}
|
||||
|
||||
const aggregatedFields: FlattenedField[] = []
|
||||
for (const collectionSlug of join.field.collection) {
|
||||
for (const field of adapter.payload.collections[collectionSlug].config.flattenedFields) {
|
||||
const { collectionConfig } = getCollection({ adapter, collectionSlug })
|
||||
|
||||
for (const field of collectionConfig.flattenedFields) {
|
||||
if (!aggregatedFields.some((eachField) => eachField.name === field.name)) {
|
||||
aggregatedFields.push(field)
|
||||
}
|
||||
@@ -89,7 +101,7 @@ export const buildJoinAggregation = async ({
|
||||
where: whereJoin,
|
||||
})
|
||||
|
||||
const sortProperty = Object.keys(sort)[0]
|
||||
const sortProperty = Object.keys(sort)[0]! // assert because buildSortParam always returns at least 1 key.
|
||||
const sortDirection = sort[sortProperty] === 'asc' ? 1 : -1
|
||||
|
||||
const projectSort = sortProperty !== '_id' && sortProperty !== 'relationTo'
|
||||
@@ -124,10 +136,12 @@ export const buildJoinAggregation = async ({
|
||||
},
|
||||
]
|
||||
|
||||
const { Model: JoinModel } = getCollection({ adapter, collectionSlug })
|
||||
|
||||
aggregate.push({
|
||||
$lookup: {
|
||||
as: alias,
|
||||
from: adapter.collections[collectionSlug].collection.name,
|
||||
from: JoinModel.collection.name,
|
||||
let: {
|
||||
root_id_: '$_id',
|
||||
},
|
||||
@@ -159,7 +173,7 @@ export const buildJoinAggregation = async ({
|
||||
aggregate.push({
|
||||
$lookup: {
|
||||
as: `${as}.totalDocs.${alias}`,
|
||||
from: adapter.collections[collectionSlug].collection.name,
|
||||
from: JoinModel.collection.name,
|
||||
let: {
|
||||
root_id_: '$_id',
|
||||
},
|
||||
@@ -232,7 +246,13 @@ export const buildJoinAggregation = async ({
|
||||
}
|
||||
|
||||
for (const slug of Object.keys(joinConfig)) {
|
||||
for (const join of joinConfig[slug]) {
|
||||
const joinsList = joinConfig[slug]
|
||||
|
||||
if (!joinsList) {
|
||||
throw new APIError(`Failed to retrieve array of joins for ${slug} in collectio ${collection}`)
|
||||
}
|
||||
|
||||
for (const join of joinsList) {
|
||||
if (projection && !projection[join.joinPath]) {
|
||||
continue
|
||||
}
|
||||
@@ -241,31 +261,34 @@ export const buildJoinAggregation = async ({
|
||||
continue
|
||||
}
|
||||
|
||||
const { collectionConfig, Model: JoinModel } = getCollection({
|
||||
adapter,
|
||||
collectionSlug: join.field.collection as string,
|
||||
})
|
||||
|
||||
const {
|
||||
count,
|
||||
limit: limitJoin = join.field.defaultLimit ?? 10,
|
||||
page,
|
||||
sort: sortJoin = join.field.defaultSort || collectionConfig.defaultSort,
|
||||
where: whereJoin,
|
||||
where: whereJoin = {},
|
||||
} = joins?.[join.joinPath] || {}
|
||||
|
||||
if (Array.isArray(join.field.collection)) {
|
||||
throw new Error('Unreachable')
|
||||
}
|
||||
|
||||
const joinModel = adapter.collections[join.field.collection]
|
||||
|
||||
const sort = buildSortParam({
|
||||
config: adapter.payload.config,
|
||||
fields: adapter.payload.collections[slug].config.flattenedFields,
|
||||
fields: collectionConfig.flattenedFields,
|
||||
locale,
|
||||
sort: sortJoin,
|
||||
timestamps: true,
|
||||
})
|
||||
const sortProperty = Object.keys(sort)[0]
|
||||
const sortProperty = Object.keys(sort)[0]!
|
||||
const sortDirection = sort[sortProperty] === 'asc' ? 1 : -1
|
||||
|
||||
const $match = await joinModel.buildQuery({
|
||||
const $match = await JoinModel.buildQuery({
|
||||
locale,
|
||||
payload: adapter.payload,
|
||||
where: whereJoin,
|
||||
@@ -301,7 +324,7 @@ export const buildJoinAggregation = async ({
|
||||
$lookup: {
|
||||
as: `${as}.totalDocs`,
|
||||
foreignField,
|
||||
from: adapter.collections[slug].collection.name,
|
||||
from: JoinModel.collection.name,
|
||||
localField: versions ? 'parent' : '_id',
|
||||
pipeline: [
|
||||
{
|
||||
@@ -329,7 +352,7 @@ export const buildJoinAggregation = async ({
|
||||
$lookup: {
|
||||
as: `${as}.docs`,
|
||||
foreignField: `${join.field.on}${code}${polymorphicSuffix}`,
|
||||
from: adapter.collections[slug].collection.name,
|
||||
from: JoinModel.collection.name,
|
||||
localField: versions ? 'parent' : '_id',
|
||||
pipeline,
|
||||
},
|
||||
@@ -390,7 +413,7 @@ export const buildJoinAggregation = async ({
|
||||
$lookup: {
|
||||
as: `${as}.docs`,
|
||||
foreignField,
|
||||
from: adapter.collections[slug].collection.name,
|
||||
from: JoinModel.collection.name,
|
||||
localField: versions ? 'parent' : '_id',
|
||||
pipeline,
|
||||
},
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import type { FieldAffectingData, FlattenedField, SelectMode, SelectType } from 'payload'
|
||||
import type {
|
||||
FieldAffectingData,
|
||||
FlattenedField,
|
||||
SelectIncludeType,
|
||||
SelectMode,
|
||||
SelectType,
|
||||
} from 'payload'
|
||||
|
||||
import {
|
||||
deepCopyObjectSimple,
|
||||
@@ -107,7 +113,7 @@ const traverseFields = ({
|
||||
const fieldSelect = select[field.name] as SelectType
|
||||
|
||||
if (field.type === 'array' && selectMode === 'include') {
|
||||
fieldSelect['id'] = true
|
||||
fieldSelect.id = true
|
||||
}
|
||||
|
||||
traverseFields({
|
||||
@@ -128,6 +134,11 @@ const traverseFields = ({
|
||||
|
||||
for (const _block of field.blockReferences ?? field.blocks) {
|
||||
const block = typeof _block === 'string' ? adapter.payload.blocks[_block] : _block
|
||||
|
||||
if (!block) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (
|
||||
(selectMode === 'include' && blocksSelect[block.slug] === true) ||
|
||||
(selectMode === 'exclude' && typeof blocksSelect[block.slug] === 'undefined')
|
||||
@@ -155,9 +166,10 @@ const traverseFields = ({
|
||||
blocksSelect[block.slug] = {}
|
||||
}
|
||||
|
||||
if (blockSelectMode === 'include') {
|
||||
blocksSelect[block.slug]['id'] = true
|
||||
blocksSelect[block.slug]['blockType'] = true
|
||||
if (blockSelectMode === 'include' && typeof blocksSelect[block.slug] === 'object') {
|
||||
const blockSelect = blocksSelect[block.slug] as SelectIncludeType
|
||||
blockSelect.id = true
|
||||
blockSelect.blockType = true
|
||||
}
|
||||
|
||||
traverseFields({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { DBIdentifierName } from 'payload'
|
||||
import { APIError, type DBIdentifierName } from 'payload'
|
||||
|
||||
type Args = {
|
||||
config: {
|
||||
@@ -22,7 +22,7 @@ export const getDBName = ({
|
||||
target = 'dbName',
|
||||
versions = false,
|
||||
}: Args): string => {
|
||||
let result: string
|
||||
let result: null | string = null
|
||||
let custom = config[target]
|
||||
|
||||
if (!custom && target === 'enumName') {
|
||||
@@ -32,12 +32,16 @@ export const getDBName = ({
|
||||
if (custom) {
|
||||
result = typeof custom === 'function' ? custom({}) : custom
|
||||
} else {
|
||||
result = name ?? slug
|
||||
result = name ?? slug ?? null
|
||||
}
|
||||
|
||||
if (versions) {
|
||||
result = `_${result}_versions`
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
throw new APIError(`Assertion for DB name of ${name} ${slug} was failed.`)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
91
packages/db-mongodb/src/utilities/getEntity.ts
Normal file
91
packages/db-mongodb/src/utilities/getEntity.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import type { Collection, SanitizedCollectionConfig, SanitizedGlobalConfig } from 'payload'
|
||||
|
||||
import { APIError } from 'payload'
|
||||
|
||||
import type { MongooseAdapter } from '../index.js'
|
||||
import type { CollectionModel, GlobalModel } from '../types.js'
|
||||
|
||||
export const getCollection = ({
|
||||
adapter,
|
||||
collectionSlug,
|
||||
versions = false,
|
||||
}: {
|
||||
adapter: MongooseAdapter
|
||||
collectionSlug: string
|
||||
versions?: boolean
|
||||
}): {
|
||||
collectionConfig: SanitizedCollectionConfig
|
||||
customIDType: Collection['customIDType']
|
||||
|
||||
Model: CollectionModel
|
||||
} => {
|
||||
const collection = adapter.payload.collections[collectionSlug]
|
||||
|
||||
if (!collection) {
|
||||
throw new APIError(
|
||||
`ERROR: Failed to retrieve collection with the slug "${collectionSlug}". Does not exist.`,
|
||||
)
|
||||
}
|
||||
|
||||
if (versions) {
|
||||
const Model = adapter.versions[collectionSlug]
|
||||
|
||||
if (!Model) {
|
||||
throw new APIError(
|
||||
`ERROR: Failed to retrieve collection version model with the slug "${collectionSlug}". Does not exist.`,
|
||||
)
|
||||
}
|
||||
|
||||
return { collectionConfig: collection.config, customIDType: collection.customIDType, Model }
|
||||
}
|
||||
|
||||
const Model = adapter.collections[collectionSlug]
|
||||
|
||||
if (!Model) {
|
||||
throw new APIError(
|
||||
`ERROR: Failed to retrieve collection model with the slug "${collectionSlug}". Does not exist.`,
|
||||
)
|
||||
}
|
||||
|
||||
return { collectionConfig: collection.config, customIDType: collection.customIDType, Model }
|
||||
}
|
||||
|
||||
type BaseGetGlobalArgs = {
|
||||
adapter: MongooseAdapter
|
||||
globalSlug: string
|
||||
}
|
||||
|
||||
interface GetGlobal {
|
||||
(args: { versions?: false | undefined } & BaseGetGlobalArgs): {
|
||||
globalConfig: SanitizedGlobalConfig
|
||||
Model: GlobalModel
|
||||
}
|
||||
(args: { versions?: true } & BaseGetGlobalArgs): {
|
||||
globalConfig: SanitizedGlobalConfig
|
||||
Model: CollectionModel
|
||||
}
|
||||
}
|
||||
|
||||
export const getGlobal: GetGlobal = ({ adapter, globalSlug, versions = false }) => {
|
||||
const globalConfig = adapter.payload.config.globals.find((each) => each.slug === globalSlug)
|
||||
|
||||
if (!globalConfig) {
|
||||
throw new APIError(
|
||||
`ERROR: Failed to retrieve global with the slug "${globalSlug}". Does not exist.`,
|
||||
)
|
||||
}
|
||||
|
||||
if (versions) {
|
||||
const Model = adapter.versions[globalSlug]
|
||||
|
||||
if (!Model) {
|
||||
throw new APIError(
|
||||
`ERROR: Failed to retrieve global version model with the slug "${globalSlug}". Does not exist.`,
|
||||
)
|
||||
}
|
||||
|
||||
return { globalConfig, Model }
|
||||
}
|
||||
|
||||
return { globalConfig, Model: adapter.globals } as any
|
||||
}
|
||||
@@ -9,7 +9,7 @@ export const handleError = ({
|
||||
req,
|
||||
}: {
|
||||
collection?: string
|
||||
error: Error
|
||||
error: unknown
|
||||
global?: string
|
||||
req?: Partial<PayloadRequest>
|
||||
}) => {
|
||||
@@ -18,14 +18,20 @@ export const handleError = ({
|
||||
}
|
||||
|
||||
// Handle uniqueness error from MongoDB
|
||||
if ('code' in error && error.code === 11000 && 'keyValue' in error && error.keyValue) {
|
||||
if (
|
||||
'code' in error &&
|
||||
error.code === 11000 &&
|
||||
'keyValue' in error &&
|
||||
error.keyValue &&
|
||||
typeof error.keyValue === 'object'
|
||||
) {
|
||||
throw new ValidationError(
|
||||
{
|
||||
collection,
|
||||
errors: [
|
||||
{
|
||||
message: req?.t ? req.t('error:valueMustBeUnique') : 'Value must be unique',
|
||||
path: Object.keys(error.keyValue)[0],
|
||||
path: Object.keys(error.keyValue)[0] ?? '',
|
||||
},
|
||||
],
|
||||
global,
|
||||
@@ -34,5 +40,6 @@ export const handleError = ({
|
||||
)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/only-throw-error
|
||||
throw error
|
||||
}
|
||||
|
||||
@@ -234,12 +234,12 @@ export const transform = ({
|
||||
fields,
|
||||
globalSlug,
|
||||
operation,
|
||||
parentIsLocalized,
|
||||
parentIsLocalized = false,
|
||||
validateRelationships = true,
|
||||
}: Args) => {
|
||||
if (Array.isArray(data)) {
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
transform({ adapter, data: data[i], fields, globalSlug, operation, validateRelationships })
|
||||
for (const item of data) {
|
||||
transform({ adapter, data: item, fields, globalSlug, operation, validateRelationships })
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -262,14 +262,16 @@ export const transform = ({
|
||||
data.globalType = globalSlug
|
||||
}
|
||||
|
||||
const sanitize: TraverseFieldsCallback = ({ field, ref }) => {
|
||||
if (!ref || typeof ref !== 'object') {
|
||||
const sanitize: TraverseFieldsCallback = ({ field, ref: incomingRef }) => {
|
||||
if (!incomingRef || typeof incomingRef !== 'object') {
|
||||
return
|
||||
}
|
||||
|
||||
if (field.type === 'date' && operation === 'read' && ref[field.name]) {
|
||||
const ref = incomingRef as Record<string, unknown>
|
||||
|
||||
if (field.type === 'date' && operation === 'read' && field.name in ref && ref[field.name]) {
|
||||
if (config.localization && fieldShouldBeLocalized({ field, parentIsLocalized })) {
|
||||
const fieldRef = ref[field.name]
|
||||
const fieldRef = ref[field.name] as Record<string, unknown>
|
||||
if (!fieldRef || typeof fieldRef !== 'object') {
|
||||
return
|
||||
}
|
||||
@@ -284,7 +286,7 @@ export const transform = ({
|
||||
} else {
|
||||
sanitizeDate({
|
||||
field,
|
||||
ref: ref as Record<string, unknown>,
|
||||
ref,
|
||||
value: ref[field.name],
|
||||
})
|
||||
}
|
||||
@@ -302,13 +304,13 @@ export const transform = ({
|
||||
// handle localized relationships
|
||||
if (config.localization && fieldShouldBeLocalized({ field, parentIsLocalized })) {
|
||||
const locales = config.localization.locales
|
||||
const fieldRef = ref[field.name]
|
||||
const fieldRef = ref[field.name] as Record<string, unknown>
|
||||
if (typeof fieldRef !== 'object') {
|
||||
return
|
||||
}
|
||||
|
||||
for (const { code } of locales) {
|
||||
const value = ref[field.name][code]
|
||||
const value = fieldRef[code]
|
||||
if (value) {
|
||||
sanitizeRelationship({
|
||||
config,
|
||||
@@ -328,7 +330,7 @@ export const transform = ({
|
||||
field,
|
||||
locale: undefined,
|
||||
operation,
|
||||
ref: ref as Record<string, unknown>,
|
||||
ref,
|
||||
validateRelationships,
|
||||
value: ref[field.name],
|
||||
})
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
/* TODO: remove the following lines */
|
||||
"strict": false,
|
||||
"noUncheckedIndexedAccess": false,
|
||||
},
|
||||
"references": [{ "path": "../payload" }, { "path": "../translations" }]
|
||||
}
|
||||
|
||||
26
pnpm-lock.yaml
generated
26
pnpm-lock.yaml
generated
@@ -264,6 +264,15 @@ importers:
|
||||
'@payloadcms/eslint-config':
|
||||
specifier: workspace:*
|
||||
version: link:../eslint-config
|
||||
'@types/mongoose-aggregate-paginate-v2':
|
||||
specifier: 1.0.12
|
||||
version: 1.0.12(@aws-sdk/credential-providers@3.687.0(@aws-sdk/client-sso-oidc@3.687.0(@aws-sdk/client-sts@3.687.0)))(socks@2.8.3)
|
||||
'@types/prompts':
|
||||
specifier: ^2.4.5
|
||||
version: 2.4.9
|
||||
'@types/uuid':
|
||||
specifier: 10.0.0
|
||||
version: 10.0.0
|
||||
mongodb:
|
||||
specifier: 6.12.0
|
||||
version: 6.12.0(@aws-sdk/credential-providers@3.687.0(@aws-sdk/client-sso-oidc@3.687.0(@aws-sdk/client-sts@3.687.0)))(socks@2.8.3)
|
||||
@@ -5349,6 +5358,9 @@ packages:
|
||||
'@types/minimist@1.2.5':
|
||||
resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==}
|
||||
|
||||
'@types/mongoose-aggregate-paginate-v2@1.0.12':
|
||||
resolution: {integrity: sha512-wL8pgJQxqJagv5f5mR7aI8WgUu22nS6rVLoJm71W2Uu+iKfS8jgph2rRLfXrjo+dFt1s7ik5Zl+uGZ4f5GM6Vw==}
|
||||
|
||||
'@types/ms@0.7.34':
|
||||
resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==}
|
||||
|
||||
@@ -14795,6 +14807,20 @@ snapshots:
|
||||
|
||||
'@types/minimist@1.2.5': {}
|
||||
|
||||
'@types/mongoose-aggregate-paginate-v2@1.0.12(@aws-sdk/credential-providers@3.687.0(@aws-sdk/client-sso-oidc@3.687.0(@aws-sdk/client-sts@3.687.0)))(socks@2.8.3)':
|
||||
dependencies:
|
||||
'@types/node': 22.5.4
|
||||
mongoose: 8.9.5(@aws-sdk/credential-providers@3.687.0(@aws-sdk/client-sso-oidc@3.687.0(@aws-sdk/client-sts@3.687.0)))(socks@2.8.3)
|
||||
transitivePeerDependencies:
|
||||
- '@aws-sdk/credential-providers'
|
||||
- '@mongodb-js/zstd'
|
||||
- gcp-metadata
|
||||
- kerberos
|
||||
- mongodb-client-encryption
|
||||
- snappy
|
||||
- socks
|
||||
- supports-color
|
||||
|
||||
'@types/ms@0.7.34': {}
|
||||
|
||||
'@types/mysql@2.15.26':
|
||||
|
||||
@@ -1129,7 +1129,6 @@ describe('Queues', () => {
|
||||
expect(jobAfterRun.input.amountTask1Retried).toBe(0)
|
||||
})
|
||||
|
||||
|
||||
it('ensure jobs can be cancelled using payload.jobs.cancelByID', async () => {
|
||||
payload.config.jobs.deleteJobOnComplete = false
|
||||
|
||||
@@ -1201,7 +1200,7 @@ describe('Queues', () => {
|
||||
expect(jobAfterRun.error?.cancelled).toBe(true)
|
||||
expect(jobAfterRun.processing).toBe(false)
|
||||
})
|
||||
|
||||
|
||||
it('can tasks throw error', async () => {
|
||||
payload.config.jobs.deleteJobOnComplete = false
|
||||
|
||||
|
||||
Reference in New Issue
Block a user