Skip to content

Commit d098a7f

Browse files
authoredOct 11, 2023
fix: support expired preview token and repository not found API error (#328)
* fix: support expired preview token API error * fix: throw `PrismicRepositoryNotFound` error when a repository does not exist
1 parent 163d03e commit d098a7f

File tree

5 files changed

+97
-8
lines changed

5 files changed

+97
-8
lines changed
 

‎src/createClient.ts

+20-6
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@ import type { PrismicDocument } from "./types/value/document";
1818
import { ForbiddenError } from "./errors/ForbiddenError";
1919
import { NotFoundError } from "./errors/NotFoundError";
2020
import { ParsingError } from "./errors/ParsingError";
21+
import { PreviewTokenExpiredError } from "./errors/PreviewTokenExpired";
2122
import { PrismicError } from "./errors/PrismicError";
2223
import { RefExpiredError } from "./errors/RefExpiredError";
2324
import { RefNotFoundError } from "./errors/RefNotFoundError";
25+
import { RepositoryNotFoundError } from "./errors/RepositoryNotFoundError";
2426

2527
import { LinkResolverFunction, asLink } from "./helpers/asLink";
2628

@@ -1911,16 +1913,28 @@ export class Client<TDocuments extends PrismicDocument = PrismicDocument> {
19111913
// Not Found
19121914
// - Incorrect repository name (this response has an empty body)
19131915
// - Ref does not exist
1916+
// - Preview token is expired
19141917
case 404: {
1915-
if (res.json && res.json.type === "api_notfound_error") {
1918+
if (res.json === undefined) {
1919+
throw new RepositoryNotFoundError(
1920+
`Prismic repository not found. Check that "${this.endpoint}" is pointing to the correct repository.`,
1921+
url,
1922+
undefined,
1923+
);
1924+
}
1925+
1926+
if (res.json.type === "api_notfound_error") {
19161927
throw new RefNotFoundError(res.json.message, url, res.json);
19171928
}
19181929

1919-
throw new NotFoundError(
1920-
`Prismic repository not found. Check that "${this.endpoint}" is pointing to the correct repository.`,
1921-
url,
1922-
undefined, // res.json is empty
1923-
);
1930+
if (
1931+
res.json.type === "api_security_error" &&
1932+
/preview token.*expired/i.test(res.json.message)
1933+
) {
1934+
throw new PreviewTokenExpiredError(res.json.message, url, res.json);
1935+
}
1936+
1937+
throw new NotFoundError(res.json.message, url, res.json);
19241938
}
19251939

19261940
// Gone

‎src/errors/PreviewTokenExpired.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { ForbiddenError } from "./ForbiddenError";
2+
3+
type PreviewTokenExpiredErrorAPIResponse = {
4+
type: "api_security_error";
5+
message: string;
6+
};
7+
8+
// This error extends `ForbiddenError` for backwards compatibility.
9+
// TODO: Extend this error from `PrismicError` in v8.
10+
export class PreviewTokenExpiredError<
11+
TResponse = PreviewTokenExpiredErrorAPIResponse,
12+
> extends ForbiddenError<TResponse> {}

‎src/errors/RepositoryNotFoundError.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { NotFoundError } from "./NotFoundError";
2+
3+
export class RepositoryNotFoundError<
4+
TResponse = undefined,
5+
> extends NotFoundError<TResponse> {}

‎src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,9 @@ export { ForbiddenError } from "./errors/ForbiddenError";
103103
export { NotFoundError } from "./errors/NotFoundError";
104104
export { RefNotFoundError } from "./errors/RefNotFoundError";
105105
export { RefExpiredError } from "./errors/RefExpiredError";
106+
export { PreviewTokenExpiredError } from "./errors/PreviewTokenExpired";
106107
export { ParsingError } from "./errors/ParsingError";
108+
export { RepositoryNotFoundError } from "./errors/RepositoryNotFoundError";
107109

108110
//=============================================================================
109111
// Types - Types representing Prismic content, models, and API payloads.

‎test/client.test.ts

+58-2
Original file line numberDiff line numberDiff line change
@@ -669,7 +669,7 @@ it("throws PrismicError if response is not JSON", async (ctx) => {
669669
await expect(() => client.get()).rejects.toThrowError(prismic.PrismicError);
670670
});
671671

672-
it("throws NotFoundError if repository does not exist", async (ctx) => {
672+
it("throws RepositoryNotFoundError if repository does not exist", async (ctx) => {
673673
const client = createTestClient();
674674

675675
ctx.server.use(
@@ -681,7 +681,9 @@ it("throws NotFoundError if repository does not exist", async (ctx) => {
681681
await expect(() => client.get()).rejects.toThrowError(
682682
/repository not found/i,
683683
);
684-
await expect(() => client.get()).rejects.toThrowError(prismic.NotFoundError);
684+
await expect(() => client.get()).rejects.toThrowError(
685+
prismic.RepositoryNotFoundError,
686+
);
685687
});
686688

687689
it("throws RefNotFoundError if ref does not exist", async (ctx) => {
@@ -738,6 +740,60 @@ it("throws RefExpiredError if ref is expired", async (ctx) => {
738740
);
739741
});
740742

743+
it("throws PreviewTokenExpiredError if preview token is expired", async (ctx) => {
744+
const queryResponse = {
745+
type: "api_security_error",
746+
message: "This preview token has expired",
747+
oauth_initiate: "https://qwerty.prismic.io/auth",
748+
oauth_token: "https://qwerty.prismic.io/auth/token",
749+
};
750+
751+
mockPrismicRestAPIV2({ ctx });
752+
753+
const client = createTestClient();
754+
755+
const queryEndpoint = new URL(
756+
"./documents/search",
757+
`${client.endpoint}/`,
758+
).toString();
759+
760+
ctx.server.use(
761+
msw.rest.get(queryEndpoint, (_req, res, ctx) => {
762+
return res(ctx.status(404), ctx.json(queryResponse));
763+
}),
764+
);
765+
766+
await expect(() => client.get()).rejects.toThrowError(queryResponse.message);
767+
await expect(() => client.get()).rejects.toThrowError(
768+
prismic.PreviewTokenExpiredError,
769+
);
770+
});
771+
772+
it("throws NotFoundError if the 404 error is unknown", async (ctx) => {
773+
const queryResponse = {
774+
type: "unknown_type",
775+
message: "message",
776+
};
777+
778+
mockPrismicRestAPIV2({ ctx });
779+
780+
const client = createTestClient();
781+
782+
const queryEndpoint = new URL(
783+
"./documents/search",
784+
`${client.endpoint}/`,
785+
).toString();
786+
787+
ctx.server.use(
788+
msw.rest.get(queryEndpoint, (_req, res, ctx) => {
789+
return res(ctx.status(404), ctx.json(queryResponse));
790+
}),
791+
);
792+
793+
await expect(() => client.get()).rejects.toThrowError(queryResponse.message);
794+
await expect(() => client.get()).rejects.toThrowError(prismic.NotFoundError);
795+
});
796+
741797
it("retries after `retry-after` milliseconds if response code is 429", async (ctx) => {
742798
const retryAfter = 200; // ms
743799
/**

0 commit comments

Comments
 (0)
Please sign in to comment.