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

feat: accept Web API Request object in enableAutoPreviewsFromReq() #240

Merged
merged 8 commits into from
May 26, 2022
52 changes: 36 additions & 16 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1207,19 +1207,27 @@ export class Client {
async resolvePreviewURL<LinkResolverReturnType>(
args: ResolvePreviewArgs<LinkResolverReturnType> & FetchParams,
): Promise<string> {
let documentID = args.documentID;
let previewToken = args.previewToken;
let documentID: string | undefined | null = args.documentID;
let previewToken: string | undefined | null = args.previewToken;

if (typeof globalThis.location !== "undefined") {
const searchParams = new URLSearchParams(globalThis.location.search);

documentID = documentID || searchParams.get("documentId") || undefined;
previewToken = previewToken || searchParams.get("token") || undefined;
} else if (this.refState.httpRequest?.query) {
documentID =
documentID || (this.refState.httpRequest.query.documentId as string);
previewToken =
previewToken || (this.refState.httpRequest.query.token as string);
documentID = documentID || searchParams.get("documentId");
previewToken = previewToken || searchParams.get("token");
} else if (this.refState.httpRequest) {
if (this.refState.httpRequest.url) {
const searchParams = new URL(this.refState.httpRequest.url)
.searchParams;

documentID = documentID || searchParams.get("documentId");
previewToken = previewToken || searchParams.get("token");
} else {
documentID =
documentID || (this.refState.httpRequest.query?.documentId as string);
previewToken =
previewToken || (this.refState.httpRequest.query?.token as string);
}
}

if (documentID != null && previewToken != null) {
Expand Down Expand Up @@ -1492,15 +1500,27 @@ export class Client {
*/
private async getResolvedRefString(params?: FetchParams): Promise<string> {
if (this.refState.autoPreviewsEnabled) {
let previewRef: string | undefined = undefined;
let previewRef: string | undefined;

let cookieJar: string | null | undefined;

if (globalThis.document?.cookie) {
previewRef = getCookie(cookie.preview, globalThis.document.cookie);
} else if (this.refState.httpRequest?.headers?.cookie) {
previewRef = getCookie(
cookie.preview,
this.refState.httpRequest.headers.cookie,
);
cookieJar = globalThis.document.cookie;
} else if (this.refState.httpRequest?.headers) {
if (
"get" in this.refState.httpRequest.headers &&
typeof this.refState.httpRequest.headers.get === "function"
) {
// Web API Headers
cookieJar = this.refState.httpRequest.headers.get("cookie");
} else if ("cookie" in this.refState.httpRequest.headers) {
// Express-style headers
cookieJar = this.refState.httpRequest.headers.cookie;
}
}

if (cookieJar) {
previewRef = getCookie(cookie.preview, cookieJar);
}

if (previewRef) {
Expand Down
8 changes: 4 additions & 4 deletions src/lib/getCookie.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ const getAll = (cookieStore: string): { [name: string]: string } =>
/**
* Returns the value of a cookie from a given cookie store.
*
* @param Name - Of the cookie.
* @param cookieStore - The stringified cookie store from which to read the cookie.
* @param name - Of the cookie.
* @param cookieJar - The stringified cookie store from which to read the cookie.
*
* @returns The value of the cookie, if it exists.
*/
export const getCookie = (
name: string,
cookieStore: string,
): string | undefined => getAll(cookieStore)[name];
cookieJar: string,
): string | undefined => getAll(cookieJar)[name];
30 changes: 24 additions & 6 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,30 @@ export interface ResponseLike {
* The minimum required properties to treat as an HTTP Request for automatic
* Prismic preview support.
*/
export interface HttpRequestLike {
headers?: {
cookie?: string;
};
query?: Record<string, unknown>;
}
export type HttpRequestLike =
| /**
* Web API Request
*
* @see http://developer.mozilla.org/en-US/docs/Web/API/Request
*/
{
headers?: {
get(name: string): string | null;
};
url?: string;
query?: never;
}

/**
* Express-style Request
*/
| {
headers?: {
cookie?: string;
};
query?: Record<string, unknown>;
url?: never;
};

/**
* An `orderings` parameter that orders the results by the specified field.
Expand Down
41 changes: 40 additions & 1 deletion test/client-resolvePreviewUrl.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import test from "ava";
import * as mswNode from "msw/node";
import { Headers } from "node-fetch";
import AbortController from "abort-controller";

import { createDocument } from "./__testutils__/createDocument";
Expand Down Expand Up @@ -76,6 +77,44 @@ test.serial("resolves a preview url using a server req object", async (t) => {
t.is(res, `/${document.uid}`);
});

test.serial(
"resolves a preview url using a Web API-based server req object",
async (t) => {
const document = createDocument();
const queryResponse = createQueryResponse([document]);

const documentId = document.id;
const previewToken = "previewToken";

const headers = new Headers();
const url = new URL("https://example.com");
url.searchParams.set("documentId", documentId);
url.searchParams.set("token", previewToken);
const req = {
headers,
url: url.toString(),
};

server.use(
createMockRepositoryHandler(t),
createMockQueryHandler(t, [queryResponse], undefined, {
ref: previewToken,
q: `[[at(document.id, "${documentId}")]]`,
lang: "*",
}),
);

const client = createTestClient(t);
client.enableAutoPreviewsFromReq(req);
const res = await client.resolvePreviewURL({
linkResolver: (document) => `/${document.uid}`,
defaultURL: "defaultURL",
});

t.is(res, `/${document.uid}`);
},
);

test.serial(
"allows providing an explicit documentId and previewToken",
async (t) => {
Expand Down Expand Up @@ -139,7 +178,7 @@ test.serial(
"returns defaultURL if req does not contain preview params in server req object",
async (t) => {
const defaultURL = "defaultURL";
const req = { query: {} };
const req = {};

const client = createTestClient(t);
client.enableAutoPreviewsFromReq(req);
Expand Down
49 changes: 48 additions & 1 deletion test/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import test from "ava";
import * as msw from "msw";
import * as mswNode from "msw/node";
import * as sinon from "sinon";
import { Response } from "node-fetch";
import { Response, Headers } from "node-fetch";

import { createMockQueryHandler } from "./__testutils__/createMockQueryHandler";
import { createMockRepositoryHandler } from "./__testutils__/createMockRepositoryHandler";
Expand Down Expand Up @@ -261,6 +261,53 @@ test.serial("uses req preview ref if available", async (t) => {
t.deepEqual(res, queryResponse);
});

test.serial("supports req with Web APIs", async (t) => {
const previewRef = "previewRef";
const headers = new Headers();
headers.set("cookie", `io.prismic.preview=${previewRef}`);
const req = {
headers,
url: "https://example.com",
};

const queryResponse = createQueryResponse();

server.use(
createMockRepositoryHandler(t),
createMockQueryHandler(t, [queryResponse], undefined, {
ref: previewRef,
}),
);

const client = createTestClient(t);
client.enableAutoPreviewsFromReq(req);
const res = await client.get();

t.deepEqual(res, queryResponse);
});

test.serial("ignores req without cookies", async (t) => {
const req = {
headers: {},
};

const repositoryResponse = createRepositoryResponse();
const queryResponse = createQueryResponse();

server.use(
createMockRepositoryHandler(t, repositoryResponse),
createMockQueryHandler(t, [queryResponse], undefined, {
ref: getMasterRef(repositoryResponse),
}),
);

const client = createTestClient(t);
client.enableAutoPreviewsFromReq(req);
const res = await client.get();

t.deepEqual(res, queryResponse);
});

test.serial(
"does not use preview ref if auto previews are disabled",
async (t) => {
Expand Down