feat: custom db naming for postgres and mongodb (#5697)
This commit is contained in:
@@ -30,6 +30,7 @@ It's often best practice to write your Collections in separate files and then im
|
||||
| **`typescript`** | An object with property `interface` as the text used in schema generation. Auto-generated from slug if not defined. |
|
||||
| **`disableDuplicate`** | When true, do not show the "Duplicate" button while editing documents within this collection and prevent `duplicate` from all APIs. |
|
||||
| **`defaultSort`** | Pass a top-level field to sort by default in the collection List view. Prefix the name of the field with a minus symbol ("-") to sort in descending order. |
|
||||
| **`dbName`** | Custom table or collection name depending on the database adapter. Auto-generated from slug if not defined.
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
@@ -26,6 +26,7 @@ As with Collection configs, it's often best practice to write your Globals in se
|
||||
| **`graphQL.name`** | Text used in schema generation. Auto-generated from slug if not defined. |
|
||||
| **`typescript`** | An object with property `interface` as the text used in schema generation. Auto-generated from slug if not defined. |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`dbName`** | Custom table or collection name for this global depending on the database adapter. Auto-generated from slug if not defined.
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
|
||||
@@ -38,12 +38,18 @@ export default buildConfig({
|
||||
|
||||
### Options
|
||||
|
||||
| Option | Description |
|
||||
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `pool` | [Pool connection options](https://orm.drizzle.team/docs/quick-postgresql/node-postgres) that will be passed to Drizzle and `node-postgres`. |
|
||||
| `push` | Disable Drizzle's [`db push`](https://orm.drizzle.team/kit-docs/overview#prototyping-with-db-push) in development mode. By default, `push` is enabled for development mode only. |
|
||||
| `migrationDir` | Customize the directory that migrations are stored. |
|
||||
| Option | Description |
|
||||
|-----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `pool` \* | [Pool connection options](https://orm.drizzle.team/docs/quick-postgresql/node-postgres) that will be passed to Drizzle and `node-postgres`. |
|
||||
| `push` | Disable Drizzle's [`db push`](https://orm.drizzle.team/kit-docs/overview#prototyping-with-db-push) in development mode. By default, `push` is enabled for development mode only. |
|
||||
| `migrationDir` | Customize the directory that migrations are stored. |
|
||||
| `logger` | The instance of the logger to be passed to drizzle. By default Payload's will be used. |
|
||||
| `schemaName` | A string for the postgres schema to use, defaults to 'public'. |
|
||||
| `localesSuffix` | A string appended to the end of table names for storing localized fields. Default is '_locales'. |
|
||||
| `relationshipsSuffix` | A string appended to the end of table names for storing relationships. Default is '_rels'. |
|
||||
| `versionsSuffix` | A string appended to the end of table names for storing versions. Defaults to '_v'. |
|
||||
|
||||
|
||||
|
||||
### Access to Drizzle
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ keywords: array, fields, config, configuration, documentation, Content Managemen
|
||||
| **`admin`** | Admin-specific configuration. See below for [more detail](#admin-config). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`interfaceName`** | Create a top level, reusable [Typescript interface](/docs/typescript/generating-types#custom-field-interfaces) & [GraphQL type](/docs/graphql/graphql-schema#custom-field-schemas). |
|
||||
| **`dbName`** | Custom table name for the field when using SQL database adapter ([Postgres](/docs/database/postgres)). Auto-generated from name if not defined. |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
|
||||
@@ -80,6 +80,7 @@ Blocks are defined as separate configs of their own.
|
||||
| **`imageAltText`** | Customize this block's image thumbnail alt text. |
|
||||
| **`interfaceName`** | Create a top level, reusable [Typescript interface](/docs/typescript/generating-types#custom-field-interfaces) & [GraphQL type](/docs/graphql/graphql-schema#custom-field-schemas). |
|
||||
| **`graphQL.singularName`** | Text to use for the GraphQL schema name. Auto-generated from slug if not defined. NOTE: this is set for deprecation, prefer `interfaceName`. |
|
||||
| **`dbName`** | Custom table name for this block type when using SQL database adapter ([Postgres](/docs/database/postgres)). Auto-generated from slug if not defined.
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
|
||||
#### Auto-generated data per block
|
||||
|
||||
@@ -20,27 +20,27 @@ keywords: number, fields, config, configuration, documentation, Content Manageme
|
||||
|
||||
### Config
|
||||
|
||||
| Option | Description |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as a field label in the Admin panel or an object with keys for each language. |
|
||||
| **`min`** | Minimum value accepted. Used in the default `validation` function. |
|
||||
| **`max`** | Maximum value accepted. Used in the default `validation` function. |
|
||||
| **`hasMany`** | Makes this field an ordered array of numbers instead of just a single number. |
|
||||
| **`minRows`** | Minimum number of numbers in the numbers array, if `hasMany` is set to true. |
|
||||
| **`maxRows`** | Maximum number of numbers in the numbers array, if `hasMany` is set to true. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/config), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide field-based hooks to control logic for this field. [More](/docs/fields/overview#field-level-hooks) |
|
||||
| **`access`** | Provide field-based access control to denote what users can see and do with this field's data. [More](/docs/fields/overview#field-level-access-control) |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. See below for [more detail](#admin-config). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| Option | Description |
|
||||
|--------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **`name`** \* | To be used as the property name when stored and retrieved from the database. [More](/docs/fields/overview#field-names) |
|
||||
| **`label`** | Text used as a field label in the Admin panel or an object with keys for each language. |
|
||||
| **`min`** | Minimum value accepted. Used in the default `validation` function. |
|
||||
| **`max`** | Maximum value accepted. Used in the default `validation` function. |
|
||||
| **`hasMany`** | Makes this field an ordered array of numbers instead of just a single number. |
|
||||
| **`minRows`** | Minimum number of numbers in the numbers array, if `hasMany` is set to true. |
|
||||
| **`maxRows`** | Maximum number of numbers in the numbers array, if `hasMany` is set to true. |
|
||||
| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |
|
||||
| **`index`** | Build an [index](/docs/database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |
|
||||
| **`validate`** | Provide a custom validation function that will be executed on both the Admin panel and the backend. [More](/docs/fields/overview#validation) |
|
||||
| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](/docs/authentication/config), include its data in the user JWT. |
|
||||
| **`hooks`** | Provide field-based hooks to control logic for this field. [More](/docs/fields/overview#field-level-hooks) |
|
||||
| **`access`** | Provide field-based access control to denote what users can see and do with this field's data. [More](/docs/fields/overview#field-level-access-control) |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. See below for [more detail](#admin-config). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ keywords: radio, fields, config, configuration, documentation, Content Managemen
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. See below for [more detail](#admin-config). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`enumName`** | Custom enum name for this field when using SQL database adapter ([Postgres](/docs/database/postgres)). Auto-generated from name if not defined.
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
|
||||
@@ -38,6 +38,8 @@ keywords: select, multi-select, fields, config, configuration, documentation, Co
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. See the [default field admin config](/docs/fields/overview#admin-config) for more details. |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`enumName`** | Custom enum name for this field when using SQL database adapter ([Postgres](/docs/database/postgres)). Auto-generated from name if not defined. |
|
||||
| **`dbName`** | Custom table name (if `hasMany` set to `true`) for this field when using SQL database adapter ([Postgres](/docs/database/postgres)). Auto-generated from name if not defined. |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
"get-port": "5.1.1",
|
||||
"http-status": "1.6.2",
|
||||
"mongoose": "6.12.3",
|
||||
"mongoose-aggregate-paginate-v2": "1.0.6",
|
||||
"mongoose-paginate-v2": "1.7.22",
|
||||
"prompts": "2.4.2",
|
||||
"uuid": "9.0.0"
|
||||
|
||||
@@ -5,11 +5,7 @@ import type { SanitizedCollectionConfig } from 'payload/types'
|
||||
|
||||
import mongoose from 'mongoose'
|
||||
import paginate from 'mongoose-paginate-v2'
|
||||
import {
|
||||
buildVersionCollectionFields,
|
||||
buildVersionGlobalFields,
|
||||
getVersionsModelName,
|
||||
} from 'payload/versions'
|
||||
import { buildVersionCollectionFields, buildVersionGlobalFields } from 'payload/versions'
|
||||
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
import type { CollectionModel } from './types.js'
|
||||
@@ -18,13 +14,14 @@ import buildCollectionSchema from './models/buildCollectionSchema.js'
|
||||
import { buildGlobalModel } from './models/buildGlobalModel.js'
|
||||
import buildSchema from './models/buildSchema.js'
|
||||
import getBuildQueryPlugin from './queries/buildQuery.js'
|
||||
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)
|
||||
|
||||
if (collection.versions) {
|
||||
const versionModelName = getVersionsModelName(collection)
|
||||
const versionModelName = getDBName({ config: collection, versions: true })
|
||||
|
||||
const versionCollectionFields = buildVersionCollectionFields(collection)
|
||||
|
||||
@@ -54,7 +51,7 @@ export const init: Init = function init(this: MongooseAdapter) {
|
||||
}
|
||||
|
||||
const model = mongoose.model(
|
||||
collection.slug,
|
||||
getDBName({ config: collection }),
|
||||
schema,
|
||||
this.autoPluralization === true ? undefined : collection.slug,
|
||||
) as CollectionModel
|
||||
@@ -72,7 +69,7 @@ export const init: Init = function init(this: MongooseAdapter) {
|
||||
|
||||
this.payload.config.globals.forEach((global) => {
|
||||
if (global.versions) {
|
||||
const versionModelName = getVersionsModelName(global)
|
||||
const versionModelName = getDBName({ config: global, versions: true })
|
||||
|
||||
const versionGlobalFields = buildVersionGlobalFields(global)
|
||||
|
||||
|
||||
@@ -363,7 +363,7 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
}
|
||||
if (field.localized && config.localization) {
|
||||
config.localization.locales.forEach((locale) => {
|
||||
schema.index({ [`${field.name}.${locale}`]: '2dsphere' }, indexOptions)
|
||||
schema.index({ [`${field.name}.${locale.code}`]: '2dsphere' }, indexOptions)
|
||||
})
|
||||
} else {
|
||||
schema.index({ [field.name]: '2dsphere' }, indexOptions)
|
||||
|
||||
41
packages/db-mongodb/src/utilities/getDBName.ts
Normal file
41
packages/db-mongodb/src/utilities/getDBName.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import type { DBIdentifierName } from 'payload/database'
|
||||
|
||||
type Args = {
|
||||
config: {
|
||||
dbName?: DBIdentifierName
|
||||
enumName?: DBIdentifierName
|
||||
name?: string
|
||||
slug?: string
|
||||
}
|
||||
locales?: boolean
|
||||
target?: 'dbName' | 'enumName'
|
||||
versions?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to name database enums and collections
|
||||
* Returns the collection or enum name for a given entity
|
||||
*/
|
||||
export const getDBName = ({
|
||||
config: { name, slug },
|
||||
config,
|
||||
target = 'dbName',
|
||||
versions = false,
|
||||
}: Args): string => {
|
||||
let result: string
|
||||
let custom = config[target]
|
||||
|
||||
if (!custom && target === 'enumName') {
|
||||
custom = config['dbName']
|
||||
}
|
||||
|
||||
if (custom) {
|
||||
result = typeof custom === 'function' ? custom({}) : custom
|
||||
} else {
|
||||
result = name ?? slug
|
||||
}
|
||||
|
||||
if (versions) result = `_${result}_versions`
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
import type { Create } from 'payload/database'
|
||||
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
import { getTableName } from './schema/getTableName.js'
|
||||
import { upsertRow } from './upsertRow/index.js'
|
||||
|
||||
export const create: Create = async function create(
|
||||
@@ -20,7 +19,10 @@ export const create: Create = async function create(
|
||||
fields: collection.fields,
|
||||
operation: 'create',
|
||||
req,
|
||||
tableName: toSnakeCase(collectionSlug),
|
||||
tableName: getTableName({
|
||||
adapter: this,
|
||||
config: collection,
|
||||
}),
|
||||
})
|
||||
|
||||
return result
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import type { CreateGlobalArgs } from 'payload/database'
|
||||
import type { PayloadRequest, TypeWithID } from 'payload/types'
|
||||
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
import { getTableName } from './schema/getTableName.js'
|
||||
import { upsertRow } from './upsertRow/index.js'
|
||||
|
||||
export async function createGlobal<T extends TypeWithID>(
|
||||
@@ -21,7 +20,10 @@ export async function createGlobal<T extends TypeWithID>(
|
||||
fields: globalConfig.fields,
|
||||
operation: 'create',
|
||||
req,
|
||||
tableName: toSnakeCase(slug),
|
||||
tableName: getTableName({
|
||||
adapter: this,
|
||||
config: globalConfig,
|
||||
}),
|
||||
})
|
||||
|
||||
return result
|
||||
|
||||
@@ -4,10 +4,10 @@ import type { PayloadRequest, TypeWithID } from 'payload/types'
|
||||
import { sql } from 'drizzle-orm'
|
||||
import { type CreateGlobalVersionArgs } from 'payload/database'
|
||||
import { buildVersionGlobalFields } from 'payload/versions'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
import { getTableName } from './schema/getTableName.js'
|
||||
import { upsertRow } from './upsertRow/index.js'
|
||||
|
||||
export async function createGlobalVersion<T extends TypeWithID>(
|
||||
@@ -16,8 +16,11 @@ export async function createGlobalVersion<T extends TypeWithID>(
|
||||
) {
|
||||
const db = this.sessions[req.transactionID]?.db || this.drizzle
|
||||
const global = this.payload.globals.config.find(({ slug }) => slug === globalSlug)
|
||||
const globalTableName = toSnakeCase(globalSlug)
|
||||
const tableName = `_${globalTableName}_v`
|
||||
const tableName = getTableName({
|
||||
adapter: this,
|
||||
config: global,
|
||||
versions: true,
|
||||
})
|
||||
|
||||
const result = await upsertRow<TypeWithVersion<T>>({
|
||||
adapter: this,
|
||||
|
||||
@@ -3,10 +3,10 @@ import type { PayloadRequest, TypeWithID } from 'payload/types'
|
||||
|
||||
import { sql } from 'drizzle-orm'
|
||||
import { buildVersionCollectionFields } from 'payload/versions'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
import { getTableName } from './schema/getTableName.js'
|
||||
import { upsertRow } from './upsertRow/index.js'
|
||||
|
||||
export async function createVersion<T extends TypeWithID>(
|
||||
@@ -21,8 +21,11 @@ export async function createVersion<T extends TypeWithID>(
|
||||
) {
|
||||
const db = this.sessions[req.transactionID]?.db || this.drizzle
|
||||
const collection = this.payload.collections[collectionSlug].config
|
||||
const collectionTableName = toSnakeCase(collectionSlug)
|
||||
const tableName = `_${collectionTableName}_v`
|
||||
const tableName = getTableName({
|
||||
adapter: this,
|
||||
config: collection,
|
||||
versions: true,
|
||||
})
|
||||
|
||||
const result = await upsertRow<TypeWithVersion<T>>({
|
||||
adapter: this,
|
||||
@@ -40,7 +43,15 @@ export async function createVersion<T extends TypeWithID>(
|
||||
})
|
||||
|
||||
const table = this.tables[tableName]
|
||||
const relationshipsTable = this.tables[`${tableName}_rels`]
|
||||
const relationshipsTable =
|
||||
this.tables[
|
||||
getTableName({
|
||||
adapter: this,
|
||||
config: collection,
|
||||
relationships: true,
|
||||
versions: true,
|
||||
})
|
||||
]
|
||||
|
||||
if (collection.versions.drafts) {
|
||||
await db.execute(sql`
|
||||
|
||||
@@ -2,11 +2,11 @@ import type { DeleteMany } from 'payload/database'
|
||||
import type { PayloadRequest } from 'payload/types'
|
||||
|
||||
import { inArray } from 'drizzle-orm'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
import { findMany } from './find/findMany.js'
|
||||
import { getTableName } from './schema/getTableName.js'
|
||||
|
||||
export const deleteMany: DeleteMany = async function deleteMany(
|
||||
this: PostgresAdapter,
|
||||
@@ -14,7 +14,7 @@ export const deleteMany: DeleteMany = async function deleteMany(
|
||||
) {
|
||||
const db = this.sessions[req.transactionID]?.db || this.drizzle
|
||||
const collectionConfig = this.payload.collections[collection].config
|
||||
const tableName = toSnakeCase(collection)
|
||||
const tableName = getTableName({ adapter: this, config: collectionConfig })
|
||||
|
||||
const result = await findMany({
|
||||
adapter: this,
|
||||
|
||||
@@ -2,13 +2,13 @@ import type { DeleteOne } from 'payload/database'
|
||||
import type { PayloadRequest } from 'payload/types'
|
||||
|
||||
import { eq } from 'drizzle-orm'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
import { buildFindManyArgs } from './find/buildFindManyArgs.js'
|
||||
import buildQuery from './queries/buildQuery.js'
|
||||
import { selectDistinct } from './queries/selectDistinct.js'
|
||||
import { getTableName } from './schema/getTableName.js'
|
||||
import { transform } from './transform/read/index.js'
|
||||
|
||||
export const deleteOne: DeleteOne = async function deleteOne(
|
||||
@@ -17,7 +17,10 @@ export const deleteOne: DeleteOne = async function deleteOne(
|
||||
) {
|
||||
const db = this.sessions[req.transactionID]?.db || this.drizzle
|
||||
const collection = this.payload.collections[collectionSlug].config
|
||||
const tableName = toSnakeCase(collectionSlug)
|
||||
const tableName = getTableName({
|
||||
adapter: this,
|
||||
config: collection,
|
||||
})
|
||||
let docToDelete: Record<string, unknown>
|
||||
|
||||
const { joinAliases, joins, selectFields, where } = await buildQuery({
|
||||
|
||||
@@ -3,11 +3,11 @@ import type { PayloadRequest, SanitizedCollectionConfig } from 'payload/types'
|
||||
|
||||
import { inArray } from 'drizzle-orm'
|
||||
import { buildVersionCollectionFields } from 'payload/versions'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
import { findMany } from './find/findMany.js'
|
||||
import { getTableName } from './schema/getTableName.js'
|
||||
|
||||
export const deleteVersions: DeleteVersions = async function deleteVersion(
|
||||
this: PostgresAdapter,
|
||||
@@ -16,7 +16,11 @@ export const deleteVersions: DeleteVersions = async function deleteVersion(
|
||||
const db = this.sessions[req.transactionID]?.db || this.drizzle
|
||||
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
|
||||
|
||||
const tableName = `_${toSnakeCase(collection)}_v`
|
||||
const tableName = getTableName({
|
||||
adapter: this,
|
||||
config: collectionConfig,
|
||||
versions: true,
|
||||
})
|
||||
const fields = buildVersionCollectionFields(collectionConfig)
|
||||
|
||||
const { docs } = await findMany({
|
||||
|
||||
@@ -1,38 +1,41 @@
|
||||
import type { Find } from 'payload/database'
|
||||
import type { PayloadRequest, SanitizedCollectionConfig } from 'payload/types'
|
||||
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
import { findMany } from './find/findMany.js'
|
||||
import { getTableName } from './schema/getTableName.js'
|
||||
|
||||
export const find: Find = async function find(
|
||||
this: PostgresAdapter,
|
||||
{
|
||||
collection,
|
||||
limit: limitArg,
|
||||
limit,
|
||||
locale,
|
||||
page = 1,
|
||||
pagination,
|
||||
req = {} as PayloadRequest,
|
||||
sort: sortArg,
|
||||
where: whereArg,
|
||||
where,
|
||||
},
|
||||
) {
|
||||
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
|
||||
const sort = typeof sortArg === 'string' ? sortArg : collectionConfig.defaultSort
|
||||
const tableName = getTableName({
|
||||
adapter: this,
|
||||
config: collectionConfig,
|
||||
})
|
||||
|
||||
return findMany({
|
||||
adapter: this,
|
||||
fields: collectionConfig.fields,
|
||||
limit: limitArg,
|
||||
limit,
|
||||
locale,
|
||||
page,
|
||||
pagination,
|
||||
req,
|
||||
sort,
|
||||
tableName: toSnakeCase(collection),
|
||||
where: whereArg,
|
||||
tableName,
|
||||
where,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
import { fieldAffectsData, tabHasName } from 'payload/types'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from '../types.js'
|
||||
import type { Result } from './buildFindManyArgs.js'
|
||||
|
||||
import { getTableName } from '../schema/getTableName.js'
|
||||
|
||||
type TraverseFieldArgs = {
|
||||
_locales: Record<string, unknown>
|
||||
adapter: PostgresAdapter
|
||||
@@ -78,9 +79,22 @@ export const traverseFields = ({
|
||||
with: {},
|
||||
}
|
||||
|
||||
const arrayTableName = `${currentTableName}_${path}${toSnakeCase(field.name)}`
|
||||
const arrayTableName = getTableName({
|
||||
adapter,
|
||||
config: field,
|
||||
parentTableName: currentTableName,
|
||||
prefix: `${currentTableName}_${path}`,
|
||||
})
|
||||
|
||||
if (adapter.tables[`${arrayTableName}_locales`]) withArray.with._locales = _locales
|
||||
const arrayTableNameWithLocales = getTableName({
|
||||
adapter,
|
||||
config: field,
|
||||
locales: true,
|
||||
parentTableName: currentTableName,
|
||||
prefix: `${currentTableName}_${path}`,
|
||||
})
|
||||
|
||||
if (adapter.tables[arrayTableNameWithLocales]) withArray.with._locales = _locales
|
||||
currentArgs.with[`${path}${field.name}`] = withArray
|
||||
|
||||
traverseFields({
|
||||
@@ -128,9 +142,16 @@ export const traverseFields = ({
|
||||
with: {},
|
||||
}
|
||||
|
||||
const tableName = `${topLevelTableName}_blocks_${toSnakeCase(block.slug)}`
|
||||
const tableName = getTableName({
|
||||
adapter,
|
||||
config: block,
|
||||
parentTableName: topLevelTableName,
|
||||
prefix: `${topLevelTableName}_blocks_`,
|
||||
})
|
||||
|
||||
if (adapter.tables[`${tableName}_locales`]) withBlock.with._locales = _locales
|
||||
if (adapter.tables[`${tableName}${adapter.localesSuffix}`]) {
|
||||
withBlock.with._locales = _locales
|
||||
}
|
||||
topLevelArgs.with[blockKey] = withBlock
|
||||
|
||||
traverseFields({
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
import type { FindGlobal } from 'payload/database'
|
||||
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
import { findMany } from './find/findMany.js'
|
||||
import { getTableName } from './schema/getTableName.js'
|
||||
|
||||
export const findGlobal: FindGlobal = async function findGlobal(
|
||||
this: PostgresAdapter,
|
||||
{ slug, locale, req, where },
|
||||
) {
|
||||
const globalConfig = this.payload.globals.config.find((config) => config.slug === slug)
|
||||
const tableName = toSnakeCase(slug)
|
||||
const tableName = getTableName({
|
||||
adapter: this,
|
||||
config: globalConfig,
|
||||
})
|
||||
|
||||
const {
|
||||
docs: [doc],
|
||||
|
||||
@@ -2,11 +2,11 @@ import type { FindGlobalVersions } from 'payload/database'
|
||||
import type { PayloadRequest, SanitizedGlobalConfig } from 'payload/types'
|
||||
|
||||
import { buildVersionGlobalFields } from 'payload/versions'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
import { findMany } from './find/findMany.js'
|
||||
import { getTableName } from './schema/getTableName.js'
|
||||
|
||||
export const findGlobalVersions: FindGlobalVersions = async function findGlobalVersions(
|
||||
this: PostgresAdapter,
|
||||
@@ -27,7 +27,11 @@ export const findGlobalVersions: FindGlobalVersions = async function findGlobalV
|
||||
)
|
||||
const sort = typeof sortArg === 'string' ? sortArg : '-createdAt'
|
||||
|
||||
const tableName = `_${toSnakeCase(global)}_v`
|
||||
const tableName = getTableName({
|
||||
adapter: this,
|
||||
config: globalConfig,
|
||||
versions: true,
|
||||
})
|
||||
const fields = buildVersionGlobalFields(globalConfig)
|
||||
|
||||
return findMany({
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
import type { FindOneArgs } from 'payload/database'
|
||||
import type { PayloadRequest, SanitizedCollectionConfig, TypeWithID } from 'payload/types'
|
||||
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
import { findMany } from './find/findMany.js'
|
||||
import { getTableName } from './schema/getTableName.js'
|
||||
|
||||
export async function findOne<T extends TypeWithID>(
|
||||
this: PostgresAdapter,
|
||||
{ collection, locale, req = {} as PayloadRequest, where: incomingWhere }: FindOneArgs,
|
||||
{ collection, locale, req = {} as PayloadRequest, where }: FindOneArgs,
|
||||
): Promise<T> {
|
||||
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
|
||||
const tableName = getTableName({
|
||||
adapter: this,
|
||||
config: collectionConfig,
|
||||
})
|
||||
|
||||
const { docs } = await findMany({
|
||||
adapter: this,
|
||||
@@ -22,8 +25,8 @@ export async function findOne<T extends TypeWithID>(
|
||||
pagination: false,
|
||||
req,
|
||||
sort: undefined,
|
||||
tableName: toSnakeCase(collection),
|
||||
where: incomingWhere,
|
||||
tableName,
|
||||
where,
|
||||
})
|
||||
|
||||
return docs?.[0] || null
|
||||
|
||||
@@ -2,11 +2,11 @@ import type { FindVersions } from 'payload/database'
|
||||
import type { PayloadRequest, SanitizedCollectionConfig } from 'payload/types'
|
||||
|
||||
import { buildVersionCollectionFields } from 'payload/versions'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
import { findMany } from './find/findMany.js'
|
||||
import { getTableName } from './schema/getTableName.js'
|
||||
|
||||
export const findVersions: FindVersions = async function findVersions(
|
||||
this: PostgresAdapter,
|
||||
@@ -25,7 +25,11 @@ export const findVersions: FindVersions = async function findVersions(
|
||||
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
|
||||
const sort = typeof sortArg === 'string' ? sortArg : collectionConfig.defaultSort
|
||||
|
||||
const tableName = `_${toSnakeCase(collection)}_v`
|
||||
const tableName = getTableName({
|
||||
adapter: this,
|
||||
config: collectionConfig,
|
||||
versions: true,
|
||||
})
|
||||
const fields = buildVersionCollectionFields(collectionConfig)
|
||||
|
||||
return findMany({
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { Payload } from 'payload'
|
||||
import type { DatabaseAdapterObj } from 'payload/database'
|
||||
export type { MigrateDownArgs, MigrateUpArgs } from './types.js'
|
||||
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
@@ -39,6 +38,8 @@ import { updateGlobal } from './updateGlobal.js'
|
||||
import { updateGlobalVersion } from './updateGlobalVersion.js'
|
||||
import { updateVersion } from './updateVersion.js'
|
||||
|
||||
export type { MigrateDownArgs, MigrateUpArgs } from './types.js'
|
||||
|
||||
export { sql } from 'drizzle-orm'
|
||||
|
||||
export function postgresAdapter(args: Args): DatabaseAdapterObj<PostgresAdapter> {
|
||||
@@ -50,20 +51,24 @@ export function postgresAdapter(args: Args): DatabaseAdapterObj<PostgresAdapter>
|
||||
name: 'postgres',
|
||||
|
||||
// Postgres-specific
|
||||
blockTableNames: {},
|
||||
drizzle: undefined,
|
||||
enums: {},
|
||||
fieldConstraints: {},
|
||||
idType,
|
||||
localesSuffix: args.localesSuffix || '_locales',
|
||||
logger: args.logger,
|
||||
pgSchema: undefined,
|
||||
pool: undefined,
|
||||
poolOptions: args.pool,
|
||||
push: args.push,
|
||||
relations: {},
|
||||
relationshipsSuffix: args.relationshipsSuffix || '_rels',
|
||||
schema: {},
|
||||
schemaName: args.schemaName,
|
||||
sessions: {},
|
||||
tables: {},
|
||||
versionsSuffix: args.versionsSuffix || '_v',
|
||||
|
||||
// DatabaseAdapter
|
||||
beginTransaction,
|
||||
|
||||
@@ -4,11 +4,11 @@ import type { SanitizedCollectionConfig } from 'payload/types'
|
||||
|
||||
import { pgEnum, pgSchema, pgTable } from 'drizzle-orm/pg-core'
|
||||
import { buildVersionCollectionFields, buildVersionGlobalFields } from 'payload/versions'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
import { buildTable } from './schema/build.js'
|
||||
import { getTableName } from './schema/getTableName.js'
|
||||
|
||||
export const init: Init = function init(this: PostgresAdapter) {
|
||||
if (this.schemaName) {
|
||||
@@ -25,7 +25,10 @@ export const init: Init = function init(this: PostgresAdapter) {
|
||||
}
|
||||
|
||||
this.payload.config.collections.forEach((collection: SanitizedCollectionConfig) => {
|
||||
const tableName = toSnakeCase(collection.slug)
|
||||
const tableName = getTableName({
|
||||
adapter: this,
|
||||
config: collection,
|
||||
})
|
||||
|
||||
buildTable({
|
||||
adapter: this,
|
||||
@@ -37,10 +40,15 @@ export const init: Init = function init(this: PostgresAdapter) {
|
||||
fields: collection.fields,
|
||||
tableName,
|
||||
timestamps: collection.timestamps,
|
||||
versions: false,
|
||||
})
|
||||
|
||||
if (collection.versions) {
|
||||
const versionsTableName = `_${tableName}_v`
|
||||
const versionsTableName = getTableName({
|
||||
adapter: this,
|
||||
config: collection,
|
||||
versions: true,
|
||||
})
|
||||
const versionFields = buildVersionCollectionFields(collection)
|
||||
|
||||
buildTable({
|
||||
@@ -53,12 +61,13 @@ export const init: Init = function init(this: PostgresAdapter) {
|
||||
fields: versionFields,
|
||||
tableName: versionsTableName,
|
||||
timestamps: true,
|
||||
versions: true,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
this.payload.config.globals.forEach((global) => {
|
||||
const tableName = toSnakeCase(global.slug)
|
||||
const tableName = getTableName({ adapter: this, config: global })
|
||||
|
||||
buildTable({
|
||||
adapter: this,
|
||||
@@ -70,10 +79,11 @@ export const init: Init = function init(this: PostgresAdapter) {
|
||||
fields: global.fields,
|
||||
tableName,
|
||||
timestamps: false,
|
||||
versions: false,
|
||||
})
|
||||
|
||||
if (global.versions) {
|
||||
const versionsTableName = `_${tableName}_v`
|
||||
const versionsTableName = getTableName({ adapter: this, config: global, versions: true })
|
||||
const versionFields = buildVersionGlobalFields(global)
|
||||
|
||||
buildTable({
|
||||
@@ -86,6 +96,7 @@ export const init: Init = function init(this: PostgresAdapter) {
|
||||
fields: versionFields,
|
||||
tableName: versionsTableName,
|
||||
timestamps: true,
|
||||
versions: true,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -92,7 +92,7 @@ async function runMigrationFile(payload: Payload, migration: Migration, batch: n
|
||||
|
||||
payload.logger.info({ msg: `Migrating: ${migration.name}` })
|
||||
|
||||
const pgAdapter = payload.db as PostgresAdapter
|
||||
const pgAdapter = payload.db
|
||||
const drizzleJSON = generateDrizzleJson(pgAdapter.schema)
|
||||
|
||||
try {
|
||||
|
||||
@@ -14,6 +14,8 @@ import { v4 as uuid } from 'uuid'
|
||||
import type { GenericColumn, GenericTable, PostgresAdapter } from '../types.js'
|
||||
import type { BuildQueryJoinAliases, BuildQueryJoins } from './buildQuery.js'
|
||||
|
||||
import { getTableName } from '../schema/getTableName.js'
|
||||
|
||||
type Constraint = {
|
||||
columnName: string
|
||||
table: GenericTable | PgTableWithColumns<any>
|
||||
@@ -183,7 +185,13 @@ export const getTableColumnFromPath = ({
|
||||
|
||||
case 'group': {
|
||||
if (locale && field.localized && adapter.payload.config.localization) {
|
||||
newTableName = `${tableName}_locales`
|
||||
newTableName = getTableName({
|
||||
adapter,
|
||||
config: field,
|
||||
locales: true,
|
||||
parentTableName: tableName,
|
||||
prefix: `${tableName}_`,
|
||||
})
|
||||
|
||||
joins[tableName] = eq(
|
||||
adapter.tables[tableName].id,
|
||||
@@ -218,7 +226,12 @@ export const getTableColumnFromPath = ({
|
||||
}
|
||||
|
||||
case 'array': {
|
||||
newTableName = `${tableName}_${tableNameSuffix}${toSnakeCase(field.name)}`
|
||||
newTableName = getTableName({
|
||||
adapter,
|
||||
config: field,
|
||||
parentTableName: `${tableName}_${tableNameSuffix}`,
|
||||
prefix: `${tableName}_${tableNameSuffix}`,
|
||||
})
|
||||
constraintPath = `${constraintPath}${field.name}.%.`
|
||||
if (locale && field.localized && adapter.payload.config.localization) {
|
||||
joins[newTableName] = and(
|
||||
@@ -265,7 +278,12 @@ export const getTableColumnFromPath = ({
|
||||
const blockTypes = Array.isArray(value) ? value : [value]
|
||||
blockTypes.forEach((blockType) => {
|
||||
const block = field.blocks.find((block) => block.slug === blockType)
|
||||
newTableName = `${tableName}_blocks_${toSnakeCase(block.slug)}`
|
||||
newTableName = getTableName({
|
||||
adapter,
|
||||
config: block,
|
||||
parentTableName: tableName,
|
||||
prefix: `${tableName}_blocks_`,
|
||||
})
|
||||
joins[newTableName] = eq(
|
||||
adapter.tables[tableName].id,
|
||||
adapter.tables[newTableName]._parentID,
|
||||
@@ -285,7 +303,12 @@ export const getTableColumnFromPath = ({
|
||||
}
|
||||
|
||||
const hasBlockField = field.blocks.some((block) => {
|
||||
newTableName = `${tableName}_blocks_${toSnakeCase(block.slug)}`
|
||||
newTableName = getTableName({
|
||||
adapter,
|
||||
config: block,
|
||||
parentTableName: tableName,
|
||||
prefix: `${tableName}_blocks_`,
|
||||
})
|
||||
constraintPath = `${constraintPath}${field.name}.%.`
|
||||
let result
|
||||
const blockConstraints = []
|
||||
@@ -351,7 +374,7 @@ export const getTableColumnFromPath = ({
|
||||
case 'relationship':
|
||||
case 'upload': {
|
||||
let relationshipFields
|
||||
const relationTableName = `${rootTableName}_rels`
|
||||
const relationTableName = `${rootTableName}${adapter.relationshipsSuffix}`
|
||||
const newCollectionPath = pathSegments.slice(1).join('.')
|
||||
const aliasRelationshipTableName = uuid()
|
||||
const aliasRelationshipTable = alias(
|
||||
@@ -392,9 +415,13 @@ export const getTableColumnFromPath = ({
|
||||
let newAliasTable
|
||||
|
||||
if (typeof field.relationTo === 'string') {
|
||||
newTableName = `${toSnakeCase(field.relationTo)}`
|
||||
const relationshipConfig = adapter.payload.collections[field.relationTo].config
|
||||
newTableName = getTableName({
|
||||
adapter,
|
||||
config: relationshipConfig,
|
||||
})
|
||||
// parent to relationship join table
|
||||
relationshipFields = adapter.payload.collections[field.relationTo].config.fields
|
||||
relationshipFields = relationshipConfig.fields
|
||||
|
||||
newAliasTable = alias(adapter.tables[newTableName], toSnakeCase(uuid()))
|
||||
|
||||
@@ -413,7 +440,11 @@ export const getTableColumnFromPath = ({
|
||||
}
|
||||
} else if (newCollectionPath === 'value') {
|
||||
const tableColumnsNames = field.relationTo.map(
|
||||
(relationTo) => `"${aliasRelationshipTableName}"."${toSnakeCase(relationTo)}_id"`,
|
||||
(relationTo) =>
|
||||
`"${aliasRelationshipTableName}"."${getTableName({
|
||||
adapter,
|
||||
config: adapter.payload.collections[relationTo].config,
|
||||
})}_id"`,
|
||||
)
|
||||
return {
|
||||
constraints,
|
||||
@@ -460,7 +491,7 @@ export const getTableColumnFromPath = ({
|
||||
if (field.localized && adapter.payload.config.localization) {
|
||||
// If localized, we go to localized table and set aliasTable to undefined
|
||||
// so it is not picked up below to be used as targetTable
|
||||
newTableName = `${tableName}_locales`
|
||||
newTableName = `${tableName}${adapter.localesSuffix}`
|
||||
|
||||
const parentTable = aliasTable || adapter.tables[tableName]
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@ import type { PayloadRequest, SanitizedCollectionConfig } from 'payload/types'
|
||||
|
||||
import { type QueryDrafts, combineQueries } from 'payload/database'
|
||||
import { buildVersionCollectionFields } from 'payload/versions'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import { findMany } from './find/findMany.js'
|
||||
import { getTableName } from './schema/getTableName.js'
|
||||
|
||||
export const queryDrafts: QueryDrafts = async function queryDrafts({
|
||||
collection,
|
||||
@@ -17,7 +17,11 @@ export const queryDrafts: QueryDrafts = async function queryDrafts({
|
||||
where,
|
||||
}) {
|
||||
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
|
||||
const tableName = `_${toSnakeCase(collection)}_v`
|
||||
const tableName = getTableName({
|
||||
adapter: this,
|
||||
config: collectionConfig,
|
||||
versions: true,
|
||||
})
|
||||
const fields = buildVersionCollectionFields(collectionConfig)
|
||||
|
||||
const combinedWhere = combineQueries({ latest: { equals: true } }, where)
|
||||
|
||||
@@ -11,10 +11,10 @@ import type { Field } from 'payload/types'
|
||||
import { relations } from 'drizzle-orm'
|
||||
import { index, integer, numeric, serial, timestamp, unique, varchar } from 'drizzle-orm/pg-core'
|
||||
import { fieldAffectsData } from 'payload/types'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { GenericColumns, GenericTable, IDType, PostgresAdapter } from '../types.js'
|
||||
|
||||
import { getTableName } from './getTableName.js'
|
||||
import { parentIDColumnMap } from './parentIDColumnMap.js'
|
||||
import { setColumnID } from './setColumnID.js'
|
||||
import { traverseFields } from './traverseFields.js'
|
||||
@@ -35,6 +35,7 @@ type Args = {
|
||||
rootTableName?: string
|
||||
tableName: string
|
||||
timestamps?: boolean
|
||||
versions: boolean
|
||||
}
|
||||
|
||||
type Result = {
|
||||
@@ -59,6 +60,7 @@ export const buildTable = ({
|
||||
rootTableName: incomingRootTableName,
|
||||
tableName,
|
||||
timestamps,
|
||||
versions,
|
||||
}: Args): Result => {
|
||||
const rootTableName = incomingRootTableName || tableName
|
||||
const columns: Record<string, PgColumnBuilder> = baseColumns
|
||||
@@ -113,6 +115,7 @@ export const buildTable = ({
|
||||
rootRelationsToBuild: rootRelationsToBuild || relationsToBuild,
|
||||
rootTableIDColType: rootTableIDColType || idColType,
|
||||
rootTableName,
|
||||
versions,
|
||||
}))
|
||||
|
||||
if (timestamps) {
|
||||
@@ -147,7 +150,7 @@ export const buildTable = ({
|
||||
adapter.tables[tableName] = table
|
||||
|
||||
if (hasLocalizedField) {
|
||||
const localeTableName = `${tableName}_locales`
|
||||
const localeTableName = `${tableName}${adapter.localesSuffix}`
|
||||
localesColumns.id = serial('id').primaryKey()
|
||||
localesColumns._locale = adapter.enums.enum__locales('_locale').notNull()
|
||||
localesColumns._parentID = parentIDColumnMap[idColType]('_parent_id')
|
||||
@@ -288,11 +291,16 @@ export const buildTable = ({
|
||||
}
|
||||
|
||||
relationships.forEach((relationTo) => {
|
||||
const formattedRelationTo = toSnakeCase(relationTo)
|
||||
const relationshipConfig = adapter.payload.collections[relationTo].config
|
||||
const formattedRelationTo = getTableName({
|
||||
adapter,
|
||||
config: relationshipConfig,
|
||||
throwValidationError: true,
|
||||
})
|
||||
let colType = adapter.idType === 'uuid' ? 'uuid' : 'integer'
|
||||
const relatedCollectionCustomID = adapter.payload.collections[
|
||||
relationTo
|
||||
].config.fields.find((field) => fieldAffectsData(field) && field.name === 'id')
|
||||
const relatedCollectionCustomID = relationshipConfig.fields.find(
|
||||
(field) => fieldAffectsData(field) && field.name === 'id',
|
||||
)
|
||||
if (relatedCollectionCustomID?.type === 'number') colType = 'numeric'
|
||||
if (relatedCollectionCustomID?.type === 'text') colType = 'varchar'
|
||||
|
||||
@@ -301,7 +309,7 @@ export const buildTable = ({
|
||||
).references(() => adapter.tables[formattedRelationTo].id, { onDelete: 'cascade' })
|
||||
})
|
||||
|
||||
const relationshipsTableName = `${tableName}_rels`
|
||||
const relationshipsTableName = `${tableName}${adapter.relationshipsSuffix}`
|
||||
|
||||
relationshipsTable = adapter.pgSchema.table(
|
||||
relationshipsTableName,
|
||||
@@ -333,7 +341,11 @@ export const buildTable = ({
|
||||
}
|
||||
|
||||
relationships.forEach((relationTo) => {
|
||||
const relatedTableName = toSnakeCase(relationTo)
|
||||
const relatedTableName = getTableName({
|
||||
adapter,
|
||||
config: adapter.payload.collections[relationTo].config,
|
||||
throwValidationError: true,
|
||||
})
|
||||
const idColumnName = `${relationTo}ID`
|
||||
result[idColumnName] = one(adapter.tables[relatedTableName], {
|
||||
fields: [relationshipsTable[idColumnName]],
|
||||
|
||||
75
packages/db-postgres/src/schema/getTableName.ts
Normal file
75
packages/db-postgres/src/schema/getTableName.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import type { DBIdentifierName } from 'payload/database'
|
||||
|
||||
import { APIError } from 'payload/errors'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from '../types.js'
|
||||
|
||||
type Args = {
|
||||
adapter: PostgresAdapter
|
||||
/** The collection, global or field config **/
|
||||
config: {
|
||||
dbName?: DBIdentifierName
|
||||
enumName?: DBIdentifierName
|
||||
name?: string
|
||||
slug?: string
|
||||
}
|
||||
/** Localized tables need to be given the locales suffix */
|
||||
locales?: boolean
|
||||
/** For nested tables passed for the user custom dbName functions to handle their own iterations */
|
||||
parentTableName?: string
|
||||
/** For sub tables (array for example) this needs to include the parentTableName */
|
||||
prefix?: string
|
||||
/** Adds the relationships suffix */
|
||||
relationships?: boolean
|
||||
/** For tables based on fields that could have both enumName and dbName (ie: select with hasMany), default: 'dbName' */
|
||||
target?: 'dbName' | 'enumName'
|
||||
throwValidationError?: boolean
|
||||
/** Adds the versions suffix, should only be used on the base collection to duplicate suffixing */
|
||||
versions?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to name database enums and tables
|
||||
* Returns the table or enum name for a given entity
|
||||
*/
|
||||
export const getTableName = ({
|
||||
adapter,
|
||||
config: { name, slug },
|
||||
config,
|
||||
locales = false,
|
||||
parentTableName,
|
||||
prefix = '',
|
||||
relationships = false,
|
||||
target = 'dbName',
|
||||
throwValidationError = false,
|
||||
versions = false,
|
||||
}: Args): string => {
|
||||
let result: string
|
||||
let custom = config[target]
|
||||
|
||||
if (!custom && target === 'enumName') {
|
||||
custom = config['dbName']
|
||||
}
|
||||
|
||||
if (custom) {
|
||||
result = typeof custom === 'function' ? custom({ tableName: parentTableName }) : custom
|
||||
} else {
|
||||
result = `${prefix}${toSnakeCase(name ?? slug)}`
|
||||
}
|
||||
|
||||
if (locales) result = `${result}${adapter.localesSuffix}`
|
||||
if (versions) result = `_${result}${adapter.versionsSuffix}`
|
||||
if (relationships) result = `${result}${adapter.relationshipsSuffix}`
|
||||
|
||||
if (!throwValidationError) {
|
||||
return result
|
||||
}
|
||||
|
||||
if (result.length > 63) {
|
||||
throw new APIError(
|
||||
`Exceeded max identifier length for table or enum name of 63 characters. Invalid name: ${result}`,
|
||||
)
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import type { GenericColumns, IDType, PostgresAdapter } from '../types.js'
|
||||
import { hasLocalesTable } from '../utilities/hasLocalesTable.js'
|
||||
import { buildTable } from './build.js'
|
||||
import { createIndex } from './createIndex.js'
|
||||
import { getTableName } from './getTableName.js'
|
||||
import { idToUUID } from './idToUUID.js'
|
||||
import { parentIDColumnMap } from './parentIDColumnMap.js'
|
||||
import { validateExistingBlockIsIdentical } from './validateExistingBlockIsIdentical.js'
|
||||
@@ -53,6 +54,7 @@ type Args = {
|
||||
rootRelationsToBuild?: Map<string, string>
|
||||
rootTableIDColType: string
|
||||
rootTableName: string
|
||||
versions: boolean
|
||||
}
|
||||
|
||||
type Result = {
|
||||
@@ -86,7 +88,9 @@ export const traverseFields = ({
|
||||
rootRelationsToBuild,
|
||||
rootTableIDColType,
|
||||
rootTableName,
|
||||
versions,
|
||||
}: Args): Result => {
|
||||
const throwValidationError = true
|
||||
let hasLocalizedField = false
|
||||
let hasLocalizedRelationshipField = false
|
||||
let hasManyTextField: 'index' | boolean = false
|
||||
@@ -217,7 +221,15 @@ export const traverseFields = ({
|
||||
|
||||
case 'radio':
|
||||
case 'select': {
|
||||
const enumName = `enum_${newTableName}_${toSnakeCase(field.name)}`
|
||||
const enumName = getTableName({
|
||||
adapter,
|
||||
config: field,
|
||||
parentTableName: newTableName,
|
||||
prefix: `enum_${newTableName}_`,
|
||||
target: 'enumName',
|
||||
throwValidationError,
|
||||
versions,
|
||||
})
|
||||
|
||||
adapter.enums[enumName] = pgEnum(
|
||||
enumName,
|
||||
@@ -231,7 +243,14 @@ export const traverseFields = ({
|
||||
)
|
||||
|
||||
if (field.type === 'select' && field.hasMany) {
|
||||
const selectTableName = `${newTableName}_${toSnakeCase(field.name)}`
|
||||
const selectTableName = getTableName({
|
||||
adapter,
|
||||
config: field,
|
||||
parentTableName: newTableName,
|
||||
prefix: `${newTableName}_`,
|
||||
throwValidationError,
|
||||
versions,
|
||||
})
|
||||
const baseColumns: Record<string, PgColumnBuilder> = {
|
||||
order: integer('order').notNull(),
|
||||
parent: parentIDColumnMap[parentIDColType]('parent_id')
|
||||
@@ -266,6 +285,7 @@ export const traverseFields = ({
|
||||
disableUnique,
|
||||
fields: [],
|
||||
tableName: selectTableName,
|
||||
versions,
|
||||
})
|
||||
|
||||
relationsToBuild.set(fieldName, selectTableName)
|
||||
@@ -296,7 +316,13 @@ export const traverseFields = ({
|
||||
case 'array': {
|
||||
const disableNotNullFromHere = Boolean(field.admin?.condition) || disableNotNull
|
||||
|
||||
const arrayTableName = `${newTableName}_${toSnakeCase(field.name)}`
|
||||
const arrayTableName = getTableName({
|
||||
adapter,
|
||||
config: field,
|
||||
parentTableName: newTableName,
|
||||
prefix: `${newTableName}_`,
|
||||
throwValidationError,
|
||||
})
|
||||
const baseColumns: Record<string, PgColumnBuilder> = {
|
||||
_order: integer('_order').notNull(),
|
||||
_parentID: parentIDColumnMap[parentIDColType]('_parent_id')
|
||||
@@ -334,6 +360,7 @@ export const traverseFields = ({
|
||||
rootTableIDColType,
|
||||
rootTableName,
|
||||
tableName: arrayTableName,
|
||||
versions,
|
||||
})
|
||||
|
||||
if (subHasManyTextField) {
|
||||
@@ -356,7 +383,7 @@ export const traverseFields = ({
|
||||
}
|
||||
|
||||
if (hasLocalesTable(field.fields)) {
|
||||
result._locales = many(adapter.tables[`${arrayTableName}_locales`])
|
||||
result._locales = many(adapter.tables[`${arrayTableName}${adapter.localesSuffix}`])
|
||||
}
|
||||
|
||||
subRelationsToBuild.forEach((val, key) => {
|
||||
@@ -375,7 +402,13 @@ export const traverseFields = ({
|
||||
const disableNotNullFromHere = Boolean(field.admin?.condition) || disableNotNull
|
||||
|
||||
field.blocks.forEach((block) => {
|
||||
const blockTableName = `${rootTableName}_blocks_${toSnakeCase(block.slug)}`
|
||||
const blockTableName = getTableName({
|
||||
adapter,
|
||||
config: block,
|
||||
parentTableName: rootTableName,
|
||||
prefix: `${rootTableName}_blocks_`,
|
||||
throwValidationError,
|
||||
})
|
||||
if (!adapter.tables[blockTableName]) {
|
||||
const baseColumns: Record<string, PgColumnBuilder> = {
|
||||
_order: integer('_order').notNull(),
|
||||
@@ -416,6 +449,7 @@ export const traverseFields = ({
|
||||
rootTableIDColType,
|
||||
rootTableName,
|
||||
tableName: blockTableName,
|
||||
versions,
|
||||
})
|
||||
|
||||
if (subHasManyTextField) {
|
||||
@@ -439,7 +473,9 @@ export const traverseFields = ({
|
||||
}
|
||||
|
||||
if (hasLocalesTable(block.fields)) {
|
||||
result._locales = many(adapter.tables[`${blockTableName}_locales`])
|
||||
result._locales = many(
|
||||
adapter.tables[`${blockTableName}${adapter.localesSuffix}`],
|
||||
)
|
||||
}
|
||||
|
||||
subRelationsToBuild.forEach((val, key) => {
|
||||
@@ -451,7 +487,7 @@ export const traverseFields = ({
|
||||
)
|
||||
|
||||
adapter.relations[`relations_${blockTableName}`] = blockTableRelations
|
||||
} else if (process.env.NODE_ENV !== 'production') {
|
||||
} else if (process.env.NODE_ENV !== 'production' && !versions) {
|
||||
validateExistingBlockIsIdentical({
|
||||
block,
|
||||
localized: field.localized,
|
||||
@@ -459,7 +495,7 @@ export const traverseFields = ({
|
||||
table: adapter.tables[blockTableName],
|
||||
})
|
||||
}
|
||||
|
||||
adapter.blockTableNames[`${rootTableName}.${toSnakeCase(block.slug)}`] = blockTableName
|
||||
rootRelationsToBuild.set(`_blocks_${block.slug}`, blockTableName)
|
||||
})
|
||||
|
||||
@@ -498,6 +534,7 @@ export const traverseFields = ({
|
||||
rootRelationsToBuild,
|
||||
rootTableIDColType,
|
||||
rootTableName,
|
||||
versions,
|
||||
})
|
||||
|
||||
if (groupHasLocalizedField) hasLocalizedField = true
|
||||
@@ -540,6 +577,7 @@ export const traverseFields = ({
|
||||
rootRelationsToBuild,
|
||||
rootTableIDColType,
|
||||
rootTableName,
|
||||
versions,
|
||||
})
|
||||
|
||||
if (groupHasLocalizedField) hasLocalizedField = true
|
||||
@@ -583,6 +621,7 @@ export const traverseFields = ({
|
||||
rootRelationsToBuild,
|
||||
rootTableIDColType,
|
||||
rootTableName,
|
||||
versions,
|
||||
})
|
||||
|
||||
if (tabHasLocalizedField) hasLocalizedField = true
|
||||
@@ -626,6 +665,7 @@ export const traverseFields = ({
|
||||
rootRelationsToBuild,
|
||||
rootTableIDColType,
|
||||
rootTableName,
|
||||
versions,
|
||||
})
|
||||
|
||||
if (rowHasLocalizedField) hasLocalizedField = true
|
||||
|
||||
@@ -24,11 +24,14 @@ export type DrizzleDB = NodePgDatabase<Record<string, unknown>>
|
||||
|
||||
export type Args = {
|
||||
idType?: 'serial' | 'uuid'
|
||||
localesSuffix?: string
|
||||
logger?: DrizzleConfig['logger']
|
||||
migrationDir?: string
|
||||
pool: PoolConfig
|
||||
push?: boolean
|
||||
relationshipsSuffix?: string
|
||||
schemaName?: string
|
||||
versionsSuffix?: string
|
||||
}
|
||||
|
||||
export type GenericColumn = PgColumn<
|
||||
@@ -58,6 +61,10 @@ export type DrizzleTransaction = PgTransaction<
|
||||
>
|
||||
|
||||
export type PostgresAdapter = BaseDatabaseAdapter & {
|
||||
/**
|
||||
* Used internally to map the block name to the table name
|
||||
*/
|
||||
blockTableNames: Record<string, string>
|
||||
drizzle: DrizzleDB
|
||||
enums: Record<string, GenericEnum>
|
||||
/**
|
||||
@@ -66,12 +73,14 @@ export type PostgresAdapter = BaseDatabaseAdapter & {
|
||||
*/
|
||||
fieldConstraints: Record<string, Record<string, string>>
|
||||
idType: Args['idType']
|
||||
localesSuffix?: string
|
||||
logger: DrizzleConfig['logger']
|
||||
pgSchema?: { table: PgTableFn } | PgSchema
|
||||
pool: Pool
|
||||
poolOptions: Args['pool']
|
||||
push: boolean
|
||||
relations: Record<string, GenericRelation>
|
||||
relationshipsSuffix?: string
|
||||
schema: Record<string, GenericEnum | GenericRelation | GenericTable>
|
||||
schemaName?: Args['schemaName']
|
||||
sessions: {
|
||||
@@ -82,6 +91,7 @@ export type PostgresAdapter = BaseDatabaseAdapter & {
|
||||
}
|
||||
}
|
||||
tables: Record<string, GenericTable | PgTableWithColumns<any>>
|
||||
versionsSuffix?: string
|
||||
}
|
||||
|
||||
export type IDType = 'integer' | 'numeric' | 'uuid' | 'varchar'
|
||||
@@ -98,9 +108,11 @@ declare module 'payload' {
|
||||
drizzle: DrizzleDB
|
||||
enums: Record<string, GenericEnum>
|
||||
fieldConstraints: Record<string, Record<string, string>>
|
||||
localeSuffix?: string
|
||||
pool: Pool
|
||||
push: boolean
|
||||
relations: Record<string, GenericRelation>
|
||||
relationshipsSuffix?: string
|
||||
schema: Record<string, GenericEnum | GenericRelation | GenericTable>
|
||||
sessions: {
|
||||
[id: string]: {
|
||||
@@ -110,5 +122,6 @@ declare module 'payload' {
|
||||
}
|
||||
}
|
||||
tables: Record<string, GenericTable>
|
||||
versionsSuffix?: string
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import type { UpdateOne } from 'payload/database'
|
||||
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
import buildQuery from './queries/buildQuery.js'
|
||||
import { selectDistinct } from './queries/selectDistinct.js'
|
||||
import { getTableName } from './schema/getTableName.js'
|
||||
import { upsertRow } from './upsertRow/index.js'
|
||||
|
||||
export const updateOne: UpdateOne = async function updateOne(
|
||||
@@ -14,7 +13,10 @@ export const updateOne: UpdateOne = async function updateOne(
|
||||
) {
|
||||
const db = this.sessions[req.transactionID]?.db || this.drizzle
|
||||
const collection = this.payload.collections[collectionSlug].config
|
||||
const tableName = toSnakeCase(collectionSlug)
|
||||
const tableName = getTableName({
|
||||
adapter: this,
|
||||
config: collection,
|
||||
})
|
||||
const whereToUse = whereArg || { id: { equals: id } }
|
||||
let idToUpdate = id
|
||||
|
||||
@@ -49,7 +51,7 @@ export const updateOne: UpdateOne = async function updateOne(
|
||||
fields: collection.fields,
|
||||
operation: 'update',
|
||||
req,
|
||||
tableName: toSnakeCase(collectionSlug),
|
||||
tableName,
|
||||
})
|
||||
|
||||
return result
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import type { UpdateGlobalArgs } from 'payload/database'
|
||||
import type { PayloadRequest, TypeWithID } from 'payload/types'
|
||||
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
import { getTableName } from './schema/getTableName.js'
|
||||
import { upsertRow } from './upsertRow/index.js'
|
||||
|
||||
export async function updateGlobal<T extends TypeWithID>(
|
||||
@@ -13,7 +12,10 @@ export async function updateGlobal<T extends TypeWithID>(
|
||||
): Promise<T> {
|
||||
const db = this.sessions[req.transactionID]?.db || this.drizzle
|
||||
const globalConfig = this.payload.globals.config.find((config) => config.slug === slug)
|
||||
const tableName = toSnakeCase(slug)
|
||||
const tableName = getTableName({
|
||||
adapter: this,
|
||||
config: globalConfig,
|
||||
})
|
||||
|
||||
const existingGlobal = await db.query[tableName].findFirst({})
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@ import type { TypeWithVersion, UpdateGlobalVersionArgs } from 'payload/database'
|
||||
import type { PayloadRequest, SanitizedGlobalConfig, TypeWithID } from 'payload/types'
|
||||
|
||||
import { buildVersionGlobalFields } from 'payload/versions'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
import buildQuery from './queries/buildQuery.js'
|
||||
import { getTableName } from './schema/getTableName.js'
|
||||
import { upsertRow } from './upsertRow/index.js'
|
||||
|
||||
export async function updateGlobalVersion<T extends TypeWithID>(
|
||||
@@ -25,7 +25,11 @@ export async function updateGlobalVersion<T extends TypeWithID>(
|
||||
({ slug }) => slug === global,
|
||||
)
|
||||
const whereToUse = whereArg || { id: { equals: id } }
|
||||
const tableName = `_${toSnakeCase(global)}_v`
|
||||
const tableName = getTableName({
|
||||
adapter: this,
|
||||
config: globalConfig,
|
||||
versions: true,
|
||||
})
|
||||
const fields = buildVersionGlobalFields(globalConfig)
|
||||
|
||||
const { where } = await buildQuery({
|
||||
|
||||
@@ -2,11 +2,11 @@ import type { TypeWithVersion, UpdateVersionArgs } from 'payload/database'
|
||||
import type { PayloadRequest, SanitizedCollectionConfig, TypeWithID } from 'payload/types'
|
||||
|
||||
import { buildVersionCollectionFields } from 'payload/versions'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
import type { PostgresAdapter } from './types.js'
|
||||
|
||||
import buildQuery from './queries/buildQuery.js'
|
||||
import { getTableName } from './schema/getTableName.js'
|
||||
import { upsertRow } from './upsertRow/index.js'
|
||||
|
||||
export async function updateVersion<T extends TypeWithID>(
|
||||
@@ -23,7 +23,11 @@ export async function updateVersion<T extends TypeWithID>(
|
||||
const db = this.sessions[req.transactionID]?.db || this.drizzle
|
||||
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
|
||||
const whereToUse = whereArg || { id: { equals: id } }
|
||||
const tableName = `_${toSnakeCase(collection)}_v`
|
||||
const tableName = getTableName({
|
||||
adapter: this,
|
||||
config: collectionConfig,
|
||||
versions: true,
|
||||
})
|
||||
const fields = buildVersionCollectionFields(collectionConfig)
|
||||
|
||||
const { where } = await buildQuery({
|
||||
|
||||
@@ -108,6 +108,7 @@ const collectionSchema = joi.object().keys({
|
||||
joi.boolean(),
|
||||
),
|
||||
custom: joi.object().pattern(joi.string(), joi.any()),
|
||||
dbName: joi.alternatives().try(joi.string(), joi.func()),
|
||||
defaultSort: joi.string(),
|
||||
disableDuplicate: joi.bool(),
|
||||
endpoints: endpointsSchema,
|
||||
|
||||
@@ -411,3 +411,10 @@ export type DatabaseAdapterResult<T = BaseDatabaseAdapter> = {
|
||||
defaultIDType: 'number' | 'text'
|
||||
init: (args: { payload: Payload }) => T
|
||||
}
|
||||
|
||||
export type DBIdentifierName =
|
||||
| ((Args: {
|
||||
/** The name of the parent table when using relational DBs */
|
||||
tableName?: string
|
||||
}) => string)
|
||||
| string
|
||||
|
||||
@@ -12,6 +12,7 @@ export type {
|
||||
CreateMigration,
|
||||
CreateVersion,
|
||||
CreateVersionArgs,
|
||||
DBIdentifierName,
|
||||
DatabaseAdapterResult as DatabaseAdapterObj,
|
||||
DeleteMany,
|
||||
DeleteManyArgs,
|
||||
|
||||
@@ -4,6 +4,5 @@ export { deleteCollectionVersions } from '../versions/deleteCollectionVersions.j
|
||||
export { enforceMaxVersions } from '../versions/enforceMaxVersions.js'
|
||||
export { getLatestCollectionVersion } from '../versions/getLatestCollectionVersion.js'
|
||||
export { getLatestGlobalVersion } from '../versions/getLatestGlobalVersion.js'
|
||||
export { getVersionsModelName } from '../versions/getVersionsModelName.js'
|
||||
export { saveVersion } from '../versions/saveVersion.js'
|
||||
export type { TypeWithVersion } from '../versions/types.js'
|
||||
|
||||
@@ -201,9 +201,11 @@ export const select = baseField.keys({
|
||||
isClearable: joi.boolean().default(false),
|
||||
isSortable: joi.boolean().default(false),
|
||||
}),
|
||||
dbName: joi.alternatives().try(joi.string(), joi.func()),
|
||||
defaultValue: joi
|
||||
.alternatives()
|
||||
.try(joi.string().allow(''), joi.array().items(joi.string().allow('')), joi.func()),
|
||||
enumName: joi.alternatives().try(joi.string(), joi.func()),
|
||||
hasMany: joi.boolean().default(false),
|
||||
options: joi
|
||||
.array()
|
||||
@@ -233,6 +235,7 @@ export const radio = baseField.keys({
|
||||
layout: joi.string().valid('vertical', 'horizontal'),
|
||||
}),
|
||||
defaultValue: joi.alternatives().try(joi.string().allow(''), joi.func()),
|
||||
enumName: joi.alternatives().try(joi.string(), joi.func()),
|
||||
options: joi
|
||||
.array()
|
||||
.min(1)
|
||||
@@ -310,6 +313,7 @@ export const array = baseField.keys({
|
||||
.default({}),
|
||||
})
|
||||
.default({}),
|
||||
dbName: joi.alternatives().try(joi.string(), joi.func()),
|
||||
defaultValue: joi.alternatives().try(joi.array().items(joi.object()), joi.func()),
|
||||
fields: joi.array().items(joi.link('#field')).required(),
|
||||
interfaceName: joi.string(),
|
||||
@@ -410,6 +414,7 @@ export const blocks = baseField.keys({
|
||||
joi.object({
|
||||
slug: joi.string().required(),
|
||||
custom: joi.object().pattern(joi.string(), joi.any()),
|
||||
dbName: joi.alternatives().try(joi.string(), joi.func()),
|
||||
fields: joi.array().items(joi.link('#field')),
|
||||
graphQL: joi.object().keys({
|
||||
singularName: joi.string(),
|
||||
|
||||
@@ -17,6 +17,7 @@ import type {
|
||||
} from '../../admin/types.js'
|
||||
import type { User } from '../../auth/index.js'
|
||||
import type { SanitizedCollectionConfig, TypeWithID } from '../../collections/config/types.js'
|
||||
import type { DBIdentifierName } from '../../database/types.js'
|
||||
import type { SanitizedGlobalConfig } from '../../globals/config/types.js'
|
||||
import type { Operation, PayloadRequest, RequestContext, Where } from '../../types/index.js'
|
||||
import type { ClientFieldConfig } from './client.js'
|
||||
@@ -68,9 +69,11 @@ export type FieldAccess<T extends TypeWithID = any, P = any, U = any> = (args: {
|
||||
* The `id` of the current document being read or updated. `id` is undefined during the `create` operation.
|
||||
*/
|
||||
id?: number | string
|
||||
/** The `Express` request object containing the currently authenticated `user` */
|
||||
req: PayloadRequest<U>
|
||||
/** The `payload` object to interface with the payload API */
|
||||
req: PayloadRequest<U>
|
||||
/**
|
||||
* Immediately adjacent data to this field. For example, if this is a `group` field, then `siblingData` will be the other fields within the group.
|
||||
*/
|
||||
siblingData?: Partial<P>
|
||||
}) => Promise<boolean> | boolean
|
||||
|
||||
@@ -478,6 +481,14 @@ export type SelectField = FieldBase & {
|
||||
isClearable?: boolean
|
||||
isSortable?: boolean
|
||||
}
|
||||
/**
|
||||
* Customize the SQL table name
|
||||
*/
|
||||
dbName?: DBIdentifierName
|
||||
/**
|
||||
* Customize the DB enum name
|
||||
*/
|
||||
enumName?: DBIdentifierName
|
||||
hasMany?: boolean
|
||||
options: Option[]
|
||||
type: 'select'
|
||||
@@ -584,6 +595,10 @@ export type ArrayField = FieldBase & {
|
||||
} & Admin['components']
|
||||
initCollapsed?: boolean
|
||||
}
|
||||
/**
|
||||
* Customize the SQL table name
|
||||
*/
|
||||
dbName?: DBIdentifierName
|
||||
fields: Field[]
|
||||
/** Customize generated GraphQL and Typescript schema names.
|
||||
* By default it is bound to the collection.
|
||||
@@ -606,6 +621,14 @@ export type RadioField = FieldBase & {
|
||||
}
|
||||
layout?: 'horizontal' | 'vertical'
|
||||
}
|
||||
/**
|
||||
* Customize the SQL table name
|
||||
*/
|
||||
dbName?: DBIdentifierName
|
||||
/**
|
||||
* Customize the DB enum name
|
||||
*/
|
||||
enumName?: DBIdentifierName
|
||||
options: Option[]
|
||||
type: 'radio'
|
||||
}
|
||||
@@ -613,6 +636,10 @@ export type RadioField = FieldBase & {
|
||||
export type Block = {
|
||||
/** Extension point to add your custom data. */
|
||||
custom?: Record<string, any>
|
||||
/**
|
||||
* Customize the SQL table name
|
||||
*/
|
||||
dbName?: DBIdentifierName
|
||||
fields: Field[]
|
||||
/** @deprecated - please migrate to the interfaceName property instead. */
|
||||
graphQL?: {
|
||||
|
||||
@@ -49,6 +49,7 @@ const globalSchema = joi
|
||||
preview: joi.func(),
|
||||
}),
|
||||
custom: joi.object().pattern(joi.string(), joi.any()),
|
||||
dbName: joi.alternatives().try(joi.string(), joi.func()),
|
||||
endpoints: endpointsSchema,
|
||||
fields: joi.array(),
|
||||
graphQL: joi.alternatives().try(
|
||||
|
||||
@@ -16,6 +16,7 @@ import type {
|
||||
GeneratePreviewURL,
|
||||
LivePreviewConfig,
|
||||
} from '../../config/types.js'
|
||||
import type { DBIdentifierName } from '../../database/types.js'
|
||||
import type { Field } from '../../fields/config/types.js'
|
||||
import type { PayloadRequest, RequestContext } from '../../types/index.js'
|
||||
import type { Where } from '../../types/index.js'
|
||||
@@ -141,6 +142,10 @@ export type GlobalConfig = {
|
||||
admin?: GlobalAdminOptions
|
||||
/** Extension point to add your custom data. */
|
||||
custom?: Record<string, any>
|
||||
/**
|
||||
* Customize the SQL table name
|
||||
*/
|
||||
dbName?: DBIdentifierName
|
||||
endpoints?: Omit<Endpoint, 'root'>[] | false
|
||||
fields: Field[]
|
||||
graphQL?:
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import type { SanitizedCollectionConfig } from '../collections/config/types.ts'
|
||||
import type { SanitizedGlobalConfig } from '../globals/config/types.js'
|
||||
|
||||
export const getVersionsModelName = (
|
||||
entity: SanitizedCollectionConfig | SanitizedGlobalConfig,
|
||||
): string => `_${entity.slug}_versions`
|
||||
77
pnpm-lock.yaml
generated
77
pnpm-lock.yaml
generated
@@ -374,9 +374,6 @@ importers:
|
||||
mongoose:
|
||||
specifier: 6.12.3
|
||||
version: 6.12.3
|
||||
mongoose-aggregate-paginate-v2:
|
||||
specifier: 1.0.6
|
||||
version: 1.0.6
|
||||
mongoose-paginate-v2:
|
||||
specifier: 1.7.22
|
||||
version: 1.7.22
|
||||
@@ -9774,7 +9771,7 @@ packages:
|
||||
dependencies:
|
||||
loader-utils: 2.0.4
|
||||
schema-utils: 3.3.0
|
||||
webpack: 5.90.3(@swc/core@1.4.2)
|
||||
webpack: 5.90.3(@swc/core@1.4.2)(esbuild@0.19.12)(webpack-cli@5.1.4)
|
||||
dev: true
|
||||
|
||||
/file-type@16.5.4:
|
||||
@@ -12275,7 +12272,7 @@ packages:
|
||||
dependencies:
|
||||
loader-utils: 2.0.4
|
||||
schema-utils: 3.3.0
|
||||
webpack: 5.90.3(@swc/core@1.4.2)
|
||||
webpack: 5.90.3(@swc/core@1.4.2)(esbuild@0.19.12)(webpack-cli@5.1.4)
|
||||
webpack-sources: 1.4.3
|
||||
dev: true
|
||||
|
||||
@@ -12416,11 +12413,6 @@ packages:
|
||||
'@mongodb-js/saslprep': 1.1.4
|
||||
dev: true
|
||||
|
||||
/mongoose-aggregate-paginate-v2@1.0.6:
|
||||
resolution: {integrity: sha512-UuALu+mjhQa1K9lMQvjLL3vm3iALvNw8PQNIh2gp1b+tO5hUa0NC0Wf6/8QrT9PSJVTihXaD8hQVy3J4e0jO0Q==}
|
||||
engines: {node: '>=4.0.0'}
|
||||
dev: false
|
||||
|
||||
/mongoose-paginate-v2@1.7.22:
|
||||
resolution: {integrity: sha512-xW5GugkE21DJiu9e13EOxKt4ejEKQkRP/S1PkkXRjnk2rRZVKBcld1nPV+VJ/YCPfm8hb3sz9OvI7O38RmixkA==}
|
||||
engines: {node: '>=4.0.0'}
|
||||
@@ -15656,31 +15648,6 @@ packages:
|
||||
webpack: 5.90.3(@swc/core@1.4.2)(esbuild@0.19.12)(webpack-cli@5.1.4)
|
||||
dev: true
|
||||
|
||||
/terser-webpack-plugin@5.3.10(@swc/core@1.4.2)(webpack@5.90.3):
|
||||
resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==}
|
||||
engines: {node: '>= 10.13.0'}
|
||||
peerDependencies:
|
||||
'@swc/core': '*'
|
||||
esbuild: '*'
|
||||
uglify-js: '*'
|
||||
webpack: ^5.1.0
|
||||
peerDependenciesMeta:
|
||||
'@swc/core':
|
||||
optional: true
|
||||
esbuild:
|
||||
optional: true
|
||||
uglify-js:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.23
|
||||
'@swc/core': 1.4.2
|
||||
jest-worker: 27.5.1
|
||||
schema-utils: 3.3.0
|
||||
serialize-javascript: 6.0.2
|
||||
terser: 5.28.1
|
||||
webpack: 5.90.3(@swc/core@1.4.2)
|
||||
dev: true
|
||||
|
||||
/terser@5.28.1:
|
||||
resolution: {integrity: sha512-wM+bZp54v/E9eRRGXb5ZFDvinrJIOaTapx3WUokyVGZu5ucVCK55zEgGd5Dl2fSr3jUo5sDiERErUWLY6QPFyA==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -16440,46 +16407,6 @@ packages:
|
||||
engines: {node: '>=10.13.0'}
|
||||
dev: true
|
||||
|
||||
/webpack@5.90.3(@swc/core@1.4.2):
|
||||
resolution: {integrity: sha512-h6uDYlWCctQRuXBs1oYpVe6sFcWedl0dpcVaTf/YF67J9bKvwJajFulMVSYKHrksMB3I/pIagRzDxwxkebuzKA==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
webpack-cli: '*'
|
||||
peerDependenciesMeta:
|
||||
webpack-cli:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@types/eslint-scope': 3.7.7
|
||||
'@types/estree': 1.0.5
|
||||
'@webassemblyjs/ast': 1.11.6
|
||||
'@webassemblyjs/wasm-edit': 1.11.6
|
||||
'@webassemblyjs/wasm-parser': 1.11.6
|
||||
acorn: 8.11.3
|
||||
acorn-import-assertions: 1.9.0(acorn@8.11.3)
|
||||
browserslist: 4.23.0
|
||||
chrome-trace-event: 1.0.3
|
||||
enhanced-resolve: 5.15.1
|
||||
es-module-lexer: 1.4.1
|
||||
eslint-scope: 5.1.1
|
||||
events: 3.3.0
|
||||
glob-to-regexp: 0.4.1
|
||||
graceful-fs: 4.2.11
|
||||
json-parse-even-better-errors: 2.3.1
|
||||
loader-runner: 4.3.0
|
||||
mime-types: 2.1.35
|
||||
neo-async: 2.6.2
|
||||
schema-utils: 3.3.0
|
||||
tapable: 2.2.1
|
||||
terser-webpack-plugin: 5.3.10(@swc/core@1.4.2)(webpack@5.90.3)
|
||||
watchpack: 2.4.0
|
||||
webpack-sources: 3.2.3
|
||||
transitivePeerDependencies:
|
||||
- '@swc/core'
|
||||
- esbuild
|
||||
- uglify-js
|
||||
dev: true
|
||||
|
||||
/webpack@5.90.3(@swc/core@1.4.2)(esbuild@0.19.12)(webpack-cli@5.1.4):
|
||||
resolution: {integrity: sha512-h6uDYlWCctQRuXBs1oYpVe6sFcWedl0dpcVaTf/YF67J9bKvwJajFulMVSYKHrksMB3I/pIagRzDxwxkebuzKA==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
|
||||
@@ -63,7 +63,98 @@ export default buildConfigWithDefaults({
|
||||
singular: 'Relation B',
|
||||
},
|
||||
},
|
||||
{
|
||||
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: true,
|
||||
},
|
||||
],
|
||||
globals: [
|
||||
{
|
||||
slug: 'global',
|
||||
// @ts-expect-error
|
||||
dbName: 'customGlobal',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
versions: true,
|
||||
},
|
||||
],
|
||||
localization: {
|
||||
defaultLocale: 'en',
|
||||
locales: ['en', 'es'],
|
||||
},
|
||||
onInit: async (payload) => {
|
||||
await payload.create({
|
||||
collection: 'users',
|
||||
|
||||
@@ -131,6 +131,59 @@ describe('database', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('schema', () => {
|
||||
it('should use custom dbNames', () => {
|
||||
expect(payload.db).toBeDefined()
|
||||
|
||||
if (payload.db.name === 'mongoose') {
|
||||
// @ts-expect-error
|
||||
const db: MongooseAdapter = payload.db
|
||||
|
||||
expect(db.collections['custom-schema'].modelName).toStrictEqual('customs')
|
||||
expect(db.versions['custom-schema'].modelName).toStrictEqual('_customs_versions')
|
||||
expect(db.versions.global.modelName).toStrictEqual('_customGlobal_versions')
|
||||
} else {
|
||||
// @ts-expect-error
|
||||
const db: PostgresAdapter = payload.db
|
||||
|
||||
// collection
|
||||
expect(db.tables.customs).toBeDefined()
|
||||
|
||||
// collection versions
|
||||
expect(db.tables._customs_v).toBeDefined()
|
||||
|
||||
// collection relationships
|
||||
expect(db.tables.customs_rels).toBeDefined()
|
||||
|
||||
// collection localized
|
||||
expect(db.tables.customs_locales).toBeDefined()
|
||||
|
||||
// global
|
||||
expect(db.tables.customGlobal).toBeDefined()
|
||||
expect(db.tables._customGlobal_v).toBeDefined()
|
||||
|
||||
// select
|
||||
expect(db.tables.customs_customSelect).toBeDefined()
|
||||
|
||||
// array
|
||||
expect(db.tables.customArrays).toBeDefined()
|
||||
|
||||
// array localized
|
||||
expect(db.tables.customArrays_locales).toBeDefined()
|
||||
|
||||
// blocks
|
||||
expect(db.tables.customBlocks).toBeDefined()
|
||||
|
||||
// localized blocks
|
||||
expect(db.tables.customBlocks_locales).toBeDefined()
|
||||
|
||||
// enum names
|
||||
expect(db.enums.selectEnum).toBeDefined()
|
||||
expect(db.enums.radioEnum).toBeDefined()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('transactions', () => {
|
||||
describe('local api', () => {
|
||||
it('should commit multiple operations in isolation', async () => {
|
||||
|
||||
Reference in New Issue
Block a user