diff --git a/packages/db-postgres/src/createGlobal.ts b/packages/db-postgres/src/createGlobal.ts new file mode 100644 index 0000000000..a610d7a560 --- /dev/null +++ b/packages/db-postgres/src/createGlobal.ts @@ -0,0 +1,23 @@ +import { PayloadRequest } from 'payload/types'; +import toSnakeCase from 'to-snake-case'; +import { CreateGlobal } from 'payload/dist/database/types'; +import { upsertRow } from './upsertRow'; +import { PostgresAdapter } from './types'; + +export const createGlobal: CreateGlobal = async function createGlobal( + this: PostgresAdapter, + { data, slug, req = {} as PayloadRequest }, +) { + const globalConfig = this.payload.globals.config.find((config) => config.slug === slug); + + const result = await upsertRow({ + adapter: this, + data, + fields: globalConfig.fields, + locale: req.locale, + operation: 'create', + tableName: toSnakeCase(slug), + }); + + return result; +}; diff --git a/packages/db-postgres/src/findGlobal.ts b/packages/db-postgres/src/findGlobal.ts new file mode 100644 index 0000000000..09286cd67a --- /dev/null +++ b/packages/db-postgres/src/findGlobal.ts @@ -0,0 +1,44 @@ +import toSnakeCase from 'to-snake-case'; +import type { FindGlobal } from 'payload/dist/database/types'; +import buildQuery from './queries/buildQuery'; +import { buildFindManyArgs } from './find/buildFindManyArgs'; +import { transform } from './transform/read'; +import { PostgresAdapter } from './types'; + +export const findGlobal: FindGlobal = async function findGlobal( + this: PostgresAdapter, + { slug, locale, where }, +) { + const globalConfig = this.payload.globals.config.find((config) => config.slug === slug); + const tableName = toSnakeCase(slug); + + const query = await buildQuery({ + adapter: this, + globalSlug: slug, + locale, + where, + }); + + const findManyArgs = buildFindManyArgs({ + adapter: this, + depth: 0, + fields: globalConfig.fields, + tableName, + }); + + findManyArgs.where = query; + + const doc = await this.db.query[tableName].findFirst(findManyArgs); + + if (doc) { + const result = transform({ + config: this.payload.config, + data: doc, + fields: globalConfig.fields, + }); + + return result; + } + + return null; +}; diff --git a/packages/db-postgres/src/index.ts b/packages/db-postgres/src/index.ts index f19bf4f1a5..36b8d61c50 100644 --- a/packages/db-postgres/src/index.ts +++ b/packages/db-postgres/src/index.ts @@ -5,7 +5,7 @@ import { init } from './init'; import { createMigration } from './createMigration'; import { webpack } from './webpack'; import { Args, PostgresAdapter, PostgresAdapterResult } from './types'; -// import { createGlobal } from './createGlobal'; +import { createGlobal } from './createGlobal'; // import { createVersion } from './createVersion'; // import { beginTransaction } from './transactions/beginTransaction'; // import { rollbackTransaction } from './transactions/rollbackTransaction'; @@ -17,9 +17,9 @@ import { find } from './find'; import { create } from './create'; // import { deleteOne } from './deleteOne'; // import { deleteVersions } from './deleteVersions'; -// import { findGlobal } from './findGlobal'; +import { findGlobal } from './findGlobal'; import { findOne } from './findOne'; -// import { updateGlobal } from './updateGlobal'; +import { updateGlobal } from './updateGlobal'; import { updateOne } from './update'; // import { updateVersion } from './updateVersion'; // import { deleteMany } from './deleteMany'; @@ -51,9 +51,9 @@ export function postgresAdapter(args: Args): PostgresAdapterResult { updateOne, // deleteOne, // deleteMany, - // findGlobal, - // createGlobal, - // updateGlobal, + findGlobal, + createGlobal, + updateGlobal, // findVersions, // findGlobalVersions, // createVersion, diff --git a/packages/db-postgres/src/init.ts b/packages/db-postgres/src/init.ts index 787ed3a442..ec47cecb3f 100644 --- a/packages/db-postgres/src/init.ts +++ b/packages/db-postgres/src/init.ts @@ -11,7 +11,7 @@ export const init: Init = async function init(this: PostgresAdapter) { '_locales', // TODO: types out of sync with core, monorepo please // this.payload.config.localization.localeCodes, - (this.payload.config.localization.locales as unknown as {code: string}[]).map(({ code }) => code) as [string, ...string[]], + (this.payload.config.localization.locales as unknown as { code: string }[]).map(({ code }) => code) as [string, ...string[]], ); } @@ -26,6 +26,12 @@ export const init: Init = async function init(this: PostgresAdapter) { }); this.payload.config.globals.forEach((global) => { - // create global model + buildTable({ + adapter: this, + buildRelationships: true, + fields: global.fields, + tableName: global.slug, + timestamps: false, + }); }); }; diff --git a/packages/db-postgres/src/transform/read/traverseFields.ts b/packages/db-postgres/src/transform/read/traverseFields.ts index c4f48a6c2a..594ae9c7f0 100644 --- a/packages/db-postgres/src/transform/read/traverseFields.ts +++ b/packages/db-postgres/src/transform/read/traverseFields.ts @@ -40,6 +40,9 @@ type TraverseFieldsArgs = { table: Record } +// TODO: clean up internal data structures: +// _order, _path, _parentID, _locales, etc. + // Traverse fields recursively, transforming data // for each field type into required Payload shape export const traverseFields = >({ diff --git a/packages/db-postgres/src/updateGlobal.ts b/packages/db-postgres/src/updateGlobal.ts new file mode 100644 index 0000000000..8ede5c293d --- /dev/null +++ b/packages/db-postgres/src/updateGlobal.ts @@ -0,0 +1,27 @@ +import { PayloadRequest } from 'payload/types'; +import toSnakeCase from 'to-snake-case'; +import { UpdateGlobal } from 'payload/dist/database/types'; +import { upsertRow } from './upsertRow'; +import { PostgresAdapter } from './types'; + +export const updateGlobal: UpdateGlobal = async function updateGlobal( + this: PostgresAdapter, + { data, slug, req = {} as PayloadRequest }, +) { + const globalConfig = this.payload.globals.config.find((config) => config.slug === slug); + const tableName = toSnakeCase(slug); + + const existingGlobal = await this.db.query[tableName].findFirst({}); + + const result = await upsertRow({ + adapter: this, + data, + fields: globalConfig.fields, + locale: req.locale, + operation: 'update', + id: existingGlobal.id, + tableName: toSnakeCase(slug), + }); + + return result; +}; diff --git a/test/postgres/config.ts b/test/postgres/config.ts index e82fffa0e0..d29d0dad8f 100644 --- a/test/postgres/config.ts +++ b/test/postgres/config.ts @@ -3,6 +3,7 @@ import { LocalizedArrays } from './collections/LocalizedArrays'; import { LocalizedBlocks } from './collections/LocalizedBlocks'; import { LocalizedGroups } from './collections/LocalizedGroups'; import { Posts } from './collections/Posts'; +import { MainMenu } from './globals/MainMenu'; const config = buildConfigWithDefaults({ collections: [ @@ -30,6 +31,9 @@ const config = buildConfigWithDefaults({ ], }, ], + globals: [ + MainMenu, + ], localization: { locales: ['en', 'es'], defaultLocale: 'en', diff --git a/test/postgres/globals/MainMenu.ts b/test/postgres/globals/MainMenu.ts new file mode 100644 index 0000000000..4f28e71de1 --- /dev/null +++ b/test/postgres/globals/MainMenu.ts @@ -0,0 +1,27 @@ +import { GlobalConfig } from '../../../src/globals/config/types'; + +export const MainMenu: GlobalConfig = { + slug: 'main-menu', + fields: [ + { + name: 'title', + type: 'text', + localized: true, + }, + { + name: 'nonLocalizedField', + type: 'text', + }, + { + name: 'array', + type: 'array', + fields: [ + { + name: 'localizedText', + type: 'text', + localized: true, + }, + ], + }, + ], +}; diff --git a/test/postgres/int.spec.ts b/test/postgres/int.spec.ts index f32f279fe6..745fb72589 100644 --- a/test/postgres/int.spec.ts +++ b/test/postgres/int.spec.ts @@ -493,4 +493,70 @@ describe('Postgres', () => { expect(retrievedGroup.group.es.number).toStrictEqual(456); }); }); + + describe('globals', () => { + let mainMenu; + it('creates global', async () => { + mainMenu = await payload.updateGlobal({ + slug: 'main-menu', + data: { + title: 'hello in english', + nonLocalizedField: 'hello', + array: [ + { + localizedText: 'row 1 en', + }, + { + localizedText: 'row 2 en', + }, + ], + }, + }); + + expect(mainMenu.title).toStrictEqual('hello in english'); + expect(mainMenu.nonLocalizedField).toStrictEqual('hello'); + expect(mainMenu.array[0].localizedText).toStrictEqual('row 1 en'); + expect(mainMenu.array[1].localizedText).toStrictEqual('row 2 en'); + }); + + it('adds locale to global', async () => { + const updated = await payload.updateGlobal({ + slug: 'main-menu', + locale: 'es', + data: { + title: 'hello in spanish', + array: [ + { + id: mainMenu.array[0].id, + localizedText: 'row 1 es', + }, + { + id: mainMenu.array[1].id, + localizedText: 'row 2 es', + }, + ], + }, + }); + + expect(updated.title).toStrictEqual('hello in spanish'); + expect(updated.nonLocalizedField).toStrictEqual('hello'); + expect(updated.array[0].localizedText).toStrictEqual('row 1 es'); + expect(updated.array[1].localizedText).toStrictEqual('row 2 es'); + }); + + it('retrieves global in all locales', async () => { + const retrieved = await payload.findGlobal({ + slug: 'main-menu', + locale: 'all', + }); + + expect(retrieved.title.en).toStrictEqual('hello in english'); + expect(retrieved.title.es).toStrictEqual('hello in spanish'); + expect(retrieved.nonLocalizedField).toStrictEqual('hello'); + expect(retrieved.array[0].localizedText.en).toStrictEqual('row 1 en'); + expect(retrieved.array[1].localizedText.en).toStrictEqual('row 2 en'); + expect(retrieved.array[0].localizedText.es).toStrictEqual('row 1 es'); + expect(retrieved.array[1].localizedText.es).toStrictEqual('row 2 es'); + }); + }); });