Compare commits
18 Commits
feat/live-
...
feat/beta-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
008437d62f | ||
|
|
95112a873b | ||
|
|
25da9b62d1 | ||
|
|
1e5e07489c | ||
|
|
bcb1d4eb57 | ||
|
|
268fff2645 | ||
|
|
349d68c719 | ||
|
|
9fefe79998 | ||
|
|
9f73743806 | ||
|
|
cb00bc5856 | ||
|
|
bd22bb28aa | ||
|
|
ed1e460ae7 | ||
|
|
78ac4fd89e | ||
|
|
c328c42b72 | ||
|
|
8ede4d0098 | ||
|
|
31ad34595e | ||
|
|
33278aa8d8 | ||
|
|
aad186c8b4 |
@@ -33,6 +33,9 @@ export default buildConfig({
|
||||
| Option | Description |
|
||||
| -------------------- | ----------- |
|
||||
| `autoPluralization` | Tell Mongoose to auto-pluralize any collection names if it encounters any singular words used as collection `slug`s. |
|
||||
| `schemaOptions` | Customize schema options for all Mongoose schemas created internally. |
|
||||
| `collections` | Options on a collection-by-collection basis. [More](#collections-options) |
|
||||
| `globals` | Options for the Globals collection created by Payload. [More](#globals-options) |
|
||||
| `connectOptions` | Customize MongoDB connection options. Payload will connect to your MongoDB database using default options which you can override and extend to include all the [options](https://mongoosejs.com/docs/connections.html#options) available to mongoose. |
|
||||
| `disableIndexHints` | Set to true to disable hinting to MongoDB to use 'id' as index. This is currently done when counting documents for pagination, as it increases the speed of the count function used in that query. Disabling this optimization might fix some problems with AWS DocumentDB. Defaults to false |
|
||||
| `migrationDir` | Customize the directory that migrations are stored. |
|
||||
@@ -49,3 +52,27 @@ You can access Mongoose models as follows:
|
||||
- Collection models - `payload.db.collections[myCollectionSlug]`
|
||||
- Globals model - `payload.db.globals`
|
||||
- Versions model (both collections and globals) - `payload.db.versions[myEntitySlug]`
|
||||
|
||||
### Collections Options
|
||||
|
||||
You can configure the way the MongoDB adapter works on a collection-by-collection basis, including customizing Mongoose `schemaOptions` for each collection schema created.
|
||||
|
||||
Example:
|
||||
|
||||
```ts
|
||||
const db = mongooseAdapter({
|
||||
url: 'your-url-here',
|
||||
collections: {
|
||||
users: {
|
||||
//
|
||||
schemaOptions: {
|
||||
strict: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Global Options
|
||||
|
||||
Payload automatically creates a single `globals` collection that correspond with any Payload globals that you define. When you initialize the `mongooseAdapter`, you can specify settings here for your globals in a similar manner to how you can for collections above. Right now, the only property available is `schemaOptions` but more may be added in the future.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { CollationOptions, TransactionOptions } from 'mongodb'
|
||||
import type { MongoMemoryReplSet } from 'mongodb-memory-server'
|
||||
import type { ClientSession, ConnectOptions, Connection } from 'mongoose'
|
||||
import type { ClientSession, ConnectOptions, Connection, SchemaOptions } from 'mongoose'
|
||||
import type { BaseDatabaseAdapter, DatabaseAdapterObj, Payload } from 'payload'
|
||||
|
||||
import fs from 'fs'
|
||||
@@ -66,6 +66,15 @@ export interface Args {
|
||||
* Defaults to disabled.
|
||||
*/
|
||||
collation?: Omit<CollationOptions, 'locale'>
|
||||
/** Define Mongoose options on a collection-by-collection basis.
|
||||
*/
|
||||
collections?: {
|
||||
[slug: string]: {
|
||||
/** Define Mongoose schema options for a given collection.
|
||||
*/
|
||||
schemaOptions?: SchemaOptions
|
||||
}
|
||||
}
|
||||
/** Extra configuration options */
|
||||
connectOptions?: {
|
||||
/** Set false to disable $facet aggregation in non-supporting databases, Defaults to true */
|
||||
@@ -73,23 +82,40 @@ export interface Args {
|
||||
} & ConnectOptions
|
||||
/** Set to true to disable hinting to MongoDB to use 'id' as index. This is currently done when counting documents for pagination. Disabling this optimization might fix some problems with AWS DocumentDB. Defaults to false */
|
||||
disableIndexHints?: boolean
|
||||
/** Define Mongoose options for the globals collection.
|
||||
*/
|
||||
globals?: {
|
||||
schemaOptions?: SchemaOptions
|
||||
}
|
||||
migrationDir?: string
|
||||
/**
|
||||
* typed as any to avoid dependency
|
||||
*/
|
||||
mongoMemoryServer?: MongoMemoryReplSet
|
||||
/** Define default Mongoose schema options for all schemas created.
|
||||
*/
|
||||
schemaOptions?: SchemaOptions
|
||||
transactionOptions?: TransactionOptions | false
|
||||
/** The URL to connect to MongoDB or false to start payload and prevent connecting */
|
||||
url: false | string
|
||||
}
|
||||
|
||||
export type MongooseAdapter = {
|
||||
collectionOptions: {
|
||||
[slug: string]: {
|
||||
schemaOptions?: SchemaOptions
|
||||
}
|
||||
}
|
||||
collections: {
|
||||
[slug: string]: CollectionModel
|
||||
}
|
||||
connection: Connection
|
||||
globals: GlobalModel
|
||||
globalsOptions: {
|
||||
schemaOptions?: SchemaOptions
|
||||
}
|
||||
mongoMemoryServer: MongoMemoryReplSet
|
||||
schemaOptions?: SchemaOptions
|
||||
sessions: Record<number | string, ClientSession>
|
||||
versions: {
|
||||
[slug: string]: CollectionModel
|
||||
@@ -100,13 +126,22 @@ export type MongooseAdapter = {
|
||||
declare module 'payload' {
|
||||
export interface DatabaseAdapter
|
||||
extends Omit<BaseDatabaseAdapter, 'sessions'>,
|
||||
Omit<Args, 'migrationDir'> {
|
||||
Omit<Args, 'collections' | 'globals' | 'migrationDir'> {
|
||||
collectionOptions: {
|
||||
[slug: string]: {
|
||||
schemaOptions?: SchemaOptions
|
||||
}
|
||||
}
|
||||
collections: {
|
||||
[slug: string]: CollectionModel
|
||||
}
|
||||
connection: Connection
|
||||
globals: GlobalModel
|
||||
globalsOptions: {
|
||||
schemaOptions?: SchemaOptions
|
||||
}
|
||||
mongoMemoryServer: MongoMemoryReplSet
|
||||
schemaOptions?: SchemaOptions
|
||||
sessions: Record<number | string, ClientSession>
|
||||
transactionOptions: TransactionOptions
|
||||
versions: {
|
||||
@@ -117,10 +152,13 @@ declare module 'payload' {
|
||||
|
||||
export function mongooseAdapter({
|
||||
autoPluralization = true,
|
||||
collections,
|
||||
connectOptions,
|
||||
disableIndexHints = false,
|
||||
globals,
|
||||
migrationDir: migrationDirArg,
|
||||
mongoMemoryServer,
|
||||
schemaOptions,
|
||||
transactionOptions = {},
|
||||
url,
|
||||
}: Args): DatabaseAdapterObj {
|
||||
@@ -133,17 +171,21 @@ export function mongooseAdapter({
|
||||
|
||||
// Mongoose-specific
|
||||
autoPluralization,
|
||||
collectionOptions: collections || {},
|
||||
collections: {},
|
||||
connectOptions: connectOptions || {},
|
||||
connection: undefined,
|
||||
count,
|
||||
disableIndexHints,
|
||||
globals: undefined,
|
||||
globalsOptions: globals || {},
|
||||
mongoMemoryServer,
|
||||
schemaOptions: schemaOptions || {},
|
||||
sessions: {},
|
||||
transactionOptions: transactionOptions === false ? undefined : transactionOptions,
|
||||
url,
|
||||
versions: {},
|
||||
|
||||
// DatabaseAdapter
|
||||
beginTransaction: transactionOptions ? beginTransaction : undefined,
|
||||
commitTransaction,
|
||||
|
||||
@@ -16,20 +16,22 @@ import { getDBName } from './utilities/getDBName.js'
|
||||
|
||||
export const init: Init = function init(this: MongooseAdapter) {
|
||||
this.payload.config.collections.forEach((collection: SanitizedCollectionConfig) => {
|
||||
const schema = buildCollectionSchema(collection, this.payload.config)
|
||||
const schema = buildCollectionSchema(collection, this)
|
||||
|
||||
if (collection.versions) {
|
||||
const versionModelName = getDBName({ config: collection, versions: true })
|
||||
|
||||
const versionCollectionFields = buildVersionCollectionFields(collection)
|
||||
|
||||
const versionSchema = buildSchema(this.payload.config, versionCollectionFields, {
|
||||
const versionSchema = buildSchema(this, versionCollectionFields, {
|
||||
disableUnique: true,
|
||||
draftsEnabled: true,
|
||||
indexSortableFields: this.payload.config.indexSortableFields,
|
||||
options: {
|
||||
minimize: false,
|
||||
timestamps: false,
|
||||
...this.schemaOptions,
|
||||
...(this.collectionOptions[collection.slug]?.schemaOptions || {}),
|
||||
},
|
||||
})
|
||||
|
||||
@@ -57,7 +59,7 @@ export const init: Init = function init(this: MongooseAdapter) {
|
||||
this.collections[collection.slug] = model
|
||||
})
|
||||
|
||||
const model = buildGlobalModel(this.payload.config)
|
||||
const model = buildGlobalModel(this)
|
||||
this.globals = model
|
||||
|
||||
this.payload.config.globals.forEach((global) => {
|
||||
@@ -66,13 +68,15 @@ export const init: Init = function init(this: MongooseAdapter) {
|
||||
|
||||
const versionGlobalFields = buildVersionGlobalFields(global)
|
||||
|
||||
const versionSchema = buildSchema(this.payload.config, versionGlobalFields, {
|
||||
const versionSchema = buildSchema(this, versionGlobalFields, {
|
||||
disableUnique: true,
|
||||
draftsEnabled: true,
|
||||
indexSortableFields: this.payload.config.indexSortableFields,
|
||||
options: {
|
||||
minimize: false,
|
||||
timestamps: false,
|
||||
...this.schemaOptions,
|
||||
...(this.globalsOptions.schemaOptions || {}),
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -1,27 +1,29 @@
|
||||
import type { PaginateOptions, Schema } from 'mongoose'
|
||||
import type { SanitizedCollectionConfig, SanitizedConfig } from 'payload'
|
||||
import type { SanitizedCollectionConfig } from 'payload'
|
||||
|
||||
import paginate from 'mongoose-paginate-v2'
|
||||
|
||||
import type { MongooseAdapter } from '../index.js'
|
||||
|
||||
import getBuildQueryPlugin from '../queries/buildQuery.js'
|
||||
import buildSchema from './buildSchema.js'
|
||||
|
||||
const buildCollectionSchema = (
|
||||
collection: SanitizedCollectionConfig,
|
||||
config: SanitizedConfig,
|
||||
schemaOptions = {},
|
||||
adapter: MongooseAdapter,
|
||||
): Schema => {
|
||||
const schema = buildSchema(config, collection.fields, {
|
||||
const schema = buildSchema(adapter, collection.fields, {
|
||||
draftsEnabled: Boolean(typeof collection?.versions === 'object' && collection.versions.drafts),
|
||||
indexSortableFields: config.indexSortableFields,
|
||||
indexSortableFields: adapter.payload.config.indexSortableFields,
|
||||
options: {
|
||||
minimize: false,
|
||||
timestamps: collection.timestamps !== false,
|
||||
...schemaOptions,
|
||||
...adapter.schemaOptions,
|
||||
...(adapter.collectionOptions[collection.slug]?.schemaOptions || {}),
|
||||
},
|
||||
})
|
||||
|
||||
if (config.indexSortableFields && collection.timestamps !== false) {
|
||||
if (adapter.payload.config.indexSortableFields && collection.timestamps !== false) {
|
||||
schema.index({ updatedAt: 1 })
|
||||
schema.index({ createdAt: 1 })
|
||||
}
|
||||
|
||||
@@ -1,27 +1,34 @@
|
||||
import type { SanitizedConfig } from 'payload'
|
||||
|
||||
import mongoose from 'mongoose'
|
||||
|
||||
import type { MongooseAdapter } from '../index.js'
|
||||
import type { GlobalModel } from '../types.js'
|
||||
|
||||
import getBuildQueryPlugin from '../queries/buildQuery.js'
|
||||
import buildSchema from './buildSchema.js'
|
||||
|
||||
export const buildGlobalModel = (config: SanitizedConfig): GlobalModel | null => {
|
||||
if (config.globals && config.globals.length > 0) {
|
||||
export const buildGlobalModel = (adapter: MongooseAdapter): GlobalModel | null => {
|
||||
if (adapter.payload.config.globals && adapter.payload.config.globals.length > 0) {
|
||||
const globalsSchema = new mongoose.Schema(
|
||||
{},
|
||||
{ discriminatorKey: 'globalType', minimize: false, timestamps: true },
|
||||
{
|
||||
discriminatorKey: 'globalType',
|
||||
minimize: false,
|
||||
...adapter.schemaOptions,
|
||||
...(adapter.globalsOptions.schemaOptions || {}),
|
||||
timestamps: true,
|
||||
},
|
||||
)
|
||||
|
||||
globalsSchema.plugin(getBuildQueryPlugin())
|
||||
|
||||
const Globals = mongoose.model('globals', globalsSchema, 'globals') as unknown as GlobalModel
|
||||
|
||||
Object.values(config.globals).forEach((globalConfig) => {
|
||||
const globalSchema = buildSchema(config, globalConfig.fields, {
|
||||
Object.values(adapter.payload.config.globals).forEach((globalConfig) => {
|
||||
const globalSchema = buildSchema(adapter, globalConfig.fields, {
|
||||
options: {
|
||||
minimize: false,
|
||||
...adapter.schemaOptions,
|
||||
...(adapter.globalsOptions.schemaOptions || {}),
|
||||
},
|
||||
})
|
||||
Globals.discriminator(globalConfig.slug, globalSchema)
|
||||
|
||||
@@ -19,7 +19,6 @@ import type {
|
||||
RelationshipField,
|
||||
RichTextField,
|
||||
RowField,
|
||||
SanitizedConfig,
|
||||
SanitizedLocalizationConfig,
|
||||
SelectField,
|
||||
Tab,
|
||||
@@ -37,6 +36,8 @@ import {
|
||||
tabHasName,
|
||||
} from 'payload/shared'
|
||||
|
||||
import type { MongooseAdapter } from '../index.js'
|
||||
|
||||
export type BuildSchemaOptions = {
|
||||
allowIDField?: boolean
|
||||
disableUnique?: boolean
|
||||
@@ -48,7 +49,7 @@ export type BuildSchemaOptions = {
|
||||
type FieldSchemaGenerator = (
|
||||
field: Field,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
) => void
|
||||
|
||||
@@ -87,10 +88,10 @@ const localizeSchema = (
|
||||
if (fieldIsLocalized(entity) && localization && Array.isArray(localization.locales)) {
|
||||
return {
|
||||
type: localization.localeCodes.reduce(
|
||||
(localeSchema, locale) => ({
|
||||
...localeSchema,
|
||||
[locale]: schema,
|
||||
}),
|
||||
(localeSchema, locale) => {
|
||||
localeSchema[locale] = schema
|
||||
return localeSchema
|
||||
},
|
||||
{
|
||||
_id: false,
|
||||
},
|
||||
@@ -102,7 +103,7 @@ const localizeSchema = (
|
||||
}
|
||||
|
||||
const buildSchema = (
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
configFields: Field[],
|
||||
buildSchemaOptions: BuildSchemaOptions = {},
|
||||
): Schema => {
|
||||
@@ -130,7 +131,7 @@ const buildSchema = (
|
||||
const addFieldSchema: FieldSchemaGenerator = fieldToSchemaMap[field.type]
|
||||
|
||||
if (addFieldSchema) {
|
||||
addFieldSchema(field, schema, config, buildSchemaOptions)
|
||||
addFieldSchema(field, schema, adapter, buildSchemaOptions)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -142,20 +143,22 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
array: (
|
||||
field: ArrayField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
) => {
|
||||
const baseSchema = {
|
||||
...formatBaseSchema(field, buildSchemaOptions),
|
||||
type: [
|
||||
buildSchema(config, field.fields, {
|
||||
buildSchema(adapter, field.fields, {
|
||||
allowIDField: true,
|
||||
disableUnique: buildSchemaOptions.disableUnique,
|
||||
draftsEnabled: buildSchemaOptions.draftsEnabled,
|
||||
options: {
|
||||
minimize: false,
|
||||
...(buildSchemaOptions.options || {}),
|
||||
_id: false,
|
||||
id: false,
|
||||
minimize: false,
|
||||
timestamps: false,
|
||||
},
|
||||
}),
|
||||
],
|
||||
@@ -163,36 +166,54 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
}
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
},
|
||||
blocks: (
|
||||
field: BlockField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const fieldSchema = {
|
||||
type: [new mongoose.Schema({}, { _id: false, discriminatorKey: 'blockType' })],
|
||||
type: [
|
||||
new mongoose.Schema(
|
||||
{},
|
||||
{
|
||||
_id: false,
|
||||
discriminatorKey: 'blockType',
|
||||
...(buildSchemaOptions.options || {}),
|
||||
timestamps: false,
|
||||
},
|
||||
),
|
||||
],
|
||||
default: undefined,
|
||||
}
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, fieldSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, fieldSchema, adapter.payload.config.localization),
|
||||
})
|
||||
|
||||
field.blocks.forEach((blockItem: Block) => {
|
||||
const blockSchema = new mongoose.Schema({}, { _id: false, id: false })
|
||||
const blockSchema = new mongoose.Schema(
|
||||
{},
|
||||
{
|
||||
...(buildSchemaOptions.options || {}),
|
||||
_id: false,
|
||||
id: false,
|
||||
timestamps: false,
|
||||
},
|
||||
)
|
||||
|
||||
blockItem.fields.forEach((blockField) => {
|
||||
const addFieldSchema: FieldSchemaGenerator = fieldToSchemaMap[blockField.type]
|
||||
if (addFieldSchema) {
|
||||
addFieldSchema(blockField, blockSchema, config, buildSchemaOptions)
|
||||
addFieldSchema(blockField, blockSchema, adapter, buildSchemaOptions)
|
||||
}
|
||||
})
|
||||
|
||||
if (field.localized && config.localization) {
|
||||
config.localization.localeCodes.forEach((localeCode) => {
|
||||
if (field.localized && adapter.payload.config.localization) {
|
||||
adapter.payload.config.localization.localeCodes.forEach((localeCode) => {
|
||||
// @ts-expect-error Possible incorrect typing in mongoose types, this works
|
||||
schema.path(`${field.name}.${localeCode}`).discriminator(blockItem.slug, blockSchema)
|
||||
})
|
||||
@@ -205,69 +226,69 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
checkbox: (
|
||||
field: CheckboxField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const baseSchema = { ...formatBaseSchema(field, buildSchemaOptions), type: Boolean }
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
},
|
||||
code: (
|
||||
field: CodeField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const baseSchema = { ...formatBaseSchema(field, buildSchemaOptions), type: String }
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
},
|
||||
collapsible: (
|
||||
field: CollapsibleField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
field.fields.forEach((subField: Field) => {
|
||||
const addFieldSchema: FieldSchemaGenerator = fieldToSchemaMap[subField.type]
|
||||
|
||||
if (addFieldSchema) {
|
||||
addFieldSchema(subField, schema, config, buildSchemaOptions)
|
||||
addFieldSchema(subField, schema, adapter, buildSchemaOptions)
|
||||
}
|
||||
})
|
||||
},
|
||||
date: (
|
||||
field: DateField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const baseSchema = { ...formatBaseSchema(field, buildSchemaOptions), type: Date }
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
},
|
||||
email: (
|
||||
field: EmailField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const baseSchema = { ...formatBaseSchema(field, buildSchemaOptions), type: String }
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
},
|
||||
group: (
|
||||
field: GroupField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const formattedBaseSchema = formatBaseSchema(field, buildSchemaOptions)
|
||||
@@ -280,26 +301,28 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
|
||||
const baseSchema = {
|
||||
...formattedBaseSchema,
|
||||
type: buildSchema(config, field.fields, {
|
||||
type: buildSchema(adapter, field.fields, {
|
||||
disableUnique: buildSchemaOptions.disableUnique,
|
||||
draftsEnabled: buildSchemaOptions.draftsEnabled,
|
||||
indexSortableFields,
|
||||
options: {
|
||||
minimize: false,
|
||||
...(buildSchemaOptions.options || {}),
|
||||
_id: false,
|
||||
id: false,
|
||||
minimize: false,
|
||||
timestamps: false,
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
},
|
||||
json: (
|
||||
field: JSONField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const baseSchema = {
|
||||
@@ -308,13 +331,13 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
}
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
},
|
||||
number: (
|
||||
field: NumberField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const baseSchema = {
|
||||
@@ -323,13 +346,13 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
}
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
},
|
||||
point: (
|
||||
field: PointField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const baseSchema: SchemaTypeOptions<unknown> = {
|
||||
@@ -348,7 +371,7 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
}
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
|
||||
if (field.index === true || field.index === undefined) {
|
||||
@@ -357,8 +380,8 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
indexOptions.sparse = true
|
||||
indexOptions.unique = true
|
||||
}
|
||||
if (field.localized && config.localization) {
|
||||
config.localization.locales.forEach((locale) => {
|
||||
if (field.localized && adapter.payload.config.localization) {
|
||||
adapter.payload.config.localization.locales.forEach((locale) => {
|
||||
schema.index({ [`${field.name}.${locale.code}`]: '2dsphere' }, indexOptions)
|
||||
})
|
||||
} else {
|
||||
@@ -369,7 +392,7 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
radio: (
|
||||
field: RadioField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const baseSchema = {
|
||||
@@ -382,21 +405,21 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
}
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
},
|
||||
relationship: (
|
||||
field: RelationshipField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
) => {
|
||||
const hasManyRelations = Array.isArray(field.relationTo)
|
||||
let schemaToReturn: { [key: string]: any } = {}
|
||||
|
||||
if (field.localized && config.localization) {
|
||||
if (field.localized && adapter.payload.config.localization) {
|
||||
schemaToReturn = {
|
||||
type: config.localization.localeCodes.reduce((locales, locale) => {
|
||||
type: adapter.payload.config.localization.localeCodes.reduce((locales, locale) => {
|
||||
let localeSchema: { [key: string]: any } = {}
|
||||
|
||||
if (hasManyRelations) {
|
||||
@@ -465,7 +488,7 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
richText: (
|
||||
field: RichTextField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const baseSchema = {
|
||||
@@ -474,27 +497,27 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
}
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
},
|
||||
row: (
|
||||
field: RowField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
field.fields.forEach((subField: Field) => {
|
||||
const addFieldSchema: FieldSchemaGenerator = fieldToSchemaMap[subField.type]
|
||||
|
||||
if (addFieldSchema) {
|
||||
addFieldSchema(subField, schema, config, buildSchemaOptions)
|
||||
addFieldSchema(subField, schema, adapter, buildSchemaOptions)
|
||||
}
|
||||
})
|
||||
},
|
||||
select: (
|
||||
field: SelectField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const baseSchema = {
|
||||
@@ -514,39 +537,41 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
[field.name]: localizeSchema(
|
||||
field,
|
||||
field.hasMany ? [baseSchema] : baseSchema,
|
||||
config.localization,
|
||||
adapter.payload.config.localization,
|
||||
),
|
||||
})
|
||||
},
|
||||
tabs: (
|
||||
field: TabsField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
field.tabs.forEach((tab) => {
|
||||
if (tabHasName(tab)) {
|
||||
const baseSchema = {
|
||||
type: buildSchema(config, tab.fields, {
|
||||
type: buildSchema(adapter, tab.fields, {
|
||||
disableUnique: buildSchemaOptions.disableUnique,
|
||||
draftsEnabled: buildSchemaOptions.draftsEnabled,
|
||||
options: {
|
||||
minimize: false,
|
||||
...(buildSchemaOptions.options || {}),
|
||||
_id: false,
|
||||
id: false,
|
||||
minimize: false,
|
||||
timestamps: false,
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
schema.add({
|
||||
[tab.name]: localizeSchema(tab, baseSchema, config.localization),
|
||||
[tab.name]: localizeSchema(tab, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
} else {
|
||||
tab.fields.forEach((subField: Field) => {
|
||||
const addFieldSchema: FieldSchemaGenerator = fieldToSchemaMap[subField.type]
|
||||
|
||||
if (addFieldSchema) {
|
||||
addFieldSchema(subField, schema, config, buildSchemaOptions)
|
||||
addFieldSchema(subField, schema, adapter, buildSchemaOptions)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -555,7 +580,7 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
text: (
|
||||
field: TextField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const baseSchema = {
|
||||
@@ -564,25 +589,25 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
}
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
},
|
||||
textarea: (
|
||||
field: TextareaField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const baseSchema = { ...formatBaseSchema(field, buildSchemaOptions), type: String }
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
},
|
||||
upload: (
|
||||
field: UploadField,
|
||||
schema: Schema,
|
||||
config: SanitizedConfig,
|
||||
adapter: MongooseAdapter,
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const baseSchema = {
|
||||
@@ -592,7 +617,7 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
}
|
||||
|
||||
schema.add({
|
||||
[field.name]: localizeSchema(field, baseSchema, config.localization),
|
||||
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ type Args<T> = {
|
||||
* The original siblingData (not modified by any hooks)
|
||||
*/
|
||||
siblingDoc: JsonObject
|
||||
siblingDocKeys: Set<string>
|
||||
}
|
||||
|
||||
// This function is responsible for the following actions, in order:
|
||||
@@ -57,6 +58,7 @@ export const promise = async <T>({
|
||||
req,
|
||||
siblingData,
|
||||
siblingDoc,
|
||||
siblingDocKeys,
|
||||
}: Args<T>): Promise<void> => {
|
||||
const { path: fieldPath, schemaPath: fieldSchemaPath } = getFieldPaths({
|
||||
field,
|
||||
@@ -65,6 +67,15 @@ export const promise = async <T>({
|
||||
})
|
||||
|
||||
if (fieldAffectsData(field)) {
|
||||
// Remove the key from siblingDocKeys
|
||||
// the goal is to keep any existing data present
|
||||
// before updating, for users that want to maintain
|
||||
// external data in the same collections as Payload manages,
|
||||
// without having fields defined for them
|
||||
if (siblingDocKeys.has(field.name)) {
|
||||
siblingDocKeys.delete(field.name)
|
||||
}
|
||||
|
||||
if (field.name === 'id') {
|
||||
if (field.type === 'number' && typeof siblingData[field.name] === 'string') {
|
||||
const value = siblingData[field.name] as string
|
||||
@@ -308,6 +319,7 @@ export const promise = async <T>({
|
||||
schemaPath: fieldSchemaPath,
|
||||
siblingData: groupData as JsonObject,
|
||||
siblingDoc: groupDoc as JsonObject,
|
||||
siblingDocKeys,
|
||||
})
|
||||
|
||||
break
|
||||
@@ -335,6 +347,7 @@ export const promise = async <T>({
|
||||
schemaPath: fieldSchemaPath,
|
||||
siblingData: row as JsonObject,
|
||||
siblingDoc: getExistingRowDoc(row as JsonObject, siblingDoc[field.name]),
|
||||
siblingDocKeys,
|
||||
}),
|
||||
)
|
||||
})
|
||||
@@ -372,6 +385,7 @@ export const promise = async <T>({
|
||||
schemaPath: fieldSchemaPath,
|
||||
siblingData: row as JsonObject,
|
||||
siblingDoc: rowSiblingDoc,
|
||||
siblingDocKeys,
|
||||
}),
|
||||
)
|
||||
}
|
||||
@@ -399,6 +413,7 @@ export const promise = async <T>({
|
||||
schemaPath: fieldSchemaPath,
|
||||
siblingData,
|
||||
siblingDoc,
|
||||
siblingDocKeys,
|
||||
})
|
||||
|
||||
break
|
||||
@@ -407,7 +422,10 @@ export const promise = async <T>({
|
||||
case 'tab': {
|
||||
let tabSiblingData
|
||||
let tabSiblingDoc
|
||||
if (tabHasName(field)) {
|
||||
|
||||
const isNamedTab = tabHasName(field)
|
||||
|
||||
if (isNamedTab) {
|
||||
if (typeof siblingData[field.name] !== 'object') siblingData[field.name] = {}
|
||||
if (typeof siblingDoc[field.name] !== 'object') siblingDoc[field.name] = {}
|
||||
|
||||
@@ -433,6 +451,7 @@ export const promise = async <T>({
|
||||
schemaPath: fieldSchemaPath,
|
||||
siblingData: tabSiblingData,
|
||||
siblingDoc: tabSiblingDoc,
|
||||
siblingDocKeys: isNamedTab ? undefined : siblingDocKeys,
|
||||
})
|
||||
|
||||
break
|
||||
@@ -454,6 +473,7 @@ export const promise = async <T>({
|
||||
schemaPath: fieldSchemaPath,
|
||||
siblingData,
|
||||
siblingDoc,
|
||||
siblingDocKeys,
|
||||
})
|
||||
|
||||
break
|
||||
|
||||
@@ -26,6 +26,7 @@ type Args<T> = {
|
||||
* The original siblingData (not modified by any hooks)
|
||||
*/
|
||||
siblingDoc: JsonObject
|
||||
siblingDocKeys?: Set<string>
|
||||
}
|
||||
|
||||
export const traverseFields = async <T>({
|
||||
@@ -43,8 +44,11 @@ export const traverseFields = async <T>({
|
||||
schemaPath,
|
||||
siblingData,
|
||||
siblingDoc,
|
||||
siblingDocKeys: incomingSiblingDocKeys,
|
||||
}: Args<T>): Promise<void> => {
|
||||
const promises = []
|
||||
const siblingDocKeys = incomingSiblingDocKeys || new Set(Object.keys(siblingDoc))
|
||||
|
||||
fields.forEach((field) => {
|
||||
promises.push(
|
||||
promise({
|
||||
@@ -62,8 +66,18 @@ export const traverseFields = async <T>({
|
||||
req,
|
||||
siblingData,
|
||||
siblingDoc,
|
||||
siblingDocKeys,
|
||||
}),
|
||||
)
|
||||
})
|
||||
await Promise.all(promises)
|
||||
|
||||
// For any siblingDocKeys that have not been deleted,
|
||||
// we will move the data to the siblingData object
|
||||
// to preserve it
|
||||
siblingDocKeys.forEach((key) => {
|
||||
if (!['createdAt', 'globalType', 'id', 'updatedAt'].includes(key)) {
|
||||
siblingData[key] = siblingDoc[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,264 +1,308 @@
|
||||
import type { Config } from 'payload'
|
||||
|
||||
import { mongooseAdapter } from '@payloadcms/db-mongodb'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import path from 'path'
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
|
||||
import { devUser } from '../credentials.js'
|
||||
|
||||
export default buildConfigWithDefaults({
|
||||
collections: [
|
||||
{
|
||||
slug: 'posts',
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'throwAfterChange',
|
||||
type: 'checkbox',
|
||||
defaultValue: false,
|
||||
hooks: {
|
||||
afterChange: [
|
||||
({ value }) => {
|
||||
if (value) {
|
||||
throw new Error('throw after change')
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
const [testSuiteDir] = process.argv.slice(4)
|
||||
const migrationDir = path.resolve(
|
||||
(process.env.PAYLOAD_CONFIG_PATH
|
||||
? path.join(process.env.PAYLOAD_CONFIG_PATH, '..')
|
||||
: testSuiteDir) || dirname,
|
||||
'migrations',
|
||||
)
|
||||
|
||||
const createDatabaseTestConfig = async () => {
|
||||
const config: Partial<Config> = {
|
||||
collections: [
|
||||
{
|
||||
slug: 'posts',
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'throwAfterChange',
|
||||
type: 'checkbox',
|
||||
defaultValue: false,
|
||||
hooks: {
|
||||
afterChange: [
|
||||
({ value }) => {
|
||||
if (value) {
|
||||
throw new Error('throw after change')
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
hooks: {
|
||||
beforeOperation: [
|
||||
({ args, operation, req }) => {
|
||||
if (operation === 'update') {
|
||||
const defaultIDType = req.payload.db.defaultIDType
|
||||
|
||||
if (defaultIDType === 'number' && typeof args.id === 'string') {
|
||||
throw new Error('ID was not sanitized to a number properly')
|
||||
}
|
||||
}
|
||||
|
||||
return args
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
slug: 'relation-a',
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'relationship',
|
||||
type: 'relationship',
|
||||
relationTo: 'relation-b',
|
||||
},
|
||||
{
|
||||
name: 'richText',
|
||||
type: 'richText',
|
||||
},
|
||||
],
|
||||
labels: {
|
||||
plural: 'Relation As',
|
||||
singular: 'Relation A',
|
||||
},
|
||||
},
|
||||
{
|
||||
slug: 'relation-b',
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'relationship',
|
||||
type: 'relationship',
|
||||
relationTo: 'relation-a',
|
||||
},
|
||||
{
|
||||
name: 'richText',
|
||||
type: 'richText',
|
||||
},
|
||||
],
|
||||
labels: {
|
||||
plural: 'Relation Bs',
|
||||
singular: 'Relation B',
|
||||
},
|
||||
},
|
||||
{
|
||||
slug: 'pg-migrations',
|
||||
fields: [
|
||||
{
|
||||
name: 'relation1',
|
||||
type: 'relationship',
|
||||
relationTo: 'relation-a',
|
||||
},
|
||||
{
|
||||
name: 'myArray',
|
||||
type: 'array',
|
||||
fields: [
|
||||
{
|
||||
name: 'relation2',
|
||||
type: 'relationship',
|
||||
relationTo: 'relation-b',
|
||||
},
|
||||
{
|
||||
name: 'mySubArray',
|
||||
type: 'array',
|
||||
fields: [
|
||||
{
|
||||
name: 'relation3',
|
||||
type: 'relationship',
|
||||
localized: true,
|
||||
relationTo: 'relation-b',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
hooks: {
|
||||
beforeOperation: [
|
||||
({ args, operation, req }) => {
|
||||
if (operation === 'update') {
|
||||
const defaultIDType = req.payload.db.defaultIDType
|
||||
|
||||
if (defaultIDType === 'number' && typeof args.id === 'string') {
|
||||
throw new Error('ID was not sanitized to a number properly')
|
||||
}
|
||||
}
|
||||
|
||||
return args
|
||||
{
|
||||
name: 'myGroup',
|
||||
type: 'group',
|
||||
fields: [
|
||||
{
|
||||
name: 'relation4',
|
||||
type: 'relationship',
|
||||
localized: true,
|
||||
relationTo: 'relation-b',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'myBlocks',
|
||||
type: 'blocks',
|
||||
blocks: [
|
||||
{
|
||||
slug: 'myBlock',
|
||||
fields: [
|
||||
{
|
||||
name: 'relation5',
|
||||
type: 'relationship',
|
||||
relationTo: 'relation-a',
|
||||
},
|
||||
{
|
||||
name: 'relation6',
|
||||
type: 'relationship',
|
||||
localized: true,
|
||||
relationTo: 'relation-b',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
versions: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
slug: 'relation-a',
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
{
|
||||
slug: 'custom-schema',
|
||||
dbName: 'customs',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'localizedText',
|
||||
type: 'text',
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'relationship',
|
||||
type: 'relationship',
|
||||
hasMany: true,
|
||||
relationTo: 'relation-a',
|
||||
},
|
||||
{
|
||||
name: 'select',
|
||||
type: 'select',
|
||||
dbName: ({ tableName }) => `${tableName}_customSelect`,
|
||||
enumName: 'selectEnum',
|
||||
hasMany: true,
|
||||
options: ['a', 'b', 'c'],
|
||||
},
|
||||
{
|
||||
name: 'radio',
|
||||
type: 'select',
|
||||
enumName: 'radioEnum',
|
||||
options: ['a', 'b', 'c'],
|
||||
},
|
||||
{
|
||||
name: 'array',
|
||||
type: 'array',
|
||||
dbName: 'customArrays',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'localizedText',
|
||||
type: 'text',
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'blocks',
|
||||
type: 'blocks',
|
||||
blocks: [
|
||||
{
|
||||
slug: 'block',
|
||||
dbName: 'customBlocks',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'localizedText',
|
||||
type: 'text',
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
versions: {
|
||||
drafts: true,
|
||||
},
|
||||
{
|
||||
name: 'richText',
|
||||
type: 'richText',
|
||||
},
|
||||
],
|
||||
labels: {
|
||||
plural: 'Relation As',
|
||||
singular: 'Relation A',
|
||||
},
|
||||
},
|
||||
{
|
||||
slug: 'relation-b',
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'relationship',
|
||||
type: 'relationship',
|
||||
relationTo: 'relation-a',
|
||||
},
|
||||
{
|
||||
name: 'richText',
|
||||
type: 'richText',
|
||||
},
|
||||
],
|
||||
labels: {
|
||||
plural: 'Relation Bs',
|
||||
singular: 'Relation B',
|
||||
],
|
||||
globals: [
|
||||
{
|
||||
slug: 'global',
|
||||
dbName: 'customGlobal',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
versions: true,
|
||||
},
|
||||
],
|
||||
localization: {
|
||||
defaultLocale: 'en',
|
||||
locales: ['en', 'es'],
|
||||
},
|
||||
{
|
||||
slug: 'pg-migrations',
|
||||
fields: [
|
||||
{
|
||||
name: 'relation1',
|
||||
type: 'relationship',
|
||||
relationTo: 'relation-a',
|
||||
onInit: async (payload) => {
|
||||
await payload.create({
|
||||
collection: 'users',
|
||||
data: {
|
||||
email: devUser.email,
|
||||
password: devUser.password,
|
||||
},
|
||||
{
|
||||
name: 'myArray',
|
||||
type: 'array',
|
||||
fields: [
|
||||
{
|
||||
name: 'relation2',
|
||||
type: 'relationship',
|
||||
relationTo: 'relation-b',
|
||||
},
|
||||
{
|
||||
name: 'mySubArray',
|
||||
type: 'array',
|
||||
fields: [
|
||||
{
|
||||
name: 'relation3',
|
||||
type: 'relationship',
|
||||
localized: true,
|
||||
relationTo: 'relation-b',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'myGroup',
|
||||
type: 'group',
|
||||
fields: [
|
||||
{
|
||||
name: 'relation4',
|
||||
type: 'relationship',
|
||||
localized: true,
|
||||
relationTo: 'relation-b',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'myBlocks',
|
||||
type: 'blocks',
|
||||
blocks: [
|
||||
{
|
||||
slug: 'myBlock',
|
||||
fields: [
|
||||
{
|
||||
name: 'relation5',
|
||||
type: 'relationship',
|
||||
relationTo: 'relation-a',
|
||||
},
|
||||
{
|
||||
name: 'relation6',
|
||||
type: 'relationship',
|
||||
localized: true,
|
||||
relationTo: 'relation-b',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
versions: true,
|
||||
})
|
||||
},
|
||||
{
|
||||
slug: 'custom-schema',
|
||||
dbName: 'customs',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'localizedText',
|
||||
type: 'text',
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'relationship',
|
||||
type: 'relationship',
|
||||
hasMany: true,
|
||||
relationTo: 'relation-a',
|
||||
},
|
||||
{
|
||||
name: 'select',
|
||||
type: 'select',
|
||||
dbName: ({ tableName }) => `${tableName}_customSelect`,
|
||||
enumName: 'selectEnum',
|
||||
hasMany: true,
|
||||
options: ['a', 'b', 'c'],
|
||||
},
|
||||
{
|
||||
name: 'radio',
|
||||
type: 'select',
|
||||
enumName: 'radioEnum',
|
||||
options: ['a', 'b', 'c'],
|
||||
},
|
||||
{
|
||||
name: 'array',
|
||||
type: 'array',
|
||||
dbName: 'customArrays',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'localizedText',
|
||||
type: 'text',
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'blocks',
|
||||
type: 'blocks',
|
||||
blocks: [
|
||||
{
|
||||
slug: 'block',
|
||||
dbName: 'customBlocks',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'localizedText',
|
||||
type: 'text',
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
versions: {
|
||||
drafts: true,
|
||||
},
|
||||
typescript: {
|
||||
outputFile: path.resolve(dirname, 'payload-types.ts'),
|
||||
},
|
||||
],
|
||||
globals: [
|
||||
{
|
||||
slug: 'global',
|
||||
dbName: 'customGlobal',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
versions: true,
|
||||
},
|
||||
],
|
||||
localization: {
|
||||
defaultLocale: 'en',
|
||||
locales: ['en', 'es'],
|
||||
},
|
||||
onInit: async (payload) => {
|
||||
await payload.create({
|
||||
collection: 'users',
|
||||
data: {
|
||||
email: devUser.email,
|
||||
password: devUser.password,
|
||||
}
|
||||
|
||||
const configWithDefaults = await buildConfigWithDefaults(config)
|
||||
|
||||
if (
|
||||
process.env.PAYLOAD_DATABASE === 'mongoose' ||
|
||||
process.env.PAYLOAD_DATABASE === 'mongodb' ||
|
||||
!process.env.PAYLOAD_DATABASE
|
||||
) {
|
||||
configWithDefaults.db = mongooseAdapter({
|
||||
migrationDir,
|
||||
url:
|
||||
process.env.MONGODB_MEMORY_SERVER_URI ||
|
||||
process.env.DATABASE_URI ||
|
||||
'mongodb://127.0.0.1/payloadtests',
|
||||
// Disable strict mode for Mongoose
|
||||
schemaOptions: {
|
||||
strict: false,
|
||||
},
|
||||
})
|
||||
},
|
||||
typescript: {
|
||||
outputFile: path.resolve(dirname, 'payload-types.ts'),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return configWithDefaults
|
||||
}
|
||||
|
||||
export default createDatabaseTestConfig()
|
||||
|
||||
export const postDoc = {
|
||||
title: 'test post',
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { MongooseAdapter } from '@payloadcms/db-mongodb'
|
||||
import type { PostgresAdapter } from '@payloadcms/db-postgres/types'
|
||||
import type { NextRESTClient } from 'helpers/NextRESTClient.js'
|
||||
import type { Payload, PayloadRequest, TypeWithID } from 'payload'
|
||||
@@ -6,6 +7,9 @@ import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { commitTransaction, initTransaction } from 'payload'
|
||||
import { fileURLToPath } from 'url'
|
||||
import { v4 as uuid } from 'uuid'
|
||||
|
||||
import type { CustomSchema } from './payload-types.js'
|
||||
|
||||
import { devUser } from '../credentials.js'
|
||||
import { initPayloadInt } from '../helpers/initPayloadInt.js'
|
||||
@@ -461,4 +465,109 @@ describe('database', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('existing data', () => {
|
||||
let existingDataDoc: CustomSchema
|
||||
|
||||
beforeAll(async () => {
|
||||
if (payload.db.name === 'mongoose') {
|
||||
const Model = payload.db.collections['custom-schema']
|
||||
|
||||
const [doc] = await Model.create([
|
||||
{
|
||||
array: [
|
||||
{
|
||||
id: uuid(),
|
||||
localizedText: {
|
||||
en: 'goodbye',
|
||||
},
|
||||
noFieldDefined: 'hi',
|
||||
text: 'hello',
|
||||
},
|
||||
],
|
||||
blocks: [
|
||||
{
|
||||
id: uuid(),
|
||||
blockType: 'block',
|
||||
localizedText: {
|
||||
en: 'goodbye',
|
||||
},
|
||||
noFieldDefined: 'hi',
|
||||
text: 'hello',
|
||||
},
|
||||
],
|
||||
localizedText: {
|
||||
en: 'goodbye',
|
||||
},
|
||||
noFieldDefined: 'hi',
|
||||
text: 'hello',
|
||||
},
|
||||
])
|
||||
|
||||
const result = JSON.parse(JSON.stringify(doc))
|
||||
result.id = result._id
|
||||
existingDataDoc = result
|
||||
}
|
||||
})
|
||||
|
||||
it('should allow storage of existing data', async () => {
|
||||
expect(payload.db).toBeDefined()
|
||||
|
||||
if (payload.db.name === 'mongoose') {
|
||||
expect(existingDataDoc.noFieldDefined).toStrictEqual('hi')
|
||||
expect(existingDataDoc.array[0].noFieldDefined).toStrictEqual('hi')
|
||||
expect(existingDataDoc.blocks[0].noFieldDefined).toStrictEqual('hi')
|
||||
|
||||
const docWithExistingData = await payload.findByID({
|
||||
id: existingDataDoc.id,
|
||||
collection: 'custom-schema',
|
||||
})
|
||||
|
||||
expect(docWithExistingData.noFieldDefined).toStrictEqual('hi')
|
||||
expect(docWithExistingData.array[0].noFieldDefined).toStrictEqual('hi')
|
||||
expect(docWithExistingData.blocks[0].noFieldDefined).toStrictEqual('hi')
|
||||
}
|
||||
})
|
||||
|
||||
it('should maintain existing data while updating', async () => {
|
||||
expect(payload.db).toBeDefined()
|
||||
|
||||
if (payload.db.name === 'mongoose') {
|
||||
const result = await payload.update({
|
||||
id: existingDataDoc.id,
|
||||
collection: 'custom-schema',
|
||||
data: {
|
||||
array: [
|
||||
{
|
||||
id: existingDataDoc.array[0].id,
|
||||
localizedText: 'adios',
|
||||
text: 'hola',
|
||||
},
|
||||
],
|
||||
blocks: [
|
||||
{
|
||||
id: existingDataDoc.blocks[0].id,
|
||||
blockType: 'block',
|
||||
localizedText: 'adios',
|
||||
text: 'hola',
|
||||
},
|
||||
],
|
||||
localizedText: 'adios',
|
||||
text: 'hola',
|
||||
},
|
||||
})
|
||||
|
||||
expect(result.text).toStrictEqual('hola')
|
||||
expect(result.array[0].text).toStrictEqual('hola')
|
||||
expect(result.blocks[0].text).toStrictEqual('hola')
|
||||
expect(result.localizedText).toStrictEqual('adios')
|
||||
expect(result.array[0].localizedText).toStrictEqual('adios')
|
||||
expect(result.blocks[0].localizedText).toStrictEqual('adios')
|
||||
|
||||
expect(result.noFieldDefined).toStrictEqual('hi')
|
||||
expect(result.array[0].noFieldDefined).toStrictEqual('hi')
|
||||
expect(result.blocks[0].noFieldDefined).toStrictEqual('hi')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user