Skip to content

Commit 130da32

Browse files
authored
Merge pull request #1573 from pelias/treat-timeouts-as-502
Return 502 response code for service timeouts instead of 400
2 parents 38ab783 + ea85c30 commit 130da32

File tree

6 files changed

+97
-20
lines changed

6 files changed

+97
-20
lines changed

middleware/sendJSON.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ const _ = require('lodash');
22
const es = require('elasticsearch');
33
const logger = require( 'pelias-logger' ).get( 'api' );
44
const PeliasParameterError = require('../sanitizer/PeliasParameterError');
5+
const PeliasTimeoutError = require('../sanitizer/PeliasTimeoutError');
56

67
function isParameterError(error) {
78
return error instanceof PeliasParameterError;
89
}
910

1011
function isTimeoutError(error) {
11-
return error instanceof es.errors.RequestTimeout;
12+
return error instanceof PeliasTimeoutError ||
13+
error instanceof es.errors.RequestTimeout;
1214
}
1315

1416
function isElasticsearchError(error) {

sanitizer/PeliasParameterError.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@ class PeliasParameterError extends Error {
33
super(message);
44
}
55

6-
// syntax had to be changed due to jshint bug
7-
// https://github.com/jshint/jshint/issues/3358
8-
['toString']() {
6+
toString() {
97
return this.message;
108
}
119

sanitizer/PeliasTimeoutError.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Error subclass for timeouts contacting Pelias services
2+
class PeliasTimeoutError extends Error {
3+
constructor(message = '') {
4+
super(message);
5+
}
6+
7+
toString() {
8+
return this.message;
9+
}
10+
11+
toJSON() {
12+
return this.message;
13+
}
14+
}
15+
16+
module.exports = PeliasTimeoutError;

sanitizer/sanitizeAll.js

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,28 @@
11
const PeliasParameterError = require('./PeliasParameterError');
2+
const PeliasTimeoutError = require('../sanitizer/PeliasTimeoutError');
3+
4+
function getCorrectErrorType(message) {
5+
if (message.includes( 'Timeout')) {
6+
return new PeliasTimeoutError(message);
7+
}
8+
9+
// most errors are parameter errors
10+
return new PeliasParameterError(message);
11+
}
12+
13+
// Some Pelias code returns Error objects,
14+
// some returns strings. We want to standardize
15+
// on Error objects here
16+
function ensureInstanceOfError(error) {
17+
if (error instanceof Error) {
18+
// preserve the message and stack trace of existing Error objecs
19+
const newError = getCorrectErrorType(error.message);
20+
newError.stack = error.stack;
21+
return newError;
22+
}
23+
24+
return getCorrectErrorType(error);
25+
}
226

327
function sanitize( req, sanitizers ){
428
// init an object to store clean (sanitized) input parameters if not initialized
@@ -21,25 +45,15 @@ function sanitize( req, sanitizers ){
2145
req.errors = req.errors.concat( sanity.errors );
2246
}
2347

24-
// all errors must be returned as PeliasParameterError object to trigger HTTP 400 errors
25-
req.errors = req.errors.map(function(error) {
26-
// replace any existing Error objects with the right class
27-
// preserve the message and stack trace
28-
if (error instanceof Error) {
29-
const new_error = new PeliasParameterError(error.message);
30-
new_error.stack = error.stack;
31-
return new_error;
32-
} else {
33-
return new PeliasParameterError(error);
34-
}
35-
});
36-
3748
// if warnings occurred then set them
3849
// on the req object.
3950
if( sanity.warnings.length ){
4051
req.warnings = req.warnings.concat( sanity.warnings );
4152
}
4253
}
54+
55+
// all errors must be mapped to a correct Error type to trigger the right HTTP response codes
56+
req.errors = req.errors.map(ensureInstanceOfError);
4357
}
4458

4559
// Adds to goodParameters every acceptable parameter passed through API call

test/unit/middleware/sendJSON.js

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
var es = require('elasticsearch'),
2-
middleware = require('../../../middleware/sendJSON');
1+
const es = require('elasticsearch');
2+
const middleware = require('../../../middleware/sendJSON');
3+
const PeliasTimeoutError = require('../../../sanitizer/PeliasTimeoutError');
34

45
module.exports.tests = {};
56

@@ -206,6 +207,24 @@ module.exports.tests.search_phase_execution_exception = function(test, common) {
206207
});
207208
};
208209

210+
module.exports.tests.service_timeout_exception = function(test, common) {
211+
test('service timeout exception', function(t) {
212+
var res = { body: { geocoding: {
213+
errors: [ new PeliasTimeoutError('Timeout: could not reach Placeholder service') ]
214+
}}};
215+
216+
res.status = function( code ){
217+
return { json: function( body ){
218+
t.equal( code, 502, 'Bad Gateway' );
219+
t.deepEqual( body, res.body, 'body set' );
220+
t.end();
221+
}};
222+
};
223+
224+
middleware(null, res);
225+
});
226+
};
227+
209228
module.exports.tests.unknown_exception = function(test, common) {
210229
test('unknown exception', function(t) {
211230
var res = { body: { geocoding: {

test/unit/sanitizer/sanitizeAll.js

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
var sanitizeAll = require('../../../sanitizer/sanitizeAll');
22
const PeliasParameterError = require('../../../sanitizer/PeliasParameterError');
3+
const PeliasTimeoutError = require('../../../sanitizer/PeliasTimeoutError');
34

45
module.exports.tests = {};
56

@@ -97,7 +98,7 @@ module.exports.tests.all = function(test, common) {
9798
t.end();
9899
});
99100

100-
test('Error objects should be converted to correct type', function(t) {
101+
test('Normal error objects should be converted to PeliasParameterError type', function(t) {
101102
var req = {};
102103
var sanitizers = {
103104
'first': {
@@ -125,6 +126,33 @@ module.exports.tests.all = function(test, common) {
125126

126127
});
127128

129+
test('Timeout error should be converted to PeliasTimeoutError type', function(t) {
130+
var req = {};
131+
const error_message = 'Timeout: could not reach Placeholder service';
132+
var sanitizers = {
133+
'first': {
134+
sanitize: function(){
135+
req.clean.a = 'first sanitizer';
136+
return {
137+
errors: [new Error(error_message)],
138+
warnings: []
139+
};
140+
}
141+
}
142+
};
143+
144+
var expected_req = {
145+
clean: {
146+
a: 'first sanitizer'
147+
},
148+
errors: [ new PeliasTimeoutError(error_message) ],
149+
warnings: []
150+
};
151+
152+
sanitizeAll.runAllChecks(req, sanitizers);
153+
t.deepEquals(req, expected_req);
154+
t.end();
155+
});
128156

129157
test('req.query should be passed to individual sanitizers when available', function(t) {
130158
var req = {

0 commit comments

Comments
 (0)