UNPKG

akua-sdk

Version:

TypeScript SDK for Akua Acquiring Processor

193 lines (192 loc) 6.97 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.HttpClient = void 0; const axios_1 = __importDefault(require("axios")); /** * HttpClient is a class that provides a client for making HTTP requests to the Akua API. * It handles the creation of Axios instances for the API endpoints, * and provides methods for making GET, POST, PUT, PATCH, and DELETE requests. * It also includes interceptors for error handling and environment validation. */ class HttpClient { client; pointsClient; baseUrl; pointsBaseUrl; environment; clientConfig; tokenData; tokenGenerationTime; isRefreshingToken = false; constructor(config) { this.environment = config.environment; const isProduction = config.environment === 'production'; this.baseUrl = isProduction ? 'https://api.akua.la' : 'https://sandbox.akua.la'; this.pointsBaseUrl = isProduction ? 'https://api.akua.la/points-api' : 'https://sandbox.akua.la/points-api'; const headers = { 'Content-Type': 'application/json', Accept: 'application/json', }; this.client = axios_1.default.create({ baseURL: this.baseUrl, headers, }); this.pointsClient = axios_1.default.create({ baseURL: this.pointsBaseUrl, headers, }); this.clientConfig = config; this.setupInterceptors(this.client); this.setupInterceptors(this.pointsClient); } async refreshToken() { // If we're already refreshing the token, don't do it again if (this.isRefreshingToken) { return; } try { this.isRefreshingToken = true; const response = await this.client.post('/oauth/token', { grant_type: 'client_credentials', audience: this.baseUrl, client_id: this.clientConfig.clientId, client_secret: this.clientConfig.clientSecret, }); this.tokenData = response.data; this.tokenGenerationTime = Date.now(); } finally { this.isRefreshingToken = false; } } setupInterceptors(client) { client.interceptors.response.use((response) => { return response; }, (error) => { if (error.response) { const errorData = error.response.data || {}; const details = Object.entries(errorData) .filter(([key]) => key !== 'message') .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}); const errorResponse = { data: null, success: false, error: { code: String(error.response.status), message: errorData.message || 'An unknown error occurred', details: Object.keys(details).length > 0 ? details : undefined, }, }; return Promise.reject(errorResponse); } const networkError = { data: null, success: false, error: { code: 'NETWORK_ERROR', message: error.message || 'Network error occurred', }, }; return Promise.reject(networkError); }); client.interceptors.request.use(async (config) => { // Refresh the token if it's about to expire, with a 5 minute buffer const shouldRefreshToken = !this.isRefreshingToken && (!this.tokenData || Date.now() > this.tokenGenerationTime + this.tokenData.expires_in * 1000 - 5 * 60 * 1000); if (shouldRefreshToken) { await this.refreshToken(); } if (this.tokenData) { config.headers.Authorization = `Bearer ${this.tokenData.access_token}`; } return config; }); } getClient(path) { return path.startsWith('/points-api') ? this.pointsClient : this.client; } async get(path, config) { const client = this.getClient(path); const response = await client.get(path, config); return { data: response.data, success: true, }; } async post(path, data, config) { const client = this.getClient(path); const response = await client.post(path, data, config); return { data: response.data, success: true, }; } async put(path, data, config) { const client = this.getClient(path); const response = await client.put(path, data, config); return { data: response.data, success: true, }; } async patch(path, data, config) { const client = this.getClient(path); const response = await client.patch(path, data, config); return { data: response.data, success: true, }; } async delete(path, config) { const client = this.getClient(path); const response = await client.delete(path, config); return { data: response.data, success: true, }; } /** * Gets the audience URL for the current environment * @returns {string} The audience URL */ getAudienceUrl() { return this.baseUrl; } /** * Gets the current environment (sandbox or production) * @returns {Environment} The current environment */ getEnvironment() { return this.environment; } /** * Validates if the current environment matches the required environment * @param {Environment} requiredEnvironment - The environment that is required for the operation * @param {string} operationName - The name of the operation being validated * @returns {ApiResponse<T> | null} Returns an error response if validation fails, null if validation passes */ validateEnvironment(requiredEnvironment, operationName) { if (this.environment !== requiredEnvironment) { return { data: null, success: false, error: { code: 'ENVIRONMENT_ERROR', message: `The ${operationName} method is only available in the ${requiredEnvironment} environment`, details: { environment: this.environment, required_environment: requiredEnvironment, }, }, }; } return null; } } exports.HttpClient = HttpClient;