@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
JavaScript
;
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;
}