Keycloak
1import { isRecord } from "../utils/isRecord";2import { CONTENT_TYPE_HEADER, CONTENT_TYPE_JSON } from "./constants";3
4export class ApiError extends Error {}5
6export async function parseResponse<T>(response: Response): Promise<T> {7const contentType = response.headers.get(CONTENT_TYPE_HEADER);8const isJSON = contentType ? contentType.includes(CONTENT_TYPE_JSON) : false;9
10if (!isJSON) {11throw new Error(12`Expected response to have a JSON content type, got '${contentType}' instead.`,13);14}15
16const data = await parseJSON(response);17
18if (!response.ok) {19throw new ApiError(getErrorMessage(data));20}21
22return data as T;23}
24
25async function parseJSON(response: Response): Promise<unknown> {26try {27return await response.json();28} catch (error) {29throw new Error("Unable to parse response as valid JSON.", {30cause: error,31});32}33}
34
35function getErrorMessage(data: unknown): string {36if (!isRecord(data)) {37throw new Error("Unable to retrieve error message from response.");38}39
40const errorKeys = ["error_description", "errorMessage", "error"];41
42for (const key of errorKeys) {43const value = data[key];44
45if (typeof value === "string") {46return value;47}48}49
50throw new Error(51"Unable to retrieve error message from response, no matching key found.",52);53}
54