diff --git a/src/baseRequest.js b/src/baseRequest.js index 260fd24..147a2a9 100644 --- a/src/baseRequest.js +++ b/src/baseRequest.js @@ -2,9 +2,13 @@ // SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) // Code is Apache-2.0 and docs are CC-BY-4.0 -import { Promise } from 'es6-promise' +import { + Promise +} from 'es6-promise' import fetchPonyfill from 'fetch-ponyfill' -import { vsprintf } from 'sprintf-js' +import { + vsprintf +} from 'sprintf-js' import formatText from './format_text' import stringifyAsQueryParam from './stringify_as_query_param' @@ -12,6 +16,46 @@ import stringifyAsQueryParam from './stringify_as_query_param' const fetch = fetchPonyfill(Promise) +/** + * @private + * Timeout function following https://github.com/github/fetch/issues/175#issuecomment-284787564 + * @param {integer} obj Source object + * @param {Promise} filter Array of key names to select or function to invoke per iteration + * @return {Object} TimeoutError if the time was consumed, otherwise the Promise will be resolved + */ +function timeout(ms, promise) { + return new Promise((resolve, reject) => { + setTimeout(() => { + const errorObject = { + message: 'TimeoutError' + } + reject(new Error(errorObject)) + }, ms) + promise.then(resolve, reject) + }) +} + +/** + * @private + * @param {Promise} res Source object + * @return {Promise} Promise that will resolve with the response if its status was 2xx; + * otherwise rejects with the response + */ +function handleResponse(res) { + // If status is not a 2xx (based on Response.ok), assume it's an error + // See https://developer.mozilla.org/en-US/docs/Web/API/GlobalFetch/fetch + if (!(res && res.ok)) { + const errorObject = { + message: 'HTTP Error: Requested page not reachable', + status: `${res.status} ${res.statusText}`, + requestURI: res.url + } + throw errorObject + } + return res +} + + /** * @private * imported from https://github.com/bigchaindb/js-utility-belt/ @@ -36,37 +80,16 @@ const fetch = fetchPonyfill(Promise) * decamelized into snake case first. * @param {*[]|Object} config.urlTemplateSpec Format spec to use to expand the url (see sprintf). * @param {*} config.* All other options are passed through to fetch. + * @param {integer} requestTimeout Timeout for a single request * - * @return {Promise} Promise that will resolve with the response if its status was 2xx; - * otherwise rejects with the response + * @return {Promise} If requestTimeout the timeout function will be called. Otherwise resolve the + * Promise with the handleResponse function */ - -const timeout = (ms, promise) => new Promise((resolve, reject) => { - setTimeout(() => { - const errorObject = { - message: 'TimeoutError' - } - reject(new Error(errorObject)) - }, ms) - return promise.then(resolve, reject) -}) - -const handleResponse = (res) => { - // If status is not a 2xx (based on Response.ok), assume it's an error - // See https://developer.mozilla.org/en-US/docs/Web/API/GlobalFetch/fetch - if (!(res && res.ok)) { - const errorObject = { - message: 'HTTP Error: Requested page not reachable', - status: `${res.status} ${res.statusText}`, - requestURI: res.url - } - throw errorObject - } - return res -} - export default function baseRequest(url, { - jsonBody, query, urlTemplateSpec, ...fetchConfig + jsonBody, + query, + urlTemplateSpec, + ...fetchConfig } = {}, requestTimeout) { let expandedUrl = url diff --git a/src/request.js b/src/request.js index d436e18..967a41f 100644 --- a/src/request.js +++ b/src/request.js @@ -12,7 +12,7 @@ const DEFAULT_REQUEST_CONFIG = { } const BACKOFF_DELAY = 500 // 0.5 seconds -const ERROR = 'HTTP Error: Requested page not reachable' +const ERROR_FROM_SERVER = 'HTTP Error: Requested page not reachable' /** * @private * Small wrapper around js-utility-belt's request that provides url resolving, @@ -87,7 +87,7 @@ export default class Request { if (!this.connectionError) { this.retries = 0 this.backoffTime = null - } else if (this.connectionError.message === ERROR) { + } else if (this.connectionError.message === ERROR_FROM_SERVER) { // If status is not a 2xx (based on Response.ok), throw error this.retries = 0 this.backoffTime = null diff --git a/test/base-request/test_baseRequest.js b/test/base-request/test_baseRequest.js index 9f89b5b..ffc11cb 100644 --- a/test/base-request/test_baseRequest.js +++ b/test/base-request/test_baseRequest.js @@ -3,7 +3,21 @@ // Code is Apache-2.0 and docs are CC-BY-4.0 import test from 'ava' -import baseRequest from '../../src/baseRequest' +import rewire from 'rewire' + +const baseRequestFile = rewire('../../src/baseRequest.js') +const baseRequest = baseRequestFile.__get__('baseRequest') +const handleResponse = baseRequestFile.__get__('handleResponse') + +test('HandleResponse does not throw error for response ok', t => { + const testObj = { + ok: true + } + const expected = testObj + const actual = handleResponse(testObj) + + t.deepEqual(actual, expected) +}) test('baseRequest test query and vsprint', async t => { const target = { diff --git a/test/request/test_request.js b/test/request/test_request.js new file mode 100644 index 0000000..99e3ce6 --- /dev/null +++ b/test/request/test_request.js @@ -0,0 +1,31 @@ +// Copyright BigchainDB GmbH and BigchainDB contributors +// SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) +// Code is Apache-2.0 and docs are CC-BY-4.0 + +import test from 'ava' +import Connection from '../../src/connection' + + +const conn = new Connection() + +test('Ensure that BackoffTimedelta works properly', t => { + const req = conn.transport.pickConnection() + req.backoffTime = Date.now() + 50 + const target = req.getBackoffTimedelta() + // The value should be close to 50 + t.is(target > 45, true) +}) + +test('Ensure that updateBackoffTime throws and error on TimeoutError', async t => { + const req = conn.transport.pickConnection() + const target = { + message: 'TimeoutError' + } + req.connectionError = target + + const error = t.throws(() => { + req.updateBackoffTime() + }) + + t.deepEqual(target, error) +})