requestretry
Advanced tools
Comparing version 1.5.0 to 1.6.0
# Change Log | ||
## [v1.5.0](https://github.com/fgribreau/node-request-retry/tree/v1.5.0) (2015-09-24) | ||
[Full Changelog](https://github.com/fgribreau/node-request-retry/compare/v1.4.1...v1.5.0) | ||
**Closed issues:** | ||
- Is there anyway to configure maxAttempts to Infinity \(Try Until available\) [\#17](https://github.com/FGRibreau/node-request-retry/issues/17) | ||
**Merged pull requests:** | ||
- Add attempts property for retry attempt info [\#16](https://github.com/FGRibreau/node-request-retry/pull/16) ([bdeitte](https://github.com/bdeitte)) | ||
## [v1.4.1](https://github.com/fgribreau/node-request-retry/tree/v1.4.1) (2015-09-21) | ||
@@ -4,0 +16,0 @@ |
83
index.js
@@ -10,2 +10,3 @@ 'use strict'; | ||
*/ | ||
var when = require('when'); | ||
var request = require('request'); | ||
@@ -19,7 +20,34 @@ var _ = require('fg-lodash'); | ||
retryDelay: 5000, // wait for 5s before trying again | ||
fullResponse: true, // resolve promise with the full response object | ||
promiseFactory: defaultPromiseFactory // Function to use a different promise implementation library | ||
}; | ||
function Request(options, f, maxAttempts, retryDelay) { | ||
this.maxAttempts = maxAttempts; | ||
this.retryDelay = retryDelay; | ||
// Default promise factory which use bluebird | ||
function defaultPromiseFactory(resolver) { | ||
return when.promise(resolver); | ||
} | ||
/** | ||
* It calls the promiseFactory function passing it the resolver for the promise | ||
* | ||
* @param {Object} requestInstance - The Request Retry instance | ||
* @param {Function} promiseFactoryFn - The Request Retry instance | ||
* @return {Object} - The promise instance | ||
*/ | ||
function makePromise(requestInstance, promiseFactoryFn) { | ||
// Resolver function wich assigns the promise (resolve, reject) functions | ||
// to the requestInstance | ||
function Resolver(resolve, reject) { | ||
this._resolve = resolve; | ||
this._reject = reject; | ||
} | ||
return promiseFactoryFn(Resolver.bind(requestInstance)); | ||
} | ||
function Request(options, f, retryConfig) { | ||
this.maxAttempts = retryConfig.maxAttempts; | ||
this.retryDelay = retryConfig.retryDelay; | ||
this.fullResponse = retryConfig.fullResponse; | ||
this.attempts = 0; | ||
@@ -39,5 +67,25 @@ | ||
this.f = _.once(f); | ||
this._timeout = null; | ||
this._req = null; | ||
this._callback = _.isFunction(f) ? _.once(f) : null; | ||
// create the promise only when no callback was provided | ||
if (!this._callback) { | ||
this._promise = makePromise(this, retryConfig.promiseFactory); | ||
} | ||
this.reply = function requestRetryReply(err, response, body) { | ||
if (this._callback) { | ||
return this._callback(err, response, body); | ||
} | ||
if (err) { | ||
return this._reject(err); | ||
} | ||
// resolve with the full response or just the body | ||
response = this.fullResponse ? response : body; | ||
this._resolve(response); | ||
}; | ||
} | ||
@@ -60,3 +108,3 @@ | ||
return this.f(err, response, body); | ||
this.reply(err, response, body); | ||
}.bind(this)); | ||
@@ -70,20 +118,25 @@ }; | ||
clearTimeout(this._timeout); | ||
this.f(new Error('Aborted')); | ||
this.reply(new Error('Aborted')); | ||
}; | ||
// expose request methods from RequestRetry | ||
['end', 'on', 'emit', 'once', 'setMaxListeners', 'start', 'removeListener', 'pipe', 'write'].forEach(function (methodName) { | ||
Request.prototype[methodName] = makeGateway(methodName); | ||
['end', 'on', 'emit', 'once', 'setMaxListeners', 'start', 'removeListener', 'pipe', 'write'].forEach(function (requestMethod) { | ||
Request.prototype[requestMethod] = function exposedRequestMethod () { | ||
return this._req[requestMethod].apply(this._req, arguments); | ||
}; | ||
}); | ||
function makeGateway(methodName) { | ||
return function () { | ||
return this._req[methodName].apply(this._req, Array.prototype.slice.call(arguments)); | ||
// expose promise methods | ||
['then', 'catch', 'finally', 'fail', 'done'].forEach(function (promiseMethod) { | ||
Request.prototype[promiseMethod] = function exposedPromiseMethod () { | ||
if (this._callback) { | ||
throw new Error('A callback was provided but waiting a promise, use only one pattern'); | ||
} | ||
return this._promise[promiseMethod].apply(this._promise, arguments); | ||
}; | ||
} | ||
}); | ||
function Factory(options, f) { | ||
f = _.isFunction(f) ? f : _.noop; | ||
var retry = _(options || {}).defaults(DEFAULTS).pick(Object.keys(DEFAULTS)).value(); | ||
var req = new Request(options, f, retry.maxAttempts, retry.retryDelay); | ||
var retryConfig = _(options || {}).defaults(DEFAULTS).pick(Object.keys(DEFAULTS)).value(); | ||
var req = new Request(options, f, retryConfig); | ||
req._tryUntilFail(); | ||
@@ -90,0 +143,0 @@ return req; |
{ | ||
"name": "requestretry", | ||
"description": "request-retry wrap nodejs request to retry http(s) requests in case of error", | ||
"version": "1.5.0", | ||
"version": "1.6.0", | ||
"author": { | ||
@@ -26,8 +26,14 @@ "name": "Francois-Guillaume Ribreau", | ||
"fg-lodash": "0.0.2", | ||
"request": "^2.62.x" | ||
"request": "^2.62.x", | ||
"when": "~3.7.5" | ||
}, | ||
"devDependencies": { | ||
"chai": "^3.2.0", | ||
"mocha": "^2.3.0" | ||
"kew": "~0.7.0", | ||
"mocha": "^2.3.0", | ||
"nock": "~3.1.0", | ||
"q": "~1.4.1", | ||
"rsvp": "~3.1.0", | ||
"bluebird": "~3.0.5" | ||
} | ||
} |
# Request-retry [](https://david-dm.org/FGRibreau/node-request-retry) [](https://drone.io/github.com/FGRibreau/node-request-retry/latest) [](https://www.npmjs.com/package/requestretry) | ||
[](https://npmjs.org/package/requestretry) | ||
  | ||
@@ -9,4 +9,6 @@ When the connection fails with one of `ECONNRESET`, `ENOTFOUND`, `ESOCKETTIMEDOUT`, `ETIMEDOUT`, `ECONNREFUSED`, `EHOSTUNREACH`, `EPIPE`, `EAI_AGAIN` or when an HTTP 5xx error occurrs, the request will automatically be re-attempted as these are often recoverable errors and will go away on retry. | ||
Request-retry is a drop-in replacement for [request](https://github.com/mikeal/request) but adds two new options `maxAttempts` and `retryDelay`. It also adds one property to the response, `attempts`. | ||
Request-retry is a drop-in replacement for [request](https://github.com/mikeal/request) but adds two new options `maxAttempts` and `retryDelay`. It also adds one property to the response, `attempts`. It supports callbacks or promises. | ||
### With callbacks | ||
```javascript | ||
@@ -19,3 +21,3 @@ var request = require('requestretry'); | ||
// The above parameters are specific to Request-retry | ||
// The below parameters are specific to request-retry | ||
maxAttempts: 5, // (default) try 5 times | ||
@@ -32,2 +34,57 @@ retryDelay: 5000, // (default) wait for 5s before trying again | ||
### With promises | ||
When you're using promises, you can pass the two following options: | ||
- `fullResponse` _(default true)_ - To resolve the promise with the full response or just the body | ||
- `promiseFactory` _(default bluebird)_ - A function to allow the usage of a different promise implementation library | ||
```javascript | ||
request({ | ||
url: 'https://api.domain.com/v1/a/b' | ||
json:true, | ||
fullResponse: true // (default) To resolve the promise with the full response or just the body | ||
}) | ||
.then(function (response) { | ||
// response = The full response object or just the body | ||
}) | ||
.catch(function(error) { | ||
// error = Any occurred error | ||
}) | ||
``` | ||
**Using `promiseFactory` option to use a different promise implementation library** | ||
```javascript | ||
// See the tests for different libraries usage examples | ||
/** | ||
* @param {Function} resolver The promise resolver function | ||
* @return {Object} The promise instance | ||
*/ | ||
function customPromiseFactory(resolver) { | ||
// With when.js | ||
return require('when').promise(resolver); | ||
// With RSVP.js | ||
var Promise = require('rsvp').Promise; | ||
return new Promise(resolver); | ||
} | ||
request({ | ||
url: 'https://api.domain.com/v1/a/b' | ||
json:true, | ||
// Custom promise factory function | ||
promiseFactory: customPromiseFactory | ||
}) | ||
.then(function (response) { | ||
// response = The full response object or just the body | ||
}) | ||
.catch(function(error) { | ||
// error = Any occurred error | ||
}) | ||
``` | ||
## Installation | ||
@@ -68,8 +125,4 @@ | ||
## Todo | ||
- Tests | ||
## [Changelog](CHANGELOG.md) | ||
Copyright 2014, [Francois-Guillaume Ribreau](http://fgribreau.com) (npm@fgribreau.com) |
30957
19
399
125
3
7
+ Addedwhen@~3.7.5
+ Addedwhen@3.7.8(transitive)