Files
payloadcms/test/database/getConfig.ts
Sasha d0543a463f fix: support hasMany: true relationships in findDistinct (#13840)
Previously, the `findDistinct` operation didn't work correctly for
relationships with `hasMany: true`. This PR fixes it.
2025-09-17 18:21:24 +03:00

1008 lines
22 KiB
TypeScript

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: 'noTimeStamps',
timestamps: false,
fields: [
{
type: 'text',
name: 'title',
},
],
},
{
slug: 'categories',
versions: { drafts: true },
fields: [
{
type: 'text',
name: 'title',
},
{
type: 'tabs',
tabs: [
{
name: 'hideout',
fields: [
{
label: 'Cameras',
type: 'tabs',
unique: true,
tabs: [
{
name: 'camera1',
fields: [
{
type: 'row',
fields: [
{
name: 'time1Image',
type: 'relationship',
relationTo: 'posts',
unique: true,
},
],
},
],
},
],
},
],
},
],
},
],
},
{
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',
hasMany: true,
name: 'categories',
},
{
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: 'textLocalized',
type: 'text',
localized: true,
},
],
},
{
name: 'arrayWithIDsLocalized',
type: 'array',
localized: true,
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'),
},
})