governify
Version:
App Middleware to control api's by SLA using AML
188 lines (159 loc) • 5.3 kB
JavaScript
var request = require('request');
var express = require('express');
var logger = require('winston');
logger.default.transports.console.timestamp = true;
var url = require('url');
var responseTime = require('response-time');
var governify = new Object();
governify.control = function(app, opt){
//default options.
var options = {
datastore : "http://datastore.governify.io/api/v6.1/",
namespace: "default",
apiKeyVariable: "apikey",
defaultPath: "/",
metrics: [
{
term: 'RequestTerm',
metric: 'Requests',
calculate: function(actualValue, req, res, callback){
//asyncronousCalculation
callback( parseInt(actualValue) + 1 );
}
},
{
metric: 'AVGResponseTime',
calculate: function(actualValue, req, res, callback){
//asyncronousCalculation
callback( res._headers['x-response-time'] );
}
}
]
}
//update good options
applyOptionsPolicy(options, opt);
//return middleware function
try{
//add middleware for calculate response time
app.use(responseTime());
//add middleware for check SLA
for(var m in options.metrics){
var metric = options.metrics[m];
if(metric.term)
app.use(metric.path, guaranteeIsComplied(options, metric.term, metric.metric, metric.method, metric.calculate));
}
//add middleware for update Metrics
for(var m in options.metrics ){
var metric = options.metrics[m];
app.use(metric.path, updateVariable(options, metric.metric, metric.method, metric.calculate));
}
}catch(e){
throw "The app param must be an expressJS or connectJS middleware app." + e;
}
}
function guaranteeIsComplied(options, term, metric, method, calculate){
logger.info("Created middleware to control " + term + " term");
return function(req, res, next){
if(method.indexOf(req.method) != -1 || method == ""){
logger.info("Checking if " + term + " is fulfilled...");
if(!req.query){
req.query = url.parse(req.url, true).query;
}
if(!req.query[options.apiKeyVariable]){
sendErrorResponse(401, 'Unauthorized! please check the user query param', res);
}else{
var propertyUrl = options.datastore + options.namespace + "agreements/" + req.query[options.apiKeyVariable] + "/guarantees/" + term;
request(propertyUrl, function(error, response, body){
if(!error && response.statusCode == 200 ){
logger.info(body);
if(body === "true"){
next();
}else{
sendErrorResponse(429, 'Unauthorized! Too many requests.', res);
}
}else{
sendErrorResponse(402, 'Unauthorized! please check your SLA.', res)
}
});
}
}else{
next();
}
}
}
function updateVariable(options, metric, method, calculate){
logger.info("Created middleware to update " + metric + " metric");
return function(req, res, next){
next();
if(method.indexOf(req.method) != -1 || method == ""){
if(!req.query){
req.query = url.parse(req.url, true).query;
}
if(!req.query[options.apiKeyVariable]){
sendErrorResponse(401, 'Unauthorized! please check the user query param', res);
}else{
var propertyUrl = options.datastore + options.namespace + "agreements/" + req.query[options.apiKeyVariable] + "/properties/" + metric;
request(propertyUrl, function(error, response, body){
if(!error && response.statusCode == 200 ){
var property = JSON.parse(body);
calculate(property.value, req, res, function(value){
property.value = value + '';
request.post({url: propertyUrl, body : JSON.stringify(property), headers:{'Content-Type':'application/json'}}, function(error, response, body){
if(!error){
logger.info(metric.toUpperCase() + " property has been updated.");
}else{
logger.info("Has occurred an error while it tried update " + metric + " property.");
}
});
});
}else{
logger.info("No data, please check your SLA.");
}
});
}
}else{
next();
}
}
}
//add suppot to Connect, modifing returned options
function sendErrorResponse(code, message, res){
var error = new Object();
error.code = code;
error.message = message;
try{
res.status(code);
res.send(JSON.stringify(error, true));
}catch(err){
res.statusCode = code;
res.end(JSON.stringify(error, true));
}
}
function applyOptionsPolicy(options, opt){
//modify default options.
if(opt){
if(opt.datastore)
options.datastore = opt.datastore;
if(opt.namespace)
options.namespace = opt.namespace;
if(opt.apiKeyVariable)
options.apiKeyVariable = opt.apiKeyVariable;
if(opt.defaultPath)
options.defaultPath = opt.defaultPath;
if(opt.customMetrics)
options.metrics = opt.customMetrics;
for(var m in options.metrics){
if(!options.metrics[m].path)
options.metrics[m].path = options.defaultPath;
if(!options.metrics[m].method)
options.metrics[m].method = "";
}
}
//add "/" to end url.
if(options.datastore[options.datastore.length-1] != "/")
options.datastore += "/";
if(options.namespace[options.namespace.length-1] != "/")
options.namespace += "/";
}
module.exports = governify;