From 48ae0bbdb145510db72c61f9b33f3542e28deced Mon Sep 17 00:00:00 2001 From: mike wakerly Date: Mon, 12 Jun 2017 09:48:40 -0400 Subject: [PATCH 1/4] Include the http `response` in request errors. --- lib/request.js | 27 +++++++++++++++++++-------- test/lib/request-test.js | 20 ++++++++++++++++++++ 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/lib/request.js b/lib/request.js index e66438a..b87c4d1 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,31 @@ 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 +132,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/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(); From 2d9aa433dedee74b6b54d9cc92323474d32c93a4 Mon Sep 17 00:00:00 2001 From: mike wakerly Date: Mon, 12 Jun 2017 09:49:33 -0400 Subject: [PATCH 2/4] v2.4.0 --- CHANGELOG.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) 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/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", From bdb331447d7da20b462df891a2a7b38d758feff3 Mon Sep 17 00:00:00 2001 From: mike wakerly Date: Mon, 12 Jun 2017 13:10:42 -0400 Subject: [PATCH 3/4] Fix style nits. --- lib/request.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/request.js b/lib/request.js index b87c4d1..76ef1ae 100644 --- a/lib/request.js +++ b/lib/request.js @@ -81,11 +81,16 @@ function responseHandler(callback) { var response = JSON.parse(rawResponse); } catch (e) { return callback( - formatError('Error parsing response as JSON: ' + rawResponse, res), null); + formatError('Error parsing response as JSON: ' + rawResponse, res), + null + ); } if (typeof response.meta !== 'object' || !response.meta.status) { - return callback(formatError('Invalid response: ' + rawResponse, res), null); + return callback( + formatError('Invalid response: ' + rawResponse, res), + null + ); } var status = response.meta.status; From 3278df329225876e7fb84a42d2583d64f7b6bcad Mon Sep 17 00:00:00 2001 From: mike wakerly Date: Mon, 12 Jun 2017 13:12:41 -0400 Subject: [PATCH 4/4] Update README. --- README.md | 2 ++ 1 file changed, 2 insertions(+) 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.