diff --git a/src/hooks/useHttpClient.tsx b/src/hooks/useHttpClient.tsx index 059484a8..a1396231 100644 --- a/src/hooks/useHttpClient.tsx +++ b/src/hooks/useHttpClient.tsx @@ -1,5 +1,9 @@ import React, { createContext, useContext, useMemo } from 'react'; -import axios, { AxiosInstance, RawAxiosRequestHeaders } from 'axios'; +import axios, { + AxiosError, + AxiosInstance, + RawAxiosRequestHeaders, +} from 'axios'; import { useAuth } from './useAuth'; import { APIClient } from '@lifeomic/one-query'; import { RestAPIEndpoints } from '../types/rest-types'; @@ -32,6 +36,32 @@ const HttpClientContext = createContext({ let requestInterceptorId: number; let responseInterceptorId: number; +/* istanbul ignore next */ +const createHumanReadableFailureMessage = (error: AxiosError) => { + const lines: string[] = ['HTTP Request Failed:']; + + if (!error.config) { + lines.push(' No request config found.'); + return lines.join('\n'); + } + + const method = error.config.method?.toUpperCase() ?? ''; + const baseUrl = error.config.baseURL ?? ''; + const url = error.config.url ?? ''; + + const status = error.response?.status ?? ''; + + const data = error.response?.data + ? JSON.stringify(error.response.data, null, 2) + : ''; + + lines.push(` Endpoint: ${method} ${baseUrl}${url}`); + lines.push(` Response Status: ${status}`); + lines.push(` Response Data: ${data}`); + + return lines.join('\n'); +}; + /** * The HttpClientContextProvider's job is to provide an HTTP client that * takes care of things like managing the HTTP Authorization header, error @@ -83,7 +113,10 @@ export const HttpClientContextProvider = ({ async function (error: Error) { if (axios.isAxiosError(error)) { if (__DEV__ && process.env.NODE_ENV !== 'test') { - console.warn('Request Failed: ', error.toJSON()); + console.log(createHumanReadableFailureMessage(error)); + console.warn( + 'An HTTP request failed. See the Metro logs for details.', + ); } if (error.response?.status === 401) {