From 364261a29fcdc958e5c6121482a5ff9a7a70680b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=ADcholas=20Andr=C3=A9?= Date: Thu, 9 Nov 2023 10:26:08 -0300 Subject: [PATCH 1/2] fix: previews for custom post types --- .changeset/cuddly-windows-report.md | 5 + .../src/handlers/__tests__/previewHandler.ts | 138 +++++++++++++++++- packages/next/src/handlers/previewHandler.ts | 21 ++- 3 files changed, 152 insertions(+), 12 deletions(-) create mode 100644 .changeset/cuddly-windows-report.md diff --git a/.changeset/cuddly-windows-report.md b/.changeset/cuddly-windows-report.md new file mode 100644 index 000000000..4635e0e78 --- /dev/null +++ b/.changeset/cuddly-windows-report.md @@ -0,0 +1,5 @@ +--- +"@headstartwp/next": patch +--- + +Fix previews for custom post types diff --git a/packages/next/src/handlers/__tests__/previewHandler.ts b/packages/next/src/handlers/__tests__/previewHandler.ts index 090b6c5a4..d6ad3cd93 100644 --- a/packages/next/src/handlers/__tests__/previewHandler.ts +++ b/packages/next/src/handlers/__tests__/previewHandler.ts @@ -1,5 +1,6 @@ import { createMocks } from 'node-mocks-http'; import { DRAFT_POST_ID, VALID_AUTH_TOKEN } from '@headstartwp/core/test'; +import { setHeadstartWPConfig } from '@headstartwp/core'; import { previewHandler } from '../previewHandler'; describe('previewHandler', () => { @@ -50,6 +51,9 @@ describe('previewHandler', () => { expect(res.setPreviewData).toHaveBeenCalled(); expect(res._getStatusCode()).toBe(302); + expect(res._getRedirectUrl()).toBe( + '/modi-qui-dignissimos-sed-assumenda-sint-iusto-preview=true', + ); }); it('sets preview cookie path', async () => { @@ -71,6 +75,83 @@ describe('previewHandler', () => { { maxAge: 300, path: '/modi-qui-dignissimos-sed-assumenda-sint-iusto-preview=true' }, ); expect(res._getStatusCode()).toBe(302); + expect(res._getRedirectUrl()).toBe( + '/modi-qui-dignissimos-sed-assumenda-sint-iusto-preview=true', + ); + }); + + it('preview works for custom post types', async () => { + setHeadstartWPConfig({ + customPostTypes: [ + { + slug: 'book', + // reuse existing posts endpoint + endpoint: '/wp-json/wp/v2/posts', + // these should match your file-system routing + single: '/book', + archive: '/books', + }, + ], + }); + + const { req, res } = createMocks({ + method: 'GET', + query: { + post_id: DRAFT_POST_ID, + token: VALID_AUTH_TOKEN, + post_type: 'book', + }, + }); + + res.setPreviewData = jest.fn(); + await previewHandler(req, res); + + expect(res.setPreviewData).toHaveBeenCalledWith( + { + authToken: 'this is a valid auth', + id: 57, + postType: 'book', + revision: false, + }, + { + maxAge: 300, + path: '/book/modi-qui-dignissimos-sed-assumenda-sint-iusto-preview=true', + }, + ); + expect(res._getStatusCode()).toBe(302); + expect(res._getRedirectUrl()).toBe( + '/book/modi-qui-dignissimos-sed-assumenda-sint-iusto-preview=true', + ); + + const { req: reqWithLocale, res: resWithLocale } = createMocks({ + method: 'GET', + query: { + post_id: DRAFT_POST_ID, + token: VALID_AUTH_TOKEN, + post_type: 'book', + locale: 'es', + }, + }); + + resWithLocale.setPreviewData = jest.fn(); + await previewHandler(reqWithLocale, resWithLocale); + + expect(resWithLocale.setPreviewData).toHaveBeenCalledWith( + { + authToken: 'this is a valid auth', + id: 57, + postType: 'book', + revision: false, + }, + { + maxAge: 300, + path: '/es/book/modi-qui-dignissimos-sed-assumenda-sint-iusto-preview=true', + }, + ); + expect(resWithLocale._getStatusCode()).toBe(302); + expect(resWithLocale._getRedirectUrl()).toBe( + '/es/book/modi-qui-dignissimos-sed-assumenda-sint-iusto-preview=true', + ); }); it('sets preview cookie path with locale', async () => { @@ -100,6 +181,9 @@ describe('previewHandler', () => { }, ); expect(res._getStatusCode()).toBe(302); + expect(res._getRedirectUrl()).toBe( + '/es/modi-qui-dignissimos-sed-assumenda-sint-iusto-preview=true', + ); }); it('set preview cookie path to all paths if onRedirect is passed without getRedirectPath', async () => { @@ -111,7 +195,7 @@ describe('previewHandler', () => { res.setPreviewData = jest.fn(); await previewHandler(req, res, { onRedirect(req, res) { - return res.redirect('/'); + return res.redirect('/custom-redirect'); }, }); @@ -125,6 +209,7 @@ describe('previewHandler', () => { { maxAge: 300, path: '/' }, ); expect(res._getStatusCode()).toBe(302); + expect(res._getRedirectUrl()).toBe('/custom-redirect'); }); it('set preview cookie path redirectPath if getRedirectPath is passed', async () => { @@ -138,9 +223,6 @@ describe('previewHandler', () => { getRedirectPath() { return '/custom-redirect-path/'; }, - onRedirect(req, res) { - return res.redirect('/'); - }, }); expect(res.setPreviewData).toHaveBeenCalledWith( @@ -153,5 +235,53 @@ describe('previewHandler', () => { { maxAge: 300, path: '/custom-redirect-path-preview=true' }, ); expect(res._getStatusCode()).toBe(302); + expect(res._getRedirectUrl()).toBe('/custom-redirect-path-preview=true'); + }); + + it('correctly takes into account `options`', async () => { + const { req, res } = createMocks({ + method: 'GET', + query: { post_id: DRAFT_POST_ID, token: VALID_AUTH_TOKEN, post_type: 'post' }, + }); + + res.setPreviewData = jest.fn(); + await previewHandler(req, res, { + preparePreviewData(req, res, post, previewData) { + return { ...previewData, myCustomData: true }; + }, + getRedirectPath(defaultRedirectPath) { + // if user already added the suffix we need to make sure it is not added again + return `${defaultRedirectPath}-preview=true`; + }, + }); + + expect(res.setPreviewData).toHaveBeenCalledWith( + { + authToken: 'this is a valid auth', + id: 57, + postType: 'post', + revision: false, + myCustomData: true, + }, + { maxAge: 300, path: '/modi-qui-dignissimos-sed-assumenda-sint-iusto-preview=true' }, + ); + expect(res._getRedirectUrl()).toBe( + '/modi-qui-dignissimos-sed-assumenda-sint-iusto-preview=true', + ); + }); + + it('fails if post type is not defined', async () => { + const { req, res } = createMocks({ + method: 'GET', + query: { post_id: DRAFT_POST_ID, token: VALID_AUTH_TOKEN, post_type: 'recipe' }, + }); + + res.setPreviewData = jest.fn(); + await previewHandler(req, res); + + expect(res._getStatusCode()).toBe(401); + expect(res._getData()).toBe( + 'Cannot preview an unknown post type, did you forget to add it to headless.config.js?', + ); }); }); diff --git a/packages/next/src/handlers/previewHandler.ts b/packages/next/src/handlers/previewHandler.ts index 0055717b8..40fbdd57b 100644 --- a/packages/next/src/handlers/previewHandler.ts +++ b/packages/next/src/handlers/previewHandler.ts @@ -142,6 +142,16 @@ export async function previewHandler( const revision = is_revision === '1'; try { + const postTypeDef = getCustomPostType(post_type as string, sourceUrl); + + if (!postTypeDef) { + return res + .status(401) + .end( + 'Cannot preview an unknown post type, did you forget to add it to headless.config.js?', + ); + } + const { data } = await fetchHookData( usePost.fetcher(sourceUrl), { @@ -179,12 +189,6 @@ export async function previewHandler( previewData = options.preparePreviewData(req, res, result, previewData); } - const postTypeDef = getCustomPostType(post_type as string, sourceUrl); - - if (!postTypeDef) { - return res.end('Cannot preview an unknown post type'); - } - /** * Builds the default redirect path * @@ -192,7 +196,8 @@ export async function previewHandler( */ const getDefaultRedirectPath = () => { const singleRoute = postTypeDef.single || '/'; - const prefixRoute = singleRoute === '/' ? '' : singleRoute; + // remove leading slashes + const prefixRoute = singleRoute.replace(/^\/+/, ''); const slugOrId = revision ? post_id : slug || post_id; const path = [locale, prefixRoute, slugOrId].filter((n) => n).join('/'); return `/${path}`; @@ -234,5 +239,5 @@ export async function previewHandler( } } - return res.status(401).end('Unable to set preview mode'); + return res.status(401).end('There was an error setting up preview'); } From bf284c4e855dbf20ce8712e7774112b9698e464c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=ADcholas=20Andr=C3=A9?= Date: Thu, 9 Nov 2023 10:57:44 -0300 Subject: [PATCH 2/2] fix: next.js warning when returning something from api handler --- packages/next/src/handlers/previewHandler.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/next/src/handlers/previewHandler.ts b/packages/next/src/handlers/previewHandler.ts index 40fbdd57b..c9a02cfe7 100644 --- a/packages/next/src/handlers/previewHandler.ts +++ b/packages/next/src/handlers/previewHandler.ts @@ -1,3 +1,4 @@ +/* eslint-disable consistent-return */ import { CustomPostType, getSiteByHost, PostEntity } from '@headstartwp/core'; import { getCustomPostType, getHeadlessConfig } from '@headstartwp/core/utils'; import type { NextApiRequest, NextApiResponse } from 'next'; @@ -44,7 +45,7 @@ export type PreviewHandlerOptions = { previewData: PreviewData, defaultRedirect?: PreviewHandlerOptions['onRedirect'], redirectpath?: string, - ) => NextApiResponse; + ) => void; /** * If passed will override the default redirect path @@ -120,7 +121,7 @@ export async function previewHandler( req: NextApiRequest, res: NextApiResponse, options: PreviewHandlerOptions = {}, -) { +): Promise { const { post_id, post_type, is_revision, token, locale } = req.query; if (req.method !== 'GET') { @@ -145,11 +146,10 @@ export async function previewHandler( const postTypeDef = getCustomPostType(post_type as string, sourceUrl); if (!postTypeDef) { - return res - .status(401) - .end( - 'Cannot preview an unknown post type, did you forget to add it to headless.config.js?', - ); + res.status(401).end( + 'Cannot preview an unknown post type, did you forget to add it to headless.config.js?', + ); + return; } const { data } = await fetchHookData( @@ -224,7 +224,7 @@ export async function previewHandler( }); const defaultRedirect: PreviewHandlerOptions['onRedirect'] = (req, res) => { - return res.redirect(redirectPath); + res.redirect(redirectPath); }; if (options?.onRedirect) { @@ -235,9 +235,9 @@ export async function previewHandler( } } catch (e) { if (e instanceof Error) { - return res.status(401).end(e.message); + res.status(401).end(e.message); } } - return res.status(401).end('There was an error setting up preview'); + res.status(401).end('There was an error setting up preview'); }