Skip to content

Commit

Permalink
Merge pull request #10 from Receiptful/feature/timeout
Browse files Browse the repository at this point in the history
Add optional timeout to requests
  • Loading branch information
stefanosala committed Apr 28, 2016
2 parents 898b189 + 8670e6c commit 8b67c18
Show file tree
Hide file tree
Showing 12 changed files with 324 additions and 304 deletions.
18 changes: 18 additions & 0 deletions .jscsrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"preset": "airbnb",
"disallowMultipleVarDecl": {
"allExcept": ["require"]
},
"maxErrors": "Infinity",
"requireTrailingComma": null,
"requirePaddingNewLinesAfterBlocks": null,
"requirePaddingNewLinesBeforeLineComments": null,
"requireSpacesInsideObjectBrackets": "all",
"disallowSpacesInFunctionExpression": {
"beforeOpeningRoundBrace": true
},
"disallowSpacesInAnonymousFunctionExpression": {
"beforeOpeningRoundBrace": true
},
"requireSpacesInAnonymousFunctionExpression": null
}
3 changes: 2 additions & 1 deletion .jshintrc
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"alert": false,
"alertify": false,
"printStackTrace": false,
"Spinner": false
"Spinner": false,
"Headers": false
}
}
17 changes: 17 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
language: node_js
node_js:
- '6'
- '5'
- '4'
deploy:
provider: npm
api_key:
secure: Fj94zdVEW7vdnA2lQ2J2u5Jgt49XG9t5vTWf6/wHrhC85b1p+QGCNbfYg7+NkiCK7ChDealidty2ZPEcjdAlBErYbg29caKT6iNgVJ+sebm5ECy/V8Vfy6bqD8M6eI5kcOR2Iwwn22bt9P2kown/O3uZvvGd58eUpDBsf/Ru5DsoMt4/gNqQLZdAE+GWVqPVA4R7hL1ehCn2SMTni0xhIEoKbG0AWWjU+67qeLrFmQdrVvlsaHlXZO5E8UgEFjqkiF8EpqPJaGZf5ivLgS84NS9RrRVuKn0lmb/YLqzx+d1BWbaTmCLioWMU31Z61uqrB1PwVIrZygMIsbwqjZ43aOnl/H43TkUAwXVzCcgLvPN9WLMMEudX3XiyfsyO/3ddTv5tpkZehDLHQxVIIPNCwLOy6hR9SVfd9SNF2QXZ2kblebATL58/jG5lQQDw8n3OUXKlFeoAVN+nsbNOv0aiV6VyDqqGUfn28UxTlJ0Cgs0o6SjhSoXMSRjLyHSJtbcOPh9hmU31prKOHKsuyz1Z4dIDfQ+XM+Q5E4j0saJWALyv2N7jOpiZaLGMFKA+j8GSGQMENGSRwXQQTO6Kb4iNwTtChqAwWmop9h1kAvGA+BJje+oUMiyjJO7yuGudNCwsovlbJcTsvU2m4o4YwV+xlzRlUZqEnJ01ccVJUnGraN4=
on:
tags: true
repo: Receiptful/node-woocommerce
email:
secure: FNuDs+J0+eAbQtnybfHroIWmnvt0SK5VW+yI31O1GTgidHCwm873Ea41tJiZp8x4MpwVgZmhxB0P4QJjVE+/QK/Ac+GRbMbm3/lcq6exqgMmInelQxRMbKejyc1xEMeGiH2nr87vxhB/PTM2ERxsLFd+sGDS3bcrjMiY82WWCxwMGE32e9zD5D4ce7PGKNn5Qjev4flcwTKwSUBvtXFXj+G/t3xkB6I5c76CkOiqI2130wpx1CuAtDvg56cteI4MEyi4L+36s6Cvj/djIuUWn0I+9Ze68A3QOuj4+Gw/KYR+KJJZN8V0gMy58sfQm4M4EtHccVeVVRN/BXnMl+a75mKn9Gg8NE8NivWENiI/0iU24+WufaX7+klFwnItZsY7TmCmCIO68wzqPhG50a3r8hbnXqdn/CdjGiV3OkZe744Rrg/+ZXrcIolyRF3VFEf05iD4YNhgU11YU7WG4Qgm3sptGGiuBJMaWU7vejE+Z3n3lKF2HN/SOsC48XJWUXtrdpYa+xgZnaOA+1V7ccfqjFerE9ngbml/wfZ9KIKApyDnZ5jlaJO5FpaIWaSYTvUHoc/sPXUwLSYQFWYzWm6FhbvkIa3wQ7xdZxvoi70+e5iqsuW+LV9CzoVNFOBgvpPAj7t2cev2jgS+b7f3Z4ivG02hse/Hyc0U8jWDxyTPvkc=
notifications:
slack:
secure: oHvm9eOny5Pg3NzQash8U75BWMZ9oT9liM2LoOmTZTpKa04nKhkwQSYdpG7VkRK9Txfvh0zg2F2lC35QrbiP7T3IqkzRF5ktqxLnO8GhNhXd754vZ3b6k093hP9l8OG8tEKqkEi7uceV+1qkmgaNTbY8qk0641oBvqM1J1cXuQTmdtixl1+fXCkybfiVNRmDoAEEk0vDB+/MVLPvUSQigvZmI85pLmGlfZWbJ1PG/lIcU3tJ48ys4T18P4C97xN8KZIxcakmbWFzcB8iS/9t3HVyuS9CvSFqab3gRVc1TIcCwIApgGxtAFQtDerz4haWnodjh18+KGwRxRUqLP3erw1s1NSbhgQzC+kfV0IL14tdrvPuA0f0edwAv4qA9s9eyjgmo/Q5BdWx5TWTNpKC4Umj6IzE5YrCT0aHxqMTT1KTqw4e4Lv+Ml6ScZhi34vhe4zo9QF0oCmRJ+8a203SgODni4l2rmW1FsYd+kGCKD9e8CBjI7kr5cPkPr6h1wgsPH6x7iW4SxWOymABdqh6HPCnMHK63fRo7wvZfw50Hrtv9ieCc64tx2TIYdxjMBHlvbxOCi7WkJSH5VXq7XAK+7zrVSuvyO27DY6DPhPS8A4YGEcOmoWfmeanTGUkpjcciyN3w+TfSfDbr7K3LCWAG5r7SynUWEWJqZW3N8XeE1k=
4 changes: 0 additions & 4 deletions Makefile

This file was deleted.

6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# node-woocommerce
Connects NodeJS to the glorious world of the WooCommerce API

[![Code Climate](https://codeclimate.com/repos/5551dd2f6956804225000037/badges/4935563d1fc24b707863/gpa.svg)](https://codeclimate.com/repos/5551dd2f6956804225000037/feed)
[![Build Status](https://travis-ci.org/Receiptful/node-woocommerce.svg?branch=master)](https://travis-ci.org/Receiptful/node-woocommerce)

## Important v2.0 Changes

Expand All @@ -18,10 +18,10 @@ wooCommerce.get('/products')
.catch(err => {
// Log the error message
console.log(err.message);

// Log the body returned from the server
console.log(err.body);

// Log the full response object and status code
console.log(err.response, err.response.statusCode);
});
Expand Down
6 changes: 2 additions & 4 deletions lib/logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,5 @@ module.exports = {
if (this.logLevel < 1) return;
console.log('\x1b[36mInfo: \x1b[0m%s', message);
},
error: function(message) {
console.log('\x1b[31mError: \x1b[0m%s', message);
}
}
error: message => console.error('\x1b[31mError: \x1b[0m%s', message)
};
15 changes: 6 additions & 9 deletions lib/request.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
'use strict';
/**
* To make the requests asynchronously
*/

const logger = require('./logger'),
oAuth = require('oauth-1.0a'),
request = require('request');

const version = require('../package.json').version;

// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
class Request {

constructor(options) {
Expand All @@ -24,9 +22,9 @@ class Request {
public: this.options.consumerKey,
secret: this.options.secret
},
'signature_method': 'HMAC-SHA256',
signature_method: 'HMAC-SHA256',
version: null,
'last_ampersand': false
last_ampersand: false
});
}

Expand Down Expand Up @@ -64,8 +62,9 @@ class Request {
qs: {},
headers: {
'User-Agent': `node-woocommerce/${version}`,
'Accept': 'application/json, *.*'
}
Accept: 'application/json, *.*'
},
timeout: this.options.timeout
};

if (data) {
Expand Down Expand Up @@ -148,10 +147,8 @@ class Request {

}
});

});
}

}

module.exports = Request;
4 changes: 2 additions & 2 deletions lib/woocommerce.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ class WooCommerce {
port: this.options.port,
consumerKey: this.options.consumerKey,
secret: this.options.secret,
logLevel: this.options.logLevel
logLevel: this.options.logLevel,
timeout: this.options.timeout
});

logger.info(require('util').inspect(this.options));
Expand Down Expand Up @@ -76,5 +77,4 @@ class WooCommerce {
}
}


module.exports = WooCommerce;
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
"description": "Connects NodeJS to the glorious world of the WooCommerce API",
"main": "lib/woocommerce.js",
"scripts": {
"test": "make test"
"exectests": "node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha",
"jshint": "node_modules/.bin/jshint lib test",
"jscs": "node_modules/.bin/jscs lib test",
"lint": "npm run jshint && npm run jscs",
"test": "npm run exectests && npm run lint"
},
"repository": {
"type": "git",
Expand All @@ -27,6 +31,8 @@
"devDependencies": {
"chai": "^3.5.0",
"istanbul": "^0.4.2",
"jscs": "^2.9.0",
"jshint": "^2.9.1",
"mocha": "^2.4.5",
"nock": "^7.2.2",
"sinon": "^1.17.3"
Expand Down
40 changes: 40 additions & 0 deletions test/logger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use strict';

const logger = require('../lib/logger'),
sinon = require('sinon');

describe('logger', () => {
const self = { };

beforeEach(() => self.sandbox = sinon.sandbox.create());
afterEach(() => self.sandbox.restore());

it('Should be set to a log level of zero by default', () => {
logger.logLevel.should.equal(0);
});

it('Should log an error at any log level', () => {
const spy = self.sandbox.spy(console, 'error');
logger.error('Logging test error at 0');
logger.level = 1;
logger.error('Logging test error at 1');

sinon.assert.calledTwice(spy);
});

it('Should log info at level one', () => {
var spy = self.sandbox.spy(console, 'log');
logger.logLevel = 1;
logger.info('Logging test error at 1');

sinon.assert.calledOnce(spy);
});

it('Should not log info at level zero', () => {
const spy = self.sandbox.spy(console, 'log');
logger.logLevel = 0;
logger.info('Logging test error at 1');

sinon.assert.notCalled(spy);
});
});
168 changes: 168 additions & 0 deletions test/request.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
'use strict';

const Request = require('../lib/request'),
chai = require('chai'),
nock = require('nock');

const should = chai.should();

describe('Request', () => {
beforeEach(() => nock.cleanAll());

const rOAuth = new Request({
hostname: 'http://foo.com',
consumerKey: 'foo',
secret: 'foo',
headers: {
test: 'header'
}
});

const rBasic = new Request({
hostname: 'https://foo.com',
ssl: true,
port: 443,
consumerKey: 'foo',
secret: 'foo',
headers: {
test: 'header'
}
});

it('Should return an error if hostname is missing', () => {
should.Throw(() => {
new Request();
}, Error);
});

it('Should return an error on bad request', done => {
const api = nock('http://foo.com')
.filteringPath(/\?.*/g, '?xxx')
.post('/orders?xxx', {})
.reply(400, { success: true });

rOAuth.complete('post', '/orders', {}, err => {
err.should.be.an.instanceof(Error);
api.done();
done();
});
});

it('Should return an error on internal server error', done => {
const api = nock('http://foo.com')
.filteringPath(/\?.*/g, '?xxx')
.post('/orders?xxx', {})
.reply(500, { success: true });

rOAuth.complete('post', '/orders', {}, err => {
err.should.be.an.instanceof(Error);
api.done();
done();
});
});

it('Should return an error the request JSON is malformed', done => {
const api = nock('http://foo.com')
.defaultReplyHeaders({
'content-type': 'application/json'
})
.filteringPath(/\?.*/g, '?xxx')
.post('/orders?xxx', {})
.reply(200, '<malformed>');

rOAuth.complete('post', '/orders', {}, err => {
err.should.be.an.instanceof(Error);
err.message.should.match(/Unexpected token </);
api.done();
done();
});
});

it('Should return content for http using OAuth', done => {
const api = nock('http://foo.com')
.filteringPath(/\?.*/g, '?xxx')
.post('/orders?xxx', {})
.reply(200, '{ "success": true }');

rOAuth.complete('post', '/orders', {}, (err, data) => {
should.not.exist(err);
data.should.be.an.instanceof(Object)
.and.have.property('success', true);
api.done();
done();
});
});

it('Should support data for GET requests', done => {
// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
const api = nock('https://foo.com/')
.get('/orders')
.query({
consumer_key: 'foo',
consumer_secret: 'foo',
filter: {
limit: 10
}
})
.reply(200, '{ "success": true }', {
'content-type': 'application/json'
});

rBasic.complete('get', '/orders', { 'filter[limit]': 10 }, (err, data) => {
should.not.exist(err);
data.should.be.an.instanceof(Object)
.and.have.property('success', true);
api.done();
done();
});
});

it('Should return content for https using Basic Auth', done => {
const api = nock('https://foo.com/')
.post('/orders')
.query(true)
.reply(200, '{ "success": true }');

rBasic.complete('post', '/orders', {}, (err, data) => {
should.not.exist(err);
data.should.be.an.instanceof(Object)
.and.have.property('success', true);
api.done();
done();
});
});

it('Should return content for when not a json', done => {
const api = nock('http://foo.com')
.defaultReplyHeaders({
'content-type': 'application/xml'
})
.filteringPath(/\?.*/g, '?xxx')
.post('/orders?xxx', {})
.reply(200, '<xml></xml>');

rOAuth.complete('post', '/orders', {}, (err, data) => {
api.done();
should.not.exist(err);
data.should.equal('<xml></xml>');
api.done();
done();
});
});

it('Should return an error if "errors" are found in the response', done => {
const api = nock('https://foo.com')
.filteringPath(/\?.*/g, '?xxx')
.get('/errors?xxx')
.reply(200, '{ "errors": ["An error has occurred."] }', {
'content-type': 'application/json'
});

rBasic.complete('get', '/errors', {}, err => {
api.done();
err.should.be.an.instanceof(Error);
err.message.should.equal('["An error has occurred."]');
done();
});
});
});
Loading

0 comments on commit 8b67c18

Please sign in to comment.