http-repository-cache
Version:
A library for making HTTP requests using the repository pattern with an built in caching system.
303 lines (302 loc) • 13.4 kB
JavaScript
import DefaultHttpExceptionType from "../exception/DefaultHttpExceptionType";
import HttpMethod from "../HttpMethod";
/**
* A class that implements the `HttpRequestAdapter` interface using the Fetch API.
* This class provides methods for making HTTP requests with various HTTP methods
* (GET, POST, PATCH, PUT, DELETE) and includes support for authentication tokens,
* custom error handling, and credentials.
*
* @template T - The type of the response custom http exception type. Defaults to `unknown`.
*/
export default class FetchHttpRequest {
constructor() {
/**
* The authentication token used for making HTTP requests.
* This token is used to authenticate the user and authorize access to resources.
* It can be null if no token is available or the user is not authenticated.
*
* @default null
*/
this.authToken = null;
/**
* Indicates whether credentials such as cookies, authorization headers, or TLS client certificates
* should be included with the request. Defaults to `false`.
*/
this._includeCredentials = false;
/**
* A custom error status handling function that can be used to handle HTTP response status codes.
* This function takes a status code and an optional response body as parameters.
* It can either perform some side effects and return void, or it can return an instance of `HttpException<T>`.
*
* @param status - The HTTP status code of the response.
* @param body - Optional. The body of the response.
* @returns Either void or an instance of `HttpException<T>`.
*
* @default null
*/
this.customErrorStatusHandling = null;
}
/**
* Sets the authorization token to be used in HTTP requests.
*
* @param authToken - The token to be set for authorization.
*/
setAuthToken(authToken) {
this.authToken = authToken;
}
/**
* Removes the authentication token from the current instance.
* This method sets the `authToken` property to `null`, effectively
* removing any stored authentication token.
*/
removeAuthToken() {
this.authToken = null;
}
/**
* Enables the inclusion of credentials (such as cookies, authorization headers, or TLS client certificates)
* in the HTTP request. When this method is called, the request will be sent with credentials.
*/
includeCredentials() {
this._includeCredentials = true;
}
/**
* Disables the inclusion of credentials in the HTTP request.
* This method sets the `_includeCredentials` property to `false`,
* ensuring that credentials such as cookies or authorization headers
* are not sent with the request.
*/
notIncludeCredentials() {
this._includeCredentials = false;
}
/**
* Sets a custom error status handling function.
*
* @param customErrorStatusHandling - A function that takes an HTTP status code and an optional response body,
* and either returns void or an instance of `HttpException<T>`.
*/
setCustomErrorStatusHandling(customErrorStatusHandling) {
this.customErrorStatusHandling = customErrorStatusHandling;
}
/**
* Unset the custom error status handling by setting it to null.
* This method is used to remove any previously set custom error status handling logic.
*/
unsetCustomErrorStatusHandling() {
this.customErrorStatusHandling = null;
}
/**
* Generates the authorization header if an authentication token is available.
*
* @returns An object containing the Authorization header with the Bearer token,
* or undefined if no authentication token is present.
*/
getAuthorizationHeader() {
return (this.authToken && {
Authorization: `Bearer ${this.authToken}`,
});
}
/**
* Builds the request headers for an HTTP request.
*
* @param httpRequestParams - The parameters for the HTTP request, including headers and content type options.
* @returns The constructed headers for the HTTP request.
*/
buildRequestHeader(httpRequestParams) {
return this.getHeaders(httpRequestParams.headers, httpRequestParams.contentTypeJSON ?? true);
}
/**
* Constructs and returns the headers for an HTTP request.
*
* @param headers - Optional additional headers to include in the request.
* @param conteTypeJSON - A boolean indicating whether to include the "Content-Type: application/json" header. Defaults to true.
* @returns An object containing the combined headers.
*/
getHeaders(headers, conteTypeJSON = true) {
return {
...headers,
...(conteTypeJSON && { "Content-Type": "application/json" }),
...this.getAuthorizationHeader(),
};
}
/**
* Generates and throws an HttpException based on the provided status code and optional body.
* If custom error status handling is set, it will use the custom handler to generate the exception.
* Otherwise, it will use the default exception handling based on the status code.
*
* @param status - The HTTP status code.
* @param body - Optional. The body of the HTTP response.
* @throws {HttpException<T>} - Throws an HttpException with a type corresponding to the status code.
*/
generateException(status, body) {
//if custom exception handling is set, use it
if (this.customErrorStatusHandling) {
const customHttpException = this.customErrorStatusHandling(status, body);
if (customHttpException) {
throw customHttpException;
}
}
//if no custom status error handling thrown HttpException proceed default exception handling
switch (status) {
case 400:
throw {
type: DefaultHttpExceptionType.BAD_REQUEST,
body
};
case 401:
throw {
type: DefaultHttpExceptionType.UNAUTHORIZED,
body
};
case 402:
throw {
type: DefaultHttpExceptionType.PAYMENT_REQUIRED,
body
};
case 403:
throw {
type: DefaultHttpExceptionType.FORBIDDEN,
body
};
case 404:
throw {
type: DefaultHttpExceptionType.NOT_FOUND,
body
};
case 409:
throw {
type: DefaultHttpExceptionType.CONFLICT,
body
};
case 500:
throw {
type: DefaultHttpExceptionType.SERVER_ERROR,
body
};
case 503:
throw {
type: DefaultHttpExceptionType.SERVER_UNAVAILABLE,
body
};
default:
throw {
type: DefaultHttpExceptionType.UNKNOWN_ERROR,
body
};
}
}
/**
* Handles errors from HTTP requests.
*
* @param response - The response object which can be an Error or HttpException.
* @throws {Object} Throws an object with a type of `DefaultHttpExceptionType.ABORT_REQUEST` if the error is an AbortError.
* @throws {HttpException} Throws the response if it is an HttpException with a type and body.
* @throws {Object} Throws an object with a type of `DefaultHttpExceptionType.UNKNOWN_ERROR` and the response as the body for any other errors.
*/
handleError(response) {
if (response.name === "AbortError") {
throw {
type: DefaultHttpExceptionType.ABORT_REQUEST,
};
}
else if (response.type && response.body) {
throw response;
}
throw { type: DefaultHttpExceptionType.UNKNOWN_ERROR, body: response };
}
/**
* Handles the response callback for an HTTP request.
*
* @template R - The type of the response body.
* @param {number[]} successStatusCodes - An array of additional status codes that should be considered successful.
* @returns {Function} An asynchronous function that processes the response.
* @throws Will throw an exception if the response status is not in the list of success status codes.
*/
handleResponseCallback(successStatusCodes) {
const defaultSuccessStatusCodes = [200, 201, 204];
return async (response) => {
const body = await response.json();
if (defaultSuccessStatusCodes.includes(response.status) || successStatusCodes.includes(response.status)) {
return body;
}
//if not a success response throw Exception
throw this.generateException(response.status, body);
};
}
/**
* Makes an HTTP request using the Fetch API.
*
* @template R - The expected response type.
* @param {HttpMethod} httpMethod - The HTTP method to use for the request (e.g., GET, POST).
* @param {HttpRequestParams<unknown, FetchRequestOptions>} httpRequestParams - The parameters for the HTTP request.
* @param {string} httpRequestParams.url - The URL to which the request is sent.
* @param {Record<string, string>} [httpRequestParams.headers] - Optional headers to include in the request.
* @param {boolean} [httpRequestParams.contentTypeJSON=true] - Whether to set the `Content-Type` header to `application/json`.
* @param {unknown} [httpRequestParams.body] - The body of the request, which will be stringified if present.
* @param {boolean} [this._includeCredentials] - Whether to include credentials in the request.
* @param {FetchRequestOptions} [httpRequestParams.options] - Additional options to pass to the Fetch API.
* @param {number[]} [httpRequestParams.successStatusCodes] - An array of status codes that are considered successful.
* @returns {Promise<R>} A promise that resolves to the response of the request.
*/
fetch(httpMethod, httpRequestParams) {
return fetch(httpRequestParams.url, {
method: httpMethod,
headers: this.buildRequestHeader(httpRequestParams),
...(!!httpRequestParams.body && { body: httpRequestParams.contentTypeJSON === false ? httpRequestParams.body : JSON.stringify(httpRequestParams.body) }),
...(this._includeCredentials && { credentials: "include" }),
...httpRequestParams.options
}).then(this.handleResponseCallback(httpRequestParams.successStatusCodes ?? [])).catch(this.handleError);
}
/**
* Sends a GET request using the Fetch API.
*
* @template R - The expected response type.
* @param {HttpRequestParams<never, FetchRequestOptions>} httpRequestParams - The parameters for the HTTP request.
* @returns {Promise<R>} - A promise that resolves to the response of type R.
*/
get(httpRequestParams) {
return this.fetch(HttpMethod.GET, { ...httpRequestParams, contentTypeJSON: false });
}
/**
* Sends a POST HTTP request using the Fetch API.
*
* @template R - The expected response type.
* @template B - The type of the request body, defaults to `unknown`.
* @param {HttpRequestParams<B, FetchRequestOptions>} httpRequestParams - The parameters for the HTTP request.
* @returns {Promise<R>} A promise that resolves to the response of type `R`.
*/
post(httpRequestParams) {
return this.fetch(HttpMethod.POST, httpRequestParams);
}
/**
* Sends a PATCH HTTP request using the Fetch API.
*
* @template R - The expected response type.
* @template B - The type of the request body, defaults to `unknown`.
* @param {HttpRequestParams<B, FetchRequestOptions>} httpRequestParams - The parameters for the HTTP request.
* @returns {Promise<R>} - A promise that resolves to the response of type `R`.
*/
patch(httpRequestParams) {
return this.fetch(HttpMethod.PATCH, httpRequestParams);
}
/**
* Sends an HTTP PUT request using the Fetch API.
*
* @template R - The expected response type.
* @template B - The type of the request body. Defaults to `unknown`.
* @param {HttpRequestParams<B, FetchRequestOptions>} httpRequestParams - The parameters for the HTTP request.
* @returns {Promise<R>} - A promise that resolves to the response of type `R`.
*/
put(httpRequestParams) {
return this.fetch(HttpMethod.PUT, httpRequestParams);
}
/**
* Sends an HTTP DELETE request using the Fetch API.
*
* @template R - The expected response type.
* @param {HttpRequestParams<never, FetchRequestOptions>} httpRequestParams - The parameters for the HTTP request.
* @returns {Promise<R>} - A promise that resolves to the response of type R.
*/
delete(httpRequestParams) {
return this.fetch(HttpMethod.DELETE, { ...httpRequestParams, contentTypeJSON: false });
}
}