From c17ad0ef44aa8e1c091d8d1ab729a5ce6c61e222 Mon Sep 17 00:00:00 2001 From: mtmail Date: Wed, 27 Oct 2021 18:32:48 +0200 Subject: [PATCH] catch and report JSON parsing errors, simulate API HTTP responses (#168) --- src/lib/api_utils.js | 36 +++++++++++++++++++++++++++++++----- test/api_errors.js | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 test/api_errors.js diff --git a/src/lib/api_utils.js b/src/lib/api_utils.js index eb2a549..8de3f0f 100644 --- a/src/lib/api_utils.js +++ b/src/lib/api_utils.js @@ -10,17 +10,43 @@ function api_request_progress(status) { export async function fetch_from_api(endpoint_name, params, callback) { var api_url = generate_nominatim_api_url(endpoint_name, params); + // For the test suite: + // If httpbin_status URL parameter is set we call https://httpbin.org/#/Status_codes + var tmp_params = new URLSearchParams(window.location.search); + if (tmp_params && tmp_params.get('httpbin_status')) { + api_url = 'https://httpbin.org/status/' + parseInt(tmp_params.get('httpbin_status'), 10); + } + api_request_progress('start'); if (endpoint_name !== 'status') last_api_request_url_store.set(null); try { await fetch(api_url, { headers: Nominatim_Config.Nominatim_API_Endpoint_Headers || {} }) - .then(response => response.json()) - .then(data => { - if (data.error) { - error_store.set(data.error.message); + .then(async (response) => { + if (!((response.status >= 200 && response.status < 300) || response.status === 404)) { + error_store.set(`Error fetching data from ${api_url} (${response.statusText})`); + return undefined; + } + + // Parse JSON here instead of returning a promise so we can catch possible + // errors. + var data; + try { + data = await response.json(); + } catch (err) { + // e.g. 'JSON.parse: unexpected non-whitespace character after JSON data at line 1' + error_store.set(`Error parsing JSON data from ${api_url} (${err})`); + return undefined; + } + return data; + }) + .then((data) => { + if (data) { + if (data.error) { + error_store.set(data.error.message); + } + callback(data); } - callback(data); api_request_progress('finish'); }); } catch (error) { diff --git a/test/api_errors.js b/test/api_errors.js new file mode 100644 index 0000000..b5eef6d --- /dev/null +++ b/test/api_errors.js @@ -0,0 +1,43 @@ +const assert = require('assert'); + +describe('Nominatim API errors', function () { + let page; + + describe('HTTP 503 - service unavailable', function () { + before(async function () { + page = await browser.newPage(); + await page.goto('http://localhost:9999/search.html?q=london&httpbin_status=503'); + }); + + after(async function () { + await page.close(); + }); + + it('should display an error', async function () { + await page.waitForSelector('#error'); + + let message = await page.$eval('#error', el => el.textContent); + assert.ok(message.includes('httpbin.org')); + assert.ok(message.includes('Error fetching data from')); + }); + }); + + describe('HTTP 200 - JSON parsing fails', function () { + before(async function () { + page = await browser.newPage(); + await page.goto('http://localhost:9999/search.html?q=london&httpbin_status=200'); + }); + + after(async function () { + await page.close(); + }); + + it('should display an error', async function () { + await page.waitForSelector('#error'); + + let message = await page.$eval('#error', el => el.textContent); + assert.ok(message.includes('httpbin.org')); + assert.ok(message.includes('Error parsing JSON data from')); + }); + }); +}); -- 2.39.5