Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace "slug" with "path" in draft/revalidate URLs #719

Merged
merged 1 commit into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions examples/example-graphql/pages/api/revalidate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@ export default async function handler(
request: NextApiRequest,
response: NextApiResponse
) {
let slug = request.query.slug as string
let path = request.query.path as string
const secret = request.query.secret as string

// Validate secret.
if (secret !== process.env.DRUPAL_REVALIDATE_SECRET) {
return response.status(401).json({ message: "Invalid secret." })
}

// Validate slug.
if (!slug) {
return response.status(400).json({ message: "Invalid slug." })
// Validate path.
if (!path) {
return response.status(400).json({ message: "Invalid path." })
}

try {
await response.revalidate(slug)
await response.revalidate(path)

return response.json({})
} catch (error) {
Expand Down
16 changes: 8 additions & 8 deletions examples/example-marketing/pages/api/revalidate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,26 @@ export default async function handler(
request: NextApiRequest,
response: NextApiResponse
) {
let slug = request.query.slug as string
let path = request.query.path as string
const secret = request.query.secret as string

// Validate secret.
if (secret !== process.env.DRUPAL_REVALIDATE_SECRET) {
return response.status(401).json({ message: "Invalid secret." })
}

// Validate slug.
if (!slug) {
return response.status(400).json({ message: "Invalid slug." })
// Validate path.
if (!path) {
return response.status(400).json({ message: "Invalid path." })
}

// Fix for home slug.
if (slug === process.env.DRUPAL_FRONT_PAGE) {
slug = "/"
// Fix for homepage.
if (path === process.env.DRUPAL_FRONT_PAGE) {
path = "/"
}

try {
await response.revalidate(slug)
await response.revalidate(path)

return response.json({})
} catch (error) {
Expand Down
4 changes: 2 additions & 2 deletions examples/example-router-migration/app/[...slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import type { Metadata, ResolvingMetadata } from "next"
import type { DrupalNode, JsonApiParams } from "next-drupal"

async function getNode(slug: string[]) {
const path = slug.join("/")
const path = `/${slug.join("/")}`

const params: JsonApiParams = {}

const draftData = getDraftData()

if (draftData.slug === `/${path}`) {
if (draftData.path === path) {
params.resourceVersion = draftData.resourceVersion
}

Expand Down
10 changes: 5 additions & 5 deletions examples/example-router-migration/app/api/revalidate/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@ import type { NextRequest } from "next/server"

async function handler(request: NextRequest) {
const searchParams = request.nextUrl.searchParams
const slug = searchParams.get("slug")
const path = searchParams.get("path")
const secret = searchParams.get("secret")

// Validate secret.
if (secret !== process.env.DRUPAL_REVALIDATE_SECRET) {
return new Response("Invalid secret.", { status: 401 })
}

// Validate slug.
if (!slug) {
return new Response("Invalid slug.", { status: 400 })
// Validate path.
if (!path) {
return new Response("Invalid path.", { status: 400 })
}

try {
revalidatePath(slug)
revalidatePath(path)

return new Response("Revalidated.")
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ protected static function buildUrl(NextSiteInterface $site, string $path): strin
return Url::fromUri("{$site->getBaseUrl()}/api/revalidate", [
'query' => [
'secret' => $site->getPreviewSecret(),
'slug' => $path,
'path' => $path,
],
])->toString();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,12 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s
*/
public function generate(NextSiteInterface $next_site, EntityInterface $entity, string $resource_version = NULL): ?Url {
$query = [];
$query['slug'] = $slug = $entity->toUrl()->toString();
$query['path'] = $path = $entity->toUrl()->toString();
$query['uuid'] = $this->user->uuid();

// Create a secret based on the timestamp, slug and the user uuid.
// Create a secret based on the timestamp, path and the user uuid.
$query['timestamp'] = $timestamp = $this->time->getRequestTime();
$query['secret'] = $secret = $this->previewSecretGenerator->generate($timestamp . $slug . $resource_version . $this->user->uuid());
$query['secret'] = $secret = $this->previewSecretGenerator->generate($timestamp . $path . $resource_version . $this->user->uuid());

// Generate a JWT and store it temporarily so that we can retrieve it on
// validate.
Expand All @@ -158,10 +158,10 @@ public function generate(NextSiteInterface $next_site, EntityInterface $entity,
public function validate(Request $request) {
$body = Json::decode($request->getContent());

// Validate the slug.
// We do not check for existing slug. We let the next.js site handle this.
if (empty($body['slug'])) {
throw new InvalidPreviewUrlRequest("Field 'slug' is missing");
// Validate the path.
// We do not check for existing path. We let the next.js site handle this.
if (empty($body['path'])) {
throw new InvalidPreviewUrlRequest("Field 'path' is missing");
}

// Validate the uuid.
Expand All @@ -184,7 +184,7 @@ public function validate(Request $request) {
throw new InvalidPreviewUrlRequest("Field 'secret' is missing");
}

if ($body['secret'] !== $this->previewSecretGenerator->generate($body['timestamp'] . $body['slug'] . $body['resourceVersion'] . $body['uuid'])) {
if ($body['secret'] !== $this->previewSecretGenerator->generate($body['timestamp'] . $body['path'] . $body['resourceVersion'] . $body['uuid'])) {
throw new InvalidPreviewUrlRequest("The provided secret is invalid.");
}

Expand Down
2 changes: 1 addition & 1 deletion modules/next/src/Entity/NextSite.php
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ public function getRevalidateUrlForPath(string $path): ?Url {
}

$query = [
'slug' => $path,
'path' => $path,
];

if ($secret = $this->getRevalidateSecret()) {
Expand Down
18 changes: 9 additions & 9 deletions modules/next/src/Plugin/Next/PreviewUrlGenerator/SimpleOauth.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,11 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s
*/
public function generate(NextSiteInterface $next_site, EntityInterface $entity, string $resource_version = NULL): ?Url {
$query = [];
$query['slug'] = $slug = $entity->toUrl()->toString();
$query['path'] = $path = $entity->toUrl()->toString();

// Create a secret based on the timestamp, slug, scope and resource version.
// Create a secret based on the timestamp, path, scope and resource version.
$query['timestamp'] = $timestamp = $this->time->getRequestTime();
$query['secret'] = $this->previewSecretGenerator->generate($timestamp . $slug . $resource_version);
$query['secret'] = $this->previewSecretGenerator->generate($timestamp . $path . $resource_version);

return Url::fromUri($next_site->getPreviewUrl(), [
'query' => $query,
Expand All @@ -143,10 +143,10 @@ public function generate(NextSiteInterface $next_site, EntityInterface $entity,
public function validate(Request $request) {
$body = Json::decode($request->getContent());

// Validate the slug.
// We do not check for existing slug. We let the next.js site handle this.
if (empty($body['slug'])) {
throw new InvalidPreviewUrlRequest("Field 'slug' is missing");
// Validate the path.
// We do not check for existing path. We let the next.js site handle this.
if (empty($body['path'])) {
throw new InvalidPreviewUrlRequest("Field 'path' is missing");
}

// Validate the timestamp.
Expand All @@ -164,12 +164,12 @@ public function validate(Request $request) {
throw new InvalidPreviewUrlRequest("Field 'secret' is missing");
}

if ($body['secret'] !== $this->previewSecretGenerator->generate($body['timestamp'] . $body['slug'] . $body['resourceVersion'])) {
if ($body['secret'] !== $this->previewSecretGenerator->generate($body['timestamp'] . $body['path'] . $body['resourceVersion'])) {
throw new InvalidPreviewUrlRequest("The provided secret is invalid.");
}

return [
'path' => $body['slug'],
'path' => $body['path'],
'maxAge' => (int) $this->configuration['secret_expiration'],
];
}
Expand Down
4 changes: 2 additions & 2 deletions modules/next/tests/src/Kernel/Entity/NextSiteTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,10 @@ public function testGetRevalidateUrlForPath() {
$this->assertNull($marketing->getRevalidateUrlForPath('/foo'));

$marketing->setRevalidateUrl('http://example.com/api/revalidate');
$this->assertSame('http://example.com/api/revalidate?slug=/foo', $marketing->getRevalidateUrlForPath('/foo')->toString());
$this->assertSame('http://example.com/api/revalidate?path=/foo', $marketing->getRevalidateUrlForPath('/foo')->toString());

$marketing->setRevalidateSecret('12345');
$this->assertSame('http://example.com/api/revalidate?slug=/foo&secret=12345', $marketing->getRevalidateUrlForPath('/foo')->toString());
$this->assertSame('http://example.com/api/revalidate?path=/foo&secret=12345', $marketing->getRevalidateUrlForPath('/foo')->toString());
}

}
18 changes: 9 additions & 9 deletions modules/next/tests/src/Kernel/Plugin/PathRevalidatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public function testRevalidate() {
$page->save();
$this->container->get('kernel')->terminate(Request::create('/'), new Response());

$client->request('GET', 'http://blog.com/api/revalidate?slug=/node/2')->shouldBeCalled()->willReturn(new GuzzleResponse());
$client->request('GET', 'http://blog.com/api/revalidate?path=/node/2')->shouldBeCalled()->willReturn(new GuzzleResponse());
$blog_site->setRevalidateUrl('http://blog.com/api/revalidate')->save();
$page = $this->createNode();
$page->save();
Expand All @@ -104,8 +104,8 @@ public function testRevalidate() {
],
])->save();

$client->request('GET', 'http://marketing.com/api/revalidate?slug=/node/3&secret=12345')->shouldBeCalled()->willReturn(new GuzzleResponse());
$client->request('GET', 'http://blog.com/api/revalidate?slug=/node/3')->shouldBeCalled()->willReturn(new GuzzleResponse());
$client->request('GET', 'http://marketing.com/api/revalidate?path=/node/3&secret=12345')->shouldBeCalled()->willReturn(new GuzzleResponse());
$client->request('GET', 'http://blog.com/api/revalidate?path=/node/3')->shouldBeCalled()->willReturn(new GuzzleResponse());
$page = $this->createNode();
$page->save();
$this->container->get('kernel')->terminate(Request::create('/'), new Response());
Expand All @@ -114,12 +114,12 @@ public function testRevalidate() {
'additional_paths' => "/\n/blog",
])->save();

$client->request('GET', 'http://marketing.com/api/revalidate?slug=/node/3&secret=12345')->shouldBeCalled()->willReturn(new GuzzleResponse());
$client->request('GET', 'http://marketing.com/api/revalidate?slug=/&secret=12345')->shouldBeCalled()->willReturn(new GuzzleResponse());
$client->request('GET', 'http://marketing.com/api/revalidate?slug=/blog&secret=12345')->shouldBeCalled()->willReturn(new GuzzleResponse());
$client->request('GET', 'http://blog.com/api/revalidate?slug=/node/3')->shouldBeCalled()->willReturn(new GuzzleResponse());
$client->request('GET', 'http://blog.com/api/revalidate?slug=/')->shouldBeCalled()->willReturn(new GuzzleResponse());
$client->request('GET', 'http://blog.com/api/revalidate?slug=/blog')->shouldBeCalled()->willReturn(new GuzzleResponse());
$client->request('GET', 'http://marketing.com/api/revalidate?path=/node/3&secret=12345')->shouldBeCalled()->willReturn(new GuzzleResponse());
$client->request('GET', 'http://marketing.com/api/revalidate?path=/&secret=12345')->shouldBeCalled()->willReturn(new GuzzleResponse());
$client->request('GET', 'http://marketing.com/api/revalidate?path=/blog&secret=12345')->shouldBeCalled()->willReturn(new GuzzleResponse());
$client->request('GET', 'http://blog.com/api/revalidate?path=/node/3')->shouldBeCalled()->willReturn(new GuzzleResponse());
$client->request('GET', 'http://blog.com/api/revalidate?path=/')->shouldBeCalled()->willReturn(new GuzzleResponse());
$client->request('GET', 'http://blog.com/api/revalidate?path=/blog')->shouldBeCalled()->willReturn(new GuzzleResponse());
$page = $this->createNode();
$page->save();
$this->container->get('kernel')->terminate(Request::create('/'), new Response());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,15 @@ public function testGenerate() {
$this->setCurrentUser($user);
$preview_url = $this->nextSite->getPreviewUrlForEntity($page);
$query = $preview_url->getOption('query');
$this->assertNotEmpty($query['slug']);
$this->assertNotEmpty($query['path']);
$this->assertNotEmpty($query['timestamp']);
$this->assertNotEmpty($query['secret']);
$this->assertSame($query['plugin'], 'simple_oauth');

// Test the secret.
/** @var \Drupal\next\PreviewSecretGeneratorInterface $secret_generator */
$secret_generator = \Drupal::service('next.preview_secret_generator');
$this->assertSame($query['secret'], $secret_generator->generate($query['timestamp'] . $query['slug'] . $query['resourceVersion']));
$this->assertSame($query['secret'], $secret_generator->generate($query['timestamp'] . $query['path'] . $query['resourceVersion']));
}

/**
Expand Down Expand Up @@ -143,7 +143,7 @@ public function testValidateSecret() {

$this->expectExceptionMessage('The provided secret is invalid.');
$query = $preview_url->getOption('query');
$query['slug'] = '/random-slug';
$query['path'] = '/random-path';
$request = Request::create('/', 'POST', [], [], [], [], Json::encode($query));
$preview_url_generator->validate($request);

Expand All @@ -162,26 +162,26 @@ public function testValidateSecret() {
*/
public function providerValidateForInvalidBody() {
return [
[[], "Field 'slug' is missing"],
[['slug' => '/node/1'], "Field 'timestamp' is missing"],
[[], "Field 'path' is missing"],
[['path' => '/node/1'], "Field 'timestamp' is missing"],
[
[
'slug' => '/node/1',
'path' => '/node/1',
'timestamp' => strtotime('now'),
],
"Field 'secret' is missing",
],
[
[
'slug' => '/node/1',
'path' => '/node/1',
'timestamp' => strtotime('-60 seconds'),
'secret' => 'secret',
],
"The provided secret has expired.",
],
[
[
'slug' => '/node/1',
'path' => '/node/1',
'timestamp' => strtotime('60 seconds'),
'secret' => 'secret',
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public function testPrepare() {
$response = $this->container->get('http_kernel')->handle($request);
$this->setRawContent($response->getContent());

$preview_url = 'https://blog.com/api/preview?slug=/node/1';
$preview_url = 'https://blog.com/api/preview?path=/node/1';
$fields = $this->xpath("//iframe[contains(@src, '$preview_url')]");
$this->assertCount(1, $fields);

Expand Down
18 changes: 9 additions & 9 deletions packages/next-drupal/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1023,11 +1023,11 @@ export class DrupalClient {
}

async validateDraftUrl(searchParams: URLSearchParams): Promise<Response> {
const slug = searchParams.get("slug")
const path = searchParams.get("path")

this.debug(`Fetching draft url validation for ${slug}.`)
this.debug(`Fetching draft url validation for ${path}.`)

// Fetch the headless CMS to check if the provided `slug` exists
// Fetch the headless CMS to check if the provided `path` exists
let response: Response
try {
// Validate the draft url.
Expand All @@ -1047,8 +1047,8 @@ export class DrupalClient {

this.debug(
response.status !== 200
? `Could not validate slug, ${slug}`
: `Validated slug, ${slug}`
? `Could not validate path, ${path}`
: `Validated path, ${path}`
)

return response
Expand All @@ -1060,7 +1060,7 @@ export class DrupalClient {
options?: Parameters<NextApiResponse["setDraftMode"]>[0]
) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { slug, resourceVersion, plugin, secret, scope, ...draftData } =
const { path, resourceVersion, plugin, secret, scope, ...draftData } =
request.query
const useDraftMode = options?.enable

Expand Down Expand Up @@ -1104,15 +1104,15 @@ export class DrupalClient {
// Adds preview data for use in app router pages.
cookies.push(
`${DRAFT_DATA_COOKIE_NAME}=${encodeURIComponent(
JSON.stringify({ slug, resourceVersion, ...draftData })
JSON.stringify({ path, resourceVersion, ...draftData })
)}; Path=/; HttpOnly; SameSite=None; Secure`
)
}
response.setHeader("Set-Cookie", cookies)

// We can safely redirect to the slug since this has been validated on the
// We can safely redirect to the path since this has been validated on the
// server.
response.writeHead(307, { Location: slug })
response.writeHead(307, { Location: path })

this.debug(`${useDraftMode ? "Draft" : "Preview"} mode enabled.`)

Expand Down
Loading
Loading