perf(drizzle): single-roundtrip db updates for simple collections (#13186)
Currently, an optimized DB update (simple data => no delete-and-create-row) does the following: 1. sql UPDATE 2. sql SELECT This PR reduces this further to one single DB call for simple collections: 1. sql UPDATE with RETURNING() This only works for simple collections that do not have any fields that need to be fetched from other tables. If a collection has fields like relationship or blocks, we'll need that separate SELECT call to join in the other tables. In 4.0, we can remove all "complex" fields from the jobs collection and replace them with a JSON field to make use of this optimization --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1210803039809814
This commit is contained in:
19
test/database/config.postgreslogs.ts
Normal file
19
test/database/config.postgreslogs.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/* eslint-disable no-restricted-exports */
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
|
||||
import { getConfig } from './getConfig.js'
|
||||
|
||||
const config = getConfig()
|
||||
|
||||
import { postgresAdapter } from '@payloadcms/db-postgres'
|
||||
|
||||
export const databaseAdapter = postgresAdapter({
|
||||
pool: {
|
||||
connectionString: process.env.POSTGRES_URL || 'postgres://127.0.0.1:5432/payloadtests',
|
||||
},
|
||||
logger: true,
|
||||
})
|
||||
|
||||
export default buildConfigWithDefaults({
|
||||
...config,
|
||||
db: databaseAdapter,
|
||||
})
|
||||
@@ -1,933 +1,4 @@
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import path from 'path'
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
import type { TextField } from 'payload'
|
||||
|
||||
import { randomUUID } from 'crypto'
|
||||
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
|
||||
import { seed } from './seed.js'
|
||||
import {
|
||||
customIDsSlug,
|
||||
customSchemaSlug,
|
||||
defaultValuesSlug,
|
||||
errorOnUnnamedFieldsSlug,
|
||||
fakeCustomIDsSlug,
|
||||
fieldsPersistanceSlug,
|
||||
pgMigrationSlug,
|
||||
placesSlug,
|
||||
postsSlug,
|
||||
relationASlug,
|
||||
relationBSlug,
|
||||
relationshipsMigrationSlug,
|
||||
} from './shared.js'
|
||||
import { getConfig } from './getConfig.js'
|
||||
|
||||
const defaultValueField: TextField = {
|
||||
name: 'defaultValue',
|
||||
type: 'text',
|
||||
defaultValue: 'default value from database',
|
||||
}
|
||||
|
||||
export default buildConfigWithDefaults({
|
||||
admin: {
|
||||
importMap: {
|
||||
baseDir: path.resolve(dirname),
|
||||
},
|
||||
},
|
||||
collections: [
|
||||
{
|
||||
slug: 'categories',
|
||||
versions: { drafts: true },
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'title',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'categories-custom-id',
|
||||
versions: { drafts: true },
|
||||
fields: [
|
||||
{
|
||||
type: 'number',
|
||||
name: 'id',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: postsSlug,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
// access: { read: () => false },
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
relationTo: 'categories',
|
||||
name: 'category',
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
relationTo: 'categories-custom-id',
|
||||
name: 'categoryCustomID',
|
||||
},
|
||||
{
|
||||
name: 'localized',
|
||||
type: 'text',
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'number',
|
||||
type: 'number',
|
||||
},
|
||||
{
|
||||
type: 'blocks',
|
||||
name: 'blocks',
|
||||
blocks: [
|
||||
{
|
||||
slug: 'block-third',
|
||||
fields: [
|
||||
{
|
||||
type: 'blocks',
|
||||
name: 'nested',
|
||||
blocks: [
|
||||
{
|
||||
slug: 'block-fourth',
|
||||
fields: [
|
||||
{
|
||||
type: 'blocks',
|
||||
name: 'nested',
|
||||
blocks: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'tabs',
|
||||
tabs: [
|
||||
{
|
||||
name: 'D1',
|
||||
fields: [
|
||||
{
|
||||
name: 'D2',
|
||||
type: 'group',
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
type: 'collapsible',
|
||||
fields: [
|
||||
{
|
||||
type: 'tabs',
|
||||
tabs: [
|
||||
{
|
||||
fields: [
|
||||
{
|
||||
name: 'D3',
|
||||
type: 'group',
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
type: 'collapsible',
|
||||
fields: [
|
||||
{
|
||||
name: 'D4',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
label: 'Collapsible2',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
label: 'Tab1',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
label: 'Collapsible2',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
label: 'Tab1',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'hasTransaction',
|
||||
type: 'checkbox',
|
||||
hooks: {
|
||||
beforeChange: [({ req }) => !!req.transactionID],
|
||||
},
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'throwAfterChange',
|
||||
type: 'checkbox',
|
||||
defaultValue: false,
|
||||
hooks: {
|
||||
afterChange: [
|
||||
({ value }) => {
|
||||
if (value) {
|
||||
throw new Error('throw after change')
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'arrayWithIDs',
|
||||
type: 'array',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'blocksWithIDs',
|
||||
type: 'blocks',
|
||||
blocks: [
|
||||
{
|
||||
slug: 'block-first',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
name: 'group',
|
||||
fields: [{ name: 'text', type: 'text' }],
|
||||
},
|
||||
{
|
||||
type: 'tabs',
|
||||
tabs: [
|
||||
{
|
||||
name: 'tab',
|
||||
fields: [{ name: 'text', type: 'text' }],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
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: errorOnUnnamedFieldsSlug,
|
||||
fields: [
|
||||
{
|
||||
type: 'tabs',
|
||||
tabs: [
|
||||
{
|
||||
label: 'UnnamedTab',
|
||||
fields: [
|
||||
{
|
||||
name: 'groupWithinUnnamedTab',
|
||||
type: 'group',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: defaultValuesSlug,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
defaultValueField,
|
||||
{
|
||||
name: 'array',
|
||||
type: 'array',
|
||||
// default array with one object to test subfield defaultValue properties for Mongoose
|
||||
defaultValue: [{}],
|
||||
fields: [defaultValueField],
|
||||
},
|
||||
{
|
||||
name: 'group',
|
||||
type: 'group',
|
||||
// we need to have to use as default in order to have subfield defaultValue properties directly for Mongoose
|
||||
defaultValue: {},
|
||||
fields: [defaultValueField],
|
||||
},
|
||||
{
|
||||
name: 'select',
|
||||
type: 'select',
|
||||
defaultValue: 'default',
|
||||
options: [
|
||||
{ value: 'option0', label: 'Option 0' },
|
||||
{ value: 'option1', label: 'Option 1' },
|
||||
{ value: 'default', label: 'Default' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'point',
|
||||
type: 'point',
|
||||
defaultValue: [10, 20],
|
||||
},
|
||||
{
|
||||
name: 'escape',
|
||||
type: 'text',
|
||||
defaultValue: "Thanks, we're excited for you to join us.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: relationASlug,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'richText',
|
||||
type: 'richText',
|
||||
},
|
||||
],
|
||||
labels: {
|
||||
plural: 'Relation As',
|
||||
singular: 'Relation A',
|
||||
},
|
||||
},
|
||||
{
|
||||
slug: relationBSlug,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'relationship',
|
||||
type: 'relationship',
|
||||
relationTo: 'relation-a',
|
||||
},
|
||||
{
|
||||
name: 'richText',
|
||||
type: 'richText',
|
||||
},
|
||||
],
|
||||
labels: {
|
||||
plural: 'Relation Bs',
|
||||
singular: 'Relation B',
|
||||
},
|
||||
},
|
||||
{
|
||||
slug: pgMigrationSlug,
|
||||
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',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
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: customSchemaSlug,
|
||||
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-second',
|
||||
dbName: 'customBlocks',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'localizedText',
|
||||
type: 'text',
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
versions: {
|
||||
drafts: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
slug: placesSlug,
|
||||
fields: [
|
||||
{
|
||||
name: 'country',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'city',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'virtual-relations',
|
||||
admin: { useAsTitle: 'postTitle' },
|
||||
access: { read: () => true },
|
||||
fields: [
|
||||
{
|
||||
name: 'postTitle',
|
||||
type: 'text',
|
||||
virtual: 'post.title',
|
||||
},
|
||||
{
|
||||
name: 'postTitleHidden',
|
||||
type: 'text',
|
||||
virtual: 'post.title',
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
name: 'postCategoryTitle',
|
||||
type: 'text',
|
||||
virtual: 'post.category.title',
|
||||
},
|
||||
{
|
||||
name: 'postCategoryID',
|
||||
type: 'json',
|
||||
virtual: 'post.category.id',
|
||||
},
|
||||
{
|
||||
name: 'postCategoryCustomID',
|
||||
type: 'number',
|
||||
virtual: 'post.categoryCustomID.id',
|
||||
},
|
||||
{
|
||||
name: 'postID',
|
||||
type: 'json',
|
||||
virtual: 'post.id',
|
||||
},
|
||||
{
|
||||
name: 'postLocalized',
|
||||
type: 'text',
|
||||
virtual: 'post.localized',
|
||||
},
|
||||
{
|
||||
name: 'post',
|
||||
type: 'relationship',
|
||||
relationTo: 'posts',
|
||||
},
|
||||
{
|
||||
name: 'customID',
|
||||
type: 'relationship',
|
||||
relationTo: 'custom-ids',
|
||||
},
|
||||
{
|
||||
name: 'customIDValue',
|
||||
type: 'text',
|
||||
virtual: 'customID.id',
|
||||
},
|
||||
],
|
||||
versions: { drafts: true },
|
||||
},
|
||||
{
|
||||
slug: fieldsPersistanceSlug,
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
virtual: true,
|
||||
},
|
||||
{
|
||||
name: 'textHooked',
|
||||
type: 'text',
|
||||
virtual: true,
|
||||
hooks: { afterRead: [() => 'hooked'] },
|
||||
},
|
||||
{
|
||||
name: 'array',
|
||||
type: 'array',
|
||||
virtual: true,
|
||||
fields: [],
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'textWithinRow',
|
||||
virtual: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'collapsible',
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'textWithinCollapsible',
|
||||
virtual: true,
|
||||
},
|
||||
],
|
||||
label: 'Colllapsible',
|
||||
},
|
||||
{
|
||||
type: 'tabs',
|
||||
tabs: [
|
||||
{
|
||||
label: 'tab',
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'textWithinTabs',
|
||||
virtual: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: customIDsSlug,
|
||||
fields: [
|
||||
{
|
||||
name: 'id',
|
||||
type: 'text',
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
hooks: {
|
||||
beforeChange: [
|
||||
({ value, operation }) => {
|
||||
if (operation === 'create') {
|
||||
return randomUUID()
|
||||
}
|
||||
return value
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
versions: { drafts: true },
|
||||
},
|
||||
{
|
||||
slug: fakeCustomIDsSlug,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'group',
|
||||
type: 'group',
|
||||
fields: [
|
||||
{
|
||||
name: 'id',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'tabs',
|
||||
tabs: [
|
||||
{
|
||||
name: 'myTab',
|
||||
fields: [
|
||||
{
|
||||
name: 'id',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: relationshipsMigrationSlug,
|
||||
fields: [
|
||||
{
|
||||
type: 'relationship',
|
||||
relationTo: 'default-values',
|
||||
name: 'relationship',
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
relationTo: ['default-values'],
|
||||
name: 'relationship_2',
|
||||
},
|
||||
],
|
||||
versions: true,
|
||||
},
|
||||
{
|
||||
slug: 'compound-indexes',
|
||||
fields: [
|
||||
{
|
||||
name: 'one',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'two',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'three',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'group',
|
||||
type: 'group',
|
||||
fields: [
|
||||
{
|
||||
name: 'four',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
indexes: [
|
||||
{
|
||||
fields: ['one', 'two'],
|
||||
unique: true,
|
||||
},
|
||||
{
|
||||
fields: ['three', 'group.four'],
|
||||
unique: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'aliases',
|
||||
fields: [
|
||||
{
|
||||
name: 'thisIsALongFieldNameThatCanCauseAPostgresErrorEvenThoughWeSetAShorterDBName',
|
||||
dbName: 'shortname',
|
||||
type: 'array',
|
||||
fields: [
|
||||
{
|
||||
name: 'nestedArray',
|
||||
type: 'array',
|
||||
dbName: 'short_nested_1',
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'blocks-docs',
|
||||
fields: [
|
||||
{
|
||||
type: 'blocks',
|
||||
localized: true,
|
||||
blocks: [
|
||||
{
|
||||
slug: 'cta',
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
name: 'testBlocksLocalized',
|
||||
},
|
||||
{
|
||||
type: 'blocks',
|
||||
blocks: [
|
||||
{
|
||||
slug: 'cta',
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
name: 'testBlocks',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'unique-fields',
|
||||
fields: [
|
||||
{
|
||||
name: 'slugField',
|
||||
type: 'text',
|
||||
unique: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
globals: [
|
||||
{
|
||||
slug: 'header',
|
||||
fields: [
|
||||
{
|
||||
name: 'itemsLvl1',
|
||||
type: 'array',
|
||||
dbName: 'header_items_lvl1',
|
||||
fields: [
|
||||
{
|
||||
name: 'label',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'itemsLvl2',
|
||||
type: 'array',
|
||||
dbName: 'header_items_lvl2',
|
||||
fields: [
|
||||
{
|
||||
name: 'label',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'itemsLvl3',
|
||||
type: 'array',
|
||||
dbName: 'header_items_lvl3',
|
||||
fields: [
|
||||
{
|
||||
name: 'label',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'itemsLvl4',
|
||||
type: 'array',
|
||||
dbName: 'header_items_lvl4',
|
||||
fields: [
|
||||
{
|
||||
name: 'label',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'global',
|
||||
dbName: 'customGlobal',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
versions: true,
|
||||
},
|
||||
{
|
||||
slug: 'global-2',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'global-3',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'virtual-relation-global',
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'postTitle',
|
||||
virtual: 'post.title',
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
name: 'post',
|
||||
relationTo: 'posts',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
localization: {
|
||||
defaultLocale: 'en',
|
||||
locales: ['en', 'es'],
|
||||
},
|
||||
onInit: async (payload) => {
|
||||
if (process.env.SEED_IN_CONFIG_ONINIT !== 'false') {
|
||||
await seed(payload)
|
||||
}
|
||||
},
|
||||
typescript: {
|
||||
outputFile: path.resolve(dirname, 'payload-types.ts'),
|
||||
},
|
||||
})
|
||||
|
||||
export const postDoc = {
|
||||
title: 'test post',
|
||||
}
|
||||
export default buildConfigWithDefaults(getConfig())
|
||||
|
||||
942
test/database/getConfig.ts
Normal file
942
test/database/getConfig.ts
Normal file
@@ -0,0 +1,942 @@
|
||||
import type { Config, TextField } from 'payload'
|
||||
|
||||
import { randomUUID } from 'crypto'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import path from 'path'
|
||||
|
||||
import { seed } from './seed.js'
|
||||
import {
|
||||
customIDsSlug,
|
||||
customSchemaSlug,
|
||||
defaultValuesSlug,
|
||||
errorOnUnnamedFieldsSlug,
|
||||
fakeCustomIDsSlug,
|
||||
fieldsPersistanceSlug,
|
||||
pgMigrationSlug,
|
||||
placesSlug,
|
||||
postsSlug,
|
||||
relationASlug,
|
||||
relationBSlug,
|
||||
relationshipsMigrationSlug,
|
||||
} from './shared.js'
|
||||
|
||||
const defaultValueField: TextField = {
|
||||
name: 'defaultValue',
|
||||
type: 'text',
|
||||
defaultValue: 'default value from database',
|
||||
}
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
export const getConfig: () => Partial<Config> = () => ({
|
||||
admin: {
|
||||
importMap: {
|
||||
baseDir: path.resolve(dirname),
|
||||
},
|
||||
},
|
||||
collections: [
|
||||
{
|
||||
slug: 'categories',
|
||||
versions: { drafts: true },
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'title',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'simple',
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'text',
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
name: 'number',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'categories-custom-id',
|
||||
versions: { drafts: true },
|
||||
fields: [
|
||||
{
|
||||
type: 'number',
|
||||
name: 'id',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: postsSlug,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
// access: { read: () => false },
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
relationTo: 'categories',
|
||||
name: 'category',
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
relationTo: 'categories-custom-id',
|
||||
name: 'categoryCustomID',
|
||||
},
|
||||
{
|
||||
name: 'localized',
|
||||
type: 'text',
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'number',
|
||||
type: 'number',
|
||||
},
|
||||
{
|
||||
type: 'blocks',
|
||||
name: 'blocks',
|
||||
blocks: [
|
||||
{
|
||||
slug: 'block-third',
|
||||
fields: [
|
||||
{
|
||||
type: 'blocks',
|
||||
name: 'nested',
|
||||
blocks: [
|
||||
{
|
||||
slug: 'block-fourth',
|
||||
fields: [
|
||||
{
|
||||
type: 'blocks',
|
||||
name: 'nested',
|
||||
blocks: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'tabs',
|
||||
tabs: [
|
||||
{
|
||||
name: 'D1',
|
||||
fields: [
|
||||
{
|
||||
name: 'D2',
|
||||
type: 'group',
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
type: 'collapsible',
|
||||
fields: [
|
||||
{
|
||||
type: 'tabs',
|
||||
tabs: [
|
||||
{
|
||||
fields: [
|
||||
{
|
||||
name: 'D3',
|
||||
type: 'group',
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
type: 'collapsible',
|
||||
fields: [
|
||||
{
|
||||
name: 'D4',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
label: 'Collapsible2',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
label: 'Tab1',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
label: 'Collapsible2',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
label: 'Tab1',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'hasTransaction',
|
||||
type: 'checkbox',
|
||||
hooks: {
|
||||
beforeChange: [({ req }) => !!req.transactionID],
|
||||
},
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'throwAfterChange',
|
||||
type: 'checkbox',
|
||||
defaultValue: false,
|
||||
hooks: {
|
||||
afterChange: [
|
||||
({ value }) => {
|
||||
if (value) {
|
||||
throw new Error('throw after change')
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'arrayWithIDs',
|
||||
type: 'array',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'blocksWithIDs',
|
||||
type: 'blocks',
|
||||
blocks: [
|
||||
{
|
||||
slug: 'block-first',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
name: 'group',
|
||||
fields: [{ name: 'text', type: 'text' }],
|
||||
},
|
||||
{
|
||||
type: 'tabs',
|
||||
tabs: [
|
||||
{
|
||||
name: 'tab',
|
||||
fields: [{ name: 'text', type: 'text' }],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
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: errorOnUnnamedFieldsSlug,
|
||||
fields: [
|
||||
{
|
||||
type: 'tabs',
|
||||
tabs: [
|
||||
{
|
||||
label: 'UnnamedTab',
|
||||
fields: [
|
||||
{
|
||||
name: 'groupWithinUnnamedTab',
|
||||
type: 'group',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: defaultValuesSlug,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
defaultValueField,
|
||||
{
|
||||
name: 'array',
|
||||
type: 'array',
|
||||
// default array with one object to test subfield defaultValue properties for Mongoose
|
||||
defaultValue: [{}],
|
||||
fields: [defaultValueField],
|
||||
},
|
||||
{
|
||||
name: 'group',
|
||||
type: 'group',
|
||||
// we need to have to use as default in order to have subfield defaultValue properties directly for Mongoose
|
||||
defaultValue: {},
|
||||
fields: [defaultValueField],
|
||||
},
|
||||
{
|
||||
name: 'select',
|
||||
type: 'select',
|
||||
defaultValue: 'default',
|
||||
options: [
|
||||
{ value: 'option0', label: 'Option 0' },
|
||||
{ value: 'option1', label: 'Option 1' },
|
||||
{ value: 'default', label: 'Default' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'point',
|
||||
type: 'point',
|
||||
defaultValue: [10, 20],
|
||||
},
|
||||
{
|
||||
name: 'escape',
|
||||
type: 'text',
|
||||
defaultValue: "Thanks, we're excited for you to join us.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: relationASlug,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'richText',
|
||||
type: 'richText',
|
||||
},
|
||||
],
|
||||
labels: {
|
||||
plural: 'Relation As',
|
||||
singular: 'Relation A',
|
||||
},
|
||||
},
|
||||
{
|
||||
slug: relationBSlug,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'relationship',
|
||||
type: 'relationship',
|
||||
relationTo: 'relation-a',
|
||||
},
|
||||
{
|
||||
name: 'richText',
|
||||
type: 'richText',
|
||||
},
|
||||
],
|
||||
labels: {
|
||||
plural: 'Relation Bs',
|
||||
singular: 'Relation B',
|
||||
},
|
||||
},
|
||||
{
|
||||
slug: pgMigrationSlug,
|
||||
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',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
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: customSchemaSlug,
|
||||
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-second',
|
||||
dbName: 'customBlocks',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'localizedText',
|
||||
type: 'text',
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
versions: {
|
||||
drafts: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
slug: placesSlug,
|
||||
fields: [
|
||||
{
|
||||
name: 'country',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'city',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'virtual-relations',
|
||||
admin: { useAsTitle: 'postTitle' },
|
||||
access: { read: () => true },
|
||||
fields: [
|
||||
{
|
||||
name: 'postTitle',
|
||||
type: 'text',
|
||||
virtual: 'post.title',
|
||||
},
|
||||
{
|
||||
name: 'postTitleHidden',
|
||||
type: 'text',
|
||||
virtual: 'post.title',
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
name: 'postCategoryTitle',
|
||||
type: 'text',
|
||||
virtual: 'post.category.title',
|
||||
},
|
||||
{
|
||||
name: 'postCategoryID',
|
||||
type: 'json',
|
||||
virtual: 'post.category.id',
|
||||
},
|
||||
{
|
||||
name: 'postCategoryCustomID',
|
||||
type: 'number',
|
||||
virtual: 'post.categoryCustomID.id',
|
||||
},
|
||||
{
|
||||
name: 'postID',
|
||||
type: 'json',
|
||||
virtual: 'post.id',
|
||||
},
|
||||
{
|
||||
name: 'postLocalized',
|
||||
type: 'text',
|
||||
virtual: 'post.localized',
|
||||
},
|
||||
{
|
||||
name: 'post',
|
||||
type: 'relationship',
|
||||
relationTo: 'posts',
|
||||
},
|
||||
{
|
||||
name: 'customID',
|
||||
type: 'relationship',
|
||||
relationTo: 'custom-ids',
|
||||
},
|
||||
{
|
||||
name: 'customIDValue',
|
||||
type: 'text',
|
||||
virtual: 'customID.id',
|
||||
},
|
||||
],
|
||||
versions: { drafts: true },
|
||||
},
|
||||
{
|
||||
slug: fieldsPersistanceSlug,
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
virtual: true,
|
||||
},
|
||||
{
|
||||
name: 'textHooked',
|
||||
type: 'text',
|
||||
virtual: true,
|
||||
hooks: { afterRead: [() => 'hooked'] },
|
||||
},
|
||||
{
|
||||
name: 'array',
|
||||
type: 'array',
|
||||
virtual: true,
|
||||
fields: [],
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'textWithinRow',
|
||||
virtual: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'collapsible',
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'textWithinCollapsible',
|
||||
virtual: true,
|
||||
},
|
||||
],
|
||||
label: 'Colllapsible',
|
||||
},
|
||||
{
|
||||
type: 'tabs',
|
||||
tabs: [
|
||||
{
|
||||
label: 'tab',
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'textWithinTabs',
|
||||
virtual: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: customIDsSlug,
|
||||
fields: [
|
||||
{
|
||||
name: 'id',
|
||||
type: 'text',
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
hooks: {
|
||||
beforeChange: [
|
||||
({ value, operation }) => {
|
||||
if (operation === 'create') {
|
||||
return randomUUID()
|
||||
}
|
||||
return value
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
versions: { drafts: true },
|
||||
},
|
||||
{
|
||||
slug: fakeCustomIDsSlug,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'group',
|
||||
type: 'group',
|
||||
fields: [
|
||||
{
|
||||
name: 'id',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'tabs',
|
||||
tabs: [
|
||||
{
|
||||
name: 'myTab',
|
||||
fields: [
|
||||
{
|
||||
name: 'id',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: relationshipsMigrationSlug,
|
||||
fields: [
|
||||
{
|
||||
type: 'relationship',
|
||||
relationTo: 'default-values',
|
||||
name: 'relationship',
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
relationTo: ['default-values'],
|
||||
name: 'relationship_2',
|
||||
},
|
||||
],
|
||||
versions: true,
|
||||
},
|
||||
{
|
||||
slug: 'compound-indexes',
|
||||
fields: [
|
||||
{
|
||||
name: 'one',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'two',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'three',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'group',
|
||||
type: 'group',
|
||||
fields: [
|
||||
{
|
||||
name: 'four',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
indexes: [
|
||||
{
|
||||
fields: ['one', 'two'],
|
||||
unique: true,
|
||||
},
|
||||
{
|
||||
fields: ['three', 'group.four'],
|
||||
unique: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'aliases',
|
||||
fields: [
|
||||
{
|
||||
name: 'thisIsALongFieldNameThatCanCauseAPostgresErrorEvenThoughWeSetAShorterDBName',
|
||||
dbName: 'shortname',
|
||||
type: 'array',
|
||||
fields: [
|
||||
{
|
||||
name: 'nestedArray',
|
||||
type: 'array',
|
||||
dbName: 'short_nested_1',
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'blocks-docs',
|
||||
fields: [
|
||||
{
|
||||
type: 'blocks',
|
||||
localized: true,
|
||||
blocks: [
|
||||
{
|
||||
slug: 'cta',
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
name: 'testBlocksLocalized',
|
||||
},
|
||||
{
|
||||
type: 'blocks',
|
||||
blocks: [
|
||||
{
|
||||
slug: 'cta',
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
name: 'testBlocks',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'unique-fields',
|
||||
fields: [
|
||||
{
|
||||
name: 'slugField',
|
||||
type: 'text',
|
||||
unique: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
globals: [
|
||||
{
|
||||
slug: 'header',
|
||||
fields: [
|
||||
{
|
||||
name: 'itemsLvl1',
|
||||
type: 'array',
|
||||
dbName: 'header_items_lvl1',
|
||||
fields: [
|
||||
{
|
||||
name: 'label',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'itemsLvl2',
|
||||
type: 'array',
|
||||
dbName: 'header_items_lvl2',
|
||||
fields: [
|
||||
{
|
||||
name: 'label',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'itemsLvl3',
|
||||
type: 'array',
|
||||
dbName: 'header_items_lvl3',
|
||||
fields: [
|
||||
{
|
||||
name: 'label',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'itemsLvl4',
|
||||
type: 'array',
|
||||
dbName: 'header_items_lvl4',
|
||||
fields: [
|
||||
{
|
||||
name: 'label',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'global',
|
||||
dbName: 'customGlobal',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
versions: true,
|
||||
},
|
||||
{
|
||||
slug: 'global-2',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'global-3',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'virtual-relation-global',
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'postTitle',
|
||||
virtual: 'post.title',
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
name: 'post',
|
||||
relationTo: 'posts',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
localization: {
|
||||
defaultLocale: 'en',
|
||||
locales: ['en', 'es'],
|
||||
},
|
||||
onInit: async (payload) => {
|
||||
if (process.env.SEED_IN_CONFIG_ONINIT !== 'false') {
|
||||
await seed(payload)
|
||||
}
|
||||
},
|
||||
typescript: {
|
||||
outputFile: path.resolve(dirname, 'payload-types.ts'),
|
||||
},
|
||||
})
|
||||
@@ -68,6 +68,7 @@ export interface Config {
|
||||
blocks: {};
|
||||
collections: {
|
||||
categories: Category;
|
||||
simple: Simple;
|
||||
'categories-custom-id': CategoriesCustomId;
|
||||
posts: Post;
|
||||
'error-on-unnamed-fields': ErrorOnUnnamedField;
|
||||
@@ -94,6 +95,7 @@ export interface Config {
|
||||
collectionsJoins: {};
|
||||
collectionsSelect: {
|
||||
categories: CategoriesSelect<false> | CategoriesSelect<true>;
|
||||
simple: SimpleSelect<false> | SimpleSelect<true>;
|
||||
'categories-custom-id': CategoriesCustomIdSelect<false> | CategoriesCustomIdSelect<true>;
|
||||
posts: PostsSelect<false> | PostsSelect<true>;
|
||||
'error-on-unnamed-fields': ErrorOnUnnamedFieldsSelect<false> | ErrorOnUnnamedFieldsSelect<true>;
|
||||
@@ -172,6 +174,17 @@ export interface Category {
|
||||
createdAt: string;
|
||||
_status?: ('draft' | 'published') | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "simple".
|
||||
*/
|
||||
export interface Simple {
|
||||
id: string;
|
||||
text?: string | null;
|
||||
number?: number | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "categories-custom-id".
|
||||
@@ -608,6 +621,10 @@ export interface PayloadLockedDocument {
|
||||
relationTo: 'categories';
|
||||
value: string | Category;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'simple';
|
||||
value: string | Simple;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'categories-custom-id';
|
||||
value: number | CategoriesCustomId;
|
||||
@@ -736,6 +753,16 @@ export interface CategoriesSelect<T extends boolean = true> {
|
||||
createdAt?: T;
|
||||
_status?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "simple_select".
|
||||
*/
|
||||
export interface SimpleSelect<T extends boolean = true> {
|
||||
text?: T;
|
||||
number?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "categories-custom-id_select".
|
||||
|
||||
91
test/database/postgres-logs.int.spec.ts
Normal file
91
test/database/postgres-logs.int.spec.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import type { Payload } from 'payload'
|
||||
|
||||
/* eslint-disable jest/require-top-level-describe */
|
||||
import assert from 'assert'
|
||||
import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
import { initPayloadInt } from '../helpers/initPayloadInt.js'
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
const describePostgres = process.env.PAYLOAD_DATABASE?.startsWith('postgres')
|
||||
? describe
|
||||
: describe.skip
|
||||
|
||||
let payload: Payload
|
||||
|
||||
describePostgres('database - postgres logs', () => {
|
||||
beforeAll(async () => {
|
||||
const initialized = await initPayloadInt(
|
||||
dirname,
|
||||
undefined,
|
||||
undefined,
|
||||
'config.postgreslogs.ts',
|
||||
)
|
||||
assert(initialized.payload)
|
||||
assert(initialized.restClient)
|
||||
;({ payload } = initialized)
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await payload.destroy()
|
||||
})
|
||||
|
||||
it('ensure simple update uses optimized upsertRow with returning()', async () => {
|
||||
const doc = await payload.create({
|
||||
collection: 'simple',
|
||||
data: {
|
||||
text: 'Some title',
|
||||
number: 5,
|
||||
},
|
||||
})
|
||||
|
||||
// Count every console log
|
||||
const consoleCount = jest.spyOn(console, 'log').mockImplementation(() => {})
|
||||
|
||||
const result: any = await payload.db.updateOne({
|
||||
collection: 'simple',
|
||||
id: doc.id,
|
||||
data: {
|
||||
text: 'Updated Title',
|
||||
number: 5,
|
||||
},
|
||||
})
|
||||
|
||||
expect(result.text).toEqual('Updated Title')
|
||||
expect(result.number).toEqual(5) // Ensure the update did not reset the number field
|
||||
|
||||
expect(consoleCount).toHaveBeenCalledTimes(1) // Should be 1 single sql call if the optimization is used. If not, this would be 2 calls
|
||||
consoleCount.mockRestore()
|
||||
})
|
||||
|
||||
it('ensure simple update of complex collection uses optimized upsertRow without returning()', async () => {
|
||||
const doc = await payload.create({
|
||||
collection: 'posts',
|
||||
data: {
|
||||
title: 'Some title',
|
||||
number: 5,
|
||||
},
|
||||
})
|
||||
|
||||
// Count every console log
|
||||
const consoleCount = jest.spyOn(console, 'log').mockImplementation(() => {})
|
||||
|
||||
const result: any = await payload.db.updateOne({
|
||||
collection: 'posts',
|
||||
id: doc.id,
|
||||
data: {
|
||||
title: 'Updated Title',
|
||||
number: 5,
|
||||
},
|
||||
})
|
||||
|
||||
expect(result.title).toEqual('Updated Title')
|
||||
expect(result.number).toEqual(5) // Ensure the update did not reset the number field
|
||||
|
||||
expect(consoleCount).toHaveBeenCalledTimes(2) // Should be 2 sql call if the optimization is used (update + find). If not, this would be 5 calls
|
||||
consoleCount.mockRestore()
|
||||
})
|
||||
})
|
||||
@@ -12,11 +12,11 @@ import { fileURLToPath } from 'url'
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
const describeToUse = process.env.PAYLOAD_DATABASE?.startsWith('postgres')
|
||||
const describePostgres = process.env.PAYLOAD_DATABASE?.startsWith('postgres')
|
||||
? describe
|
||||
: describe.skip
|
||||
|
||||
describeToUse('postgres vector custom column', () => {
|
||||
describePostgres('postgres vector custom column', () => {
|
||||
const vectorColumnQueryTest = async (vectorType: string) => {
|
||||
const {
|
||||
databaseAdapter,
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
import type { Payload } from 'payload'
|
||||
|
||||
import path from 'path'
|
||||
import { getFileByPath } from 'payload'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
import { devUser } from '../credentials.js'
|
||||
import { seedDB } from '../helpers/seed.js'
|
||||
import { collectionSlugs } from './shared.js'
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
export const _seed = async (_payload: Payload) => {
|
||||
await _payload.create({
|
||||
|
||||
@@ -20,18 +20,3 @@ export const customIDsSlug = 'custom-ids'
|
||||
export const fakeCustomIDsSlug = 'fake-custom-ids'
|
||||
|
||||
export const relationshipsMigrationSlug = 'relationships-migration'
|
||||
|
||||
export const collectionSlugs = [
|
||||
postsSlug,
|
||||
errorOnUnnamedFieldsSlug,
|
||||
defaultValuesSlug,
|
||||
relationASlug,
|
||||
relationBSlug,
|
||||
pgMigrationSlug,
|
||||
customSchemaSlug,
|
||||
placesSlug,
|
||||
fieldsPersistanceSlug,
|
||||
customIDsSlug,
|
||||
fakeCustomIDsSlug,
|
||||
relationshipsMigrationSlug,
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user