webapi-eca/js/engine.js
2014-04-03 17:41:51 +02:00

245 lines
7.7 KiB
JavaScript

'use strict';
var path = require('path'),
regex = /\$X\.[\w\.\[\]]*/g, // find properties of $X
listRules = {},
listActionModules = {},
isRunning = true,
dynmod = require('./dynamic-modules'),
db = require('./persistence'), log;
exports = module.exports = function( args ) {
log = args.logger;
db( args);
dynmod(args);
pollQueue();
return module.exports;
};
var updateActionModules = function() {
for ( var user in listRules ) {
if(!listActionModules[user]) listActionModules[user] = {};
for ( var rule in listRules[user] ) {
var actions = listRules[user][rule].actions;
console.log(actions);
for ( var module in actions ){
for ( var i = 0; i < actions[module]['functions'].length; i++ ){
db.actionInvokers.getModule(module, function( err, objAM ){
db.actionInvokers.getUserParams(module, user, function( err, objParams ) {
console.log (objAM);
//FIXME am name is called 'actions'???
// if(objParams) { //TODO we don't need them for all modules
var answ = dynmod.compileString(objAM.code, objAM.actions + "_" + user, objParams, objAM.lang);
console.log('answ');
console.log(answ);
listActionModules[user][module] = answ.module;
console.log('loaded ' + user + ': ' + module);
console.log(listActionModules);
// }
});
});
}
}
}
}
};
exports.internalEvent = function( evt ) {
try {
// TODO do we need to determine init from newRule?
console.log (evt);
// console.log (data);
// var obj = JSON.parse( data );
// db.getRuleActivatedUsers(obj.id, function ( err, arrUsers ) {
// console.log (arrUsers);
// for(var i = 0; i < arrUsers.length; i++) {
// if( !listRules[arrUsers[i]]) listRules[arrUsers[i]] = {};
// listRules[arrUsers[i]][obj.id] = obj;
// updateActionModules();
// }
// });
} catch( err ) {
console.log( err );
}
console.log('internal event handled');
};
function pollQueue() {
if(isRunning) {
db.popEvent(function (err, obj) {
if(!err && obj) {
processEvent(obj);
}
setTimeout(pollQueue, 50); //TODO adapt to load
});
}
}
/**
* Handles correctly posted events
* @param {Object} evt The event object
*/
function processEvent(evt) {
log.info('EN', 'processing event: ' + evt.event + '(' + evt.eventid + ')');
var actions = checkEvent(evt);
console.log('found actions to invoke:');
console.log(actions);
for(var user in actions) {
for(var module in actions[user]) {
for(var i = 0; i < actions[user][module]['functions'].length; i++) {
var act = {
module: module,
function: actions[user][module]['functions'][i]
}
invokeAction(evt, user, act);
}
}
}
}
/**
FIXME merge with processEvent
* Check an event against the rules repository and return the actions
* if the conditons are met.
* @param {Object} evt the event to check
*/
function checkEvent(evt) {
var actions = {}, tEvt;
for(var user in listRules) {
actions[user] = {};
for(var rule in listRules[user]) {
//TODO this needs to get depth safe, not only data but eventually also
// on one level above (eventid and other meta)
tEvt = listRules[user][rule].event;
if(tEvt.module + ' -> ' + tEvt.function === evt.event && validConditions(evt.payload, listRules[user][rule])) {
log.info('EN', 'Rule "' + rule + '" fired');
var oAct = listRules[user][rule].actions;
console.log (oAct);
for(var module in oAct) {
if(!actions[user][module]) {
actions[user][module] = {
functions: []
};
}
for(var i = 0; i < oAct[module]['functions'].length; i++ ){
console.log ('processing action ' + i + ', ' + oAct[module]['functions'][i]);
actions[user][module]['functions'].push(oAct[module]['functions'][i]);
// if(actions[user].indexOf(arrAct[i]) === -1) actions[user].push(arrAct[i]);
}
}
}
}
}
return actions;
}
// {
// "event": "emailyak -> newMail",
// "payload": {
// "TextBody": "hello"
// }
// }
// exports.sendMail = ( args ) ->
// url = 'https://api.emailyak.com/v1/ps1g59ndfcwg10w/json/send/email/'
// data =
// FromAddress: 'tester@mscliveweb.simpleyak.com'
// ToAddress: 'dominic.bosch.db@gmail.com'
// TextBody: 'test'
// needle.post url, JSON.stringify( data ), {json: true}, ( err, resp, body ) ->
// log err
// log body
/**
* Checks whether all conditions of the rule are met by the event.
* @param {Object} evt the event to check
* @param {Object} rule the rule with its conditions
*/
function validConditions(evt, rule) {
for(var property in rule.conditions){
if(!evt[property] || evt[property] != rule.condition[property]) return false;
}
return true;
}
/**
* Invoke an action according to its type.
* @param {Object} evt The event that invoked the action
* @param {Object} action The action to be invoked
*/
function invokeAction( evt, user, action ) {
console.log('invoking action');
var actionargs = {};
//FIXME internal events, such as loopback ha sno arrow
//TODO this requires change. the module property will be the identifier
// in the actions object (or shall we allow several times the same action?)
console.log(action.module);
console.log(listActionModules);
var srvc = listActionModules[user][action.module];
console.log(srvc);
if(srvc && srvc[action.function]) {
//FIXME preprocessing not only on data
//FIXME no preprocessing at all, why don't we just pass the whole event to the action?'
// preprocessActionArguments(evt.payload, action.arguments, actionargs);
try {
if(srvc[action.function]) srvc[action.function](evt.payload);
} catch(err) {
log.error('EN', 'during action execution: ' + err);
}
}
else log.info('EN', 'No api interface found for: ' + action.module);
}
// /**
// * Action properties may contain event properties which need to be resolved beforehand.
// * @param {Object} evt The event whose property values can be used in the rules action
// * @param {Object} act The rules action arguments
// * @param {Object} res The object to be used to enter the new properties
// */
// function preprocessActionArguments(evt, act, res) {
// for(var prop in act) {
// /*
// * If the property is an object itself we go into recursion
// */
// if(typeof act[prop] === 'object') {
// res[prop] = {};
// preprocessActionArguments(evt, act[prop], res[prop]);
// }
// else {
// var txt = act[prop];
// var arr = txt.match(regex);
// * If rules action property holds event properties we resolve them and
// * replace the original action property
// // console.log(evt);
// if(arr) {
// for(var i = 0; i < arr.length; i++) {
// /*
// * The first three characters are '$X.', followed by the property
// */
// var actionProp = arr[i].substring(3).toLowerCase();
// // console.log(actionProp);
// for(var eprop in evt) {
// // our rules language doesn't care about upper or lower case
// if(eprop.toLowerCase() === actionProp) {
// txt = txt.replace(arr[i], evt[eprop]);
// }
// }
// txt = txt.replace(arr[i], '[property not available]');
// }
// }
// res[prop] = txt;
// }
// }
// }
exports.shutDown = function() {
if(log) log.info('EN', 'Shutting down Poller and DB Link');
isRunning = false;
if(db) db.shutDown();
};