From 3f571b198fa3622c828adaea1f817f68ee1d707b Mon Sep 17 00:00:00 2001 From: Lucas Rosa Date: Mon, 4 Jul 2022 20:36:29 -0300 Subject: [PATCH 1/4] Allow custom path for preview mode cookies --- packages/next/server/api-utils/node.ts | 9 +++++++-- packages/next/shared/lib/utils.ts | 5 +++++ .../prerender-preview/pages/api/preview.js | 14 ++++++------- .../prerender-preview/test/index.test.js | 20 +++++++++++++++++++ 4 files changed, 38 insertions(+), 10 deletions(-) diff --git a/packages/next/server/api-utils/node.ts b/packages/next/server/api-utils/node.ts index 6ce945c09da7..59d9539a941b 100644 --- a/packages/next/server/api-utils/node.ts +++ b/packages/next/server/api-utils/node.ts @@ -454,6 +454,7 @@ function setPreviewData( data: object | string, // TODO: strict runtime type checking options: { maxAge?: number + path?: string } & __ApiPreviewProps ): NextApiResponse { if (isNotValidData(options.previewModeId)) { @@ -503,7 +504,9 @@ function setPreviewData( httpOnly: true, sameSite: process.env.NODE_ENV !== 'development' ? 'none' : 'lax', secure: process.env.NODE_ENV !== 'development', - path: '/', + ...(options.path !== undefined + ? ({ path: options.path } as CookieSerializeOptions) + : undefined), ...(options.maxAge !== undefined ? ({ maxAge: options.maxAge } as CookieSerializeOptions) : undefined), @@ -512,7 +515,9 @@ function setPreviewData( httpOnly: true, sameSite: process.env.NODE_ENV !== 'development' ? 'none' : 'lax', secure: process.env.NODE_ENV !== 'development', - path: '/', + ...(options.path !== undefined + ? ({ path: options.path } as CookieSerializeOptions) + : undefined), ...(options.maxAge !== undefined ? ({ maxAge: options.maxAge } as CookieSerializeOptions) : undefined), diff --git a/packages/next/shared/lib/utils.ts b/packages/next/shared/lib/utils.ts index 49c9d5460f91..438b458f4a6d 100644 --- a/packages/next/shared/lib/utils.ts +++ b/packages/next/shared/lib/utils.ts @@ -245,6 +245,11 @@ export type NextApiResponse = ServerResponse & { setPreviewData: ( data: object | string, options?: { + /** + * Specifies the path for the preview session to work under. By default, + * the path is considered the "default path", i.e., any pages under "/". + */ + path?: string /** * Specifies the number (in seconds) for the preview session to last for. * The given number will be converted to an integer by rounding down. diff --git a/test/integration/prerender-preview/pages/api/preview.js b/test/integration/prerender-preview/pages/api/preview.js index 5476b7fd4c46..9303bcd24662 100644 --- a/test/integration/prerender-preview/pages/api/preview.js +++ b/test/integration/prerender-preview/pages/api/preview.js @@ -6,14 +6,12 @@ export default (req, res) => { return res.status(500).end('too big') } } else { - res.setPreviewData( - req.query, - req.query.cookieMaxAge - ? { - maxAge: req.query.cookieMaxAge, - } - : undefined - ) + res.setPreviewData(req.query, { + ...(req.query.cookieMaxAge + ? { maxAge: req.query.cookieMaxAge } + : undefined), + ...(req.query.cookiePath ? { path: req.query.cookiePath } : undefined), + }) } res.status(200).end() diff --git a/test/integration/prerender-preview/test/index.test.js b/test/integration/prerender-preview/test/index.test.js index df2fb44573e4..47fe85145c51 100644 --- a/test/integration/prerender-preview/test/index.test.js +++ b/test/integration/prerender-preview/test/index.test.js @@ -121,6 +121,26 @@ function runTests(startServer = nextStart) { expect(cookies[1]).toHaveProperty('__next_preview_data') expect(cookies[1]['Max-Age']).toBe(expiry) }) + it('should set custom path cookies', async () => { + const path = '/preview-test' + const res = await fetchViaHTTP(appPort, '/api/preview', { + cookiePath: path, + }) + expect(res.status).toBe(200) + + const originalCookies = res.headers.get('set-cookie').split(',') + const cookies = originalCookies.map(cookie.parse) + + expect(originalCookies.every((c) => c.includes('; Secure;'))).toBe(true) + + expect(cookies.length).toBe(2) + expect(cookies[0]).toMatchObject({ SameSite: 'None' }) + expect(cookies[0]).toHaveProperty('__prerender_bypass') + expect(cookies[0]['Path']).toBe(path) + expect(cookies[1]).toMatchObject({ SameSite: 'None' }) + expect(cookies[1]).toHaveProperty('__next_preview_data') + expect(cookies[1]['Max-Age']).toBe(path) + }) it('should not return fallback page on preview request', async () => { const res = await fetchViaHTTP( appPort, From 944e993c560bd0d21dacb3efcafcc7c205e816ee Mon Sep 17 00:00:00 2001 From: Lucas Rosa Date: Mon, 4 Jul 2022 21:14:21 -0300 Subject: [PATCH 2/4] update params order and tests --- packages/next/server/api-utils/node.ts | 12 ++++----- packages/next/shared/lib/utils.ts | 10 +++---- .../prerender-preview/test/index.test.js | 8 +++--- test/unit/split-cookies-string.test.ts | 27 +++++++++++++++++++ 4 files changed, 42 insertions(+), 15 deletions(-) diff --git a/packages/next/server/api-utils/node.ts b/packages/next/server/api-utils/node.ts index 59d9539a941b..3acf4055cc4c 100644 --- a/packages/next/server/api-utils/node.ts +++ b/packages/next/server/api-utils/node.ts @@ -504,23 +504,23 @@ function setPreviewData( httpOnly: true, sameSite: process.env.NODE_ENV !== 'development' ? 'none' : 'lax', secure: process.env.NODE_ENV !== 'development', - ...(options.path !== undefined - ? ({ path: options.path } as CookieSerializeOptions) - : undefined), ...(options.maxAge !== undefined ? ({ maxAge: options.maxAge } as CookieSerializeOptions) : undefined), + ...(options.path !== undefined + ? ({ path: options.path } as CookieSerializeOptions) + : undefined), }), serialize(COOKIE_NAME_PRERENDER_DATA, payload, { httpOnly: true, sameSite: process.env.NODE_ENV !== 'development' ? 'none' : 'lax', secure: process.env.NODE_ENV !== 'development', - ...(options.path !== undefined - ? ({ path: options.path } as CookieSerializeOptions) - : undefined), ...(options.maxAge !== undefined ? ({ maxAge: options.maxAge } as CookieSerializeOptions) : undefined), + ...(options.path !== undefined + ? ({ path: options.path } as CookieSerializeOptions) + : undefined), }), ]) return res diff --git a/packages/next/shared/lib/utils.ts b/packages/next/shared/lib/utils.ts index 438b458f4a6d..357d5258f11d 100644 --- a/packages/next/shared/lib/utils.ts +++ b/packages/next/shared/lib/utils.ts @@ -245,11 +245,6 @@ export type NextApiResponse = ServerResponse & { setPreviewData: ( data: object | string, options?: { - /** - * Specifies the path for the preview session to work under. By default, - * the path is considered the "default path", i.e., any pages under "/". - */ - path?: string /** * Specifies the number (in seconds) for the preview session to last for. * The given number will be converted to an integer by rounding down. @@ -257,6 +252,11 @@ export type NextApiResponse = ServerResponse & { * when the client shuts down (browser is closed). */ maxAge?: number + /** + * Specifies the path for the preview session to work under. By default, + * the path is considered the "default path", i.e., any pages under "/". + */ + path?: string } ) => NextApiResponse clearPreviewData: () => NextApiResponse diff --git a/test/integration/prerender-preview/test/index.test.js b/test/integration/prerender-preview/test/index.test.js index 47fe85145c51..71e0591ff097 100644 --- a/test/integration/prerender-preview/test/index.test.js +++ b/test/integration/prerender-preview/test/index.test.js @@ -122,7 +122,7 @@ function runTests(startServer = nextStart) { expect(cookies[1]['Max-Age']).toBe(expiry) }) it('should set custom path cookies', async () => { - const path = '/preview-test' + const path = '/path' const res = await fetchViaHTTP(appPort, '/api/preview', { cookiePath: path, }) @@ -134,12 +134,12 @@ function runTests(startServer = nextStart) { expect(originalCookies.every((c) => c.includes('; Secure;'))).toBe(true) expect(cookies.length).toBe(2) - expect(cookies[0]).toMatchObject({ SameSite: 'None' }) + expect(cookies[0]).toMatchObject({ Path: '/', SameSite: 'None' }) expect(cookies[0]).toHaveProperty('__prerender_bypass') expect(cookies[0]['Path']).toBe(path) - expect(cookies[1]).toMatchObject({ SameSite: 'None' }) + expect(cookies[0]).toMatchObject({ Path: '/', SameSite: 'None' }) expect(cookies[1]).toHaveProperty('__next_preview_data') - expect(cookies[1]['Max-Age']).toBe(path) + expect(cookies[1]['Path']).toBe(path) }) it('should not return fallback page on preview request', async () => { const res = await fetchViaHTTP( diff --git a/test/unit/split-cookies-string.test.ts b/test/unit/split-cookies-string.test.ts index cf6764b42e9b..b6c7d28f60f8 100644 --- a/test/unit/split-cookies-string.test.ts +++ b/test/unit/split-cookies-string.test.ts @@ -44,6 +44,16 @@ describe('splitCookiesString', () => { expect(result).toEqual(expected) }) + it('should parse path', () => { + const { joined, expected } = generateCookies({ + name: 'foo', + value: 'bar', + path: '/path', + }) + const result = splitCookiesString(joined) + expect(result).toEqual(expected) + }) + it('should parse with all the options', () => { const { joined, expected } = generateCookies({ name: 'foo', @@ -111,6 +121,23 @@ describe('splitCookiesString', () => { expect(result).toEqual(expected) }) + it('should parse path', () => { + const { joined, expected } = generateCookies( + { + name: 'foo', + value: 'bar', + path: '/path', + }, + { + name: 'x', + value: 'y', + maxAge: '/path', + } + ) + const result = splitCookiesString(joined) + expect(result).toEqual(expected) + }) + it('should parse with all the options', () => { const { joined, expected } = generateCookies( { From 62f86564ed3723d8772bc802337612775a23842f Mon Sep 17 00:00:00 2001 From: Lucas Rosa Date: Wed, 6 Jul 2022 12:47:49 -0300 Subject: [PATCH 3/4] update tests --- test/unit/split-cookies-string.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/split-cookies-string.test.ts b/test/unit/split-cookies-string.test.ts index b6c7d28f60f8..2d5e89fc091b 100644 --- a/test/unit/split-cookies-string.test.ts +++ b/test/unit/split-cookies-string.test.ts @@ -131,7 +131,7 @@ describe('splitCookiesString', () => { { name: 'x', value: 'y', - maxAge: '/path', + path: '/path', } ) const result = splitCookiesString(joined) From 1d146049bfc340206e5db216b642571aa5bbefa4 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Sun, 7 Aug 2022 20:25:25 -0500 Subject: [PATCH 4/4] fix tests and update docs --- docs/advanced-features/preview-mode.md | 2 ++ packages/next/server/api-utils/node.ts | 2 ++ test/integration/prerender-preview/test/index.test.js | 4 ++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/advanced-features/preview-mode.md b/docs/advanced-features/preview-mode.md index c3fdf3608d53..a6700718692d 100644 --- a/docs/advanced-features/preview-mode.md +++ b/docs/advanced-features/preview-mode.md @@ -194,10 +194,12 @@ Then, send a request to `/api/clear-preview-mode-cookies` to invoke the API Rout `setPreviewData` takes an optional second parameter which should be an options object. It accepts the following keys: - `maxAge`: Specifies the number (in seconds) for the preview session to last for. +- `path`: Specifies the path the cookie should be applied under. Defaults to `/` enabling preview mode for all paths. ```js setPreviewData(data, { maxAge: 60 * 60, // The preview mode cookies expire in 1 hour + path: '/about', // The preview mode cookies apply to paths with /about }) ``` diff --git a/packages/next/server/api-utils/node.ts b/packages/next/server/api-utils/node.ts index 57ad310631f9..0f9a7654a8d8 100644 --- a/packages/next/server/api-utils/node.ts +++ b/packages/next/server/api-utils/node.ts @@ -519,6 +519,7 @@ function setPreviewData( httpOnly: true, sameSite: process.env.NODE_ENV !== 'development' ? 'none' : 'lax', secure: process.env.NODE_ENV !== 'development', + path: '/', ...(options.maxAge !== undefined ? ({ maxAge: options.maxAge } as CookieSerializeOptions) : undefined), @@ -530,6 +531,7 @@ function setPreviewData( httpOnly: true, sameSite: process.env.NODE_ENV !== 'development' ? 'none' : 'lax', secure: process.env.NODE_ENV !== 'development', + path: '/', ...(options.maxAge !== undefined ? ({ maxAge: options.maxAge } as CookieSerializeOptions) : undefined), diff --git a/test/integration/prerender-preview/test/index.test.js b/test/integration/prerender-preview/test/index.test.js index 71e0591ff097..ce9d77087f93 100644 --- a/test/integration/prerender-preview/test/index.test.js +++ b/test/integration/prerender-preview/test/index.test.js @@ -134,10 +134,10 @@ function runTests(startServer = nextStart) { expect(originalCookies.every((c) => c.includes('; Secure;'))).toBe(true) expect(cookies.length).toBe(2) - expect(cookies[0]).toMatchObject({ Path: '/', SameSite: 'None' }) + expect(cookies[0]).toMatchObject({ Path: path, SameSite: 'None' }) expect(cookies[0]).toHaveProperty('__prerender_bypass') expect(cookies[0]['Path']).toBe(path) - expect(cookies[0]).toMatchObject({ Path: '/', SameSite: 'None' }) + expect(cookies[0]).toMatchObject({ Path: path, SameSite: 'None' }) expect(cookies[1]).toHaveProperty('__next_preview_data') expect(cookies[1]['Path']).toBe(path) })