2018-08-10 12:49:26 +02:00
|
|
|
// 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
|
|
|
|
|
2021-03-09 07:52:03 +01:00
|
|
|
import { Promise } from 'es6-promise'
|
2017-06-12 16:57:29 +02:00
|
|
|
import fetchPonyfill from 'fetch-ponyfill'
|
2021-03-09 07:52:03 +01:00
|
|
|
import { vsprintf } from 'sprintf-js'
|
2017-04-26 15:58:19 +02:00
|
|
|
|
2017-06-12 16:57:29 +02:00
|
|
|
import formatText from './format_text'
|
|
|
|
import stringifyAsQueryParam from './stringify_as_query_param'
|
2017-04-26 15:58:19 +02:00
|
|
|
|
2017-06-12 16:57:29 +02:00
|
|
|
const fetch = fetchPonyfill(Promise)
|
2017-04-26 15:58:19 +02:00
|
|
|
|
2021-03-09 14:20:00 +01:00
|
|
|
export function ResponseError(message, status, requestURI) {
|
2021-03-09 07:52:03 +01:00
|
|
|
this.name = 'ResponseError'
|
|
|
|
this.message = message
|
|
|
|
this.status = status
|
|
|
|
this.requestURI = requestURI
|
|
|
|
this.stack = (new Error()).stack
|
|
|
|
}
|
|
|
|
|
2021-03-09 14:20:00 +01:00
|
|
|
ResponseError.prototype = new Error()
|
2017-04-26 15:58:19 +02:00
|
|
|
|
2018-08-30 12:26:14 +02:00
|
|
|
/**
|
|
|
|
* @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)) {
|
2021-03-09 14:20:00 +01:00
|
|
|
throw new ResponseError(
|
|
|
|
'HTTP Error: Requested page not reachable',
|
|
|
|
`${res.status} ${res.statusText}`,
|
|
|
|
res.url
|
|
|
|
)
|
2018-08-30 12:26:14 +02:00
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
2017-04-26 15:58:19 +02:00
|
|
|
/**
|
2018-05-14 17:14:40 +02:00
|
|
|
* @private
|
2017-04-26 15:58:19 +02:00
|
|
|
* imported from https://github.com/bigchaindb/js-utility-belt/
|
|
|
|
*
|
|
|
|
* Global fetch wrapper that adds some basic error handling and ease of use enhancements.
|
|
|
|
* Considers any non-2xx response as an error.
|
|
|
|
*
|
|
|
|
* For more information on fetch, see https://developer.mozilla.org/en-US/docs/Web/API/GlobalFetch/fetch.
|
|
|
|
*
|
|
|
|
* Expects fetch to already be available (either in a ES6 environment, bundled through webpack, or
|
|
|
|
* injected through a polyfill).
|
|
|
|
*
|
|
|
|
* @param {string} url Url to request. Can be specified as a sprintf format string (see
|
|
|
|
* https://github.com/alexei/sprintf.js) that will be resolved using
|
|
|
|
* `config.urlTemplateSpec`.
|
2018-05-14 17:14:40 +02:00
|
|
|
* @param {Object} config Additional configuration, mostly passed to fetch as its 'init' config
|
2017-04-26 15:58:19 +02:00
|
|
|
* (see https://developer.mozilla.org/en-US/docs/Web/API/GlobalFetch/fetch#Parameters).
|
|
|
|
* @param {*} config.jsonBody Json payload to the request. Will automatically be
|
|
|
|
* JSON.stringify()-ed and override `config.body`.
|
2018-05-14 17:14:40 +02:00
|
|
|
* @param {string|Object} config.query Query parameter to append to the end of the url.
|
2017-04-26 15:58:19 +02:00
|
|
|
* If specified as an object, keys will be
|
|
|
|
* decamelized into snake case first.
|
2018-05-14 17:14:40 +02:00
|
|
|
* @param {*[]|Object} config.urlTemplateSpec Format spec to use to expand the url (see sprintf).
|
2017-04-26 15:58:19 +02:00
|
|
|
* @param {*} config.* All other options are passed through to fetch.
|
2018-08-30 12:26:14 +02:00
|
|
|
* @param {integer} requestTimeout Timeout for a single request
|
2017-04-26 15:58:19 +02:00
|
|
|
*
|
2018-08-30 12:26:14 +02:00
|
|
|
* @return {Promise} If requestTimeout the timeout function will be called. Otherwise resolve the
|
|
|
|
* Promise with the handleResponse function
|
2017-04-26 15:58:19 +02:00
|
|
|
*/
|
2018-01-04 10:15:45 +01:00
|
|
|
export default function baseRequest(url, {
|
2018-08-30 12:26:14 +02:00
|
|
|
jsonBody,
|
|
|
|
query,
|
|
|
|
urlTemplateSpec,
|
|
|
|
...fetchConfig
|
2018-08-29 16:39:15 +02:00
|
|
|
} = {}, requestTimeout) {
|
2017-06-12 16:57:29 +02:00
|
|
|
let expandedUrl = url
|
2017-04-26 15:58:19 +02:00
|
|
|
|
|
|
|
if (urlTemplateSpec != null) {
|
|
|
|
if (Array.isArray(urlTemplateSpec) && urlTemplateSpec.length) {
|
|
|
|
// Use vsprintf for the array call signature
|
2017-06-12 16:57:29 +02:00
|
|
|
expandedUrl = vsprintf(url, urlTemplateSpec)
|
2017-04-26 15:58:19 +02:00
|
|
|
} else if (urlTemplateSpec &&
|
2018-05-14 17:14:40 +02:00
|
|
|
typeof urlTemplateSpec === 'object' &&
|
|
|
|
Object.keys(urlTemplateSpec).length) {
|
2017-06-12 16:57:29 +02:00
|
|
|
expandedUrl = formatText(url, urlTemplateSpec)
|
2017-04-26 15:58:19 +02:00
|
|
|
} else if (process.env.NODE_ENV !== 'production') {
|
|
|
|
// eslint-disable-next-line no-console
|
2017-06-12 16:57:29 +02:00
|
|
|
console.warn('Supplied urlTemplateSpec was not an array or object. Ignoring...')
|
2017-04-26 15:58:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (query != null) {
|
|
|
|
if (typeof query === 'string') {
|
2017-06-12 16:57:29 +02:00
|
|
|
expandedUrl += query
|
2017-04-26 15:58:19 +02:00
|
|
|
} else if (query && typeof query === 'object') {
|
2017-06-12 16:57:29 +02:00
|
|
|
expandedUrl += stringifyAsQueryParam(query)
|
2017-04-26 15:58:19 +02:00
|
|
|
} else if (process.env.NODE_ENV !== 'production') {
|
|
|
|
// eslint-disable-next-line no-console
|
2017-06-12 16:57:29 +02:00
|
|
|
console.warn('Supplied query was not a string or object. Ignoring...')
|
2017-04-26 15:58:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (jsonBody != null) {
|
2017-06-12 16:57:29 +02:00
|
|
|
fetchConfig.body = JSON.stringify(jsonBody)
|
2017-04-26 15:58:19 +02:00
|
|
|
}
|
2018-08-29 16:39:15 +02:00
|
|
|
if (requestTimeout) {
|
|
|
|
return timeout(requestTimeout, fetch.fetch(expandedUrl, fetchConfig))
|
|
|
|
.then(handleResponse)
|
|
|
|
} else {
|
|
|
|
return fetch.fetch(expandedUrl, fetchConfig)
|
|
|
|
.then(handleResponse)
|
|
|
|
}
|
2017-06-12 16:57:29 +02:00
|
|
|
}
|