expanded
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
// Replace entire file with simplified string enum
|
||||
export enum HttpMethod {
|
||||
Get = 'GET',
|
||||
Post = 'POST',
|
||||
Put = 'PUT',
|
||||
Patch = 'PATCH',
|
||||
Delete = 'DELETE'
|
||||
}
|
||||
GET = 'GET',
|
||||
POST = 'POST',
|
||||
PUT = 'PUT',
|
||||
PATCH = 'PATCH',
|
||||
DELETE = 'DELETE'
|
||||
}
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
import {HttpMethod} from './HttpMethod';
|
||||
|
||||
const defaultRequestTimeoutDurationInMilliseconds = 30_000;
|
||||
const httpMethodsThatMustNotHaveBody = [HttpMethod.GET]; // Head, Options
|
||||
|
||||
export class HttpRequestError extends Error {
|
||||
constructor(
|
||||
message: string,
|
||||
public readonly status?: number,
|
||||
public readonly statusText?: string,
|
||||
public readonly originalError?: unknown
|
||||
) {
|
||||
super(message);
|
||||
this.name = 'HttpRequestError';
|
||||
}
|
||||
}
|
||||
|
||||
export class HttpRequests {
|
||||
private readonly _baseUri: string;
|
||||
@@ -13,32 +26,36 @@ export class HttpRequests {
|
||||
this._requestTimeoutDuration = options.requestTimeoutDuration ?? defaultRequestTimeoutDurationInMilliseconds;
|
||||
}
|
||||
|
||||
public async request<T>(method: HttpMethod, path: string, options?: RequestInit & {body?: Record<string, unknown> | string}): Promise<T | void> {
|
||||
public async request<T>(method: HttpMethod, path: string, options: RequestInit & {body?: Record<string, unknown> | string} = {}): Promise<T> {
|
||||
const abortController = new AbortController();
|
||||
const abortSignal = abortController.signal;
|
||||
|
||||
let requestOptions: RequestInit = {
|
||||
const requestOptions: RequestInit = {
|
||||
headers: this._baseHeaders,
|
||||
method: method.toString(), // Convert enum to string
|
||||
signal: abortSignal
|
||||
method: HttpMethod[method], // Convert enum to string
|
||||
signal: abortSignal,
|
||||
...options
|
||||
};
|
||||
|
||||
if (options?.body && method !== HttpMethod.Get) {
|
||||
requestOptions.body = typeof options.body === 'string' ? options.body : JSON.stringify(options.body);
|
||||
if (httpMethodsThatMustNotHaveBody.includes(method)) {
|
||||
requestOptions.body = undefined;
|
||||
}
|
||||
|
||||
return this.makeRequest<T>(path, requestOptions, abortController, this._requestTimeoutDuration);
|
||||
}
|
||||
|
||||
private async makeRequest<T>(path: string, options: RequestInit, abortController: AbortController, timeoutDuration: number): Promise<T | void> {
|
||||
private async makeRequest<T>(path: string, options: RequestInit, abortController: AbortController, timeoutDuration: number): Promise<T> {
|
||||
const requestTimeoutId = globalThis.setTimeout(() => abortController.abort(), timeoutDuration);
|
||||
|
||||
try {
|
||||
const requestPath = `${this._baseUri}${path}`;
|
||||
const response = await fetch(requestPath, options);
|
||||
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status [${response.status}] ${response.statusText}`);
|
||||
throw new HttpRequestError(
|
||||
`HTTP error! status [${response.status}] ${response.statusText}`,
|
||||
response.status,
|
||||
response.statusText
|
||||
);
|
||||
}
|
||||
|
||||
const responseContentType = response.headers.get('content-type');
|
||||
@@ -49,8 +66,18 @@ export class HttpRequests {
|
||||
|
||||
return response.text() as T;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return;
|
||||
if (e instanceof HttpRequestError) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (e instanceof Error) {
|
||||
if (e.name === 'AbortError') {
|
||||
throw new HttpRequestError(`Request timeout after ${timeoutDuration}ms`, undefined, undefined, e);
|
||||
}
|
||||
throw new HttpRequestError(`Request failed: ${e.message}`, undefined, undefined, e);
|
||||
}
|
||||
|
||||
throw new HttpRequestError(`Unknown request error: ${String(e)}`, undefined, undefined, e);
|
||||
} finally {
|
||||
globalThis.clearTimeout(requestTimeoutId);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user