diff --git a/.gitignore b/.gitignore
index c81b8d3..1ed0588 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
node_modules
npm-debug.log
.env
+coverage
diff --git a/README.md b/README.md
index 4031ff5..e3e2e7d 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,10 @@
-# Request-retry
-[![Deps]( https://img.shields.io/david/FGRibreau/node-request-retry.svg)](https://david-dm.org/FGRibreau/node-request-retry) [![Build Status]( https://img.shields.io/circleci/project/FGRibreau/node-request-retry.svg)](https://drone.io/github.com/FGRibreau/node-request-retry/latest) [![Downloads](http://img.shields.io/npm/dm/requestretry.svg)](https://www.npmjs.com/package/requestretry) ![extra](https://img.shields.io/badge/actively%20maintained-yes-ff69b4.svg)
+
+
request-retry - HTTP(s) request retry on recoverable errors.
+
+
+=========================================
+
+[![Build Status](https://img.shields.io/circleci/project/FGRibreau/node-request-retry.svg)](https://circleci.com/gh/FGRibreau/node-request-retry/) [![Coverage Status](https://img.shields.io/coveralls/FGRibreau/node-request-retry/master.svg)](https://coveralls.io/github/FGRibreau/node-request-retry?branch=master) [![Deps]( https://img.shields.io/david/FGRibreau/node-request-retry.svg)](https://david-dm.org/FGRibreau/node-request-retry) [![NPM version](https://img.shields.io/npm/v/requestretry.svg)](http://badge.fury.io/js/requestretry) [![Downloads](http://img.shields.io/npm/dm/requestretry.svg)](https://www.npmjs.com/package/requestretry) ![extra](https://img.shields.io/badge/actively%20maintained-yes-ff69b4.svg)
![NPM](https://nodei.co/npm/requestretry.png?downloadRank=true) ![NPM](https://nodei.co/npm-dl/requestretry.png?months=3&height=2)
@@ -121,17 +126,21 @@ You can use the `defaults` method to provide default options like so:
var request = require('requestretry').defaults({ json: true, retryStrategy: myRetryStrategy });
```
-## Convenience methods
-
-As with `request`, several helpers are provided for various HTTP methods:
-
-* `request.get(url)` - same as `request(options, callback)` or `request(options)`.
-* `request.head(url)` - same as `request(options, callback)` or `request(options)`, but it defaults `options.method` to `HEAD`.
-* `request.post(url)` - same as `request(options, callback)` or `request(options)`, but it defaults `options.method` to `POST`.
-* `request.put(url)` - same as `request(options, callback)` or `request(options)`, but it defaults `options.method` to `PUT`.
-* `request.patch(url)` - same as `request(options, callback)` or `request(options)`, but it defaults `options.method` to `PATCH`.
-* `request.del(url)` - same as `request(options, callback)` or `request(options)`, but it defaults `options.method` to `DELETE`.
-* `request.delete(url)` - same as `request(options, callback)` or `request(options)`, but it defaults `options.method` to `DELETE`.
+## API surface
+
+As with `request`, several helpers are provided for various HTTP methods and usage:
+
+* `request(options [, callback])`.
+* `request(url [, callback])` - same as `request(options [, callback])`.
+* `request(url, options [, callback])` - same as `request(options [, callback])`.
+* `request.get(url [, callback])` - same as `request(options [, callback])`, defaults `options.method` to `GET`.
+* `request.get(url, options [, callback])` - same as `request(options [, callback])`, defaults `options.method` to `GET`.
+* `request.head(url)` - same as `request(options [, callback])`, defaults `options.method` to `HEAD`.
+* `request.post(url)` - same as `request(options [, callback])`, defaults `options.method` to `POST`.
+* `request.put(url)` - same as `request(options [, callback])`, defaults `options.method` to `PUT`.
+* `request.patch(url)` - same as `request(options [, callback])`, defaults `options.method` to `PATCH`.
+* `request.del(url)` - same as `request(options [, callback])`, defaults `options.method` to `DELETE`.
+* `request.delete(url)` - same as `request(options [, callback])`, defaults `options.method` to `DELETE`.
## [Changelog](CHANGELOG.md)
diff --git a/circle.yml b/circle.yml
new file mode 100644
index 0000000..e3c2322
--- /dev/null
+++ b/circle.yml
@@ -0,0 +1,10 @@
+machine:
+ node:
+ version: 6.3
+ environment:
+ COVERALLS_SERVICE_NAME: circleci
+
+test:
+ override:
+ - npm run test
+ - npm run send-coverage
diff --git a/index.js b/index.js
index 28ec500..e623b4f 100644
--- a/index.js
+++ b/index.js
@@ -10,9 +10,8 @@
var extend = require('extend');
var when = require('when');
var request = require('request');
-var _ = require('fg-lodash');
var RetryStrategies = require('./strategies');
-
+var _ = require('lodash');
var DEFAULTS = {
maxAttempts: 5, // try 5 times
@@ -45,7 +44,29 @@ function makePromise(requestInstance, promiseFactoryFn) {
return promiseFactoryFn(Resolver.bind(requestInstance));
}
-function Request(options, f, retryConfig) {
+function Request(url, options, f, retryConfig) {
+ // ('url')
+ if(_.isString(url)){
+ // ('url', f)
+ if(_.isFunction(options)){
+ f = options;
+ }
+
+ if(!_.isObject(options)){
+ options = {};
+ }
+
+ // ('url', {object})
+ options.url = url;
+ }
+
+ if(_.isObject(url)){
+ if(_.isFunction(options)){
+ f = options;
+ }
+ options = url;
+ }
+
this.maxAttempts = retryConfig.maxAttempts;
this.retryDelay = retryConfig.retryDelay;
this.fullResponse = retryConfig.fullResponse;
@@ -132,24 +153,39 @@ Request.prototype.abort = function () {
};
});
-function Factory(options, f) {
- var retryConfig = _(options || {}).defaults(DEFAULTS).pick(Object.keys(DEFAULTS)).value();
- var req = new Request(options, f, retryConfig);
+function Factory(url, options, f) {
+ var retryConfig = _.chain(_.isObject(url) ? url : options || {}).defaults(DEFAULTS).pick(Object.keys(DEFAULTS)).value();
+ var req = new Request(url, options, f, retryConfig);
req._tryUntilFail();
return req;
}
// adds a helper for HTTP method `verb` to object `obj`
function makeHelper(obj, verb) {
- obj[verb] = function helper(options, f) {
- if (typeof options === 'object') {
- options.method = verb.toUpperCase();
- } else if (typeof options === 'string') {
- options = {
- method: verb.toUpperCase(),
- uri: options
- };
+ obj[verb] = function helper(url, options, f) {
+ // ('url')
+ if(_.isString(url)){
+ // ('url', f)
+ if(_.isFunction(options)){
+ f = options;
+ }
+
+ if(!_.isObject(options)){
+ options = {};
+ }
+
+ // ('url', {object})
+ options.url = url;
}
+
+ if(_.isObject(url)){
+ if(_.isFunction(options)){
+ f = options;
+ }
+ options = url;
+ }
+
+ options.method = verb.toUpperCase();
return obj(options, f);
};
}
diff --git a/package.json b/package.json
index 3a582e6..10a0648 100644
--- a/package.json
+++ b/package.json
@@ -18,27 +18,40 @@
},
"main": "index.js",
"scripts": {
- "test": "mocha test",
+ "test": "mocha -t 1000 -R spec $(find test -name '*.test.js')",
+ "test-watch": "mocha -t 100000 -R min -w $(find test -name '*.test.js')",
+ "test-coverage": "nyc --all --statements=100 --lines=100 --functions=100 --branches=100 --check-coverage --reporter=lcov --reporter=cobertura --report-dir=coverage -- mocha -R spec -t 100000 $(find test -name '*.test.js')",
+ "send-coverage": "cat ./coverage/lcov.info | coveralls",
"update": "updtr",
"changelog-init": "conventional-changelog -i CHANGELOG.md -s -r 0",
"changelog": "conventional-changelog -i CHANGELOG.md -s",
"changelog-git": "npm run changelog && git add CHANGELOG.md && git commit -m 'docs(changelog): updated' && git push origin master"
},
"license": "MIT",
+ "nyc": {
+ "exclude": [
+ "node_modules",
+ "dist",
+ "coverage",
+ "webpack.config.js",
+ "test"
+ ]
+ },
"dependencies": {
"extend": "^3.0.0",
- "fg-lodash": "0.0.2",
+ "lodash": "^4.15.0",
"request": "^2.74.0",
- "when": "~3.7.5"
+ "when": "^3.7.7"
},
"devDependencies": {
"bluebird": "^3.4.1",
"chai": "^3.2.0",
"conventional-changelog": "^1.1.0",
"conventional-changelog-cli": "^1.2.0",
+ "coveralls": "^2.11.12",
"kew": "~0.7.0",
"mocha": "^3.0.2",
- "nock": "^8.0.0",
+ "nyc": "^8.1.0",
"q": "~1.4.1",
"rsvp": "^3.2.1",
"updtr": "^0.2.1"
diff --git a/strategies/NetworkError.js b/strategies/NetworkError.js
index 46d8d2a..37cd5d0 100644
--- a/strategies/NetworkError.js
+++ b/strategies/NetworkError.js
@@ -1,7 +1,7 @@
'use strict';
var RETRIABLE_ERRORS = ['ECONNRESET', 'ENOTFOUND', 'ESOCKETTIMEDOUT', 'ETIMEDOUT', 'ECONNREFUSED', 'EHOSTUNREACH', 'EPIPE', 'EAI_AGAIN'];
-var _ = require('fg-lodash');
+var _ = require('lodash');
/**
* @param {Null | Object} err
@@ -9,7 +9,7 @@ var _ = require('fg-lodash');
* @return {Boolean} true if the request had a network error
*/
function NetworkError(err /*, response*/ ) {
- return err && _.contains(RETRIABLE_ERRORS, err.code);
+ return err && _.includes(RETRIABLE_ERRORS, err.code);
}
NetworkError.RETRIABLE_ERRORS = RETRIABLE_ERRORS;
diff --git a/test/api-surface.test.js b/test/api-surface.test.js
new file mode 100644
index 0000000..0df137f
--- /dev/null
+++ b/test/api-surface.test.js
@@ -0,0 +1,76 @@
+'use strict';
+
+var request = require('../');
+var t = require('chai').assert;
+
+describe('API surface', function () {
+
+ describe('callback api', function(){
+ [['request', request], ['request.get', request.get]].forEach(function(pair){
+ it('should work with '+pair[0]+'(url, f)', function (done) {
+ pair[1]('http://www.filltext.com/?rows=1', function (err, response, body) {
+ t.strictEqual(response.statusCode, 200);
+ t.strictEqual(response.body, '[{}]');
+ done();
+ });
+ });
+
+ it('should work with '+pair[0]+'(url, object, f)', function (done) {
+ pair[1]('http://www.filltext.com/?rows=1', {
+ json:true,
+ }, function (err, response, body) {
+ t.strictEqual(response.statusCode, 200);
+ t.deepEqual(response.body, [{}]);
+ done();
+ });
+ });
+
+ it('should work with '+pair[0]+'(object, f)', function (done) {
+ pair[1]({
+ url: 'http://www.filltext.com/?rows=1',
+ json:true
+ }, function (err, response, body) {
+ t.strictEqual(response.statusCode, 200);
+ t.deepEqual(response.body, [{}]);
+ done();
+ });
+ });
+ });
+ });
+
+ describe('promise api', function(){
+ [['request', request], ['request.get', request.get]].forEach(function(pair){
+ it('should work with '+pair[0]+'(url)', function (done) {
+ pair[1]('http://www.filltext.com/?rows=1')
+ .then(function (response) {
+ t.strictEqual(response.statusCode, 200);
+ t.strictEqual(response.body, '[{}]');
+ done();
+ });
+ });
+
+ it('should work with request(url, object)', function (done) {
+ pair[1]('http://www.filltext.com/?rows=1', {
+ json:true,
+ })
+ .then(function (response) {
+ t.strictEqual(response.statusCode, 200);
+ t.deepEqual(response.body, [{}]);
+ done();
+ });
+ });
+
+ it('should work with '+pair[0]+'(object)', function (done) {
+ pair[1]({
+ url: 'http://www.filltext.com/?rows=1',
+ json:true
+ })
+ .then(function (response) {
+ t.strictEqual(response.statusCode, 200);
+ t.deepEqual(response.body, [{}]);
+ done();
+ });
+ });
+ });
+ });
+});
diff --git a/test/promises.test.js b/test/promises.test.js
index 7cb3a13..d77991d 100644
--- a/test/promises.test.js
+++ b/test/promises.test.js
@@ -2,7 +2,6 @@
var request = require('../');
var t = require('chai').assert;
-var nock = require('nock');
describe('Promises support', function () {
@@ -60,24 +59,18 @@ describe('Promises support', function () {
});
it('should reject the response on any error', function (done) {
- nock('http://www.filltext.com')
- .get('/')
- .query({rows: 1})
- .replyWithError('Some error');
-
request({
- url: 'http://www.filltext.com/?rows=1', // return 1 row of data
+ url: 'http://localhost:1', // return 1 row of data
maxAttempts: 1,
+ retryStrategy: request.RetryStrategies.HTTPOrNetworkError
})
.catch(function (err) {
- t.strictEqual(err.message, 'Some error');
+ t.strictEqual(err.message, 'connect ECONNREFUSED 127.0.0.1:1');
done();
});
});
it('should still work with callbacks', function (done) {
- nock.restore(); // Allow next requests
-
request({url: 'http://www.filltext.com/?rows=1'}, function requestCallback(err, response, body) {
t.strictEqual(response.statusCode, 200);
t.strictEqual(response.attempts, 1);