import { injectable } from "inversify"; import { IHttpClient, IFetchOptions, } from "./interfaces"; import createTraceToken from "./utils/createTraceToken"; import * as fetch from "isomorphic-fetch"; import JsonApiError from "./jsonApiError"; @injectable() export default class HttpClient implements IHttpClient { async fetch(url, options: IFetchOptions = {}): Promise { const requestOptions = { ...{ ...options, body: JSON.stringify(options.body), headers: { ...options.headers, "content-type": "application/vnd.api+json", "X-Trace-Token": createTraceToken(url), }, credentials: "include", } }; if (process.env.OPEN_PLANET_DEBUG) { console.log(url); } const response = await fetch(url, requestOptions); if (!response.ok) { const error = await this.createError(url, options, response); throw error; } if (options.method && options.method.toLowerCase() === "delete") { return await response.text(); } if (response.status === 204) { return; } return response.json(); } private async createError(url, options, response) { const textBody = await response.text(); const maybeJsonApiError = this.parseJsonApiError(textBody, response.status); if (maybeJsonApiError) { return maybeJsonApiError; } return new Error(`Error on ${options.method || "GET"} ${url}: ${textBody}`); } private parseJsonApiError(textBody: string, statusCode: number) { try { const parsed = JSON.parse(textBody); const error = parsed.errors[0]; if (error && (error.title || error.description)) { const status = error.status ? `${error.status}` : `${statusCode}`; return new JsonApiError({ ...error, status }); } } catch (err) { // ignore parsing error } } }