From ca599c9974664a59e9104b49302d0d0069359081 Mon Sep 17 00:00:00 2001 From: Julian Simioni Date: Wed, 31 Oct 2018 22:24:26 -0400 Subject: [PATCH] fix(errors): Only return 400 status on known parameter errors Pelias has for a long time returned 400 as a default status whenever anything goes wrong, as well as when a user has passed invalid parameters. By using a new exception class, it is now possible to differetiate between known parameter errors, and unexpected errors that truly represent an HTTP 500. --- middleware/sendJSON.js | 94 +++++++++++------------------- package.json | 1 - sanitizer/PeliasParameterError.js | 15 +++++ sanitizer/sanitizeAll.js | 7 +++ test/unit/middleware/sendJSON.js | 4 +- test/unit/sanitizer/sanitizeAll.js | 11 +++- 6 files changed, 67 insertions(+), 65 deletions(-) create mode 100644 sanitizer/PeliasParameterError.js diff --git a/middleware/sendJSON.js b/middleware/sendJSON.js index 584fdf0ee..6f1e248e2 100644 --- a/middleware/sendJSON.js +++ b/middleware/sendJSON.js @@ -1,71 +1,45 @@ -var check = require('check-types'), - es = require('elasticsearch'), - logger = require( 'pelias-logger' ).get( 'api' ), - exceptions = require('elasticsearch-exceptions/lib/exceptions/SupportedExceptions'); +const _ = require('lodash'); +const es = require('elasticsearch'); +const logger = require( 'pelias-logger' ).get( 'api' ); +const PeliasParameterError = require('../sanitizer/PeliasParameterError'); -// create a list of regular expressions to match against. -// note: list created when the server starts up; for performance reasons. -var exceptionRegexList = exceptions.map( function( exceptionName ){ - return new RegExp( '^' + exceptionName ); -}); +function isTimeoutError(error) { + return error instanceof es.errors.RequestTimeout; +} + +function isElasticsearchError(error) { + const knownErrors = [ es.errors.NoConnections, + es.errors.ConnectionFault ]; + + return knownErrors.some(function(esError) { + return error instanceof esError; + }); +} function sendJSONResponse(req, res, next) { // do nothing if no result data set - if (!res || !check.object(res.body) || !check.object(res.body.geocoding)) { + const geocoding = _.get(res, 'body.geocoding'); + + if (!_.isObject(geocoding)) { return next(); } - // default status - var statusCode = 200; // 200 OK - - // vary status code whenever an error was reported - var geocoding = res.body.geocoding; - - if( check.array( geocoding.errors ) && geocoding.errors.length ){ - - // default status for errors is 400 Bad Request - statusCode = 400; // 400 Bad Request - - // iterate over all reported errors - geocoding.errors.forEach( function( err ){ - - // custom status codes for instances of the Error() object. - if( err instanceof Error ){ - /* - elasticsearch errors - see: https://github.com/elastic/elasticsearch-js/blob/master/src/lib/errors.js - - 408 Request Timeout - 500 Internal Server Error - 502 Bad Gateway - */ - if( err instanceof es.errors.RequestTimeout ){ statusCode = Math.max( statusCode, 408 ); } - else if( err instanceof es.errors.NoConnections ){ statusCode = Math.max( statusCode, 502 ); } - else if( err instanceof es.errors.ConnectionFault ){ statusCode = Math.max( statusCode, 502 ); } - else { - logger.warn( 'unknown geocoding error object:', err.constructor.name, err ); - statusCode = Math.max( statusCode, 500 ); - } - - /* - some elasticsearch errors are only returned as strings (not instances of Error). - in this case we (unfortunately) need to match the exception at position 0 inside the string. - */ - } else if( check.string( err ) ){ - for( var i=0; i