Compare commits
20 Commits
db-postgre
...
db-postgre
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d1cad3adb | ||
|
|
e31f72da8e | ||
|
|
7aa058d604 | ||
|
|
64e80d242e | ||
|
|
e8f2ca484e | ||
|
|
ceca5c4e97 | ||
|
|
ee13736288 | ||
|
|
815bdfac0b | ||
|
|
7a7f0ed7e8 | ||
|
|
ad42d541b3 | ||
|
|
32ed95e1ee | ||
|
|
70e57fef18 | ||
|
|
0a07f607b9 | ||
|
|
3918fc7c21 | ||
|
|
13f71ac475 | ||
|
|
07720e777a | ||
|
|
efff47e400 | ||
|
|
453ac218ea | ||
|
|
d4b09bd9cd | ||
|
|
dd67e03fc1 |
13
.github/workflows/main.yml
vendored
13
.github/workflows/main.yml
vendored
@@ -85,7 +85,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
database: [ mongoose, postgres, postgres-uuid, supabase ]
|
||||
database: [mongoose, postgres, postgres-custom-schema, postgres-uuid, supabase]
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
@@ -123,7 +123,7 @@ jobs:
|
||||
postgresql db: ${{ env.POSTGRES_DB }}
|
||||
postgresql user: ${{ env.POSTGRES_USER }}
|
||||
postgresql password: ${{ env.POSTGRES_PASSWORD }}
|
||||
if: matrix.database == 'postgres' || matrix.database == 'postgres-uuid'
|
||||
if: startsWith(matrix.database, 'postgres')
|
||||
|
||||
- name: Install Supabase CLI
|
||||
uses: supabase/setup-cli@v1
|
||||
@@ -139,14 +139,19 @@ jobs:
|
||||
|
||||
- name: Wait for PostgreSQL
|
||||
run: sleep 30
|
||||
if: matrix.database == 'postgres' || matrix.database == 'postgres-uuid'
|
||||
if: startsWith(matrix.database, 'postgres')
|
||||
|
||||
- name: Configure PostgreSQL
|
||||
run: |
|
||||
psql "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" -c "CREATE ROLE runner SUPERUSER LOGIN;"
|
||||
psql "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" -c "SELECT version();"
|
||||
echo "POSTGRES_URL=postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" >> $GITHUB_ENV
|
||||
if: matrix.database == 'postgres' || matrix.database == 'postgres-uuid'
|
||||
if: startsWith(matrix.database, 'postgres')
|
||||
|
||||
- name: Configure PostgreSQL with custom schema
|
||||
run: |
|
||||
psql "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" -c "CREATE SCHEMA custom;"
|
||||
if: matrix.database == 'postgres-custom-schema'
|
||||
|
||||
- name: Configure Supabase
|
||||
run: |
|
||||
|
||||
30
CHANGELOG.md
30
CHANGELOG.md
@@ -1,10 +1,24 @@
|
||||
## [2.11.2](https://github.com/payloadcms/payload/compare/v2.11.1...v2.11.2) (2024-02-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **db-postgres:** configurable custom schema to use ([#5047](https://github.com/payloadcms/payload/issues/5047)) ([e8f2ca4](https://github.com/payloadcms/payload/commit/e8f2ca484ee56cd7767d5111e46ebd24752ff8de))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Add Context Provider in EditMany Component ([#5005](https://github.com/payloadcms/payload/issues/5005)) ([70e57fe](https://github.com/payloadcms/payload/commit/70e57fef184f7fcf56344ea755465f246f2253a5))
|
||||
* **db-mongodb:** unique sparse for not required fields ([#5114](https://github.com/payloadcms/payload/issues/5114)) ([815bdfa](https://github.com/payloadcms/payload/commit/815bdfac0b0afbff2a20e54d5aee64b90f6b3a77))
|
||||
* **db-postgres:** set _parentID for array nested localized fields ([#5117](https://github.com/payloadcms/payload/issues/5117)) ([ceca5c4](https://github.com/payloadcms/payload/commit/ceca5c4e97f53f1346797a31b6abfc0375e98215))
|
||||
* disabling API Key does not remove the key ([#5145](https://github.com/payloadcms/payload/issues/5145)) ([7a7f0ed](https://github.com/payloadcms/payload/commit/7a7f0ed7e8132253be607c111c160163b84bd770))
|
||||
* handle thrown errors in config-level afterError hook ([#5147](https://github.com/payloadcms/payload/issues/5147)) ([32ed95e](https://github.com/payloadcms/payload/commit/32ed95e1ee87409db234f1b7bd6d2e462fd9ed5d))
|
||||
* only replace the drawer content with full edit component if it exists ([#5144](https://github.com/payloadcms/payload/issues/5144)) ([0a07f60](https://github.com/payloadcms/payload/commit/0a07f607b9fb1217ad956cd05b2a84a4042a19ca))
|
||||
* transaction error from access endpoint ([#5156](https://github.com/payloadcms/payload/issues/5156)) ([ad42d54](https://github.com/payloadcms/payload/commit/ad42d541b342ed56463b81cee6d6307df6f06d7f))
|
||||
|
||||
## [2.11.1](https://github.com/payloadcms/payload/compare/v2.11.0...v2.11.1) (2024-02-16)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **richtext-lexical:** Update lexical from 0.12.6 to 0.13.1, port over all useful changes from playground (#5066)
|
||||
|
||||
### Features
|
||||
|
||||
* **db-postgres:** adds idType to use uuid or serial id columns ([#3864](https://github.com/payloadcms/payload/issues/3864)) ([d6c2578](https://github.com/payloadcms/payload/commit/d6c25783cfa97983bf9db27ceb5ccd39a62c62f1))
|
||||
@@ -25,6 +39,14 @@
|
||||
* **richtext-lexical:** do not remove adjacent paragraph node when inserting certain nodes in empty editor ([#5061](https://github.com/payloadcms/payload/issues/5061)) ([6323965](https://github.com/payloadcms/payload/commit/6323965c652ea68dffeb716957b124d165b9ce96))
|
||||
* **uploads:** account for serverURL when retrieving external file ([#5102](https://github.com/payloadcms/payload/issues/5102)) ([25cee8b](https://github.com/payloadcms/payload/commit/25cee8bb102bf80b3a4bfb4b4e46712722cc7f0d))
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES: @payloadcms/richtext-lexical
|
||||
|
||||
* **richtext-lexical:** Update lexical from 0.12.6 to 0.13.1, port over all useful changes from playground (#5066)
|
||||
|
||||
- You HAVE to make sure that any versions of the lexical packages (IF you have any installed) match the lexical version which richtext-lexical uses: v0.13.1. If you do not do this, you may be plagued by React useContext / "cannot find active editor state" errors
|
||||
- Updates to lexical's API, e.g. the removal of INTERNAL_isPointSelection, could be breaking depending on your code. Please consult the [lexical changelog](https://github.com/facebook/lexical/blob/main/CHANGELOG.md).
|
||||
|
||||
## [2.11.0](https://github.com/payloadcms/payload/compare/v2.10.1...v2.11.0) (2024-02-09)
|
||||
|
||||
|
||||
|
||||
@@ -37,11 +37,12 @@ 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. |
|
||||
| `schemaName` | A string for the postgres schema to use, defaults to 'public'. |
|
||||
|
||||
### Access to Drizzle
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ If your Hook simply performs a side-effect, such as updating a CRM, it might be
|
||||
|
||||
#### Server-only execution
|
||||
|
||||
Payload Hooks do not have any effect within the Payload Admin panel. You can safely [remove your hooks](/docs/admin/webpack#aliasing-server-only-modules) from your Admin panel's code by customizing the Webpack config, which not only keeps your Admin bundles' filesize small but also ensures that any server-side only code does not cause problems within browser environments.
|
||||
Payload Hooks are only triggered on the server. You can safely [remove your hooks](/docs/admin/webpack#aliasing-server-only-modules) from your Admin panel's client-side code by customizing the Webpack config, which not only keeps your Admin bundles' filesize small but also ensures that any server-side only code does not cause problems within browser environments.
|
||||
|
||||
## Hook Types
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-mongodb",
|
||||
"version": "1.4.2",
|
||||
"version": "1.4.3",
|
||||
"description": "The officially supported MongoDB database adapter for Payload",
|
||||
"repository": "https://github.com/payloadcms/payload",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -14,8 +14,10 @@ import type {
|
||||
DateField,
|
||||
EmailField,
|
||||
Field,
|
||||
FieldAffectingData,
|
||||
GroupField,
|
||||
JSONField,
|
||||
NonPresentationalField,
|
||||
NumberField,
|
||||
PointField,
|
||||
RadioField,
|
||||
@@ -23,12 +25,12 @@ import type {
|
||||
RichTextField,
|
||||
RowField,
|
||||
SelectField,
|
||||
Tab,
|
||||
TabsField,
|
||||
TextField,
|
||||
TextareaField,
|
||||
UploadField,
|
||||
} from 'payload/types'
|
||||
import type { FieldAffectingData, NonPresentationalField, Tab, UnnamedTab } from 'payload/types'
|
||||
|
||||
import { Schema } from 'mongoose'
|
||||
import {
|
||||
@@ -61,7 +63,15 @@ const formatBaseSchema = (field: FieldAffectingData, buildSchemaOptions: BuildSc
|
||||
unique: (!disableUnique && field.unique) || false,
|
||||
}
|
||||
|
||||
if (schema.unique && (field.localized || draftsEnabled)) {
|
||||
if (
|
||||
schema.unique &&
|
||||
(field.localized ||
|
||||
draftsEnabled ||
|
||||
(fieldAffectsData(field) &&
|
||||
field.type !== 'group' &&
|
||||
field.type !== 'tab' &&
|
||||
field.required !== true))
|
||||
) {
|
||||
schema.sparse = true
|
||||
}
|
||||
|
||||
@@ -79,7 +89,6 @@ const localizeSchema = (
|
||||
) => {
|
||||
if (fieldIsLocalized(entity) && localization && Array.isArray(localization.locales)) {
|
||||
return {
|
||||
localized: true,
|
||||
type: localization.localeCodes.reduce(
|
||||
(localeSchema, locale) => ({
|
||||
...localeSchema,
|
||||
@@ -89,6 +98,7 @@ const localizeSchema = (
|
||||
_id: false,
|
||||
},
|
||||
),
|
||||
localized: true,
|
||||
}
|
||||
}
|
||||
return schema
|
||||
@@ -140,7 +150,6 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
) => {
|
||||
const baseSchema = {
|
||||
...formatBaseSchema(field, buildSchemaOptions),
|
||||
default: undefined,
|
||||
type: [
|
||||
buildSchema(config, field.fields, {
|
||||
allowIDField: true,
|
||||
@@ -153,6 +162,7 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
},
|
||||
}),
|
||||
],
|
||||
default: undefined,
|
||||
}
|
||||
|
||||
schema.add({
|
||||
@@ -166,8 +176,8 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const fieldSchema = {
|
||||
default: undefined,
|
||||
type: [new Schema({}, { _id: false, discriminatorKey: 'blockType' })],
|
||||
default: undefined,
|
||||
}
|
||||
|
||||
schema.add({
|
||||
@@ -187,12 +197,12 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
if (field.localized && config.localization) {
|
||||
config.localization.localeCodes.forEach((localeCode) => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore Possible incorrect typing in mongoose types, this works
|
||||
// @ts-expect-error Possible incorrect typing in mongoose types, this works
|
||||
schema.path(`${field.name}.${localeCode}`).discriminator(blockItem.slug, blockSchema)
|
||||
})
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore Possible incorrect typing in mongoose types, this works
|
||||
// @ts-expect-error Possible incorrect typing in mongoose types, this works
|
||||
schema.path(field.name).discriminator(blockItem.slug, blockSchema)
|
||||
}
|
||||
})
|
||||
@@ -325,14 +335,14 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
): void => {
|
||||
const baseSchema: SchemaTypeOptions<unknown> = {
|
||||
type: {
|
||||
type: String,
|
||||
enum: ['Point'],
|
||||
},
|
||||
coordinates: {
|
||||
type: [Number],
|
||||
default: field.defaultValue || undefined,
|
||||
required: false,
|
||||
type: [Number],
|
||||
},
|
||||
type: {
|
||||
enum: ['Point'],
|
||||
type: String,
|
||||
},
|
||||
}
|
||||
if (buildSchemaOptions.disableUnique && field.unique && field.localized) {
|
||||
@@ -366,11 +376,11 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
): void => {
|
||||
const baseSchema = {
|
||||
...formatBaseSchema(field, buildSchemaOptions),
|
||||
type: String,
|
||||
enum: field.options.map((option) => {
|
||||
if (typeof option === 'object') return option.value
|
||||
return option
|
||||
}),
|
||||
type: String,
|
||||
}
|
||||
|
||||
schema.add({
|
||||
@@ -388,7 +398,6 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
|
||||
if (field.localized && config.localization) {
|
||||
schemaToReturn = {
|
||||
localized: true,
|
||||
type: config.localization.localeCodes.reduce((locales, locale) => {
|
||||
let localeSchema: { [key: string]: any } = {}
|
||||
|
||||
@@ -396,56 +405,57 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
localeSchema = {
|
||||
...formatBaseSchema(field, buildSchemaOptions),
|
||||
_id: false,
|
||||
relationTo: { enum: field.relationTo, type: String },
|
||||
type: Schema.Types.Mixed,
|
||||
relationTo: { type: String, enum: field.relationTo },
|
||||
value: {
|
||||
refPath: `${field.name}.${locale}.relationTo`,
|
||||
type: Schema.Types.Mixed,
|
||||
refPath: `${field.name}.${locale}.relationTo`,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
localeSchema = {
|
||||
...formatBaseSchema(field, buildSchemaOptions),
|
||||
ref: field.relationTo,
|
||||
type: Schema.Types.Mixed,
|
||||
ref: field.relationTo,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...locales,
|
||||
[locale]: field.hasMany ? { default: undefined, type: [localeSchema] } : localeSchema,
|
||||
[locale]: field.hasMany ? { type: [localeSchema], default: undefined } : localeSchema,
|
||||
}
|
||||
}, {}),
|
||||
localized: true,
|
||||
}
|
||||
} else if (hasManyRelations) {
|
||||
schemaToReturn = {
|
||||
...formatBaseSchema(field, buildSchemaOptions),
|
||||
_id: false,
|
||||
relationTo: { enum: field.relationTo, type: String },
|
||||
type: Schema.Types.Mixed,
|
||||
relationTo: { type: String, enum: field.relationTo },
|
||||
value: {
|
||||
refPath: `${field.name}.relationTo`,
|
||||
type: Schema.Types.Mixed,
|
||||
refPath: `${field.name}.relationTo`,
|
||||
},
|
||||
}
|
||||
|
||||
if (field.hasMany) {
|
||||
schemaToReturn = {
|
||||
default: undefined,
|
||||
type: [schemaToReturn],
|
||||
default: undefined,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
schemaToReturn = {
|
||||
...formatBaseSchema(field, buildSchemaOptions),
|
||||
ref: field.relationTo,
|
||||
type: Schema.Types.Mixed,
|
||||
ref: field.relationTo,
|
||||
}
|
||||
|
||||
if (field.hasMany) {
|
||||
schemaToReturn = {
|
||||
default: undefined,
|
||||
type: [schemaToReturn],
|
||||
default: undefined,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -488,11 +498,11 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
): void => {
|
||||
const baseSchema = {
|
||||
...formatBaseSchema(field, buildSchemaOptions),
|
||||
type: String,
|
||||
enum: field.options.map((option) => {
|
||||
if (typeof option === 'object') return option.value
|
||||
return option
|
||||
}),
|
||||
type: String,
|
||||
}
|
||||
|
||||
if (buildSchemaOptions.draftsEnabled || !field.required) {
|
||||
@@ -576,8 +586,8 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
): void => {
|
||||
const baseSchema = {
|
||||
...formatBaseSchema(field, buildSchemaOptions),
|
||||
ref: field.relationTo,
|
||||
type: Schema.Types.Mixed,
|
||||
ref: field.relationTo,
|
||||
}
|
||||
|
||||
schema.add({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-postgres",
|
||||
"version": "0.6.0",
|
||||
"version": "0.7.0",
|
||||
"description": "The officially supported Postgres database adapter for Payload",
|
||||
"repository": "https://github.com/payloadcms/payload",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { Connect } from 'payload/database'
|
||||
|
||||
import { eq, sql } from 'drizzle-orm'
|
||||
import { drizzle } from 'drizzle-orm/node-postgres'
|
||||
import { numeric, pgTable, timestamp, varchar } from 'drizzle-orm/pg-core'
|
||||
import { numeric, timestamp, varchar } from 'drizzle-orm/pg-core'
|
||||
import { Pool } from 'pg'
|
||||
import prompts from 'prompts'
|
||||
|
||||
@@ -61,9 +61,13 @@ export const connect: Connect = async function connect(this: PostgresAdapter, pa
|
||||
|
||||
this.drizzle = drizzle(this.pool, { logger, schema: this.schema })
|
||||
if (process.env.PAYLOAD_DROP_DATABASE === 'true') {
|
||||
this.payload.logger.info('---- DROPPING TABLES ----')
|
||||
await this.drizzle.execute(sql`drop schema public cascade;
|
||||
create schema public;`)
|
||||
this.payload.logger.info(`---- DROPPING TABLES SCHEMA(${this.schemaName || 'public'}) ----`)
|
||||
await this.drizzle.execute(
|
||||
sql.raw(`
|
||||
drop schema if exists ${this.schemaName || 'public'} cascade;
|
||||
create schema ${this.schemaName || 'public'};
|
||||
`),
|
||||
)
|
||||
this.payload.logger.info('---- DROPPED TABLES ----')
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -120,7 +124,7 @@ export const connect: Connect = async function connect(this: PostgresAdapter, pa
|
||||
await apply()
|
||||
|
||||
// Migration table def in order to use query using drizzle
|
||||
const migrationsSchema = pgTable('payload_migrations', {
|
||||
const migrationsSchema = this.pgSchema.table('payload_migrations', {
|
||||
name: varchar('name'),
|
||||
batch: numeric('batch'),
|
||||
created_at: timestamp('created_at'),
|
||||
|
||||
@@ -78,7 +78,7 @@ export const traverseFields = ({
|
||||
with: {},
|
||||
}
|
||||
|
||||
const arrayTableName = `${currentTableName}_${toSnakeCase(field.name)}`
|
||||
const arrayTableName = `${currentTableName}_${path}${toSnakeCase(field.name)}`
|
||||
|
||||
if (adapter.tables[`${arrayTableName}_locales`]) withArray.with._locales = _locales
|
||||
currentArgs.with[`${path}${field.name}`] = withArray
|
||||
|
||||
@@ -52,11 +52,13 @@ export function postgresAdapter(args: Args): PostgresAdapterResult {
|
||||
fieldConstraints: {},
|
||||
idType,
|
||||
logger: args.logger,
|
||||
pgSchema: undefined,
|
||||
pool: undefined,
|
||||
poolOptions: args.pool,
|
||||
push: args.push,
|
||||
relations: {},
|
||||
schema: {},
|
||||
schemaName: args.schemaName,
|
||||
sessions: {},
|
||||
tables: {},
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import type { Init } from 'payload/database'
|
||||
import type { SanitizedCollectionConfig } from 'payload/types'
|
||||
|
||||
import { pgEnum } from 'drizzle-orm/pg-core'
|
||||
import { pgEnum, pgSchema, pgTable } from 'drizzle-orm/pg-core'
|
||||
import { buildVersionCollectionFields, buildVersionGlobalFields } from 'payload/versions'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
@@ -11,6 +11,12 @@ import type { PostgresAdapter } from './types'
|
||||
import { buildTable } from './schema/build'
|
||||
|
||||
export const init: Init = async function init(this: PostgresAdapter) {
|
||||
if (this.schemaName) {
|
||||
this.pgSchema = pgSchema(this.schemaName)
|
||||
} else {
|
||||
this.pgSchema = { table: pgTable }
|
||||
}
|
||||
|
||||
if (this.payload.config.localization) {
|
||||
this.enums.enum__locales = pgEnum(
|
||||
'_locales',
|
||||
|
||||
@@ -39,7 +39,7 @@ export async function migrate(this: PostgresAdapter): Promise<void> {
|
||||
latestBatch = Number(migrationsInDB[0]?.batch)
|
||||
}
|
||||
} else {
|
||||
await createMigrationTable(this.drizzle)
|
||||
await createMigrationTable(this)
|
||||
}
|
||||
|
||||
if (migrationsInDB.find((m) => m.batch === -1)) {
|
||||
|
||||
@@ -44,8 +44,10 @@ export async function migrateFresh(
|
||||
msg: `Dropping database.`,
|
||||
})
|
||||
|
||||
await this.drizzle.execute(sql`drop schema public cascade;
|
||||
create schema public;`)
|
||||
await this.drizzle.execute(
|
||||
sql.raw(`drop schema ${this.schemaName || 'public'} cascade;
|
||||
create schema ${this.schemaName || 'public'};`),
|
||||
)
|
||||
|
||||
const migrationFiles = await readMigrationFiles({ payload })
|
||||
payload.logger.debug({
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { SQL } from 'drizzle-orm'
|
||||
import type { PgTableWithColumns } from 'drizzle-orm/pg-core'
|
||||
import type { Field, Where } from 'payload/types'
|
||||
|
||||
import { asc, desc } from 'drizzle-orm'
|
||||
@@ -12,7 +13,7 @@ export type BuildQueryJoins = Record<string, SQL>
|
||||
|
||||
export type BuildQueryJoinAliases = {
|
||||
condition: SQL
|
||||
table: GenericTable
|
||||
table: GenericTable | PgTableWithColumns<any>
|
||||
}[]
|
||||
|
||||
type BuildQueryArgs = {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import type { SQL } from 'drizzle-orm'
|
||||
import type { Field, FieldAffectingData, NumberField, TabAsField, TextField } from 'payload/types'
|
||||
import type { PgTableWithColumns } from 'drizzle-orm/pg-core'
|
||||
import type { Field, FieldAffectingData, NumberField, TabAsField, TextField } from 'payload/types'
|
||||
|
||||
import { and, eq, like, sql } from 'drizzle-orm'
|
||||
import { alias } from 'drizzle-orm/pg-core'
|
||||
@@ -15,7 +16,7 @@ import type { BuildQueryJoinAliases, BuildQueryJoins } from './buildQuery'
|
||||
|
||||
type Constraint = {
|
||||
columnName: string
|
||||
table: GenericTable
|
||||
table: GenericTable | PgTableWithColumns<any>
|
||||
value: unknown
|
||||
}
|
||||
|
||||
@@ -26,12 +27,12 @@ type TableColumn = {
|
||||
getNotNullColumnByValue?: (val: unknown) => string
|
||||
pathSegments?: string[]
|
||||
rawColumn?: SQL
|
||||
table: GenericTable
|
||||
table: GenericTable | PgTableWithColumns<any>
|
||||
}
|
||||
|
||||
type Args = {
|
||||
adapter: PostgresAdapter
|
||||
aliasTable?: GenericTable
|
||||
aliasTable?: GenericTable | PgTableWithColumns<any>
|
||||
collectionPath: string
|
||||
columnPrefix?: string
|
||||
constraintPath?: string
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import type { Relation } from 'drizzle-orm'
|
||||
import type { IndexBuilder, PgColumnBuilder, UniqueConstraintBuilder } from 'drizzle-orm/pg-core'
|
||||
import type {
|
||||
IndexBuilder,
|
||||
PgColumnBuilder,
|
||||
PgTableWithColumns,
|
||||
UniqueConstraintBuilder,
|
||||
} from 'drizzle-orm/pg-core'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
import { relations } from 'drizzle-orm'
|
||||
import {
|
||||
index,
|
||||
integer,
|
||||
numeric,
|
||||
pgTable,
|
||||
serial,
|
||||
timestamp,
|
||||
unique,
|
||||
varchar,
|
||||
} from 'drizzle-orm/pg-core'
|
||||
import { index, integer, numeric, serial, timestamp, unique, varchar } from 'drizzle-orm/pg-core'
|
||||
import { fieldAffectsData } from 'payload/types'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
@@ -77,14 +73,14 @@ export const buildTable = ({
|
||||
|
||||
const localesColumns: Record<string, PgColumnBuilder> = {}
|
||||
const localesIndexes: Record<string, (cols: GenericColumns) => IndexBuilder> = {}
|
||||
let localesTable: GenericTable
|
||||
let textsTable: GenericTable
|
||||
let numbersTable: GenericTable
|
||||
let localesTable: GenericTable | PgTableWithColumns<any>
|
||||
let textsTable: GenericTable | PgTableWithColumns<any>
|
||||
let numbersTable: GenericTable | PgTableWithColumns<any>
|
||||
|
||||
// Relationships to the base collection
|
||||
const relationships: Set<string> = rootRelationships || new Set()
|
||||
|
||||
let relationshipsTable: GenericTable
|
||||
let relationshipsTable: GenericTable | PgTableWithColumns<any>
|
||||
|
||||
// Drizzle relations
|
||||
const relationsToBuild: Map<string, string> = new Map()
|
||||
@@ -136,7 +132,7 @@ export const buildTable = ({
|
||||
.notNull()
|
||||
}
|
||||
|
||||
const table = pgTable(tableName, columns, (cols) => {
|
||||
const table = adapter.pgSchema.table(tableName, columns, (cols) => {
|
||||
const extraConfig = Object.entries(baseExtraConfig).reduce((config, [key, func]) => {
|
||||
config[key] = func(cols)
|
||||
return config
|
||||
@@ -158,7 +154,7 @@ export const buildTable = ({
|
||||
.references(() => table.id, { onDelete: 'cascade' })
|
||||
.notNull()
|
||||
|
||||
localesTable = pgTable(localeTableName, localesColumns, (cols) => {
|
||||
localesTable = adapter.pgSchema.table(localeTableName, localesColumns, (cols) => {
|
||||
return Object.entries(localesIndexes).reduce(
|
||||
(acc, [colName, func]) => {
|
||||
acc[colName] = func(cols)
|
||||
@@ -201,7 +197,7 @@ export const buildTable = ({
|
||||
columns.locale = adapter.enums.enum__locales('locale')
|
||||
}
|
||||
|
||||
textsTable = pgTable(textsTableName, columns, (cols) => {
|
||||
textsTable = adapter.pgSchema.table(textsTableName, columns, (cols) => {
|
||||
const indexes: Record<string, IndexBuilder> = {
|
||||
orderParentIdx: index(`${textsTableName}_order_parent_idx`).on(cols.order, cols.parent),
|
||||
}
|
||||
@@ -245,7 +241,7 @@ export const buildTable = ({
|
||||
columns.locale = adapter.enums.enum__locales('locale')
|
||||
}
|
||||
|
||||
numbersTable = pgTable(numbersTableName, columns, (cols) => {
|
||||
numbersTable = adapter.pgSchema.table(numbersTableName, columns, (cols) => {
|
||||
const indexes: Record<string, IndexBuilder> = {
|
||||
orderParentIdx: index(`${numbersTableName}_order_parent_idx`).on(cols.order, cols.parent),
|
||||
}
|
||||
@@ -307,19 +303,23 @@ export const buildTable = ({
|
||||
|
||||
const relationshipsTableName = `${tableName}_rels`
|
||||
|
||||
relationshipsTable = pgTable(relationshipsTableName, relationshipColumns, (cols) => {
|
||||
const result: Record<string, unknown> = {
|
||||
order: index(`${relationshipsTableName}_order_idx`).on(cols.order),
|
||||
parentIdx: index(`${relationshipsTableName}_parent_idx`).on(cols.parent),
|
||||
pathIdx: index(`${relationshipsTableName}_path_idx`).on(cols.path),
|
||||
}
|
||||
relationshipsTable = adapter.pgSchema.table(
|
||||
relationshipsTableName,
|
||||
relationshipColumns,
|
||||
(cols) => {
|
||||
const result: Record<string, unknown> = {
|
||||
order: index(`${relationshipsTableName}_order_idx`).on(cols.order),
|
||||
parentIdx: index(`${relationshipsTableName}_parent_idx`).on(cols.parent),
|
||||
pathIdx: index(`${relationshipsTableName}_path_idx`).on(cols.path),
|
||||
}
|
||||
|
||||
if (hasLocalizedRelationshipField) {
|
||||
result.localeIdx = index(`${relationshipsTableName}_locale_idx`).on(cols.locale)
|
||||
}
|
||||
if (hasLocalizedRelationshipField) {
|
||||
result.localeIdx = index(`${relationshipsTableName}_locale_idx`).on(cols.locale)
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
return result
|
||||
},
|
||||
)
|
||||
|
||||
adapter.tables[relationshipsTableName] = relationshipsTable
|
||||
|
||||
|
||||
@@ -7,7 +7,14 @@ import type {
|
||||
Relations,
|
||||
} from 'drizzle-orm'
|
||||
import type { NodePgDatabase, NodePgQueryResultHKT } from 'drizzle-orm/node-postgres'
|
||||
import type { PgColumn, PgEnum, PgTableWithColumns, PgTransaction } from 'drizzle-orm/pg-core'
|
||||
import type {
|
||||
PgColumn,
|
||||
PgEnum,
|
||||
PgSchema,
|
||||
PgTableWithColumns,
|
||||
PgTransaction,
|
||||
} from 'drizzle-orm/pg-core'
|
||||
import type { PgTableFn } from 'drizzle-orm/pg-core/table'
|
||||
import type { Payload } from 'payload'
|
||||
import type { BaseDatabaseAdapter } from 'payload/database'
|
||||
import type { PayloadRequest } from 'payload/types'
|
||||
@@ -21,6 +28,7 @@ export type Args = {
|
||||
migrationDir?: string
|
||||
pool: PoolConfig
|
||||
push?: boolean
|
||||
schemaName?: string
|
||||
}
|
||||
|
||||
export type GenericColumn = PgColumn<
|
||||
@@ -59,11 +67,13 @@ export type PostgresAdapter = BaseDatabaseAdapter & {
|
||||
fieldConstraints: Record<string, Record<string, string>>
|
||||
idType: Args['idType']
|
||||
logger: DrizzleConfig['logger']
|
||||
pgSchema?: { table: PgTableFn } | PgSchema
|
||||
pool: Pool
|
||||
poolOptions: Args['pool']
|
||||
push: boolean
|
||||
relations: Record<string, GenericRelation>
|
||||
schema: Record<string, GenericEnum | GenericRelation | GenericTable>
|
||||
schemaName?: Args['schemaName']
|
||||
sessions: {
|
||||
[id: string]: {
|
||||
db: DrizzleTransaction
|
||||
@@ -71,7 +81,7 @@ export type PostgresAdapter = BaseDatabaseAdapter & {
|
||||
resolve: () => Promise<void>
|
||||
}
|
||||
}
|
||||
tables: Record<string, GenericTable>
|
||||
tables: Record<string, GenericTable | PgTableWithColumns<any>>
|
||||
}
|
||||
|
||||
export type IDType = 'integer' | 'numeric' | 'uuid' | 'varchar'
|
||||
|
||||
@@ -40,7 +40,7 @@ export const insertArrays = async ({ adapter, arrays, db, parentRows }: Args): P
|
||||
|
||||
// Add any sub arrays that need to be created
|
||||
// We will call this recursively below
|
||||
arrayRows.forEach((arrayRow) => {
|
||||
arrayRows.forEach((arrayRow, i) => {
|
||||
if (Object.keys(arrayRow.arrays).length > 0) {
|
||||
rowsByTable[tableName].arrays.push(arrayRow.arrays)
|
||||
}
|
||||
@@ -53,6 +53,9 @@ export const insertArrays = async ({ adapter, arrays, db, parentRows }: Args): P
|
||||
arrayRowLocaleData._parentID = arrayRow.row.id
|
||||
arrayRowLocaleData._locale = arrayRowLocale
|
||||
rowsByTable[tableName].locales.push(arrayRowLocaleData)
|
||||
if (!arrayRow.row.id) {
|
||||
arrayRowLocaleData._getParentID = (rows) => rows[i].id
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -69,6 +72,15 @@ export const insertArrays = async ({ adapter, arrays, db, parentRows }: Args): P
|
||||
|
||||
// Insert locale rows
|
||||
if (adapter.tables[`${tableName}_locales`] && row.locales.length > 0) {
|
||||
if (!row.locales[0]._parentID) {
|
||||
row.locales = row.locales.map((localeRow, i) => {
|
||||
if (typeof localeRow._getParentID === 'function') {
|
||||
localeRow._parentID = localeRow._getParentID(insertedRows)
|
||||
delete localeRow._getParentID
|
||||
}
|
||||
return localeRow
|
||||
})
|
||||
}
|
||||
await db.insert(adapter.tables[`${tableName}_locales`]).values(row.locales).returning()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import { sql } from 'drizzle-orm'
|
||||
|
||||
import type { DrizzleDB } from '../types'
|
||||
import type { PostgresAdapter } from '../types'
|
||||
|
||||
export const createMigrationTable = async (db: DrizzleDB): Promise<void> => {
|
||||
await db.execute(sql`CREATE TABLE IF NOT EXISTS "payload_migrations" (
|
||||
export const createMigrationTable = async (adapter: PostgresAdapter): Promise<void> => {
|
||||
const prependSchema = adapter.schemaName ? `"${adapter.schemaName}".` : ''
|
||||
|
||||
await adapter.drizzle.execute(
|
||||
sql.raw(`CREATE TABLE IF NOT EXISTS ${prependSchema}"payload_migrations" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"name" varchar,
|
||||
"batch" numeric,
|
||||
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
|
||||
);`)
|
||||
);`),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "payload",
|
||||
"version": "2.11.1",
|
||||
"version": "2.11.2",
|
||||
"description": "Node, React and MongoDB Headless CMS and Application Framework",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@@ -52,21 +52,9 @@ const Content: React.FC<DocumentDrawerProps> = ({
|
||||
|
||||
const { id, docPermissions, getDocPreferences } = useDocumentInfo()
|
||||
|
||||
// The component definition could come from multiple places in the config
|
||||
// we need to cascade into the proper component from the top-down
|
||||
// 1. "components.Edit"
|
||||
// 2. "components.Edit.Default"
|
||||
// 3. "components.Edit.Default.Component"
|
||||
const CustomEditView =
|
||||
typeof Edit === 'function'
|
||||
? Edit
|
||||
: typeof Edit === 'object' && typeof Edit.Default === 'function'
|
||||
? Edit.Default
|
||||
: typeof Edit?.Default === 'object' &&
|
||||
'Component' in Edit.Default &&
|
||||
typeof Edit.Default.Component === 'function'
|
||||
? Edit.Default.Component
|
||||
: undefined
|
||||
// If they are replacing the entire edit view, use that.
|
||||
// Else let the DefaultEdit determine what to render.
|
||||
const CustomEditView = typeof Edit === 'function' ? Edit : undefined
|
||||
|
||||
const [fields, setFields] = useState(() => formatFields(collectionConfig, true))
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import { fieldTypes } from '../../forms/field-types'
|
||||
import X from '../../icons/X'
|
||||
import { useAuth } from '../../utilities/Auth'
|
||||
import { useConfig } from '../../utilities/Config'
|
||||
import { DocumentInfoProvider } from '../../utilities/DocumentInfo'
|
||||
import { OperationContext } from '../../utilities/OperationProvider'
|
||||
import { SelectAllStatus, useSelection } from '../../views/collections/List/SelectionProvider'
|
||||
import { Drawer, DrawerToggler } from '../Drawer'
|
||||
@@ -120,53 +121,55 @@ const EditMany: React.FC<Props> = (props) => {
|
||||
{t('edit')}
|
||||
</DrawerToggler>
|
||||
<Drawer header={null} slug={drawerSlug}>
|
||||
<OperationContext.Provider value="update">
|
||||
<Form className={`${baseClass}__form`} onSuccess={onSuccess}>
|
||||
<div className={`${baseClass}__main`}>
|
||||
<div className={`${baseClass}__header`}>
|
||||
<h2 className={`${baseClass}__header__title`}>
|
||||
{t('editingLabel', { count, label: getTranslation(plural, i18n) })}
|
||||
</h2>
|
||||
<button
|
||||
aria-label={t('close')}
|
||||
className={`${baseClass}__header__close`}
|
||||
id={`close-drawer__${drawerSlug}`}
|
||||
onClick={() => closeModal(drawerSlug)}
|
||||
type="button"
|
||||
>
|
||||
<X />
|
||||
</button>
|
||||
</div>
|
||||
<FieldSelect fields={fields} setSelected={setSelected} />
|
||||
<RenderFields fieldSchema={selected} fieldTypes={fieldTypes} />
|
||||
<div className={`${baseClass}__sidebar-wrap`}>
|
||||
<div className={`${baseClass}__sidebar`}>
|
||||
<div className={`${baseClass}__sidebar-sticky-wrap`}>
|
||||
<div className={`${baseClass}__document-actions`}>
|
||||
{collection.versions ? (
|
||||
<React.Fragment>
|
||||
<Publish
|
||||
<DocumentInfoProvider collection={collection}>
|
||||
<OperationContext.Provider value="update">
|
||||
<Form className={`${baseClass}__form`} onSuccess={onSuccess}>
|
||||
<div className={`${baseClass}__main`}>
|
||||
<div className={`${baseClass}__header`}>
|
||||
<h2 className={`${baseClass}__header__title`}>
|
||||
{t('editingLabel', { count, label: getTranslation(plural, i18n) })}
|
||||
</h2>
|
||||
<button
|
||||
aria-label={t('close')}
|
||||
className={`${baseClass}__header__close`}
|
||||
id={`close-drawer__${drawerSlug}`}
|
||||
onClick={() => closeModal(drawerSlug)}
|
||||
type="button"
|
||||
>
|
||||
<X />
|
||||
</button>
|
||||
</div>
|
||||
<FieldSelect fields={fields} setSelected={setSelected} />
|
||||
<RenderFields fieldSchema={selected} fieldTypes={fieldTypes} />
|
||||
<div className={`${baseClass}__sidebar-wrap`}>
|
||||
<div className={`${baseClass}__sidebar`}>
|
||||
<div className={`${baseClass}__sidebar-sticky-wrap`}>
|
||||
<div className={`${baseClass}__document-actions`}>
|
||||
{collection.versions ? (
|
||||
<React.Fragment>
|
||||
<Publish
|
||||
action={`${serverURL}${api}/${slug}${getQueryParams()}`}
|
||||
disabled={selected.length === 0}
|
||||
/>
|
||||
<SaveDraft
|
||||
action={`${serverURL}${api}/${slug}${getQueryParams()}`}
|
||||
disabled={selected.length === 0}
|
||||
/>
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<Submit
|
||||
action={`${serverURL}${api}/${slug}${getQueryParams()}`}
|
||||
disabled={selected.length === 0}
|
||||
/>
|
||||
<SaveDraft
|
||||
action={`${serverURL}${api}/${slug}${getQueryParams()}`}
|
||||
disabled={selected.length === 0}
|
||||
/>
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<Submit
|
||||
action={`${serverURL}${api}/${slug}${getQueryParams()}`}
|
||||
disabled={selected.length === 0}
|
||||
/>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
</OperationContext.Provider>
|
||||
</Form>
|
||||
</OperationContext.Provider>
|
||||
</DocumentInfoProvider>
|
||||
</Drawer>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -7,13 +7,14 @@ import { extractTranslations } from '../../translations/extractTranslations'
|
||||
const labels = extractTranslations(['authentication:enableAPIKey', 'authentication:apiKey'])
|
||||
|
||||
const encryptKey: FieldHook = ({ req, value }) =>
|
||||
value ? req.payload.encrypt(value as string) : undefined
|
||||
value ? req.payload.encrypt(value as string) : null
|
||||
const decryptKey: FieldHook = ({ req, value }) =>
|
||||
value ? req.payload.decrypt(value as string) : undefined
|
||||
|
||||
export default [
|
||||
{
|
||||
name: 'enableAPIKey',
|
||||
type: 'checkbox',
|
||||
admin: {
|
||||
components: {
|
||||
Field: () => null,
|
||||
@@ -21,10 +22,10 @@ export default [
|
||||
},
|
||||
defaultValue: false,
|
||||
label: labels['authentication:enableAPIKey'],
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'apiKey',
|
||||
type: 'text',
|
||||
admin: {
|
||||
components: {
|
||||
Field: () => null,
|
||||
@@ -35,10 +36,10 @@ export default [
|
||||
beforeChange: [encryptKey],
|
||||
},
|
||||
label: labels['authentication:apiKey'],
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'apiKeyIndex',
|
||||
type: 'text',
|
||||
admin: {
|
||||
disabled: true,
|
||||
},
|
||||
@@ -59,6 +60,5 @@ export default [
|
||||
},
|
||||
],
|
||||
},
|
||||
type: 'text',
|
||||
},
|
||||
] as Field[]
|
||||
|
||||
@@ -72,6 +72,8 @@ async function login<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
overrideAccess,
|
||||
req,
|
||||
req: {
|
||||
fallbackLocale,
|
||||
locale,
|
||||
payload,
|
||||
payload: { config, secret },
|
||||
},
|
||||
@@ -196,7 +198,9 @@ async function login<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
context: req.context,
|
||||
depth,
|
||||
doc: user,
|
||||
fallbackLocale,
|
||||
global: null,
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
|
||||
@@ -56,30 +56,33 @@ const args = minimist(process.argv.slice(2))
|
||||
const scriptIndex = args._.findIndex((x) => x === 'build')
|
||||
|
||||
const script = scriptIndex === -1 ? args._[0] : args._[scriptIndex]
|
||||
if (script) {
|
||||
if (script.startsWith('migrate')) {
|
||||
migrate(args).then(() => process.exit(0))
|
||||
} else {
|
||||
switch (script.toLowerCase()) {
|
||||
case 'build': {
|
||||
build()
|
||||
break
|
||||
}
|
||||
|
||||
if (script.startsWith('migrate')) {
|
||||
migrate(args).then(() => process.exit(0))
|
||||
} else {
|
||||
switch (script.toLowerCase()) {
|
||||
case 'build': {
|
||||
build()
|
||||
break
|
||||
case 'generate:types': {
|
||||
generateTypes()
|
||||
break
|
||||
}
|
||||
|
||||
case 'generate:graphqlschema': {
|
||||
generateGraphQLSchema()
|
||||
break
|
||||
}
|
||||
|
||||
default:
|
||||
console.log(`Unknown script "${script}".`)
|
||||
break
|
||||
}
|
||||
|
||||
case 'generate:types': {
|
||||
generateTypes()
|
||||
break
|
||||
}
|
||||
|
||||
case 'generate:graphqlschema': {
|
||||
generateGraphQLSchema()
|
||||
break
|
||||
}
|
||||
|
||||
default:
|
||||
console.log(`Unknown script "${script}".`)
|
||||
break
|
||||
}
|
||||
} else {
|
||||
console.error('No payload script specified. Did you mean to run `payload migrate`?')
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,7 +17,6 @@ import type {
|
||||
import executeAccess from '../../auth/executeAccess'
|
||||
import sendVerificationEmail from '../../auth/sendVerificationEmail'
|
||||
import { registerLocalStrategy } from '../../auth/strategies/local/register'
|
||||
import { fieldAffectsData } from '../../fields/config/types'
|
||||
import { afterChange } from '../../fields/hooks/afterChange'
|
||||
import { afterRead } from '../../fields/hooks/afterRead'
|
||||
import { beforeChange } from '../../fields/hooks/beforeChange'
|
||||
@@ -26,12 +25,12 @@ import { generateFileData } from '../../uploads/generateFileData'
|
||||
import { unlinkTempFiles } from '../../uploads/unlinkTempFiles'
|
||||
import { uploadFiles } from '../../uploads/uploadFiles'
|
||||
import { commitTransaction } from '../../utilities/commitTransaction'
|
||||
import flattenFields from '../../utilities/flattenTopLevelFields'
|
||||
import { initTransaction } from '../../utilities/initTransaction'
|
||||
import { killTransaction } from '../../utilities/killTransaction'
|
||||
import sanitizeInternalFields from '../../utilities/sanitizeInternalFields'
|
||||
import { saveVersion } from '../../versions/saveVersion'
|
||||
import { buildAfterOperation } from './utils'
|
||||
import flattenFields from '../../utilities/flattenTopLevelFields'
|
||||
|
||||
const unlinkFile = promisify(fs.unlink)
|
||||
|
||||
@@ -88,6 +87,8 @@ async function create<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
overrideAccess,
|
||||
overwriteExistingFiles = false,
|
||||
req: {
|
||||
fallbackLocale,
|
||||
locale,
|
||||
payload,
|
||||
payload: { config, emailOptions },
|
||||
},
|
||||
@@ -289,7 +290,9 @@ async function create<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
context: req.context,
|
||||
depth,
|
||||
doc: result,
|
||||
fallbackLocale,
|
||||
global: null,
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
|
||||
@@ -66,6 +66,7 @@ async function deleteOperation<TSlug extends keyof GeneratedTypes['collections']
|
||||
depth,
|
||||
overrideAccess,
|
||||
req: {
|
||||
fallbackLocale,
|
||||
locale,
|
||||
payload: { config },
|
||||
payload,
|
||||
@@ -149,9 +150,9 @@ async function deleteOperation<TSlug extends keyof GeneratedTypes['collections']
|
||||
if (collectionConfig.versions) {
|
||||
await deleteCollectionVersions({
|
||||
id,
|
||||
slug: collectionConfig.slug,
|
||||
payload,
|
||||
req,
|
||||
slug: collectionConfig.slug,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -178,7 +179,9 @@ async function deleteOperation<TSlug extends keyof GeneratedTypes['collections']
|
||||
context: req.context,
|
||||
depth,
|
||||
doc: result || doc,
|
||||
fallbackLocale,
|
||||
global: null,
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
|
||||
@@ -59,6 +59,8 @@ async function deleteByID<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
depth,
|
||||
overrideAccess,
|
||||
req: {
|
||||
fallbackLocale,
|
||||
locale,
|
||||
payload: { config },
|
||||
payload,
|
||||
t,
|
||||
@@ -120,9 +122,9 @@ async function deleteByID<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
if (collectionConfig.versions) {
|
||||
await deleteCollectionVersions({
|
||||
id,
|
||||
slug: collectionConfig.slug,
|
||||
payload,
|
||||
req,
|
||||
slug: collectionConfig.slug,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -156,7 +158,9 @@ async function deleteByID<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
context: req.context,
|
||||
depth,
|
||||
doc: result,
|
||||
fallbackLocale,
|
||||
global: null,
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
|
||||
@@ -68,7 +68,7 @@ async function find<T extends TypeWithID & Record<string, unknown>>(
|
||||
overrideAccess,
|
||||
page,
|
||||
pagination = true,
|
||||
req: { locale, payload },
|
||||
req: { fallbackLocale, locale, payload },
|
||||
req,
|
||||
showHiddenFields,
|
||||
sort,
|
||||
@@ -196,8 +196,10 @@ async function find<T extends TypeWithID & Record<string, unknown>>(
|
||||
currentDepth,
|
||||
depth,
|
||||
doc,
|
||||
fallbackLocale,
|
||||
findMany: true,
|
||||
global: null,
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
|
||||
@@ -56,7 +56,7 @@ async function findByID<T extends TypeWithID>(incomingArgs: Arguments): Promise<
|
||||
disableErrors,
|
||||
draft: draftEnabled = false,
|
||||
overrideAccess = false,
|
||||
req: { locale, t },
|
||||
req: { fallbackLocale, locale, t },
|
||||
req,
|
||||
showHiddenFields,
|
||||
} = args
|
||||
@@ -139,7 +139,9 @@ async function findByID<T extends TypeWithID>(incomingArgs: Arguments): Promise<
|
||||
currentDepth,
|
||||
depth,
|
||||
doc: result,
|
||||
fallbackLocale,
|
||||
global: null,
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
|
||||
@@ -34,7 +34,7 @@ async function findVersionByID<T extends TypeWithID = any>(
|
||||
depth,
|
||||
disableErrors,
|
||||
overrideAccess,
|
||||
req: { locale, payload, t },
|
||||
req: { fallbackLocale, locale, payload, t },
|
||||
req,
|
||||
showHiddenFields,
|
||||
} = args
|
||||
@@ -112,7 +112,9 @@ async function findVersionByID<T extends TypeWithID = any>(
|
||||
currentDepth,
|
||||
depth,
|
||||
doc: result.version,
|
||||
fallbackLocale,
|
||||
global: null,
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
|
||||
@@ -37,7 +37,7 @@ async function findVersions<T extends TypeWithVersion<T>>(
|
||||
overrideAccess,
|
||||
page,
|
||||
pagination = true,
|
||||
req: { locale, payload },
|
||||
req: { fallbackLocale, locale, payload },
|
||||
req,
|
||||
showHiddenFields,
|
||||
sort,
|
||||
@@ -125,8 +125,10 @@ async function findVersions<T extends TypeWithVersion<T>>(
|
||||
context: req.context,
|
||||
depth,
|
||||
doc: data.version,
|
||||
fallbackLocale,
|
||||
findMany: true,
|
||||
global: null,
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
|
||||
@@ -34,7 +34,7 @@ async function restoreVersion<T extends TypeWithID = any>(args: Arguments): Prom
|
||||
depth,
|
||||
overrideAccess = false,
|
||||
req,
|
||||
req: { locale, payload, t },
|
||||
req: { fallbackLocale, locale, payload, t },
|
||||
showHiddenFields,
|
||||
} = args
|
||||
|
||||
@@ -140,7 +140,9 @@ async function restoreVersion<T extends TypeWithID = any>(args: Arguments): Prom
|
||||
context: req.context,
|
||||
depth,
|
||||
doc: result,
|
||||
fallbackLocale,
|
||||
global: null,
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
|
||||
@@ -75,6 +75,7 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
overrideAccess,
|
||||
overwriteExistingFiles = false,
|
||||
req: {
|
||||
fallbackLocale,
|
||||
locale,
|
||||
payload: { config },
|
||||
payload,
|
||||
@@ -176,7 +177,9 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
context: req.context,
|
||||
depth: 0,
|
||||
doc,
|
||||
fallbackLocale,
|
||||
global: null,
|
||||
locale,
|
||||
overrideAccess: true,
|
||||
req,
|
||||
showHiddenFields: true,
|
||||
@@ -309,7 +312,9 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
context: req.context,
|
||||
depth,
|
||||
doc: result,
|
||||
fallbackLocale: null,
|
||||
global: null,
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
|
||||
@@ -76,6 +76,7 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
overrideAccess,
|
||||
overwriteExistingFiles = false,
|
||||
req: {
|
||||
fallbackLocale,
|
||||
locale,
|
||||
payload: { config },
|
||||
payload,
|
||||
@@ -130,7 +131,9 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
context: req.context,
|
||||
depth: 0,
|
||||
doc: docWithLocales,
|
||||
fallbackLocale: null,
|
||||
global: null,
|
||||
locale,
|
||||
overrideAccess: true,
|
||||
req,
|
||||
showHiddenFields: true,
|
||||
@@ -297,7 +300,9 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
context: req.context,
|
||||
depth,
|
||||
doc: result,
|
||||
fallbackLocale,
|
||||
global: null,
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
|
||||
@@ -56,7 +56,7 @@ const errorHandler =
|
||||
err,
|
||||
response,
|
||||
req.context,
|
||||
req.collection.config,
|
||||
null,
|
||||
)) || {
|
||||
response,
|
||||
status,
|
||||
|
||||
@@ -11,9 +11,11 @@ type Args = {
|
||||
currentDepth?: number
|
||||
depth: number
|
||||
doc: Record<string, unknown>
|
||||
fallbackLocale: null | string
|
||||
findMany?: boolean
|
||||
flattenLocales?: boolean
|
||||
global: SanitizedGlobalConfig | null
|
||||
locale: string
|
||||
overrideAccess: boolean
|
||||
req: PayloadRequest
|
||||
showHiddenFields: boolean
|
||||
@@ -26,9 +28,11 @@ export async function afterRead<T = any>(args: Args): Promise<T> {
|
||||
currentDepth: incomingCurrentDepth,
|
||||
depth: incomingDepth,
|
||||
doc: incomingDoc,
|
||||
fallbackLocale,
|
||||
findMany,
|
||||
flattenLocales = true,
|
||||
global,
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
@@ -52,11 +56,13 @@ export async function afterRead<T = any>(args: Args): Promise<T> {
|
||||
currentDepth,
|
||||
depth,
|
||||
doc,
|
||||
fallbackLocale,
|
||||
fieldPromises,
|
||||
fields: collection?.fields || global?.fields,
|
||||
findMany,
|
||||
flattenLocales,
|
||||
global,
|
||||
locale,
|
||||
overrideAccess,
|
||||
populationPromises,
|
||||
req,
|
||||
|
||||
@@ -16,11 +16,13 @@ type Args = {
|
||||
currentDepth: number
|
||||
depth: number
|
||||
doc: Record<string, unknown>
|
||||
fallbackLocale: null | string
|
||||
field: Field | TabAsField
|
||||
fieldPromises: Promise<void>[]
|
||||
findMany: boolean
|
||||
flattenLocales: boolean
|
||||
global: SanitizedGlobalConfig | null
|
||||
locale: null | string
|
||||
overrideAccess: boolean
|
||||
populationPromises: Promise<void>[]
|
||||
req: PayloadRequest
|
||||
@@ -44,11 +46,13 @@ export const promise = async ({
|
||||
currentDepth,
|
||||
depth,
|
||||
doc,
|
||||
fallbackLocale,
|
||||
field,
|
||||
fieldPromises,
|
||||
findMany,
|
||||
flattenLocales,
|
||||
global,
|
||||
locale,
|
||||
overrideAccess,
|
||||
populationPromises,
|
||||
req,
|
||||
@@ -72,18 +76,13 @@ export const promise = async ({
|
||||
typeof siblingDoc[field.name] === 'object' &&
|
||||
siblingDoc[field.name] !== null &&
|
||||
field.localized &&
|
||||
req.locale !== 'all' &&
|
||||
locale !== 'all' &&
|
||||
req.payload.config.localization
|
||||
|
||||
if (shouldHoistLocalizedValue) {
|
||||
// replace actual value with localized value before sanitizing
|
||||
// { [locale]: fields } -> fields
|
||||
const { locale } = req
|
||||
const value = siblingDoc[field.name][locale]
|
||||
const fallbackLocale =
|
||||
req.payload.config.localization &&
|
||||
req.payload.config.localization?.fallback &&
|
||||
req.fallbackLocale
|
||||
|
||||
let hoistedValue = value
|
||||
|
||||
@@ -201,7 +200,7 @@ export const promise = async ({
|
||||
|
||||
const shouldRunHookOnAllLocales =
|
||||
field.localized &&
|
||||
(req.locale === 'all' || !flattenLocales) &&
|
||||
(locale === 'all' || !flattenLocales) &&
|
||||
typeof siblingDoc[field.name] === 'object'
|
||||
|
||||
if (shouldRunHookOnAllLocales) {
|
||||
@@ -277,7 +276,7 @@ export const promise = async ({
|
||||
) {
|
||||
siblingDoc[field.name] = await getValueWithDefault({
|
||||
defaultValue: field.defaultValue,
|
||||
locale: req.locale,
|
||||
locale,
|
||||
user: req.user,
|
||||
value: siblingDoc[field.name],
|
||||
})
|
||||
@@ -288,7 +287,9 @@ export const promise = async ({
|
||||
relationshipPopulationPromise({
|
||||
currentDepth,
|
||||
depth,
|
||||
fallbackLocale,
|
||||
field,
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
@@ -309,11 +310,13 @@ export const promise = async ({
|
||||
currentDepth,
|
||||
depth,
|
||||
doc,
|
||||
fallbackLocale,
|
||||
fieldPromises,
|
||||
fields: field.fields,
|
||||
findMany,
|
||||
flattenLocales,
|
||||
global,
|
||||
locale,
|
||||
overrideAccess,
|
||||
populationPromises,
|
||||
req,
|
||||
@@ -337,11 +340,13 @@ export const promise = async ({
|
||||
currentDepth,
|
||||
depth,
|
||||
doc,
|
||||
fallbackLocale,
|
||||
fieldPromises,
|
||||
fields: field.fields,
|
||||
findMany,
|
||||
flattenLocales,
|
||||
global,
|
||||
locale,
|
||||
overrideAccess,
|
||||
populationPromises,
|
||||
req,
|
||||
@@ -361,11 +366,13 @@ export const promise = async ({
|
||||
currentDepth,
|
||||
depth,
|
||||
doc,
|
||||
fallbackLocale,
|
||||
fieldPromises,
|
||||
fields: field.fields,
|
||||
findMany,
|
||||
flattenLocales,
|
||||
global,
|
||||
locale,
|
||||
overrideAccess,
|
||||
populationPromises,
|
||||
req,
|
||||
@@ -397,11 +404,13 @@ export const promise = async ({
|
||||
currentDepth,
|
||||
depth,
|
||||
doc,
|
||||
fallbackLocale,
|
||||
fieldPromises,
|
||||
fields: block.fields,
|
||||
findMany,
|
||||
flattenLocales,
|
||||
global,
|
||||
locale,
|
||||
overrideAccess,
|
||||
populationPromises,
|
||||
req,
|
||||
@@ -425,11 +434,13 @@ export const promise = async ({
|
||||
currentDepth,
|
||||
depth,
|
||||
doc,
|
||||
fallbackLocale,
|
||||
fieldPromises,
|
||||
fields: block.fields,
|
||||
findMany,
|
||||
flattenLocales,
|
||||
global,
|
||||
locale,
|
||||
overrideAccess,
|
||||
populationPromises,
|
||||
req,
|
||||
@@ -457,11 +468,13 @@ export const promise = async ({
|
||||
currentDepth,
|
||||
depth,
|
||||
doc,
|
||||
fallbackLocale,
|
||||
fieldPromises,
|
||||
fields: field.fields,
|
||||
findMany,
|
||||
flattenLocales,
|
||||
global,
|
||||
locale,
|
||||
overrideAccess,
|
||||
populationPromises,
|
||||
req,
|
||||
@@ -487,11 +500,13 @@ export const promise = async ({
|
||||
currentDepth,
|
||||
depth,
|
||||
doc,
|
||||
fallbackLocale,
|
||||
fieldPromises,
|
||||
fields: field.fields,
|
||||
findMany,
|
||||
flattenLocales,
|
||||
global,
|
||||
locale,
|
||||
overrideAccess,
|
||||
populationPromises,
|
||||
req,
|
||||
@@ -511,11 +526,13 @@ export const promise = async ({
|
||||
currentDepth,
|
||||
depth,
|
||||
doc,
|
||||
fallbackLocale,
|
||||
fieldPromises,
|
||||
fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })),
|
||||
findMany,
|
||||
flattenLocales,
|
||||
global,
|
||||
locale,
|
||||
overrideAccess,
|
||||
populationPromises,
|
||||
req,
|
||||
|
||||
@@ -8,9 +8,11 @@ type PopulateArgs = {
|
||||
data: Record<string, unknown>
|
||||
dataReference: Record<string, any>
|
||||
depth: number
|
||||
fallbackLocale: null | string
|
||||
field: RelationshipField | UploadField
|
||||
index?: number
|
||||
key?: string
|
||||
locale: null | string
|
||||
overrideAccess: boolean
|
||||
req: PayloadRequest
|
||||
showHiddenFields: boolean
|
||||
@@ -21,9 +23,11 @@ const populate = async ({
|
||||
data,
|
||||
dataReference,
|
||||
depth,
|
||||
fallbackLocale,
|
||||
field,
|
||||
index,
|
||||
key,
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
@@ -54,8 +58,8 @@ const populate = async ({
|
||||
id,
|
||||
depth,
|
||||
currentDepth + 1,
|
||||
req.locale,
|
||||
req.fallbackLocale,
|
||||
locale,
|
||||
fallbackLocale,
|
||||
overrideAccess,
|
||||
showHiddenFields,
|
||||
]),
|
||||
@@ -90,7 +94,9 @@ const populate = async ({
|
||||
type PromiseArgs = {
|
||||
currentDepth: number
|
||||
depth: number
|
||||
fallbackLocale: null | string
|
||||
field: RelationshipField | UploadField
|
||||
locale: null | string
|
||||
overrideAccess: boolean
|
||||
req: PayloadRequest
|
||||
showHiddenFields: boolean
|
||||
@@ -100,7 +106,9 @@ type PromiseArgs = {
|
||||
const relationshipPopulationPromise = async ({
|
||||
currentDepth,
|
||||
depth,
|
||||
fallbackLocale,
|
||||
field,
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
@@ -112,7 +120,7 @@ const relationshipPopulationPromise = async ({
|
||||
|
||||
if (fieldSupportsMany(field) && field.hasMany) {
|
||||
if (
|
||||
req.locale === 'all' &&
|
||||
locale === 'all' &&
|
||||
typeof siblingDoc[field.name] === 'object' &&
|
||||
siblingDoc[field.name] !== null
|
||||
) {
|
||||
@@ -125,9 +133,11 @@ const relationshipPopulationPromise = async ({
|
||||
data: siblingDoc[field.name][key][index],
|
||||
dataReference: resultingDoc,
|
||||
depth: populateDepth,
|
||||
fallbackLocale,
|
||||
field,
|
||||
index,
|
||||
key,
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
@@ -146,8 +156,10 @@ const relationshipPopulationPromise = async ({
|
||||
data: relatedDoc,
|
||||
dataReference: resultingDoc,
|
||||
depth: populateDepth,
|
||||
fallbackLocale,
|
||||
field,
|
||||
index,
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
@@ -161,7 +173,7 @@ const relationshipPopulationPromise = async ({
|
||||
} else if (
|
||||
typeof siblingDoc[field.name] === 'object' &&
|
||||
siblingDoc[field.name] !== null &&
|
||||
req.locale === 'all'
|
||||
locale === 'all'
|
||||
) {
|
||||
Object.keys(siblingDoc[field.name]).forEach((key) => {
|
||||
const rowPromise = async () => {
|
||||
@@ -170,8 +182,10 @@ const relationshipPopulationPromise = async ({
|
||||
data: siblingDoc[field.name][key],
|
||||
dataReference: resultingDoc,
|
||||
depth: populateDepth,
|
||||
fallbackLocale,
|
||||
field,
|
||||
key,
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
@@ -187,7 +201,9 @@ const relationshipPopulationPromise = async ({
|
||||
data: siblingDoc[field.name],
|
||||
dataReference: resultingDoc,
|
||||
depth: populateDepth,
|
||||
fallbackLocale,
|
||||
field,
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
|
||||
@@ -11,11 +11,13 @@ type Args = {
|
||||
currentDepth: number
|
||||
depth: number
|
||||
doc: Record<string, unknown>
|
||||
fallbackLocale: null | string
|
||||
fieldPromises: Promise<void>[]
|
||||
fields: (Field | TabAsField)[]
|
||||
findMany: boolean
|
||||
flattenLocales: boolean
|
||||
global: SanitizedGlobalConfig | null
|
||||
locale: null | string
|
||||
overrideAccess: boolean
|
||||
populationPromises: Promise<void>[]
|
||||
req: PayloadRequest
|
||||
@@ -31,11 +33,13 @@ export const traverseFields = ({
|
||||
currentDepth,
|
||||
depth,
|
||||
doc,
|
||||
fallbackLocale,
|
||||
fieldPromises,
|
||||
fields,
|
||||
findMany,
|
||||
flattenLocales,
|
||||
global,
|
||||
locale,
|
||||
overrideAccess,
|
||||
populationPromises,
|
||||
req,
|
||||
@@ -52,11 +56,13 @@ export const traverseFields = ({
|
||||
currentDepth,
|
||||
depth,
|
||||
doc,
|
||||
fallbackLocale,
|
||||
field,
|
||||
fieldPromises,
|
||||
findMany,
|
||||
flattenLocales,
|
||||
global,
|
||||
locale,
|
||||
overrideAccess,
|
||||
populationPromises,
|
||||
req,
|
||||
|
||||
@@ -23,14 +23,14 @@ type Args = {
|
||||
|
||||
async function findOne<T extends Record<string, unknown>>(args: Args): Promise<T> {
|
||||
const {
|
||||
slug,
|
||||
depth,
|
||||
draft: draftEnabled = false,
|
||||
globalConfig,
|
||||
overrideAccess = false,
|
||||
req: { locale, payload },
|
||||
req: { fallbackLocale, locale, payload },
|
||||
req,
|
||||
showHiddenFields,
|
||||
slug,
|
||||
} = args
|
||||
|
||||
try {
|
||||
@@ -51,9 +51,9 @@ async function findOne<T extends Record<string, unknown>>(args: Args): Promise<T
|
||||
// /////////////////////////////////////
|
||||
|
||||
let doc = await req.payload.db.findGlobal({
|
||||
slug,
|
||||
locale,
|
||||
req,
|
||||
slug,
|
||||
where: overrideAccess ? undefined : (accessResult as Where),
|
||||
})
|
||||
if (!doc) {
|
||||
@@ -100,7 +100,9 @@ async function findOne<T extends Record<string, unknown>>(args: Args): Promise<T
|
||||
context: req.context,
|
||||
depth,
|
||||
doc,
|
||||
fallbackLocale,
|
||||
global: globalConfig,
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
|
||||
@@ -31,7 +31,7 @@ async function findVersionByID<T extends TypeWithVersion<T> = any>(args: Argumen
|
||||
disableErrors,
|
||||
globalConfig,
|
||||
overrideAccess,
|
||||
req: { locale, payload, t },
|
||||
req: { fallbackLocale, locale, payload, t },
|
||||
req,
|
||||
showHiddenFields,
|
||||
} = args
|
||||
@@ -108,7 +108,9 @@ async function findVersionByID<T extends TypeWithVersion<T> = any>(args: Argumen
|
||||
currentDepth,
|
||||
depth,
|
||||
doc: result.version,
|
||||
fallbackLocale,
|
||||
global: globalConfig,
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
|
||||
@@ -35,7 +35,7 @@ async function findVersions<T extends TypeWithVersion<T>>(
|
||||
limit,
|
||||
overrideAccess,
|
||||
page,
|
||||
req: { locale, payload },
|
||||
req: { fallbackLocale, locale, payload },
|
||||
req,
|
||||
showHiddenFields,
|
||||
sort,
|
||||
@@ -97,8 +97,10 @@ async function findVersions<T extends TypeWithVersion<T>>(
|
||||
// Patch globalType onto version doc
|
||||
globalType: globalConfig.slug,
|
||||
},
|
||||
fallbackLocale,
|
||||
findMany: true,
|
||||
global: globalConfig,
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
|
||||
@@ -25,7 +25,7 @@ async function restoreVersion<T extends TypeWithVersion<T> = any>(args: Argument
|
||||
depth,
|
||||
globalConfig,
|
||||
overrideAccess,
|
||||
req: { payload, t },
|
||||
req: { fallbackLocale, locale, payload, t },
|
||||
req,
|
||||
showHiddenFields,
|
||||
} = args
|
||||
@@ -66,9 +66,9 @@ async function restoreVersion<T extends TypeWithVersion<T> = any>(args: Argument
|
||||
// /////////////////////////////////////
|
||||
|
||||
const previousDoc = await payload.findGlobal({
|
||||
slug: globalConfig.slug,
|
||||
depth,
|
||||
req,
|
||||
slug: globalConfig.slug,
|
||||
})
|
||||
|
||||
// /////////////////////////////////////
|
||||
@@ -76,23 +76,23 @@ async function restoreVersion<T extends TypeWithVersion<T> = any>(args: Argument
|
||||
// /////////////////////////////////////
|
||||
|
||||
const global = await payload.db.findGlobal({
|
||||
req,
|
||||
slug: globalConfig.slug,
|
||||
req,
|
||||
})
|
||||
|
||||
let result = rawVersion.version
|
||||
|
||||
if (global) {
|
||||
result = await payload.db.updateGlobal({
|
||||
slug: globalConfig.slug,
|
||||
data: result,
|
||||
req,
|
||||
slug: globalConfig.slug,
|
||||
})
|
||||
} else {
|
||||
result = await payload.db.createGlobal({
|
||||
slug: globalConfig.slug,
|
||||
data: result,
|
||||
req,
|
||||
slug: globalConfig.slug,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -105,7 +105,9 @@ async function restoreVersion<T extends TypeWithVersion<T> = any>(args: Argument
|
||||
context: req.context,
|
||||
depth,
|
||||
doc: result,
|
||||
fallbackLocale,
|
||||
global: globalConfig,
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
|
||||
@@ -32,15 +32,15 @@ async function update<TSlug extends keyof GeneratedTypes['globals']>(
|
||||
args: Args<GeneratedTypes['globals'][TSlug]>,
|
||||
): Promise<GeneratedTypes['globals'][TSlug]> {
|
||||
const {
|
||||
slug,
|
||||
autosave,
|
||||
depth,
|
||||
draft: draftArg,
|
||||
globalConfig,
|
||||
overrideAccess,
|
||||
req: { locale, payload },
|
||||
req: { fallbackLocale, locale, payload },
|
||||
req,
|
||||
showHiddenFields,
|
||||
slug,
|
||||
} = args
|
||||
|
||||
try {
|
||||
@@ -74,11 +74,11 @@ async function update<TSlug extends keyof GeneratedTypes['globals']>(
|
||||
// 2. Retrieve document
|
||||
// /////////////////////////////////////
|
||||
const { global, globalExists } = await getLatestGlobalVersion({
|
||||
slug,
|
||||
config: globalConfig,
|
||||
locale,
|
||||
payload,
|
||||
req,
|
||||
slug,
|
||||
where: query,
|
||||
})
|
||||
|
||||
@@ -97,7 +97,9 @@ async function update<TSlug extends keyof GeneratedTypes['globals']>(
|
||||
context: req.context,
|
||||
depth: 0,
|
||||
doc: globalJSON,
|
||||
fallbackLocale,
|
||||
global: globalConfig,
|
||||
locale,
|
||||
overrideAccess: true,
|
||||
req,
|
||||
showHiddenFields,
|
||||
@@ -175,15 +177,15 @@ async function update<TSlug extends keyof GeneratedTypes['globals']>(
|
||||
if (!shouldSaveDraft) {
|
||||
if (globalExists) {
|
||||
result = await payload.db.updateGlobal({
|
||||
slug,
|
||||
data: result,
|
||||
req,
|
||||
slug,
|
||||
})
|
||||
} else {
|
||||
result = await payload.db.createGlobal({
|
||||
slug,
|
||||
data: result,
|
||||
req,
|
||||
slug,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -218,7 +220,9 @@ async function update<TSlug extends keyof GeneratedTypes['globals']>(
|
||||
context: req.context,
|
||||
depth,
|
||||
doc: result,
|
||||
fallbackLocale: null,
|
||||
global: globalConfig,
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
showHiddenFields,
|
||||
|
||||
@@ -274,7 +274,7 @@
|
||||
"near": "nær"
|
||||
},
|
||||
"upload": {
|
||||
"crop": "Avling",
|
||||
"crop": "Beskjær",
|
||||
"cropToolDescription": "Dra hjørnene av det valgte området, tegn et nytt område eller juster verdiene nedenfor.",
|
||||
"dragAndDrop": "Dra og slipp en fil",
|
||||
"dragAndDropHere": "eller dra og slipp en fil her",
|
||||
|
||||
@@ -57,8 +57,10 @@ export async function getEntityPolicies<T extends Args>(args: T): Promise<Return
|
||||
if (typeof where === 'object') {
|
||||
const paginatedRes = await req.payload.find({
|
||||
collection: entity.slug,
|
||||
depth: 0,
|
||||
limit: 1,
|
||||
overrideAccess: true,
|
||||
pagination: false,
|
||||
req,
|
||||
where: {
|
||||
...where,
|
||||
@@ -79,6 +81,7 @@ export async function getEntityPolicies<T extends Args>(args: T): Promise<Return
|
||||
return req.payload.findByID({
|
||||
id,
|
||||
collection: entity.slug,
|
||||
depth: 0,
|
||||
overrideAccess: true,
|
||||
req,
|
||||
})
|
||||
@@ -98,8 +101,13 @@ export async function getEntityPolicies<T extends Args>(args: T): Promise<Return
|
||||
const mutablePolicies = policiesObj
|
||||
|
||||
if (accessLevel === 'field' && docBeingAccessed === undefined) {
|
||||
docBeingAccessed = await getEntityDoc()
|
||||
// assign docBeingAccessed first as the promise to avoid multiple calls to getEntityDoc
|
||||
docBeingAccessed = getEntityDoc().then((doc) => {
|
||||
docBeingAccessed = doc
|
||||
})
|
||||
}
|
||||
// awaiting the promise to ensure docBeingAccessed is assigned before it is used
|
||||
await docBeingAccessed
|
||||
|
||||
const data = req?.body
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-nested-docs",
|
||||
"version": "1.0.11",
|
||||
"version": "1.0.12",
|
||||
"description": "The official Nested Docs plugin for Payload",
|
||||
"repository": "https://github.com/payloadcms/payload",
|
||||
"license": "MIT",
|
||||
@@ -20,6 +20,9 @@
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"payload": "^0.18.5 || ^1.0.0 || ^2.0.0"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"default": "./src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-search",
|
||||
"version": "1.0.1",
|
||||
"version": "1.1.0",
|
||||
"homepage:": "https://payloadcms.com",
|
||||
"repository": "git@github.com:payloadcms/plugin-search.git",
|
||||
"description": "Search plugin for Payload",
|
||||
|
||||
@@ -2,10 +2,12 @@ import en from './en.json'
|
||||
import es from './es.json'
|
||||
import fa from './fa.json'
|
||||
import fr from './fr.json'
|
||||
import pl from './pl.json'
|
||||
|
||||
export default {
|
||||
en,
|
||||
es,
|
||||
fa,
|
||||
fr,
|
||||
pl,
|
||||
}
|
||||
|
||||
22
packages/plugin-seo/src/translations/pl.json
Normal file
22
packages/plugin-seo/src/translations/pl.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"$schema": "./translation-schema.json",
|
||||
"plugin-seo": {
|
||||
"autoGenerate": "Wygeneruj automatycznie",
|
||||
"imageAutoGenerationTip": "Automatyczne generowanie pobierze wybrany główny obraz.",
|
||||
"bestPractices": "najlepsze praktyki",
|
||||
"lengthTipTitle": "Długość powinna wynosić od {{minLength}} do {{maxLength}} znaków. Po porady dotyczące pisania wysokiej jakości meta tytułów zobacz ",
|
||||
"lengthTipDescription": "Długość powinna wynosić od {{minLength}} do {{maxLength}} znaków. Po porady dotyczące pisania wysokiej jakości meta opisów zobacz ",
|
||||
"good": "Dobrze",
|
||||
"tooLong": "Zbyt długie",
|
||||
"tooShort": "Zbyt krótkie",
|
||||
"almostThere": "Prawie gotowe",
|
||||
"characterCount": "{{current}}/{{minLength}}-{{maxLength}} znaków, ",
|
||||
"charactersToGo": "pozostało {{characters}} znaków",
|
||||
"charactersLeftOver": "zostało {{characters}} znaków",
|
||||
"charactersTooMany": "{{characters}} znaków za dużo",
|
||||
"noImage": "Brak obrazu",
|
||||
"checksPassing": "{{current}}/{{max}} testów zakończonych pomyślnie",
|
||||
"preview": "Podgląd",
|
||||
"previewDescription": "Dokładne wyniki listowania mogą się różnić w zależności od treści i zgodności z kryteriami wyszukiwania."
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/richtext-lexical",
|
||||
"version": "0.6.1",
|
||||
"version": "0.7.0",
|
||||
"description": "The officially supported Lexical richtext adapter for Payload",
|
||||
"repository": "https://github.com/payloadcms/payload",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -46,11 +46,13 @@ export const recurseNestedFields = ({
|
||||
currentDepth,
|
||||
depth,
|
||||
doc: data as any, // Looks like it's only needed for hooks and access control, so doesn't matter what we pass here right now
|
||||
fallbackLocale: req.fallbackLocale,
|
||||
fieldPromises: promises, // Not sure if what I pass in here makes sense. But it doesn't seem like it's used at all anyways
|
||||
fields,
|
||||
findMany,
|
||||
flattenLocales,
|
||||
global: null, // Pass from core? This is only needed for hooks, so we can leave this null for now
|
||||
locale: req.locale,
|
||||
overrideAccess,
|
||||
populationPromises, // This is not the same as populationPromises passed into this recurseNestedFields. These are just promises resolved at the very end.
|
||||
req,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { mapAsync } from '../../packages/payload/src/utilities/mapAsync'
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults'
|
||||
import { devUser } from '../credentials'
|
||||
import { AuthDebug } from './AuthDebug'
|
||||
import { namedSaveToJWTValue, saveToJWTKey, slug } from './shared'
|
||||
import { apiKeysSlug, namedSaveToJWTValue, saveToJWTKey, slug } from './shared'
|
||||
|
||||
export default buildConfigWithDefaults({
|
||||
admin: {
|
||||
@@ -171,11 +171,7 @@ export default buildConfigWithDefaults({
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'api-keys',
|
||||
labels: {
|
||||
singular: 'API Key',
|
||||
plural: 'API Keys',
|
||||
},
|
||||
slug: apiKeysSlug,
|
||||
access: {
|
||||
read: ({ req: { user } }) => {
|
||||
if (user.collection === 'api-keys') {
|
||||
@@ -193,6 +189,10 @@ export default buildConfigWithDefaults({
|
||||
useAPIKey: true,
|
||||
},
|
||||
fields: [],
|
||||
labels: {
|
||||
plural: 'API Keys',
|
||||
singular: 'API Key',
|
||||
},
|
||||
},
|
||||
{
|
||||
slug: 'public-users',
|
||||
|
||||
@@ -2,28 +2,34 @@ import type { Page } from '@playwright/test'
|
||||
|
||||
import { expect, test } from '@playwright/test'
|
||||
|
||||
import payload from '../../packages/payload/src'
|
||||
import { initPageConsoleErrorCatch, login, saveDocAndAssert } from '../helpers'
|
||||
import { AdminUrlUtil } from '../helpers/adminUrlUtil'
|
||||
import { initPayloadE2E } from '../helpers/configHelpers'
|
||||
import { slug } from './shared'
|
||||
import { apiKeysSlug, slug } from './shared'
|
||||
|
||||
/**
|
||||
* TODO: Auth
|
||||
* create first user
|
||||
* unlock
|
||||
* generate api key
|
||||
* log out
|
||||
*/
|
||||
|
||||
const { beforeAll, describe } = test
|
||||
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
|
||||
describe('auth', () => {
|
||||
let page: Page
|
||||
let url: AdminUrlUtil
|
||||
let serverURL: string
|
||||
let apiURL: string
|
||||
|
||||
beforeAll(async ({ browser }) => {
|
||||
const { serverURL } = await initPayloadE2E(__dirname)
|
||||
url = new AdminUrlUtil(serverURL, slug)
|
||||
serverURL = (await initPayloadE2E(__dirname)).serverURL
|
||||
apiURL = `${serverURL}/api`
|
||||
|
||||
const context = await browser.newContext()
|
||||
page = await context.newPage()
|
||||
@@ -36,6 +42,10 @@ describe('auth', () => {
|
||||
})
|
||||
|
||||
describe('authenticated users', () => {
|
||||
beforeAll(async ({ browser }) => {
|
||||
url = new AdminUrlUtil(serverURL, slug)
|
||||
})
|
||||
|
||||
test('should allow change password', async () => {
|
||||
await page.goto(url.account)
|
||||
const emailBeforeSave = await page.locator('#field-email').inputValue()
|
||||
@@ -62,4 +72,56 @@ describe('auth', () => {
|
||||
await expect(page.locator('#use-auth-result')).toHaveText('Goodbye, world!')
|
||||
})
|
||||
})
|
||||
|
||||
describe('api-keys', () => {
|
||||
let user
|
||||
|
||||
beforeAll(async () => {
|
||||
url = new AdminUrlUtil(serverURL, apiKeysSlug)
|
||||
|
||||
user = await payload.create({
|
||||
collection: apiKeysSlug,
|
||||
data: {
|
||||
enableAPIKey: true,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test('should enable api key', async () => {
|
||||
await page.goto(url.create)
|
||||
|
||||
// click enable api key checkbox
|
||||
await page.locator('#field-enableAPIKey').click()
|
||||
|
||||
// assert that the value is set
|
||||
const apiKey = await page.locator('#apiKey').inputValue()
|
||||
expect(apiKey).toBeDefined()
|
||||
|
||||
await saveDocAndAssert(page)
|
||||
|
||||
expect(await page.locator('#apiKey').inputValue()).toStrictEqual(apiKey)
|
||||
})
|
||||
|
||||
test('should disable api key', async () => {
|
||||
await page.goto(url.edit(user.id))
|
||||
|
||||
// click enable api key checkbox
|
||||
await page.locator('#field-enableAPIKey').click()
|
||||
|
||||
// assert that the apiKey field is hidden
|
||||
await expect(page.locator('#apiKey')).toBeHidden()
|
||||
|
||||
await saveDocAndAssert(page)
|
||||
|
||||
// use the api key in a fetch to assert that it is disabled
|
||||
const response = await fetch(`${apiURL}/${apiKeysSlug}/me`, {
|
||||
headers: {
|
||||
...headers,
|
||||
Authorization: `${slug} API-Key ${user.apiKey}`,
|
||||
},
|
||||
}).then((res) => res.json())
|
||||
|
||||
expect(response.user).toBeNull()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export const slug = 'users'
|
||||
export const apiKeysSlug = 'api-keys'
|
||||
|
||||
export const namedSaveToJWTValue = 'namedSaveToJWT value'
|
||||
|
||||
|
||||
@@ -35,6 +35,13 @@ const databaseAdapters = {
|
||||
connectionString: process.env.POSTGRES_URL || 'postgres://127.0.0.1:5432/payloadtests',
|
||||
},
|
||||
}),
|
||||
'postgres-custom-schema': postgresAdapter({
|
||||
migrationDir,
|
||||
pool: {
|
||||
connectionString: process.env.POSTGRES_URL || 'postgres://127.0.0.1:5432/payloadtests',
|
||||
},
|
||||
schemaName: 'custom',
|
||||
}),
|
||||
'postgres-uuid': postgresAdapter({
|
||||
idType: 'uuid',
|
||||
migrationDir,
|
||||
|
||||
@@ -3,7 +3,7 @@ import fs from 'fs'
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
import path from 'path'
|
||||
|
||||
import type { DrizzleDB } from '../../packages/db-postgres/src/types'
|
||||
import type { PostgresAdapter } from '../../packages/db-postgres/src/types'
|
||||
import type { TypeWithID } from '../../packages/payload/src/collections/config/types'
|
||||
import type { PayloadRequest } from '../../packages/payload/src/express/types'
|
||||
|
||||
@@ -44,10 +44,14 @@ describe('database', () => {
|
||||
describe('migrations', () => {
|
||||
beforeAll(async () => {
|
||||
if (process.env.PAYLOAD_DROP_DATABASE === 'true' && 'drizzle' in payload.db) {
|
||||
const drizzle = payload.db.drizzle as DrizzleDB
|
||||
// @ts-expect-error drizzle raw sql typing
|
||||
await drizzle.execute(sql`drop schema public cascade;
|
||||
create schema public;`)
|
||||
const db = payload.db as unknown as PostgresAdapter
|
||||
const drizzle = db.drizzle
|
||||
const schemaName = db.schemaName || 'public'
|
||||
|
||||
await drizzle.execute(
|
||||
sql.raw(`drop schema ${schemaName} cascade;
|
||||
create schema ${schemaName};`),
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -21,8 +21,13 @@ const ArrayFields: CollectionConfig = {
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
required: true,
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'localizedText',
|
||||
type: 'text',
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'subArray',
|
||||
|
||||
@@ -32,26 +32,34 @@ const IndexedFields: CollectionConfig = {
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
index: true,
|
||||
required: true,
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'uniqueText',
|
||||
type: 'text',
|
||||
unique: true,
|
||||
},
|
||||
{
|
||||
name: 'uniqueRequiredText',
|
||||
type: 'text',
|
||||
defaultValue: 'uniqueRequired',
|
||||
required: true,
|
||||
unique: true,
|
||||
},
|
||||
{
|
||||
name: 'point',
|
||||
type: 'point',
|
||||
},
|
||||
{
|
||||
name: 'group',
|
||||
type: 'group',
|
||||
fields: [
|
||||
{
|
||||
name: 'localizedUnique',
|
||||
localized: true,
|
||||
type: 'text',
|
||||
localized: true,
|
||||
unique: true,
|
||||
},
|
||||
{
|
||||
@@ -64,25 +72,24 @@ const IndexedFields: CollectionConfig = {
|
||||
type: 'point',
|
||||
},
|
||||
],
|
||||
type: 'group',
|
||||
},
|
||||
{
|
||||
type: 'collapsible',
|
||||
fields: [
|
||||
{
|
||||
name: 'collapsibleLocalizedUnique',
|
||||
localized: true,
|
||||
type: 'text',
|
||||
localized: true,
|
||||
unique: true,
|
||||
},
|
||||
{
|
||||
name: 'collapsibleTextUnique',
|
||||
label: 'collapsibleTextUnique',
|
||||
type: 'text',
|
||||
label: 'collapsibleTextUnique',
|
||||
unique: true,
|
||||
},
|
||||
],
|
||||
label: 'Collapsible',
|
||||
type: 'collapsible',
|
||||
},
|
||||
],
|
||||
versions: true,
|
||||
|
||||
@@ -249,6 +249,7 @@ describe('fields', () => {
|
||||
unique: uniqueText,
|
||||
},
|
||||
text: 'text',
|
||||
uniqueRequiredText: 'text',
|
||||
uniqueText,
|
||||
},
|
||||
})
|
||||
@@ -814,6 +815,95 @@ describe('fields', () => {
|
||||
).toHaveValue(`${assertGroupText3} duplicate`)
|
||||
})
|
||||
})
|
||||
test('should bulk update', async () => {
|
||||
await Promise.all([
|
||||
payload.create({
|
||||
collection: 'array-fields',
|
||||
data: {
|
||||
title: 'for test 1',
|
||||
items: [
|
||||
{
|
||||
text: 'test 1',
|
||||
},
|
||||
{
|
||||
text: 'test 2',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
payload.create({
|
||||
collection: 'array-fields',
|
||||
data: {
|
||||
title: 'for test 2',
|
||||
items: [
|
||||
{
|
||||
text: 'test 3',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
payload.create({
|
||||
collection: 'array-fields',
|
||||
data: {
|
||||
title: 'for test 3',
|
||||
items: [
|
||||
{
|
||||
text: 'test 4',
|
||||
},
|
||||
{
|
||||
text: 'test 5',
|
||||
},
|
||||
{
|
||||
text: 'test 6',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
])
|
||||
|
||||
const bulkText = 'Bulk update text'
|
||||
await page.goto(url.list)
|
||||
await page.waitForSelector('.table > table > tbody > tr td.cell-title')
|
||||
const rows = page.locator('.table > table > tbody > tr', {
|
||||
has: page.locator('td.cell-title span', {
|
||||
hasText: 'for test',
|
||||
}),
|
||||
})
|
||||
const count = await rows.count()
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
await rows
|
||||
.nth(i)
|
||||
.locator('td.cell-_select .checkbox-input__input > input[type="checkbox"]')
|
||||
.check()
|
||||
}
|
||||
await page.locator('.edit-many__toggle').click()
|
||||
await page.locator('.field-select .rs__control').click()
|
||||
|
||||
const arrayOption = page.locator('.rs__option', {
|
||||
hasText: exactText('Items'),
|
||||
})
|
||||
|
||||
await expect(arrayOption).toBeVisible()
|
||||
|
||||
await arrayOption.click()
|
||||
const addRowButton = page.locator('#field-items > .btn.array-field__add-row')
|
||||
|
||||
await expect(addRowButton).toBeVisible()
|
||||
|
||||
await addRowButton.click()
|
||||
|
||||
const targetInput = page.locator('#field-items__0__text')
|
||||
|
||||
await expect(targetInput).toBeVisible()
|
||||
|
||||
await targetInput.fill(bulkText)
|
||||
|
||||
await page.locator('.form-submit button[type="submit"].edit-many__publish').click()
|
||||
await expect(page.locator('.Toastify__toast--success')).toContainText(
|
||||
'Updated 3 Array Fields successfully.',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('tabs', () => {
|
||||
|
||||
@@ -424,7 +424,7 @@ describe('Fields', () => {
|
||||
const definitions: Record<string, IndexDirection> = {}
|
||||
const options: Record<string, IndexOptions> = {}
|
||||
|
||||
beforeEach(() => {
|
||||
beforeAll(() => {
|
||||
indexes = (payload.db as MongooseAdapter).collections[
|
||||
'indexed-fields'
|
||||
].schema.indexes() as [Record<string, IndexDirection>, IndexOptions]
|
||||
@@ -441,8 +441,13 @@ describe('Fields', () => {
|
||||
expect(definitions.text).toEqual(1)
|
||||
})
|
||||
|
||||
it('should have unique indexes', () => {
|
||||
it('should have unique sparse indexes when field is not required', () => {
|
||||
expect(definitions.uniqueText).toEqual(1)
|
||||
expect(options.uniqueText).toMatchObject({ sparse: true, unique: true })
|
||||
})
|
||||
|
||||
it('should have unique indexes that are not sparse when field is required', () => {
|
||||
expect(definitions.uniqueRequiredText).toEqual(1)
|
||||
expect(options.uniqueText).toMatchObject({ unique: true })
|
||||
})
|
||||
|
||||
@@ -594,6 +599,25 @@ describe('Fields', () => {
|
||||
return result.error
|
||||
}).toBeDefined()
|
||||
})
|
||||
|
||||
it('should not throw validation error saving multiple null values for unique fields', async () => {
|
||||
const data = {
|
||||
text: 'a',
|
||||
uniqueRequiredText: 'a',
|
||||
// uniqueText omitted on purpose
|
||||
}
|
||||
await payload.create({
|
||||
collection: 'indexed-fields',
|
||||
data,
|
||||
})
|
||||
data.uniqueRequiredText = 'b'
|
||||
const result = await payload.create({
|
||||
collection: 'indexed-fields',
|
||||
data,
|
||||
})
|
||||
|
||||
expect(result.id).toBeDefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('array', () => {
|
||||
@@ -620,6 +644,51 @@ describe('Fields', () => {
|
||||
expect(doc.localized).toMatchObject(arrayDefaultValue)
|
||||
})
|
||||
|
||||
it('should create and update localized subfields with versions', async () => {
|
||||
const doc = await payload.create({
|
||||
collection,
|
||||
data: {
|
||||
items: [
|
||||
{
|
||||
localizedText: 'test',
|
||||
text: 'required',
|
||||
},
|
||||
],
|
||||
localized: [
|
||||
{
|
||||
text: 'english',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
const spanish = await payload.update({
|
||||
id: doc.id,
|
||||
collection,
|
||||
data: {
|
||||
items: [
|
||||
{
|
||||
id: doc.items[0].id,
|
||||
localizedText: 'spanish',
|
||||
text: 'required',
|
||||
},
|
||||
],
|
||||
},
|
||||
locale: 'es',
|
||||
})
|
||||
|
||||
const result = await payload.findByID({
|
||||
id: doc.id,
|
||||
collection,
|
||||
locale: 'all',
|
||||
})
|
||||
|
||||
expect(doc.items[0].localizedText).toStrictEqual('test')
|
||||
expect(spanish.items[0].localizedText).toStrictEqual('spanish')
|
||||
expect(result.items[0].localizedText.en).toStrictEqual('test')
|
||||
expect(result.items[0].localizedText.es).toStrictEqual('spanish')
|
||||
})
|
||||
|
||||
it('should create with nested array', async () => {
|
||||
const subArrayText = 'something expected'
|
||||
const doc = await payload.create({
|
||||
|
||||
@@ -20,7 +20,7 @@ export async function resetDB(_payload: Payload, collectionSlugs: string[]) {
|
||||
return
|
||||
}
|
||||
const queries = Object.values(schema).map((table: any) => {
|
||||
return sql.raw(`DELETE FROM ${table.dbName}`)
|
||||
return sql.raw(`DELETE FROM ${db.schemaName ? db.schemaName + '.' : ''}${table.dbName}`)
|
||||
})
|
||||
|
||||
await db.drizzle.transaction(async (trx) => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { SanitizedConfig } from '../../packages/payload/src/config/types'
|
||||
|
||||
import { APIError } from '../../packages/payload/errors'
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults'
|
||||
import AfterOperation from './collections/AfterOperation'
|
||||
import ChainingHooks from './collections/ChainingHooks'
|
||||
@@ -24,6 +25,18 @@ export const HooksConfig: Promise<SanitizedConfig> = buildConfigWithDefaults({
|
||||
DataHooks,
|
||||
],
|
||||
globals: [DataHooksGlobal],
|
||||
endpoints: [
|
||||
{
|
||||
path: '/throw-to-after-error',
|
||||
method: 'get',
|
||||
handler: () => {
|
||||
throw new APIError("I'm a teapot", 418)
|
||||
},
|
||||
},
|
||||
],
|
||||
hooks: {
|
||||
afterError: () => console.log('Running afterError hook'),
|
||||
},
|
||||
onInit: async (payload) => {
|
||||
await seedHooksUsers(payload)
|
||||
await payload.create({
|
||||
|
||||
@@ -462,4 +462,13 @@ describe('Hooks', () => {
|
||||
expect(doc.field_globalAndField).toStrictEqual(globalAndFieldString + globalAndFieldString)
|
||||
})
|
||||
})
|
||||
|
||||
describe('config level after error hook', () => {
|
||||
it('should handle error', async () => {
|
||||
const response = await fetch(`${apiUrl}/throw-to-after-error`)
|
||||
const body = await response.json()
|
||||
expect(response.status).toEqual(418)
|
||||
expect(body).toEqual({ errors: [{ message: "I'm a teapot" }] })
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -157,6 +157,7 @@ describe('Localization', () => {
|
||||
id,
|
||||
collection: localizedPostsSlug,
|
||||
data: {
|
||||
localizedCheckbox: false,
|
||||
title: spanishTitle,
|
||||
},
|
||||
locale: spanishLocale,
|
||||
@@ -174,8 +175,6 @@ describe('Localization', () => {
|
||||
await changeLocale(page, spanishLocale)
|
||||
await expect(page.locator('#field-title')).toHaveValue(spanishTitle)
|
||||
|
||||
// click checkbox manually
|
||||
await page.locator('#field-localizedCheckbox').click()
|
||||
await expect(page.locator('#field-localizedCheckbox')).not.toBeChecked()
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user