Comparing version 3.8.1 to 4.0.0
@@ -0,1 +1,33 @@ | ||
### 4.0.0 | ||
#### Breaking Changes | ||
Now requires a modern browser that can support basic | ||
ES6+ syntax | ||
*See: [`739cf50`](https://github.com/groupon/gofer/commit/739cf508b64976b1eb69dcb281d4968a1fa1b2f7)* | ||
you no longer get a Bluebird-flavored Promise back when | ||
you call gofer functions in promise mode | ||
*See: [`f2bc735`](https://github.com/groupon/gofer/commit/f2bc73587bbfae740adf191425538f3ea5dacc25)* | ||
Now requires Node 8.x and/or a browser with native URL and URLSearchParams support | ||
*See: [`bbe30c1`](https://github.com/groupon/gofer/commit/bbe30c1491689fabe3288073f39781043b0d4d62)* | ||
#### Commits | ||
* Modernize and Reduce package size - **[@dbushong](https://github.com/dbushong)** [#103](https://github.com/groupon/gofer/pull/103) | ||
- [`c9e684c`](https://github.com/groupon/gofer/commit/c9e684c4cb56816a88848b37127343c79aae42bb) **chore:** upgrade linting & node reqs | ||
- [`56a3394`](https://github.com/groupon/gofer/commit/56a339462f5f48d04acc7bcb358f950714fe4303) **refactor:** eslint --fix test | ||
- [`739cf50`](https://github.com/groupon/gofer/commit/739cf508b64976b1eb69dcb281d4968a1fa1b2f7) **refactor:** eslint --fix lib | ||
- [`7f5a2ea`](https://github.com/groupon/gofer/commit/7f5a2ea1dbb397d28a66ffb34e439ed9acf45056) **test:** fix test for new .eslintrc.json | ||
- [`f34c7eb`](https://github.com/groupon/gofer/commit/f34c7eb3d9f23e7688ce3b36228d7ae013e3e224) **test:** fix tests for new mochify | ||
- [`f2bc735`](https://github.com/groupon/gofer/commit/f2bc73587bbfae740adf191425538f3ea5dacc25) **refactor:** use native Promises instead of bluebird | ||
- [`d869ea6`](https://github.com/groupon/gofer/commit/d869ea632e45682554fb6a855bee293b02e276fb) **refactor:** use lodash.* for remaining needs | ||
- [`0e77ee5`](https://github.com/groupon/gofer/commit/0e77ee56425ad0a76c5aa1f7bb63f299715e9710) **test:** use proper `Buffer.from()` | ||
- [`bbe30c1`](https://github.com/groupon/gofer/commit/bbe30c1491689fabe3288073f39781043b0d4d62) **refactor:** use native URL & URLSearchParams | ||
### 3.8.1 | ||
@@ -2,0 +34,0 @@ |
@@ -38,11 +38,4 @@ /* | ||
this.message = | ||
'API Request returned a response outside the status code range ' + | ||
'(code: ' + | ||
statusCode + | ||
', range: [' + | ||
min + | ||
', ' + | ||
max + | ||
'])'; | ||
this.message = `${'API Request returned a response outside the status code range ' + | ||
'(code: '}${statusCode}, range: [${min}, ${max}])`; | ||
this.headers = headers; | ||
@@ -93,3 +86,3 @@ this.statusCode = statusCode; | ||
) { | ||
var error; | ||
let error; | ||
switch (statusCode) { | ||
@@ -96,0 +89,0 @@ case 301: |
@@ -36,19 +36,8 @@ /* | ||
/* eslint-env browser */ | ||
/* global URLSearchParams */ | ||
var Url = require('url'); | ||
var Bluebird = require('bluebird'); | ||
var assign = require('lodash/assign'); | ||
var noop = require('lodash/noop'); | ||
var partial = require('lodash/partial'); | ||
var qsParser = require('qs'); | ||
const StatusCodeError = require('./errors').StatusCodeError; | ||
const { replacePathParams, updateSearch } = require('./url'); | ||
var StatusCodeError = require('./errors').StatusCodeError; | ||
var urlUtils = require('./url'); | ||
const DEFAULT_TIMEOUT = 10 * 1000; | ||
var applyBaseUrl = urlUtils.applyBaseUrl; | ||
var replacePathParams = urlUtils.replacePathParams; | ||
var DEFAULT_TIMEOUT = 10 * 1000; | ||
if (typeof fetch !== 'function') { | ||
@@ -66,3 +55,3 @@ throw new Error('Requires native fetch or polyfill'); | ||
var reqProperties = { | ||
const reqProperties = { | ||
json: { | ||
@@ -87,3 +76,3 @@ value: function json() { | ||
(typeof FormData !== 'undefined' && body instanceof FormData) || | ||
(typeof URLSearchParams !== 'undefined' && body instanceof URLSearchParams) | ||
body instanceof URLSearchParams | ||
); | ||
@@ -94,3 +83,3 @@ } | ||
if (!isValidBody) { | ||
throw new TypeError('Invalid body ' + typeof body); | ||
throw new TypeError(`Invalid body ${typeof body}`); | ||
} | ||
@@ -100,22 +89,6 @@ return body; | ||
function generateSearch(queryString, qs) { | ||
var query = assign(qsParser.parse(queryString), qs || {}); | ||
var filtered = {}; | ||
var queryKeys = Object.keys(query).filter(function ensureSet(key) { | ||
var value = query[key]; | ||
var isSet = value !== null && value !== undefined; | ||
if (isSet) { | ||
filtered[key] = value; | ||
} | ||
return isSet; | ||
}); | ||
if (queryKeys.length === 0) return ''; | ||
return '?' + qsParser.stringify(filtered); | ||
} | ||
function filterHeaders(headers) { | ||
var filtered = {}; | ||
Object.keys(headers).forEach(function ensureSet(name) { | ||
var value = headers[name]; | ||
const filtered = {}; | ||
Object.keys(headers).forEach(name => { | ||
const value = headers[name]; | ||
if (value !== null && value !== undefined) { | ||
@@ -135,3 +108,3 @@ filtered[name] = headers[name]; | ||
throw new TypeError( | ||
'Invalid status code ' + JSON.stringify(value) + ', not a number' | ||
`Invalid status code ${JSON.stringify(value)}, not a number` | ||
); | ||
@@ -145,3 +118,3 @@ } | ||
function parseErrorBody(rawBody) { | ||
var source = '' + rawBody; | ||
const source = `${rawBody}`; | ||
try { | ||
@@ -155,4 +128,4 @@ return JSON.parse(source); | ||
function verifyAndExtendResponse(url, options, response) { | ||
var min = defaultStatusCode(options.minStatusCode, 200); | ||
var max = defaultStatusCode(options.maxStatusCode, 299); | ||
const min = defaultStatusCode(options.minStatusCode, 200); | ||
const max = defaultStatusCode(options.maxStatusCode, 299); | ||
@@ -163,8 +136,8 @@ function isAcceptableStatus(code) { | ||
var originalHeaders = response.headers; | ||
var cachedHeaders; | ||
const originalHeaders = response.headers; | ||
let cachedHeaders; | ||
function getCachedHeaders() { | ||
if (!cachedHeaders) { | ||
cachedHeaders = Object.create(originalHeaders); | ||
originalHeaders.forEach(function addHeader(value, name) { | ||
originalHeaders.forEach((value, name) => { | ||
if (name) { | ||
@@ -185,3 +158,3 @@ cachedHeaders[name] = value; | ||
function generateStatusCodeError(code) { | ||
var error = StatusCodeError.create( | ||
const error = StatusCodeError.create( | ||
code, | ||
@@ -203,3 +176,3 @@ min, | ||
.then(parseErrorBody) | ||
.then(null, noop) | ||
.then(null, () => {}) | ||
.then(rejectWithBody); | ||
@@ -219,3 +192,3 @@ } | ||
throw new TypeError( | ||
'Invalid timeout ' + JSON.stringify(value) + ', not a number' | ||
`Invalid timeout ${JSON.stringify(value)}, not a number` | ||
); | ||
@@ -229,18 +202,35 @@ } | ||
function fetchUrl(url, options) { | ||
if (typeof url !== 'string') { | ||
throw new TypeError('url has to be a string'); | ||
} | ||
options = options || {}; | ||
options = options || {}; | ||
var urlObj = Url.parse(url); | ||
if (options.baseUrl && typeof options.baseUrl === 'string') { | ||
urlObj = applyBaseUrl(urlObj, options.baseUrl); | ||
const { | ||
auth, | ||
json, | ||
form, | ||
headers, | ||
method = 'GET', | ||
redirect, | ||
serviceName, | ||
endpointName, | ||
pathParams, | ||
methodName, | ||
} = options; | ||
let { baseUrl } = options; | ||
if (!baseUrl && url && (typeof url === 'string' || url instanceof URL)) { | ||
baseUrl = location.href; | ||
} | ||
if (baseUrl) { | ||
if (baseUrl.includes('?')) { | ||
throw new Error('baseUrl may not contain a query string'); | ||
} | ||
if (baseUrl.substr(-1) !== '/') baseUrl += '/'; | ||
if (typeof url === 'string' && url[0] === '/') url = url.substr(1); | ||
} | ||
var json = options.json; | ||
var form = options.form; | ||
var body = validateBody(options.body); | ||
const urlObj = new URL(url, baseUrl); | ||
var defaultHeaders = {}; | ||
let body = validateBody(options.body); | ||
const defaultHeaders = {}; | ||
if (json !== undefined && json !== null) { | ||
@@ -252,3 +242,3 @@ defaultHeaders['Content-Type'] = 'application/json;charset=UTF-8'; | ||
throw new TypeError( | ||
'Invalid form body (' + typeof form + ', expected object)' | ||
`Invalid form body (${typeof form}, expected object)` | ||
); | ||
@@ -258,42 +248,36 @@ } | ||
'application/x-www-form-urlencoded;charset=UTF-8'; | ||
body = qsParser.stringify(form); | ||
body = updateSearch(new URLSearchParams(), form).toString(); | ||
} | ||
var auth = options.auth; | ||
if (typeof auth === 'string') { | ||
defaultHeaders.Authorization = 'Basic ' + btoa(auth); | ||
defaultHeaders.Authorization = `Basic ${btoa(auth)}`; | ||
} else if (auth !== null && typeof auth === 'object') { | ||
defaultHeaders.Authorization = | ||
'Basic ' + btoa(auth.username + ':' + auth.password); | ||
defaultHeaders.Authorization = `Basic ${btoa( | ||
`${auth.username}:${auth.password}` | ||
)}`; | ||
} | ||
var timeout = defaultTimeout(options.timeout, DEFAULT_TIMEOUT); | ||
const timeout = defaultTimeout(options.timeout, DEFAULT_TIMEOUT); | ||
var method = options.method || 'GET'; | ||
var nativeOptions = { | ||
const nativeOptions = { | ||
// All official fetch options: | ||
method: method, | ||
headers: filterHeaders(assign(defaultHeaders, options.headers)), | ||
body: body, | ||
redirect: options.redirect, | ||
method, | ||
headers: filterHeaders({ ...defaultHeaders, ...headers }), | ||
body, | ||
redirect, | ||
// Some things we might want to expose for instrumentation to pick up: | ||
serviceName: options.serviceName, | ||
endpointName: options.endpointName, | ||
methodName: options.methodName || method.toLowerCase(), | ||
pathParams: options.pathParams, | ||
serviceName, | ||
endpointName, | ||
methodName: methodName || method.toLowerCase(), | ||
pathParams, | ||
}; | ||
var patchedPathname = replacePathParams(urlObj.pathname, options.pathParams); | ||
var patchedSearch = generateSearch(urlObj.query, options.qs); | ||
var patchedUrl = { | ||
protocol: urlObj.protocol, | ||
hostname: urlObj.hostname, | ||
port: urlObj.port, | ||
pathname: patchedPathname, | ||
search: patchedSearch, | ||
path: patchedPathname + patchedSearch, | ||
}; | ||
var nativeUrl = Url.format(patchedUrl); | ||
var result = new Bluebird(function withResponseTimeout(resolve, reject) { | ||
urlObj.pathname = replacePathParams(urlObj.pathname, options.pathParams); | ||
updateSearch(urlObj.searchParams, options.qs); | ||
const nativeUrl = urlObj.toString(); | ||
const result = new Promise((resolve, reject) => { | ||
function onTimedOut() { | ||
var error = new Error('Fetching from ' + urlObj.hostname + ' timed out'); | ||
const error = new Error(`Fetching from ${urlObj.hostname} timed out`); | ||
error.code = 'ETIMEDOUT'; | ||
@@ -303,3 +287,3 @@ error.timeout = options.timeout; | ||
} | ||
var timeoutHandle = setTimeout(onTimedOut, timeout); | ||
const timeoutHandle = setTimeout(onTimedOut, timeout); | ||
function killTimeout(response) { | ||
@@ -309,5 +293,5 @@ clearTimeout(timeoutHandle); | ||
} | ||
Bluebird.resolve(fetch(nativeUrl, nativeOptions)) | ||
Promise.resolve(fetch(nativeUrl, nativeOptions)) | ||
.then(killTimeout) | ||
.then(partial(verifyAndExtendResponse, nativeUrl, options)) | ||
.then(verifyAndExtendResponse.bind(null, nativeUrl, options)) | ||
.then(resolve, reject); | ||
@@ -314,0 +298,0 @@ }); |
150
lib/fetch.js
@@ -35,23 +35,17 @@ /* | ||
var http = require('http'); | ||
var https = require('https'); | ||
var parseUrl = require('url').parse; | ||
const http = require('http'); | ||
const https = require('https'); | ||
const { URL, URLSearchParams } = require('url'); | ||
var assign = require('lodash/assign'); | ||
var qsParser = require('qs'); | ||
const request = require('./request'); | ||
const wrapForCallback = require('./legacy'); | ||
const { replacePathParams, updateSearch } = require('./url'); | ||
var urlUtils = require('./url'); | ||
var request = require('./request'); | ||
var wrapForCallback = require('./legacy'); | ||
const DEFAULT_CONNECT_TIMEOUT = 1000; | ||
const DEFAULT_TIMEOUT = 10 * 1000; | ||
var applyBaseUrl = urlUtils.applyBaseUrl; | ||
var replacePathParams = urlUtils.replacePathParams; | ||
var DEFAULT_CONNECT_TIMEOUT = 1000; | ||
var DEFAULT_TIMEOUT = 10 * 1000; | ||
var agentsByService = {}; | ||
const agentsByService = {}; | ||
function getAgentsForService(options) { | ||
var serviceName = options.serviceName; | ||
var agents = agentsByService[serviceName]; | ||
const serviceName = options.serviceName; | ||
let agents = agentsByService[serviceName]; | ||
if (!agents) { | ||
@@ -77,3 +71,3 @@ agents = agentsByService[serviceName] = { | ||
if (!isValidBody(body)) { | ||
throw new TypeError('Invalid body ' + typeof body); | ||
throw new TypeError(`Invalid body ${typeof body}`); | ||
} | ||
@@ -84,4 +78,4 @@ return body; | ||
function getAgent(options, urlObj) { | ||
var agent; | ||
var isHttps = urlObj.protocol === 'https:'; | ||
let agent; | ||
const isHttps = urlObj.protocol === 'https:'; | ||
if (options.agent === false) { | ||
@@ -100,22 +94,6 @@ return isHttps ? new https.Agent() : new http.Agent(); | ||
function generateSearch(queryString, qs) { | ||
var query = assign(qsParser.parse(queryString), qs || {}); | ||
var filtered = {}; | ||
var queryKeys = Object.keys(query).filter(function ensureSet(key) { | ||
var value = query[key]; | ||
var isSet = value !== null && value !== undefined; | ||
if (isSet) { | ||
filtered[key] = value; | ||
} | ||
return isSet; | ||
}); | ||
if (queryKeys.length === 0) return ''; | ||
return '?' + qsParser.stringify(filtered); | ||
} | ||
function filterHeaders(headers) { | ||
var filtered = {}; | ||
Object.keys(headers).forEach(function ensureSet(name) { | ||
var value = headers[name]; | ||
const filtered = {}; | ||
Object.keys(headers).forEach(name => { | ||
const value = headers[name]; | ||
if (value !== null && value !== undefined) { | ||
@@ -132,25 +110,16 @@ filtered[name] = headers[name]; | ||
if (typeof auth !== 'object') { | ||
throw new TypeError('Invalid auth option ' + typeof auth); | ||
throw new TypeError(`Invalid auth option ${typeof auth}`); | ||
} | ||
var user = auth.user || auth.username; | ||
var pass = auth.pass || auth.password; | ||
const user = auth.user || auth.username; | ||
const pass = auth.pass || auth.password; | ||
if (typeof user !== 'string' || typeof pass !== 'string') { | ||
throw new TypeError('Auth has to be a user/pass pair'); | ||
} | ||
return user + ':' + pass; | ||
return `${user}:${pass}`; | ||
} | ||
function buildUserAgent(options) { | ||
return ( | ||
(options.clientName || 'noServiceName') + | ||
'/' + | ||
(options.clientVersion || 'noServiceVersion') + | ||
' (' + | ||
(options.appName || 'noAppName') + | ||
'/' + | ||
(options.appSha || 'noAppSha') + | ||
'; ' + | ||
(options.fqdn || 'noFQDN') + | ||
')' | ||
); | ||
function buildUserAgent({ clientName, clientVersion, appName, appSha, fqdn }) { | ||
return `${clientName || 'noServiceName'}/${clientVersion || | ||
'noServiceVersion'} (${appName || 'noAppName'}/${appSha || | ||
'noAppSha'}; ${fqdn || 'noFQDN'})`; | ||
} | ||
@@ -162,3 +131,3 @@ | ||
throw new TypeError( | ||
'Invalid timeout ' + JSON.stringify(value) + ', not a number' | ||
`Invalid timeout ${JSON.stringify(value)}, not a number` | ||
); | ||
@@ -178,3 +147,3 @@ } | ||
throw new TypeError( | ||
'Invalid status code ' + JSON.stringify(value) + ', not a number' | ||
`Invalid status code ${JSON.stringify(value)}, not a number` | ||
); | ||
@@ -187,3 +156,3 @@ } | ||
var IPv4 = /^\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}$/; | ||
const IPv4 = /^\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}$/; | ||
function canApplySearchDomain(hostname) { | ||
@@ -198,6 +167,6 @@ if (hostname === 'localhost') return false; | ||
if (!hostname || typeof hostname !== 'string') { | ||
throw new Error('Invalid URI ' + JSON.stringify(hostname)); | ||
throw new Error(`Invalid URI ${JSON.stringify(hostname)}`); | ||
} | ||
if (searchDomain && canApplySearchDomain(hostname)) { | ||
return hostname + '.' + searchDomain + '.'; | ||
return `${hostname}.${searchDomain}.`; | ||
} | ||
@@ -208,13 +177,11 @@ return hostname; | ||
function fetchUrlObj(urlObj, options) { | ||
if (options.baseUrl && typeof options.baseUrl === 'string') { | ||
urlObj = applyBaseUrl(urlObj, options.baseUrl); | ||
} | ||
const { json, form, baseUrl, qs, headers, pathParams, auth } = options; | ||
var defaultHeaders = { | ||
if (baseUrl && typeof baseUrl === 'string') urlObj = new URL(urlObj, baseUrl); | ||
const defaultHeaders = { | ||
'Accept-Encoding': 'gzip', | ||
'User-Agent': buildUserAgent(options), | ||
}; | ||
var body = validateBody(options.body); | ||
var json = options.json; | ||
var form = options.form; | ||
let body = validateBody(options.body); | ||
@@ -227,3 +194,3 @@ if (json !== undefined && json !== null) { | ||
throw new TypeError( | ||
'Invalid form body (' + typeof form + ', expected object)' | ||
`Invalid form body (${typeof form}, expected object)` | ||
); | ||
@@ -233,9 +200,9 @@ } | ||
'application/x-www-form-urlencoded;charset=UTF-8'; | ||
body = qsParser.stringify(form); | ||
body = updateSearch(new URLSearchParams(), form).toString(); | ||
} | ||
var hostname = buildHostname(urlObj.hostname, options.searchDomain); | ||
const hostname = buildHostname(urlObj.hostname, options.searchDomain); | ||
var agent = getAgent(options, urlObj); | ||
assign(agent.options, { | ||
const agent = getAgent(options, urlObj); | ||
Object.assign(agent.options, { | ||
// http: | ||
@@ -254,3 +221,3 @@ keepAlive: options.keepAlive, | ||
}); | ||
assign(agent, { | ||
Object.assign(agent, { | ||
maxSockets: options.maxSockets || Infinity, | ||
@@ -262,17 +229,17 @@ keepAliveMsecs: options.keepAliveMsecs || 1000, | ||
var method = options.method || 'GET'; | ||
updateSearch(urlObj.searchParams, qs); | ||
const method = options.method || 'GET'; | ||
return request({ | ||
agent: agent, | ||
agent, | ||
protocol: urlObj.protocol, | ||
host: urlObj.host, | ||
hostname: hostname, | ||
hostname, | ||
port: urlObj.port, | ||
method: method, | ||
path: | ||
replacePathParams(urlObj.pathname, options.pathParams) + | ||
generateSearch(urlObj.query, options.qs), | ||
headers: filterHeaders(assign(defaultHeaders, options.headers)), | ||
auth: unifyAuth(options.auth || urlObj.auth), | ||
method, | ||
path: replacePathParams(urlObj.pathname, pathParams) + urlObj.search, | ||
headers: filterHeaders({ ...defaultHeaders, ...headers }), | ||
auth: unifyAuth(auth || (urlObj.username && urlObj)), | ||
localAddress: options.localAddress, | ||
body: body, | ||
body, | ||
connectTimeout: defaultTimeout( | ||
@@ -304,8 +271,17 @@ options.connectTimeout, | ||
function fetch(url, options, callback) { | ||
if (typeof url !== 'string') { | ||
throw new TypeError('url has to be a string'); | ||
if (!options) options = {}; | ||
let { baseUrl } = options; | ||
if (baseUrl) { | ||
if (baseUrl.includes('?')) { | ||
throw new Error('baseUrl may not contain a query string'); | ||
} | ||
if (baseUrl.substr(-1) !== '/') baseUrl += '/'; | ||
if (typeof url === 'string' && url[0] === '/') url = url.substr(1); | ||
} | ||
return nodeify(fetchUrlObj(parseUrl(url), options || {}), callback); | ||
return nodeify( | ||
fetchUrlObj(new URL(url, baseUrl || undefined), options), | ||
callback | ||
); | ||
} | ||
module.exports = fetch; |
@@ -35,8 +35,8 @@ /* | ||
var isObjectLike = require('lodash/isObjectLike'); | ||
var isPlainObject = require('lodash/isPlainObject'); | ||
var merge = require('lodash/merge'); | ||
var mergeWith = require('lodash/mergeWith'); | ||
const fetch = require('./fetch'); | ||
var fetch = require('./fetch'); | ||
const merge = require('lodash.merge'); | ||
const mergeWith = require('lodash.mergewith'); | ||
const isObjectLike = require('lodash.isobjectlike'); | ||
const isPlainObject = require('lodash.isplainobject'); | ||
@@ -49,9 +49,9 @@ function Gofer(config, serviceName, clientVersion, clientName) { | ||
var globalDefaults = config.globalDefaults || {}; | ||
var serviceDefaults = config[serviceName] || {}; | ||
const globalDefaults = config.globalDefaults || {}; | ||
const serviceDefaults = config[serviceName] || {}; | ||
this.defaults = merge( | ||
{ | ||
serviceName: serviceName, | ||
clientVersion: clientVersion, | ||
clientName: clientName, | ||
serviceName, | ||
clientVersion, | ||
clientName, | ||
endpointDefaults: {}, | ||
@@ -68,3 +68,3 @@ }, | ||
Gofer.prototype.with = function withOverrides(overrides) { | ||
var cloned = this.clone(); | ||
const cloned = this.clone(); | ||
merge(cloned.defaults, overrides); | ||
@@ -80,4 +80,4 @@ return cloned; | ||
Gofer.prototype.clone = function clone() { | ||
var Client = this.constructor || Gofer; | ||
var config = { globalDefaults: this.defaults }; | ||
const Client = this.constructor || Gofer; | ||
const config = { globalDefaults: this.defaults }; | ||
return new Client(config); | ||
@@ -98,3 +98,3 @@ }; | ||
var DEFAULT_MERGER; // = undefined (see lodash/mergeWith docs) | ||
const DEFAULT_MERGER = undefined; // (see lodash/mergeWith docs) | ||
function preventComplexMerge(objValue, srcValue) { | ||
@@ -123,4 +123,4 @@ if (!isObjectLike(objValue) && !isObjectLike(srcValue)) { | ||
options = options || {}; | ||
var endpointName = options.endpointName || defaults.endpointName; | ||
var mergedOptions = mergeWith( | ||
const endpointName = options.endpointName || defaults.endpointName; | ||
const mergedOptions = mergeWith( | ||
{}, | ||
@@ -145,4 +145,4 @@ this.defaults, | ||
['get', 'put', 'post', 'patch', 'del', 'head', 'options', 'delete'].forEach( | ||
function withVerb(verb) { | ||
var httpMethod = verb === 'del' ? 'DELETE' : verb.toUpperCase(); | ||
verb => { | ||
const httpMethod = verb === 'del' ? 'DELETE' : verb.toUpperCase(); | ||
Gofer.prototype[verb] = fetchWithDefaults({ method: httpMethod }); | ||
@@ -157,5 +157,5 @@ } | ||
get: function _getCachedEndpoint() { | ||
var withDefaults = fetchWithDefaults({ endpointName: name }).bind(this); | ||
var value = endpointFn(withDefaults); | ||
Object.defineProperty(this, name, { value: value }); | ||
const withDefaults = fetchWithDefaults({ endpointName: name }).bind(this); | ||
const value = endpointFn(withDefaults); | ||
Object.defineProperty(this, name, { value }); | ||
return value; | ||
@@ -162,0 +162,0 @@ }, |
@@ -36,3 +36,3 @@ /* | ||
function hasJsonHeader(res) { | ||
var contentType = res.headers['content-type']; | ||
const contentType = res.headers['content-type']; | ||
if (!contentType) return false; | ||
@@ -47,3 +47,3 @@ return contentType.indexOf('application/json') === 0; | ||
function safeParseJSON(source) { | ||
var data = { parseError: null, body: source }; | ||
const data = { parseError: null, body: source }; | ||
try { | ||
@@ -58,12 +58,6 @@ data.body = JSON.parse(source); | ||
function wrapForCallback(promise, callback) { | ||
function onResponse(error, response) { | ||
if (error) { | ||
return void callback(error, error.body, response); | ||
} | ||
function onBody(bodyError, body) { | ||
if (bodyError) { | ||
return void callback(bodyError, null, response); | ||
} | ||
function onResponse(response) { | ||
function onBody(body) { | ||
if (isJsonResponse(response, body)) { | ||
var result = safeParseJSON(body.toString()); | ||
const result = safeParseJSON(body.toString()); | ||
return void callback(result.parseError, result.body, response); | ||
@@ -73,6 +67,11 @@ } | ||
} | ||
response.rawBody().nodeify(onBody); | ||
response.rawBody().then(onBody, error => { | ||
callback(error); | ||
}); | ||
} | ||
promise.nodeify(onResponse); | ||
promise.then(onResponse, error => { | ||
callback(error, error.body); | ||
}); | ||
} | ||
module.exports = wrapForCallback; |
@@ -35,11 +35,10 @@ /* | ||
var http = require('http'); | ||
var https = require('https'); | ||
var formatUrl = require('url').format; | ||
const http = require('http'); | ||
const https = require('https'); | ||
const { URL } = require('url'); | ||
var Bluebird = require('bluebird'); | ||
var debug = require('debug')('gofer'); | ||
const debug = require('debug')('gofer'); | ||
var StatusCodeError = require('./errors').StatusCodeError; | ||
var resProperties = require('./response'); | ||
const StatusCodeError = require('./errors').StatusCodeError; | ||
const resProperties = require('./response'); | ||
@@ -57,5 +56,5 @@ function noop() {} | ||
function setIOTimeout(callback, ms) { | ||
var initialHandle = null; | ||
var delayHandle = null; | ||
var done = false; | ||
let initialHandle = null; | ||
let delayHandle = null; | ||
let done = false; | ||
function onDelay() { | ||
@@ -105,3 +104,3 @@ if (done) return; | ||
function parseErrorBody(rawBody) { | ||
var source = rawBody.toString(); | ||
const source = rawBody.toString(); | ||
try { | ||
@@ -114,3 +113,3 @@ return JSON.parse(source); | ||
var reqProperties = { | ||
const reqProperties = { | ||
json: { | ||
@@ -142,28 +141,26 @@ value: function json() { | ||
function buildFullUrl(options) { | ||
var pathParts = options.path.split('?'); | ||
return formatUrl({ | ||
protocol: options.protocol, | ||
hostname: options.hostname, | ||
const pathParts = options.path.split('?'); | ||
return Object.assign(new URL(`${options.protocol}//${options.hostname}`), { | ||
port: options.port, | ||
pathname: pathParts[0], | ||
search: pathParts[1], | ||
}); | ||
search: pathParts[1] || '', | ||
}).toString(); | ||
} | ||
function requestFunc(options, resolve, reject) { | ||
var host = options.host; | ||
var setHost = options.setHost; | ||
var fullUrl = buildFullUrl(options); | ||
const host = options.host; | ||
const setHost = options.setHost; | ||
const fullUrl = buildFullUrl(options); | ||
options.setHost = false; | ||
debug('-> %s %s', options.method, fullUrl); | ||
var reqObj = null; | ||
var resObj = null; | ||
var connectTimer = null; | ||
var responseTimer = null; | ||
var completionTimer = null; | ||
var socketTimer = null; | ||
let reqObj = null; | ||
let resObj = null; | ||
let connectTimer = null; | ||
let responseTimer = null; | ||
let completionTimer = null; | ||
let socketTimer = null; | ||
var startTime = Date.now(); | ||
var timing = { | ||
const startTime = Date.now(); | ||
const timing = { | ||
socket: null, | ||
@@ -205,4 +202,4 @@ connect: null, | ||
function isAcceptableStatus(code) { | ||
var min = options.minStatusCode; | ||
var max = options.maxStatusCode; | ||
const min = options.minStatusCode; | ||
const max = options.maxStatusCode; | ||
return (min === false || code >= min) && (max === false || code <= max); | ||
@@ -212,3 +209,3 @@ } | ||
function generateStatusCodeError() { | ||
var error = StatusCodeError.create( | ||
const error = StatusCodeError.create( | ||
resObj.statusCode, | ||
@@ -225,3 +222,3 @@ options.minStatusCode, | ||
.then(null, noop) | ||
.then(function rejectWithBody(body) { | ||
.then(body => { | ||
error.body = body; | ||
@@ -256,3 +253,3 @@ emitError(error); | ||
function onCompletionTimedOut() { | ||
var error = new Error('Request to ' + host + ' timed out'); | ||
const error = new Error(`Request to ${host} timed out`); | ||
error.code = 'ETIMEDOUT'; | ||
@@ -267,3 +264,3 @@ error.timeout = options.completionTimeout; | ||
function onResponseTimedOut(code) { | ||
var error = new Error('Fetching from ' + host + ' timed out'); | ||
const error = new Error(`Fetching from ${host} timed out`); | ||
error.code = code || 'ETIMEDOUT'; | ||
@@ -277,3 +274,3 @@ error.timeout = options.timeout; | ||
function onConnectTimedOut() { | ||
var error = new Error('Connection to ' + host + ' timed out'); | ||
const error = new Error(`Connection to ${host} timed out`); | ||
error.code = 'ECONNECTTIMEDOUT'; | ||
@@ -293,3 +290,3 @@ error.connectTimeout = options.connectTimeout; | ||
function onSocketTimedOut() { | ||
socketTimer = setImmediate(function checkRealTimeout() { | ||
socketTimer = setImmediate(() => { | ||
socketTimer = null; | ||
@@ -334,9 +331,9 @@ if (reqObj && reqObj.socket && reqObj.socket.readable) { | ||
var body = options.body; | ||
const body = options.body; | ||
if (typeof body === 'string') { | ||
req.setHeader('Content-Length', '' + Buffer.byteLength(body)); | ||
req.setHeader('Content-Length', `${Buffer.byteLength(body)}`); | ||
req.end(body); | ||
} else if (Buffer.isBuffer(body)) { | ||
req.setHeader('Content-Length', '' + body.length); | ||
req.setHeader('Content-Length', `${body.length}`); | ||
req.end(body); | ||
@@ -350,3 +347,3 @@ } else if (body && typeof body.pipe === 'function') { | ||
var protocolLib = options.protocol === 'https:' ? https : http; | ||
const protocolLib = options.protocol === 'https:' ? https : http; | ||
onRequest(protocolLib.request(options)); | ||
@@ -356,5 +353,5 @@ } | ||
function request(options) { | ||
var result = new Bluebird(requestFunc.bind(null, options)); | ||
const result = new Promise(requestFunc.bind(null, options)); | ||
return Object.defineProperties(result, reqProperties); | ||
} | ||
module.exports = request; |
@@ -35,8 +35,6 @@ /* | ||
var zlib = require('zlib'); | ||
const zlib = require('zlib'); | ||
var Bluebird = require('bluebird'); | ||
function getStreamForResponse(res) { | ||
var encoding = (res.headers['content-encoding'] || 'identity') | ||
const encoding = (res.headers['content-encoding'] || 'identity') | ||
.trim() | ||
@@ -53,4 +51,4 @@ .toLowerCase(); | ||
default: | ||
process.nextTick(function _invalidEncoding() { | ||
res.emit('error', new Error('Unknown content-encoding ' + encoding)); | ||
process.nextTick(() => { | ||
res.emit('error', new Error(`Unknown content-encoding ${encoding}`)); | ||
}); | ||
@@ -62,4 +60,4 @@ return res; | ||
function readBody(resolve, reject) { | ||
var stream = this; | ||
var chunks = []; | ||
const stream = this; | ||
const chunks = []; | ||
@@ -80,3 +78,3 @@ function addChunk(chunk) { | ||
function parseBody(rawBody) { | ||
var source = rawBody.toString(); | ||
const source = rawBody.toString(); | ||
try { | ||
@@ -104,3 +102,3 @@ return JSON.parse(source); | ||
value: function rawBody() { | ||
return new Bluebird(readBody.bind(this.stream())); | ||
return new Promise(readBody.bind(this.stream())); | ||
}, | ||
@@ -111,3 +109,3 @@ }, | ||
value: function text() { | ||
return new Bluebird(readBody.bind(this.stream())).then(toString); | ||
return new Promise(readBody.bind(this.stream())).then(toString); | ||
}, | ||
@@ -114,0 +112,0 @@ }, |
@@ -35,39 +35,39 @@ /* | ||
var url = require('url'); | ||
function applyBaseUrl(urlObj, baseUrl) { | ||
// If the url already is absolute, baseUrl isn't needed. | ||
if (urlObj.hostname) return urlObj; | ||
var base = url.parse(baseUrl); | ||
if (base.query) { | ||
throw new Error('baseUrl may not contain a query string'); | ||
/** | ||
* @typedef {string | QSVal[] | { [name: string]: QSVal }} QSVal | ||
* | ||
* @param {URLSearchParams} query | ||
* @param {{ [name: string]: QSVal }} qs | ||
* @param {string} prefix | ||
*/ | ||
function updateSearch(query, qs, path = []) { | ||
for (const [key, val] of Object.entries(qs || {})) { | ||
if (val == null) continue; | ||
if (typeof val === 'object') updateSearch(query, val, [...path, key]); | ||
else { | ||
const [first, ...rest] = [...path, key]; | ||
query.set(`${first}${rest.map(r => `[${r}]`).join('')}`, val); | ||
} | ||
} | ||
var basePath = base.pathname && base.pathname !== '/' ? base.pathname : ''; | ||
return { | ||
// Protocol/auth/hostname/port always apply | ||
protocol: base.protocol, | ||
auth: base.auth, | ||
host: base.host, | ||
hostname: base.hostname, | ||
port: base.port, | ||
// For the pathname, we join. E.g. http://host/v2 + /my-resource | ||
pathname: basePath + (urlObj.pathname || '') || '/', | ||
query: urlObj.query, | ||
}; | ||
return query; | ||
} | ||
exports.applyBaseUrl = applyBaseUrl; | ||
exports.updateSearch = updateSearch; | ||
/** | ||
* @param {string} pathname | ||
* @param {{ [name: string]: string }} pathParams | ||
*/ | ||
function replacePathParams(pathname, pathParams) { | ||
pathParams = pathParams || {}; | ||
/** | ||
* @param {string} match | ||
* @param {string} fromCurly | ||
* @param {string} fromEscaped | ||
*/ | ||
function onPlaceHolder(match, fromCurly, fromEscaped) { | ||
var key = fromCurly || fromEscaped; | ||
var value = pathParams[fromCurly || fromEscaped]; | ||
const key = fromCurly || fromEscaped; | ||
const value = pathParams[fromCurly || fromEscaped]; | ||
if (value === undefined) { | ||
throw new Error('Missing value for path param ' + key); | ||
throw new Error(`Missing value for path param ${key}`); | ||
} | ||
@@ -74,0 +74,0 @@ return encodeURIComponent(value); |
{ | ||
"name": "gofer", | ||
"version": "3.8.1", | ||
"version": "4.0.0", | ||
"description": "A general purpose service client library", | ||
@@ -21,6 +21,14 @@ "license": "BSD-3-Clause", | ||
"scripts": { | ||
"pretest": "eslint lib test", | ||
"test": "mocha", | ||
"posttest": "nlm verify" | ||
"lint": "npm-run-all lint:*", | ||
"lint:js": "eslint .", | ||
"pretest": "npm-run-all pretest:*", | ||
"test": "npm-run-all test:*", | ||
"posttest": "npm-run-all posttest:*", | ||
"pretest:lint": "npm-run-all lint:*", | ||
"posttest:nlm": "nlm verify", | ||
"test:unit": "mocha" | ||
}, | ||
"engines": { | ||
"node": ">=8" | ||
}, | ||
"nlm": { | ||
@@ -35,24 +43,23 @@ "license": { | ||
"dependencies": { | ||
"bluebird": "^3.3.3", | ||
"debug": "^2.2.0", | ||
"lodash": "^4.6.1", | ||
"qs": "^6.1.0", | ||
"url": "^0.11.0" | ||
"lodash.isobjectlike": "^4.0.0", | ||
"lodash.isplainobject": "^4.0.6", | ||
"lodash.merge": "^4.6.2", | ||
"lodash.mergewith": "^4.6.2" | ||
}, | ||
"devDependencies": { | ||
"assertive": "^2.1.0", | ||
"eslint": "^5.1.0", | ||
"eslint-config-groupon": "^7.0.0", | ||
"eslint-plugin-import": "^2.8.0", | ||
"eslint-plugin-mocha": "^5.1.1", | ||
"eslint-plugin-node": "^7.0.1", | ||
"eslint-plugin-prettier": "^2.6.2", | ||
"eslint": "^6.2.1", | ||
"eslint-config-groupon": "^7.2.0", | ||
"eslint-plugin-import": "^2.18.2", | ||
"eslint-plugin-mocha": "^6.1.0", | ||
"eslint-plugin-node": "^9.1.0", | ||
"eslint-plugin-prettier": "^3.1.0", | ||
"form-data": "^1.0.0-rc4", | ||
"mocha": "^5.2.0", | ||
"mochify": "^4.0.0", | ||
"mocha": "^6.2.0", | ||
"mochify": "^6.4.1", | ||
"nlm": "^3.6.1", | ||
"prettier": "^1.6.1", | ||
"promise": "^7.1.1", | ||
"self-signed": "^1.3.1", | ||
"whatwg-fetch": "^0.11.0" | ||
"npm-run-all": "^4.1.5", | ||
"prettier": "^1.18.2", | ||
"self-signed": "^1.3.1" | ||
}, | ||
@@ -59,0 +66,0 @@ "author": { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
72541
14
1323
+ Addedlodash.isobjectlike@^4.0.0
+ Addedlodash.isplainobject@^4.0.6
+ Addedlodash.merge@^4.6.2
+ Addedlodash.mergewith@^4.6.2
+ Addedlodash.isobjectlike@4.0.0(transitive)
+ Addedlodash.isplainobject@4.0.6(transitive)
+ Addedlodash.merge@4.6.2(transitive)
+ Addedlodash.mergewith@4.6.2(transitive)
- Removedbluebird@^3.3.3
- Removedlodash@^4.6.1
- Removedqs@^6.1.0
- Removedurl@^0.11.0
- Removedbluebird@3.7.2(transitive)
- Removedcall-bind-apply-helpers@1.0.1(transitive)
- Removedcall-bound@1.0.3(transitive)
- Removeddunder-proto@1.0.1(transitive)
- Removedes-define-property@1.0.1(transitive)
- Removedes-errors@1.3.0(transitive)
- Removedes-object-atoms@1.0.0(transitive)
- Removedfunction-bind@1.1.2(transitive)
- Removedget-intrinsic@1.2.6(transitive)
- Removedgopd@1.2.0(transitive)
- Removedhas-symbols@1.1.0(transitive)
- Removedhasown@2.0.2(transitive)
- Removedlodash@4.17.21(transitive)
- Removedmath-intrinsics@1.1.0(transitive)
- Removedobject-inspect@1.13.3(transitive)
- Removedpunycode@1.4.1(transitive)
- Removedqs@6.13.1(transitive)
- Removedside-channel@1.1.0(transitive)
- Removedside-channel-list@1.0.0(transitive)
- Removedside-channel-map@1.0.1(transitive)
- Removedside-channel-weakmap@1.0.2(transitive)
- Removedurl@0.11.4(transitive)