diff --git a/index.ts b/index.ts index e94a4f139..371b83c40 100644 --- a/index.ts +++ b/index.ts @@ -1,4 +1,5 @@ import k8s from "@kubernetes/client-node"; +import { StatusCodes as fetchStatus } from "http-status-codes"; import utils from "ramda"; import { Capability } from "./src/lib/capability"; import { fetch, fetchRaw } from "./src/lib/fetch"; @@ -22,6 +23,7 @@ export { utils, fetch, fetchRaw, + fetchStatus, k8s, // Export the imported type information for external packages diff --git a/package-lock.json b/package-lock.json index d79d19ecc..ecd9699a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "commander": "10.0.1", "express": "4.18.2", "fast-json-patch": "3.1.1", + "http-status-codes": "2.2.0", "node-fetch": "2.6.9", "node-forge": "1.3.1", "prettier": "2.8.8", @@ -2617,6 +2618,11 @@ "npm": ">=1.3.7" } }, + "node_modules/http-status-codes": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.2.0.tgz", + "integrity": "sha512-feERVo9iWxvnejp3SEfm/+oNG517npqL2/PIA8ORjyOZjGC7TwCRQsZylciLS64i6pJ0wRYz3rkXLRwbtFa8Ng==" + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", diff --git a/package.json b/package.json index 5297d75dc..562d727ec 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "commander": "10.0.1", "express": "4.18.2", "fast-json-patch": "3.1.1", + "http-status-codes": "2.2.0", "node-fetch": "2.6.9", "node-forge": "1.3.1", "prettier": "2.8.8", diff --git a/src/cli/init/templates/capabilities/hello-pepr.ts b/src/cli/init/templates/capabilities/hello-pepr.ts index 40797c432..f9b272622 100644 --- a/src/cli/init/templates/capabilities/hello-pepr.ts +++ b/src/cli/init/templates/capabilities/hello-pepr.ts @@ -168,12 +168,14 @@ When(a.ConfigMap) .IsCreated() .WithLabel("chuck-norris") .Then(async change => { - const joke = await fetch( + const response = await fetch( "https://api.chucknorris.io/jokes/random?category=dev" ); - // Add the Chuck Norris joke to the configmap - change.Raw.data["chuck-says"] = joke.value; + if (response.ok) { + // Add the Chuck Norris joke to the configmap + change.Raw.data["chuck-says"] = response.data.value; + } }); /** diff --git a/src/lib/fetch.test.ts b/src/lib/fetch.test.ts index d43a910a6..c66bd3f3a 100644 --- a/src/lib/fetch.test.ts +++ b/src/lib/fetch.test.ts @@ -26,8 +26,9 @@ test.beforeEach(() => { test("fetch: should return without type data", async t => { const url = "https://jsonplaceholder.typicode.com/todos/1"; - const response = await fetch(url); - t.is(response["title"], "Example title"); + const { data, ok } = await fetch(url); + t.is(ok, true); + t.is(data["title"], "Example title"); }); test("fetch: should return parsed JSON response as a specific type", async t => { @@ -39,7 +40,8 @@ test("fetch: should return parsed JSON response as a specific type", async t => } const url = "https://jsonplaceholder.typicode.com/todos/1"; - const data = await fetch(url); + const { data, ok } = await fetch(url); + t.is(ok, true); t.is(data.id, 1); t.is(typeof data.title, "string"); t.is(typeof data.completed, "boolean"); @@ -59,7 +61,8 @@ test("fetch: should handle additional request options", async t => { }, }; - const data = await fetch(url, requestOptions); + const { data, ok } = await fetch(url, requestOptions); + t.is(ok, true); t.is(data["title"], "test todo"); t.is(data["userId"], 1); t.is(data["completed"], false); diff --git a/src/lib/fetch.ts b/src/lib/fetch.ts index 1890c26cd..ed8244ba8 100644 --- a/src/lib/fetch.ts +++ b/src/lib/fetch.ts @@ -1,7 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023-Present The Pepr Authors +import { StatusCodes } from "http-status-codes"; import f, { RequestInfo, RequestInit } from "node-fetch"; +import logger from "./logger"; export { f as fetchRaw }; /** @@ -18,13 +20,30 @@ export { f as fetchRaw }; * @returns */ export async function fetch(url: URL | RequestInfo, init?: RequestInit) { - const resp = await f(url, init); + try { + logger.debug(`Fetching ${url}`); - // Throw an error if the response is not OK - if (!resp.ok) { - throw new Error(`HTTP ${resp.status} ${resp.statusText}`); - } + const resp = await f(url, init); + let data: T; + + if (resp.ok) { + data = await resp.json(); + } - const data = await resp.json(); - return data as T; + return { + data, + ok: resp.ok, + status: resp.status, + statusText: resp.statusText, + }; + } catch (e) { + logger.debug(`Fetch failed: ${e.message}`); + + return { + data: null, + ok: false, + status: e.code || StatusCodes.BAD_REQUEST, + statusText: e.message, + }; + } }