From 0e8dac4ecd699dce113637f8e885807d1e4e6ff1 Mon Sep 17 00:00:00 2001 From: Jon Winton Date: Mon, 21 May 2018 12:30:18 -0400 Subject: [PATCH] initial work --- lib/render.js | 4 +++- lib/responses.js | 6 +++++- lib/responses.test.js | 10 ++++++++++ lib/services/composer.js | 16 +++++++++++----- lib/services/composer.test.js | 21 +++++++++++++++++++++ 5 files changed, 50 insertions(+), 7 deletions(-) diff --git a/lib/render.js b/lib/render.js index bbb3abc3..66f492f8 100644 --- a/lib/render.js +++ b/lib/render.js @@ -13,6 +13,7 @@ const _ = require('lodash'), clayUtils = require('clayutils'), db = require('./services/db'), composer = require('./services/composer'), + responses = require('./responses'), mapLayoutToPageData = require('./utils/layout-to-page-data'); /** @@ -116,7 +117,8 @@ function renderPage(uri, req, res, hrStart) { return getDBObject(uri) .then(formDataFromLayout(locals, uri)) .tap(logTime(hrStart, uri)) - .then(({ data, options }) => renderer.render(data, options, res));; + .then(({ data, options }) => renderer.render(data, options, res)) + .catch(responses.handleError(res)); } /** diff --git a/lib/responses.js b/lib/responses.js index 3963484e..0153fd7f 100644 --- a/lib/responses.js +++ b/lib/responses.js @@ -332,7 +332,11 @@ function clientError(err, res) { */ function handleError(res) { return function (err) { - if (err.name === 'NotFoundError' || + if (err.status && err.name !== 'NotFoundError') { + // If we're in this block, the error has a defined `status` property and + // the error should be directed out immediately + sendDefaultResponseForCode(err.status, err.message, res); + } else if (err.name === 'NotFoundError' || err.message.indexOf('ENOENT') !== -1 || err.message.indexOf('not found') !== -1) { notFound(err, res); diff --git a/lib/responses.test.js b/lib/responses.test.js index f6398fb8..77ea8750 100644 --- a/lib/responses.test.js +++ b/lib/responses.test.js @@ -104,6 +104,16 @@ describe(_.startCase(filename), function () { }); fn(res)(new Error('something')); }); + + it('sends the error code defined in the `status` property', function (done) { + const res = createMockRes(), + myError = new Error('something'); + + myError.status = 403; + expectStatus(res, 403); + expectResult(res, 'sendStatus: whatever', done); + fn(res)(myError); + }); }); describe('unauthorized', function () { diff --git a/lib/services/composer.js b/lib/services/composer.js index 24101b3c..e152e1c8 100644 --- a/lib/services/composer.js +++ b/lib/services/composer.js @@ -27,15 +27,21 @@ function resolveComponentReferences(data, locals, filter = referenceProperty) { return resolveComponentReferences(obj, locals, filter).finally(function () { _.assign(referenceObject, _.omit(obj, referenceProperty)); }).catch(function (error) { - log('error', `${error.message} within ${referenceObject[referenceProperty]}`, { - name: error.name, - stack: error.stack - }); + const logObj = { + stack: error.stack, + cmpt: referenceObject[referenceProperty] + }; + + if (error.status) { + logObj.status = error.status; + } + + log('error', `${error.message}`, logObj); return bluebird.reject(error); }); }); - }).return(data); + }).then(() => data); } /** diff --git a/lib/services/composer.test.js b/lib/services/composer.test.js index f103a762..e1b85aa4 100644 --- a/lib/services/composer.test.js +++ b/lib/services/composer.test.js @@ -113,6 +113,27 @@ describe(_.startCase(filename), function () { done(); }); }); + + it('adds the status to the error message if it one is defined', function (done) { + const data = { + a: {_ref: '/c/b'}, + c: {d: {_ref: '/c/e'}} + }, + myError = new Error('hello!'); + + myError.status = 404; + components.get.withArgs('/c/b').returns(bluebird.resolve({g: 'h'})); + components.get.withArgs('/c/e').returns(bluebird.resolve({i: 'j', k: {_ref: '/c/m'}})); + components.get.withArgs('/c/m').returns(bluebird.reject(myError)); + + // use done() rather than returning the promise, so we can catch and test errors + fn(data).then(done).catch((error) => { + sinon.assert.calledOnce(logSpy); + expect(error.message).to.equal('hello!'); + sinon.assert.calledWith(logSpy, 'error', 'hello!', { status: myError.status, cmpt: '/c/e', stack: myError.stack }); + done(); + }); + }); }); describe('composePage', function () {