Skip to content

Commit

Permalink
Merge pull request #103 from palantir/mf/capture-fetch-errors
Browse files Browse the repository at this point in the history
Handle error status codes when making network calls
  • Loading branch information
mfedderly authored Feb 29, 2024
2 parents 8ce5afb + 4b064a5 commit 6771d06
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 0 deletions.
5 changes: 5 additions & 0 deletions packages/shared.net/changelog/@unreleased/pr-103.v2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type: fix
fix:
description: Handle error status codes when making network calls
links:
- https://github.com/palantir/osdk-ts/pull/103
39 changes: 39 additions & 0 deletions packages/shared.net/src/client/createOpenApiRequest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@

import type { OpenApiRequest } from "@osdk/gateway/types";
import { describe, expect, expectTypeOf, it, vi } from "vitest";
import { PalantirApiError } from "../PalantirApiError.js";
import { createOpenApiRequest } from "./createOpenApiRequest.js";

describe("createOpenApiRequest", () => {
it("should construct request correctly", async () => {
const mockFetch = vi.fn();

mockFetch.mockResolvedValue({
ok: true,
json: () => Promise.resolve({ test: 1 }),
});

Expand Down Expand Up @@ -66,6 +68,7 @@ describe("createOpenApiRequest", () => {
const mockFetch = vi.fn();

mockFetch.mockResolvedValue({
ok: true,
json: () => Promise.resolve({ test: 1 }),
});

Expand Down Expand Up @@ -97,6 +100,7 @@ describe("createOpenApiRequest", () => {
const mockFetch = vi.fn();

mockFetch.mockResolvedValue({
ok: true,
json: () => Promise.resolve({ test: 1 }),
});

Expand All @@ -123,6 +127,7 @@ describe("createOpenApiRequest", () => {
const mockFetch = vi.fn();

mockFetch.mockResolvedValue({
ok: true,
json: () => Promise.resolve({ test: 1 }),
});

Expand Down Expand Up @@ -150,6 +155,7 @@ describe("createOpenApiRequest", () => {

const stream = new ReadableStream();
mockFetch.mockResolvedValue({
ok: true,
body: stream,
});

Expand Down Expand Up @@ -194,6 +200,7 @@ describe("createOpenApiRequest", () => {

const blob = new Blob();
mockFetch.mockResolvedValue({
ok: true,
blob: () => Promise.resolve(blob),
});

Expand Down Expand Up @@ -234,4 +241,36 @@ describe("createOpenApiRequest", () => {
},
);
});

it("handles error status codes", async () => {
const mockFetch = vi.fn();

const mockResponse: ReturnType<typeof fetch> = Promise.resolve({
ok: false,
status: 500,
json: () =>
Promise.resolve({
errorCode: "INTERNAL",
errorName: "Default:Internal",
errorInstanceId: "00000000-0000-0000-0000-000000000000",
parameters: {},
}),
} as Response);

mockFetch.mockImplementationOnce(() => mockResponse);

const request = createOpenApiRequest<{}>(
"http://example.com",
mockFetch,
undefined,
false,
);

try {
await request("POST", "/");
expect.fail();
} catch (error) {
expect(error).toBeInstanceOf(PalantirApiError);
}
});
});
30 changes: 30 additions & 0 deletions packages/shared.net/src/client/createOpenApiRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/

import type { OpenApiRequest } from "@osdk/gateway/types";
import { PalantirApiError } from "../PalantirApiError.js";
import { UnknownError } from "../UnknownError.js";
import { replaceHttpIfNotLocalhost } from "../util/index.js";

export function createOpenApiRequest<
Expand Down Expand Up @@ -73,6 +75,12 @@ export function createOpenApiRequest<
headers: headersInit,
});

// error status codes are not thrown by fetch automatically,
// we have to look at the ok property and behave accordingly
if (!response.ok) {
throw await convertError(response);
}

if (responseMediaType && responseMediaType === "*/*") {
if (asReadableStream) {
return response.body;
Expand Down Expand Up @@ -104,3 +112,25 @@ function withHttps(url: string): string {
? replaceHttpIfNotLocalhost(url)
: `${httpsProtocol}${url}`;
}

async function convertError(response: Response) {
try {
const convertedError = await response.json();
return new PalantirApiError(
convertedError.message,
convertedError.errorName,
convertedError.errorCode,
response.status,
convertedError.errorInstanceId,
convertedError.parameters,
);
} catch (e) {
if (e instanceof Error) {
return new UnknownError(e.message, "UNKNOWN");
}
return new UnknownError(
"Unable to parse error response",
"UNKNOWN",
);
}
}

0 comments on commit 6771d06

Please sign in to comment.