From 66c33d03db7a2c26d2ebf2e0818749ab04ea04d2 Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Mon, 9 Oct 2023 15:39:58 -1000 Subject: [PATCH 1/2] fix: support expired preview token API error --- src/createClient.ts | 24 ++++++++++---- src/errors/PreviewTokenExpired.ts | 12 +++++++ src/index.ts | 1 + test/client.test.ts | 54 +++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 6 deletions(-) create mode 100644 src/errors/PreviewTokenExpired.ts diff --git a/src/createClient.ts b/src/createClient.ts index 18febe10..1a44257c 100644 --- a/src/createClient.ts +++ b/src/createClient.ts @@ -18,6 +18,7 @@ import type { PrismicDocument } from "./types/value/document"; import { ForbiddenError } from "./errors/ForbiddenError"; import { NotFoundError } from "./errors/NotFoundError"; import { ParsingError } from "./errors/ParsingError"; +import { PreviewTokenExpiredError } from "./errors/PreviewTokenExpired"; import { PrismicError } from "./errors/PrismicError"; import { RefExpiredError } from "./errors/RefExpiredError"; import { RefNotFoundError } from "./errors/RefNotFoundError"; @@ -1912,15 +1913,26 @@ export class Client { // - Incorrect repository name (this response has an empty body) // - Ref does not exist case 404: { - if (res.json && res.json.type === "api_notfound_error") { + if (res.json === undefined) { + throw new NotFoundError( + `Prismic repository not found. Check that "${this.endpoint}" is pointing to the correct repository.`, + url, + undefined, + ); + } + + if (res.json.type === "api_notfound_error") { throw new RefNotFoundError(res.json.message, url, res.json); } - throw new NotFoundError( - `Prismic repository not found. Check that "${this.endpoint}" is pointing to the correct repository.`, - url, - undefined, // res.json is empty - ); + if ( + res.json.type === "api_security_error" && + /preview token.*expired/i.test(res.json.message) + ) { + throw new PreviewTokenExpiredError(res.json.message, url, res.json); + } + + throw new NotFoundError(res.json.message, url, res.json); } // Gone diff --git a/src/errors/PreviewTokenExpired.ts b/src/errors/PreviewTokenExpired.ts new file mode 100644 index 00000000..799ed97b --- /dev/null +++ b/src/errors/PreviewTokenExpired.ts @@ -0,0 +1,12 @@ +import { ForbiddenError } from "./ForbiddenError"; + +type PreviewTokenExpiredErrorAPIResponse = { + type: "api_security_error"; + message: string; +}; + +// This error extends `ForbiddenError` for backwards compatibility. +// TODO: Extend this error from `PrismicError` in v8. +export class PreviewTokenExpiredError< + TResponse = PreviewTokenExpiredErrorAPIResponse, +> extends ForbiddenError {} diff --git a/src/index.ts b/src/index.ts index ad63443c..0af2a813 100644 --- a/src/index.ts +++ b/src/index.ts @@ -103,6 +103,7 @@ export { ForbiddenError } from "./errors/ForbiddenError"; export { NotFoundError } from "./errors/NotFoundError"; export { RefNotFoundError } from "./errors/RefNotFoundError"; export { RefExpiredError } from "./errors/RefExpiredError"; +export { PreviewTokenExpiredError } from "./errors/PreviewTokenExpired"; export { ParsingError } from "./errors/ParsingError"; //============================================================================= diff --git a/test/client.test.ts b/test/client.test.ts index a6dbb975..82f129f2 100644 --- a/test/client.test.ts +++ b/test/client.test.ts @@ -738,6 +738,60 @@ it("throws RefExpiredError if ref is expired", async (ctx) => { ); }); +it("throws PreviewTokenExpiredError if preview token is expired", async (ctx) => { + const queryResponse = { + type: "api_security_error", + message: "This preview token has expired", + oauth_initiate: "https://qwerty.prismic.io/auth", + oauth_token: "https://qwerty.prismic.io/auth/token", + }; + + mockPrismicRestAPIV2({ ctx }); + + const client = createTestClient(); + + const queryEndpoint = new URL( + "./documents/search", + `${client.endpoint}/`, + ).toString(); + + ctx.server.use( + msw.rest.get(queryEndpoint, (_req, res, ctx) => { + return res(ctx.status(404), ctx.json(queryResponse)); + }), + ); + + await expect(() => client.get()).rejects.toThrowError(queryResponse.message); + await expect(() => client.get()).rejects.toThrowError( + prismic.PreviewTokenExpiredError, + ); +}); + +it("throws NotFoundError if the 404 error is unknown", async (ctx) => { + const queryResponse = { + type: "unknown_type", + message: "message", + }; + + mockPrismicRestAPIV2({ ctx }); + + const client = createTestClient(); + + const queryEndpoint = new URL( + "./documents/search", + `${client.endpoint}/`, + ).toString(); + + ctx.server.use( + msw.rest.get(queryEndpoint, (_req, res, ctx) => { + return res(ctx.status(404), ctx.json(queryResponse)); + }), + ); + + await expect(() => client.get()).rejects.toThrowError(queryResponse.message); + await expect(() => client.get()).rejects.toThrowError(prismic.NotFoundError); +}); + it("retries after `retry-after` milliseconds if response code is 429", async (ctx) => { const retryAfter = 200; // ms /** From f719e319a35f3cf57725aac8cc323db2c8137040 Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Mon, 9 Oct 2023 15:50:38 -1000 Subject: [PATCH 2/2] fix: throw `PrismicRepositoryNotFound` error when a repository does not exist --- src/createClient.ts | 4 +++- src/errors/RepositoryNotFoundError.ts | 5 +++++ src/index.ts | 1 + test/client.test.ts | 6 ++++-- 4 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 src/errors/RepositoryNotFoundError.ts diff --git a/src/createClient.ts b/src/createClient.ts index 1a44257c..ebb99507 100644 --- a/src/createClient.ts +++ b/src/createClient.ts @@ -22,6 +22,7 @@ import { PreviewTokenExpiredError } from "./errors/PreviewTokenExpired"; import { PrismicError } from "./errors/PrismicError"; import { RefExpiredError } from "./errors/RefExpiredError"; import { RefNotFoundError } from "./errors/RefNotFoundError"; +import { RepositoryNotFoundError } from "./errors/RepositoryNotFoundError"; import { LinkResolverFunction, asLink } from "./helpers/asLink"; @@ -1912,9 +1913,10 @@ export class Client { // Not Found // - Incorrect repository name (this response has an empty body) // - Ref does not exist + // - Preview token is expired case 404: { if (res.json === undefined) { - throw new NotFoundError( + throw new RepositoryNotFoundError( `Prismic repository not found. Check that "${this.endpoint}" is pointing to the correct repository.`, url, undefined, diff --git a/src/errors/RepositoryNotFoundError.ts b/src/errors/RepositoryNotFoundError.ts new file mode 100644 index 00000000..60d1af44 --- /dev/null +++ b/src/errors/RepositoryNotFoundError.ts @@ -0,0 +1,5 @@ +import { NotFoundError } from "./NotFoundError"; + +export class RepositoryNotFoundError< + TResponse = undefined, +> extends NotFoundError {} diff --git a/src/index.ts b/src/index.ts index 0af2a813..d6e25b1f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -105,6 +105,7 @@ export { RefNotFoundError } from "./errors/RefNotFoundError"; export { RefExpiredError } from "./errors/RefExpiredError"; export { PreviewTokenExpiredError } from "./errors/PreviewTokenExpired"; export { ParsingError } from "./errors/ParsingError"; +export { RepositoryNotFoundError } from "./errors/RepositoryNotFoundError"; //============================================================================= // Types - Types representing Prismic content, models, and API payloads. diff --git a/test/client.test.ts b/test/client.test.ts index 82f129f2..c6307040 100644 --- a/test/client.test.ts +++ b/test/client.test.ts @@ -669,7 +669,7 @@ it("throws PrismicError if response is not JSON", async (ctx) => { await expect(() => client.get()).rejects.toThrowError(prismic.PrismicError); }); -it("throws NotFoundError if repository does not exist", async (ctx) => { +it("throws RepositoryNotFoundError if repository does not exist", async (ctx) => { const client = createTestClient(); ctx.server.use( @@ -681,7 +681,9 @@ it("throws NotFoundError if repository does not exist", async (ctx) => { await expect(() => client.get()).rejects.toThrowError( /repository not found/i, ); - await expect(() => client.get()).rejects.toThrowError(prismic.NotFoundError); + await expect(() => client.get()).rejects.toThrowError( + prismic.RepositoryNotFoundError, + ); }); it("throws RefNotFoundError if ref does not exist", async (ctx) => {