diff --git a/.eslintrc b/.eslintrc index 1bdeb5d..b2aaf72 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,8 +1,19 @@ { + "extends": [ + "eslint:recommended" + ], + "env": { + "node": true + }, + "plugins": [ + "mocha" + ], "parserOptions": { "ecmaVersion": 6 }, "rules": { + "eol-last": [2], + "no-trailing-spaces" : [2], "keyword-spacing": [ 2, {} @@ -16,6 +27,23 @@ "max-len": [ 2, 100 + ], + "comma-spacing": [ + 2, + { "before": false, "after": true } + ], + "space-infix-ops": [ + 2, + {"int32Hint": true} + ] + }, + "overrides": [ + { + "files": [ "test/**/*.js" ], + "env": { "mocha": true }, + "rules": { + "max-nested-callbacks": [ "error", 8 ] + } + } ] - } } diff --git a/.gitignore b/.gitignore index cdaa1c3..5cdc7d8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ node_modules datastore .env pidfile +nocommit +.nyc_output +coverage diff --git a/includes/buildURI.js b/includes/buildURI.js new file mode 100644 index 0000000..584c1c0 --- /dev/null +++ b/includes/buildURI.js @@ -0,0 +1,31 @@ +/** + * Build URI from routeItem.endpointUrl and targetRequest.path + * + * @param String endpointUrl + * @param String endpointUrl + * + * @return String URI + */ +'use strict'; + +module.exports = function(endpointUrl, path){ + let URI = '' + if (endpointUrl && endpointUrl.length) { + URI += endpointUrl + if (endpointUrl[endpointUrl.length - 1] !== '/') { + URI += '/' + } + } else { + URI += '/' + } + if (path && path.length) { + for ( let i = 0; i < path.length; i++ ) { + if (path[i] !== '/') { + URI += path.substring(i) + break + } + } + } + + return URI +} diff --git a/includes/checkConditions.js b/includes/checkConditions.js new file mode 100644 index 0000000..a9699f5 --- /dev/null +++ b/includes/checkConditions.js @@ -0,0 +1,76 @@ +/** + * Check Conditions for request. + * + * @param Object conditions object with headers, payload and methods properties + * @param Object requestDetails http request details + * @param Object requestDetails http request json body. + * + * @return Boolean true if conditions are met + */ +'use strict'; +const debug = require('debug')('proxy:check-conditions'); +const getProperty = require('./getProperty.js') + +module.exports = function(conditions, requestDetails, jsonData) { + debug('checkConditions %O requestDetails: %O json: %O', conditions, requestDetails, jsonData) + if (conditions.headers && conditions.headers.length) { + for (let header of conditions.headers ) { + if (!requestDetails.headers[header.name]) { + return false + } + let receivedHeaderValue = requestDetails.headers[header.name] + if (header.isRegex) { + let pattern = new RegExp(header.value, "i") + if (!pattern.test(receivedHeaderValue)) { + return false + } + } else { + if (receivedHeaderValue !== header.value) { + return false + } + } + } + } + // check methods + if (conditions.methods && conditions.methods.length) { + if (conditions.methods.toUpperCase) { + if (requestDetails.method != conditions.methods.toUpperCase()) { + return false + } + } else { + for (let i in conditions.methods) { + conditions.methods[i] = conditions.methods[i].toUpperCase() + } + if (conditions.methods.indexOf(requestDetails.method) === -1) { + return false; + } + } + + } + // check payload + if (conditions.payload && conditions.payload.length && jsonData) { + if (typeof jsonData != "object") { + return false + } + for (let payload of conditions.payload ) { + debug('Checking for payload conditions %O', payload) + let receivedPayloadValue = getProperty(payload.name, jsonData) + debug('receivedPayloadValue %O', receivedPayloadValue) + if (receivedPayloadValue instanceof Error) { + return false + } + if (payload.isRegex) { + let pattern = new RegExp(payload.value, "i") + debug('pattern.test %O', pattern.test(receivedPayloadValue)) + if (!pattern.test(receivedPayloadValue)) { + return false + } + } else { + if (receivedPayloadValue !== payload.value) { + return false + } + } + } + } + return true +} diff --git a/includes/checkConditionsMingo.js b/includes/checkConditionsMingo.js new file mode 100644 index 0000000..b8543bd --- /dev/null +++ b/includes/checkConditionsMingo.js @@ -0,0 +1,49 @@ +/** + * Check Conditions for request. + * + * @param Object conditions object with headers, payload and methods properties + * @param Object requestDetails http request details + * @param Object requestDetails http request json body. + * + * @return Boolean true if conditions are met + */ +'use strict'; +const debug = require('debug')('proxy:check-conditions'); +const mingo = require('mingo') + +module.exports = function(conditions, requestDetails, jsonData) { + debug('checkConditions %O requestDetails: %O json: %O', conditions, requestDetails, jsonData) + if (conditions.headers ) { + let query = new mingo.Query(conditions.headers); + if (!query.test(requestDetails.headers)){ + return false + } + //return mingo.find([requestDetails.headers], conditions.headers).count() + + } + // check methods + if (conditions.methods && conditions.methods.length) { + if (conditions.methods instanceof Array) { + for (let i in conditions.methods) { + conditions.methods[i] = conditions.methods[i].toUpperCase() + } + } else if (conditions.methods.toUpperCase){ + conditions.methods = conditions.methods.toUpperCase() + } + if (conditions.methods.indexOf(requestDetails.method) === -1) { + return false; + } + } + // check payload + if (conditions.payload) { + if (typeof jsonData != "object") { + return false + } + //return mingo.find([jsonData], conditions.payload).count() + let query = new mingo.Query(conditions.payload); + if (!query.test(jsonData)) { + return false + } + } + return true +} diff --git a/includes/checkConditionsMongo.js b/includes/checkConditionsMongo.js new file mode 100644 index 0000000..5d11070 --- /dev/null +++ b/includes/checkConditionsMongo.js @@ -0,0 +1,47 @@ +/** + * Check Conditions for request. + * + * @param Object conditions object with headers, payload and methods properties + * @param Object requestDetails http request details + * @param Object requestDetails http request json body. + * + * @return Boolean true if conditions are met + */ +'use strict'; +const debug = require('debug')('proxy:check-conditions'); +const sift = require('sift').default + +module.exports = function(conditions, requestDetails, jsonData) { + debug('checkConditions %O requestDetails: %O json: %O', conditions, requestDetails, jsonData) + if (conditions.headers ) { + let result = sift(conditions.headers, [requestDetails.headers]) + if (!result.length){ + return false + } + + } + // check methods + if (conditions.methods && conditions.methods.length) { + if (conditions.methods instanceof Array) { + for (let i in conditions.methods) { + conditions.methods[i] = conditions.methods[i].toUpperCase() + } + } else if (conditions.methods.toUpperCase){ + conditions.methods = conditions.methods.toUpperCase() + } + if (conditions.methods.indexOf(requestDetails.method) === -1) { + return false; + } + } + // check payload + if (conditions.payload) { + if (typeof jsonData != "object") { + return false + } + let result = sift(conditions.payload, [jsonData]) + if (!result.length){ + return false + } + } + return true +} diff --git a/includes/checkPath.js b/includes/checkPath.js new file mode 100644 index 0000000..8d01636 --- /dev/null +++ b/includes/checkPath.js @@ -0,0 +1,44 @@ +/** + * Check if path match route path. + * + * @param Object targetRequest request details + * @param Object routeItem ob route table + * + * @return Boolean true if conditions are met + */ +'use strict'; + +module.exports = function(targetRequest, routeItem) { + let targetRoutePathItems = targetRequest.route.split('/'); + + // routeItem is array of supporthed path (endpoints like /foo /bar/:test/foo) + for (let path of routeItem.path) { + // If route qual saved path + if (path == targetRequest.route) { + return true; + } + + // If targetRoutePathItems.length == 1, and did not match + if (targetRoutePathItems.length == 1) { + continue; + } + + let pathItems = path.split('/'); + if (pathItems.length != targetRoutePathItems.length) { + continue; + } + + routeItem.matchVariables = {} + for (var i = 0; i < targetRoutePathItems.length; i++) { + if (pathItems[i].charAt(0) == ':') { + routeItem.matchVariables[pathItems[i].substring(1)] = targetRoutePathItems[i]; + } else { + if (targetRoutePathItems[i] != pathItems[i]) { + //if not matching items + return false + } + } + } + return true; + } +} diff --git a/includes/decodeData.js b/includes/decodeData.js new file mode 100644 index 0000000..7619dd2 --- /dev/null +++ b/includes/decodeData.js @@ -0,0 +1,25 @@ +/** + * Decode buffer to specidied by content-type format. + * + * @param String phase(optional) hook phase: before, after, metric + * @param Buffer type(optional) hook type: adapter, notify, broadcast + * + * @return return buffer or object + */ +'use strict'; + +module.exports = function(contentType, buffer){ + let data = false + switch (contentType) { + case undefined: // version 1.x compatibility. If no content-type provided, assume json. + case 'application/json': { + data = JSON.parse(buffer); + break; + } + // Todo support more decoders here? + default: { + data = buffer + } + } + return data +} diff --git a/includes/findAllTargets.js b/includes/findAllTargets.js new file mode 100644 index 0000000..c50c1f6 --- /dev/null +++ b/includes/findAllTargets.js @@ -0,0 +1,50 @@ +/** + * Find all targets function + * + * @param Object targetRequest request details + * @param String routeType(optional) of router: hook, handler, metric + * @param Object object all available routes + * + * @return array of objects with available routes + */ +'use strict'; + +const debug = require('debug')('proxy:find-all-targets'); +const matchRoute = require('./matchRoute.js') + +module.exports = function(targetRequest, routeType, allRegisteredRoutes) { + debug('Find all routes %s', targetRequest.route); + + var availableRoutes = []; + for (let i in allRegisteredRoutes) { + if (routeType) { + if (!allRegisteredRoutes[i].type) { + // Version 1.x compatibility + allRegisteredRoutes[i].type = "handler" + } + if (allRegisteredRoutes[i].type.toLowerCase() !== routeType) { + continue + } + } + // For easy deployment when service need to stop receiving new requests. + // Version 1.x compatibility + if (allRegisteredRoutes[i].online !== undefined) { + if (allRegisteredRoutes[i].online === false) { + continue + } + } + // Making copy of the router. + let routeItem = JSON.parse(JSON.stringify(allRegisteredRoutes[i])); + routeItem.matchVariables = {}; + if (matchRoute(targetRequest, routeItem)) { + availableRoutes.push(routeItem); + } + } + debug('Available routes type: %s route: %s availableRoutes: %s', routeType, + targetRequest.route, JSON.stringify(availableRoutes, null, 2)); + if (availableRoutes.length == 0) { + debug('Not found for %s', targetRequest.route); + return new Error('Endpoint not found'); + } + return availableRoutes; +} diff --git a/includes/findHookTarget.js b/includes/findHookTarget.js new file mode 100644 index 0000000..111f8ed --- /dev/null +++ b/includes/findHookTarget.js @@ -0,0 +1,58 @@ +/** + * Find all targets for hook + * + * @param Object targetRequest request details + * @param String phase(optional) hook phase: before, after, metric + * @param String type(optional) hook type: adapter, notify, broadcast + * @param String group(optional) hook group defined by routeItem + * @param Object object all available routes + * + * @return array of objects with available routes + */ +'use strict'; + +const debug = require('debug')('proxy:find-hook-target'); +const findAllTargets = require('./findAllTargets.js') + +module.exports = function(targetRequest, phase, type, group, allRegisteredRoutes){ + debug('Find all hooks route: %s phase: %s type: %s group: %s', + targetRequest.route, phase, type, group); + let allHookTargets = findAllTargets(targetRequest, 'hook', allRegisteredRoutes) + if (allHookTargets instanceof Error) { + return allHookTargets + } + let finalHookTable = [] + for (let target of allHookTargets){ + // skip hooks with no hook properties + if (!target.hook) { + continue + } + + if (phase){ + if (target.hook.phase !== phase) { + continue + } + } + if (target.hook.type !== type) { + continue + } + let targetCopy = JSON.parse(JSON.stringify(target)) + if (target.hook.group) { + targetCopy.group = target.hook.group + } else { + targetCopy.group = '_default' + } + + finalHookTable.push(targetCopy) + } + if (group) { + finalHookTable = finalHookTable.filter(function(elem){ + return elem.group == group + }) + } + if (!finalHookTable.length) { + debug('Hook instance %s not found', group); + return new Error('Hook instance not found'); + } + return finalHookTable +} diff --git a/includes/getHeaders.js b/includes/getHeaders.js new file mode 100644 index 0000000..380edb3 --- /dev/null +++ b/includes/getHeaders.js @@ -0,0 +1,56 @@ +/** + * get Headers for Hook + * + * @param Object targetRequest request details + * @param Object routeItem ob route table + * @param String phase(optional) hook phase: before, after, metric + * @param String hookType(optional) hook type: adapter, notify, broadcast + * @param String hookGroup(optional) user defined routerItem group + * @param Boolean generate Signature(optional) + * @param Object object headers + * + * @return array of objects with available routes + */ +'use strict'; + +const debug = require('debug')('proxy:get-headers'); +const signature = require('./signature.js'); +const skipHeaders = [ + 'host', // issue to properly connect + 'connection', // if it is closed, behavior is unexpected + 'transfer-encoding', //we need to ignore that one. + 'content-length', //issue with recounting length of the package +] + +module.exports = function(targetRequest, routeItem, phase, hookType, hookGroup, isSignature) { + let headers = {}; + for (let i in targetRequest.requestDetails.headers) { + if (skipHeaders.indexOf(i) != -1) { + continue + } + headers[i] = targetRequest.requestDetails.headers[i]; + } + for (let i in routeItem.matchVariables) { + headers['mfw-' + i] = routeItem.matchVariables[i]; + } + if (phase) { + headers['x-origin-url'] = targetRequest.route + headers['x-origin-method'] = targetRequest.method + headers['x-endpoint-scope'] = targetRequest.endpoint.scope + headers['x-hook-phase'] = phase + } + if (hookType) { + headers['x-hook-type'] = hookType + } + if (hookGroup) { + headers['x-hook-group'] = hookGroup + } + + if (isSignature) { + headers['x-hook-signature'] = 'sha256=' + + signature('sha256', targetRequest.requestDetails._buffer, routeItem.secureKey) + } + debug('TargetRequest %O routeItem %O phase %s hookType headers %O', targetRequest, + routeItem, phase, hookType, headers); + return headers +} diff --git a/includes/getProperty.js b/includes/getProperty.js new file mode 100644 index 0000000..788d1a6 --- /dev/null +++ b/includes/getProperty.js @@ -0,0 +1,29 @@ +/** + * Source: https://gist.github.com/jasonrhodes/2321581 + * A function to take a string written in dot notation style, and use it to + * find a nested object property inside of an object. + * + * Useful in a plugin or module that accepts a JSON array of objects, but + * you want to let the user specify where to find various bits of data + * inside of each custom object instead of forcing a standardized + * property list. + * + * @param String nested A dot notation style parameter reference (ie "urls.small") + * @param Object object (optional) The object to search + * + * @return the value of the property in question + */ +'use strict'; + +module.exports = function ( propertyName, object ) { + let parts = propertyName.split( "." ); + let length = parts.length; + let property = object; + for ( let i = 0; i < length; i++ ) { + if (property[parts[i]] === undefined) { + return new Error('Property Does not exists') + } + property = property[parts[i]]; + } + return property; +} diff --git a/includes/matchRoute.js b/includes/matchRoute.js new file mode 100644 index 0000000..6119185 --- /dev/null +++ b/includes/matchRoute.js @@ -0,0 +1,29 @@ +/** + * Check if route match request. + * + * @param Object targetRequest request details + * @param Object routeItem ob route table + * + * @return Boolean true if conditions are met + */ +'use strict'; + +const checkPath = require('./checkPath.js') +const checkConditions = require('./checkConditions.js') + +module.exports = function(targetRequest, routeItem) { + if (routeItem.path && routeItem.path.length == 1 && routeItem.path[0] == '*') { + if (!routeItem.conditions) { + return true + } + return checkConditions(routeItem.conditions, + targetRequest.requestDetails, targetRequest.jsonData) + } + if (!checkPath(targetRequest, routeItem)) { + return false + } + if (!routeItem.conditions) { + return true + } + return checkConditions(routeItem.conditions, targetRequest.requestDetails, targetRequest.jsonData) +} diff --git a/includes/request.js b/includes/request.js new file mode 100644 index 0000000..035cf88 --- /dev/null +++ b/includes/request.js @@ -0,0 +1,156 @@ +/** + * Process proxy request + * + * @param Object targetRequest request details + * @param Object object all available routes + * @param function callback when request processed + * + * @return + */ + +'use strict'; + +const findAllTargets = require('./findAllTargets.js') +const sendBroadcastMessage = require('./sendBroadcastMessage.js') +const decodeData = require('./decodeData.js') +const sendRequest = require('./sendRequest.js') +const sendHookBroadcast = require('./sendHookBroadcast.js') +const sendHookNotify = require('./sendHookNotify.js') +const sendHookAdapter = require('./sendHookAdapter.js') +const getHeaders = require('./getHeaders.js') +const BuildURI = require('./buildURI.js') +const debug = require('debug')('proxy:request-wrapper'); + +/** + * Process before Hooks. + */ +function hookCall(targetRequest, globalServices, phase, callback) { + sendHookBroadcast(targetRequest, phase, globalServices) + sendHookNotify(targetRequest, phase, globalServices) + sendHookAdapter(targetRequest, phase, globalServices, callback) +} + +function processEndpoint(endpointTargets, targetRequest, globalServices, callback) { + if (!endpointTargets.length) { + // TODO: policy for repeat. + return callback(new Error('Endpoint not found')) + } + // TODO apply tags based vouting here + let routerItem = endpointTargets.pop() + let requestOptions = { + uri: BuildURI(routerItem.endpointUrl, targetRequest.path), + method: targetRequest.method, + headers: getHeaders(targetRequest, routerItem, false, false, false, false), + body: targetRequest.requestDetails._buffer + } + sendRequest(requestOptions, targetRequest, globalServices, function(err, response, body){ + if (err) { + return processEndpoint(endpointTargets, targetRequest, globalServices, callback) + } + // targetRequest.router used in sendBroadcastMessage + targetRequest.router = routerItem + callback(err, response, body) + }) + +} + +module.exports = function(targetRequest, globalServices, callback){ + debug('Route base: %s', targetRequest.route); + + let endpointTargets = findAllTargets(targetRequest, 'handler', globalServices); + if (endpointTargets instanceof Error) { + debug('Route %s err %O', targetRequest.route, endpointTargets); + return callback(endpointTargets, null); + } + + targetRequest.endpoint = { + scope: endpointTargets[0].scope, + secureKey: endpointTargets[0].secureKey + } + + hookCall(targetRequest, globalServices, 'before', function(){ + processEndpoint(endpointTargets, targetRequest, globalServices, function(err, response, body) { + if (err) { + // TODO possible call hoot after + return callback(err, null) + } + let bodyJSON = "" + try { + bodyJSON = decodeData(response.headers['content-type'], body) + } catch (e) { + debug('decodeData Error received: %O', e); + return callback(e); + } + debug('Requesnt endpoint on %s body: %O', targetRequest.route, body); + // Process after hooks + let answerDetails = { + headers: response.headers, + _buffer: body, + method: targetRequest.method + } + let targetAnswer = { + route: targetRequest.route, + path: targetRequest.path, + method: targetRequest.method, + jsonData: bodyJSON, + requestDetails: answerDetails, + endpoint: targetRequest.endpoint + } + hookCall(targetAnswer, globalServices, 'after', function(){ + let body = decodeData(answerDetails.headers['content-type'], answerDetails._buffer) + if (typeof body == "object") { + // prefix with base_URL all urls + if (targetRequest.method != 'OPTIONS') { + if (body.url) { + // Make sure that url is not absolute + if (body.url.indexOf('http://') == -1 + && body.url.indexOf('https://') == -1) { + body.url = BuildURI(process.env.BASE_URL, body.url) + } + } else if (body.id) { + body.url = BuildURI(process.env.BASE_URL, BuildURI(targetRequest.route, body.id)) + } + } + } + if (body instanceof Array) { + for (let i in body) { + if (body[i].url) { + // Make sure that url is not absolute + if (body[i].url.indexOf('http://') == -1 + && body[i].url.indexOf('https://') == -1 ) { + body[i].url = BuildURI(process.env.BASE_URL, body[i].url) + } + } else if (body[i].id) { + body[i].url = BuildURI(process.env.BASE_URL, + BuildURI(targetRequest.route, body[i].id)) + } + } + } + let responseHeaders = { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, DELETE, PUT, SEARCH', + 'Access-Control-Allow-Headers': 'content-type, signature, access_token,' + + ' token, Access-Token', + 'Access-Control-Expose-Headers': 'x-total-count', + }; + for (let i in answerDetails.headers) { + if (i.substring(0, 1) == 'x') { + responseHeaders[i] = answerDetails.headers[i]; + } + } + if (answerDetails.headers['content-type']) { + responseHeaders['content-type'] = answerDetails.headers['content-type']; + } + // deprecated. websoket need to be rewriten as a hook broadcast + if (response.statusCode == 200) { + sendBroadcastMessage(targetRequest, body, globalServices); + } + callback(null, { + code: response.statusCode, + answer: body, + headers: responseHeaders + }); + }) + }) + }) +} diff --git a/includes/sendBroadcastMessage.js b/includes/sendBroadcastMessage.js new file mode 100644 index 0000000..54dfb72 --- /dev/null +++ b/includes/sendBroadcastMessage.js @@ -0,0 +1,73 @@ +/** + * Find all targets function + * + * @param Object targetRequest request details + * @param Object or Buffer to send + * @param Object object all available routes + * + * @return nothing + */ +'use strict'; + +const debug = require('debug')('proxy:send-udp-broadcast'); +const signature = require('./signature.js'); +const dgram = require('dgram'); +const url = require('url'); + +/** + * Send broadcast message to UDP client. + * deprecated. WS need to be replaced by boracast hook + */ +function sendBroadcastMessageToClient(bufferedMessage, URL) { + let client = dgram.createSocket('udp4'); + client.send(bufferedMessage, URL.port, URL.hostname, function(err) { + // Only happen if client close while sending some data. + if (err) { + debug('UDP broadcast Error: %O', err); + client.close(); + } + }); + client.on('message', function(message) { + debug('Received from server: ' + message); + client.close(); + }); +} + +/** + * Proxy request to backend server. + * deprecated. WS need to be replaced by boracast hook + */ +module.exports = function (targetRequest, message, allRegisteredRoutes) { + debug('UDP broadcast %O %s %s %O', targetRequest, message); + + for (let routeItem of allRegisteredRoutes) { + if (routeItem.type !== 'websocket') { + continue + } + var broadcastMessage = { + method: targetRequest.method, + route: targetRequest.router.path, + scope: targetRequest.router.scope, + loaders: targetRequest.router.matchVariables, + path: targetRequest.requestDetails.url + }; + switch (routeItem.methods[targetRequest.method.toLowerCase()]) { + case 'data': { + broadcastMessage.message = message; + break; + } + default : { + broadcastMessage.meta = true; + break; + } + } + var URL = url.parse(routeItem.endpointUrl); + + broadcastMessage.signature = ['sha256', + signature('sha256', JSON.stringify(broadcastMessage), routeItem.secureKey)]; + + debug('UDP broadcast to %O %O', routeItem, URL); + var bufferedMessage = Buffer.from(JSON.stringify(broadcastMessage)); + sendBroadcastMessageToClient(bufferedMessage, URL); + } +} diff --git a/includes/sendHookAdapter.js b/includes/sendHookAdapter.js new file mode 100644 index 0000000..a8bda2b --- /dev/null +++ b/includes/sendHookAdapter.js @@ -0,0 +1,121 @@ +/** + * Send Hook Notify + * + * @param Object targetRequest request details + * @param String phase(optional) hook phase: before, after, metric + * @param Object object all available routes + * @param function callback when adapter processed + * + * @return + */ +'use strict'; + +const debug = require('debug')('proxy:send-hook-adapter'); + +const getHookHeaders = require('./getHeaders.js') +const sendRequest = require('./sendRequest.js') +const findHookTarget = require('./findHookTarget.js') +const BuildURI = require('./buildURI.js') +const signature = require('./signature.js'); +const decodeData = require('./decodeData.js') + +function processAdapter(adapterTargets, targetRequest, + phase, globalServices, callbackAdapterGroup) { + // TODO apply tags based vouting here + let routerItem = adapterTargets.pop() + let requestOptions = { + uri: BuildURI(routerItem.endpointUrl, targetRequest.path), + method: 'NOTIFY', + headers: getHookHeaders(targetRequest, routerItem, phase, 'adapter', routerItem.group, true), + body: targetRequest.requestDetails._buffer + } + sendRequest(requestOptions, targetRequest, globalServices, function(err, response, body){ + let headerStatusName = 'x-hook-adapter-status-' + routerItem.group + '-' + phase + try { + decodeData(response.headers['content-type'], body) + } catch (e) { + debug('decodeData Error received: %O', e); + err = e + } + if (err) { + debug('Adapter failed %O', err); + // It's communication error and we need to try next adapter + if (!adapterTargets.length) { + // no more adapters in current group. Set Status. + targetRequest.requestDetails.headers[headerStatusName] = 'error: ' + err.message + return callbackAdapterGroup() + } + return processAdapter(adapterTargets, targetRequest, + phase, globalServices, callbackAdapterGroup) + } + if (response.statusCode != 200) { + debug('Adapter failed with code: %s body: %s', response.statusCode, body) + for (let i in response.headers) { + if (i.substring(0, 6) == 'x-set-') { + let headerName = i.substr(6) + targetRequest.requestDetails.headers[headerName] = response.headers[i]; + } + } + // TODO Maybe if addapter return !200 code, we need to return it to client + return callbackAdapterGroup() + } + debug('adapter processed'); + targetRequest.requestDetails._buffer = body + debug('Adapter Headers received: %O code: %s', response.headers, response.statusCode); + for (let i in response.headers) { + if (i.substring(0, 6) == 'x-set-') { + let headerName = i.substr(6) + targetRequest.requestDetails.headers[headerName] = response.headers[i]; + } + } + delete targetRequest.requestDetails.headers['content-length'] + if (phase == 'before') { + // resign it + if (targetRequest.requestDetails.headers.signature) { + targetRequest.requestDetails.headers.signature = 'sha256=' + + signature('sha256', + targetRequest.requestDetails._buffer, + targetRequest.endpoint.secureKey); + } + } + callbackAdapterGroup() + }) +} + +function processAdapterGroup(adapterGroups, adapterTargets, targetRequest, + phase, globalServices, callback){ + if (!adapterGroups.length){ + debug('No more adapter groups left'); + return callback(false) + } + + let currentAdapterGroup = adapterGroups.shift() + let currentAdapterTargets = adapterTargets.filter(function(a) { + return a.group == currentAdapterGroup + }) + processAdapter(currentAdapterTargets, targetRequest, + phase, globalServices, function(){ + processAdapterGroup(adapterGroups, adapterTargets, targetRequest, + phase, globalServices, callback) + }) + +} + +module.exports = function(targetRequest, phase, globalServices, callback){ + let adapterTargets = findHookTarget(targetRequest, phase, 'adapter', false, globalServices) + debug('Adapter: Phase %s for %s result: %O', phase, targetRequest.route, adapterTargets); + if (adapterTargets instanceof Error) { + // No adapters found. return true, no error but nothing to process. + debug('No adapter groups found'); + return callback(adapterTargets) + } + let adapterGroups = [] + for (let target of adapterTargets) { + if (adapterGroups.indexOf(target.group) == -1) { + adapterGroups.push(target.group) + } + } + adapterGroups.sort() + debug('adapter Groups %O', adapterGroups); + processAdapterGroup(adapterGroups, adapterTargets, targetRequest, phase, globalServices, callback) +} diff --git a/includes/sendHookBroadcast.js b/includes/sendHookBroadcast.js new file mode 100644 index 0000000..9c13110 --- /dev/null +++ b/includes/sendHookBroadcast.js @@ -0,0 +1,52 @@ +/** + * Send Hook Broadcast + * + * @param Object targetRequest request details + * @param String phase(optional) hook phase: before, after, metric + * @param Object object all available routes + * + * @return + */ +'use strict'; + +const debug = require('debug')('proxy:send-hook-broadcast'); + +const getHookHeaders = require('./getHeaders.js') +const sendRequest = require('./sendRequest.js') +const findHookTarget = require('./findHookTarget.js') +const BuildURI = require('./buildURI.js') + +function processBroadcast(broadcastTargets, targetRequest, phase, globalServices) { + if (!broadcastTargets.length) { + return false + } + let routerItem = broadcastTargets.pop() + let requestOptions = { + uri: BuildURI(routerItem.endpointUrl, targetRequest.path), + method: 'NOTIFY', + headers: getHookHeaders(targetRequest, routerItem, phase, 'broadcast', false, true), + body: targetRequest.requestDetails._buffer + } + if (routerItem.hook.phase == "metric" && routerItem.hook.meta) { + let jsonCopy = JSON.parse(JSON.stringify(targetRequest.jsonData)) + delete jsonCopy.request + delete jsonCopy.response + requestOptions.body = JSON.stringify(jsonCopy) + } + sendRequest(requestOptions, targetRequest, globalServices, function(err){ + if (err) { + debug('broadcast failed %O', err); + } else { + debug('broadcast sent'); + } + processBroadcast(broadcastTargets, targetRequest, phase, globalServices) + }) +} + +module.exports = function(targetRequest, phase, globalServices){ + let broadcastTargets = findHookTarget(targetRequest, phase, 'broadcast', false, globalServices) + debug('Broadcast: Phase %s for %s result: %O', phase, targetRequest.route, broadcastTargets); + if (broadcastTargets instanceof Array) { + processBroadcast(broadcastTargets, targetRequest, phase, globalServices) + } +} diff --git a/includes/sendHookNotify.js b/includes/sendHookNotify.js new file mode 100644 index 0000000..5d869d4 --- /dev/null +++ b/includes/sendHookNotify.js @@ -0,0 +1,75 @@ +/** + * Send Hook Notify + * + * @param Object targetRequest request details + * @param String phase(optional) hook phase: before, after, metric + * @param Object object all available routes + * + * @return + */ +'use strict'; + +const debug = require('debug')('proxy:send-hook-notify'); + +const getHookHeaders = require('./getHeaders.js') +const sendRequest = require('./sendRequest.js') +const findHookTarget = require('./findHookTarget.js') +const BuildURI = require('./buildURI.js') + +function processNotify(notifyTargets, targetRequest, phase, globalServices, callback) { + if (!notifyTargets.length) { + return callback() + } + // TODO apply tags based vouting here + let routerItem = notifyTargets.pop() + let requestOptions = { + uri: BuildURI(routerItem.endpointUrl, targetRequest.path), + method: 'NOTIFY', + headers: getHookHeaders(targetRequest, routerItem, phase, 'notify', routerItem.group, true), + body: targetRequest.requestDetails._buffer + } + if (routerItem.hook.phase == "metric" && routerItem.hook.meta) { + let jsonCopy = JSON.parse(JSON.stringify(targetRequest.jsonData)) + delete jsonCopy.request + delete jsonCopy.response + requestOptions.body = JSON.stringify(jsonCopy) + } + sendRequest(requestOptions, targetRequest, globalServices, function(err){ + if (err) { + debug('notify failed %O', err); + return processNotify(notifyTargets, targetRequest, phase, globalServices, callback) + } + debug('notify sent'); + callback() + }) +} + +function processNotifyGroup(notifyGroups, notifyTargets, targetRequest, phase, globalServices){ + if (notifyGroups.length){ + let currentNotifyGroup = notifyGroups.shift() + let currentNotifyTargets = notifyTargets.filter(function(a) { + return a.group == currentNotifyGroup + }) + debug('notify Groups %s %O', currentNotifyGroup, notifyGroups); + + processNotify(currentNotifyTargets, targetRequest, phase, globalServices, function(){ + processNotifyGroup(notifyGroups, notifyTargets, targetRequest, phase, globalServices) + }) + } +} + +module.exports = function(targetRequest, phase, globalServices){ + let notifyTargets = findHookTarget(targetRequest, phase, 'notify', false, globalServices) + debug('Notify: Phase %s for %s result: %O', phase, targetRequest.route, notifyTargets); + if (notifyTargets instanceof Array) { + let notifyGroups = [] + for (let target of notifyTargets) { + if (notifyGroups.indexOf(target.group) == -1) { + notifyGroups.push(target.group) + } + } + notifyGroups.sort() + debug('notify Groups %O', notifyGroups); + processNotifyGroup(notifyGroups, notifyTargets, targetRequest, phase, globalServices) + } +} diff --git a/includes/sendMetrics.js b/includes/sendMetrics.js new file mode 100644 index 0000000..312c891 --- /dev/null +++ b/includes/sendMetrics.js @@ -0,0 +1,63 @@ +/** + * Send Metrics + * + * @param Object targetRequest request details + * @param String phase(optional) hook phase: before, after, metric + * @param Object object all available routes + * @param function callback when adapter processed + * + * @return + */ +'use strict'; + +const debug = require('debug')('proxy:send-hook-metric'); +const sendHookBroadcast = require('./sendHookBroadcast.js') +const sendHookNotify = require('./sendHookNotify.js') + +module.exports = function(error, response, body, startTime, + targetRequest, requestOptions, globalServices){ + let endTime = Date.now(); + let executeTime = endTime - startTime + debug('requestOptions: %O executeTime: %s', requestOptions, executeTime); + let statusCode = 0 + if (error) { + statusCode = error.code + } else { + statusCode = response.statusCode + } + let metricJSON = { + startTime: startTime, + endTime: endTime, + executeTime: executeTime, + code: statusCode, + method: requestOptions.method, + headers: requestOptions.headers, + uri: requestOptions.uri, + route: targetRequest.route, + } + if (body && body.length) { + metricJSON.responseLength = body.length; + } + if (requestOptions.body && requestOptions.body.length) { + metricJSON.requestLength = requestOptions.body.length; + } + metricJSON.request = targetRequest.requestDetails._buffer; + metricJSON.response = body; + + let targetMetrics = { + route: targetRequest.route, + path: targetRequest.path, + method: targetRequest.method, + jsonData: metricJSON, + requestDetails: { + headers: targetRequest.headers, + _buffer: JSON.stringify(metricJSON), + method: targetRequest.method + }, + endpoint: targetRequest.endpoint, + isMetric: true + } + + sendHookNotify(targetMetrics, 'metric', globalServices) + sendHookBroadcast(targetMetrics, 'metric', globalServices) +} diff --git a/includes/sendRequest.js b/includes/sendRequest.js new file mode 100644 index 0000000..72c6d54 --- /dev/null +++ b/includes/sendRequest.js @@ -0,0 +1,41 @@ +/** + * Find all targets for hook + * + * @param Object RequestOptions options for request module + * @param Object targetRequest request details + * @param Object object all available routes + * @param Function callback that receive answer + * + * @return array of objects with available routes + */ +'use strict'; + +const request = require('request'); +const debug = require('debug')('proxy:request'); +const debugBody = require('debug')('proxy:request-body'); +const url = require('url'); + + +module.exports = function(requestOptions, targetRequest, globalServices, callback){ + // Validate URI + let uri = url.parse(requestOptions.uri); + if (!(uri.host || (uri.hostname && uri.port)) && !uri.isUnix) { + return callback(new Error('Invalid URI' + requestOptions.uri)) + } + let startTime = Date.now(); + debug('requestOptions: %O', requestOptions); + request(requestOptions, function(error, response, body) { + debug('requestOptions: %O answer err %O body %s', requestOptions, error, body); + if (!targetRequest.isMetric) { + const sendMetrics = require('./sendMetrics.js') + sendMetrics(error, response, body, startTime, targetRequest, requestOptions, globalServices) + } + if (error) { + // TODO add limit to re send + debug('_request %O Error received: %O', requestOptions, error); + } else { + debugBody('_request %O Body: %s', requestOptions, body); + } + return callback(error, response, body) + }) +} diff --git a/includes/signature.js b/includes/signature.js index d59015b..3f91aca 100644 --- a/includes/signature.js +++ b/includes/signature.js @@ -6,7 +6,5 @@ const crypto = require('crypto'); module.exports = function signature(protocol, data, secret) { - return crypto.createHmac(protocol, secret) - .update(data) - .digest('hex'); + return crypto.createHmac(protocol, secret).update(data).digest('hex') }; diff --git a/kill.js b/kill.js index fa4d096..8c4cdff 100644 --- a/kill.js +++ b/kill.js @@ -4,14 +4,14 @@ require('dotenv').config(); if (process.env.PROXY && process.env.PIDFILE) { try { - var pid = fs.readFileSync(process.env.PIDFILE + '.proxy'); + let pid = fs.readFileSync(process.env.PIDFILE + '.proxy'); process.kill(pid, 'SIGINT'); } catch (e) {} } if (process.env.ADMIN && process.env.PIDFILE) { try { - var pid = fs.readFileSync(process.env.PIDFILE); + let pid = fs.readFileSync(process.env.PIDFILE); process.kill(pid, 'SIGINT'); } catch (e) {} } diff --git a/router-proxy.js b/router-proxy.js index d94ab7e..ca4ae03 100644 --- a/router-proxy.js +++ b/router-proxy.js @@ -11,6 +11,7 @@ const dgram = require('dgram'); const url = require('url'); const signature = require('./includes/signature.js'); const ExplorerClass = require('./includes/explorerClass.js'); +const proxyRequest = require('./includes/request.js') var debug = { log: debugF('proxy:log'), @@ -109,7 +110,14 @@ function ProxyRequestGet(url, requestDetails, callback) { let cutPosition = requestDetails.url.lastIndexOf('/'); let route = requestDetails.url.substring(0, cutPosition); let path = requestDetails.url.substring(cutPosition + 1); - proxyRequest(route, path, 'GET', url, requestDetails, callback); + let targetRequest = { + route: route, + path: path, + method: 'GET', + jsonData: url, + requestDetails: requestDetails + } + proxyRequest(targetRequest, globalServices, callback); } /** @@ -122,7 +130,14 @@ function ProxyRequestPOST(jsonData, requestDetails, callback) { if (requestDetails.url.charAt(requestDetails.url.length - 1) == '/') { route = requestDetails.url.substring(0, requestDetails.url.length - 1); } - proxyRequest(route, path, 'POST', jsonData, requestDetails, callback); + let targetRequest = { + route: route, + path: path, + method: 'POST', + jsonData: jsonData, + requestDetails: requestDetails + } + proxyRequest(targetRequest, globalServices, callback); } /** @@ -133,7 +148,14 @@ function ProxyRequestPUT(jsonData, requestDetails, callback) { let cutPosition = requestDetails.url.lastIndexOf('/'); let route = requestDetails.url.substring(0, cutPosition); let path = requestDetails.url.substring(cutPosition + 1); - proxyRequest(route, path, 'PUT', jsonData, requestDetails, callback); + let targetRequest = { + route: route, + path: path, + method: 'PUT', + jsonData: jsonData, + requestDetails: requestDetails + } + proxyRequest(targetRequest, globalServices, callback); } /** @@ -144,7 +166,14 @@ function ProxyRequestDELETE(url, requestDetails, callback) { let cutPosition = requestDetails.url.lastIndexOf('/'); let route = requestDetails.url.substring(0, cutPosition); let path = requestDetails.url.substring(cutPosition + 1); - proxyRequest(route, path, 'DELETE', url, requestDetails, callback); + let targetRequest = { + route: route, + path: path, + method: 'DELETE', + jsonData: url, + requestDetails: requestDetails + } + proxyRequest(targetRequest, globalServices, callback); } @@ -158,8 +187,14 @@ function ProxyRequestSEARCH(jsonData, requestDetails, callback) { if (requestDetails.url.charAt(requestDetails.url.length - 1) == '/') { route = requestDetails.url.substring(0, requestDetails.url.length - 1); } - - proxyRequest(route, path, 'SEARCH', jsonData, requestDetails, callback); + let targetRequest = { + route: route, + path: path, + method: 'SEARCH', + jsonData: jsonData, + requestDetails: requestDetails + } + proxyRequest(targetRequest, globalServices, callback); } @@ -186,921 +221,14 @@ function ProxyRequestOPTIONS(jsonData, requestDetails, callbacks, callback) { if (requestDetails.url.charAt(requestDetails.url.length - 1) == '/') { route = requestDetails.url.substring(0, requestDetails.url.length - 1); } - proxyRequest(route, path, 'OPTIONS', jsonData, requestDetails, callback); -} - -/** - * Source: https://gist.github.com/jasonrhodes/2321581 - * A function to take a string written in dot notation style, and use it to - * find a nested object property inside of an object. - * - * Useful in a plugin or module that accepts a JSON array of objects, but - * you want to let the user specify where to find various bits of data - * inside of each custom object instead of forcing a standardized - * property list. - * - * @param String nested A dot notation style parameter reference (ie "urls.small") - * @param Object object (optional) The object to search - * - * @return the value of the property in question - */ -function getProperty( propertyName, object ) { - let parts = propertyName.split( "." ), - length = parts.length, - i, - property = object; - for ( i = 0; i < length; i++ ) { - if (property[parts[i]] === undefined) { - return new Error('Property Does not exists') - } - property = property[parts[i]]; - } - return property; -} -/** - * Check Conditions for request. - */ -function checkConditions(conditions, requestDetails, jsonData) { - debug.debug('checkConditions %O requestDetails: %O json: %O', conditions, - requestDetails, jsonData) - if (conditions.headers && conditions.headers.length) { - for (let header of conditions.headers ) { - if (!requestDetails.headers[header.name]) { - return false - } - let receivedHeaderValue = requestDetails.headers[header.name] - if (header.isRegex) { - let pattern = new RegExp(header.value, "i") - if (!pattern.test(receivedHeaderValue)) { - return false - } - } else { - if (receivedHeaderValue !== header.value) { - return false - } - } - } - } - // check methods - if (conditions.methods && conditions.methods.length) { - if (conditions.methods.indexOf(requestDetails.method) == -1) { - return false; - } - } - // check payload - if (conditions.payload && conditions.payload.length - && jsonData) { - if (typeof jsonData != "object") { - return false - } - for (let payload of conditions.payload ) { - debug.debug('Checking for condition %O', payload) - let receivedPayloadValue = getProperty(payload.name, jsonData) - debug.debug('receivedPayloadValue %O', receivedPayloadValue) - if (receivedPayloadValue instanceof Error) { - - return false - } - if (payload.isRegex) { - let pattern = new RegExp(payload.value, "i") - debug.debug('pattern.test %O', pattern.test(receivedPayloadValue)) - if (!pattern.test(receivedPayloadValue)) { - return false - } - } else { - if (receivedPayloadValue !== payload.value) { - return false - } - } - } - } - return true -} - -/** - * Check if route match request. - */ -function matchRoute(targetRequest, routeItem) { - let routeItems = targetRequest.route.split('/'); - - if (routeItem.type == "metric") { - if (routeItem.conditions) { - if (!checkConditions(routeItem.conditions, - targetRequest.requestDetails, targetRequest.jsonData)) { - return false - } - } - return true - } - if (routeItem.path && routeItem.path.length == 1 && routeItem.path[0] == '*') { - if (routeItem.conditions) { - if (!checkConditions(routeItem.conditions, - targetRequest.requestDetails, targetRequest.jsonData)) { - return false - } - } - return true - } - // Check path and if match, set routeItem.matchVariables with values. - let checkPath = function(paths){ - for (let path of paths) { - // If route qual saved path - if (path == targetRequest.route) { - return true; - } - - // If routeItems.length == 1, and did not match - if (routeItems.length == 1) { - if (path != targetRequest.route) { - continue; - } - } - - var pathItems = path.split('/'); - if (pathItems.length != routeItems.length) { - continue; - } - var fullPathMatched = true; - for (var i = 0; i < routeItems.length; i++) { - if (pathItems[i].charAt(0) == ':') { - routeItem.matchVariables[pathItems[i].substring(1)] = routeItems[i]; - } else { - if (routeItems[i] != pathItems[i]) { - fullPathMatched = false; - break; - } - } - } - if (fullPathMatched) { - return true; - } - } - } - if (!checkPath(routeItem.path)) { - return false - } - if (routeItem.conditions) { - if (!checkConditions(routeItem.conditions, - targetRequest.requestDetails, targetRequest.jsonData)) { - return false - } - } - return true; -} - -/** - * Process before Hooks. - */ -function hookCall(targetRequest, phase, callback) { - - let getHeaders = function(router, hookType){ - let headers = {}; - // TODO verify date,content-type, transfer-encoding headers - let skipHeaders = [ - 'host', // issue to properly connect - 'connection', // if it is closed, behavior is unexpected - 'transfer-encoding', //we need to ignore that one. - 'content-length', //issue with recounting length of the package - ] - for (var i in targetRequest.requestDetails.headers) { - if (skipHeaders.indexOf(i) != -1) { - continue - } - headers[i] = targetRequest.requestDetails.headers[i]; - } - for (var i in router.matchVariables) { - headers['mfw-' + i] = router.matchVariables[i]; - } - headers['x-origin-url'] = targetRequest.route - headers['x-origin-method'] = targetRequest.method - headers['x-hook-phase'] = phase - headers['x-hook-type'] = hookType - headers['x-endpoint-scope'] = targetRequest.endpoint.scope - debug.debug('%s headers %O', targetRequest.route, headers); - return headers; - } - // send Broadcast - let broadcastTargets = findHookTarget(targetRequest, phase, 'broadcast') - debug.debug('Bradcast: Phase %s for %s result: %O', phase, targetRequest.route, broadcastTargets); - if (broadcastTargets instanceof Array) { - let getBroadcastRequest = function(){ - if (broadcastTargets instanceof Error) { - return broadcastTargets - } - if (!broadcastTargets.length) { - return false - } - let router = broadcastTargets.pop() - debug.log('Notify route %s result %O', targetRequest.route, router); - let headers = getHeaders(router, 'broadcast') - // Sign request for hook - headers['x-hook-signature'] = 'sha256=' - + signature('sha256', - targetRequest.requestDetails._buffer, - router.secureKey); - return { - uri: router.url + targetRequest.path, - method: 'NOTIFY', - headers: headers, - body: targetRequest.requestDetails._buffer - } - } - let callbackBroadcastRequest = function(err, response, body){ - // No action on broadcast hook. - if (err) { - debug.log('broadcast failed %O', err); - } - debug.log('broadcast sent'); - // If more in queue left - send more - if (broadcastTargets.length) { - _request(getBroadcastRequest, callbackBroadcastRequest, targetRequest) - } - } - _request(getBroadcastRequest, callbackBroadcastRequest, targetRequest) - } - - // send Notify - let notifyTargets = findHookTarget(targetRequest, phase, 'notify') - debug.debug('Notify: Phase %s for %s result: %O', phase, targetRequest.route, notifyTargets); - if (notifyTargets instanceof Array) { - let notifyGroups = [] - for (let target of notifyTargets) { - if (target.group) { - if (notifyGroups.indexOf(target.group) == -1) { - notifyGroups.push(target.group) - } - } - } - notifyGroups.sort() - debug.debug('notify Groups %O', notifyGroups); - if (notifyGroups.length){ - let currentNotifyGroup = notifyGroups.shift() - let getNotifyRequest = function(){ - debug.debug('notify Groups %s %O',currentNotifyGroup, notifyGroups); - if (!currentNotifyGroup) { - return false - } - let notifyGroupTargets = findHookTarget(targetRequest, phase, 'notify', currentNotifyGroup) - debug.debug('Notify: Phase %s result: %O', phase, notifyGroupTargets); - if (notifyGroupTargets instanceof Error) { - return notifyGroupTargets - } - if (!notifyGroupTargets.length) { - return false - } - let router = false - if (notifyGroupTargets.length == 1) { - router = notifyGroupTargets.pop() - } else { - // TODO: add diferent strategy to choose one of the routes - router = getMinLoadedRouter(notifyGroupTargets); - } - debug.log('Notify route %s result %O', targetRequest.route, router); - let headers = getHeaders(router, 'notify') - headers['x-hook-group'] = currentNotifyGroup - // Sign request for hook - headers['x-hook-signature'] = 'sha256=' - + signature('sha256', - targetRequest.requestDetails._buffer, - router.secureKey); - return { - uri: router.url + targetRequest.path, - method: 'NOTIFY', - headers: headers, - body: targetRequest.requestDetails._buffer - } - } - let callbackNotifyRequest = function(err, response, body) { - if (err) { - debug.log('notify failed %O', err); - } - debug.log('notify sent'); - // If more groups left - send more - if (notifyGroups.length) { - currentNotifyGroup = notifyGroups.shift() - _request(getNotifyRequest, callbackNotifyRequest, targetRequest) - } - } - _request(getNotifyRequest, callbackNotifyRequest, targetRequest) - } - } - - - // send adapter - let adapterTargets = findHookTarget(targetRequest, phase, 'adapter') - debug.debug('Adapter: Phase %s for %s result: %O', phase, targetRequest.route, adapterTargets); - if (adapterTargets instanceof Error) { - // No adapters found. return true, no error but nothing to process. - debug.debug('No adapter groups found'); - return callback(true) - } - - let adapterGroups = [] - for (let target of adapterTargets) { - if (target.group) { - if (adapterGroups.indexOf(target.group) == -1) { - adapterGroups.push(target.group) - } - } - } - adapterGroups.sort() - debug.debug('adapter Groups %O', adapterGroups); - if (!adapterGroups.length) { - // No adapters found. return true, no error but nothing to process. - debug.debug('No adapter groups found'); - return callback(true) - } - let currentAdapterGroup = adapterGroups.shift() - let getAdapterRequest = function(){ - if (!currentAdapterGroup) { - return false - } - let adapterGroupTargets = findHookTarget(targetRequest, phase, 'adapter', currentAdapterGroup) - if (adapterGroupTargets instanceof Error) { - return adapterGroupTargets - } - if (!adapterGroupTargets.length) { - return false - } - let router = false - if (adapterGroupTargets.length == 1) { - router = adapterGroupTargets.pop() - } else { - // TODO: add diferent strategy to choose one of the routes - router = getMinLoadedRouter(adapterGroupTargets); - } - debug.log('Notify route %s result %O', targetRequest.route, router); - let headers = getHeaders(router, 'adapter') - headers['x-hook-group'] = currentAdapterGroup - // Sign request for hook - headers['x-hook-signature'] = 'sha256=' - + signature('sha256', - targetRequest.requestDetails._buffer, - router.secureKey); - return { - uri: router.url + targetRequest.path, - method: 'NOTIFY', - headers: headers, - body: targetRequest.requestDetails._buffer - } - } - let callbackAdapterRequest = function(err, response, body) { - let headerStatusName = 'x-hook-adapter-status-' + currentAdapterGroup + '-' + phase - if (err || response.statusCode != 200) { - if (err) { - debug.log('adapter failed %O', err); - // TODO status header for adapter - targetRequest.requestDetails.headers[headerStatusName] = 'error: ' + err.message - } else { - debug.log('Adapter failed with code: %s body: %s', response.statusCode, body) - for (var i in response.headers) { - if (i.substring(0,6) == 'x-set-') { - let headerName = i.substr(6) - targetRequest.requestDetails.headers[headerName] = response.headers[i]; - } - } - } - - } else { - debug.log('adapter processed'); - targetRequest.requestDetails._buffer = body - // need to set headers x-set-XXXXX - debug.debug('Adapter Headers received: %O code: %s', response.headers, response.statusCode); - for (var i in response.headers) { - if (i.substring(0,6) == 'x-set-') { - let headerName = i.substr(6) - targetRequest.requestDetails.headers[headerName] = response.headers[i]; - } - } - delete targetRequest.requestDetails.headers['content-length'] - if (phase == 'before') { - // resign it - if (targetRequest.requestDetails.headers.signature) { - targetRequest.requestDetails.headers.signature = 'sha256=' - + signature('sha256', - targetRequest.requestDetails._buffer, - targetRequest.endpoint.secureKey); - } - } - } - - // If more groups left - send more - if (adapterGroups.length) { - currentAdapterGroup = adapterGroups.shift() - return _request(getAdapterRequest, callbackAdapterRequest, targetRequest) - } - // return back via callback - callback() - } - _request(getAdapterRequest, callbackAdapterRequest, targetRequest) - -} - -/** - * decode buffer to specidied by content-type format. - */ -function decodeData(contentType, buffer){ - let data = false - switch (contentType) { - case undefined: // version 1.x compatibility. If no content-type provided, assume json. - case 'application/json': { - data = JSON.parse(buffer); - break; - } - // Todo support more decoders here? - default: { - data = buffer - } - } - return data -} - -/** - * Find all hook routes by stage. - */ -function findHookTarget(targetRequest, phase, type, group){ - debug.debugHook('Find all hooks route: %s phase: %s type: %s group: %s', - targetRequest.route, phase, type, group); - let allHookTargets = findAllTargets(targetRequest, 'hook') - if (allHookTargets instanceof Error) { - return allHookTargets - } - let finalHookTable = [] - for (let target of allHookTargets){ - // skip hooks with no hook properties - if (!target.hook || !target.hook.length) { - continue - } - for (let hook of target.hook) { - if (phase !== null && hook.phase !== phase) { - continue - } - if (hook.type !== type) { - continue - } - let targetCopy = JSON.parse(JSON.stringify(target)) - delete targetCopy.hook - if (hook.group) { - targetCopy.group = hook.group - } else { - targetCopy.group = '_default' - } - - finalHookTable.push(targetCopy) - } - } - if (typeof group !== "undefined") { - finalHookTable = finalHookTable.filter(function(elem){ - return elem.group == group - }) - } - if (!finalHookTable.length) { - debug.debug('Not found for %s', targetRequest.route); - debug.log('Hook instance %s not found', group); - debug.debugHook('Hook instance %s not found', group); - return new Error('Hook instance not found'); - } - return finalHookTable -} - -/** - * Find all routes. - */ -function findAllTargets(targetRequest, type) { - debug.debug('Find all routes %s', targetRequest.route); - - var availableRoutes = []; - for (let i in globalServices) { - if (globalServices[i].type && globalServices[i].type.toLowerCase() !== type) { - continue - } - // For easy deployment when service need to stop receiving new requests. - if (!globalServices[i].online) { - continue - } - // Making copy of the router. - let routeItem = JSON.parse(JSON.stringify(globalServices[i])); - routeItem.matchVariables = {}; - if (matchRoute(targetRequest, routeItem)) { - availableRoutes.push(routeItem); - } - } - debug.debug('Available routes type: %s route: %s availableRoutes: %s', type, targetRequest.route, - JSON.stringify(availableRoutes , null, 2)); - if (availableRoutes.length == 0) { - debug.debug('Not found for %s', targetRequest.route); - return new Error('Endpoint not found'); - } - return availableRoutes; -} - -/** - * Get Router with minimum CPU used. - */ -function getMinLoadedRouter(availableRoutes) { - let minRouter = availableRoutes.pop(); - let totalCPU = { - cpu:0, - loadavg: 0 - } - if (minRouter.metrics) { - totalCPU = minRouter.metrics.reduce(function(a, b) { - - return { - cpu : parseFloat(a.cpu) + parseFloat(b.cpu) + a.loadavg[0] + b.loadavg[0], - loadavg: [0] - }; - }); - } - minRouter.cpu = totalCPU.cpu; - debug.debug('MinRouter %O', minRouter); - for (let i in availableRoutes) { - let totalCPU = { - cpu:0, - loadavg: 0 - } - if (availableRoutes[i].metrics) { - totalCPU = availableRoutes[i].metrics.reduce(function(a, b) { - return { - cpu : parseFloat(a.cpu) + parseFloat(b.cpu) + a.loadavg[0] + b.loadavg[0], - loadavg: [0] - }; - }); - } - availableRoutes[i].cpu = totalCPU.cpu; - if (availableRoutes[i].cpu < minRouter.cpu) { - minRouter = availableRoutes[i]; - } - debug.debug('MinRouter %O', minRouter); - debug.debug('availableRoutes %s %O',i, availableRoutes[i]); - } - return minRouter; -} - -function _request(getRequest, callback, targetRequest, noMetric) { - let requestOptions = getRequest() - - if (requestOptions instanceof Error) { - return callback(requestOptions) - } - if (requestOptions === false) { - return callback(false) - } - - // Validate URI - let uri = url.parse(requestOptions.uri); - if (!(uri.host || (uri.hostname && uri.port)) && !uri.isUnix) { - return callback(new Error('Invalid URI' + requestOptions.uri)) - } - - let getHeaders = function(router, hookType){ - let headers = {}; - // TODO verify date,content-type, transfer-encoding headers - let skipHeaders = [ - 'host', - 'date', - 'connection', - 'content-length', - 'transfer-encoding' - ] - for (var i in targetRequest.requestDetails.headers) { - if (skipHeaders.indexOf(i) != -1) { - continue - } - headers[i] = targetRequest.requestDetails.headers[i]; - } - for (var i in router.matchVariables) { - headers['mfw-' + i] = router.matchVariables[i]; - } - headers['x-origin-url'] = targetRequest.route - headers['x-origin-method'] = targetRequest.method - headers['x-endpoint-scope'] = targetRequest.endpoint.scope - debug.debug('%s headers %O', targetRequest.route, headers); - return headers; - } - let startTime = Date.now(); - debug.request('requestOptions: %O', requestOptions); - request(requestOptions, function(error, response, body) { - debug.request('requestOptions: %O answer err %O body %s' , requestOptions, error, body); - let endTime = Date.now(); - debug.debugMetric('requestOptions: %O time: %s', requestOptions, endTime - startTime); - if (!noMetric) { - let metricTargets = findAllTargets(targetRequest, 'metric') - debug.debugMetric('findHookTarget: for %s result: %O', targetRequest.route, metricTargets); - - if (metricTargets instanceof Array) { - let getMetricRequest = function(){ - if (metricTargets instanceof Error) { - return metricTargets - } - if (!metricTargets.length) { - return false - } - let router = metricTargets.pop() - debug.log('Metric route %s result %O', targetRequest.route, router); - - let statusCode = 0 - if (error) { - statusCode = error.code - } else { - if (response.statusCode) { - statusCode = response.statusCode - } - } - - let metricJSON = { - startTime: startTime, - endTime: endTime, - code: statusCode, - method: requestOptions.method, - headers: requestOptions.headers, - uri: requestOptions.uri, - route: targetRequest.route, - } - if (body && body.length) { - metricJSON.responseLength = body.length; - } - if (requestOptions.body && requestOptions.body.length) { - metricJSON.requestLength = requestOptions.body.length; - } - if (!router.meta) { - metricJSON.request = targetRequest.requestDetails._buffer; - metricJSON.response = body; - } - let metricBody = JSON.stringify(metricJSON) - let headers = getHeaders(router, 'metric') - // Sign request for hook - headers['x-hook-signature'] = 'sha256=' - + signature('sha256', metricBody, router.secureKey); - return { - uri: router.url + targetRequest.path, - method: 'NOTIFY', - headers: headers, - body: metricBody, - timeout: 300 // For metrics we limit to 300 ms. - } - } - let callbackMetricRequest = function(err, response, body){ - // No action on broadcast hook. - if (err) { - debug.log('metric failed %O', err); - } - debug.log('metric sent'); - debug.debugMetric('Metric targetRequest %O ', targetRequest); - // If more in queue left - send more - if (metricTargets.length) { - _request(getMetricRequest, callbackMetricRequest, targetRequest, true) - } - } - _request(getMetricRequest, callbackMetricRequest, targetRequest, true) - } else { - debug.debugMetric('no metric enpoints'); - } - } else { - debug.debugMetric('metric disabled'); - } - - if (error) { - // TODO add limit to re send - debug.debug('_request Error received: %O', error); - debug.debug('_request Restart request: %O', requestOptions); - // Do not try to redeliver metrics. can lock a event loop - if(!noMetric) { - return _request(getRequest, callback, targetRequest); - } - } - - debug.debug('%s body: %s', requestOptions.uri, body); - return callback(null, response, body) - }) -} - -/** - * Proxy request to backend server. - */ -function proxyRequest(route, path, method, jsonData, requestDetails, callback) { - debug.debug('Route base: %s', route); let targetRequest = { route: route, path: path, - method: method, + method: 'OPTIONS', jsonData: jsonData, requestDetails: requestDetails } - let endpointTargets = findAllTargets(targetRequest, 'handler'); - if (endpointTargets instanceof Error) { - debug.debug('Route %s err %O', route, endpointTargets); - return callback(endpointTargets, null); - } - - targetRequest.endpoint = { - scope: endpointTargets[0].scope, - secureKey: endpointTargets[0].secureKey - } - - hookCall(targetRequest, 'before', function(){ - //used later in sendBroadcastMessage - let router = false - // process request to endpoint - let getEndpointRequest = function(){ - let endpointTargets = findAllTargets(targetRequest, 'handler'); - if (endpointTargets instanceof Error) { - return endpointTargets - } - if (!endpointTargets.length) { - return false - } - - if (endpointTargets.length == 1) { - router = endpointTargets.pop(); - } else { - // TODO: add diferent strategy to choose one of the routes - router = getMinLoadedRouter(endpointTargets); - } - debug.log('Endpoint route %s result %O', route, router); - let headers = {}; - let i; - let skipHeaders = [ - 'host', - 'connection', - 'content-length' - ] - for (i in requestDetails.headers) { - if (skipHeaders.indexOf(i) != -1) { - continue; - } - headers[i] = requestDetails.headers[i]; - } - - for (i in router.matchVariables) { - headers['mfw-' + i] = router.matchVariables[i]; - } - return { - uri: router.url + path, - method: method, - headers: headers, - body: requestDetails._buffer - } - } - let callbackEndpointRequest = function(err, response, body){ - if (err) { - debug.log('endpoint failed %O', err); - if (err !== false) { - // TODO call after hooks - return callback(err, null) - } - // TODO call after hooks - return callback(new Error('Endpoint not found'), null) - } - let bodyJSON = "" - try { - bodyJSON = decodeData(response.headers['content-type'], body) - } catch (e) { - debug.debug('decodeData Error received: %O', e); - return callback(e); - } - debug.debug('%s body: %O', route, body); - - // process after hooks - // hookCall requestDetails.headers and _buffer should contain response data. - let answerDetails = { - headers: response.headers, - _buffer: body, - method: method - } - let targetAnswer = { - route: route, - path: path, - method: method, - jsonData: bodyJSON, - requestDetails: answerDetails, - endpoint: targetRequest.endpoint - } - hookCall(targetAnswer, 'after', function(){ - - // Double check updated _buffer after proxy. - let body = false - try { - body = decodeData(answerDetails.headers['content-type'], answerDetails._buffer) - } catch (e) { - debug.debug('decodeData Error received: %O', e); - return callback(e); - } - if (typeof body == "object") { - // prefix with base_URL all urls - if (method != 'OPTIONS') { - if (body.url) { - // Make sure that url is not absolute - if (body.url.indexOf('http://') == -1 - && body.url.indexOf('https://') == -1) { - body.url = process.env.BASE_URL + body.url; - } - } else if (body.id) { - body.url = process.env.BASE_URL + route + '/' + body.id; - } - } - } - if (body instanceof Array) { - for (var i in body) { - if (body[i].url) { - // Make sure that url is not absolute - if (body[i].url.indexOf('http://') == -1 - && body[i].url.indexOf('https://') == -1 ) { - body[i].url = process.env.BASE_URL + body[i].url; - } - } else if (body[i].id) { - body[i].url = process.env.BASE_URL + route + '/' + body[i].id; - } - } - } - - let responseHeaders = { - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, DELETE, PUT, SEARCH', - 'Access-Control-Allow-Headers': 'content-type, signature, access_token,' - + ' token, Access-Token', - 'Access-Control-Expose-Headers': 'x-total-count', - }; - for (var i in answerDetails.headers) { - if (i.substring(0,1) == 'x') { - responseHeaders[i] = answerDetails.headers[i]; - } - } - // deprecated. websoket need to be rewriten as a hook broadcast - if (response.statusCode == 200) { - sendBroadcastMessage(router, method, requestDetails.url, body); - } - callback(null, { - code: response.statusCode, - answer: body, - headers: responseHeaders - }); - - }) - } - _request(getEndpointRequest, callbackEndpointRequest, targetRequest) - - }) -} - -/** - * Proxy request to backend server. - * deprecated. WS need to be replaced by boracast hook - */ -function sendBroadcastMessage(router, method, path, message) { - debug.debug('UDP broadcast %O %s %s %O', router, method, path, message); - - for (let routeItem of globalServices) { - if (routeItem.type !== 'websocket') { - continue - } - var broadcastMessage = { - method: method, - route: router.path, - scope: router.scope, - loaders: router.matchVariables, - path: path - }; - switch (routeItem.methods[method.toLowerCase()]) { - case 'data': { - broadcastMessage.message = message; - break; - } - case 'meta': { - broadcastMessage.meta = true; - break; - } - default: { - continue; - } - } - var URL = url.parse(routeItem.url); - - broadcastMessage.signature = ['sha256', - signature('sha256', JSON.stringify(broadcastMessage), routeItem.secureKey)]; - - debug.debug('UDP broadcast to %O %O', routeItem, URL); - var bufferedMessage = Buffer.from(JSON.stringify(broadcastMessage)); - sendBroadcastMessageToClient(bufferedMessage, URL); - } -} - -/** - * Send broadcast message to UDP client. - * deprecated. WS need to be replaced by boracast hook - */ -function sendBroadcastMessageToClient(bufferedMessage, URL) { - let client = dgram.createSocket('udp4'); - client.send(bufferedMessage, URL.port, URL.hostname, function(err) { - if (err) { - debug.debug('UDP broadcast Error: %O', err); - client.close(); - } - }); - client.on('message', function(message, remote) { - debug.debug('Received from server: ' + message); - client.close(); - }); + proxyRequest(targetRequest, globalServices, callback); } /** diff --git a/test/BuildURI.js b/test/BuildURI.js new file mode 100644 index 0000000..88d8974 --- /dev/null +++ b/test/BuildURI.js @@ -0,0 +1,26 @@ +const expect = require("chai").expect; + +const buildURI = require('../includes/buildURI.js') + +describe('buildURI', function(){ + it('Build without ending slash', function(done){ + expect(buildURI('http://test', 'id')).to.equal('http://test/id') + done(); + }); + it('Build with ending slash', function(done){ + expect(buildURI('http://test/', 'id')).to.equal('http://test/id') + done(); + }); + it('Build with begining slash', function(done){ + expect(buildURI('http://test', '/id')).to.equal('http://test/id') + done(); + }); + it('Build with first part is null', function(done){ + expect(buildURI(null, 'id')).to.equal('/id') + done(); + }); + it('Build with second part is null', function(done){ + expect(buildURI('http://test', null)).to.equal('http://test/') + done(); + }); +}) diff --git a/test/checkConditions.js b/test/checkConditions.js new file mode 100644 index 0000000..0ecb57a --- /dev/null +++ b/test/checkConditions.js @@ -0,0 +1,210 @@ +const expect = require("chai").expect; + +const checkConditions = require('../includes/checkConditions.js') + +let requestDetails = { + headers: { + string: "test", + number: "10", + float: "10.1" + }, + method: "POST", +} +let jsonData = { + string: "test", + number: 10, + float: 10.1, + sub: { + string: "test", + number: 10, + float: 10.1, + } +} + +describe('conditions Check', function(){ + it('check missing string header', function(done){ + expect(checkConditions({ + headers: [{ + name: "string-missing", + value: "test" + }] + }, requestDetails, jsonData)).to.equal(false) + done(); + }) + it('check equal string header', function(done){ + expect(checkConditions({ + headers: [{ + name: "string", + value: "test" + }] + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check not equal string header', function(done){ + expect(checkConditions({ + headers: [{ + name: "string", + value: "test1" + }] + }, requestDetails, jsonData)).to.equal(false) + done(); + }) + it('check regex string header', function(done){ + expect(checkConditions({ + headers: [{ + name: "string", + value: "test", + isRegex: true + }] + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check regex string header fail', function(done){ + expect(checkConditions({ + headers: [{ + name: "string", + value: "test1", + isRegex: true + }] + }, requestDetails, jsonData)).to.equal(false) + done(); + }) + it('check payload is string', function(done){ + expect(checkConditions({ + payload: [{ + name: "string", + value: "test" + }] + }, requestDetails, 'jsonData')).to.equal(false) + done(); + }) + it('check payload equal string', function(done){ + expect(checkConditions({ + payload: [{ + name: "string", + value: "test" + }] + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check payload not equal string', function(done){ + expect(checkConditions({ + payload: [{ + name: "string", + value: "test1" + }] + }, requestDetails, jsonData)).to.equal(false) + done(); + }) + it('check payload regex string', function(done){ + expect(checkConditions({ + payload: [{ + name: "string", + value: "test", + isRegex: true + }] + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check payload regex missing', function(done){ + expect(checkConditions({ + payload: [{ + name: "string", + value: "test2", + isRegex: true + }] + }, requestDetails, jsonData)).to.equal(false) + done(); + }) + it('check subProperty missing payload', function(done){ + expect(checkConditions({ + payload: [{ + name: "sub.string2", + value: "test" + }] + }, requestDetails, jsonData)).to.equal(false) + done(); + }) + it('check subProperty equal string payload', function(done){ + expect(checkConditions({ + payload: [{ + name: "sub.string", + value: "test" + }] + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check subProperty not equal string payload', function(done){ + expect(checkConditions({ + payload: [{ + name: "sub.string", + value: "test1" + }] + }, requestDetails, jsonData)).to.equal(false) + done(); + }) + it('check subProperty regex string payload', function(done){ + expect(checkConditions({ + payload: [{ + name: "sub.string", + value: "test", + isRegex: true + }] + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check method as a string', function(done){ + expect(checkConditions({ + methods: 'post' + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check method LowerCase', function(done){ + expect(checkConditions({ + methods: ['post'] + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check method UpperCase', function(done){ + expect(checkConditions({ + methods: ['POST'] + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check method CamelCase', function(done){ + expect(checkConditions({ + methods: ['PosT'] + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check method CamelCase mismatch', function(done){ + expect(checkConditions({ + methods: ['Get'] + }, requestDetails, jsonData)).to.equal(false) + done(); + }) + it('check method as a string LowerCase', function(done){ + expect(checkConditions({ + methods: 'post' + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check method as a string UpperCase', function(done){ + expect(checkConditions({ + methods: 'POST' + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check method as a string CamelCase', function(done){ + expect(checkConditions({ + methods: 'PosT' + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check method as a string CamelCase mismatch', function(done){ + expect(checkConditions({ + methods: 'Get' + }, requestDetails, jsonData)).to.equal(false) + done(); + }) +}) diff --git a/test/checkConditionsMingo.js b/test/checkConditionsMingo.js new file mode 100644 index 0000000..232db9e --- /dev/null +++ b/test/checkConditionsMingo.js @@ -0,0 +1,151 @@ +const expect = require("chai").expect; + +const checkConditions = require('../includes/checkConditionsMingo.js') + +let requestDetails = { + headers: { + string: "test", + number: 10, + float: 10.1 + }, + method: "POST", +} +let jsonData = { + string: "test", + number: 10, + float: 10.1, + sub: { + string: "test", + number: 10, + float: 10.1, + } +} + +describe('conditions Check Mongo query Language', function(){ + it('check equal string header', function(done){ + expect(checkConditions({ + headers: { + string: "test" + } + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check not equal string header', function(done){ + expect(checkConditions({ + headers: { + string: "test1" + } + }, requestDetails, jsonData)).to.equal(false) + done(); + }) + it('check regex string header', function(done){ + expect(checkConditions({ + headers: { + string: { + $regex: "test" + } + } + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check payload equal string', function(done){ + expect(checkConditions({ + payload: { + string: "test" + } + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check payload not equal string', function(done){ + expect(checkConditions({ + payload: { + string: "test1" + } + }, requestDetails, jsonData)).to.equal(false) + done(); + }) + it('check payload regex string', function(done){ + expect(checkConditions({ + payload: { + string: { + $regex: "test" + } + } + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check subProperty equal string payload', function(done){ + expect(checkConditions({ + payload: { + "sub.string": "test" + } + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check subProperty not equal string payload', function(done){ + expect(checkConditions({ + payload: { + "sub.string": "test1" + } + }, requestDetails, jsonData)).to.equal(false) + done(); + }) + it('check subProperty regex string payload', function(done){ + expect(checkConditions({ + payload: { + "sub.string": { + $regex: "test" + } + } + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check method LowerCase', function(done){ + expect(checkConditions({ + methods: ['post'] + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check method UpperCase', function(done){ + expect(checkConditions({ + methods: ['POST'] + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check method CamelCase', function(done){ + expect(checkConditions({ + methods: ['PosT'] + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check method CamelCase mismatch', function(done){ + expect(checkConditions({ + methods: ['Get'] + }, requestDetails, jsonData)).to.equal(false) + done(); + }) + it('check method as a string LowerCase', function(done){ + expect(checkConditions({ + methods: 'post' + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check method as a string UpperCase', function(done){ + expect(checkConditions({ + methods: 'POST' + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check method as a string CamelCase', function(done){ + expect(checkConditions({ + methods: 'PosT' + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check method as a string CamelCase mismatch', function(done){ + expect(checkConditions({ + methods: 'Get' + }, requestDetails, jsonData)).to.equal(false) + done(); + }) +}) diff --git a/test/checkConditionsSift.js b/test/checkConditionsSift.js new file mode 100644 index 0000000..53c21bf --- /dev/null +++ b/test/checkConditionsSift.js @@ -0,0 +1,151 @@ +const expect = require("chai").expect; + +const checkConditions = require('../includes/checkConditionsMongo.js') + +let requestDetails = { + headers: { + string: "test", + number: 10, + float: 10.1 + }, + method: "POST", +} +let jsonData = { + string: "test", + number: 10, + float: 10.1, + sub: { + string: "test", + number: 10, + float: 10.1, + } +} + +describe('conditions Check Mongo query Language', function(){ + it('check equal string header', function(done){ + expect(checkConditions({ + headers: { + string: "test" + } + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check not equal string header', function(done){ + expect(checkConditions({ + headers: { + string: "test1" + } + }, requestDetails, jsonData)).to.equal(false) + done(); + }) + it('check regex string header', function(done){ + expect(checkConditions({ + headers: { + string: { + $regex: "test" + } + } + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check payload equal string', function(done){ + expect(checkConditions({ + payload: { + string: "test" + } + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check payload not equal string', function(done){ + expect(checkConditions({ + payload: { + string: "test1" + } + }, requestDetails, jsonData)).to.equal(false) + done(); + }) + it('check payload regex string', function(done){ + expect(checkConditions({ + payload: { + string: { + $regex: "test" + } + } + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check subProperty equal string payload', function(done){ + expect(checkConditions({ + payload: { + "sub.string": "test" + } + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check subProperty not equal string payload', function(done){ + expect(checkConditions({ + payload: { + "sub.string": "test1" + } + }, requestDetails, jsonData)).to.equal(false) + done(); + }) + it('check subProperty regex string payload', function(done){ + expect(checkConditions({ + payload: { + "sub.string": { + $regex: "test" + } + } + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check method LowerCase', function(done){ + expect(checkConditions({ + methods: ['post'] + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check method UpperCase', function(done){ + expect(checkConditions({ + methods: ['POST'] + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check method CamelCase', function(done){ + expect(checkConditions({ + methods: ['PosT'] + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check method CamelCase mismatch', function(done){ + expect(checkConditions({ + methods: ['Get'] + }, requestDetails, jsonData)).to.equal(false) + done(); + }) + it('check method as a string LowerCase', function(done){ + expect(checkConditions({ + methods: 'post' + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check method as a string UpperCase', function(done){ + expect(checkConditions({ + methods: 'POST' + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check method as a string CamelCase', function(done){ + expect(checkConditions({ + methods: 'PosT' + }, requestDetails, jsonData)).to.equal(true) + done(); + }) + it('check method as a string CamelCase mismatch', function(done){ + expect(checkConditions({ + methods: 'Get' + }, requestDetails, jsonData)).to.equal(false) + done(); + }) +}) diff --git a/test/checkPath.js b/test/checkPath.js new file mode 100644 index 0000000..7d00919 --- /dev/null +++ b/test/checkPath.js @@ -0,0 +1,227 @@ +const expect = require("chai").expect; +const sift = require('sift').default + +const checkPath = require('../includes/checkPath.js') +let targetRequests = require('./targetRequests.js') +let routeItems = require('./routeItems.js') + +/* +Future debug code +for(let targetRequest of targetRequests) { + for(let routeItem of routeItems) { + if(checkPath(targetRequest, routeItem)) { + console.log('match') + console.log('targetRequest', targetRequest) + console.log('routeItem', routeItem) + console.log("\n") + } + } +}*/ +describe('checkPath', function(){ + it('checking post register', function(done){ + let subTargetRequest = sift({ + route: 'register', + method: 'POST' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + for (let routeItem of routeItems) { + if (checkPath(targetRequest, routeItem)) { + expect(routeItem.path).to.be.an('array').that.include('register'); + } else { + expect(routeItem.path).to.be.an('array').that.not.include('register'); + } + } + } + done() + }) + it('checking get register', function(done){ + let subTargetRequest = sift({ + route: 'register', + method: 'GET' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + for (let routeItem of routeItems) { + if (checkPath(targetRequest, routeItem)) { + expect(routeItem.path).to.be.an('array').that.include('register'); + } else { + expect(routeItem.path).to.be.an('array').that.not.include('register'); + } + } + } + done() + }) + it('checking post repos/test', function(done){ + let subTargetRequest = sift({ + route: 'repos/test', + method: 'POST' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + for (let routeItem of routeItems) { + if (checkPath(targetRequest, routeItem)) { + expect(routeItem.path).to.be.an('array').that.include('repos/:owner'); + expect(routeItem.matchVariables).to.be.an('object').to.have.property('owner', 'test'); + delete routeItem.matchVariables + } else { + expect(routeItem.path).to.be.an('array').that.not.include('repos/:owner'); + } + } + } + done() + }) + it('checking get repos/test/reponame', function(done){ + let subTargetRequest = sift({ + route: 'repos/test', + method: 'GET' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + for (let routeItem of routeItems) { + if (checkPath(targetRequest, routeItem)) { + expect(routeItem.path).to.be.an('array').that.include('repos/:owner'); + expect(routeItem.matchVariables).to.be.an('object').to.have.property('owner', 'test'); + delete routeItem.matchVariables + } else { + if (routeItem.type != "hook") { + expect(routeItem.path).to.be.an('array').that.not.include('repos/:owner'); + } + } + } + } + done() + }) + it('checking search repos', function(done){ + let subTargetRequest = sift({ + route: 'repos', + method: 'SEARCH' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + for (let routeItem of routeItems) { + if (checkPath(targetRequest, routeItem)) { + expect(routeItem.path).to.be.an('array').that.include('repos'); + expect(routeItem).to.not.have.property('matchVariables'); + } else { + expect(routeItem.path).to.be.an('array').that.not.include('repos'); + } + } + } + done() + }) + it('checking get repos/reponame', function(done){ + let subTargetRequest = sift({ + route: 'repos', + path: 'reponame', + method: 'GET' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + for (let routeItem of routeItems) { + if (checkPath(targetRequest, routeItem)) { + expect(routeItem.path).to.be.an('array').that.include('repos'); + expect(routeItem).to.not.have.property('matchVariables'); + } else { + expect(routeItem.path).to.be.an('array').that.not.include('repos'); + } + } + } + done() + }) + it('checking post repos/ownername/reponame/files', function(done){ + let subTargetRequest = sift({ + route: 'repos/ownername/reponame/files', + method: 'POST' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + for (let routeItem of routeItems) { + if (checkPath(targetRequest, routeItem)) { + expect(routeItem.path).to.be.an('array').that.include('repos/:owner/:repo/files'); + expect(routeItem.matchVariables).to.be.an('object') + .to.have.property('owner', 'ownername'); + expect(routeItem.matchVariables).to.be.an('object') + .to.have.property('repo', 'reponame'); + delete routeItem.matchVariables + } else { + expect(routeItem.path).to.be.an('array').that.not.include('repos/:owner/:repo/files'); + } + } + } + done() + }) + it('checking get repos/ownername/reponame/files/fileid', function(done){ + let subTargetRequest = sift({ + route: 'repos/ownername/reponame/files', + path: 'fileid', + method: 'GET' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + for (let routeItem of routeItems) { + if (checkPath(targetRequest, routeItem)) { + expect(routeItem.path).to.be.an('array').that.include('repos/:owner/:repo/files'); + expect(routeItem.matchVariables).to.be.an('object') + .to.have.property('owner', 'ownername'); + expect(routeItem.matchVariables).to.be.an('object') + .to.have.property('repo', 'reponame'); + delete routeItem.matchVariables + } else { + expect(routeItem.path).to.be.an('array').that.not.include('repos/:owner/:repo/files'); + } + } + } + done() + }) + it('checking search repos/ownername/files', function(done){ + let subTargetRequest = sift({ + route: 'repos/ownername/files', + method: 'SEARCH' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + for (let routeItem of routeItems) { + if (checkPath(targetRequest, routeItem)) { + expect(routeItem.path).to.be.an('array').that.include('repos/:owner/files'); + expect(routeItem.matchVariables).to.be.an('object') + .to.have.property('owner', 'ownername'); + delete routeItem.matchVariables + } else { + expect(routeItem.path).to.be.an('array').that.not.include('repos/:owner/files'); + } + } + } + done() + }) + it('checking search repos/ownername/files', function(done){ + let subTargetRequest = sift({ + route: 'repos/ownername/files', + method: 'SEARCH' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + for (let routeItem of routeItems) { + if (checkPath(targetRequest, routeItem)) { + expect(routeItem.path).to.be.an('array').that.include('repos/:owner/files'); + expect(routeItem.matchVariables).to.be.an('object') + .to.have.property('owner', 'ownername'); + delete routeItem.matchVariables + } else { + expect(routeItem.path).to.be.an('array').that.not.include('repos/:owner/files'); + } + } + } + done() + }) + it('checking get repos/ownername/files/fileid', function(done){ + let subTargetRequest = sift({ + route: 'repos/ownername/files', + path: 'fileid', + method: 'GET' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + for (let routeItem of routeItems) { + if (checkPath(targetRequest, routeItem)) { + expect(routeItem.path).to.be.an('array').that.include('repos/:owner/files'); + expect(routeItem.matchVariables).to.be.an('object') + .to.have.property('owner', 'ownername'); + delete routeItem.matchVariables + } else { + expect(routeItem.path).to.be.an('array').that.not.include('repos/:owner/files'); + } + } + } + done() + }) +}) diff --git a/test/decodeData.js b/test/decodeData.js new file mode 100644 index 0000000..c5f90c6 --- /dev/null +++ b/test/decodeData.js @@ -0,0 +1,33 @@ +const expect = require("chai").expect; + +const decodeData = require('../includes/decodeData.js') + +describe('decodeData', function(){ + it('Content-type: application/json', function(done){ + let decoded = decodeData('application/json', '{"test":"test"}') + expect(decoded).to.be.an('object').to.have.property('test', 'test'); + done(); + }); + it('Content-type: not provided. V1.3.x compatibility', function(done){ + let decoded = decodeData(undefined, '{"test":"test"}') + expect(decoded).to.be.an('object').to.have.property('test', 'test'); + done(); + }); + it('Content-type: text/plain', function(done){ + let decoded = decodeData('text/plain', '{"test":"test"}') + expect(decoded).to.be.an('string').to.equal('{"test":"test"}'); + done(); + }); + it('Content-type: application/json, broken JSON', function(done){ + let decoded = false + try { + decoded = decodeData('application/json', '{"test":"test"') + } catch (e) { + expect(e).to.be.an.instanceof(SyntaxError); + } + expect(decoded).to.equal(false); + done(); + }); + + +}) diff --git a/test/findAllTargets.js b/test/findAllTargets.js new file mode 100644 index 0000000..1c2c487 --- /dev/null +++ b/test/findAllTargets.js @@ -0,0 +1,189 @@ +const expect = require("chai").expect; +const sift = require('sift').default + +const findAllTargets = require('../includes/findAllTargets.js') +let targetRequests = require('./targetRequests.js') +let routeItems = require('./routeItems.js') + +/* Debuging code +for (let targetRequest of targetRequests) { + let result = findAllTargets(targetRequest, false, routeItems) + console.log('match') + console.log('targetRequest', targetRequest) + console.log('routeItems', result) + console.log("\n") +} +*/ + +describe('findAllTargets', function(){ + it('findAllTargets post register', function(done){ + let subTargetRequest = sift({ + route: 'register', + method: 'POST' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + let targets = findAllTargets(targetRequest, false, routeItems) + for (let routeItem of targets) { + if (routeItem.path[0] != '*') { + expect(routeItem.path).to.be.an('array').that.include('register'); + } + } + } + done() + }) + it('checking get register', function(done){ + let subTargetRequest = sift({ + route: 'register', + method: 'GET' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + let targets = findAllTargets(targetRequest, false, routeItems) + for (let routeItem of targets) { + if (routeItem.path[0] != '*') { + expect(routeItem.path).to.be.an('array').that.include('register'); + } + } + } + done() + }) + it('checking post repos/test', function(done){ + let subTargetRequest = sift({ + route: 'repos/test', + method: 'POST' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + let targets = findAllTargets(targetRequest, false, routeItems) + for (let routeItem of targets) { + if (routeItem.path[0] != '*') { + expect(routeItem.path).to.be.an('array').that.include('repos/:owner'); + expect(routeItem.matchVariables).to.be.an('object').to.have.property('owner', 'test'); + } + } + } + done() + }) + it('checking get repos/test/reponame', function(done){ + let subTargetRequest = sift({ + route: 'repos/test', + method: 'GET' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + let targets = findAllTargets(targetRequest, false, routeItems) + for (let routeItem of targets) { + if (routeItem.path[0] != '*') { + expect(routeItem.path).to.be.an('array').that.include('repos/:owner'); + expect(routeItem.matchVariables).to.be.an('object') + .to.have.property('owner', 'test'); + } + } + } + done() + }) + it('checking search repos', function(done){ + let subTargetRequest = sift({ + route: 'repos', + method: 'SEARCH' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + let targets = findAllTargets(targetRequest, false, routeItems) + for (let routeItem of targets) { + if (routeItem.path[0] != '*') { + expect(routeItem.path).to.be.an('array').that.include('repos'); + expect(routeItem.matchVariables).to.be.an('object').to.be.empty; + } + } + } + done() + }) + it('checking get repos/reponame', function(done){ + let subTargetRequest = sift({ + route: 'repos', + path: 'reponame', + method: 'GET' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + let targets = findAllTargets(targetRequest, false, routeItems) + for (let routeItem of targets) { + if (routeItem.path[0] != '*') { + expect(routeItem.path).to.be.an('array').that.include('repos'); + expect(routeItem.matchVariables).to.be.an('object').to.be.empty; + } + } + } + done() + }) + it('checking post repos/ownername/reponame/files', function(done){ + let subTargetRequest = sift({ + route: 'repos/ownername/reponame/files', + method: 'POST' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + let targets = findAllTargets(targetRequest, false, routeItems) + for (let routeItem of targets) { + if (routeItem.path[0] != '*') { + expect(routeItem.path).to.be.an('array').that.include('repos/:owner/:repo/files'); + expect(routeItem.matchVariables).to.be.an('object') + .to.have.property('owner', 'ownername'); + expect(routeItem.matchVariables).to.be.an('object') + .to.have.property('repo', 'reponame'); + } + } + } + done() + }) + it('checking get repos/ownername/reponame/files/fileid', function(done){ + let subTargetRequest = sift({ + route: 'repos/ownername/reponame/files', + path: 'fileid', + method: 'GET' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + let targets = findAllTargets(targetRequest, false, routeItems) + for (let routeItem of targets) { + if (routeItem.path[0] != '*') { + expect(routeItem.path).to.be.an('array').that.include('repos/:owner/:repo/files'); + expect(routeItem.matchVariables).to.be.an('object') + .to.have.property('owner', 'ownername'); + expect(routeItem.matchVariables).to.be.an('object') + .to.have.property('repo', 'reponame'); + } + } + } + done() + }) + it('checking search repos/ownername/files', function(done){ + let subTargetRequest = sift({ + route: 'repos/ownername/files', + method: 'SEARCH' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + let targets = findAllTargets(targetRequest, false, routeItems) + for (let routeItem of targets) { + if (routeItem.path[0] != '*') { + expect(routeItem.path).to.be.an('array').that.include('repos/:owner/files'); + expect(routeItem.matchVariables).to.be.an('object') + .to.have.property('owner', 'ownername'); + } + } + } + done() + }) + it('checking get repos/ownername/files/fileid', function(done){ + let subTargetRequest = sift({ + route: 'repos/ownername/files', + path: 'fileid', + method: 'GET' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + let targets = findAllTargets(targetRequest, false, routeItems) + for (let routeItem of targets) { + if (routeItem.path[0] != '*') { + expect(routeItem.path).to.be.an('array').that.include('repos/:owner/files'); + expect(routeItem.matchVariables).to.be.an('object') + .to.have.property('owner', 'ownername') + } + } + } + done() + }) +}) diff --git a/test/findHookTarget.js b/test/findHookTarget.js new file mode 100644 index 0000000..779dacd --- /dev/null +++ b/test/findHookTarget.js @@ -0,0 +1,102 @@ +const expect = require("chai").expect; +const sift = require('sift').default + +const findHookTarget = require('../includes/findHookTarget.js') +let targetRequests = require('./targetRequests.js') +let routeItems = require('./routeItems.js') + +/* Debuging code +for (let targetRequest of targetRequests) { + let result = findHookTarget(targetRequest, 'after', 'notify', false, routeItems) + if(!(result instanceof Error)) { + console.log('match') + console.log('targetRequest', targetRequest) + console.log('routeItems', result) + console.log("\n") + } +} +*/ + +describe('findHookTarget', function(){ + it('checking post repos/test', function(done){ + let subTargetRequest = sift({ + route: 'repos/test', + method: 'POST' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + let targets = findHookTarget(targetRequest, 'after', 'notify', false, routeItems) + expect(targets).to.be.an('array').that.is.not.empty; + for (let routeItem of targets) { + expect(routeItem.path).to.be.an('array').that.include('repos/:owner'); + expect(routeItem.matchVariables).to.be.an('object').to.have.property('owner', 'test'); + } + } + done() + }) + + it('checking post repos/ownername/reponame/files', function(done){ + let subTargetRequest = sift({ + route: 'repos/ownername/reponame/files', + method: 'POST' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + let targets = findHookTarget(targetRequest, 'after', 'notify', false, routeItems) + expect(targets).to.be.an.instanceof(Error) + } + done() + }) + + it('checking for null phase', function(done){ + for (let targetRequest of targetRequests) { + let targets = findHookTarget(targetRequest, null, 'notify', false, routeItems) + for (let routeItem of targets) { + expect(routeItem.hook.phase).to.exist; + } + + } + done() + }) + it('checking for group repo-email', function(done){ + for (let targetRequest of targetRequests) { + let targets = findHookTarget(targetRequest, null, 'notify', "repo-email", routeItems) + if (!targets.length) { + expect(targets).to.be.an.instanceof(Error) + } else { + for (let routeItem of targets) { + expect(routeItem.hook.phase).to.exist; + expect(routeItem.hook.group).to.equal("repo-email"); + } + } + + } + done() + }) + it('checking for false phase', function(done){ + for (let targetRequest of targetRequests) { + let targets = findHookTarget(targetRequest, false, 'notify', false, routeItems) + + for (let routeItem of targets) { + expect(routeItem.hook.phase).to.exist; + } + } + done() + }) + + it('finding Hook when they are missing', function(done){ + let subTargetRequest = sift({ + route: 'repos/ownername/reponame/files', + method: 'POST' + }, targetRequests) + routeItems + + let routeNoHookItems = sift({ + "type": {$ne: "hook"}, + }, routeItems) + + for (let targetRequest of subTargetRequest) { + let targets = findHookTarget(targetRequest, 'before', 'notify', false, routeNoHookItems) + expect(targets).to.be.an.instanceof(Error) + } + done() + }) +}) diff --git a/test/getHeaders.js b/test/getHeaders.js new file mode 100644 index 0000000..eba52f2 --- /dev/null +++ b/test/getHeaders.js @@ -0,0 +1,107 @@ +const expect = require("chai").expect; + +const getHeaders = require('../includes/getHeaders.js') +const findAllTargets = require('../includes/findAllTargets.js') +let targetRequests = require('./targetRequests.js') +let routeItems = require('./routeItems.js') +const signature = require('../includes/signature.js'); + +/* debug code +for (let targetRequest of targetRequests) { + targetRequest.requestDetails._buffer = JSON.stringify(targetRequest.jsonData) + let result = findAllTargets(targetRequest, false, routeItems) + for(let routeItem of result) { + console.log('match') + console.log('targetRequest', targetRequest) + console.log('routeItem', routeItem) + if(routeItem.hook){ + console.log('getHeaders', getHeaders(targetRequest, routeItem, + routeItem.hook.phase, routeItem.hook.type, routeItem.hook.group, true )) + } else { + console.log('getHeaders', getHeaders(targetRequest, routeItem )) + } + console.log("\n") + } +} +*/ + +for (let targetRequest of targetRequests) { + targetRequest.requestDetails._buffer = JSON.stringify(targetRequest.jsonData) + let result = findAllTargets(targetRequest, false, routeItems) + describe('getHeaders ' + targetRequest.route, function(){ + for (let i in result) { + let routeItem = result[i] + if (Object.keys(routeItem.matchVariables).length) { + + let headers = getHeaders(targetRequest, routeItem ) + if (routeItem.matchVariables) { + for (let key in routeItem.matchVariables) { + it(routeItem.type + ' [' + i + '] checking mfw-' + key + + ': ' + routeItem.matchVariables[key], function(done) { + expect(headers['mfw-' + key]).to.equal(routeItem.matchVariables[key]) + done() + }) + } + } + } + if (routeItem.hook){ + + + let headers = getHeaders(targetRequest, routeItem, routeItem.hook.phase, + routeItem.hook.type, routeItem.hook.group, true ) + + it(routeItem.type + ' [' + i + '] checking x-origin-url', function(done) { + expect(headers['x-origin-url']).to.equal(targetRequest.route) + done() + }) + + it(routeItem.type + ' [' + i + '] checking x-origin-method', function(done) { + expect(headers['x-origin-method']).to.equal(targetRequest.method) + done() + }) + + it(routeItem.type + ' [' + i + '] checking x-endpoint-scope', function(done) { + expect(headers['x-endpoint-scope']).to.equal(targetRequest.endpoint.scope) + done() + }) + + it(routeItem.type + ' [' + i + '] checking x-hook-phase', function(done) { + expect(headers['x-hook-phase']).to.equal(routeItem.hook.phase) + done() + }) + + it(routeItem.type + ' [' + i + '] checking x-hook-type', function(done) { + expect(headers['x-hook-type']).to.equal(routeItem.hook.type) + done() + }) + it(routeItem.type + ' [' + i + '] checking x-hook-group', function(done) { + expect(headers['x-hook-group']).to.equal(routeItem.hook.group) + done() + }) + + it(routeItem.type + ' [' + i + '] checking x-hook-signature', function(done) { + let hash = 'sha256=' + + signature('sha256', targetRequest.requestDetails._buffer, routeItem.secureKey) + expect(headers['x-hook-signature']).to.equal(hash) + done() + }) + } else { + if (Object.keys(targetRequest.requestDetails.headers).length) { + let headers = getHeaders(targetRequest, routeItem ) + for (let key in targetRequest.requestDetails.headers) { + it(routeItem.type + ' [' + i + '] check header ' + key, function(done) { + expect(headers[key]).to.equal(targetRequest.requestDetails.headers[key]) + done() + }) + } + } else { + it(routeItem.type + ' [' + i + '] no headers', function(done) { + done() + }) + } + } + } + }) +} + + diff --git a/test/getProperty.js b/test/getProperty.js new file mode 100644 index 0000000..5e58e46 --- /dev/null +++ b/test/getProperty.js @@ -0,0 +1,28 @@ +const expect = require("chai").expect; + +const getProperty = require('../includes/getProperty.js') + +let data = { + test: "testresult", + sub: { + test2: "test2result", + sub: { + test3: "test3result" + } + } +} + +describe('getProperty', function(){ + it('getProperty("test", data)', function(done){ + expect(getProperty('test', data)).to.equal('testresult') + done(); + }); + it('getProperty("sub.test2", data)', function(done){ + expect(getProperty('sub.test2', data)).to.equal('test2result') + done(); + }); + it('getProperty("sub.sub.test3", data)', function(done){ + expect(getProperty('sub.sub.test3', data)).to.equal('test3result') + done(); + }); +}) diff --git a/test/matchRoute.js b/test/matchRoute.js new file mode 100644 index 0000000..dc68385 --- /dev/null +++ b/test/matchRoute.js @@ -0,0 +1,271 @@ +const expect = require("chai").expect; +const sift = require('sift').default + +const matchRoute = require('../includes/matchRoute.js') +let targetRequests = require('./targetRequests.js') +let routeItems = require('./routeItems.js') + +/* +Future debug code +for(let targetRequest of targetRequests) { + for(let routeItem of routeItems) { + if(matchRoute(targetRequest, routeItem)) { + console.log('match') + console.log('targetRequest', targetRequest) + console.log('routeItem', routeItem) + console.log("\n") + } + } +}*/ +describe('matchRoute', function(){ + it('checking post register', function(done){ + let subTargetRequest = sift({ + route: 'register', + method: 'POST' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + for (let routeItem of routeItems) { + if (matchRoute(targetRequest, routeItem)) { + if (routeItem.path[0] != '*') { + expect(routeItem.path).to.be.an('array').that.include('register'); + } + } else { + if (routeItem.type !== 'hook') { + expect(routeItem.path).to.be.an('array').that.not.include('register'); + } + } + } + } + done() + }) + it('checking get register', function(done){ + let subTargetRequest = sift({ + route: 'register', + method: 'GET' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + for (let routeItem of routeItems) { + if (matchRoute(targetRequest, routeItem)) { + if (routeItem.path[0] != '*') { + expect(routeItem.path).to.be.an('array').that.include('register'); + } + } else { + if (routeItem.type !== 'hook') { + expect(routeItem.path).to.be.an('array').that.not.include('register'); + } + } + } + } + done() + }) + it('checking post repos/test', function(done){ + let subTargetRequest = sift({ + route: 'repos/test', + method: 'POST' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + for (let routeItem of routeItems) { + if (matchRoute(targetRequest, routeItem)) { + if (routeItem.path[0] != '*') { + expect(routeItem.path).to.be.an('array').that.include('repos/:owner'); + expect(routeItem.matchVariables).to.be.an('object').to.have.property('owner', 'test'); + } + delete routeItem.matchVariables + } else { + if (routeItem.type !== 'hook') { + expect(routeItem.path).to.be.an('array').that.not.include('repos/:owner'); + } + } + } + } + done() + }) + it('checking get repos/test/reponame', function(done){ + let subTargetRequest = sift({ + route: 'repos/test', + method: 'GET' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + for (let routeItem of routeItems) { + if (matchRoute(targetRequest, routeItem)) { + if (routeItem.path[0] != '*') { + expect(routeItem.path).to.be.an('array').that.include('repos/:owner'); + expect(routeItem.matchVariables).to.be.an('object').to.have.property('owner', 'test'); + delete routeItem.matchVariables + } + } else { + if (routeItem.type != "hook") { + expect(routeItem.path).to.be.an('array').that.not.include('repos/:owner'); + } + } + } + } + done() + }) + it('checking search repos', function(done){ + let subTargetRequest = sift({ + route: 'repos', + method: 'SEARCH' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + for (let routeItem of routeItems) { + if (matchRoute(targetRequest, routeItem)) { + if (routeItem.path[0] != '*') { + expect(routeItem.path).to.be.an('array').that.include('repos'); + expect(routeItem).to.not.have.property('matchVariables'); + } + } else { + if (routeItem.type !== 'hook') { + expect(routeItem.path).to.be.an('array').that.not.include('repos'); + } + } + } + } + done() + }) + it('checking get repos/reponame', function(done){ + let subTargetRequest = sift({ + route: 'repos', + path: 'reponame', + method: 'GET' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + for (let routeItem of routeItems) { + if (matchRoute(targetRequest, routeItem)) { + if (routeItem.path[0] != '*') { + expect(routeItem.path).to.be.an('array').that.include('repos'); + expect(routeItem).to.not.have.property('matchVariables'); + } + } else { + if (routeItem.type !== 'hook') { + expect(routeItem.path).to.be.an('array').that.not.include('repos'); + } + } + } + } + done() + }) + it('checking post repos/ownername/reponame/files', function(done){ + let subTargetRequest = sift({ + route: 'repos/ownername/reponame/files', + method: 'POST' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + for (let routeItem of routeItems) { + if (matchRoute(targetRequest, routeItem)) { + if (routeItem.path[0] != '*') { + expect(routeItem.path).to.be.an('array').that.include('repos/:owner/:repo/files'); + expect(routeItem.matchVariables).to.be.an('object') + .to.have.property('owner', 'ownername'); + expect(routeItem.matchVariables).to.be.an('object') + .to.have.property('repo', 'reponame'); + delete routeItem.matchVariables + } + } else { + if (routeItem.type !== 'hook') { + expect(routeItem.path).to.be.an('array').that.not.include('repos/:owner/:repo/files'); + } + } + } + } + done() + }) + it('checking get repos/ownername/reponame/files/fileid', function(done){ + let subTargetRequest = sift({ + route: 'repos/ownername/reponame/files', + path: 'fileid', + method: 'GET' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + for (let routeItem of routeItems) { + if (matchRoute(targetRequest, routeItem)) { + if (routeItem.path[0] != '*') { + expect(routeItem.path).to.be.an('array').that.include('repos/:owner/:repo/files'); + expect(routeItem.matchVariables).to.be.an('object') + .to.have.property('owner', 'ownername'); + expect(routeItem.matchVariables).to.be.an('object') + .to.have.property('repo', 'reponame'); + delete routeItem.matchVariables + } + } else { + if (routeItem.type !== 'hook') { + expect(routeItem.path).to.be.an('array').that.not.include('repos/:owner/:repo/files'); + } + } + } + } + done() + }) + it('checking search repos/ownername/files', function(done){ + let subTargetRequest = sift({ + route: 'repos/ownername/files', + method: 'SEARCH' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + for (let routeItem of routeItems) { + if (matchRoute(targetRequest, routeItem)) { + if (routeItem.path[0] != '*') { + expect(routeItem.path).to.be.an('array').that.include('repos/:owner/files'); + expect(routeItem.matchVariables).to.be.an('object') + .to.have.property('owner', 'ownername'); + delete routeItem.matchVariables + } + } else { + if (routeItem.type !== 'hook') { + expect(routeItem.path).to.be.an('array').that.not.include('repos/:owner/files'); + } + } + } + } + done() + }) + it('checking get repos/ownername/files/fileid', function(done){ + let subTargetRequest = sift({ + route: 'repos/ownername/files', + path: 'fileid', + method: 'GET' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + for (let routeItem of routeItems) { + if (matchRoute(targetRequest, routeItem)) { + if (routeItem.path[0] != '*') { + expect(routeItem.path).to.be.an('array').that.include('repos/:owner/files'); + expect(routeItem.matchVariables).to.be.an('object') + .to.have.property('owner', 'ownername'); + delete routeItem.matchVariables + } + } else { + if (routeItem.type !== 'hook') { + expect(routeItem.path).to.be.an('array').that.not.include('repos/:owner/files'); + } + } + } + } + done() + }) + it('checking without conditions', function(done){ + let subTargetRequest = sift({ + route: 'repos/ownername/files2', + path: 'fileid', + method: 'GET' + }, targetRequests) + for (let targetRequest of subTargetRequest) { + for (let routeItem of routeItems) { + if (matchRoute(targetRequest, routeItem)) { + //console.log(targetRequest, routeItem) + if (routeItem.path[0] != '*') { + expect(routeItem.path).to.be.an('array').that.include('repos/:owner/files2'); + expect(routeItem.matchVariables).to.be.an('object') + .to.have.property('owner', 'ownername'); + delete routeItem.matchVariables + } + } else { + if (routeItem.type !== 'hook') { + expect(routeItem.path).to.be.an('array').that.not.include('repos/:owner/files2'); + } + } + } + } + done() + }) +}) diff --git a/test/request.js b/test/request.js new file mode 100644 index 0000000..9243706 --- /dev/null +++ b/test/request.js @@ -0,0 +1,293 @@ +const expect = require("chai").expect; +const sift = require('sift').default +var http = require('http'); + +const sendRequest = require('../includes/request.js') +let targetRequests = JSON.parse(JSON.stringify(require('./targetRequests.js'))) +let routeItems = JSON.parse(JSON.stringify(require('./routeItems.js'))) + + +describe('sendEndpoint request', function(){ + before(function(){ + + this.routeItems = sift({ + type: "handler", + endpointUrl: { + $in: ["http://127.0.0.1:8808/"] + } + }, routeItems) + + for (let targetRequest of this.routeItems) { + if (targetRequest.endpointUrl == "http://127.0.0.1:8808/") { + targetRequest.endpointUrl = "http://127.0.0.1:5808/" + } + } + + this.receivedData = false + this.httpEndPointServer = http.createServer().listen(5808); + this.httpEndPointServer.on('request', (request, response) => { + // the same kind of magic happens here! + let body = []; + request.on('error', (err) => { + console.error(err); + }).on('data', (chunk) => { + body.push(chunk); + }).on('end', () => { + body = Buffer.concat(body).toString(); + body = JSON.parse(body) + let code = 200 + if (body.test == "503") { + code = 503 + } + + let answer = false + let headers = { + 'Content-Type': 'application/json', + } + switch (body.test) { + case 'some': { + answer = JSON.stringify({ + some: 'some' + }) + break + } + case 'url' : { + answer = JSON.stringify({ + url: 'http://ya.ru/test/2', + id: 2 + }) + break + } + case 'urls' : { + answer = JSON.stringify({ + url: 'https://ya.ru/test/2', + id: 2 + }) + break + } + case 'urlshort' : { + answer = JSON.stringify({ + url: 'test/2', + id: 2 + }) + break + } + case 'id' : { + answer = JSON.stringify({ + id: 2 + }) + break + } + case 'array' : { + answer = JSON.stringify([{ + url: 'test/1', + id: 1 + }, { + url: 'test/2', + id: 2 + }, { + url: 'test/3', + id: 3 + }, { + id: 4 + }, { + url: 'http://ya.ru' + }, { + url: 'https://ya.ru' + }, { + some: 'unknown' + }]) + break + } + case 'string': { + answer = "string" + headers = {'content-type': 'text/plain'} + break + } + case 'no-content-type': { + answer = JSON.stringify({ + id: 2 + }) + headers = {} + } + default: { + answer = JSON.stringify({ + headers: request.headers, + body: body + }) + } + } + response.writeHead(code, headers); + response.write(answer) + response.end(); + }); + }); + }) + after(function(){ + this.httpEndPointServer.close() + }) + afterEach(function(){ + this.receivedData = false + }) + it('Endpoint response 200 ', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + + sendRequest(targetRequest, this.routeItems, function(err, response) { + expect(response.code).to.equal(200) + expect(response.answer.headers.test).to.equal("test") + expect(response.answer.body.test).to.equal("test") + done() + }) + + }) + it('Endpoint response no id or url', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "some"}' + + sendRequest(targetRequest, this.routeItems, function(err, response) { + expect(response.code).to.equal(200) + expect(response.answer).to.have.any.keys('url', 'id', 'some') + done() + }) + + }) + it('Endpoint response with id ', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "id"}' + + sendRequest(targetRequest, this.routeItems, function(err, response) { + expect(response.code).to.equal(200) + expect(response.answer).to.have.any.keys('url', 'id', 'some') + done() + }) + + }) + it('Endpoint response with url ', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "url"}' + + sendRequest(targetRequest, this.routeItems, function(err, response) { + expect(response.code).to.equal(200) + expect(response.answer.url).to.equal('http://ya.ru/test/2') + expect(response.answer).to.have.any.keys('url', 'id', 'some') + done() + }) + + }) + it('Endpoint response with https url ', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "urls"}' + + sendRequest(targetRequest, this.routeItems, function(err, response) { + expect(response.code).to.equal(200) + expect(response.answer.url).to.equal('https://ya.ru/test/2') + expect(response.answer).to.have.any.keys('url', 'id', 'some') + done() + }) + + }) + it('Endpoint response with string ', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "string"}' + + sendRequest(targetRequest, this.routeItems, function(err, response) { + expect(response.code).to.equal(200) + expect(response.answer).to.equal('string') + expect(response.headers['content-type']).to.equal('text/plain') + done() + }) + + }) + it('Endpoint response without content-type ', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "no-content-type"}' + + sendRequest(targetRequest, this.routeItems, function(err, response) { + expect(response.code).to.equal(200) + expect(response.headers['content-type']).to.not.exist + done() + }) + + }) + it('Endpoint response on OPTIONS', function(done){ + let targetRequest = JSON.parse(JSON.stringify(targetRequests[0])); + targetRequest.requestDetails._buffer = '{"test": "no-content-type"}' + targetRequest.method = 'OPTIONS' + + sendRequest(targetRequest, this.routeItems, function(err, response) { + //console.log(err, response) + expect(response.code).to.equal(200) + expect(response.headers['content-type']).to.not.exist + done() + }) + + }) + it('Endpoint response with short url ', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "urlshort"}' + + sendRequest(targetRequest, this.routeItems, function(err, response) { + expect(response.code).to.equal(200) + expect(response.answer).to.have.any.keys('url', 'id', 'some') + done() + }) + + }) + it('Endpoint response 200 array', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "array"}' + + sendRequest(targetRequest, this.routeItems, function(err, response) { + expect(response.code).to.equal(200) + expect(response.answer).to.be.an('array') + for (let i in response.answer) { + expect(response.answer[i]).to.have.any.keys('url', 'id', 'some') + } + + done() + }) + + }) + it('Endpoint response 503', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "503"}' + + sendRequest(targetRequest, this.routeItems, function(err, response) { + expect(response.code).to.equal(503) + expect(response.answer.headers.test).to.equal("test") + expect(response.answer.body.test).to.equal("503") + done() + }) + + }) + it('No endpoint found', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + let routeNoRegisterItems = sift({ + path: { $ne: 'register'}, + }, this.routeItems) + let self = this + sendRequest(targetRequest, routeNoRegisterItems, function(err, response) { + expect(err).to.be.instanceof(Error) + done() + + }) + + }) + it('No endpoint available', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + this.httpEndPointServer.close() + let self = this + sendRequest(targetRequest, this.routeItems, function(err, response) { + expect(err).to.be.instanceof(Error) + done() + /*setTimeout(function(){ + expect(self.receivedData).to.equal(false) + done() + }, 100)*/ + }) + + }) +}) diff --git a/test/routeItems.js b/test/routeItems.js new file mode 100644 index 0000000..d0a2adc --- /dev/null +++ b/test/routeItems.js @@ -0,0 +1,1179 @@ +module.exports = [{ + "path": [ + "register" + ], + "endpointUrl": "http://127.0.0.1:8808/", + "secureKey": "xxxx", + "online": true, + "scope": "admin", + "metrics": [ + { + "cpu": "0.27", + "memory": 57.4375, + "loadavg": [ + 1.47900390625, + 1.99755859375, + 2.3427734375 + ] + }, + { + "cpu": "0.23", + "memory": 667.46875, + "loadavg": [ + 1.47900390625, + 1.99755859375, + 2.3427734375 + ] + } + ], + "type": "handler", + "created": 1551039431949, + "changed": 1553011381956, + "token": "xxx", + "id": "5c72fbc7cb542e60b8da21e4" +}, +{ + "path": [ + "repos/:owner", + "repos" + ], + "endpointUrl": "http://127.0.0.1:8807/", + "secureKey": "xxx", + "provides": { + ":repo": { + "field": "repository", + "type": "string" + } + }, + "metrics": [ + { + "cpu": "0.00", + "memory": 24.75390625, + "loadavg": [ + 1.39208984375, + 2.05908203125, + 2.46337890625 + ] + }, + { + "cpu": "0.00", + "memory": 27.3984375, + "loadavg": [ + 1.39208984375, + 2.05908203125, + 2.46337890625 + ] + } + ], + "scope": "repos", + "type": "handler", + "online": true, + "created": 1552536822294, + "changed": 1553011395202, + "token": "xxx", + "id": "5c89d4f6cb542e60b8da2354" +}, +{ + "path": [ + "repos/:owner/:repo/files", + "repos/:owner/files" + ], + "endpointUrl": "https://api.zen.ci/http://127.0.0.1:10047/", + "secureKey": "xxx", + "metrics": [ + { + "cpu": "0.00", + "memory": 23.453125, + "loadavg": [ + 1.39208984375, + 2.05908203125, + 2.46337890625 + ] + }, + { + "cpu": "0.00", + "memory": 23.59765625, + "loadavg": [ + 1.39208984375, + 2.05908203125, + 2.46337890625 + ] + } + ], + "scope": "files", + "type": "handler", + "online": true, + "created": 1552536823177, + "changed": 1553011394363, + "token": "xxx", + "id": "5c89d4f7cf087c449c9ddc39" +}, +{ + "path": [ + "repos/:owner/:repo/files2", + "repos/:owner/files2" + ], + "endpointUrl": "https://api.zen.ci/http://127.0.0.1:10047/", + "secureKey": "xxx", + "metrics": [ + { + "cpu": "0.00", + "memory": 23.453125, + "loadavg": [ + 1.39208984375, + 2.05908203125, + 2.46337890625 + ] + }, + { + "cpu": "0.00", + "memory": 23.59765625, + "loadavg": [ + 1.39208984375, + 2.05908203125, + 2.46337890625 + ] + } + ], + "scope": "files", + "type": "handler", + "online": true, + "created": 1552536823177, + "changed": 1553011394363, + "token": "xxx", + "id": "5c89d4f7cf087c449c9ddc39" +}, +{ + "path": [ + "register" + ], + "endpointUrl": "http://127.0.0.1:8808/", + "secureKey": "xxxx", + "online": true, + "scope": "admin", + "metrics": [ + { + "cpu": "0.27", + "memory": 57.4375, + "loadavg": [ + 1.47900390625, + 1.99755859375, + 2.3427734375 + ] + }, + { + "cpu": "0.23", + "memory": 667.46875, + "loadavg": [ + 1.47900390625, + 1.99755859375, + 2.3427734375 + ] + } + ], + "type": "handler", + "created": 1551039431949, + "changed": 1553011381956, + "token": "xxx", + "id": "5c72fbc7cb542e60b8da21e4" +}, +{ + "path": [ + "repos/:owner", + "repos" + ], + "endpointUrl": "http://127.0.0.1:10046/", + "secureKey": "xxx", + "provides": { + ":repo": { + "field": "repository", + "type": "string" + } + }, + "metrics": [ + { + "cpu": "0.00", + "memory": 24.75390625, + "loadavg": [ + 1.39208984375, + 2.05908203125, + 2.46337890625 + ] + }, + { + "cpu": "0.00", + "memory": 27.3984375, + "loadavg": [ + 1.39208984375, + 2.05908203125, + 2.46337890625 + ] + } + ], + "scope": "repos", + "type": "handler", + "online": true, + "created": 1552536822294, + "changed": 1553011395202, + "token": "xxx", + "id": "5c89d4f6cb542e60b8da2354" +}, +{ + "path": [ + "repos/:owner/:repo/files", + "repos/:owner/files" + ], + "endpointUrl": "https://api.zen.ci/http://127.0.0.1:10047/", + "secureKey": "xxx", + "metrics": [ + { + "cpu": "0.00", + "memory": 23.453125, + "loadavg": [ + 1.39208984375, + 2.05908203125, + 2.46337890625 + ] + }, + { + "cpu": "0.00", + "memory": 23.59765625, + "loadavg": [ + 1.39208984375, + 2.05908203125, + 2.46337890625 + ] + } + ], + "scope": "files", + "type": "handler", + "online": true, + "created": 1552536823177, + "changed": 1553011394363, + "token": "xxx", + "id": "5c89d4f7cf087c449c9ddc39" +}, +{ + "path": [ + "repos/:owner/:repo/files", + "repos/:owner/files" + ], + "endpointUrl": "https://api.zen.ci/http://127.0.0.1:10047/", + "secureKey": "xxx", + "metrics": [ + { + "cpu": "0.00", + "memory": 23.453125, + "loadavg": [ + 1.39208984375, + 2.05908203125, + 2.46337890625 + ] + }, + { + "cpu": "0.00", + "memory": 23.59765625, + "loadavg": [ + 1.39208984375, + 2.05908203125, + 2.46337890625 + ] + } + ], + "scope": "files", + "created": 1552536823177, + "changed": 1553011394363, + "token": "xxx", + "id": "5c89d4f7cf087c449c9ddc39" +}, +{ + "path": [ + "repos/:owner/:repo/files", + "repos/:owner/files" + ], + "endpointUrl": "https://api.zen.ci/http://127.0.0.1:10047/", + "secureKey": "xxx", + "metrics": [ + { + "cpu": "0.00", + "memory": 23.453125, + "loadavg": [ + 1.39208984375, + 2.05908203125, + 2.46337890625 + ] + }, + { + "cpu": "0.00", + "memory": 23.59765625, + "loadavg": [ + 1.39208984375, + 2.05908203125, + 2.46337890625 + ] + } + ], + "online": false, + "scope": "files", + "created": 1552536823177, + "changed": 1553011394363, + "token": "xxx", + "id": "5c89d4f7cf087c449c9ddc39" +}, +{ + "_id": "5c72edae9196913851b214f3", + "type": "hook", + "hook":{ + "phase": "after", + "type": "notify", + "group": "repo-perm" + }, + "group": "repo-perm", + "conditions": { + "methods": [ + "POST" + ] + }, + "path": [ + "repos/:owner" + ], + "endpointUrl": "http://127.0.0.1:10083/", + "secureKey": "xxxx", + "online": true, + "metrics": [ + { + "cpu": "0.00", + "memory": 31.2734375, + "loadavg": [ + 13.87158203125, + 12.900390625, + 8.81787109375 + ] + }, + { + "cpu": "0.00", + "memory": 31.51171875, + "loadavg": [ + 13.87158203125, + 12.900390625, + 8.81787109375 + ] + } + ], + "created": 1551035822972, + "changed": 1551036325110, + "token": "xxxx" +}, +{ + "_id": "5c72edb07ca0193860fdef7b", + "type": "hook", + "hook": + { + "phase": "after", + "type": "notify", + "group": "repo-perm" + }, + "conditions": { + "methods": [ + "POST" + ] + }, + "group": "repo-perm", + "path": [ + "repos/:owner" + ], + "endpointUrl": "http://127.0.0.1:10095/", + "secureKey": "xxxx", + "online": true, + "metrics": [ + { + "cpu": "0.00", + "memory": 30.00390625, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + }, + { + "cpu": "0.00", + "memory": 27.59375, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + } + ], + "created": 1551035824669, + "changed": 1551036353626, + "token": "xxx", +}, +{ + "_id": "5c72edb19196913851b214f9", + "type": "hook", + "hook": + { + "phase": "after", + "type": "notify", + "group": "repo-email" + }, + "group": "repo-email", + "conditions": { + "methods": [ + "POST" + ] + }, + "path": [ + "repos/:owner" + ], + "endpointUrl": "http://127.0.0.1:10096/", + "secureKey": "xxx", + "online": true, + "metrics": [ + { + "cpu": "0.00", + "memory": 22.90234375, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + }, + { + "cpu": "0.00", + "memory": 23.22265625, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + } + ], + "created": 1551035825950, + "changed": 1551036353688, + "token": "xxx", +}, +{ + "_id": "5c72edb49196913851b214fa", + "type": "hook", + "hook":{ + "phase": "after", + "type": "notify", + "group": "repo-email" + }, + "conditions": { + "methods": [ + "POST" + ] + }, + "group": "repo-email", + "path": [ + "repos/:owner" + ], + "endpointUrl": "http://127.0.0.1:10084/", + "secureKey": "xxx", + "online": true, + "metrics": [ + { + "cpu": "0.00", + "memory": 30.41796875, + "loadavg": [ + 13.4814453125, + 12.83544921875, + 8.81884765625 + ] + }, + { + "cpu": "0.00", + "memory": 29.5859375, + "loadavg": [ + 13.4814453125, + 12.83544921875, + 8.81884765625 + ] + } + ], + "created": 1551035828020, + "changed": 1551036353638, + "token": "xxx", +}, +{ + "_id": "5c72edb49196913851b214fa", + "type": "hook", + "conditions": { + "methods": [ + "POST" + ] + }, + "group": "missing-hook-settings", + "path": [ + "repos/:owner" + ], + "endpointUrl": "http://127.0.0.1:10084/", + "secureKey": "xxx", + "online": true, + "metrics": [ + { + "cpu": "0.00", + "memory": 30.41796875, + "loadavg": [ + 13.4814453125, + 12.83544921875, + 8.81884765625 + ] + }, + { + "cpu": "0.00", + "memory": 29.5859375, + "loadavg": [ + 13.4814453125, + 12.83544921875, + 8.81884765625 + ] + } + ], + "created": 1551035828020, + "changed": 1551036353638, + "token": "xxx", +}, +{ + "_id": "5c72edb49196913851b214fa", + "type": "hook", + "conditions": { + "methods": [ + "POST" + ] + }, + "hook":{ + "phase": "after", + "type": "notify" + }, + "path": [ + "repos/:owner" + ], + "endpointUrl": "http://127.0.0.1:10084/", + "secureKey": "xxx", + "online": true, + "metrics": [ + { + "cpu": "0.00", + "memory": 30.41796875, + "loadavg": [ + 13.4814453125, + 12.83544921875, + 8.81884765625 + ] + }, + { + "cpu": "0.00", + "memory": 29.5859375, + "loadavg": [ + 13.4814453125, + 12.83544921875, + 8.81884765625 + ] + } + ], + "created": 1551035828020, + "changed": 1551036353638, + "token": "xxx", +}, +{ + "path": [ + "ws" + ], + "endpointUrl": "http://127.0.0.1:1018/", + "secureKey": "xx11xx", + "scope": "ws", + "type": "websocket", + "methods": { + "post": "data", + "put": "data", + "delete": "data", + "search": "meta", + "get": "meta" + }, + "metrics": [ + { + "cpu": "0.00", + "memory": 70.6953125, + "loadavg": [ + 1.919921875, + 2.08984375, + 2.201171875 + ] + }, + { + "cpu": "0.00", + "memory": 76.671875, + "loadavg": [ + 1.919921875, + 2.08984375, + 2.201171875 + ] + } + ], + "online": true, + "created": 1553000093132, + "changed": 1553049380061, + "token": "xxx", + "id": "5c90e69d12d92e11fd978f03" +}, +{ + "_id": "5c72edb07ca0193860fdef7b", + "type": "hook", + "hook": + { + "phase": "before", + "type": "adapter", + "group": "test1" + }, + "conditions": { + "methods": [ + "POST" + ] + }, + "path": [ + "register" + ], + "endpointUrl": "http://127.0.0.1:8888/", + "secureKey": "xxxx", + "online": true, + "metrics": [ + { + "cpu": "0.00", + "memory": 30.00390625, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + }, + { + "cpu": "0.00", + "memory": 27.59375, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + } + ], + "created": 1551035824669, + "changed": 1551036353626, + "token": "xxx", +}, +{ + "_id": "5c72edb07ca0193860fdef7b", + "type": "hook", + "hook": + { + "phase": "after", + "type": "adapter", + "group": "test1" + }, + "conditions": { + "methods": [ + "POST" + ] + }, + "path": [ + "register" + ], + "endpointUrl": "http://127.0.0.1:9000/", + "secureKey": "xxxx", + "online": true, + "metrics": [ + { + "cpu": "0.00", + "memory": 30.00390625, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + }, + { + "cpu": "0.00", + "memory": 27.59375, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + } + ], + "created": 1551035824669, + "changed": 1551036353626, + "token": "xxx", +}, +{ + "_id": "5c72edb07ca0193860fdef7b", + "type": "hook", + "hook": + { + "phase": "before", + "type": "adapter", + "group": "test2" + }, + "conditions": { + "methods": [ + "POST" + ] + }, + "path": [ + "register" + ], + "endpointUrl": "http://127.0.0.1:1008/", + "secureKey": "xxxx", + "online": true, + "metrics": [ + { + "cpu": "0.00", + "memory": 30.00390625, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + }, + { + "cpu": "0.00", + "memory": 27.59375, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + } + ], + "created": 1551035824669, + "changed": 1551036353626, + "token": "xxx", +}, + +{ + "_id": "5c72edb07ca0193860fdef7b", + "type": "hook", + "hook": + { + "phase": "before", + "type": "adapter", + "group": "test2" + }, + "conditions": { + "methods": [ + "POST" + ] + }, + "path": [ + "register" + ], + "endpointUrl": "http://127.0.0.1:1007/", + "secureKey": "xxxx", + "online": true, + "metrics": [ + { + "cpu": "0.00", + "memory": 30.00390625, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + }, + { + "cpu": "0.00", + "memory": 27.59375, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + } + ], + "created": 1551035824669, + "changed": 1551036353626, + "token": "xxx", +}, +{ + "_id": "5c72edb07ca0193860fdef7b", + "type": "hook", + "hook": + { + "phase": "after", + "type": "broadcast", + "group": "test1" + }, + "conditions": { + "methods": [ + "POST" + ] + }, + "path": [ + "register" + ], + "endpointUrl": "http://127.0.0.1:8889/", + "secureKey": "xxxx", + "online": true, + "metrics": [ + { + "cpu": "0.00", + "memory": 30.00390625, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + }, + { + "cpu": "0.00", + "memory": 27.59375, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + } + ], + "created": 1551035824669, + "changed": 1551036353626, + "token": "xxx", +}, +{ + "_id": "5c72edb07ca0193860fdef7b", + "type": "hook", + "hook": + { + "phase": "after", + "type": "broadcast", + "group": "test1" + }, + "conditions": { + "methods": [ + "POST" + ] + }, + "path": [ + "register" + ], + "endpointUrl": "http://127.0.0.1:8890/", + "secureKey": "xxxx", + "online": true, + "metrics": [ + { + "cpu": "0.00", + "memory": 30.00390625, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + }, + { + "cpu": "0.00", + "memory": 27.59375, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + } + ], + "created": 1551035824669, + "changed": 1551036353626, + "token": "xxx", +}, +{ + "_id": "5c72edb07ca0193860fdef7b", + "type": "hook", + "hook": + { + "phase": "after", + "type": "notify", + "group": "test1" + }, + "conditions": { + "methods": [ + "POST" + ] + }, + "path": [ + "register" + ], + "endpointUrl": "http://127.0.0.1:8892/", + "secureKey": "xxxx", + "online": true, + "metrics": [ + { + "cpu": "0.00", + "memory": 30.00390625, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + }, + { + "cpu": "0.00", + "memory": 27.59375, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + } + ], + "created": 1551035824669, + "changed": 1551036353626, + "token": "xxx", +}, +{ + "_id": "5c72edb07ca0193860fdef7b", + "type": "hook", + "hook": + { + "phase": "after", + "type": "notify", + "group": "test1" + }, + "conditions": { + "methods": [ + "POST" + ] + }, + "path": [ + "register" + ], + "endpointUrl": "http://127.0.0.1:8891/", + "secureKey": "xxxx", + "online": true, + "metrics": [ + { + "cpu": "0.00", + "memory": 30.00390625, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + }, + { + "cpu": "0.00", + "memory": 27.59375, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + } + ], + "created": 1551035824669, + "changed": 1551036353626, + "token": "xxx", +}, +{ + "_id": "5c72edb07ca0193860fdef7b", + "type": "hook", + "hook": + { + "phase": "metric", + "type": "broadcast", + "group": "test1" + }, + "path": [ + "*" + ], + "endpointUrl": "http://127.0.0.1:8893/", + "secureKey": "xxxx", + "online": true, + "metrics": [ + { + "cpu": "0.00", + "memory": 30.00390625, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + }, + { + "cpu": "0.00", + "memory": 27.59375, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + } + ], + "created": 1551035824669, + "changed": 1551036353626, + "token": "xxx", +}, +{ + "_id": "5c72edb07ca0193860fdef7b", + "type": "hook", + "hook": + { + "phase": "metric", + "type": "broadcast", + "group": "test1", + "meta": true + }, + "path": [ + "*" + ], + "endpointUrl": "http://127.0.0.1:8894/", + "secureKey": "xxxx", + "online": true, + "metrics": [ + { + "cpu": "0.00", + "memory": 30.00390625, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + }, + { + "cpu": "0.00", + "memory": 27.59375, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + } + ], + "created": 1551035824669, + "changed": 1551036353626, + "token": "xxx", +}, +{ + "_id": "5c72edb07ca0193860fdef7b", + "type": "hook", + "hook": + { + "phase": "metric", + "type": "notify", + "group": "test1" + }, + "path": [ + "*" + ], + "endpointUrl": "http://127.0.0.1:8895/", + "secureKey": "xxxx", + "online": true, + "metrics": [ + { + "cpu": "0.00", + "memory": 30.00390625, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + }, + { + "cpu": "0.00", + "memory": 27.59375, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + } + ], + "created": 1551035824669, + "changed": 1551036353626, + "token": "xxx", +}, +{ + "_id": "5c72edb07ca0193860fdef7b", + "type": "hook", + "hook": + { + "phase": "metric", + "type": "notify", + "group": "test1", + "meta": true + }, + "path": [ + "*" + ], + "endpointUrl": "http://127.0.0.1:8896/", + "secureKey": "xxxx", + "online": true, + "metrics": [ + { + "cpu": "0.00", + "memory": 30.00390625, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + }, + { + "cpu": "0.00", + "memory": 27.59375, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + } + ], + "created": 1551035824669, + "changed": 1551036353626, + "token": "xxx", +}, +{ + "_id": "5c72edb07ca0193860fdef7b", + "type": "hook", + "hook": + { + "phase": "metric", + "type": "notify", + "group": "test1", + "meta": true + }, + "path": [ + "*" + ], + "endpointUrl": "http://127.0.0.1:8896/", + "secureKey": "xxxx", + "online": true, + "metrics": [ + { + "cpu": "0.00", + "memory": 30.00390625, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + }, + { + "cpu": "0.00", + "memory": 27.59375, + "loadavg": [ + 13.13427734375, + 11.650390625, + 8.79296875 + ] + } + ], + "conditions": { + "methods": [ + "NOTIFY" + ] + }, + "created": 1551035824669, + "changed": 1551036353626, + "token": "xxx", +} +] diff --git a/test/sendBroadcastMessage.js b/test/sendBroadcastMessage.js new file mode 100644 index 0000000..11b844a --- /dev/null +++ b/test/sendBroadcastMessage.js @@ -0,0 +1,92 @@ +const expect = require("chai").expect; +const sift = require('sift').default +const dgram = require('dgram'); +const signature = require('../includes/signature.js'); + +const sendBroadcastMessage = require('../includes/sendBroadcastMessage.js') +let targetRequests = JSON.parse(JSON.stringify(require('./targetRequests.js'))) +let routeItems = JSON.parse(JSON.stringify(require('./routeItems.js'))) + + +describe('sendBroadcastMessage', function(){ + let UDPServer + before(function() { + + this.routeItems = sift({ + endpointUrl: { + $in: ["http://127.0.0.1:1018/"] + } + }, routeItems) + + for (let targetRequest of this.routeItems) { + if (targetRequest.endpointUrl == "http://127.0.0.1:1018/") { + targetRequest.endpointUrl = "http://127.0.0.1:1019/" + } + } + + UDPServer = dgram.createSocket('udp4'); + UDPServer.bind('1019'); + UDPServer.on('error', function(err) { + console.log('UDP Server error %O', err); + }); + + }); + after(function(){ + if (UDPServer) { + UDPServer.close() + } + }) + it('send success udp message', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + + let routeWSItems = sift({ + path: 'ws', + }, routeItems) + + targetRequest.router = routeWSItems[0] + targetRequest.method = "POST" + targetRequest.requestDetails.url = 'TEST' + //console.log('targetRequest', targetRequest ) + + sendBroadcastMessage(targetRequest, JSON.parse(targetRequest.requestDetails._buffer), this.routeItems) + UDPServer.on('message', function(message, rinfo) { + message = JSON.parse(message); + let sign = message.signature; + delete message.signature; + let hash = signature(sign[0], JSON.stringify(message), targetRequest.router.secureKey) + //console.log('RECEIVED', message, rinfo) + expect(message.message.test).to.equal('test') + expect(message.method).to.equal(targetRequest.method) + expect(message.route[0]).to.equal(targetRequest.router.path[0]) + expect(message.scope).to.equal(targetRequest.router.scope) + expect(message.path).to.equal(targetRequest.requestDetails.url) + expect(sign[1]).to.equal(hash) + done() + }) + }) + it('send fail udp message', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + + let routeWSItems = sift({ + path: 'ws', + }, routeItems) + + targetRequest.router = routeWSItems[0] + targetRequest.requestDetails.url = 'TEST' + UDPServer.close() + + sendBroadcastMessage(targetRequest, JSON.parse(targetRequest.requestDetails._buffer), this.routeItems) + let messageReceived = false; + UDPServer.on('message', function(message, rinfo) { + messageReceived = JSON.parse(message); + + }) + setTimeout(function(){ + expect(messageReceived).to.equal(false) + UDPServer = false + done() + }, 100) + }) +}) diff --git a/test/sendHookAdapterAfter.js b/test/sendHookAdapterAfter.js new file mode 100644 index 0000000..a247f14 --- /dev/null +++ b/test/sendHookAdapterAfter.js @@ -0,0 +1,131 @@ +const expect = require("chai").expect; +const sift = require('sift').default +var http = require('http'); + +const sendRequest = require('../includes/request.js') +let targetRequests = JSON.parse(JSON.stringify(require('./targetRequests.js'))) +let routeItems = JSON.parse(JSON.stringify(require('./routeItems.js'))) + + +describe('sendHookAdapter (After)', function(){ + before(function(){ + this.receivedData = false + + this.routeItems = sift({ + endpointUrl: { + $in: ["http://127.0.0.1:9000/", "http://127.0.0.1:8808/"] + } + }, routeItems) + + for (let targetRequest of this.routeItems) { + if (targetRequest.endpointUrl == "http://127.0.0.1:8808/") { + targetRequest.endpointUrl = "http://127.0.0.1:3019/" + } + } + + this.httpAdapterServer = http.createServer().listen(9000); + this.httpAdapterServer.on('request', (request, response) => { + let body = []; + request.on('error', (err) => { + console.error(err); + }).on('data', (chunk) => { + body.push(chunk); + }).on('end', () => { + body = Buffer.concat(body).toString(); + response.writeHead(200, { + 'Content-Type': 'application/json', + 'x-set-x-adapter-after-test': 'adapter-test' + }); + this.receivedData = JSON.parse(body) + body = JSON.parse(body) + body.after = true + response.write(JSON.stringify(body)) + response.end(); + + }); + }); + this.httpEndPointServer = http.createServer().listen(3019); + this.httpEndPointServer.on('request', (request, response) => { + // the same kind of magic happens here! + let body = []; + request.on('error', (err) => { + console.error(err); + }).on('data', (chunk) => { + body.push(chunk); + }).on('end', () => { + body = Buffer.concat(body).toString(); + response.setHeader('Content-Type', 'application/json'); + response.write(JSON.stringify({ + headers: request.headers, + body: JSON.parse(body) + })) + response.end(); + }); + }); + }) + after(function(){ + this.httpAdapterServer.close() + this.httpEndPointServer.close() + }) + afterEach(function(){ + this.receivedData = false + }) + it('Endpoint response', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + sendRequest(targetRequest, this.routeItems, function(err, response) { + expect(response.answer.headers.test).to.equal("test") + expect(response.answer.body.test).to.equal("test") + expect(response.answer.after).to.equal(true) + done() + }) + + }) + it('Adapter transformed request', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + + sendRequest(targetRequest, this.routeItems, function(err, response) { + expect(response.answer.after).to.equal(true) + done() + }) + + }) + it('Adapter set adapter-test header', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + + sendRequest(targetRequest, this.routeItems, function(err, response) { + expect(response.headers['x-adapter-after-test']).to.equal('adapter-test') + done() + }) + + }) + it('Adapter received request', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + let self = this + + sendRequest(targetRequest, this.routeItems, function(err, response) { + expect(self.receivedData.body.test).to.equal("test") + done() + }) + + }) + it('No Adapter received', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + + let routeNoAdapterItems = sift({ + "hook.type": {$ne: "adapter"}, + }, this.routeItems) + let self = this + sendRequest(targetRequest, routeNoAdapterItems, function(err, response) { + setTimeout(function(){ + expect(self.receivedData).to.equal(false) + done() + }, 100) + }) + + }) +}) diff --git a/test/sendHookAdapterAfterFail.js b/test/sendHookAdapterAfterFail.js new file mode 100644 index 0000000..f4a5da9 --- /dev/null +++ b/test/sendHookAdapterAfterFail.js @@ -0,0 +1,152 @@ +const expect = require("chai").expect; +const sift = require('sift').default +var http = require('http'); + +const sendRequest = require('../includes/request.js') +let targetRequests = JSON.parse(JSON.stringify(require('./targetRequests.js'))) +let routeItems = JSON.parse(JSON.stringify(require('./routeItems.js'))) + + +describe('sendHookAdapter (After) with error', function(){ + before(function(){ + this.receivedData = false + + this.routeItems = sift({ + endpointUrl: { + $in: ["http://127.0.0.1:9000/", "http://127.0.0.1:8808/"] + } + }, routeItems) + + for (let targetRequest of this.routeItems) { + if (targetRequest.endpointUrl == "http://127.0.0.1:8808/") { + targetRequest.endpointUrl = "http://127.0.0.1:3018/" + } + if (targetRequest.endpointUrl == "http://127.0.0.1:9000/") { + targetRequest.endpointUrl = "http://127.0.0.1:3015/" + } + } + + this.httpAdapterServer = http.createServer().listen(3015); + this.httpAdapterServer.on('request', (request, response) => { + let body = []; + request.on('error', (err) => { + console.error(err); + }).on('data', (chunk) => { + body.push(chunk); + }).on('end', () => { + body = Buffer.concat(body).toString(); + + this.receivedData = JSON.parse(body) + body = JSON.parse(body) + body.after = true + answer = JSON.stringify(body) + let code = 503 + if (body.body.test == "broken") { + code = 200 + answer = "{ test"; + } + response.writeHead(code, { + 'Content-Type': 'application/json', + 'x-set-x-adapter-after-test': 'adapter-test' + }); + response.write(answer) + response.end(); + + }); + }); + this.httpEndPointServer = http.createServer().listen(3018); + this.httpEndPointServer.on('request', (request, response) => { + // the same kind of magic happens here! + let body = []; + request.on('error', (err) => { + console.error(err); + }).on('data', (chunk) => { + body.push(chunk); + }).on('end', () => { + body = Buffer.concat(body).toString(); + body = JSON.parse(body) + response.setHeader('Content-Type', 'application/json'); + let answer = JSON.stringify({ + headers: request.headers, + body: body + }) + response.write(answer) + response.end(); + }); + }); + }) + after(function(){ + this.httpAdapterServer.close() + this.httpEndPointServer.close() + }) + afterEach(function(){ + this.receivedData = false + }) + it('Endpoint response', function(done){ + let targetRequest = JSON.parse(JSON.stringify(targetRequests[0])); + targetRequest.requestDetails._buffer = '{"test": "test"}' + sendRequest(targetRequest, this.routeItems, function(err, response) { + expect(response.answer.headers.test).to.equal("test") + expect(response.answer.body.test).to.equal("test") + expect(response.answer).to.have.not.property('after') + done() + }) + + }) + it('Adapter do not transformed request', function(done){ + let targetRequest = JSON.parse(JSON.stringify(targetRequests[0])); + targetRequest.requestDetails._buffer = '{"test": "test"}' + + sendRequest(targetRequest, this.routeItems, function(err, response) { + expect(response.answer).to.have.not.property('after') + done() + }) + + }) + it('Adapter set adapter-test header', function(done){ + let targetRequest = JSON.parse(JSON.stringify(targetRequests[0])); + targetRequest.requestDetails._buffer = '{"test": "test"}' + + sendRequest(targetRequest, this.routeItems, function(err, response) { + expect(response.headers['x-adapter-after-test']).to.equal('adapter-test') + done() + }) + + }) + it('Adapter send broken data', function(done){ + let targetRequest = JSON.parse(JSON.stringify(targetRequests[0])); + targetRequest.requestDetails._buffer = '{"test": "broken"}' + sendRequest(targetRequest, this.routeItems, function(err, response) { + expect(response.headers).to.has.property('x-hook-adapter-status-test1-after', 'error: Unexpected token t in JSON at position 2') + done() + }) + + }) + it('Adapter received request', function(done){ + let targetRequest = JSON.parse(JSON.stringify(targetRequests[0])); + targetRequest.requestDetails._buffer = '{"test": "test"}' + let self = this + + sendRequest(targetRequest, this.routeItems, function(err, response) { + expect(self.receivedData.body.test).to.equal("test") + done() + }) + + }) + it('No Adapter received', function(done){ + let targetRequest = JSON.parse(JSON.stringify(targetRequests[0])); + targetRequest.requestDetails._buffer = '{"test": "test"}' + + let routeNoAdapterItems = sift({ + "hook.type": {$ne: "adapter"}, + }, this.routeItems) + let self = this + sendRequest(targetRequest, routeNoAdapterItems, function(err, response) { + setTimeout(function(){ + expect(self.receivedData).to.equal(false) + done() + }, 100) + }) + + }) +}) diff --git a/test/sendHookAdapterBefore.js b/test/sendHookAdapterBefore.js new file mode 100644 index 0000000..c01e474 --- /dev/null +++ b/test/sendHookAdapterBefore.js @@ -0,0 +1,180 @@ +const expect = require("chai").expect; +const sift = require('sift').default +var http = require('http'); + +const sendRequest = require('../includes/request.js') +let targetRequests = require('./targetRequests.js') +const signature = require('../includes/signature.js'); + +let routeALLItems = require('./routeItems.js') + +let routeItems = sift({ + endpointUrl: { + $in: ["http://127.0.0.1:8888/", "http://127.0.0.1:8808/"] + } +}, routeALLItems) + + +describe('sendHookAdapter (Before)', function(){ + before(function(){ + this.receivedData = false + this.httpAdapterServer = http.createServer().listen(8888); + this.httpAdapterServer.on('request', (request, response) => { + let body = []; + request.on('error', (err) => { + console.error(err); + }).on('data', (chunk) => { + body.push(chunk); + }).on('end', () => { + body = Buffer.concat(body).toString(); + response.writeHead(200, { + 'Content-Type': 'application/json', + 'x-set-adapter-test': 'adapter-test' + }); + this.receivedData = JSON.parse(body) + body = JSON.parse(body) + body.extra = true + response.write(JSON.stringify(body)) + response.end(); + + }); + }); + this.httpEndPointServer = http.createServer().listen(8808); + this.httpEndPointServer.on('request', (request, response) => { + // the same kind of magic happens here! + let body = []; + request.on('error', (err) => { + console.error(err); + }).on('data', (chunk) => { + body.push(chunk); + }).on('end', () => { + body = Buffer.concat(body).toString(); + response.setHeader('Content-Type', 'application/json'); + //console.log('headers', request.headers) + //console.log('body', body) + + if (request.headers.signature) { + let routeRegisterItems = sift({ + "path": 'register', + }, routeItems) + let hash = 'sha256=' + + signature('sha256', body, routeRegisterItems[0].secureKey) + if (hash !== request.headers.signature) { + //console.log('sig missmatch', hash, request.headers.signature) + //console.log('endpoint', body, routeRegisterItems[0].secureKey) + return response.end(); + } + } + response.write(JSON.stringify({ + headers: request.headers, + body: JSON.parse(body) + })) + response.end(); + }); + }); + }) + after(function(){ + this.httpAdapterServer.close() + this.httpEndPointServer.close() + }) + afterEach(function(){ + this.receivedData = false + }) + it('Endpoint response', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + + sendRequest(targetRequest, routeItems, function(err, response) { + expect(response.answer.headers.test).to.equal("test") + expect(response.answer.body.test).to.equal("test") + expect(response.answer.body.extra).to.equal(true) + done() + }) + + }) + it('Endpoint request with signature', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + + let routeRegisterItems = sift({ + "path": 'register', + }, routeItems) + let hash = 'sha256=' + + signature('sha256', targetRequest.requestDetails._buffer, routeRegisterItems[0].secureKey) + targetRequest.requestDetails.headers.signature = hash + + sendRequest(targetRequest, routeItems, function(err, response) { + expect(response.answer.headers.test).to.equal("test") + expect(response.answer.body.test).to.equal("test") + expect(response.answer.body.extra).to.equal(true) + done() + }) + + }) + it('Adapter transformed request', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + + sendRequest(targetRequest, routeItems, function(err, response) { + expect(response.answer.body.extra).to.equal(true) + done() + }) + + }) + it('Adapter set adapter-test header', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + + sendRequest(targetRequest, routeItems, function(err, response) { + expect(response.answer.headers['adapter-test']).to.equal('adapter-test') + done() + }) + + }) + it('Adapter received request', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + let self = this + + sendRequest(targetRequest, routeItems, function(err, response) { + expect(self.receivedData.test).to.equal("test") + done() + }) + + }) + it('No Adapter received', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + + let routeNoAdapterItems = sift({ + "hook.type": {$ne: "adapter"}, + }, routeItems) + let self = this + sendRequest(targetRequest, routeNoAdapterItems, function(err, response) { + setTimeout(function(){ + expect(self.receivedData).to.equal(false) + done() + }, 100) + }) + + }) + it('Adapter is down', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + let self = this + + this.httpAdapterServer.close() + let routeRegisterItems = sift({ + "path": 'register', + }, routeItems) + let hash = 'sha256=' + + signature('sha256', targetRequest.requestDetails._buffer, routeRegisterItems[0].secureKey) + targetRequest.requestDetails.headers.signature = hash + + sendRequest(targetRequest, routeItems, function(err, response) { + expect(self.receivedData).to.equal(false) + done() + }) + + }) +}) diff --git a/test/sendHookAdapterBeforeFail.js b/test/sendHookAdapterBeforeFail.js new file mode 100644 index 0000000..456bef0 --- /dev/null +++ b/test/sendHookAdapterBeforeFail.js @@ -0,0 +1,118 @@ +const expect = require("chai").expect; +const sift = require('sift').default +var http = require('http'); + +const sendRequest = require('../includes/request.js') +let targetRequests = require('./targetRequests.js') +let routeItems = require('./routeItems.js') + +describe('sendHookAdapter (Before) with error', function(){ + before(function(){ + this.receivedData = false + this.httpAdapterServer = http.createServer().listen(8888); + this.httpAdapterServer.on('request', (request, response) => { + let body = []; + request.on('error', (err) => { + console.error(err); + }).on('data', (chunk) => { + body.push(chunk); + }).on('end', () => { + body = Buffer.concat(body).toString(); + response.writeHead(503, { + 'Content-Type': 'application/json', + 'x-set-adapter-test': 'adapter-test' + }); + this.receivedData = JSON.parse(body) + body = JSON.parse(body) + body.extra = true + response.write(JSON.stringify(body)) + response.end(); + + }); + }); + this.httpEndPointServer = http.createServer().listen(8808); + this.httpEndPointServer.on('request', (request, response) => { + // the same kind of magic happens here! + let body = []; + request.on('error', (err) => { + console.error(err); + }).on('data', (chunk) => { + body.push(chunk); + }).on('end', () => { + body = Buffer.concat(body).toString(); + response.setHeader('Content-Type', 'application/json'); + response.write(JSON.stringify({ + headers: request.headers, + body: JSON.parse(body) + })) + response.end(); + }); + }); + }) + after(function(){ + this.httpAdapterServer.close() + this.httpEndPointServer.close() + }) + afterEach(function(){ + this.receivedData = false + }) + it('Endpoint response', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + + sendRequest(targetRequest, routeItems, function(err, response) { + expect(response.answer.headers.test).to.equal("test") + expect(response.answer.body.test).to.equal("test") + expect(response.answer.body).to.have.not.property('extra') + done() + }) + + }) + it('Adapter do not transformed request', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + + sendRequest(targetRequest, routeItems, function(err, response) { + expect(response.answer.body).to.have.not.property('extra') + done() + }) + + }) + it('Adapter set adapter-test header', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + + sendRequest(targetRequest, routeItems, function(err, response) { + expect(response.answer.headers['adapter-test']).to.equal('adapter-test') + done() + }) + + }) + it('Adapter received request', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + let self = this + + sendRequest(targetRequest, routeItems, function(err, response) { + expect(self.receivedData.test).to.equal("test") + done() + }) + + }) + it('No Adapter received', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + + let routeNoAdapterItems = sift({ + "hook.type": {$ne: "adapter"}, + }, routeItems) + let self = this + sendRequest(targetRequest, routeNoAdapterItems, function(err, response) { + setTimeout(function(){ + expect(self.receivedData).to.equal(false) + done() + }, 100) + }) + + }) +}) diff --git a/test/sendHookBroadcast.js b/test/sendHookBroadcast.js new file mode 100644 index 0000000..36933ff --- /dev/null +++ b/test/sendHookBroadcast.js @@ -0,0 +1,162 @@ +const expect = require("chai").expect; +const sift = require('sift').default +var http = require('http'); + +const request = require('../includes/request.js') +let targetRequests = JSON.parse(JSON.stringify(require('./targetRequests.js'))) +let routeItems = JSON.parse(JSON.stringify(require('./routeItems.js'))) + + +describe('sendHookBroadcast', function(){ + before(function(){ + + this.routeItems = sift({ + endpointUrl: { + $in: ["http://127.0.0.1:8889/", "http://127.0.0.1:8890/", "http://127.0.0.1:8808/"] + } + }, routeItems) + + + + for (let targetRequest of this.routeItems) { + if (targetRequest.endpointUrl == "http://127.0.0.1:8808/") { + targetRequest.endpointUrl = "http://127.0.0.1:4808/" + } + } + this.receivedBroadcastData1 = false + this.receivedBroadcastData2 = false + this.httpBroadcast1Server = http.createServer().listen(8889); + this.httpBroadcast1Server.on('request', (request, response) => { + let body = []; + request.on('error', (err) => { + console.error(err); + }).on('data', (chunk) => { + body.push(chunk); + }).on('end', () => { + body = Buffer.concat(body).toString(); + response.writeHead(200, { + 'Content-Type': 'application/json', + }); + this.receivedBroadcastData1 = JSON.parse(body) + body = JSON.parse(body) + body.extra = true + response.write(JSON.stringify(body)) + response.end(); + + }); + }); + this.httpBroadcast2Server = http.createServer().listen(8890); + this.httpBroadcast2Server.on('request', (request, response) => { + let body = []; + request.on('error', (err) => { + console.error(err); + }).on('data', (chunk) => { + body.push(chunk); + }).on('end', () => { + body = Buffer.concat(body).toString(); + response.writeHead(200, { + 'Content-Type': 'application/json', + }); + this.receivedBroadcastData2 = JSON.parse(body) + body = JSON.parse(body) + body.extra = true + response.write(JSON.stringify(body)) + response.end(); + + }); + }); + this.httpEndPointServer = http.createServer().listen(4808); + this.httpEndPointServer.on('request', (request, response) => { + // the same kind of magic happens here! + let body = []; + request.on('error', (err) => { + console.error(err); + }).on('data', (chunk) => { + body.push(chunk); + }).on('end', () => { + body = Buffer.concat(body).toString(); + response.setHeader('Content-Type', 'application/json'); + response.write(JSON.stringify({ + headers: request.headers, + body: JSON.parse(body) + })) + response.end(); + }); + }); + }) + afterEach(function(){ + this.receivedBroadcastData1 = false + this.receivedBroadcastData2 = false + }) + after(function(){ + this.httpBroadcast1Server.close() + this.httpBroadcast2Server.close() + this.httpEndPointServer.close() + }) + it('Endpoint response', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + + request(targetRequest, this.routeItems, function(err, response) { + expect(response.answer.headers.test).to.equal("test") + expect(response.answer.body.test).to.equal("test") + setTimeout(function(){ + done() + }, 10) + }) + + }) + it('Broadcast 1 request', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + let self = this + + request(targetRequest, this.routeItems, function(err, response) { + let interval = setInterval(function(){ + if(self.receivedBroadcastData1) { + expect(self.receivedBroadcastData1.body.test).to.equal("test") + expect(self.receivedBroadcastData1.headers.test).to.equal("test") + done() + clearInterval(interval) + } + }, 10) + }) + + }) + it('Broadcast 2 request', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + let self = this + + request(targetRequest, this.routeItems, function(err, response) { + let interval = setInterval(function(){ + if(self.receivedBroadcastData2) { + expect(self.receivedBroadcastData2.body.test).to.equal("test") + expect(self.receivedBroadcastData2.headers.test).to.equal("test") + done() + clearInterval(interval) + } + }, 10) + }) + + }) + it('No Broadcast messages', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + + let routeNoBCItems = sift({ + "hook.type": {$ne: "broadcast"}, + }, this.routeItems) + let self = this + request(targetRequest, routeNoBCItems, function(err, response) { + expect(response.answer.headers.test).to.equal("test") + expect(response.answer.body.test).to.equal("test") + setTimeout(function(){ + expect(self.receivedBroadcastData2).to.equal(false) + expect(self.receivedBroadcastData1).to.equal(false) + done() + }, 50) + }) + + }) +}) diff --git a/test/sendHookNotify.js b/test/sendHookNotify.js new file mode 100644 index 0000000..17223de --- /dev/null +++ b/test/sendHookNotify.js @@ -0,0 +1,115 @@ +const expect = require("chai").expect; +const sift = require('sift').default +var http = require('http'); + +const request = require('../includes/request.js') +let targetRequests = require('./targetRequests.js') +let routeItems = require('./routeItems.js') + +describe('sendHookNotify', function(){ + before(function(){ + this.receivedData1 = false + this.receivedData2 = false + this.httpNotify1Server = http.createServer().listen(8891); + this.httpNotify1Server.on('request', (request, response) => { + let body = []; + request.on('error', (err) => { + console.error(err); + }).on('data', (chunk) => { + body.push(chunk); + }).on('end', () => { + body = Buffer.concat(body).toString(); + response.writeHead(200, { + 'Content-Type': 'application/json', + }); + this.receivedData1 = JSON.parse(body) + body = JSON.parse(body) + body.extra = true + response.write(JSON.stringify(body)) + response.end(); + + }); + }); + this.httpNotify2Server = http.createServer().listen(8892); + this.httpNotify2Server.on('request', (request, response) => { + let body = []; + request.on('error', (err) => { + console.error(err); + }).on('data', (chunk) => { + body.push(chunk); + }).on('end', () => { + body = Buffer.concat(body).toString(); + response.writeHead(200, { + 'Content-Type': 'application/json', + }); + this.receivedData2 = JSON.parse(body) + body = JSON.parse(body) + body.extra = true + response.write(JSON.stringify(body)) + response.end(); + + }); + }); + this.httpEndPointServer = http.createServer().listen(8808); + this.httpEndPointServer.on('request', (request, response) => { + // the same kind of magic happens here! + let body = []; + request.on('error', (err) => { + console.error(err); + }).on('data', (chunk) => { + body.push(chunk); + }).on('end', () => { + body = Buffer.concat(body).toString(); + response.setHeader('Content-Type', 'application/json'); + response.write(JSON.stringify({ + headers: request.headers, + body: JSON.parse(body) + })) + response.end(); + }); + }); + }) + afterEach(function(){ + this.receivedData1 = false + this.receivedData2 = false + }) + after(function(){ + this.httpNotify1Server.close() + this.httpNotify2Server.close() + this.httpEndPointServer.close() + }) + it('Endpoint response', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + + request(targetRequest, routeItems, function(err, response) { + expect(response.answer.headers.test).to.equal("test") + expect(response.answer.body.test).to.equal("test") + done() + }) + + }) + it('Notify 1 request', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + let self = this + + request(targetRequest, routeItems, function(err, response) { + expect(self.receivedData1.body.test).to.equal("test") + expect(self.receivedData1.headers.test).to.equal("test") + done() + }) + + }) + it('Notify 2 request not received', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + let self = this + + request(targetRequest, routeItems, function(err, response) { + expect(self.receivedData2).to.equal(false) + done() + }) + + }) +}) diff --git a/test/sendMetricsBroadcast.js b/test/sendMetricsBroadcast.js new file mode 100644 index 0000000..57978d0 --- /dev/null +++ b/test/sendMetricsBroadcast.js @@ -0,0 +1,132 @@ +const expect = require("chai").expect; +const sift = require('sift').default +var http = require('http'); + +const request = require('../includes/request.js') +let targetRequests = require('./targetRequests.js') +let routeALLItems = require('./routeItems.js') + +let routeItems = sift({ + endpointUrl: { + $in: ["http://127.0.0.1:8893/", "http://127.0.0.1:8894/", "http://127.0.0.1:8807/"] + } +}, routeALLItems) + +describe('sendHookMetricBroadcast', function(){ + before(function(){ + this.receivedData1 = [] + this.receivedData2 = [] + this.httpMetric1Server = http.createServer().listen(8893); + this.httpMetric1Server.on('request', (request, response) => { + let body = []; + request.on('error', (err) => { + console.error(err); + }).on('data', (chunk) => { + body.push(chunk); + }).on('end', () => { + body = Buffer.concat(body).toString(); + //console.log('httpMetric1Server', body) + response.writeHead(200, { + 'Content-Type': 'application/json', + }); + this.receivedData1.push(JSON.parse(body)) + body = JSON.parse(body) + body.extra = true + response.write(JSON.stringify(body)) + response.end(); + + }); + }); + this.httpMetric2Server = http.createServer().listen(8894); + this.httpMetric2Server.on('request', (request, response) => { + let body = []; + request.on('error', (err) => { + console.error(err); + }).on('data', (chunk) => { + body.push(chunk); + }).on('end', () => { + body = Buffer.concat(body).toString(); + //console.log('httpMetric2Server', body) + response.writeHead(200, { + 'Content-Type': 'application/json', + }); + this.receivedData2.push(JSON.parse(body)) + body = JSON.parse(body) + body.extra = true + response.write(JSON.stringify(body)) + response.end(); + + }); + }); + this.httpEndPointServer = http.createServer().listen(8807); + this.httpEndPointServer.on('request', (request, response) => { + // the same kind of magic happens here! + let body = []; + request.on('error', (err) => { + console.error(err); + }).on('data', (chunk) => { + body.push(chunk); + }).on('end', () => { + body = Buffer.concat(body).toString(); + response.setHeader('Content-Type', 'application/json'); + response.write(JSON.stringify({ + headers: request.headers, + body: JSON.parse(body) + })) + response.end(); + }); + }); + }) + afterEach(function(){ + this.receivedData1 = [] + this.receivedData2 = [] + }) + after(function(){ + this.httpMetric1Server.close() + this.httpMetric2Server.close() + this.httpEndPointServer.close() + }) + it('Endpoint response', function(done){ + let targetRequest = targetRequests[4]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + + request(targetRequest, routeItems, function(err, response) { + expect(response.answer.headers.test).to.equal("test") + expect(response.answer.body.test).to.equal("test") + setTimeout(function(){ + done() + }, 10) + }) + + }) + it('Metric 1 request', function(done){ + let targetRequest = targetRequests[4]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + let self = this + + request(targetRequest, routeItems, function(err, response) { + let interval = setInterval(function(){ + if(self.receivedData1.length) { + expect(self.receivedData1.length).to.equal(1) + done() + clearInterval(interval) + } + }, 10) + }) + + }) + it('Metric 2 request', function(done){ + let targetRequest = targetRequests[4]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + let self = this + request(targetRequest, routeItems, function(err, response) { + let interval = setInterval(function(){ + if(self.receivedData2.length) { + expect(self.receivedData2.length).to.equal(1) + done() + clearInterval(interval) + } + }, 10) + }) + }) +}) diff --git a/test/sendMetricsNotify.js b/test/sendMetricsNotify.js new file mode 100644 index 0000000..e151ab8 --- /dev/null +++ b/test/sendMetricsNotify.js @@ -0,0 +1,134 @@ +const expect = require("chai").expect; +const sift = require('sift').default +var http = require('http'); + +const request = require('../includes/request.js') +let targetRequests = require('./targetRequests.js') +let routeALLItems = require('./routeItems.js') + +let routeItems = sift({ + endpointUrl: { + $in: ["http://127.0.0.1:8895/", "http://127.0.0.1:8896/", "http://127.0.0.1:8808/"] + } +}, routeALLItems) + +let MAGIC_NUMBER = 1 //Amount of notifications that need to be send based on current routeItems file records + +describe('sendHookMetricNotify', function(){ + before(function(){ + this.receivedData1 = [] + this.receivedData2 = [] + this.httpMetric1Server = http.createServer().listen(8895); + this.httpMetric1Server.on('request', (request, response) => { + let body = []; + request.on('error', (err) => { + console.error(err); + }).on('data', (chunk) => { + body.push(chunk); + }).on('end', () => { + body = Buffer.concat(body).toString(); + response.writeHead(200, { + 'Content-Type': 'application/json', + }); + this.receivedData1.push(JSON.parse(body)) + body = JSON.parse(body) + body.extra = true + response.write(JSON.stringify(body)) + response.end(); + + }); + }); + this.httpMetric2Server = http.createServer().listen(8896); + this.httpMetric2Server.on('request', (request, response) => { + let body = []; + request.on('error', (err) => { + console.error(err); + }).on('data', (chunk) => { + body.push(chunk); + }).on('end', () => { + body = Buffer.concat(body).toString(); + response.writeHead(200, { + 'Content-Type': 'application/json', + }); + this.receivedData2.push(JSON.parse(body)) + body = JSON.parse(body) + body.extra = true + response.write(JSON.stringify(body)) + response.end(); + + }); + }); + this.httpEndPointServer = http.createServer().listen(8808); + this.httpEndPointServer.on('request', (request, response) => { + // the same kind of magic happens here! + let body = []; + request.on('error', (err) => { + console.error(err); + }).on('data', (chunk) => { + body.push(chunk); + }).on('end', () => { + body = Buffer.concat(body).toString(); + response.setHeader('Content-Type', 'application/json'); + response.write(JSON.stringify({ + headers: request.headers, + body: JSON.parse(body) + })) + response.end(); + }); + }); + }) + afterEach(function(){ + this.receivedData1 = [] + this.receivedData2 = [] + }) + after(function(){ + this.httpMetric1Server.close() + this.httpMetric2Server.close() + this.httpEndPointServer.close() + }) + it('Endpoint response', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + let self = this + request(targetRequest, routeItems, function(err, response) { + expect(response.answer.headers.test).to.equal("test") + expect(response.answer.body.test).to.equal("test") + setTimeout(function(){ + done() + }, 10) + }) + + }) + it('Metric 1 request', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + let self = this + + request(targetRequest, routeItems, function(err, response) { + let interval = setInterval(function(){ + let TotalLength = self.receivedData1.length + self.receivedData2.length + if(TotalLength == MAGIC_NUMBER) { + expect(TotalLength).to.equal(MAGIC_NUMBER) + done() + clearInterval(interval) + } + }, 10) + }) + + }) + it('Metric 2 request', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + let self = this + request(targetRequest, routeItems, function(err, response) { + let interval = setInterval(function(){ + let TotalLength = self.receivedData1.length + self.receivedData2.length + if(TotalLength == MAGIC_NUMBER) { + expect(TotalLength).to.equal(MAGIC_NUMBER) + done() + clearInterval(interval) + } + }, 10) + }) + }) +}) diff --git a/test/sendRequest.js b/test/sendRequest.js new file mode 100644 index 0000000..f740aff --- /dev/null +++ b/test/sendRequest.js @@ -0,0 +1,119 @@ +const expect = require("chai").expect; +const sift = require('sift').default +var http = require('http'); + +const sendRequest = require('../includes/sendRequest.js') +let targetRequests = JSON.parse(JSON.stringify(require('./targetRequests.js'))) +let routeItems = JSON.parse(JSON.stringify(require('./routeItems.js'))) + +describe('sendRequest', function(){ + before(function(){ + + this.routeItems = sift({ + endpointUrl: { + $in: ["http://127.0.0.1:8808/"] + } + }, routeItems) + + + + for (let targetRequest of this.routeItems) { + if (targetRequest.endpointUrl == "http://127.0.0.1:8808/") { + targetRequest.endpointUrl = "http://127.0.0.1:4908/" + } + } + + this.httpEndPointServer = http.createServer().listen(4908); + this.httpEndPointServer.on('request', (request, response) => { + // the same kind of magic happens here! + let body = []; + request.on('error', (err) => { + console.error(err); + }).on('data', (chunk) => { + body.push(chunk); + }).on('end', () => { + body = Buffer.concat(body).toString(); + response.setHeader('Content-Type', 'application/json'); + response.write(JSON.stringify({ + headers: request.headers, + body: JSON.parse(body) + })) + response.end(); + }); + }); + + }) + + it('general headers and body', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + let options = { + uri: 'http://127.0.0.1:4908', + headers: { + test:"test" + }, + method: 'post', + body: targetRequest.requestDetails._buffer + } + sendRequest(options, targetRequest, this.routeItems, function(err, response, body) { + let json = JSON.parse(body) + expect(json.headers.test).to.equal("test") + expect(json.body.test).to.equal("test") + done() + }) + + }) + it('error if url is invalid', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + let options = { + uri: 'testcom', + headers: { + test:"test" + }, + method: 'post', + body: targetRequest.requestDetails._buffer + } + sendRequest(options, targetRequest, this.routeItems, function(err, response, body) { + expect(err).to.be.an.instanceof(Error) + done() + }) + + }) + it('error if no response', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + let options = { + uri: 'http://127.0.0.1:9999', + headers: { + test:"test" + }, + method: 'post', + body: targetRequest.requestDetails._buffer + } + sendRequest(options, targetRequest, this.routeItems, function(err, response, body) { + expect(err).to.be.an.instanceof(Error) + done() + }) + }) + it('Request with no metric check', function(done){ + let targetRequest = targetRequests[0]; + targetRequest.requestDetails._buffer = '{"test": "test"}' + let options = { + uri: 'http://127.0.0.1:4908', + headers: { + test:"test" + }, + method: 'post', + body: targetRequest.requestDetails._buffer + } + targetRequest.isMetric = true + sendRequest(options, targetRequest, this.routeItems, function(err, response, body) { + let json = JSON.parse(body) + expect(json.headers.test).to.equal("test") + expect(json.body.test).to.equal("test") + done() + }) + + }) +}) diff --git a/test/targetRequests.js b/test/targetRequests.js new file mode 100644 index 0000000..24261fa --- /dev/null +++ b/test/targetRequests.js @@ -0,0 +1,178 @@ +module.exports = [ + { + route: 'register', + path: '', + method: 'POST', + jsonData: {}, + requestDetails: { + method: 'POST', + headers: { + test: "test" + } + }, + endpoint: { + scope: 'admin', + secureKey: 'xxxx' + } + }, + { + route: 'register', + path: 'xxxxx', + method: 'GET', + jsonData: {}, + requestDetails: { + method: 'GET', + headers: { + test: "test" + } + }, + endpoint: { + scope: 'admin', + secureKey: 'xxxx' + } + }, + { + route: 'repos/test', + path: '', + method: 'POST', + jsonData: {}, + requestDetails: { + method: 'POST', + headers: { + test: "test" + } + }, + endpoint: { + scope: 'admin', + secureKey: 'xxxx' + } + }, + { + route: 'repos/test', + path: 'reponame', + method: 'GET', + jsonData: {}, + requestDetails: { + method: 'GET', + headers: { + test: "test" + } + }, + endpoint: { + scope: 'admin', + secureKey: 'xxxx' + } + }, + { + route: 'repos', + path: '', + method: 'SEARCH', + jsonData: {}, + requestDetails: { + method: 'SEARCH', + headers: { + test: "test" + } + }, + endpoint: { + scope: 'admin', + secureKey: 'xxxx' + } + }, + { + route: 'repos', + path: 'reponame', + method: 'GET', + jsonData: {}, + requestDetails: { + method: 'GET', + headers: { + test: "test" + } + }, + endpoint: { + scope: 'admin', + secureKey: 'xxxx' + } + }, + { + route: 'repos/ownername/reponame/files', + path: '', + method: 'POST', + jsonData: {}, + requestDetails: { + method: 'POST', + headers: { + test: "test" + } + }, + endpoint: { + scope: 'admin', + secureKey: 'xxxx' + } + }, + { + route: 'repos/ownername/reponame/files', + path: 'fileid', + method: 'GET', + jsonData: {}, + requestDetails: { + method: 'GET', + headers: { + test: "test" + } + }, + endpoint: { + scope: 'admin', + secureKey: 'xxxx' + } + }, + { + route: 'repos/ownername/files', + path: '', + method: 'SEARCH', + jsonData: {}, + requestDetails: { + method: 'SEARCH', + headers: { + test: "test" + } + }, + endpoint: { + scope: 'admin', + secureKey: 'xxxx' + } + }, + { + route: 'repos/ownername/files', + path: 'fileid', + method: 'GET', + jsonData: {}, + requestDetails: { + method: 'GET', + headers: { + test: "test" + } + }, + endpoint: { + scope: 'admin', + secureKey: 'xxxx' + } + }, + { + route: 'repos/ownername/files2', + path: 'fileid', + method: 'GET', + jsonData: {}, + requestDetails: { + method: 'GET', + headers: { + test: "test" + } + }, + endpoint: { + scope: 'admin', + secureKey: 'xxxx' + } + } +] diff --git a/test/tests.js b/test/tests.js deleted file mode 100644 index 4fc190a..0000000 --- a/test/tests.js +++ /dev/null @@ -1,56 +0,0 @@ -const expect = require("chai").expect; -const MicroserviceClient = require('zenci-microservice-client'); - -require('dotenv').config(); - -describe('Router CRUD API',function(){ - var client = new MicroserviceClient({ - URL: "http://localhost:" + process.env.PORT, - secureKey: process.env.SECURE_KEY - }); - var RecordID; - var RecordToken; - - it('POST should return 200',function(done){ - client.post({ - path: 'test', - url: 'http://localhost:2000', - }, function(err, handlerResponse){ - expect(err).to.equal(null); - RecordID = handlerResponse.id; - RecordToken = handlerResponse.token; - done(); - }); - }); - - it('SEARCH should return 200',function(done){ - client.search({ "path": "test" }, function(err, handlerResponse){ - expect(err).to.equal(null); - expect(handlerResponse).to.not.equal(null); - done(); - }); - }); - - it('GET should return 200',function(done){ - client.get(RecordID, RecordToken, function(err, handlerResponse){ - expect(err).to.equal(null); - done(); - }); - }); - - - it('DELETE should return 200',function(done){ - client.delete(RecordID, RecordToken, function(err, handlerResponse){ - expect(err).to.equal(null); - done(); - }); - }); - - it('GET after delete should return nothing',function(done){ - client.get(RecordID, RecordToken, function(err, handlerResponse){ - expect(handlerResponse).to.equal(null); - done(); - }); - }); - -});