Comparing version
{ | ||
"name": "nodegate", | ||
"description": "API gateway made simple, fast and easy to configure.", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"author": "Julien Martin <martin.julien82@gmail.com>", | ||
@@ -15,3 +15,3 @@ "license": "MIT", | ||
"cors": "^2.8.5", | ||
"deepmerge": "^4.0.0", | ||
"deepmerge": "^4.1.1", | ||
"express": "^4.16.4", | ||
@@ -23,8 +23,7 @@ "lodash": "^4.17.11", | ||
"devDependencies": { | ||
"eslint": "^6.4.0", | ||
"eslint": "^6.5.1", | ||
"eslint-config-airbnb-base": "^14.0.0", | ||
"eslint-plugin-import": "^2.17.2", | ||
"jest": "^24.7.1", | ||
"nock": "^11.3.5", | ||
"nodemon": "^1.19.2", | ||
"nock": "^11.4.0", | ||
"supertest": "^4.0.2" | ||
@@ -31,0 +30,0 @@ }, |
@@ -8,41 +8,75 @@ /** | ||
const { get, merge } = require('lodash'); | ||
const requestNative = require('request-promise-native'); | ||
const { getConfiguration } = require('./configuration'); | ||
const { getConfiguration } = require('../services/configuration'); | ||
const defaultOptions = () => getConfiguration().request; | ||
const project = (container, value) => { | ||
if (typeof value === 'object' && !Array.isArray(value)) { | ||
Object.keys(value).forEach((key) => { | ||
value[key] = project(container, value[key]); | ||
}); | ||
return value; | ||
} | ||
if (typeof value === 'string') { | ||
return get(container, value); | ||
} | ||
throw new TypeError('Invalid projection, must be an object or a string'); | ||
}; | ||
const getHeaders = (container, options) => ({ | ||
...((defaultOptions() && defaultOptions().headers) || {}), | ||
...((container && container.headers) || {}), | ||
...((options && options.headers) || {}), | ||
}); | ||
const validateArguments = (container, method, url, options) => { | ||
if (typeof container !== 'object' || Array.isArray(container)) { | ||
throw new TypeError('Invalid argument: "container", must be an object'); | ||
} | ||
if (typeof method !== 'string') { | ||
throw new TypeError('Invalid argument: "method", must be a string'); | ||
} | ||
if (typeof url !== 'string' && typeof url !== 'function') { | ||
throw new TypeError('Invalid argument: "url", must be an string or a function'); | ||
} | ||
if (typeof options !== 'object' || Array.isArray(options)) { | ||
throw new TypeError('Invalid argument: "options", must be an object'); | ||
} | ||
}; | ||
const doRequest = (container, method, url, options) => { | ||
const parameters = { | ||
...defaultOptions(), | ||
const projectKey = (key, requestOptions, container, options) => { | ||
if (options[key] === null) { | ||
requestOptions[key] = null; | ||
} else if (!options[key]) { | ||
requestOptions[key] = container[key] || null; | ||
} else { | ||
requestOptions[key] = project(container, options[key]); | ||
} | ||
}; | ||
const mergeConfiguration = (requestOptions, configuration) => { | ||
if (!configuration.headers) { | ||
return; | ||
} | ||
if (typeof requestOptions.headers === 'object' && requestOptions.headers !== null) { | ||
merge(requestOptions.headers, configuration.headers); | ||
} else { | ||
requestOptions.headers = configuration.headers; | ||
} | ||
}; | ||
const request = (container, method, url, options = {}) => { | ||
validateArguments(container, method, url, options); | ||
const { request: configuration } = getConfiguration(); | ||
const requestOptions = { | ||
...configuration, | ||
...options, | ||
headers: getHeaders(container, options), | ||
method, | ||
url: (typeof url === 'function' && url(container)) || url, | ||
body: (container && container.body) || {}, | ||
qs: (container && container.query) || {}, | ||
uri: typeof url === 'function' ? url(container) : url, | ||
}; | ||
return requestNative(parameters); | ||
}; | ||
projectKey('headers', requestOptions, container, options); | ||
projectKey('body', requestOptions, container, options); | ||
const request = (container) => ({ | ||
get: (url, options) => doRequest(container, 'get', url, options), | ||
post: (url, options) => doRequest(container, 'post', url, options), | ||
patch: (url, options) => doRequest(container, 'patch', url, options), | ||
put: (url, options) => doRequest(container, 'put', url, options), | ||
delete: (url, options) => doRequest(container, 'delete', url, options), | ||
}); | ||
mergeConfiguration(requestOptions, configuration); | ||
request.get = (url, options) => doRequest(null, 'get', url, options); | ||
request.post = (url, options) => doRequest(null, 'post', url, options); | ||
request.patch = (url, options) => doRequest(null, 'patch', url, options); | ||
request.put = (url, options) => doRequest(null, 'put', url, options); | ||
request.delete = (url, options) => doRequest(null, 'delete', url, options); | ||
return requestNative(requestOptions); | ||
}; | ||
module.exports = request; |
@@ -60,5 +60,5 @@ /** | ||
if (parsedUrl.variables.length === 0) { | ||
return url; | ||
return () => url; | ||
} | ||
return (container) => mergeUrl(container, parsedUrl); | ||
}; |
@@ -13,30 +13,36 @@ /** | ||
module.exports = (method, url, path) => { | ||
const setBodyToContainer = (body, container, options) => { | ||
if (!options.path && typeof body !== 'object') { | ||
return; | ||
} | ||
if (options.path && !container.body[options.path]) { | ||
container.body[options.path] = body; | ||
return; | ||
} | ||
if (options.path) { | ||
merge(container.body[options.path], body); | ||
return; | ||
} | ||
merge(container.body, body); | ||
}; | ||
module.exports = (method, url, options = {}) => { | ||
const buildedUrl = urlBuilder(url); | ||
return async (container) => { | ||
try { | ||
const { body, statusCode } = await request(container)[method](buildedUrl); | ||
const { body, statusCode } = await request( | ||
container, | ||
method, | ||
buildedUrl, | ||
options, | ||
); | ||
container.statusCode = statusCode; | ||
if (!path && typeof body !== 'object') { | ||
return; | ||
} | ||
if (path && !container.body[path]) { | ||
container.body[path] = body; | ||
return; | ||
} | ||
if (path) { | ||
merge(container.body[path], body); | ||
return; | ||
} | ||
merge(container.body, body); | ||
setBodyToContainer(body, container, options); | ||
} catch (err) { | ||
const error = new WorkflowError(err, err.response); | ||
error.setContainer({ | ||
...container, | ||
...( | ||
err.response && err.response.body | ||
? { errorBody: err.response.body } | ||
: null | ||
), | ||
}); | ||
error.setContainer(container); | ||
if (err.response && err.response.body) { | ||
container.errorBody = err.response.body; | ||
} | ||
container.statusCode = err.response ? err.response.statusCode : 500; | ||
throw error; | ||
@@ -43,0 +49,0 @@ } |
@@ -8,8 +8,26 @@ /** | ||
const { get, set } = require('lodash'); | ||
const { get, merge } = require('lodash'); | ||
const project = (container, value) => { | ||
if (typeof value === 'object') { | ||
Object.keys(value).forEach((key) => { | ||
value[key] = project(container, value[key]); | ||
}); | ||
return value; | ||
} | ||
if (typeof value === 'string') { | ||
return get(container, value); | ||
} | ||
throw new TypeError('Bad projection configuration'); | ||
}; | ||
module.exports = (projections) => (container) => { | ||
projections.forEach((projection) => { | ||
set(container.body, projection[1], get(container.body, projection[0])); | ||
if (typeof projections !== 'object') { | ||
throw new TypeError('Bad projection configuration'); | ||
} | ||
const projectedValues = project(container, projections); | ||
Object.keys(container.body).forEach((key) => { | ||
delete container.body[key]; | ||
}); | ||
merge(container.body, projectedValues); | ||
}; |
@@ -14,10 +14,10 @@ /** | ||
const tryRequest = async (container, method, url, test, settings, tentatives = 0) => { | ||
const { statusCode, headers, body } = await request(container)[method](url); | ||
const tryRequest = async (container, method, url, test, configuration, tentatives = 0) => { | ||
const { statusCode, headers, body } = await request(container, method, url); | ||
if (!test({ statusCode, headers, body }, container)) { | ||
if (tentatives < settings.tentatives) { | ||
await timeout(settings.delay); | ||
return tryRequest(container, method, url, test, settings, tentatives + 1); | ||
if (tentatives < configuration.tentatives) { | ||
await timeout(configuration.delay); | ||
return tryRequest(container, method, url, test, configuration, tentatives + 1); | ||
} | ||
throw new Error(`Wait for ${url} failed`); | ||
throw new Error(`Wait for ${url(container)} failed`); | ||
} | ||
@@ -29,4 +29,4 @@ return Promise.resolve(container); | ||
const buildedUrl = urlBuilder(url); | ||
const settings = getConfiguration().workers.waitFor; | ||
return async (container) => tryRequest(container, method, buildedUrl, test, settings); | ||
const configuration = getConfiguration().workers.waitFor; | ||
return async (container) => tryRequest(container, method, buildedUrl, test, configuration); | ||
}; |
20375
8.08%6
-14.29%514
11.26%Updated