diff --git a/packages/db-postgres/src/init.ts b/packages/db-postgres/src/init.ts index c4caba43b9..8f68389b48 100644 --- a/packages/db-postgres/src/init.ts +++ b/packages/db-postgres/src/init.ts @@ -24,6 +24,7 @@ export const init: Init = async function init(this: PostgresAdapter) { buildTable({ adapter: this, buildRelationships: true, + disableNotNull: !!collection?.versions?.drafts, disableUnique: false, fields: collection.fields, tableName, @@ -37,6 +38,7 @@ export const init: Init = async function init(this: PostgresAdapter) { buildTable({ adapter: this, buildRelationships: true, + disableNotNull: !!collection.versions?.drafts, disableUnique: true, fields: versionFields, tableName: versionsTableName, @@ -51,6 +53,7 @@ export const init: Init = async function init(this: PostgresAdapter) { buildTable({ adapter: this, buildRelationships: true, + disableNotNull: !!global?.versions?.drafts, disableUnique: false, fields: global.fields, tableName, @@ -64,6 +67,7 @@ export const init: Init = async function init(this: PostgresAdapter) { buildTable({ adapter: this, buildRelationships: true, + disableNotNull: !!global.versions?.drafts, disableUnique: true, fields: versionFields, tableName: versionsTableName, diff --git a/packages/db-postgres/src/schema/build.ts b/packages/db-postgres/src/schema/build.ts index 3194ab6e54..93179e5313 100644 --- a/packages/db-postgres/src/schema/build.ts +++ b/packages/db-postgres/src/schema/build.ts @@ -27,6 +27,7 @@ type Args = { baseColumns?: Record baseExtraConfig?: Record IndexBuilder | UniqueConstraintBuilder> buildRelationships?: boolean + disableNotNull: boolean disableUnique: boolean fields: Field[] rootRelationsToBuild?: Map @@ -46,6 +47,7 @@ export const buildTable = ({ baseColumns = {}, baseExtraConfig = {}, buildRelationships, + disableNotNull, disableUnique = false, fields, rootRelationsToBuild, @@ -102,6 +104,7 @@ export const buildTable = ({ adapter, buildRelationships, columns, + disableNotNull, disableUnique, fields, indexes, diff --git a/packages/db-postgres/src/schema/traverseFields.ts b/packages/db-postgres/src/schema/traverseFields.ts index 8d730ad492..f1e9c4d2f5 100644 --- a/packages/db-postgres/src/schema/traverseFields.ts +++ b/packages/db-postgres/src/schema/traverseFields.ts @@ -34,6 +34,7 @@ type Args = { buildRelationships: boolean columnPrefix?: string columns: Record + disableNotNull: boolean disableUnique?: boolean fieldPrefix?: string fields: (Field | TabAsField)[] @@ -62,6 +63,7 @@ export const traverseFields = ({ buildRelationships, columnPrefix, columns, + disableNotNull, disableUnique = false, fieldPrefix, fields, @@ -218,6 +220,7 @@ export const traverseFields = ({ adapter, baseColumns, baseExtraConfig, + disableNotNull, disableUnique, fields: [], tableName: selectTableName, @@ -274,6 +277,7 @@ export const traverseFields = ({ adapter, baseColumns, baseExtraConfig, + disableNotNull, disableUnique, fields: field.fields, rootRelationsToBuild, @@ -339,6 +343,7 @@ export const traverseFields = ({ adapter, baseColumns, baseExtraConfig, + disableNotNull, disableUnique, fields: block.fields, rootRelationsToBuild, @@ -399,6 +404,7 @@ export const traverseFields = ({ buildRelationships, columnPrefix, columns, + disableNotNull, disableUnique, fieldPrefix, fields: field.fields, @@ -432,6 +438,7 @@ export const traverseFields = ({ buildRelationships, columnPrefix: `${columnName}_`, columns, + disableNotNull, disableUnique, fieldPrefix: `${fieldName}_`, fields: field.fields, @@ -466,6 +473,7 @@ export const traverseFields = ({ buildRelationships, columnPrefix, columns, + disableNotNull, disableUnique, fieldPrefix, fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })), @@ -502,6 +510,7 @@ export const traverseFields = ({ buildRelationships, columnPrefix, columns, + disableNotNull, disableUnique, fieldPrefix, fields: field.fields, @@ -544,7 +553,13 @@ export const traverseFields = ({ const condition = field.admin && field.admin.condition - if (targetTable[fieldName] && 'required' in field && field.required && !condition) { + if ( + !disableNotNull && + targetTable[fieldName] && + 'required' in field && + field.required && + !condition + ) { targetTable[fieldName].notNull() } }) diff --git a/test/versions/int.spec.ts b/test/versions/int.spec.ts index b05e6ce2d8..d6a8ebe0ed 100644 --- a/test/versions/int.spec.ts +++ b/test/versions/int.spec.ts @@ -6,6 +6,7 @@ import { initPayloadTest } from '../helpers/configHelpers' import AutosavePosts from './collections/Autosave' import configPromise from './config' import AutosaveGlobal from './globals/Autosave' +import { autosaveSlug } from './shared' let collectionLocalPostID: string let collectionLocalVersionID @@ -56,8 +57,8 @@ describe('Versions', () => { // First: delete potential existing versions from previous tests if (collectionLocalPostID) { await payload.delete({ - collection, id: collectionLocalPostID, + collection, }) } @@ -65,8 +66,8 @@ describe('Versions', () => { const autosavePost = await payload.create({ collection, data: { - title: 'Here is an autosave post in EN', description: '345j23o4ifj34jf54g', + title: 'Here is an autosave post in EN', }, }) collectionLocalPostID = autosavePost.id @@ -91,10 +92,23 @@ describe('Versions', () => { describe('Collections - Local', () => { describe('Create', () => { + it('should allow creating a draft with missing required field data', async () => { + const draft = await payload.create({ + collection: autosaveSlug, + data: { + description: undefined, + title: 'i have a title', + }, + draft: true, + }) + + expect(draft.id).toBeDefined() + }) + it('should allow a new version to be created and updated', async () => { const updatedPost = await payload.findByID({ - collection, id: collectionLocalPostID, + collection, }) expect(updatedPost.title).toBe(updatedTitle) expect(updatedPost._status).toStrictEqual('draft') @@ -105,8 +119,8 @@ describe('Versions', () => { const autosavePost = await payload.create({ collection, data: { - title: 'unique unchanging title', description: 'description 1', + title: 'unique unchanging title', }, }) @@ -133,8 +147,8 @@ describe('Versions', () => { it('should allow a version to be retrieved by ID', async () => { const version = await payload.findVersionByID({ - collection, id: collectionLocalVersionID, + collection, }) expect(version.id).toStrictEqual(collectionLocalVersionID) @@ -145,20 +159,20 @@ describe('Versions', () => { const spanishTitle = 'Title in ES' await payload.update({ - collection, id: collectionLocalPostID, + collection, data: { title: englishTitle, }, }) const updatedPostES = await payload.update({ - collection, id: collectionLocalPostID, - locale: 'es', + collection, data: { title: spanishTitle, }, + locale: 'es', }) expect(updatedPostES.title).toBe(spanishTitle) @@ -166,8 +180,8 @@ describe('Versions', () => { const newEnglishTitle = 'New title in EN' await payload.update({ - collection, id: collectionLocalPostID, + collection, data: { title: newEnglishTitle, }, @@ -193,14 +207,14 @@ describe('Versions', () => { const somePost = await payload.create({ collection, data: { - title: 'first post', description: 'description 1', + title: 'first post', }, }) const updatedPost = await payload.update({ - collection, id: collectionLocalPostID, + collection, data: { title: 'This should be the latest version', }, @@ -216,8 +230,8 @@ describe('Versions', () => { const title2 = 'Another updated post title in EN' const updatedPost = await payload.update({ - collection, id: collectionLocalPostID, + collection, data: { title: title2, }, @@ -227,8 +241,8 @@ describe('Versions', () => { // Make sure it was updated correctly const draftFromUpdatedPost = await payload.findByID({ - collection, id: collectionLocalPostID, + collection, draft: true, }) expect(draftFromUpdatedPost.title).toBe(title2) @@ -239,15 +253,15 @@ describe('Versions', () => { // restore to latest version const restoredVersion = await payload.restoreVersion({ - collection, id: versions.docs[1].id, + collection, }) expect(restoredVersion.title).toBeDefined() const latestDraft = await payload.findByID({ - collection, id: collectionLocalPostID, + collection, draft: true, }) @@ -262,9 +276,9 @@ describe('Versions', () => { const originalPublishedPost = await payload.create({ collection, data: { - title: originalTitle, - description: 'kjnjyhbbdsfseankuhsjsfghb', _status: 'published', + description: 'kjnjyhbbdsfseankuhsjsfghb', + title: originalTitle, }, }) @@ -273,11 +287,11 @@ describe('Versions', () => { await payload.update({ id: originalPublishedPost.id, collection, - locale: 'en', data: { title: patchedTitle, }, draft: true, + locale: 'en', }) const spanishTitle = 'es title' @@ -286,23 +300,23 @@ describe('Versions', () => { await payload.update({ id: originalPublishedPost.id, collection, - locale: 'es', data: { title: spanishTitle, }, draft: true, + locale: 'es', }) const publishedPost = await payload.findByID({ - collection, id: originalPublishedPost.id, + collection, }) const draftPost = await payload.findByID({ - collection, - locale: 'all', id: originalPublishedPost.id, + collection, draft: true, + locale: 'all', }) expect(publishedPost.title).toBe(originalTitle) @@ -315,39 +329,39 @@ describe('Versions', () => { it('creates proper number of drafts', async () => { const originalDraft = await payload.create({ collection: 'draft-posts', - draft: true, data: { - title: 'A', + _status: 'draft', description: 'A', - _status: 'draft', + title: 'A', }, + draft: true, }) await payload.update({ - collection: 'draft-posts', id: originalDraft.id, - draft: true, + collection: 'draft-posts', data: { - title: 'B', + _status: 'draft', description: 'B', - _status: 'draft', + title: 'B', }, + draft: true, }) await payload.update({ - collection: 'draft-posts', id: originalDraft.id, - draft: true, + collection: 'draft-posts', data: { - title: 'C', - description: 'C', _status: 'draft', + description: 'C', + title: 'C', }, + draft: true, }) const mostRecentDraft = await payload.findByID({ - collection: 'draft-posts', id: originalDraft.id, + collection: 'draft-posts', draft: true, }) @@ -373,52 +387,52 @@ describe('Versions', () => { const doc1 = await payload.create({ collection: 'version-posts', data: { - title: 'A', description: 'A', + title: 'A', }, }) await payload.update({ - collection: 'version-posts', id: doc1.id, + collection: 'version-posts', data: { - title: 'B', description: 'B', + title: 'B', }, }) await payload.update({ - collection: 'version-posts', id: doc1.id, + collection: 'version-posts', data: { - title: 'C', description: 'C', + title: 'C', }, }) const doc2 = await payload.create({ collection: 'version-posts', data: { - title: 'D', description: 'D', + title: 'D', }, }) await payload.update({ - collection: 'version-posts', id: doc2.id, + collection: 'version-posts', data: { - title: 'E', description: 'E', + title: 'E', }, }) await payload.update({ - collection: 'version-posts', id: doc2.id, + collection: 'version-posts', data: { - title: 'F', description: 'F', + title: 'F', }, }) @@ -473,31 +487,31 @@ describe('Versions', () => { firstDraft = await payload.create({ collection: 'draft-posts', data: { - title: originalTitle, description: 'my description', radio: 'test', + title: originalTitle, }, }) // This will be created in the `_draft-posts_versions` collection await payload.update({ - collection: 'draft-posts', id: firstDraft.id, - draft: true, + collection: 'draft-posts', data: { title: updatedTitle1, }, + draft: true, }) // This will be created in the `_draft-posts_versions` collection // and will be the newest draft, able to be queried on await payload.update({ - collection: 'draft-posts', id: firstDraft.id, - draft: true, + collection: 'draft-posts', data: { title: updatedTitle2, }, + draft: true, }) }) @@ -583,8 +597,8 @@ describe('Versions', () => { // First: delete potential existing versions from previous tests if (collectionGraphQLPostID) { await payload.delete({ - collection, id: collectionGraphQLPostID, + collection, }) } @@ -780,17 +794,17 @@ describe('Versions', () => { beforeEach(async () => { const title2 = 'Here is an updated global title in EN' await payload.updateGlobal({ - slug: globalSlug, data: { title: 'Test Global', }, + slug: globalSlug, }) const updatedGlobal = await payload.updateGlobal({ - slug: globalSlug, data: { title: title2, }, + slug: globalSlug, }) const versions = await payload.findGlobalVersions({ @@ -814,8 +828,8 @@ describe('Versions', () => { describe('Read', () => { it('should allow a version to be retrieved by ID', async () => { const version = await payload.findGlobalVersionByID({ - slug: globalSlug, id: globalLocalVersionID, + slug: globalSlug, }) expect(version.id).toStrictEqual(globalLocalVersionID) @@ -828,18 +842,18 @@ describe('Versions', () => { const spanishTitle = 'Title in ES' await payload.updateGlobal({ - slug: globalSlug, data: { title: englishTitle, }, + slug: globalSlug, }) const updatedGlobalES = await payload.updateGlobal({ - slug: globalSlug, - locale: 'es', data: { title: spanishTitle, }, + locale: 'es', + slug: globalSlug, }) expect(updatedGlobalES.title).toBe(spanishTitle) @@ -847,15 +861,15 @@ describe('Versions', () => { const newEnglishTitle = 'New title in EN' await payload.updateGlobal({ - slug: globalSlug, data: { title: newEnglishTitle, }, + slug: globalSlug, }) const versions = await payload.findGlobalVersions({ - slug: globalSlug, locale: 'all', + slug: globalSlug, }) expect(versions.docs[0].version.title.en).toStrictEqual(newEnglishTitle) @@ -868,18 +882,18 @@ describe('Versions', () => { const title2 = 'Another updated title in EN' const updatedGlobal = await payload.updateGlobal({ - slug: globalSlug, data: { title: title2, }, + slug: globalSlug, }) expect(updatedGlobal.title).toBe(title2) // Make sure it was updated correctly const foundUpdatedGlobal = await payload.findGlobal({ - slug: globalSlug, draft: true, + slug: globalSlug, }) expect(foundUpdatedGlobal.title).toBe(title2) @@ -890,15 +904,15 @@ describe('Versions', () => { globalLocalVersionID = versions.docs[1].id const restore = await payload.restoreGlobalVersion({ - slug: globalSlug, id: globalLocalVersionID, + slug: globalSlug, }) expect(restore.title).toBeDefined() const restoredGlobal = await payload.findGlobal({ - slug: globalSlug, draft: true, + slug: globalSlug, }) expect(restoredGlobal.title).toBe(restore.title) @@ -910,43 +924,43 @@ describe('Versions', () => { const originalTitle = 'Here is a published global' await payload.updateGlobal({ - slug: globalSlug, data: { - title: originalTitle, - description: 'kjnjyhbbdsfseankuhsjsfghb', _status: 'published', + description: 'kjnjyhbbdsfseankuhsjsfghb', + title: originalTitle, }, + slug: globalSlug, }) const publishedGlobal = await payload.findGlobal({ - slug: globalSlug, draft: true, + slug: globalSlug, }) const updatedTitle2 = 'Here is a draft global with a patched title' await payload.updateGlobal({ - slug: globalSlug, - locale: 'en', data: { title: updatedTitle2, }, draft: true, + locale: 'en', + slug: globalSlug, }) await payload.updateGlobal({ - slug: globalSlug, - locale: 'es', data: { title: updatedTitle2, }, draft: true, + locale: 'es', + slug: globalSlug, }) const updatedGlobal = await payload.findGlobal({ - slug: globalSlug, - locale: 'all', draft: true, + locale: 'all', + slug: globalSlug, }) expect(publishedGlobal.title).toBe(originalTitle) @@ -958,22 +972,22 @@ describe('Versions', () => { const originalTitle = 'Here is a draft' await payload.updateGlobal({ - slug: globalSlug, data: { - title: originalTitle, _status: 'draft', + title: originalTitle, }, draft: true, + slug: globalSlug, }) const updatedTitle2 = 'Now try to publish' const result = await payload.updateGlobal({ - slug: globalSlug, data: { - title: updatedTitle2, _status: 'published', + title: updatedTitle2, }, + slug: globalSlug, }) expect(result.title).toBe(updatedTitle2)