From 3be1fa02a265da917e3391abdee26fdbd2a0a16a Mon Sep 17 00:00:00 2001 From: Jessica Chowdhury Date: Wed, 8 Jan 2025 18:27:06 +0000 Subject: [PATCH 1/4] fix(plugin-nested-docs): update draft and published child docs on resave --- .../src/hooks/resaveChildren.ts | 116 ++++++++++-------- 1 file changed, 64 insertions(+), 52 deletions(-) diff --git a/packages/plugin-nested-docs/src/hooks/resaveChildren.ts b/packages/plugin-nested-docs/src/hooks/resaveChildren.ts index 27321a91b80..31120e9a8a1 100644 --- a/packages/plugin-nested-docs/src/hooks/resaveChildren.ts +++ b/packages/plugin-nested-docs/src/hooks/resaveChildren.ts @@ -22,8 +22,14 @@ type ResaveArgs = { const resave = async ({ collection, doc, draft, pluginConfig, req }: ResaveArgs) => { const parentSlug = pluginConfig?.parentFieldSlug || 'parent' - const parentDocIsPublished = doc._status === 'published' - const children = await req.payload.find({ + const breadcrumbSlug = pluginConfig.breadcrumbsFieldSlug || 'breadcrumbs' + + if (draft) { + // If the parent is a draft, don't resave children + return null + } + + const draftChildren = await req.payload.find({ collection: collection.slug, depth: 0, draft: true, @@ -36,51 +42,67 @@ const resave = async ({ collection, doc, draft, pluginConfig, req }: ResaveArgs) }, }) - const breadcrumbSlug = pluginConfig.breadcrumbsFieldSlug || 'breadcrumbs' - - try { - await children.docs.reduce(async (priorSave, child) => { - await priorSave + const publishedChildren = await req.payload.find({ + collection: collection.slug, + depth: 0, + draft: false, + locale: req.locale, + req, + where: { + [parentSlug]: { + equals: doc.id, + }, + }, + }) - const childIsPublished = - typeof collection.versions === 'object' && - collection.versions.drafts && - child._status === 'published' + const childrenById = [...draftChildren.docs, ...publishedChildren.docs].reduce((acc, child) => { + acc[child.id] = acc[child.id] || [] + acc[child.id].push(child) + return acc + }, {}) - if (!parentDocIsPublished && childIsPublished) { - return + const sortedChildren = Object.values(childrenById).flatMap((group: JsonObject[]) => { + return group.sort((a, b) => { + if (a.updatedAt !== b.updatedAt) { + return a.updatedAt > b.updatedAt ? -1 : 1 } + return a._status === 'published' ? -1 : 1 + }) + }) - await req.payload.update({ - id: child.id, - collection: collection.slug, - data: { - ...child, - [breadcrumbSlug]: await populateBreadcrumbs(req, pluginConfig, collection, child), - }, - depth: 0, - draft: !childIsPublished, - locale: req.locale, - req, - }) - }, Promise.resolve()) - } catch (err: unknown) { - req.payload.logger.error( - `Nested Docs plugin has had an error while re-saving a child document${ - draft ? ' as draft' : ' as published' - }.`, - ) - req.payload.logger.error(err) + if (sortedChildren) { + try { + for (const child of sortedChildren) { + const isDraft = child._status !== 'published' - // Use type assertion until we can use instanceof reliably with our Error types - if ( - (err as ValidationError)?.name === 'ValidationError' && - (err as ValidationError)?.data?.errors?.length - ) { - throw new APIError( - 'Could not publish or save changes: One or more children are invalid.', - 400, + await req.payload.update({ + id: child.id, + collection: collection.slug, + data: { + ...child, + [breadcrumbSlug]: await populateBreadcrumbs(req, pluginConfig, collection, child), + }, + depth: 0, + draft: isDraft, + locale: req.locale, + req, + }) + } + } catch (err: unknown) { + req.payload.logger.error( + `Nested Docs plugin encountered an error while re-saving a child document.`, ) + req.payload.logger.error(err) + + if ( + (err as ValidationError)?.name === 'ValidationError' && + (err as ValidationError)?.data?.errors?.length + ) { + throw new APIError( + 'Could not publish or save changes: One or more children are invalid.', + 400, + ) + } } } } @@ -91,20 +113,10 @@ export const resaveChildren = await resave({ collection, doc, - draft: true, + draft: doc._status === 'published' ? false : true, pluginConfig, req, }) - if (doc._status === 'published') { - await resave({ - collection, - doc, - draft: false, - pluginConfig, - req, - }) - } - return undefined } From c85dd10dcc8603a3e2847b30c895b7143a9fbc79 Mon Sep 17 00:00:00 2001 From: Jessica Chowdhury Date: Fri, 24 Jan 2025 14:21:36 +0000 Subject: [PATCH 2/4] chore: handle no draft results and correctly order resave --- .../src/hooks/resaveChildren.ts | 140 +++++++++--------- 1 file changed, 71 insertions(+), 69 deletions(-) diff --git a/packages/plugin-nested-docs/src/hooks/resaveChildren.ts b/packages/plugin-nested-docs/src/hooks/resaveChildren.ts index ed119506776..783cde5bab4 100644 --- a/packages/plugin-nested-docs/src/hooks/resaveChildren.ts +++ b/packages/plugin-nested-docs/src/hooks/resaveChildren.ts @@ -26,84 +26,86 @@ const resave = async ({ collection, doc, draft, pluginConfig, req }: ResaveArgs) if (draft) { // If the parent is a draft, don't resave children - return null - } - - const draftChildren = await req.payload.find({ - collection: collection.slug, - depth: 0, - draft: true, - limit: 0, - locale: req.locale, - req, - where: { - [parentSlug]: { - equals: doc.id, + return + } else { + const initialDraftChildren = await req.payload.find({ + collection: collection.slug, + depth: 0, + draft: true, + limit: 0, + locale: req.locale, + req, + where: { + [parentSlug]: { + equals: doc.id, + }, }, - }, - }) + }) + + const draftChildren = initialDraftChildren.docs.filter((child) => child._status === 'draft') - const publishedChildren = await req.payload.find({ - collection: collection.slug, - depth: 0, - draft: false, - limit: 0, - locale: req.locale, - req, - where: { - [parentSlug]: { - equals: doc.id, + const publishedChildren = await req.payload.find({ + collection: collection.slug, + depth: 0, + draft: false, + limit: 0, + locale: req.locale, + req, + where: { + [parentSlug]: { + equals: doc.id, + }, }, - }, - }) + }) - const childrenById = [...draftChildren.docs, ...publishedChildren.docs].reduce((acc, child) => { - acc[child.id] = acc[child.id] || [] - acc[child.id].push(child) - return acc - }, {}) + const childrenById = [...draftChildren, ...publishedChildren.docs].reduce((acc, child) => { + acc[child.id] = acc[child.id] || [] + acc[child.id].push(child) + return acc + }, {}) - const sortedChildren = Object.values(childrenById).flatMap((group: JsonObject[]) => { - return group.sort((a, b) => { - if (a.updatedAt !== b.updatedAt) { - return a.updatedAt > b.updatedAt ? -1 : 1 - } - return a._status === 'published' ? -1 : 1 + const sortedChildren = Object.values(childrenById).flatMap((group: JsonObject[]) => { + return group.sort((a, b) => { + if (a.updatedAt !== b.updatedAt) { + return a.updatedAt > b.updatedAt ? 1 : -1 + } + return a._status === 'published' ? 1 : -1 + }) }) - }) - if (sortedChildren) { - try { - for (const child of sortedChildren) { - const isDraft = child._status !== 'published' + if (sortedChildren) { + try { + for (const child of sortedChildren) { + const isDraft = child._status !== 'published' - await req.payload.update({ - id: child.id, - collection: collection.slug, - data: { - ...child, - [breadcrumbSlug]: await populateBreadcrumbs(req, pluginConfig, collection, child), - }, - depth: 0, - draft: isDraft, - locale: req.locale, - req, - }) - } - } catch (err: unknown) { - req.payload.logger.error( - `Nested Docs plugin encountered an error while re-saving a child document.`, - ) - req.payload.logger.error(err) - - if ( - (err as ValidationError)?.name === 'ValidationError' && - (err as ValidationError)?.data?.errors?.length - ) { - throw new APIError( - 'Could not publish or save changes: One or more children are invalid.', - 400, + await req.payload.update({ + id: child.id, + collection: collection.slug, + data: { + ...child, + [breadcrumbSlug]: await populateBreadcrumbs(req, pluginConfig, collection, child), + }, + depth: 0, + draft: isDraft, + locale: req.locale, + req, + }) + } + } catch (err: unknown) { + req.payload.logger.error( + `Nested Docs plugin encountered an error while re-saving a child document.`, ) + req.payload.logger.error(err) + + if ( + (err as ValidationError)?.name === 'ValidationError' && + (err as ValidationError)?.data?.errors?.length + ) { + throw new APIError( + 'Could not publish or save changes: One or more children are invalid.', + 400, + ) + } } } } From b8672c779a401fbfc77fe2eeedb4ebb9c3851785 Mon Sep 17 00:00:00 2001 From: Jessica Chowdhury Date: Fri, 24 Jan 2025 16:03:01 +0000 Subject: [PATCH 3/4] chore: update failing plugin-nested-doc test --- test/plugin-nested-docs/int.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test/plugin-nested-docs/int.spec.ts b/test/plugin-nested-docs/int.spec.ts index defadf9534c..d3e0713eaf0 100644 --- a/test/plugin-nested-docs/int.spec.ts +++ b/test/plugin-nested-docs/int.spec.ts @@ -90,7 +90,6 @@ describe('@payloadcms/plugin-nested-docs', () => { const { docs } = await payload.find({ collection: 'pages', limit: 0, - draft: true, where: { parent: { equals: parentDoc.id, From d6da4566f3c1b800f9d1a188719ff4b54508c8e2 Mon Sep 17 00:00:00 2001 From: Jessica Chowdhury Date: Mon, 27 Jan 2025 13:03:06 +0000 Subject: [PATCH 4/4] chore: fix failing plugin-nested-doc test --- test/plugin-nested-docs/int.spec.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/plugin-nested-docs/int.spec.ts b/test/plugin-nested-docs/int.spec.ts index d3e0713eaf0..6c7359cd9b0 100644 --- a/test/plugin-nested-docs/int.spec.ts +++ b/test/plugin-nested-docs/int.spec.ts @@ -72,6 +72,7 @@ describe('@payloadcms/plugin-nested-docs', () => { title: `Child ${i + 1}`, slug: `child-${i + 1}`, parent: parentDoc.id, + _status: 'published', }, }) } @@ -83,6 +84,7 @@ describe('@payloadcms/plugin-nested-docs', () => { data: { title: '11 children updated', slug: '11-children-updated', + _status: 'published', }, })