Skip to content

Commit

Permalink
fix: previews for custom post types (#634)
Browse files Browse the repository at this point in the history
  • Loading branch information
nicholasio authored Nov 9, 2023
1 parent 36e20be commit 556aba2
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 16 deletions.
5 changes: 5 additions & 0 deletions .changeset/cuddly-windows-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@headstartwp/next": patch
---

Fix previews for custom post types
138 changes: 134 additions & 4 deletions packages/next/src/handlers/__tests__/previewHandler.ts
Original file line number Diff line number Diff line change
@@ -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', () => {
Expand Down Expand Up @@ -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 () => {
Expand All @@ -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 () => {
Expand Down Expand Up @@ -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 () => {
Expand All @@ -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');
},
});

Expand All @@ -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 () => {
Expand All @@ -138,9 +223,6 @@ describe('previewHandler', () => {
getRedirectPath() {
return '/custom-redirect-path/';
},
onRedirect(req, res) {
return res.redirect('/');
},
});

expect(res.setPreviewData).toHaveBeenCalledWith(
Expand All @@ -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?',
);
});
});
29 changes: 17 additions & 12 deletions packages/next/src/handlers/previewHandler.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -44,7 +45,7 @@ export type PreviewHandlerOptions = {
previewData: PreviewData,
defaultRedirect?: PreviewHandlerOptions['onRedirect'],
redirectpath?: string,
) => NextApiResponse;
) => void;

/**
* If passed will override the default redirect path
Expand Down Expand Up @@ -120,7 +121,7 @@ export async function previewHandler(
req: NextApiRequest,
res: NextApiResponse,
options: PreviewHandlerOptions = {},
) {
): Promise<void> {
const { post_id, post_type, is_revision, token, locale } = req.query;

if (req.method !== 'GET') {
Expand All @@ -142,6 +143,15 @@ export async function previewHandler(
const revision = is_revision === '1';

try {
const postTypeDef = getCustomPostType(post_type as string, sourceUrl);

if (!postTypeDef) {
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(
usePost.fetcher(sourceUrl),
{
Expand Down Expand Up @@ -179,20 +189,15 @@ 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
*
* @returns the default redirec tpath
*/
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}`;
Expand All @@ -219,7 +224,7 @@ export async function previewHandler(
});

const defaultRedirect: PreviewHandlerOptions['onRedirect'] = (req, res) => {
return res.redirect(redirectPath);
res.redirect(redirectPath);
};

if (options?.onRedirect) {
Expand All @@ -230,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('Unable to set preview mode');
res.status(401).end('There was an error setting up preview');
}

1 comment on commit 556aba2

@vercel
Copy link

@vercel vercel bot commented on 556aba2 Nov 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.