Skip to content

Commit

Permalink
fix: support expired preview token and repository not found API error (
Browse files Browse the repository at this point in the history
…#328)

* fix: support expired preview token API error

* fix: throw `PrismicRepositoryNotFound` error when a repository does not exist
  • Loading branch information
angeloashmore authored Oct 11, 2023
1 parent 163d03e commit d098a7f
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 8 deletions.
26 changes: 20 additions & 6 deletions src/createClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ 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";
import { RepositoryNotFoundError } from "./errors/RepositoryNotFoundError";

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

Expand Down Expand Up @@ -1911,16 +1913,28 @@ export class Client<TDocuments extends PrismicDocument = PrismicDocument> {
// Not Found
// - Incorrect repository name (this response has an empty body)
// - Ref does not exist
// - Preview token is expired
case 404: {
if (res.json && res.json.type === "api_notfound_error") {
if (res.json === undefined) {
throw new RepositoryNotFoundError(
`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
Expand Down
12 changes: 12 additions & 0 deletions src/errors/PreviewTokenExpired.ts
Original file line number Diff line number Diff line change
@@ -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<TResponse> {}
5 changes: 5 additions & 0 deletions src/errors/RepositoryNotFoundError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { NotFoundError } from "./NotFoundError";

export class RepositoryNotFoundError<
TResponse = undefined,
> extends NotFoundError<TResponse> {}
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@ 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";
export { RepositoryNotFoundError } from "./errors/RepositoryNotFoundError";

//=============================================================================
// Types - Types representing Prismic content, models, and API payloads.
Expand Down
60 changes: 58 additions & 2 deletions test/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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) => {
Expand Down Expand Up @@ -738,6 +740,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
/**
Expand Down

0 comments on commit d098a7f

Please sign in to comment.