@airwallex/node-sdk
Version:
Airwallex Node.js SDK
214 lines • 9.78 kB
JavaScript
"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