UNPKG

@alba-cars/common-modules

Version:

A package containing DTOs, validation classes and common modules and interfaces for Alba Cars

207 lines (206 loc) 8.12 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.apiRequest = exports.interceptors = void 0; const qs_1 = __importDefault(require("qs")); const enums_1 = require("../enums"); const utils_1 = require("../utils"); const BASE_URL = process.env.NEXT_PUBLIC_API_URL; const DEFAULT_TIMEOUT = (0, utils_1.timeAsMilliseconds)({ seconds: 30 }); // Interceptor manager class InterceptorManager { constructor() { this.requestInterceptors = []; this.responseInterceptors = []; this.responseErrorInterceptors = []; } addRequestInterceptor(interceptor) { return this.requestInterceptors.push(interceptor) - 1; } addResponseInterceptor(interceptor) { return this.responseInterceptors.push(interceptor) - 1; } addResponseErrorInterceptor(interceptor) { return this.responseErrorInterceptors.push(interceptor) - 1; } removeRequestInterceptor(index) { if (index >= 0) { this.requestInterceptors.splice(index, 1); } } removeResponseInterceptor(index) { if (index >= 0) { this.responseInterceptors.splice(index, 1); } } removeResponseErrorInterceptor(index) { if (index >= 0) { this.responseErrorInterceptors.splice(index, 1); } } clearRequestInterceptors() { this.requestInterceptors = []; } clearResponseInterceptors() { this.responseInterceptors = []; } clearResponseErrorInterceptors() { this.responseErrorInterceptors = []; } clearAllInterceptors() { this.clearRequestInterceptors(); this.clearResponseInterceptors(); this.clearResponseErrorInterceptors(); } async applyRequestInterceptors(config) { let modifiedConfig = { ...config }; for (const interceptor of this.requestInterceptors) { modifiedConfig = await interceptor(modifiedConfig); } return modifiedConfig; } async applyResponseInterceptors(response, requestConfig) { let modifiedResponse = response; for (const interceptor of this.responseInterceptors) { modifiedResponse = await interceptor(modifiedResponse, requestConfig); } return modifiedResponse; } async applyResponseErrorInterceptors(error, requestConfig) { // Create a chain of error interceptors that can potentially resolve the error let promise = Promise.reject(error); for (const interceptor of this.responseErrorInterceptors) { promise = promise.catch((currentError) => interceptor(currentError, requestConfig)); } return promise; } async retryRequest(config) { // Mark this as a retry request to avoid infinite loops const retryConfig = { ...config, options: { ...config.options, isRetry: true, }, }; try { // Apply request interceptors before retrying const interceptedConfig = await this.applyRequestInterceptors(retryConfig); // Make a new request with the updated config const response = await fetch(interceptedConfig.url, interceptedConfig); // Apply response interceptors const interceptedResponse = await this.applyResponseInterceptors(response, interceptedConfig); // Process the response const contentType = interceptedResponse.headers.get("content-type"); const responseData = (contentType === null || contentType === void 0 ? void 0 : contentType.includes("application/json")) ? await interceptedResponse.json() : await interceptedResponse.text(); if (interceptedResponse.ok) { return responseData; } // Handle errors const apiError = createApiError(interceptedResponse.status, responseData); throw apiError; } catch (error) { // For retry requests, don't apply error interceptors again to avoid loops if (config.options.isRetry) { throw error; } // Apply response error interceptors return this.applyResponseErrorInterceptors(error, retryConfig); } } } // Create the interceptor manager instance exports.interceptors = new InterceptorManager(); const buildUrl = (endpoint, query) => { let url = `${BASE_URL}${endpoint}`; if (query && Object.keys(query).length > 0) { url += `?${qs_1.default.stringify(query, { allowDots: true, arrayFormat: "brackets", allowEmptyArrays: false, skipNulls: true, })}`; } return url; }; async function apiRequest(endpoint, options = {}) { var _a; let timeoutId; const controller = options.timeout ? new AbortController() : undefined; // If timeout is specified, create a timeout abort controller if (options.timeout && controller) { timeoutId = setTimeout(() => { controller.abort("Request timeout"); }, options.timeout || DEFAULT_TIMEOUT); } // Use provided signal or timeout controller's signal const signal = options.signal || (controller === null || controller === void 0 ? void 0 : controller.signal); const headers = { Accept: "application/json", "Content-Type": "application/json", ...options.headers, }; const config = { method: (_a = options.method) !== null && _a !== void 0 ? _a : enums_1.HttpMethods.GET, headers, ...(signal && { signal }), ...options, options, // Keep a reference to the original options }; try { const response = await makeRequest(endpoint, config); return response; } finally { if (timeoutId) { clearTimeout(timeoutId); } } } exports.apiRequest = apiRequest; async function makeRequest(endpoint, config) { const url = buildUrl(endpoint, config.options.query); // Apply request interceptors with the full context including options let requestConfig = await exports.interceptors.applyRequestInterceptors({ ...config, url, }); // Extract the URL after interceptors may have modified it const interceptedUrl = requestConfig.url; try { // Make the actual request const response = await fetch(interceptedUrl, requestConfig); // Apply response interceptors with the full context const interceptedResponse = await exports.interceptors.applyResponseInterceptors(response, { ...requestConfig, url: interceptedUrl }); const contentType = interceptedResponse.headers.get("content-type"); const responseData = (contentType === null || contentType === void 0 ? void 0 : contentType.includes("application/json")) ? await interceptedResponse.json() : await interceptedResponse.text(); if (interceptedResponse.ok) { return responseData; } // Handle other errors const apiError = createApiError(interceptedResponse.status, responseData); throw apiError; } catch (error) { // Apply response error interceptors with the full context return exports.interceptors.applyResponseErrorInterceptors(error, { ...requestConfig, url: interceptedUrl, }); } } function createApiError(status, responseData) { const error = new Error(typeof responseData === "object" ? responseData.message || "API Error" : "API Error"); error.status = status; error.data = responseData; return error; }