coddyger
Version:
Coddyger est une bibliothèque JavaScript/TypeScript qui fournit des fonctions communes et des plugins pour la gestion des données, la communication entre services, et des utilitaires avancés pour le développement d'applications.
276 lines (275 loc) • 11.7 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AxiosService = void 0;
const axios_1 = __importDefault(require("axios"));
const logger_service_1 = require("./logger.service");
class AxiosService {
static connect(config) {
// Merge default retry config with provided config
if (config.retryConfig) {
this.retryConfig = Object.assign(Object.assign({}, this.retryConfig), config.retryConfig);
}
this.instance = axios_1.default.create(Object.assign({ baseURL: config.baseURL, timeout: config.timeout || 30000, headers: Object.assign({ 'Content-Type': 'application/json' }, config.headers), withCredentials: config.withCredentials || false }, config));
this.setupInterceptors();
return this.instance;
}
static setupInterceptors() {
// Request interceptor with logging
this.instance.interceptors.request.use((config) => {
var _a;
const requestId = Math.random().toString(36).substring(7);
config.headers['X-Request-ID'] = requestId;
logger_service_1.LoggerService.debug('API Request', {
requestId,
method: (_a = config.method) === null || _a === void 0 ? void 0 : _a.toUpperCase(),
url: config.url,
headers: this.sanitizeHeaders(config.headers),
data: config.data
});
return config;
}, (error) => {
logger_service_1.LoggerService.error('API Request Error', { error });
return Promise.reject(error);
});
// Response interceptor with logging
this.instance.interceptors.response.use((response) => {
const requestId = response.config.headers['X-Request-ID'];
logger_service_1.LoggerService.debug('API Response', {
requestId,
status: response.status,
headers: this.sanitizeHeaders(response.headers),
data: response.data
});
return response;
}, (error) => {
var _a;
const requestId = (_a = error.config) === null || _a === void 0 ? void 0 : _a.headers['X-Request-ID'];
const errorResponse = this.handleAxiosError(error);
logger_service_1.LoggerService.error('API Response Error', {
requestId,
error: errorResponse
});
return Promise.reject(errorResponse);
});
}
static sanitizeHeaders(headers) {
const sensitiveHeaders = ['authorization', 'cookie', 'x-api-key'];
const sanitized = Object.assign({}, headers);
for (const header of sensitiveHeaders) {
if (sanitized[header]) {
sanitized[header] = '[REDACTED]';
}
}
return sanitized;
}
static handleAxiosError(error) {
var _a, _b, _c, _d, _e;
const defaultError = {
message: 'Une erreur inattendue s\'est produite',
code: 'UNKNOWN_ERROR',
status: 500,
timestamp: new Date().toISOString()
};
if (!error.response) {
return {
message: 'Erreur réseau - veuillez vérifier votre connexion',
code: 'NETWORK_ERROR',
status: 0,
timestamp: new Date().toISOString()
};
}
const status = error.response.status;
const data = error.response.data;
const timestamp = new Date().toISOString();
const path = ((_a = error.config) === null || _a === void 0 ? void 0 : _a.url) || '';
const errorMap = {
400: {
message: ((_b = data === null || data === void 0 ? void 0 : data.error) === null || _b === void 0 ? void 0 : _b.message) || 'Requête invalide',
code: 'BAD_REQUEST',
status: 400,
errors: (_c = data === null || data === void 0 ? void 0 : data.error) === null || _c === void 0 ? void 0 : _c.errors
},
401: {
message: 'Votre session a expiré. Veuillez vous reconnecter',
code: 'UNAUTHORIZED',
status: 401
},
403: {
message: 'Vous n\'avez pas les permissions nécessaires',
code: 'FORBIDDEN',
status: 403
},
404: {
message: 'La ressource demandée n\'a pas été trouvée',
code: 'NOT_FOUND',
status: 404
},
422: {
message: ((_d = data === null || data === void 0 ? void 0 : data.error) === null || _d === void 0 ? void 0 : _d.message) || 'Erreur de validation',
code: 'VALIDATION_ERROR',
status: 422,
errors: (_e = data === null || data === void 0 ? void 0 : data.error) === null || _e === void 0 ? void 0 : _e.errors
},
429: {
message: 'Trop de requêtes. Veuillez réessayer plus tard',
code: 'RATE_LIMIT_EXCEEDED',
status: 429
},
500: {
message: 'Erreur serveur interne. Veuillez réessayer plus tard',
code: 'SERVER_ERROR',
status: 500
},
503: {
message: 'Service temporairement indisponible',
code: 'SERVICE_UNAVAILABLE',
status: 503
}
};
const errorResponse = errorMap[status] || defaultError;
return Object.assign(Object.assign({}, errorResponse), { timestamp,
path });
}
static getCacheKey(method, url, data) {
return `${method}:${url}:${data ? JSON.stringify(data) : ''}`;
}
static getFromCache(cacheKey) {
const cached = this.cache.get(cacheKey);
if (!cached)
return null;
const now = Date.now();
if (now - cached.timestamp > cached.ttl) {
this.cache.delete(cacheKey);
return null;
}
return cached.data;
}
static setCache(cacheKey, data, ttlMs) {
this.cache.set(cacheKey, {
data,
timestamp: Date.now(),
ttl: ttlMs
});
}
static withRetry(requestFn, customConfig) {
return __awaiter(this, void 0, void 0, function* () {
const config = Object.assign(Object.assign({}, this.retryConfig), customConfig);
let lastError;
for (let attempt = 0; attempt < config.maxRetries; attempt++) {
try {
return yield requestFn();
}
catch (error) {
lastError = error;
if (!config.retryCondition(error)) {
break;
}
if (attempt < config.maxRetries - 1) {
const delay = config.delayMs * Math.pow(2, attempt);
logger_service_1.LoggerService.warn(`Retry attempt ${attempt + 1}/${config.maxRetries} after ${delay}ms`);
yield new Promise(resolve => setTimeout(resolve, delay));
}
}
}
throw lastError;
});
}
// Utility methods with improved type safety and caching support
static get(url, config) {
return __awaiter(this, void 0, void 0, function* () {
const cacheKey = this.getCacheKey('GET', url);
if (config === null || config === void 0 ? void 0 : config.cache) {
const cached = this.getFromCache(cacheKey);
if (cached)
return cached;
}
const request = () => this.instance.get(url, config);
const response = yield ((config === null || config === void 0 ? void 0 : config.retry) ? this.withRetry(request) : request());
if (config === null || config === void 0 ? void 0 : config.cache) {
this.setCache(cacheKey, response, config.cacheTTL || 5 * 60 * 1000); // Default 5 minutes
}
return response;
});
}
static post(url, data, config) {
return __awaiter(this, void 0, void 0, function* () {
const request = () => this.instance.post(url, data, config);
return (config === null || config === void 0 ? void 0 : config.retry) ? this.withRetry(request) : request();
});
}
static put(url, data, config) {
return __awaiter(this, void 0, void 0, function* () {
const request = () => this.instance.put(url, data, config);
return (config === null || config === void 0 ? void 0 : config.retry) ? this.withRetry(request) : request();
});
}
static delete(url, config) {
return __awaiter(this, void 0, void 0, function* () {
const request = () => this.instance.delete(url, config);
return (config === null || config === void 0 ? void 0 : config.retry) ? this.withRetry(request) : request();
});
}
static patch(url, data, config) {
return __awaiter(this, void 0, void 0, function* () {
const request = () => this.instance.patch(url, data, config);
return (config === null || config === void 0 ? void 0 : config.retry) ? this.withRetry(request) : request();
});
}
// Enhanced header management
static setHeader(key, value, type = 'common') {
if (this.instance) {
if (type === 'common') {
this.instance.defaults.headers.common[key] = value;
}
else {
this.instance.defaults.headers[type][key] = value;
}
}
}
static removeHeader(key, type = 'common') {
if (this.instance) {
if (type === 'common') {
delete this.instance.defaults.headers.common[key];
}
else {
delete this.instance.defaults.headers[type][key];
}
}
}
static getInstance() {
if (!this.instance) {
throw new Error('Instance Axios non initialisée. Appelez connect() d\'abord.');
}
return this.instance;
}
// Utility method to clear cache
static clearCache() {
this.cache.clear();
}
// Utility method to get cache size
static getCacheSize() {
return this.cache.size;
}
}
exports.AxiosService = AxiosService;
AxiosService.cache = new Map();
AxiosService.retryConfig = {
maxRetries: 3,
delayMs: 1000,
retryCondition: (error) => {
// Retry on network errors and 5xx server errors
return !error.response || (error.response.status >= 500 && error.response.status < 600);
}
};