UNPKG

@airwallex/node-sdk

Version:

Airwallex Node.js SDK

214 lines 9.78 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.apiClient = exports.APIClient = void 0; const airwallex_core_1 = require("../airwallex.core"); const errors_1 = require("../common/errors"); const analyticsClient_1 = require("./analyticsClient"); const httpClient_1 = require("./httpClient"); class APIClient { constructor() { this.initialized = false; } initialize(airwallex, httpClient) { var _a; if (this.initialized && ((_a = this.airwallex) === null || _a === void 0 ? void 0 : _a.config) == airwallex.config) { return; } this.airwallex = airwallex; this.client = httpClient || new httpClient_1.HttpClientImpl(airwallex.basePath, airwallex.config); this.setInterceptors(); this.initialized = true; } getClient() { if (!this.client) { throw new Error('Initialize Airwallex(...) first'); } return this.client; } getAirwallex() { if (!this.airwallex) { throw new Error('Initialize Airwallex(...) first'); } return this.airwallex; } setInterceptors() { this.getClient().setRequestInterceptor(this.handleRequest.bind(this), this.handleRequestError.bind(this)); this.getClient().setResponseInterceptor(this.handleResponse.bind(this), this.handleResponseError.bind(this)); } async handleRequest(config) { var _a; if (config.url !== '/api/v1/authentication/login') { await this.getAirwallex().login(); } config.headers = config.headers || {}; config.headers['Authorization'] = this.getAirwallex().token; config.headers['x-api-version'] = this.getAirwallex().config.apiVersion; if (((_a = config.method) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'get') { config.params = { ...config.params, _t: Date.now(), }; } return config; } handleRequestError(error) { return Promise.reject(new errors_1.ApiError('Request configuration error', { data: error.message || error, })); } handleResponse(response) { return response; } async handleResponseError(error) { var _a; const headers = (_a = error.response) === null || _a === void 0 ? void 0 : _a.headers; const httpCode = error.code; if (httpCode === 'ECONNABORTED') { throw new errors_1.TimeoutError('Request timeout', { httpCode, data: error.message, headers }); } if (httpCode === 'ECONNREFUSED' || httpCode === 'ENOTFOUND') { throw new errors_1.NetworkError(`Network error: ${httpCode}`, { httpCode, data: error.message, headers }); } if (error.response) { const data = error.response.data; const status = error.response.status; let message = (data === null || data === void 0 ? void 0 : data.message) || (data === null || data === void 0 ? void 0 : data.error_description) || (data === null || data === void 0 ? void 0 : data.error) || error.response.statusText || `Server error: ${status}`; if ((data === null || data === void 0 ? void 0 : data.details) && Array.isArray(data.details) && data.details.length > 0) { message += ` - ${data.details.join(', ')}`; } if (status === 401) { throw new errors_1.AuthenticationError(message, { httpCode, status, data, headers }); } else if (status === 403) { throw new errors_1.AuthorizationError(message, { httpCode, status, data, headers }); } else if (status === 404) { throw new errors_1.NotFoundError(message, { httpCode, status, data, headers }); } else if (status === 429) { throw new errors_1.RateLimitError(message, { httpCode, status, data, headers }); } else if (status === 400) { throw new errors_1.ValidationError(message, { httpCode, status, data, headers }); } else if (status >= 500) { throw new errors_1.ServerError(message, { httpCode, status, data, headers }); } else { throw new errors_1.ApiError(message, { httpCode, status, data, headers }); } } else if (error.request) { throw new errors_1.NetworkError('No response from server', { httpCode, data: error.request, headers }); } else { throw new errors_1.ApiError(error.message || 'Unknown error', { httpCode, data: error.request, headers }); } } async request(method, url, data, config) { const startTime = Date.now(); try { const response = await this._request(method, url, data, config); const duration = Date.now() - startTime; airwallex_core_1.analyticsClient === null || airwallex_core_1.analyticsClient === void 0 ? void 0 : airwallex_core_1.analyticsClient.sendTelemetry(analyticsClient_1.ANALYTICS_CODES.latency, { duration: duration, url, config: config }); return response; } catch (error) { const duration = Date.now() - startTime; airwallex_core_1.analyticsClient === null || airwallex_core_1.analyticsClient === void 0 ? void 0 : airwallex_core_1.analyticsClient.sendTelemetry(analyticsClient_1.ANALYTICS_CODES.latency, { duration: duration, url, config: config }); if (error instanceof errors_1.ApiError) { const response = await this.retryRequest(error, method, url, data, config); if (response) { return response; } } throw error; } } shouldRetry(error, method, url, data) { if (!(0, errors_1.isRetryableError)(error)) { return false; } if (method.toLowerCase() === 'get') { return true; } if (method.toLowerCase() === 'post' && (url === '/api/v1/authentication/login' || (data === null || data === void 0 ? void 0 : data.request_id))) { return true; } return false; } async retryRequest(error, method, url, data, config) { if (!this.shouldRetry(error, method, url, data)) { return undefined; } const maxAttempts = this.getAirwallex().config.retry || 1; let attemptNumber = 0; const baseDelay = this.getAirwallex().config.retryDelay || 300; while (attemptNumber < maxAttempts) { const delay = baseDelay * Math.pow(2, attemptNumber) * (0.75 + Math.random() * 0.5); await new Promise((resolve) => setTimeout(resolve, delay)); attemptNumber++; const startTime = Date.now(); try { const response = await this._request(method, url, config, data); const duration = Date.now() - startTime; airwallex_core_1.analyticsClient === null || airwallex_core_1.analyticsClient === void 0 ? void 0 : airwallex_core_1.analyticsClient.sendTelemetry(analyticsClient_1.ANALYTICS_CODES.latency, { duration: duration, url, config: config }); return response; } catch (error) { const duration = Date.now() - startTime; airwallex_core_1.analyticsClient === null || airwallex_core_1.analyticsClient === void 0 ? void 0 : airwallex_core_1.analyticsClient.sendTelemetry(analyticsClient_1.ANALYTICS_CODES.latency, { duration: duration, url, config: config }); if (!(error instanceof errors_1.ApiError) || !this.shouldRetry(error, method, url, data)) { throw error; } } } return undefined; } async _request(method, url, data, config) { switch (method) { case 'GET': return await this.getClient().get(url, config); case 'POST': return await this.getClient().post(url, data, config); case 'PUT': return await this.getClient().put(url, data, config); case 'PATCH': return await this.getClient().patch(url, data, config); case 'DELETE': return await this.getClient().delete(url, config); default: throw new errors_1.ApiError(`Invalid HTTP method: ${method}`); } } getRequestConfig(requestConfig) { return { headers: requestConfig.headers, timeout: requestConfig.timeout || this.getAirwallex().config.timeout, responseType: requestConfig.responseType || 'json', params: requestConfig.params, }; } async get(url, requestConfig) { return this.request('GET', url, undefined, this.getRequestConfig(requestConfig)); } async post(url, requestConfig, data = {}) { return this.request('POST', url, data, this.getRequestConfig(requestConfig)); } async put(url, requestConfig, data = {}) { return this.request('PUT', url, data, this.getRequestConfig(requestConfig)); } async patch(url, requestConfig, data = {}) { return this.request('PATCH', url, data, this.getRequestConfig(requestConfig)); } async delete(url, requestConfig) { return this.request('DELETE', url, undefined, this.getRequestConfig(requestConfig)); } } exports.APIClient = APIClient; exports.apiClient = new APIClient(); //# sourceMappingURL=apiClient.js.map