feat (docs): on-demand revalidation for graphql and wrappers (#35177)

Before:

Revalidation was turned off entirely for wrappers pages, and done daily for GraphQL pages.

After:

Revalidation is via tag. There is an /api/revalidate endpoint that is used by GitHub Actions on the federated repos to trigger the revalidation. This decreases the number of unnecessary page generations by Vercel, and gives the maintainers of federated repos more fine-grained control over when to reploy their docs (see the docs deployment playbook for more info).
This commit is contained in:
Charis
2025-04-24 14:59:38 -04:00
committed by GitHub
parent 9227e83e56
commit 9d9fdcd9fc
5 changed files with 34 additions and 19 deletions

View File

@@ -40,6 +40,9 @@ describe('_handleRevalidateRequest', () => {
process.env.NEXT_PUBLIC_SUPABASE_URL = 'http://localhost:3000'
process.env.SUPABASE_SECRET_KEY = 'secret_key'
// Silence intentional console errors for cleaner test output
vi.spyOn(console, 'error').mockImplementation(() => {})
// Mock current date
mockDate = new Date('2023-01-01T12:00:00Z')
vi.setSystemTime(mockDate)
@@ -109,7 +112,7 @@ describe('_handleRevalidateRequest', () => {
headers: {
Authorization: 'Bearer basic_key',
},
body: JSON.stringify({ tags: ['tag1', 'tag2'] }),
body: JSON.stringify({ tags: ['graphql', 'wrappers'] }),
})
vi.mocked(headers).mockReturnValue(new Headers(request.headers))
@@ -119,8 +122,8 @@ describe('_handleRevalidateRequest', () => {
const response = await _handleRevalidateRequest(request)
expect(response.status).toBe(204)
expect(revalidateTag).toHaveBeenCalledTimes(2)
expect(revalidateTag).toHaveBeenCalledWith('tag1')
expect(revalidateTag).toHaveBeenCalledWith('tag2')
expect(revalidateTag).toHaveBeenCalledWith('graphql')
expect(revalidateTag).toHaveBeenCalledWith('wrappers')
})
it('should return 429 if last revalidation was less than 6 hours ago with basic permissions', async () => {
@@ -129,7 +132,7 @@ describe('_handleRevalidateRequest', () => {
headers: {
Authorization: 'Bearer basic_key',
},
body: JSON.stringify({ tags: ['tag1'] }),
body: JSON.stringify({ tags: ['graphql'] }),
})
vi.mocked(headers).mockReturnValue(new Headers(request.headers))
@@ -150,7 +153,7 @@ describe('_handleRevalidateRequest', () => {
headers: {
Authorization: 'Bearer basic_key',
},
body: JSON.stringify({ tags: ['tag1'] }),
body: JSON.stringify({ tags: ['graphql'] }),
})
vi.mocked(headers).mockReturnValue(new Headers(request.headers))
@@ -162,7 +165,7 @@ describe('_handleRevalidateRequest', () => {
const response = await _handleRevalidateRequest(request)
expect(response.status).toBe(204)
expect(revalidateTag).toHaveBeenCalledWith('tag1')
expect(revalidateTag).toHaveBeenCalledWith('graphql')
})
it('should revalidate regardless of last revalidation time with override permissions', async () => {
@@ -171,7 +174,7 @@ describe('_handleRevalidateRequest', () => {
headers: {
Authorization: 'Bearer override_key',
},
body: JSON.stringify({ tags: ['tag1'] }),
body: JSON.stringify({ tags: ['graphql'] }),
})
vi.mocked(headers).mockReturnValue(new Headers(request.headers))
@@ -183,6 +186,6 @@ describe('_handleRevalidateRequest', () => {
const response = await _handleRevalidateRequest(request)
expect(response.status).toBe(204)
expect(revalidateTag).toHaveBeenCalledWith('tag1')
expect(revalidateTag).toHaveBeenCalledWith('graphql')
})
})

View File

@@ -1,10 +1,10 @@
import { createClient } from '@supabase/supabase-js'
import { type Database } from 'common'
import { revalidateTag } from 'next/cache'
import { headers } from 'next/headers'
import { type NextRequest } from 'next/server'
import { z } from 'zod'
import { type Database } from 'common'
import { VALID_REVALIDATION_TAGS } from '~/features/helpers.fetch'
enum AuthorizationLevel {
Unauthorized,
@@ -13,7 +13,7 @@ enum AuthorizationLevel {
}
const requestBodySchema = z.object({
tags: z.array(z.string()),
tags: z.array(z.enum(VALID_REVALIDATION_TAGS)),
})
export const POST = handleError(_handleRevalidateRequest)
@@ -35,14 +35,12 @@ export async function _handleRevalidateRequest(request: NextRequest) {
}
let authorizationLevel = AuthorizationLevel.Unauthorized
const token = authorization.replace(/^Bearer\s+/, '')
const token = authorization.replace(/^Bearer /, '')
if (overrideKeys.includes(token)) {
authorizationLevel = AuthorizationLevel.Override
} else if (basicKeys.includes(token)) {
authorizationLevel = AuthorizationLevel.Basic
}
if (authorizationLevel === AuthorizationLevel.Unauthorized) {
return new Response('Invalid Authorization header', { status: 401 })
}