Skip to content

Commit 26d5b0f

Browse files
authoredOct 6, 2023
feat: support expired and not-found ref API errors (#327)
* feat: support expired and not-found ref API errors * fix: extend `RefNotFoundError` and `RefExpiredError` from `ForbiddenError` for backwards compatibility
1 parent fb555fd commit 26d5b0f

8 files changed

+113
-10
lines changed
 

‎src/createClient.ts

+17-4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import { ForbiddenError } from "./errors/ForbiddenError";
1919
import { NotFoundError } from "./errors/NotFoundError";
2020
import { ParsingError } from "./errors/ParsingError";
2121
import { PrismicError } from "./errors/PrismicError";
22+
import { RefExpiredError } from "./errors/RefExpiredError";
23+
import { RefNotFoundError } from "./errors/RefNotFoundError";
2224

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

@@ -1900,22 +1902,33 @@ export class Client<TDocuments extends PrismicDocument = PrismicDocument> {
19001902
// - Incorrect access token for query endpoint
19011903
case 403: {
19021904
throw new ForbiddenError(
1903-
"error" in res.json ? res.json.error : res.json.message,
1905+
res.json.error || res.json.message,
19041906
url,
19051907
res.json,
19061908
);
19071909
}
19081910

1909-
// Not Found (this response has an empty body)
1910-
// - Incorrect repository name
1911+
// Not Found
1912+
// - Incorrect repository name (this response has an empty body)
1913+
// - Ref does not exist
19111914
case 404: {
1915+
if (res.json && res.json.type === "api_notfound_error") {
1916+
throw new RefNotFoundError(res.json.message, url, res.json);
1917+
}
1918+
19121919
throw new NotFoundError(
19131920
`Prismic repository not found. Check that "${this.endpoint}" is pointing to the correct repository.`,
19141921
url,
1915-
undefined,
1922+
undefined, // res.json is empty
19161923
);
19171924
}
19181925

1926+
// Gone
1927+
// - Ref is expired
1928+
case 410: {
1929+
throw new RefExpiredError(res.json.message, url, res.json);
1930+
}
1931+
19191932
// Too Many Requests
19201933
// - Exceeded the maximum number of requests per second
19211934
case 429: {

‎src/errors/ForbiddenError.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ type ForbiddenErrorQueryAPIResponse = {
99
error: string;
1010
};
1111

12-
export class ForbiddenError extends PrismicError<
13-
ForbiddenErrorRepositoryAPIResponse | ForbiddenErrorQueryAPIResponse
14-
> {}
12+
export class ForbiddenError<
13+
TResponse =
14+
| ForbiddenErrorRepositoryAPIResponse
15+
| ForbiddenErrorQueryAPIResponse,
16+
> extends PrismicError<TResponse> {}

‎src/errors/NotFoundError.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
import { PrismicError } from "./PrismicError";
22

3-
export class NotFoundError extends PrismicError<undefined> {}
3+
export class NotFoundError<
4+
TResponse = undefined,
5+
> extends PrismicError<TResponse> {}

‎src/errors/ParsingError.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,6 @@ type ParsingErrorAPIResponse = {
99
location: string;
1010
};
1111

12-
export class ParsingError extends PrismicError<ParsingErrorAPIResponse> {}
12+
export class ParsingError<
13+
TResponse = ParsingErrorAPIResponse,
14+
> extends PrismicError<TResponse> {}

‎src/errors/RefExpiredError.ts

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { ForbiddenError } from "./ForbiddenError";
2+
3+
type RefExpiredErrorAPIResponse = {
4+
type: "api_validation_error";
5+
message: string;
6+
};
7+
8+
// This error extends `ForbiddenError` for backwards compatibility. Before the
9+
// API started returning 410 for expired refs, it returnd 403, which threw a
10+
// `ForbiddenError`.
11+
// TODO: Extend this error from `PrismicError` in v8.
12+
export class RefExpiredError<
13+
TResponse = RefExpiredErrorAPIResponse,
14+
> extends ForbiddenError<TResponse> {}

‎src/errors/RefNotFoundError.ts

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { ForbiddenError } from "./ForbiddenError";
2+
3+
type RefNotFoundErrorAPIResponse = {
4+
type: "api_notfound_error";
5+
message: string;
6+
};
7+
8+
// This error extends `ForbiddenError` for backwards compatibility. Before the
9+
// API started returning 404 for not found refs, it returnd 403, which threw a
10+
// `ForbiddenError`.
11+
// TODO: Extend this error from `PrismicError` in v8.
12+
export class RefNotFoundError<
13+
TResponse = RefNotFoundErrorAPIResponse,
14+
> extends ForbiddenError<TResponse> {}

‎src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ export type { HTMLRichTextSerializer } from "./helpers/asHTML";
101101
export { PrismicError } from "./errors/PrismicError";
102102
export { ForbiddenError } from "./errors/ForbiddenError";
103103
export { NotFoundError } from "./errors/NotFoundError";
104+
export { RefNotFoundError } from "./errors/RefNotFoundError";
105+
export { RefExpiredError } from "./errors/RefExpiredError";
104106
export { ParsingError } from "./errors/ParsingError";
105107

106108
//=============================================================================

‎test/client.test.ts

+55-1
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,7 @@ it("throws PrismicError if response is not 200, 400, 401, 403, or 404", async (c
631631
const client = createTestClient();
632632

633633
const queryEndpoint = new URL(
634-
"documents/search",
634+
"./documents/search",
635635
`${client.endpoint}/`,
636636
).toString();
637637

@@ -684,6 +684,60 @@ it("throws NotFoundError if repository does not exist", async (ctx) => {
684684
await expect(() => client.get()).rejects.toThrowError(prismic.NotFoundError);
685685
});
686686

687+
it("throws RefNotFoundError if ref does not exist", async (ctx) => {
688+
const queryResponse = {
689+
type: "api_notfound_error",
690+
message: "message",
691+
};
692+
693+
mockPrismicRestAPIV2({ ctx });
694+
695+
const client = createTestClient();
696+
697+
const queryEndpoint = new URL(
698+
"./documents/search",
699+
`${client.endpoint}/`,
700+
).toString();
701+
702+
ctx.server.use(
703+
msw.rest.get(queryEndpoint, (_req, res, ctx) => {
704+
return res(ctx.status(404), ctx.json(queryResponse));
705+
}),
706+
);
707+
708+
await expect(() => client.get()).rejects.toThrowError(queryResponse.message);
709+
await expect(() => client.get()).rejects.toThrowError(
710+
prismic.RefNotFoundError,
711+
);
712+
});
713+
714+
it("throws RefExpiredError if ref is expired", async (ctx) => {
715+
const queryResponse = {
716+
type: "api_validation_error",
717+
message: "message",
718+
};
719+
720+
mockPrismicRestAPIV2({ ctx });
721+
722+
const client = createTestClient();
723+
724+
const queryEndpoint = new URL(
725+
"./documents/search",
726+
`${client.endpoint}/`,
727+
).toString();
728+
729+
ctx.server.use(
730+
msw.rest.get(queryEndpoint, (_req, res, ctx) => {
731+
return res(ctx.status(410), ctx.json(queryResponse));
732+
}),
733+
);
734+
735+
await expect(() => client.get()).rejects.toThrowError(queryResponse.message);
736+
await expect(() => client.get()).rejects.toThrowError(
737+
prismic.RefExpiredError,
738+
);
739+
});
740+
687741
it("retries after `retry-after` milliseconds if response code is 429", async (ctx) => {
688742
const retryAfter = 200; // ms
689743
/**

0 commit comments

Comments
 (0)
Please sign in to comment.