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

fix: support expired preview token and repository not found API error #328

Merged
merged 2 commits into from
Oct 11, 2023
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
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