diff --git a/CHANGELOG.md b/CHANGELOG.md index 27572b4..2559521 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +2.4.0 June 12, 2017 + - Request errors now include the `response` object. + 2.3.0 January 6, 2017 - Add `merchants` resource + `merchants#all` diff --git a/README.md b/README.md index e06f9be..963a280 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,8 @@ client.orders.get('btnorder-XXX', function(err, res) { All callbacks will be invoked with two arguments. `err` will be an `Error` object if an error occurred and `null` otherwise. `res` will be the API response if the request succeeded or `null` otherwise. +If an `Error` is returned after the client receives a response, such as for an upstream HTTP error, the `Error.response` property will be set to the NodeJS `response` object. + #### Promise `button-client-node` supports a promise interface. To make a promise-based request, supply a function that accepts a single resolver function and returns a new promise on the `promise` key of your `config`. Additionally, you must omit the callback from your API function call. diff --git a/lib/request.js b/lib/request.js index e66438a..76ef1ae 100644 --- a/lib/request.js +++ b/lib/request.js @@ -72,7 +72,7 @@ function responseHandler(callback) { res.on('end', function onEnd() { if (!rawResponse) { return callback( - new Error('Client received an empty response from the server'), + formatError('Client received an empty response from the server', res), null ); } @@ -80,26 +80,36 @@ function responseHandler(callback) { try { var response = JSON.parse(rawResponse); } catch (e) { - return callback(new Error('Error parsing response as JSON: ' + rawResponse), null); + return callback( + formatError('Error parsing response as JSON: ' + rawResponse, res), + null + ); } if (typeof response.meta !== 'object' || !response.meta.status) { - return callback(new Error('Invalid response: ' + rawResponse), null); + return callback( + formatError('Invalid response: ' + rawResponse, res), + null + ); } var status = response.meta.status; if (status === 'ok') { return callback(null, formatResponse(response)); - } else if (status === 'error') { + } + + var msg; + if (status === 'error') { if (typeof response.error !== 'object' || !response.error.message) { - return callback(new Error('Invalid response: ' + rawResponse), null); + msg = 'Invalid response: ' + rawResponse; + } else { + msg = response.error.message; } - - return callback(new Error(response.error.message), null); } else { - return callback(new Error('Unknown status: ' + status), null); + msg = 'Unknown status: ' + status; } + return callback(formatError(msg, res), null); }); }; } @@ -127,6 +137,12 @@ function formatResponse(response) { }; } +function formatError(message, response) { + var err = new Error(message); + err.response = response; + return err; +} + function formatCursor(url) { if (typeof url === 'string') { var parsed = parse(url, true); diff --git a/package.json b/package.json index 7096af3..2d16c8f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@button/button-client-node", - "version": "2.3.0", + "version": "2.4.0", "description": "node.js client for the Button Order API", "repository": { "type": "git", diff --git a/test/lib/request-test.js b/test/lib/request-test.js index 0c6a74f..28e0712 100644 --- a/test/lib/request-test.js +++ b/test/lib/request-test.js @@ -133,6 +133,8 @@ describe('lib/#request', function() { hostname: hostname }, function(err, res) { expect(err.message).to.be(error); + expect(err.response).to.be.ok(); + expect(err.response.statusCode).to.eql(404); expect(res).to.eql(null); scope.done(); done(); @@ -154,6 +156,7 @@ describe('lib/#request', function() { hostname: hostname }, function(err, res) { expect(err.message).to.be(error); + expect(err.response).to.not.be.ok(); expect(res).to.eql(null); scope.done(); done(); @@ -176,6 +179,7 @@ describe('lib/#request', function() { hostname: hostname }, function(err, res) { expect(err.message).to.be('Request timed out'); + expect(err.response).to.not.be.ok(); expect(res).to.eql(null); scope.done(); done(); @@ -219,6 +223,8 @@ describe('lib/#request', function() { hostname: hostname }, function(err, res) { expect(err.message).to.be('Error parsing response as JSON: not json'); + expect(err.response).to.be.ok(); + expect(err.response.statusCode).to.eql(200); expect(res).to.eql(null); scope.done(); done(); @@ -239,6 +245,8 @@ describe('lib/#request', function() { hostname: hostname }, function(err, res) { expect(err.message).to.be('Client received an empty response from the server'); + expect(err.response).to.be.ok(); + expect(err.response.statusCode).to.eql(200); expect(res).to.eql(null); scope.done(); done(); @@ -259,6 +267,8 @@ describe('lib/#request', function() { hostname: hostname }, function(err, res) { expect(err.message).to.be('Unknown status: ???'); + expect(err.response).to.be.ok(); + expect(err.response.statusCode).to.eql(200); expect(res).to.eql(null); scope.done(); done(); @@ -279,6 +289,8 @@ describe('lib/#request', function() { hostname: hostname }, function(err, res) { expect(err.message).to.be('Unknown status: ???'); + expect(err.response).to.be.ok(); + expect(err.response.statusCode).to.eql(200); expect(res).to.eql(null); scope.done(); done(); @@ -299,6 +311,8 @@ describe('lib/#request', function() { hostname: hostname }, function(err, res) { expect(err.message).to.be('Invalid response: {}'); + expect(err.response).to.be.ok(); + expect(err.response.statusCode).to.eql(200); expect(res).to.eql(null); scope.done(); done(); @@ -319,6 +333,8 @@ describe('lib/#request', function() { hostname: hostname }, function(err, res) { expect(err.message).to.be('Invalid response: {"meta":"wat"}'); + expect(err.response).to.be.ok(); + expect(err.response.statusCode).to.eql(200); expect(res).to.eql(null); scope.done(); done(); @@ -339,6 +355,8 @@ describe('lib/#request', function() { hostname: hostname }, function(err, res) { expect(err.message).to.be('Invalid response: {"meta":{"status":"error"}}'); + expect(err.response).to.be.ok(); + expect(err.response.statusCode).to.eql(200); expect(res).to.eql(null); scope.done(); done(); @@ -359,6 +377,8 @@ describe('lib/#request', function() { hostname: hostname }, function(err, res) { expect(err.message).to.be('Invalid response: {"meta":{"status":"error"},"error":"wat"}'); + expect(err.response).to.be.ok(); + expect(err.response.statusCode).to.eql(200); expect(res).to.eql(null); scope.done(); done();