// Generated by CoffeeScript 1.7.1 /* Engine ================== > The heart of the WebAPI ECA System. The engine loads action invoker modules > corresponding to active rules actions and invokes them if an appropriate event > is retrieved. */ (function() { var db, dynmod, exports, isRunning, jsonQuery, listUserRules, pollQueue, processEvent, semaphore, updateActionModules, validConditions; db = require('./persistence'); dynmod = require('./dynamic-modules'); jsonQuery = require('js-select'); /* This is ging to have a structure like: An object of users with their active rules and the required action modules "user-1": "rule-1": "rule": oRule-1 "actions": "action-1": oAction-1 "action-2": oAction-2 "rule-2": "rule": oRule-2 "actions": "action-1": oAction-1 "user-2": "rule-3": "rule": oRule-3 "actions": "action-3": oAction-3 */ listUserRules = {}; isRunning = false; /* Module call ----------- Initializes the Engine and starts polling the event queue for new events. @param {Object} args */ exports = module.exports = (function(_this) { return function(args) { if (!isRunning) { _this.log = args.logger; db(args); dynmod(args); setTimeout(exports.startEngine, 10); return module.exports; } }; })(this); /* This is a helper function for the unit tests so we can verify that action modules are loaded correctly @public getListUserRules () */ exports.getListUserRules = function() { return listUserRules; }; exports.startEngine = function() { if (!isRunning) { isRunning = true; return pollQueue(); } }; /* An event associated to rules happened and is captured here. Such events are basically CRUD on rules. @public internalEvent ( *evt* ) @param {Object} evt */ exports.internalEvent = (function(_this) { return function(evt) { var oRule, oUser; if (!listUserRules[evt.user] && evt.event !== 'del') { listUserRules[evt.user] = {}; } oUser = listUserRules[evt.user]; oRule = evt.rule; if (evt.event === 'new' || (evt.event === 'init' && !oUser[oRule.id])) { oUser[oRule.id] = { rule: oRule, actions: {} }; updateActionModules(oRule.id); } if (evt.event === 'del' && oUser) { delete oUser[evt.ruleId]; } if (JSON.stringify(oUser) === "{}") { return delete listUserRules[evt.user]; } }; })(this); /* As soon as changes were made to the rule set we need to ensure that the aprropriate action invoker modules are loaded, updated or deleted. @private updateActionModules ( *updatedRuleId* ) @param {Object} updatedRuleId */ updateActionModules = function(updatedRuleId) { var fAddRequired, fRemoveNotRequired, name, oUser, userName, _results; fRemoveNotRequired = function(oUser) { var action, fRequired, _results; fRequired = function(actionName) { var action, _i, _len, _ref; _ref = oUser[updatedRuleId].rule.actions; for (_i = 0, _len = _ref.length; _i < _len; _i++) { action = _ref[_i]; if ((action.split(' -> '))[0] === actionName) { return true; } } return false; }; _results = []; for (action in oUser[updatedRuleId].rule.actions) { if (!fRequired(action)) { _results.push(delete oUser[updatedRuleId].actions[action]); } else { _results.push(void 0); } } return _results; }; for (name in listUserRules) { oUser = listUserRules[name]; fRemoveNotRequired(oUser); } fAddRequired = function(userName, oUser) { var fCheckRules, nmRl, oRl, _results; fCheckRules = function(oMyRule) { var action, fAddIfNewOrNotExisting, _i, _len, _ref, _results; fAddIfNewOrNotExisting = function(actionName) { var moduleName; moduleName = (actionName.split(' -> '))[0]; if (!oMyRule.actions[moduleName] || oMyRule.rule.id === updatedRuleId) { return db.actionInvokers.getModule(moduleName, function(err, obj) { return dynmod.compileString(obj.data, userName, oMyRule.rule.id, moduleName, obj.lang, db.actionInvokers, function(result) { if (!result.answ === 200) { this.log.error("EN | Compilation of code failed! " + userName + ", " + oMyRule.rule.id + ", " + moduleName); } return oMyRule.actions[moduleName] = result.module; }); }); } }; _ref = oMyRule.rule.actions; _results = []; for (_i = 0, _len = _ref.length; _i < _len; _i++) { action = _ref[_i]; _results.push(fAddIfNewOrNotExisting(action)); } return _results; }; _results = []; for (nmRl in oUser) { oRl = oUser[nmRl]; _results.push(fCheckRules(oRl)); } return _results; }; _results = []; for (userName in listUserRules) { oUser = listUserRules[userName]; _results.push(fAddRequired(userName, oUser)); } return _results; }; semaphore = 0; pollQueue = function() { if (isRunning) { db.popEvent(function(err, obj) { if (!err && obj) { processEvent(obj); } return semaphore--; }); return setTimeout(pollQueue, 20 * semaphore); } }; /* Checks whether all conditions of the rule are met by the event. @private validConditions ( *evt, rule* ) @param {Object} evt @param {Object} rule */ validConditions = function(evt, rule) { var prop, _i, _len, _ref; if (rule.conditions.length === 0) { return true; } _ref = rule.conditions; for (_i = 0, _len = _ref.length; _i < _len; _i++) { prop = _ref[_i]; if (jsonQuery(evt, prop).nodes().length === 0) { return false; } } return true; }; semaphore = 0; /* Handles retrieved events. @private processEvent ( *evt* ) @param {Object} evt */ processEvent = (function(_this) { return function(evt) { var action, arr, fSearchAndInvokeAction, oMyRule, oUser, ruleName, userName, _results; fSearchAndInvokeAction = function(node, arrPath, funcName, evt, depth) { var err; if (!node) { this.log.error("EN | Didn't find property in user rule list: " + arrPath.join(', ' + " at depth " + depth)); return; } if (depth === arrPath.length) { try { semaphore++; node[funcName](evt.payload); } catch (_error) { err = _error; this.log.info("EN | ERROR IN ACTION INVOKER: " + err.message); node.logger(err.message); } if (semaphore-- % 100 === 0) { return this.log.warn("EN | The system is producing too many tokens! Currently: " + semaphore); } } else { return fSearchAndInvokeAction(node[arrPath[depth]], arrPath, funcName, evt, depth + 1); } }; _this.log.info('EN | processing event: ' + evt.event + '(' + evt.eventid + ')'); _results = []; for (userName in listUserRules) { oUser = listUserRules[userName]; _results.push((function() { var _results1; _results1 = []; for (ruleName in oUser) { oMyRule = oUser[ruleName]; if (evt.event === oMyRule.rule.event && validConditions(evt, oMyRule.rule)) { this.log.info('EN | EVENT FIRED: ' + evt.event + '(' + evt.eventid + ') for rule ' + ruleName); _results1.push((function() { var _i, _len, _ref, _results2; _ref = oMyRule.rule.actions; _results2 = []; for (_i = 0, _len = _ref.length; _i < _len; _i++) { action = _ref[_i]; arr = action.split(' -> '); _results2.push(fSearchAndInvokeAction(listUserRules, [userName, ruleName, 'actions', arr[0]], arr[1], evt, 0)); } return _results2; })()); } else { _results1.push(void 0); } } return _results1; }).call(_this)); } return _results; }; })(this); exports.shutDown = function() { return isRunning = false; }; }).call(this);