From d315ee1786fddbef728e2c6a26596e79cc916c48 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Thu, 4 Aug 2022 08:23:54 -0500 Subject: [PATCH] Ensure On-Demand revalidate does not consider preview cookie (#39313) --- packages/next/server/api-utils/node.ts | 7 +++ test/e2e/prerender.test.ts | 75 ++++++++++++++++++++++++++ test/e2e/prerender/pages/api/enable.js | 4 ++ test/e2e/prerender/pages/preview.js | 17 ++++++ 4 files changed, 103 insertions(+) create mode 100644 test/e2e/prerender/pages/api/enable.js create mode 100644 test/e2e/prerender/pages/preview.js diff --git a/packages/next/server/api-utils/node.ts b/packages/next/server/api-utils/node.ts index e86cca14e05b..85fa6f1f2a5a 100644 --- a/packages/next/server/api-utils/node.ts +++ b/packages/next/server/api-utils/node.ts @@ -2,6 +2,7 @@ import type { IncomingMessage, ServerResponse } from 'http' import type { NextApiRequest, NextApiResponse } from '../../shared/lib/utils' import type { PageConfig } from 'next/types' import { + checkIsManualRevalidate, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER, __ApiPreviewProps, } from '.' @@ -41,6 +42,12 @@ export function tryGetPreviewData( res: ServerResponse | BaseNextResponse, options: __ApiPreviewProps ): PreviewData { + // if an On-Demand revalidation is being done preview mode + // is disabled + if (options && checkIsManualRevalidate(req, options).isManualRevalidate) { + return false + } + // Read cached preview data if present if (SYMBOL_PREVIEW_DATA in req) { return (req as any)[SYMBOL_PREVIEW_DATA] as any diff --git a/test/e2e/prerender.test.ts b/test/e2e/prerender.test.ts index 35e4d9acdebd..7bf0023ef515 100644 --- a/test/e2e/prerender.test.ts +++ b/test/e2e/prerender.test.ts @@ -1,4 +1,5 @@ import fs from 'fs-extra' +import cookie from 'cookie' import cheerio from 'cheerio' import { join, sep } from 'path' import escapeRegex from 'escape-string-regexp' @@ -178,6 +179,11 @@ describe('Prerender', () => { initialRevalidateSeconds: 1, srcRoute: null, }, + '/preview': { + dataRoute: `/_next/data/${next.buildId}/preview.json`, + initialRevalidateSeconds: false, + srcRoute: null, + }, '/api-docs/first': { dataRoute: `/_next/data/${next.buildId}/api-docs/first.json`, initialRevalidateSeconds: false, @@ -1518,6 +1524,14 @@ describe('Prerender', () => { p: 'p', }, }, + { + dataRouteRegex: normalizeRegEx( + `^\\/_next\\/data\\/${escapeRegex( + next.buildId + )}\\/preview.json$` + ), + page: '/preview', + }, { dataRouteRegex: normalizeRegEx( `^\\/_next\\/data\\/${escapeRegex( @@ -1772,6 +1786,67 @@ describe('Prerender', () => { }) } + it('should revalidate manual revalidate with preview cookie', async () => { + const initialRes = await fetchViaHTTP(next.url, '/preview') + expect(initialRes.status).toBe(200) + + const initial$ = cheerio.load(await initialRes.text()) + const initialProps = JSON.parse(initial$('#props').text()) + + expect(initialProps).toEqual({ + preview: false, + previewData: null, + }) + + const previewRes = await fetchViaHTTP(next.url, '/api/enable') + let previewCookie = '' + + expect(previewRes.headers.get('set-cookie')).toMatch( + /(__prerender_bypass|__next_preview_data)/ + ) + + previewRes.headers + .get('set-cookie') + .split(',') + .forEach((c) => { + c = cookie.parse(c) + const isBypass = c.__prerender_bypass + + if (isBypass || c.__next_preview_data) { + if (previewCookie) previewCookie += '; ' + + previewCookie += `${ + isBypass ? '__prerender_bypass' : '__next_preview_data' + }=${c[isBypass ? '__prerender_bypass' : '__next_preview_data']}` + } + }) + + const apiRes = await fetchViaHTTP( + next.url, + '/api/manual-revalidate', + { pathname: '/preview' }, + { + headers: { + cookie: previewCookie, + }, + } + ) + + expect(apiRes.status).toBe(200) + expect(await apiRes.json()).toEqual({ revalidated: true }) + + const postRevalidateRes = await fetchViaHTTP(next.url, '/preview') + expect(initialRes.status).toBe(200) + + const postRevalidate$ = cheerio.load(await postRevalidateRes.text()) + const postRevalidateProps = JSON.parse(postRevalidate$('#props').text()) + + expect(postRevalidateProps).toEqual({ + preview: false, + previewData: null, + }) + }) + it('should handle revalidating HTML correctly', async () => { const route = '/blog/post-2/comment-2' const initialHtml = await renderViaHTTP(next.url, route) diff --git a/test/e2e/prerender/pages/api/enable.js b/test/e2e/prerender/pages/api/enable.js new file mode 100644 index 000000000000..0d64511b2cb8 --- /dev/null +++ b/test/e2e/prerender/pages/api/enable.js @@ -0,0 +1,4 @@ +export default function handler(req, res) { + res.setPreviewData({ hello: 'world' }) + res.json({ enabled: true }) +} diff --git a/test/e2e/prerender/pages/preview.js b/test/e2e/prerender/pages/preview.js new file mode 100644 index 000000000000..2cc47d91f9b2 --- /dev/null +++ b/test/e2e/prerender/pages/preview.js @@ -0,0 +1,17 @@ +export function getStaticProps({ preview, previewData }) { + return { + props: { + preview: preview || false, + previewData: previewData || null, + }, + } +} + +export default function Page(props) { + return ( + <> +

/preview

+

{JSON.stringify(props)}

+ + ) +}