Skip to content

Commit

Permalink
update axios implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Iulian Masar committed Jan 13, 2024
1 parent f9aff81 commit fcf184f
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 214 deletions.
136 changes: 56 additions & 80 deletions lib/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,14 @@ var Api = function (config) {
axios.defaults.baseURL = config.baseUrl;
// response timeout
axios.defaults.timeout = config.responseTimeout;
// request timeout
axios.defaults.signal = AbortSignal.timeout(config.connectionTimeout);

this.errorHandler = config.errorHandler;

this.rateLimits = [];

/**
* Add default request configuration options
*/
// Add default request configuration options
_.extend(this.requestOptions, {
/**
* Path options are replacing the ${placeholders} from apiMethods
*/
// Path options are replacing the ${placeholders} from apiMethods
path: {
clientId: config.clientId,
apiVersion: config.apiVersion
Expand All @@ -50,9 +44,7 @@ var Api = function (config) {
}
});

/**
* Adds the services to API object
*/
// Adds the services to API object
this._servicesLoader();

return this;
Expand All @@ -74,7 +66,7 @@ Api.prototype = {
_getOptions: function (callback, options, params) {
var finalOptions = options || ((_.isObject(callback) && !_.isFunction(callback)) ? callback : {});
if (params) {
finalOptions = _.extend({}, options, params);
finalOptions = _.extend({}, finalOptions, params);
}

return finalOptions;
Expand All @@ -94,24 +86,20 @@ Api.prototype = {
this.config.logClass(method, options);
}

/**
* If data has parse method, call it before passing data
*/
// If data has parse method, call it before passing data
if (options.data && options.data instanceof this.models.EntityBase) {
options.data = this.buildRequestData(options.data);
} else if (options.data && options.data.toJSON) {
options.data = options.data.toJSON();
}
var self = this;

/**
* If there's no OAuthKey, request one
*/
// If there's no OAuthKey, request one
if (!this.requestOptions.headers.Authorization || this.isExpired()) {
return new Promise(function(resolve, reject){
return new Promise(function (resolve, reject) {
self.authorize()
.then(function(){
self.method.call(self, method, function(data, response){
.then(function () {
self.method.call(self, method, function (data, response) {
// Check if we have to wrap data into a model
if (_.isFunction(callback)) {
callback(data, response);
Expand All @@ -124,21 +112,15 @@ Api.prototype = {
});
}

/**
* Extend default request options with custom ones, if present
*/
// Extend default request options with custom ones, if present
var requestOptions = _.extend({}, this.requestOptions, options);

/**
* If we have custom headers and they don't contain Content-Type, we have to prevent them to override Authentication header
*/
// If we have custom headers and they don't contain Content-Type, we have to prevent them to override Authentication header
if (options && options.headers && options.headers['Content-Type'] === undefined) {
_.extend(requestOptions.headers, this.requestOptions.headers, options.headers);
}

/**
* Append the path placeholders in order to build the proper url for the request
*/
// Append the path placeholders in order to build the proper url for the request
if (options && options.path) {
_.extend(requestOptions.path, this.requestOptions.path, options.path);
}
Expand All @@ -157,11 +139,14 @@ Api.prototype = {
var url;
var methodType;

// Allow direct requests: api.method('post', function(){ ... });
if (['post', 'get', 'put'].indexOf(method) === -1) {
// predefined requests
url = apiMethods[method][0];
url = replaceUrl(url, requestOptions.path);
methodType = apiMethods[method][1];
} else {
// manual requests
if (!requestOptions.url) {
throw new Error('Url must be specified when doing a manual request');
}
Expand All @@ -174,69 +159,60 @@ Api.prototype = {
}

var resolveWithFullResponse = requestOptions.resolveWithFullResponse || false;
var abortSignal = AbortSignal.timeout(self.config.connectionTimeout)

axios({
method: methodType,
url: url,
data: requestOptions.data,
headers: requestOptions.headers,
params: requestOptions.parameters
params: requestOptions.parameters,
signal: abortSignal
})
.then(function (response) {
var resolveArgument = (resolveWithFullResponse) ? _.extend(response, {body: response.data}) : response.data;

if (response.status === 401) {
// Authorization error
self.authorize()
.then(function(){
self.method.call(self, method, function(data, response){
var resolveArgument = (resolveWithFullResponse) ? response : data;
// Check if we have to wrap data into a model
if (_.isFunction(callback)) {
callback(resolveArgument, response);
}
}, requestOptions)
.then(resolve)
.catch(reject);
})
.catch(reject);
} else if (response.statusCode < 200 || response.statusCode > 299) {
if (_.isFunction(callback)) {
callback(resolveArgument, response);
}
self.errorHandler(response.statusText, response.data);
reject(resolveArgument, response);
} else {
setRateLimits(self, response.headers);
if (['post', 'get', 'put'].indexOf(method) === -1) {
/**
* Add raw response data under 'data' property of the object and at the root of the object,
* when the request is not "manual" - post, get, put
*/
_.extend(requestOptions.data, response.data, {data: response.data});

/**
* Check if we have to instantiate returned data
*/
if (requestOptions.dataClass && !resolveWithFullResponse) {
if (_.isArray(response.data)) {
resolveArgument = _.map(response.data, function (dataItem) {
return new requestOptions.dataClass(dataItem);
});
} else {
resolveArgument = new requestOptions.dataClass(response.data);
}
var resolveArgument = (resolveWithFullResponse) ?
_.extend(response, {body: response.data}) : response.data;

setRateLimits(self, response.headers);

// For predefined requests:
// Add raw response data under 'data' property of the object and at the root of the object
if (['post', 'get', 'put'].indexOf(method) === -1) {
_.extend(requestOptions.data, response.data, {data: response.data});

// Check if we have to instantiate returned data
if (requestOptions.dataClass && !resolveWithFullResponse) {
if (_.isArray(response.data)) {
resolveArgument = _.map(response.data, function (dataItem) {
return new requestOptions.dataClass(dataItem);
});
} else {
resolveArgument = new requestOptions.dataClass(response.data);
}
}
if (_.isFunction(callback)) {
callback(resolveArgument, response);
}
resolve(resolveArgument);
}

if (_.isFunction(callback)) {
callback(resolveArgument, response);
}

resolve(resolveArgument);
})
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
.catch(function (error) {
// if (error.code === "ERR_CANCELED" && abortSignal.aborted) {
// self.errorHandler(error.code, "Request timed out");
// }

var resolveArgument = (resolveWithFullResponse) ?
_.extend(error.response, {body: error.response.data}) : error.response.data;

if (_.isFunction(callback)) {
callback(resolveArgument, error.response);
}

self.errorHandler(error.message, error.response.data);
reject(error.response.data);
});
});
},
Expand Down Expand Up @@ -317,7 +293,7 @@ Api.prototype = {
}
})
.catch(function (error) {
reject(error.response.data);
reject(error.response ? error.response.data : error.message);
});
});
},
Expand Down
2 changes: 1 addition & 1 deletion lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ module.exports = {
/**
* Set the response timeout limit (in milliseconds)
*/
responseTimeout: 80000,
responseTimeout: 30000,

/**
* Mangopay REST API version - will be appended in the front of the endpoints
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"scripts": {
"start": "npm install -g mocha && npm install -g jsdox && npm install",
"documentation": "jsdox -t docs/templates/ -i README -o docs lib/services",
"test": "mocha 'test/**/*.js' --recursive --timeout 60000 --ui bdd",
"test": "mocha 'test/**/*.js' --recursive --timeout 30000 --ui bdd",
"test:types": "dtslint ./typings"
}
}
8 changes: 2 additions & 6 deletions test/services/CardRegistrations.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,14 +208,10 @@ describe('Card Registrations', function () {
var failedResponse;

before(function(done) {
api.CardRegistrations.create(newInvalidCardRegistration)
.then(function(){
done('Creating invalid card registration did not failed the promise');
})
.catch(function(data, response){
api.CardRegistrations.create(newInvalidCardRegistration, function (data) {
failedResponse = data;
done();
});
});
});

it('should fail', function () {
Expand Down
98 changes: 29 additions & 69 deletions test/services/InstantConversions.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,42 +89,22 @@ describe('InstantConversions', function () {
IpAddress: "2001:0620:0000:0000:0211:24FF:FE80:C12C"
}, function (data, response) {
api.Wallets.create(creditedWallet).then(function () {
api.Transfers.create({
instantConversion = {
AuthorId: john.Id,
Tag: 'DefaultTag',
CreditedUserId: john.Id,
DebitedFunds: {
Currency: 'GBP',
Amount: 1
CreditedWalletId: creditedWallet.Id,
DebitedWalletId: debitedWallet.Id,
CreditedFunds: {
Currency: 'GBP'
},
Fees: {
Currency: 'GBP',
Amount: 0
DebitedFunds: {
Currency: 'EUR',
Amount: 79
},
DebitedWalletId: debitedWallet.Id,
CreditedWalletId: creditedWallet.Id
}, function (data, response) {
api.Wallets.get(debitedWallet.Id).then(function (data) {
debitedWallet = data;

instantConversion = {
AuthorId: john.Id,
CreditedWalletId: creditedWallet.Id,
DebitedWalletId: debitedWallet.Id,
CreditedFunds: {
Currency: 'GBP'
},
DebitedFunds: {
Currency: 'EUR',
Amount: 79
},
Tag: 'Instant conversion test'
};
api.InstantConversions.createInstantConversion(instantConversion, function (data, response) {
instantConversion = data;
done();
});
});
Tag: 'Instant conversion test'
};
api.InstantConversions.createInstantConversion(instantConversion, function (data, response) {
instantConversion = data;
done();
});
});
});
Expand Down Expand Up @@ -203,44 +183,24 @@ describe('InstantConversions', function () {
IpAddress: "2001:0620:0000:0000:0211:24FF:FE80:C12C"
}, function (data, response) {
api.Wallets.create(creditedWallet).then(function () {
api.Transfers.create({
instantConversion = {
AuthorId: john.Id,
Tag: 'DefaultTag',
CreditedUserId: john.Id,
DebitedFunds: {
Currency: 'GBP',
Amount: 1
CreditedWalletId: creditedWallet.Id,
DebitedWalletId: debitedWallet.Id,
CreditedFunds: {
Currency: 'GBP'
},
Fees: {
Currency: 'GBP',
Amount: 0
DebitedFunds: {
Currency: 'EUR',
Amount: 79
},
DebitedWalletId: debitedWallet.Id,
CreditedWalletId: creditedWallet.Id
}, function (data, response) {
api.Wallets.get(debitedWallet.Id).then(function (data) {
debitedWallet = data;

instantConversion = {
AuthorId: john.Id,
CreditedWalletId: creditedWallet.Id,
DebitedWalletId: debitedWallet.Id,
CreditedFunds: {
Currency: 'GBP'
},
DebitedFunds: {
Currency: 'EUR',
Amount: 79
},
Tag: 'Instant conversion test'
};
api.InstantConversions.createInstantConversion(instantConversion, function (data, response) {
instantConversion = data;
api.InstantConversions.getInstantConversion(instantConversion.Id, function (data, response) {
returnedInstantConversion = data;
done();
});
});
Tag: 'Instant conversion test'
};
api.InstantConversions.createInstantConversion(instantConversion, function (data, response) {
instantConversion = data;
api.InstantConversions.getInstantConversion(instantConversion.Id, function (data, response) {
returnedInstantConversion = data;
done();
});
});
});
Expand All @@ -258,4 +218,4 @@ describe('InstantConversions', function () {
expect(returnedInstantConversion.Status).to.equal('SUCCEEDED');
});
});
});
});
Loading

0 comments on commit fcf184f

Please sign in to comment.