Engine up and running again, now in coffeescript. only event-poller remains

This commit is contained in:
Dominic Bosch 2014-04-03 23:35:02 +02:00
parent 0cb2241ea0
commit c71bf520a4
14 changed files with 1068 additions and 849 deletions

View file

@ -15,6 +15,10 @@ db = require './persistence'
# - [Dynamic Modules](dynamic-modules.html)
dynmod = require './dynamic-modules'
# - External Modules:
# [js-select](https://www.npmjs.org/package/js-select)
jsonQuery = require 'js-select'
listUserRules = {}
isRunning = false
@ -36,14 +40,25 @@ exports = module.exports = ( args ) =>
###
Add an event handler (eh) that listens for rules.
@public addRuleListener ( *eh* )
@param {function} eh
This is a helper function for the unit tests so we can verify that action
modules are loaded correctly
#TODO we should change this to functions returning true or false rather than returning
#the whole list
@public getListUserRules ()
###
exports.getListUserRules = () ->
listUserRules
###
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 = ( evt ) =>
if not listUserRules[evt.user]
if not listUserRules[evt.user] and evt.event isnt 'del'
listUserRules[evt.user] =
rules: {}
actions: {}
@ -52,20 +67,42 @@ exports.internalEvent = ( evt ) =>
oRule = evt.rule
if evt.event is 'new' or ( evt.event is 'init' and not oUser.rules[oRule.id] )
oUser.rules[oRule.id] = oRule
updateActionModules oRule
updateActionModules oRule, false
updateActionModules = ( oNewRule ) ->
if evt.event is 'del' and oUser
delete oUser.rules[oRule.id]
updateActionModules oRule, true
###
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 ( *oNewRule* )
@param {Object} oNewRule
###
updateActionModules = ( oNewRule, isDeleteOp ) ->
# Remove all action invoker modules that are not required anymore
fRemoveNotRequired = ( oUser ) ->
# Check whether the action is still existing in a rule
fRequired = ( actionName ) ->
return true for nmRl, oRl of oUser.rules when actionName in oRl.actions
return false
# return true for nmRl, oRl of oUser.rules when actionName in oRl.actions
for nmRl, oRl of oUser.rules
for action in oRl.actions
mod = (action.split ' -> ')[0]
if mod is actionName
return true
false
# Go thorugh all actions and check whether the action is still required
delete oUser.actions[action] for action of oUser.actions when not fRequired action
for action of oUser.actions
req = fRequired action
if not req
delete oUser.actions[action]
# delete oUser.actions[action] for action of oUser.actions when not fRequired action
fRemoveNotRequired oUser for name, oUser of listUserRules
@ -78,7 +115,7 @@ updateActionModules = ( oNewRule ) ->
# Load the action invoker module if it was part of the updated rule or if it's new
fAddIfNewOrNotExisting = ( actionName ) ->
moduleName = (actionName.split ' -> ')[0]
if not oUser.actions[moduleName] or oRule.id is oNewRule.id
if not isDeleteOp and ( not oUser.actions[moduleName] or oRule.id is oNewRule.id )
db.actionInvokers.getModule moduleName, ( err, obj ) ->
params = {}
res = dynmod.compileString obj.data, userName, moduleName, params, obj.lang
@ -88,6 +125,8 @@ updateActionModules = ( oNewRule ) ->
# Go thorugh all actions and check whether the action is still required
fCheckRules oRl for nmRl, oRl of oUser.rules
if JSON.stringify( oUser.rules ) is "{}" # TODO check whether this is really doing what it is supposed to do
delete listUserRules[userName]
fAddRequired userName, oUser for userName, oUser of listUserRules
@ -106,8 +145,8 @@ Checks whether all conditions of the rule are met by the event.
@param {Object} rule
###
validConditions = ( evt, rule ) ->
conds = rule.conditions
return false for prop of conds when not evt[prop] or evt[prop] isnt conds[prop]
for prop in rule.conditions
return false if jsonQuery( evt, prop ).nodes().length is 0
return true
###
@ -116,78 +155,16 @@ Handles retrieved events.
@private processEvent ( *evt* )
@param {Object} evt
###
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);
# */
# 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;
# }
# /**
# * 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);
# }
processEvent = ( evt ) =>
@log.info 'EN | processing event: ' + evt.event + '(' + evt.eventid + ')'
for userName, oUser of listUserRules
for ruleName, oRule of oUser.rules
if evt.event is oRule.event and validConditions evt, oRule
@log.info 'EN | EVENT FIRED: ' + evt.event + '(' + evt.eventid + ') for rule ' + ruleName
# fStoreAction userName, action for action in oRule.actions
for action in oRule.actions
arr = action.split ' -> '
listUserRules[userName]['actions'][arr[0]][arr[1]] evt
exports.shutDown = () ->
isRunning = false

View file

@ -1,4 +1,5 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.1
/*
Components Manager
@ -6,12 +7,10 @@ Components Manager
> The components manager takes care of the dynamic JS modules and the rules.
> Event Poller and Action Invoker modules are loaded as strings and stored in the database,
> then compiled into node modules and rules and used in the engine and event poller.
*/
*/
(function() {
var commandFunctions, db, dynmod, eventEmitter, events, exports, forgeModule, fs, getModuleParams, getModules, hasRequiredParams, path,
_this = this;
var commandFunctions, db, dynmod, eventEmitter, events, exports, forgeModule, fs, getModuleParams, getModules, hasRequiredParams, path;
db = require('./persistence');
@ -25,61 +24,67 @@ Components Manager
eventEmitter = new events.EventEmitter();
/*
Module call
-----------
Initializes the Components Manager and constructs a new Event Emitter.
@param {Object} args
*/
*/
exports = module.exports = (function(_this) {
return function(args) {
_this.log = args.logger;
db(args);
dynmod(args);
return module.exports;
};
})(this);
exports = module.exports = function(args) {
_this.log = args.logger;
db(args);
dynmod(args);
return module.exports;
};
/*
Add an event handler (eh) that listens for rules.
@public addRuleListener ( *eh* )
@param {function} eh
*/
*/
exports.addRuleListener = function(eh) {
eventEmitter.addListener('rule', eh);
return db.getAllActivatedRuleIdsPerUser(function(err, objUsers) {
var fGoThroughUsers, rules, user, _results;
fGoThroughUsers = function(user, rules) {
var fFetchRule, rule, _i, _len, _results;
fFetchRule = function(rule) {
var _this = this;
return db.getRule(rule, function(err, oRule) {
return eventEmitter.emit('rule', {
event: 'init',
user: user,
rule: JSON.parse(oRule)
});
});
exports.addRuleListener = (function(_this) {
return function(eh) {
eventEmitter.addListener('rule', eh);
return db.getAllActivatedRuleIdsPerUser(function(err, objUsers) {
var fGoThroughUsers, rules, user, _results;
fGoThroughUsers = function(user, rules) {
var fFetchRule, rule, _i, _len, _results;
fFetchRule = function(rule) {
return db.getRule(rule, (function(_this) {
return function(err, oRule) {
return eventEmitter.emit('rule', {
event: 'init',
user: user,
rule: JSON.parse(oRule)
});
};
})(this));
};
_results = [];
for (_i = 0, _len = rules.length; _i < _len; _i++) {
rule = rules[_i];
_results.push(fFetchRule(rule));
}
return _results;
};
_results = [];
for (_i = 0, _len = rules.length; _i < _len; _i++) {
rule = rules[_i];
_results.push(fFetchRule(rule));
for (user in objUsers) {
rules = objUsers[user];
_results.push(fGoThroughUsers(user, rules));
}
return _results;
};
_results = [];
for (user in objUsers) {
rules = objUsers[user];
_results.push(fGoThroughUsers(user, rules));
}
return _results;
});
};
});
};
})(this);
/*
Processes a user request coming through the request-handler.
@ -94,8 +99,7 @@ Components Manager
@param {Object} user
@param {Object} oReq
@param {function} callback
*/
*/
exports.processRequest = function(user, oReq, callback) {
var dat, err;
@ -140,8 +144,7 @@ Components Manager
getModules = function(user, oPayload, dbMod, callback) {
return dbMod.getAvailableModuleIds(user.username, function(err, arrNames) {
var answReq, fGetFunctions, id, oRes, sem, _i, _len, _results,
_this = this;
var answReq, fGetFunctions, id, oRes, sem, _i, _len, _results;
oRes = {};
answReq = function() {
return callback({
@ -153,16 +156,18 @@ Components Manager
if (sem === 0) {
return answReq();
} else {
fGetFunctions = function(id) {
return dbMod.getModule(id, function(err, oModule) {
if (oModule) {
oRes[id] = JSON.parse(oModule.functions);
}
if (--sem === 0) {
return answReq();
}
});
};
fGetFunctions = (function(_this) {
return function(id) {
return dbMod.getModule(id, function(err, oModule) {
if (oModule) {
oRes[id] = JSON.parse(oModule.functions);
}
if (--sem === 0) {
return answReq();
}
});
};
})(this);
_results = [];
for (_i = 0, _len = arrNames.length; _i < _len; _i++) {
id = arrNames[_i];
@ -186,41 +191,43 @@ Components Manager
}
};
forgeModule = function(user, oPayload, dbMod, callback) {
var answ;
answ = hasRequiredParams(['id', 'params', 'lang', 'data'], oPayload);
if (answ.code !== 200) {
return callback(answ);
} else {
return dbMod.getModule(oPayload.id, function(err, mod) {
var cm, funcs, id, name, src, _ref;
if (mod) {
answ.code = 409;
answ.message = 'Event Poller module name already existing: ' + oPayload.id;
} else {
src = oPayload.data;
cm = dynmod.compileString(src, user.username, oPayload.id, {}, oPayload.lang);
answ = cm.answ;
if (answ.code === 200) {
funcs = [];
_ref = cm.module;
for (name in _ref) {
id = _ref[name];
funcs.push(name);
}
_this.log.info("CM | Storing new module with functions " + (funcs.join()));
answ.message = "Event Poller module successfully stored! Found following function(s): " + funcs;
oPayload.functions = JSON.stringify(funcs);
dbMod.storeModule(user.username, oPayload);
if (oPayload["public"] === 'true') {
dbMod.publish(oPayload.id);
forgeModule = (function(_this) {
return function(user, oPayload, dbMod, callback) {
var answ;
answ = hasRequiredParams(['id', 'params', 'lang', 'data'], oPayload);
if (answ.code !== 200) {
return callback(answ);
} else {
return dbMod.getModule(oPayload.id, function(err, mod) {
var cm, funcs, id, name, src, _ref;
if (mod) {
answ.code = 409;
answ.message = 'Event Poller module name already existing: ' + oPayload.id;
} else {
src = oPayload.data;
cm = dynmod.compileString(src, user.username, oPayload.id, {}, oPayload.lang);
answ = cm.answ;
if (answ.code === 200) {
funcs = [];
_ref = cm.module;
for (name in _ref) {
id = _ref[name];
funcs.push(name);
}
_this.log.info("CM | Storing new module with functions " + (funcs.join()));
answ.message = "Event Poller module successfully stored! Found following function(s): " + funcs;
oPayload.functions = JSON.stringify(funcs);
dbMod.storeModule(user.username, oPayload);
if (oPayload["public"] === 'true') {
dbMod.publish(oPayload.id);
}
}
}
}
return callback(answ);
});
}
};
return callback(answ);
});
}
};
})(this);
commandFunctions = {
get_public_key: function(user, oPayload, callback) {

View file

@ -1,20 +1,20 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.1
/*
Configuration
=============
> Loads the configuration file and acts as an interface to it.
*/
*/
(function() {
var exports, fetchProp, fs, loadConfigFile, path,
_this = this;
var exports, fetchProp, fs, loadConfigFile, path;
fs = require('fs');
path = require('path');
/*
Module call
-----------
@ -24,21 +24,23 @@ Configuration
be generated) and configPath for a custom configuration file path.
@param {Object} args
*/
*/
exports = module.exports = (function(_this) {
return function(args) {
args = args != null ? args : {};
if (args.nolog) {
_this.nolog = true;
}
if (args.configPath) {
loadConfigFile(args.configPath);
} else {
loadConfigFile(path.join('config', 'system.json'));
}
return module.exports;
};
})(this);
exports = module.exports = function(args) {
args = args != null ? args : {};
if (args.nolog) {
_this.nolog = true;
}
if (args.configPath) {
loadConfigFile(args.configPath);
} else {
loadConfigFile(path.join('config', 'system.json'));
}
return module.exports;
};
/*
Tries to load a configuration file from the path relative to this module's parent folder.
@ -46,97 +48,102 @@ Configuration
@private loadConfigFile
@param {String} configPath
*/
*/
loadConfigFile = function(configPath) {
var confProperties, e, prop, _i, _len;
_this.config = null;
confProperties = ['log', 'http-port', 'db-port'];
try {
_this.config = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', configPath)));
_this.isReady = true;
for (_i = 0, _len = confProperties.length; _i < _len; _i++) {
prop = confProperties[_i];
if (!_this.config[prop]) {
_this.isReady = false;
loadConfigFile = (function(_this) {
return function(configPath) {
var confProperties, e, prop, _i, _len;
_this.config = null;
confProperties = ['log', 'http-port', 'db-port'];
try {
_this.config = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', configPath)));
_this.isReady = true;
for (_i = 0, _len = confProperties.length; _i < _len; _i++) {
prop = confProperties[_i];
if (!_this.config[prop]) {
_this.isReady = false;
}
}
if (!_this.isReady && !_this.nolog) {
return console.error("Missing property in config file, requires:\n" + (" - " + (confProperties.join("\n - "))));
}
} catch (_error) {
e = _error;
_this.isReady = false;
if (!_this.nolog) {
return console.error("Failed loading config file: " + e.message);
}
}
if (!_this.isReady && !_this.nolog) {
return console.error("Missing property in config file, requires:\n" + (" - " + (confProperties.join("\n - "))));
}
} catch (_error) {
e = _error;
_this.isReady = false;
if (!_this.nolog) {
return console.error("Failed loading config file: " + e.message);
}
}
};
};
})(this);
/*
Fetch a property from the configuration
@private fetchProp( *prop* )
@param {String} prop
*/
*/
fetchProp = (function(_this) {
return function(prop) {
var _ref;
return (_ref = _this.config) != null ? _ref[prop] : void 0;
};
})(this);
fetchProp = function(prop) {
var _ref;
return (_ref = _this.config) != null ? _ref[prop] : void 0;
};
/*
***Returns*** true if the config file is ready, else false
@public isReady()
*/
*/
exports.isReady = (function(_this) {
return function() {
return _this.isReady;
};
})(this);
exports.isReady = function() {
return _this.isReady;
};
/*
***Returns*** the HTTP port
@public getHttpPort()
*/
*/
exports.getHttpPort = function() {
return fetchProp('http-port');
};
/*
***Returns*** the DB port*
@public getDBPort()
*/
*/
exports.getDbPort = function() {
return fetchProp('db-port');
};
/*
***Returns*** the log conf object
@public getLogConf()
*/
*/
exports.getLogConf = function() {
return fetchProp('log');
};
/*
***Returns*** the crypto key
@public getCryptoKey()
*/
*/
exports.getKeygenPassphrase = function() {
return fetchProp('keygen-passphrase');

View file

@ -1,16 +1,15 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.1
/*
Dynamic Modules
===============
> Compiles CoffeeScript modules and loads JS modules in a VM, together
> with only a few allowed node.js modules.
*/
*/
(function() {
var cryptico, cs, exports, needle, vm,
_this = this;
var cryptico, cs, exports, needle, vm;
vm = require('vm');
@ -20,31 +19,36 @@ Dynamic Modules
cryptico = require('my-cryptico');
/*
Module call
-----------
Initializes the dynamic module handler.
@param {Object} args
*/
*/
exports = module.exports = (function(_this) {
return function(args) {
var numBits, passPhrase;
_this.log = args.logger;
if (!_this.strPublicKey && args['keygen']) {
passPhrase = args['keygen'];
numBits = 1024;
_this.oPrivateRSAkey = cryptico.generateRSAKey(passPhrase, numBits);
_this.strPublicKey = cryptico.publicKeyString(_this.oPrivateRSAkey);
_this.log.info("Public Key generated: " + _this.strPublicKey);
}
return module.exports;
};
})(this);
exports = module.exports = function(args) {
var numBits, passPhrase;
_this.log = args.logger;
if (!_this.strPublicKey && args['keygen']) {
passPhrase = args['keygen'];
numBits = 1024;
_this.oPrivateRSAkey = cryptico.generateRSAKey(passPhrase, numBits);
_this.strPublicKey = cryptico.publicKeyString(_this.oPrivateRSAkey);
_this.log.info("Public Key generated: " + _this.strPublicKey);
}
return module.exports;
};
exports.getPublicKey = (function(_this) {
return function() {
return _this.strPublicKey;
};
})(this);
exports.getPublicKey = function() {
return _this.strPublicKey;
};
/*
Try to run a JS module from a string, together with the
@ -56,43 +60,44 @@ Dynamic Modules
@param {String} id
@param {Object} params
@param {String} lang
*/
*/
exports.compileString = function(src, userId, modId, params, lang) {
var answ, err, ret, sandbox;
answ = {
code: 200,
message: 'Successfully compiled'
};
if (lang === 'CoffeeScript') {
exports.compileString = (function(_this) {
return function(src, userId, modId, params, lang) {
var answ, err, ret, sandbox;
answ = {
code: 200,
message: 'Successfully compiled'
};
if (lang === 'CoffeeScript') {
try {
src = cs.compile(src);
} catch (_error) {
err = _error;
answ.code = 400;
answ.message = 'Compilation of CoffeeScript failed at line ' + err.location.first_line;
}
}
sandbox = {
id: userId + '.' + modId + '.vm',
params: params,
needle: needle,
log: console.log,
exports: {}
};
try {
src = cs.compile(src);
vm.runInNewContext(src, sandbox, sandbox.id);
} catch (_error) {
err = _error;
answ.code = 400;
answ.message = 'Compilation of CoffeeScript failed at line ' + err.location.first_line;
answ.message = 'Loading Module failed: ' + err.message;
}
}
sandbox = {
id: userId + '.' + modId + '.vm',
params: params,
needle: needle,
log: console.log,
exports: {}
ret = {
answ: answ,
module: sandbox.exports
};
return ret;
};
try {
vm.runInNewContext(src, sandbox, sandbox.id);
} catch (_error) {
err = _error;
answ.code = 400;
answ.message = 'Loading Module failed: ' + err.message;
}
ret = {
answ: answ,
module: sandbox.exports
};
return ret;
};
})(this);
}).call(this);

View file

@ -1,4 +1,5 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.1
/*
Engine
@ -6,85 +7,123 @@ 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, listUserRules, pollQueue, processEvent, updateActionModules, validConditions,
_this = this,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
var db, dynmod, exports, isRunning, jsonQuery, listUserRules, pollQueue, processEvent, updateActionModules, validConditions;
db = require('./persistence');
dynmod = require('./dynamic-modules');
jsonQuery = require('js-select');
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) {
isRunning = true;
_this.log = args.logger;
db(args);
dynmod(args);
pollQueue();
return module.exports;
}
};
})(this);
exports = module.exports = function(args) {
if (!isRunning) {
isRunning = true;
_this.log = args.logger;
db(args);
dynmod(args);
pollQueue();
return module.exports;
}
};
/*
Add an event handler (eh) that listens for rules.
@public addRuleListener ( *eh* )
@param {function} eh
*/
This is a helper function for the unit tests so we can verify that action
modules are loaded correctly
*TODO we should change this to functions returning true or false rather than returning
*the whole list
@public getListUserRules ()
*/
exports.internalEvent = function(evt) {
var oRule, oUser;
if (!listUserRules[evt.user]) {
listUserRules[evt.user] = {
rules: {},
actions: {}
};
}
oUser = listUserRules[evt.user];
oRule = evt.rule;
if (evt.event === 'new' || (evt.event === 'init' && !oUser.rules[oRule.id])) {
oUser.rules[oRule.id] = oRule;
return updateActionModules(oRule);
}
exports.getListUserRules = function() {
return listUserRules;
};
updateActionModules = function(oNewRule) {
/*
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] = {
rules: {},
actions: {}
};
}
oUser = listUserRules[evt.user];
oRule = evt.rule;
if (evt.event === 'new' || (evt.event === 'init' && !oUser.rules[oRule.id])) {
oUser.rules[oRule.id] = oRule;
updateActionModules(oRule, false);
}
if (evt.event === 'del' && oUser) {
delete oUser.rules[oRule.id];
return updateActionModules(oRule, true);
}
};
})(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 ( *oNewRule* )
@param {Object} oNewRule
*/
updateActionModules = function(oNewRule, isDeleteOp) {
var fAddRequired, fRemoveNotRequired, name, oUser, userName, _results;
fRemoveNotRequired = function(oUser) {
var action, fRequired, _results;
var action, fRequired, req, _results;
fRequired = function(actionName) {
var nmRl, oRl, _ref;
var action, mod, nmRl, oRl, _i, _len, _ref, _ref1;
_ref = oUser.rules;
for (nmRl in _ref) {
oRl = _ref[nmRl];
if (__indexOf.call(oRl.actions, actionName) >= 0) {
return true;
_ref1 = oRl.actions;
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
action = _ref1[_i];
mod = (action.split(' -> '))[0];
if (mod === actionName) {
return true;
}
}
}
return false;
};
_results = [];
for (action in oUser.actions) {
if (!fRequired(action)) {
req = fRequired(action);
if (!req) {
_results.push(delete oUser.actions[action]);
} else {
_results.push(void 0);
}
}
return _results;
@ -94,13 +133,13 @@ Engine
fRemoveNotRequired(oUser);
}
fAddRequired = function(userName, oUser) {
var fCheckRules, nmRl, oRl, _ref, _results;
var fCheckRules, nmRl, oRl, _ref;
fCheckRules = function(oRule) {
var action, fAddIfNewOrNotExisting, _i, _len, _ref, _results;
fAddIfNewOrNotExisting = function(actionName) {
var moduleName;
moduleName = (actionName.split(' -> '))[0];
if (!oUser.actions[moduleName] || oRule.id === oNewRule.id) {
if (!isDeleteOp && (!oUser.actions[moduleName] || oRule.id === oNewRule.id)) {
return db.actionInvokers.getModule(moduleName, function(err, obj) {
var params, res;
params = {};
@ -118,12 +157,13 @@ Engine
return _results;
};
_ref = oUser.rules;
_results = [];
for (nmRl in _ref) {
oRl = _ref[nmRl];
_results.push(fCheckRules(oRl));
fCheckRules(oRl);
}
if (JSON.stringify(oUser.rules) === "{}") {
return delete listUserRules[userName];
}
return _results;
};
_results = [];
for (userName in listUserRules) {
@ -144,37 +184,71 @@ Engine
}
};
/*
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 conds, prop;
conds = rule.conditions;
for (prop in conds) {
if (!evt[prop] || evt[prop] !== conds[prop]) {
var prop, _i, _len, _ref;
_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;
};
/*
Handles retrieved events.
@private processEvent ( *evt* )
@param {Object} evt
*/
*/
processEvent = function(evt) {
return this.log.info('EN | processing event: ' + evt.event + '(' + evt.eventid + ')');
};
processEvent = (function(_this) {
return function(evt) {
var action, arr, oRule, oUser, ruleName, userName, _results;
_this.log.info('EN | processing event: ' + evt.event + '(' + evt.eventid + ')');
_results = [];
for (userName in listUserRules) {
oUser = listUserRules[userName];
_results.push((function() {
var _ref, _results1;
_ref = oUser.rules;
_results1 = [];
for (ruleName in _ref) {
oRule = _ref[ruleName];
if (evt.event === oRule.event && validConditions(evt, oRule)) {
this.log.info('EN | EVENT FIRED: ' + evt.event + '(' + evt.eventid + ') for rule ' + ruleName);
_results1.push((function() {
var _i, _len, _ref1, _results2;
_ref1 = oRule.actions;
_results2 = [];
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
action = _ref1[_i];
arr = action.split(' -> ');
_results2.push(listUserRules[userName]['actions'][arr[0]][arr[1]](evt));
}
return _results2;
})());
} else {
_results1.push(void 0);
}
}
return _results1;
}).call(_this));
}
return _results;
};
})(this);
exports.shutDown = function() {
return isRunning = false;

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,5 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.1
/*
WebAPI-ECA Engine
@ -9,12 +10,10 @@ WebAPI-ECA Engine
> node webapi-eca [opt]
>
> See below in the optimist CLI preparation for allowed optional parameters `[opt]`.
*/
*/
(function() {
var argv, cm, conf, cp, db, engine, fs, http, init, logconf, logger, nameEP, opt, optimist, path, procCmds, shutDown, usage,
_this = this;
var argv, cm, conf, cp, db, engine, fs, http, init, logconf, logger, nameEP, opt, optimist, path, procCmds, shutDown, usage;
logger = require('./logging');
@ -40,10 +39,10 @@ WebAPI-ECA Engine
procCmds = {};
/*
Let's prepare the optimist CLI optional arguments `[opt]`:
*/
*/
usage = 'This runs your webapi-based ECA engine';
@ -130,72 +129,76 @@ WebAPI-ECA Engine
this.log.info('RS | STARTING SERVER');
/*
This function is invoked right after the module is loaded and starts the server.
@private init()
*/
*/
init = function() {
var args;
args = {
logger: _this.log,
logconf: logconf
init = (function(_this) {
return function() {
var args;
args = {
logger: _this.log,
logconf: logconf
};
args['http-port'] = parseInt(argv.w || conf.getHttpPort());
args['db-port'] = parseInt(argv.d || conf.getDbPort());
args['keygen'] = conf.getKeygenPassphrase();
_this.log.info('RS | Initialzing DB');
db(args);
return db.isConnected(function(err) {
var cliArgs, poller;
if (err) {
_this.log.error('RS | No DB connection, shutting down system!');
return shutDown();
} else {
_this.log.info('RS | Initialzing engine');
engine(args);
_this.log.info('RS | Forking a child process for the event poller');
cliArgs = [args.logconf['mode'], args.logconf['io-level'], args.logconf['file-level'], args.logconf['file-path'], args.logconf['nolog']];
poller = cp.fork(path.resolve(__dirname, nameEP), cliArgs);
_this.log.info('RS | Initialzing module manager');
cm(args);
cm.addRuleListener(engine.internalEvent);
cm.addRuleListener(function(evt) {
return poller.send(evt);
});
_this.log.info('RS | Initialzing http listener');
args['request-service'] = cm.processRequest;
args['shutdown-function'] = shutDown;
return http(args);
}
});
};
args['http-port'] = parseInt(argv.w || conf.getHttpPort());
args['db-port'] = parseInt(argv.d || conf.getDbPort());
args['keygen'] = conf.getKeygenPassphrase();
_this.log.info('RS | Initialzing DB');
db(args);
return db.isConnected(function(err) {
var cliArgs, poller;
if (err) {
_this.log.error('RS | No DB connection, shutting down system!');
return shutDown();
} else {
_this.log.info('RS | Initialzing engine');
engine(args);
_this.log.info('RS | Forking a child process for the event poller');
cliArgs = [args.logconf['mode'], args.logconf['io-level'], args.logconf['file-level'], args.logconf['file-path'], args.logconf['nolog']];
poller = cp.fork(path.resolve(__dirname, nameEP), cliArgs);
_this.log.info('RS | Initialzing module manager');
cm(args);
cm.addRuleListener(engine.internalEvent);
cm.addRuleListener(function(evt) {
return poller.send(evt);
});
_this.log.info('RS | Initialzing http listener');
args['request-service'] = cm.processRequest;
args['shutdown-function'] = shutDown;
return http(args);
}
});
};
})(this);
/*
Shuts down the server.
@private shutDown()
*/
*/
shutDown = (function(_this) {
return function() {
_this.log.warn('RS | Received shut down command!');
if (db != null) {
db.shutDown();
}
engine.shutDown();
return process.exit();
};
})(this);
shutDown = function() {
_this.log.warn('RS | Received shut down command!');
if (db != null) {
db.shutDown();
}
engine.shutDown();
return process.exit();
};
/*
## Process Commands
*# Process Commands
When the server is run as a child process, this function handles messages
from the parent process (e.g. the testing suite)
*/
*/
process.on('message', function(cmd) {
return typeof procCmds[cmd] === "function" ? procCmds[cmd]() : void 0;

View file

@ -15,6 +15,7 @@
"crypto-js": "3.1.2",
"express": "3.4.8",
"groc": "0.6.1",
"js-select": "0.6.0",
"mustache": "0.8.1",
"needle": "0.6.3",
"nodeunit": "0.8.4",

View file

@ -11,6 +11,15 @@
},
"eventTwo":{
"event": "test_2"
},
"eventReal":{
"event": "epOne -> newMail",
"payload": {
"property": "test_1",
"nestedProperty": {
"more": "really nested"
}
}
}
},
"eps": {
@ -43,10 +52,10 @@
"aiTwo": {
"id":"aiTwo",
"lang":"CoffeeScript",
"data":"# Send a mail through emailyak\nexports.sendMail = ( args ) ->\n\turl = 'https://api.emailyak.com/v1/' + params.apikey + '/json/send/email/'\n\tpayload =\n\t FromAddress : \"testsender@mscliveweb.simpleyak.com\",\n\t ToAddress: \"dominic.bosch@gmail.com\",\n\t Subject: \"TestMAIL\",\n\t TextBody: \"Hello\"\n\t\n\tneedle.post url, payload, ( err, resp, body ) ->\n\t\tif err\n\t\t\tlog err\n\t\tif resp.statusCode isnt 200\n\t\t\tlog 'Request not successful:'\n\t\t\tlog body\n",
"data":"# Send a mail through emailyak\nexports.otherEvent = ( args ) ->\n\turl = 'https://api.emailyak.com/v1/' + params.apikey + '/json/send/email/'\n\tpayload =\n\t FromAddress : \"testsender@mscliveweb.simpleyak.com\",\n\t ToAddress: \"dominic.bosch@gmail.com\",\n\t Subject: \"TestMAIL\",\n\t TextBody: \"Hello\"\n\t\n\tneedle.post url, payload, ( err, resp, body ) ->\n\t\tif err\n\t\t\tlog err\n\t\tif resp.statusCode isnt 200\n\t\t\tlog 'Request not successful:'\n\t\t\tlog body\n",
"public":"false",
"params":"[\"apikey\",\"andmore\"]",
"functions":"[\"sendMail\"]"
"functions":"[\"otherEvent\"]"
}
},
"userparams": {
@ -58,32 +67,32 @@
"ruleOne": {
"id": "ruleOne_id",
"event": "custom-test-1",
"conditions": {
"property": "yourValue"
},
"conditions": [],
"actions": []
},
"ruleTwo": {
"id": "ruleTwo_id",
"event": "custom-test-2",
"conditions": {
"property": "yourValue2"
},
"conditions": [],
"actions": []
},
"ruleThree": {
"id": "ruleThree_id",
"event": "custom-test-3",
"conditions": {
"property": "yourValue3"
},
"conditions": [],
"actions": []
},
"ruleReal": {
"id": "ruleReal",
"event": "epOne -> newMail",
"conditions": {},
"conditions": [".more:val(\"really nested\")"],
"actions": ["aiOne -> sendMail"]
},
"ruleRealTwo": {
"id": "ruleRealTwo",
"event": "epOne -> newMail",
"conditions": [],
"actions": ["aiTwo -> otherEvent"]
}
},
"users": {

View file

@ -79,7 +79,6 @@ exports.testListener = ( test ) =>
cm.addRuleListener ( evt ) =>
strEvt = JSON.stringify evt.rule
console.log evt
if evt.event is 'init'
if strEvt is strRuleOne or strEvt is strRuleTwo
test.ok true, 'Dummy true to fill expected tests!'

View file

@ -22,63 +22,136 @@ engine opts
db = require path.join '..', 'js-coffee', 'persistence'
db opts
listRules = engine.getListUserRules()
oUser = objects.users.userOne
oRuleOne = objects.rules.ruleOne
oRuleTwo = objects.rules.ruleTwo
oRuleReal = objects.rules.ruleReal
oEpOne = objects.eps.epOne
oEpTwo = objects.eps.epTwo
oRuleRealTwo = objects.rules.ruleRealTwo
oAiOne = objects.ais.aiOne
oAiTwo = objects.ais.aiTwo
exports.tearDown = ( cb ) ->
db.deleteRule oRuleOne.id
db.deleteRule oRuleTwo.id
db.deleteRule oRuleReal.id
db.actionInvokers.deleteModule oAiOne.id
db.actionInvokers.deleteModule oAiTwo.id
# TODO if user is deleted all his modules should be unlinked and deleted
db.deleteUser oUser.username
engine.internalEvent
event: 'del'
user: oUser.username
rule: oRuleReal
engine.internalEvent
event: 'del'
user: oUser.username
rule: oRuleRealTwo
setTimeout cb, 100
exports.ruleEvents =
# init: first registration, multiple registration
# actions loaded and added correctly
# new: new actions added
# old actions removed
# delete: all actions removed if not required anymore
testInit: ( test ) ->
testInitAddDeleteMultiple: ( test ) ->
test.expect 2 + 2 * oRuleReal.actions.length + oRuleRealTwo.actions.length
db.storeUser oUser
test.done()
strRuleOne = JSON.stringify oRuleOne
strRuleTwo = JSON.stringify oRuleTwo
strRuleReal = JSON.stringify oRuleReal
db.actionInvokers.storeModule oUser.username, oAiOne
db.storeRule oRuleOne.id, strRuleOne
db.linkRule oRuleOne.id, oUser.username
db.activateRule oRuleOne.id, oUser.username
db.storeRule oRuleTwo.id, strRuleTwo
db.linkRule oRuleTwo.id, oUser.username
db.activateRule oRuleTwo.id, oUser.username
db.storeRule oRuleReal.id, strRuleReal
db.storeRule oRuleReal.id, JSON.stringify oRuleReal
db.linkRule oRuleReal.id, oUser.username
db.activateRule oRuleReal.id, oUser.username
db.actionInvokers.storeModule oUser.username, oAiOne
db.actionInvokers.storeModule oUser.username, oAiTwo
db.getAllActivatedRuleIdsPerUser ( err, obj ) =>
@existingRules = obj
console.log 'existing'
console.log obj
test.strictEqual listRules[oUser.username], undefined, 'Initial user object exists!?'
engine.internalEvent
event: 'new'
user: oUser.username
rule: oRuleReal
fWaitForPersistence = () ->
for act in oRuleReal.actions
mod = ( act.split ' -> ' )[0]
test.ok listRules[oUser.username].actions[mod], 'Missing action!'
engine.internalEvent
event: 'init'
event: 'new'
user: oUser.username
rule: oRuleReal
rule: oRuleRealTwo
fCheckRules = () ->
db.getAllActivatedRuleIdsPerUser ( err, obj ) =>
console.log 'after init'
console.log obj
console.log @existingRules is obj
fWaitAgainForPersistence = () ->
for act in oRuleRealTwo.actions
mod = ( act.split ' -> ' )[0]
test.ok listRules[oUser.username].actions[mod], 'Missing action!'
engine.internalEvent
event: 'del'
user: oUser.username
rule: oRuleRealTwo
for act in oRuleReal.actions
mod = ( act.split ' -> ' )[0]
test.ok listRules[oUser.username].actions[mod], 'Missing action!'
engine.internalEvent
event: 'del'
user: oUser.username
rule: oRuleReal
test.strictEqual listRules[oUser.username], undefined, 'Final user object exists!?'
test.done()
setTimeout fWaitAgainForPersistence, 200
setTimeout fWaitForPersistence, 200
# #TODO
# testUpdate: ( test ) ->
# test.expect 0
# test.done()
# db.storeUser oUser
# db.storeRule oRuleReal.id, JSON.stringify oRuleReal
# db.linkRule oRuleReal.id, oUser.username
# db.activateRule oRuleReal.id, oUser.username
# db.actionInvokers.storeModule oUser.username, oAiOne
# db.getAllActivatedRuleIdsPerUser ( err, obj ) ->
# console.log 'existing'
# console.log obj
# engine.internalEvent
# event: 'init'
# user: oUser.username
# rule: oRuleReal
# fCheckRules = () ->
# db.getAllActivatedRuleIdsPerUser ( err, obj ) ->
# console.log 'after init'
# console.log obj
# setTimeout fCheckRules, 500
exports.engine =
matchingEvent: ( test ) ->
db.storeUser oUser
db.storeRule oRuleReal.id, JSON.stringify oRuleReal
db.linkRule oRuleReal.id, oUser.username
db.activateRule oRuleReal.id, oUser.username
db.actionInvokers.storeModule oUser.username, oAiOne
engine.internalEvent
event: 'new'
user: oUser.username
rule: oRuleReal
fWaitForPersistence = () ->
evt = objects.events.eventReal
evt.eventid = 'event_testid'
db.pushEvent evt
setTimeout fWaitForPersistence, 200
setTimeout test.done, 500
setTimeout fCheckRules, 500

View file

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.1
(function() {
var fOnLoad;

View file

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.1
(function() {
var fOnLoad;

View file

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.6.3
// Generated by CoffeeScript 1.7.1
(function() {
var fOnLoad, strPublicKey;
@ -183,7 +183,7 @@
table.append(tr);
if ($('#ap_' + arrAI[0]).length === 0) {
div = $('<div>').attr('id', 'ap_' + arrAI[0]);
div.append($('<div> ').attr('class', 'underlined').text(arrAI[0]));
div.append($('<div> ')).attr('class', 'underlined').text(arrAI[0]);
$('#action_params').append(div);
fFetchActionParams(div, arrAI[0]);
}