mirror of
https://github.com/Hopiu/webapi-eca.git
synced 2026-03-16 22:10:31 +00:00
very rude commit, basically porting everything from the original project to create a new clean approach with thorough documentation and testing. This commit relies on the huge TODO list to be solved in order to go into a release state.
This commit is contained in:
parent
09024f287e
commit
dfe3df5df5
43 changed files with 2915 additions and 23 deletions
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
*~
|
||||
*.log
|
||||
*credentials.json
|
||||
*node_modules
|
||||
doc
|
||||
.project
|
||||
.settings
|
||||
20
LICENSE
20
LICENSE
|
|
@ -1,20 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Dominic Bosch
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
25
LICENSE.js
Normal file
25
LICENSE.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* LICENSE
|
||||
* =======
|
||||
* Copyright (c) 2013 by Dominic Bosch and The
|
||||
* Regents of the University of Basel. All rights reserved.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software and its
|
||||
* documentation for any purpose, without fee, and without written agreement is
|
||||
* hereby granted, provided that the above copyright notice and the following
|
||||
* two paragraphs appear in all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE UNIVERSITY OF BASEL BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
|
||||
* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
|
||||
* BASEL HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* THE UNIVERSITY OF BASEL SPECIFICALLY DISCLAIMS ANY WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE UNIVERSITY OF BASEL HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*
|
||||
* Authors: Dominic Bosch <dominic.bosch@stud.unibas.ch>
|
||||
*
|
||||
*/
|
||||
55
README.md
55
README.md
|
|
@ -1,4 +1,53 @@
|
|||
webapi-eca
|
||||
==========
|
||||
README: webapi-eca
|
||||
==================
|
||||
|
||||
An ECA engine which acts as a middleware between WebAPI's.
|
||||
>A Modular ECA Engine Server which acts as a middleware between WebAPI's.
|
||||
>This folder continues examples of an ECA engine and how certain use cases could be implemented together with a rules language.
|
||||
>Be sure the user which runs the server doesn't have ANY write rights on the server!
|
||||
>Malicious modules could capture or destroy your server!
|
||||
>
|
||||
>
|
||||
>The server is started through the [rules_server.js](rules_server.html) module by calling `node rule_server.js`.
|
||||
|
||||
|
||||
Getting started
|
||||
---------------
|
||||
Clone project:
|
||||
|
||||
git clone https://github.com/dominicbosch/webapi-eca.git
|
||||
|
||||
Download and install dependencies:
|
||||
|
||||
cd webapi-eca
|
||||
npm install
|
||||
|
||||
Get your [redis](http://redis.io/) instance up and running (and find the port for the config file below) or create your own `db_interface.js`.
|
||||
|
||||
Create the configuration file:
|
||||
|
||||
mkdir config
|
||||
vi config/config.json
|
||||
|
||||
Insert your settings, for example:
|
||||
|
||||
{
|
||||
"http_port": 8125,
|
||||
"db_port": 6379,
|
||||
"crypto_key": "[your key]"
|
||||
}
|
||||
|
||||
Start the server:
|
||||
|
||||
node js/server
|
||||
|
||||
*Congratulations, your own WebAPI based ECA engine server is now up and running!*
|
||||
|
||||
Optional command line tools:
|
||||
----------------------------
|
||||
Run test suite:
|
||||
|
||||
node run_tests
|
||||
|
||||
Create the doc *(to be accessed via the webserver, e.g.: localhost:8125/doc/)*:
|
||||
|
||||
node create_doc
|
||||
|
|
|
|||
24
TODO.js
Normal file
24
TODO.js
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* TODO's:
|
||||
* =======
|
||||
* - [ ] Redis queue
|
||||
* - [ ] user handling (personal credentials)
|
||||
* - [ ] security in terms of users (private key, login)
|
||||
* - [ ] vm for modules, only give few libraries (no fs!)
|
||||
* - [ ] rules generator (provide webpage that is used to create rules dependent on the existing modues)
|
||||
* - [ ] geo location module, test on smartphone
|
||||
*
|
||||
*
|
||||
* TODO's per module:
|
||||
* ==================
|
||||
* Testing | clean documentation | Clean error handling (Especially in loading of modules):
|
||||
*
|
||||
* - [ ] DB Interface
|
||||
* - [ ] Engine
|
||||
* - [ ] Event Poller
|
||||
* - [ ] HTTP Listener
|
||||
* - [ ] Logging
|
||||
* - [ ] Module Loader
|
||||
* - [ ] Module Manager
|
||||
* - [ ] Server
|
||||
*/
|
||||
1
config/.gitignore
vendored
Normal file
1
config/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
config.json
|
||||
20
create_doc.js
Normal file
20
create_doc.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* # groc Documentation
|
||||
* Create the documentation to be displayed through the webserver.
|
||||
*/
|
||||
require('groc').CLI([
|
||||
"README.md",
|
||||
"TODO.js",
|
||||
"LICENSE.js",
|
||||
"js/*",
|
||||
"mod_actions/**/*.js",
|
||||
"mod_events/**/*.js"
|
||||
// ,
|
||||
// "rules/*.json"
|
||||
],
|
||||
function(error) {
|
||||
if (error) {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
);
|
||||
10
documentation/user_acceptance_tests.txt
Normal file
10
documentation/user_acceptance_tests.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
load rules rules
|
||||
load rules
|
||||
load action probinder
|
||||
load actions
|
||||
load event probinder
|
||||
load events
|
||||
-> all these from server started from shell and node (different cwd!)
|
||||
shutdown
|
||||
upload rules/actions/events
|
||||
look at doc ([host]/doc/)
|
||||
264
js/db_interface.js
Normal file
264
js/db_interface.js
Normal file
|
|
@ -0,0 +1,264 @@
|
|||
// # DB Interface
|
||||
// Handles the connection to the database and provides functionalities for
|
||||
// event/action modules, rules and the encrypted storing of authentication tokens.
|
||||
|
||||
// ## General
|
||||
// General functionality as a wrapper for the module holds initialization,
|
||||
// encryption/decryption, the retrieval of modules and shut down.
|
||||
// Modules of the same group, e.g. action modules are registered in an unordered
|
||||
// set in the database, from where they can be retrieved again. For example a new
|
||||
// action module has its ID (e.g 'probinder') first registered in the set
|
||||
// 'action_modules' and then stored in the db with the key 'action\_module\_' + ID
|
||||
// (e.g. action\_module\_probinder).
|
||||
'use strict';
|
||||
var redis = require('redis'),
|
||||
crypto = require('crypto'),
|
||||
log = require('./logging'),
|
||||
crypto_key, db;
|
||||
|
||||
|
||||
// @function init()
|
||||
|
||||
/*
|
||||
* Initializes the DB connection. Requires a port where the DB listens to requests
|
||||
* and a key that is used for encryptions.
|
||||
* @param {int} db_port
|
||||
*/
|
||||
exports.init = function(db_port, key, cbDone){
|
||||
if(!db_port || !key) {
|
||||
log.error('DB', 'No DB port or cipher key defined!');
|
||||
return null;
|
||||
}
|
||||
crypto_key = key;
|
||||
db = redis.createClient(db_port);
|
||||
db.on("error", function (err) {
|
||||
log.error('DB', ' Message from DB: ' + err);
|
||||
});
|
||||
if(cbDone) cbDone();
|
||||
};
|
||||
|
||||
/**
|
||||
* ### encrypt
|
||||
*/
|
||||
function encrypt(plainText) {
|
||||
if(!plainText) return null;
|
||||
try {
|
||||
var enciph = crypto.createCipher('aes-256-cbc', crypto_key);
|
||||
var et = enciph.update(plainText, 'utf8', 'base64') + enciph.final('base64');
|
||||
log.print('DB', 'Encrypted credentials into: ' + et);
|
||||
return et;
|
||||
} catch (err) {
|
||||
log.error('DB', 'in encrypting: ' + err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ### decrypt
|
||||
*/
|
||||
function decrypt(crypticText) {
|
||||
if(!crypticText) return null;
|
||||
try {
|
||||
var deciph = crypto.createDecipher('aes-256-cbc', crypto_key);
|
||||
return deciph.update(crypticText, 'base64', 'utf8') + deciph.final('utf8');
|
||||
} catch (err) {
|
||||
log.error('DB', 'in decrypting: ' + err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ### replyHandler
|
||||
* Abstraction answer handling for simple information replies from the DB.
|
||||
* @param {String} action the action to be displayed in the output string.
|
||||
*/
|
||||
function replyHandler(action) {
|
||||
return function(err, reply) {
|
||||
if(err) log.error('DB', ' during "' + action + '": ' + err);
|
||||
else log.print('DB', action + ': ' + reply);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* ### getSetRecords
|
||||
* The general structure for modules is that the key is stored in a set.
|
||||
* By fetching all set entries we can then fetch all modules, which is
|
||||
* automated in this function.
|
||||
*
|
||||
* @param {String} set the set name how it is stored in the DB
|
||||
* @param {function} funcSingle the function that fetches single entries from the DB
|
||||
* @param {function} callback the function to be called on success or error, receives
|
||||
* arguments (err, obj)
|
||||
*/
|
||||
function getSetRecords(set, funcSingle, callback) {
|
||||
db.smembers(set, function(err, reply) {
|
||||
if(err) log.error('DB', 'fetching ' + set + ': ' + err);
|
||||
else {
|
||||
if(reply.length === 0) {
|
||||
callback(null, null);
|
||||
} else {
|
||||
var semaphore = reply.length, objReplies = {};
|
||||
setTimeout(function() {
|
||||
if(semaphore > 0) {
|
||||
callback('Timeout fetching ' + set, null);
|
||||
}
|
||||
}, 1000);
|
||||
for(var i = 0; i < reply.length; i++){
|
||||
funcSingle(reply[i], function(prop) {
|
||||
return function(err, reply) {
|
||||
if(err) log.error('DB', ' fetching single element: ' + prop);
|
||||
else {
|
||||
objReplies[prop] = reply;
|
||||
if(--semaphore === 0) callback(null, objReplies);
|
||||
}
|
||||
};
|
||||
}(reply[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// @method shutDown()
|
||||
|
||||
// Shuts down the db link.
|
||||
exports.shutDown = function() { db.quit(); };
|
||||
|
||||
// ## Action Modules
|
||||
|
||||
/**
|
||||
* ### storeActionModule
|
||||
* Store a string representation of an action module in the DB.
|
||||
* @param {String} id the unique identifier of the module
|
||||
* @param {String} data the string representation
|
||||
*/
|
||||
exports.storeActionModule = function(id, data) {
|
||||
db.sadd('action_modules', id, replyHandler('storing action module key ' + id));
|
||||
db.set('action_module_' + id, data, replyHandler('storing action module ' + id));
|
||||
};
|
||||
|
||||
/**
|
||||
* ### getActionModule(id, callback)
|
||||
* Query the DB for an action module.
|
||||
* @param {String} id the module id
|
||||
* @param {function} callback the callback to receive the answer (err, obj)
|
||||
*/
|
||||
exports.getActionModule = function(id, callback) {
|
||||
if(callback) db.get('action_module_' + id, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* ### getActionModules(callback)
|
||||
* Fetch all action modules.
|
||||
* @param {function} callback the callback to receive the answer (err, obj)
|
||||
*/
|
||||
exports.getActionModules = function(callback) {
|
||||
getSetRecords('action_modules', exports.getActionModule, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* storeActionModuleAuth(id, data)
|
||||
* Store a string representation of the authentication parameters for an action module.
|
||||
* @param {String} id the unique identifier of the module
|
||||
* @param {String} data the string representation
|
||||
*/
|
||||
exports.storeActionModuleAuth = function(id, data) {
|
||||
if(data) {
|
||||
db.sadd('action_modules_auth', id, replyHandler('storing action module auth key ' + id));
|
||||
db.set('action_module_' + id +'_auth', encrypt(data), replyHandler('storing action module auth ' + id));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* ### getActionModuleAuth(id, callback)
|
||||
* Query the DB for an action module authentication token.
|
||||
* @param {String} id the module id
|
||||
* @param {function} callback the callback to receive the answer (err, obj)
|
||||
*/
|
||||
exports.getActionModuleAuth = function(id, callback) {
|
||||
if(callback) db.get('action_module_' + id + '_auth', function(err, txt) { callback(err, decrypt(txt)); });
|
||||
};
|
||||
|
||||
// ## Event Modules
|
||||
|
||||
/**
|
||||
* ### storeEventModule(id, data)
|
||||
* Store a string representation of an event module in the DB.
|
||||
* @param {String} id the unique identifier of the module
|
||||
* @param {String} data the string representation
|
||||
*/
|
||||
exports.storeEventModule = function(id, data) {
|
||||
db.sadd('event_modules', id, replyHandler('storing event module key ' + id));
|
||||
db.set('event_module_' + id, data, replyHandler('storing event module ' + id));
|
||||
};
|
||||
|
||||
/**
|
||||
* ### getEventModule(id, callback)
|
||||
* Query the DB for an event module.
|
||||
* @param {String} id the module id
|
||||
* @param {function} callback the callback to receive the answer (err, obj)
|
||||
*/
|
||||
exports.getEventModule = function(id, callback) {
|
||||
if(callback) db.get('event_module_' + id, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* ### getEventModules(callback)
|
||||
* Fetch all event modules.
|
||||
* @param {function} callback the callback that receives the arguments (err, obj)
|
||||
*/
|
||||
exports.getEventModules = function(callback) {
|
||||
getSetRecords('event_modules', exports.getEventModule, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* ### storeEventModuleAuth(id, data)
|
||||
* Store a string representation of he authentication parameters for an event module.
|
||||
* @param {String} id the unique identifier of the module
|
||||
* @param {String} data the string representation
|
||||
*/
|
||||
exports.storeEventModuleAuth = function(id, data) {
|
||||
if(data) {
|
||||
db.sadd('event_modules_auth', id, replyHandler('storing event module auth key ' + id));
|
||||
db.set('event_module_' + id +'_auth', encrypt(data), replyHandler('storing event module auth ' + id));
|
||||
}
|
||||
};
|
||||
|
||||
// @method getEventModuleAuth(id, callback)
|
||||
|
||||
// Query the DB for an event module authentication token.
|
||||
// @param {String} id the module id
|
||||
// @param {function} callback the callback to receive the answer (err, obj)
|
||||
exports.getEventModuleAuth = function(id, callback) {
|
||||
if(callback) db.get('event_module_' + id +'_auth', function(err, txt) { callback(err, decrypt(txt)); });
|
||||
};
|
||||
|
||||
// ## Rules
|
||||
|
||||
// @method storeRule(id, data)
|
||||
|
||||
// Store a string representation of a rule in the DB.
|
||||
// @param {String} id the unique identifier of the rule
|
||||
// @param {String} data the string representation
|
||||
exports.storeRule = function(id, data) {
|
||||
db.sadd('rules', id, replyHandler('storing rule key ' + id));
|
||||
db.set('rule_' + id, data, replyHandler('storing rule ' + id));
|
||||
};
|
||||
|
||||
// @method getRule(id, callback)
|
||||
|
||||
// Query the DB for a rule.
|
||||
// @param {String} id the rule id
|
||||
// @param {function} callback the callback to receive the answer (err, obj)
|
||||
exports.getRule = function(id, callback) {
|
||||
db.get('rule_' + id, callback);
|
||||
};
|
||||
|
||||
// @method getRules(callback)
|
||||
|
||||
// Fetch all rules from the database.
|
||||
// @param {function} callback the callback to receive the answer (err, obj)
|
||||
exports.getRules = function(callback) {
|
||||
getSetRecords('rules', exports.getRule, callback);
|
||||
};
|
||||
|
||||
253
js/engine.js
Normal file
253
js/engine.js
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
'use strict';
|
||||
|
||||
var path = require('path'),
|
||||
cp = require('child_process'),
|
||||
ml = require('./module_loader'),
|
||||
log = require('./logging'),
|
||||
poller, db, isRunning = true,
|
||||
qEvents = new (require('./queue')).Queue(); // export queue into redis
|
||||
|
||||
var regex = /\$X\.[\w\.\[\]]*/g, // find properties of $X
|
||||
listRules = {},
|
||||
listActionModules = {},
|
||||
actionsLoaded = false, eventsLoaded = false;
|
||||
/*
|
||||
* Initialize the rules engine which initializes the module loader.
|
||||
* @param {Object} db_link the link to the db, see [db\_interface](db_interface.html)
|
||||
* @param {String} db_port the db port
|
||||
* @param {String} crypto_key the key to be used for encryption on the db, max legnth 256
|
||||
*/
|
||||
function init(db_link, db_port, crypto_key) {
|
||||
db = db_link;
|
||||
loadActions();
|
||||
poller = cp.fork(path.resolve(__dirname, 'eventpoller'), [db_port, crypto_key]);
|
||||
poller.on('message', function(evt) {
|
||||
if(evt.event === 'ep_finished_loading') {
|
||||
eventsLoaded = true;
|
||||
tryToLoadRules();
|
||||
} else pushEvent(evt);
|
||||
});
|
||||
//start to poll the event queue
|
||||
pollQueue();
|
||||
}
|
||||
|
||||
function loadActions() {
|
||||
db.getActionModules(function(err, obj) {
|
||||
if(err) log.error('EN', 'retrieving Action Modules from DB!');
|
||||
else {
|
||||
if(!obj) {
|
||||
log.print('EN', 'No Action Modules found in DB!');
|
||||
actionsLoaded = true;
|
||||
tryToLoadRules();
|
||||
} else {
|
||||
var m, semaphore = 0;
|
||||
for(var el in obj) {
|
||||
semaphore++;
|
||||
log.print('EN', 'Loading Action Module from DB: ' + el);
|
||||
m = ml.requireFromString(obj[el], el);
|
||||
db.getActionModuleAuth(el, function(mod) {
|
||||
return function(err, obj) {
|
||||
if(--semaphore == 0) {
|
||||
actionsLoaded = true;
|
||||
tryToLoadRules();
|
||||
}
|
||||
if(obj && mod.loadCredentials) mod.loadCredentials(JSON.parse(obj));
|
||||
};
|
||||
}(m));
|
||||
listActionModules[el] = m;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function tryToLoadRules() {
|
||||
if(eventsLoaded && actionsLoaded) {
|
||||
db.getRules(function(err, obj) {
|
||||
for(var el in obj) loadRule(JSON.parse(obj[el]));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert an action module into the list of available interfaces.
|
||||
* @param {Object} objModule the action module object
|
||||
*/
|
||||
function loadActionModule(name, objModule) {
|
||||
log.print('EN', 'Action module "' + name + '" loaded');
|
||||
listActionModules[name] = objModule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a rule into the eca rules repository
|
||||
* @param {Object} objRule the rule object
|
||||
*/
|
||||
function loadRule(objRule) {
|
||||
//TODO validate rule
|
||||
log.print('EN', 'Loading Rule: ' + objRule.id);
|
||||
if(listRules[objRule.id]) log.print('EN', 'Replacing rule: ' + objRule.id);
|
||||
listRules[objRule.id] = objRule;
|
||||
|
||||
// Notify poller about eventual candidate
|
||||
try {
|
||||
poller.send('event|'+objRule.event);
|
||||
} catch (err) {
|
||||
log.print('EN', 'Unable to inform poller about new active rule!');
|
||||
}
|
||||
}
|
||||
|
||||
function pollQueue() {
|
||||
if(isRunning) {
|
||||
var evt = qEvents.dequeue();
|
||||
if(evt) {
|
||||
processEvent(evt);
|
||||
}
|
||||
setTimeout(pollQueue, 50); //TODO adapt to load
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores correctly posted events in the queue
|
||||
* @param {Object} evt The event object
|
||||
*/
|
||||
function pushEvent(evt) {
|
||||
qEvents.enqueue(evt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles correctly posted events
|
||||
* @param {Object} evt The event object
|
||||
*/
|
||||
function processEvent(evt) {
|
||||
log.print('EN', 'processing event: ' + evt.event + '(' + evt.eventid + ')');
|
||||
var actions = checkEvent(evt);
|
||||
for(var i = 0; i < actions.length; i++) {
|
||||
invokeAction(evt, actions[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = [];
|
||||
for(var rn in listRules) {
|
||||
//TODO this needs to get depth safe, not only data but eventually also
|
||||
// on one level above (eventid and other meta)
|
||||
if(listRules[rn].event === evt.event && validConditions(evt.data, listRules[rn])) {
|
||||
log.print('EN', 'Rule "' + rn + '" fired');
|
||||
actions = actions.concat(listRules[rn].actions);
|
||||
}
|
||||
}
|
||||
return actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.condition){
|
||||
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, action) {
|
||||
var actionargs = {},
|
||||
arrModule = action.module.split('->');
|
||||
if(arrModule.length < 2) {
|
||||
log.error('EN', 'Invalid rule detected!');
|
||||
return;
|
||||
}
|
||||
var srvc = listActionModules[arrModule[0]];
|
||||
if(srvc && srvc[arrModule[1]]) {
|
||||
//FIXME preprocessing not only on data
|
||||
preprocessActionArguments(evt.data, action.arguments, actionargs);
|
||||
try {
|
||||
if(srvc[arrModule[1]]) srvc[arrModule[1]](actionargs);
|
||||
} catch(err) {
|
||||
log.error('EN', 'during action execution: ' + err);
|
||||
}
|
||||
}
|
||||
else log.print('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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadEventModule(args, answHandler) {
|
||||
if(args && args.name) {
|
||||
answHandler.answerSuccess('Loading event module ' + args.name + '...');
|
||||
poller.send('cmd|loadevent|'+args.name);
|
||||
} else if(args) answHandler.answerError(args.name + ' not found');
|
||||
}
|
||||
|
||||
function loadEventModules(args, answHandler) {
|
||||
answHandler.answerSuccess('Loading event moules...');
|
||||
poller.send('cmd|loadevents');
|
||||
}
|
||||
|
||||
function shutDown() {
|
||||
log.print('EN', 'Shutting down Poller and DB Link');
|
||||
isRunning = false;
|
||||
poller.send('cmd|shutdown');
|
||||
db.shutDown();
|
||||
}
|
||||
|
||||
exports.init = init;
|
||||
exports.loadActionModule = loadActionModule;
|
||||
exports.loadRule = loadRule;
|
||||
exports.loadEventModule = loadEventModule;
|
||||
exports.loadEventModules = loadEventModules;
|
||||
exports.pushEvent = pushEvent;
|
||||
exports.shutDown = shutDown;
|
||||
146
js/eventpoller.js
Normal file
146
js/eventpoller.js
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
// # Event Poller
|
||||
|
||||
'use strict';
|
||||
|
||||
if(process.argv.length < 3) {
|
||||
log.error('EP', 'No DB port defined! Not starting poller...');
|
||||
} else {
|
||||
(function() {
|
||||
var fs = require('fs'),
|
||||
path = require('path'),
|
||||
log = require('./logging'),
|
||||
db = require('./db_interface'),
|
||||
ml = require('./module_loader'),
|
||||
listMessageActions = {},
|
||||
listAdminCommands = {},
|
||||
listEventModules = {},
|
||||
listPoll = {}, //TODO this will change in the future because it could have
|
||||
//several parameterized (user-specific) instances of each event module
|
||||
isRunning = true,
|
||||
eId = 0;
|
||||
//TODO allow different polling intervals (a wrapper together with settimeout per to be polled could be an easy and solution)
|
||||
|
||||
db.init(process.argv[2], process.argv[3]);
|
||||
|
||||
//TODO eventpoller will not load event modules from filesystem, this will be done by
|
||||
// the moduel manager and the eventpoller receives messages about new/updated active rules
|
||||
|
||||
db.getEventModules(function(err, obj) {
|
||||
if(err) log.error('EP', 'retrieving Event Modules from DB!');
|
||||
else {
|
||||
if(!obj) {
|
||||
log.print('EP', 'No Event Modules found in DB!');
|
||||
process.send({ event: 'ep_finished_loading' });
|
||||
} else {
|
||||
var m, semaphore = 0;
|
||||
for(var el in obj) {
|
||||
semaphore++;
|
||||
m = ml.requireFromString(obj[el], el);
|
||||
db.getEventModuleAuth(el, function(mod) {
|
||||
return function(err, obj) {
|
||||
if(--semaphore === 0) process.send({ event: 'ep_finished_loading' });
|
||||
if(obj && mod.loadCredentials) mod.loadCredentials(JSON.parse(obj));
|
||||
};
|
||||
}(m));
|
||||
log.print('EP', 'Loading Event Module: ' + el);
|
||||
listEventModules[el] = m;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
listMessageActions['event'] = function(args) {
|
||||
var prop = args[1], arrModule = prop.split('->');
|
||||
// var arrModule = obj.module.split('->');
|
||||
if(arrModule.length > 1){
|
||||
var module = listEventModules[arrModule[0]];
|
||||
for(var i = 1; i < arrModule.length; i++) {
|
||||
if(module) module = module[arrModule[i]];
|
||||
}
|
||||
if(module) {
|
||||
log.print('EP', 'Found active event module "' + prop + '", adding it to polling list');
|
||||
//FIXME change this to [module][prop] = module; because like this identical properties get overwritten
|
||||
// also add some on a per user basis information because this should go into a user context for the users
|
||||
// that sat up this rule!
|
||||
listPoll[prop] = module;
|
||||
} else {
|
||||
log.print('EP', 'No property "' + prop + '" found');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
listAdminCommands['loadevent'] = function(args) {
|
||||
ml.loadModule('mod_events', args[2], loadEventCallback);
|
||||
};
|
||||
|
||||
listAdminCommands['loadevents'] = function(args) {
|
||||
ml.loadModules('mod_events', loadEventCallback);
|
||||
};
|
||||
|
||||
listAdminCommands['shutdown'] = function(args) {
|
||||
log.print('EP', 'Shutting down DB Link');
|
||||
isRunning = false;
|
||||
db.shutDown();
|
||||
};
|
||||
|
||||
//TODO this goes into module_manager, this will receive notification about
|
||||
// new loaded/stored event modules and fetch them from the db
|
||||
listMessageActions['cmd'] = function(args) {
|
||||
var func = listAdminCommands[args[1]];
|
||||
if(typeof(func) === 'function') func(args);
|
||||
};
|
||||
|
||||
process.on('message', function(strProps) {
|
||||
var arrProps = strProps.split('|');
|
||||
if(arrProps.length < 2) log.error('EP', 'too few parameter in message!');
|
||||
else {
|
||||
var func = listMessageActions[arrProps[0]];
|
||||
if(func) func(arrProps);
|
||||
}
|
||||
});
|
||||
function loadEventCallback(name, data, mod, auth) {
|
||||
db.storeEventModule(name, data); // store module in db
|
||||
if(auth) db.storeEventModuleAuth(name, auth);
|
||||
listEventModules[name] = mod; // store compiled module for polling
|
||||
}
|
||||
|
||||
function checkRemotes() {
|
||||
for(var prop in listPoll) {
|
||||
try {
|
||||
listPoll[prop](
|
||||
/*
|
||||
* define and immediately call anonymous function with param prop.
|
||||
* This places the value of prop into the context of the callback
|
||||
* and thus doesn't change when the for loop keeps iterating over listPoll
|
||||
*/
|
||||
(function(p) {
|
||||
return function(err, obj) {
|
||||
if(err) {
|
||||
err.additionalInfo = 'module: ' + p;
|
||||
log.error('EP', err);
|
||||
} else {
|
||||
process.send({
|
||||
event: p,
|
||||
eventid: 'polled_' + eId++,
|
||||
data: obj
|
||||
});
|
||||
}
|
||||
};
|
||||
})(prop)
|
||||
);
|
||||
} catch (e) {
|
||||
log.error('EP', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function pollLoop() {
|
||||
if(isRunning) {
|
||||
checkRemotes();
|
||||
setTimeout(pollLoop, 10000);
|
||||
}
|
||||
}
|
||||
|
||||
pollLoop();
|
||||
})();
|
||||
}
|
||||
96
js/http_listener.js
Normal file
96
js/http_listener.js
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
// # HTTP Listener
|
||||
// Isso
|
||||
'use strict';
|
||||
var path = require('path'),
|
||||
express = require('express'),
|
||||
port = express(),
|
||||
log = require('./logging'),
|
||||
qs = require('querystring'),
|
||||
adminHandler, eventHandler, server;
|
||||
|
||||
function init(http_port, funcAdminHandler, funcEvtHandler) {
|
||||
if(!http_port || !funcEvtHandler) {
|
||||
log.error('HL', 'ERROR: either port or eventHandler function not defined!');
|
||||
return;
|
||||
}
|
||||
adminHandler = funcAdminHandler;
|
||||
eventHandler = funcEvtHandler;
|
||||
port.use('/doc/', express.static(path.resolve(__dirname, '..', 'doc/')));
|
||||
port.get('/admin', onAdminCommand);
|
||||
port.post('/pushEvents', onPushEvent);
|
||||
server = port.listen(http_port); // inbound event channel
|
||||
log.print('HL', 'Started listening for http requests on port ' + http_port);
|
||||
}
|
||||
|
||||
function answerHandler(r) {
|
||||
var response = r, hasBeenAnswered = false;
|
||||
function postAnswer(msg) {
|
||||
if(!hasBeenAnswered) {
|
||||
response.write(msg);
|
||||
response.end();
|
||||
hasBeenAnswered = true;
|
||||
}
|
||||
}
|
||||
return {
|
||||
answerSuccess: function(msg) {
|
||||
if(!hasBeenAnswered) response.writeHead(200, { "Content-Type": "text/plain" });
|
||||
postAnswer(msg);
|
||||
},
|
||||
answerError: function(msg) {
|
||||
if(!hasBeenAnswered) response.writeHead(400, { "Content-Type": "text/plain" });
|
||||
postAnswer(msg);
|
||||
},
|
||||
isAnswered: function() { return hasBeenAnswered; }
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles correct event posts, replies thank you.
|
||||
*/
|
||||
function answerSuccess(resp, msg){
|
||||
resp.writeHead(200, { "Content-Type": "text/plain" });
|
||||
resp.write(msg);
|
||||
resp.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles erroneous requests.
|
||||
* @param {Object} msg the error message to be returned
|
||||
*/
|
||||
function answerError(resp, msg) {
|
||||
resp.writeHead(400, { "Content-Type": "text/plain" });
|
||||
resp.write(msg);
|
||||
resp.end();
|
||||
}
|
||||
|
||||
//FIXME this answer handling is a very ugly hack, improve!
|
||||
function onAdminCommand(request, response) {
|
||||
var q = request.query;
|
||||
log.print('HL', 'Received admin request: ' + request.originalUrl);
|
||||
if(q.cmd) {
|
||||
adminHandler(q, answerHandler(response));
|
||||
// answerSuccess(response, 'Thank you, we try our best!');
|
||||
} else answerError(response, 'I\'m not sure about what you want from me...');
|
||||
}
|
||||
|
||||
/**
|
||||
* If a request is made to the server, this function is used to handle it.
|
||||
*/
|
||||
function onPushEvent(request, response) {
|
||||
var body = '';
|
||||
request.on('data', function (data) { body += data; });
|
||||
request.on('end', function () {
|
||||
var obj = qs.parse(body);
|
||||
/* If required event properties are present we process the event */
|
||||
if(obj && obj.event && obj.eventid){
|
||||
answerSuccess(response, 'Thank you for the event (' + obj.event + '[' + obj.eventid + '])!');
|
||||
eventHandler(obj);
|
||||
} else answerError(response, 'Your event was missing important parameters!');
|
||||
});
|
||||
}
|
||||
|
||||
exports.init = init;
|
||||
exports.shutDown = function() {
|
||||
log.print('HL', 'Shutting down HTTP listener');
|
||||
process.exit(); // This is a bit brute force...
|
||||
};
|
||||
37
js/logging.js
Normal file
37
js/logging.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Logging
|
||||
* =======
|
||||
* Functions to handle logging and errors.
|
||||
*/
|
||||
|
||||
// @function print(module, msg)
|
||||
|
||||
/*
|
||||
* Prints a log to stdout.
|
||||
* @param {String} module
|
||||
* @param {String} msg
|
||||
*/
|
||||
exports.print = function(module, msg) {
|
||||
console.log((new Date()).toISOString() + ' | ' + module + ' | ' + msg);
|
||||
};
|
||||
|
||||
// @function error(module, msg)
|
||||
|
||||
/*
|
||||
* Prints a log to stderr.
|
||||
* @param {String} module
|
||||
* @param {Error} err
|
||||
*/
|
||||
exports.error = function(module, err) {
|
||||
var ts = (new Date()).toISOString() + ' | ', ai = '';
|
||||
if(typeof err === 'string') {
|
||||
var e = new Error();
|
||||
if(module) console.error(ts + module + ' | ERROR AND BAD HANDLING: ' + err + '\n' + e.stack);
|
||||
else console.error(ts + '!N/A! | ERROR, BAD HANDLING AND NO MODULE NAME: ' + err + '\n' + e.stack);
|
||||
} else {
|
||||
if(err.addInfo) ai = ' (' + err.addInfo + ')';
|
||||
if(!err.message) err.message = 'UNKNOWN REASON!\n' + err.stack;
|
||||
if(module) console.error(ts + module + ' | ERROR'+ai+': ' + err.message);
|
||||
else console.error(ts + '!N/A! | ERROR AND NO MODULE NAME'+ai+': ' + err.message + '\n' + err.stack);
|
||||
}
|
||||
};
|
||||
69
js/module_loader.js
Normal file
69
js/module_loader.js
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
var fs = require('fs'),
|
||||
path = require('path'),
|
||||
log = require('./logging');
|
||||
|
||||
function requireFromString(src, name, dir) {
|
||||
if(!dir) dir = __dirname;
|
||||
//FIXME load modules only into a safe environment with given modules, no access to whole application
|
||||
var id = path.resolve(dir, name, name + '.js');
|
||||
var m = new module.constructor(id, module);
|
||||
m.paths = module.paths;
|
||||
try {
|
||||
m._compile(src);
|
||||
} catch(err) {
|
||||
err.addInfo = 'during compilation of module ' + name;
|
||||
log.error('LM', err);
|
||||
// log.error('LM', ' during compilation of ' + name + ': ' + err);
|
||||
}
|
||||
return m.exports;
|
||||
}
|
||||
|
||||
function loadModule(directory, name, callback) {
|
||||
try {
|
||||
fs.readFile(path.resolve(__dirname, '..', directory, name, name + '.js'), 'utf8', function (err, data) {
|
||||
if (err) {
|
||||
log.error('LM', 'Loading module file!');
|
||||
return;
|
||||
}
|
||||
var mod = requireFromString(data, name, directory);
|
||||
if(mod && fs.existsSync(path.resolve(__dirname, '..', directory, name, 'credentials.json'))) {
|
||||
fs.readFile(path.resolve(__dirname, '..', directory, name, 'credentials.json'), 'utf8', function (err, auth) {
|
||||
if (err) {
|
||||
log.error('LM', 'Loading credentials file for "' + name + '"!');
|
||||
callback(name, data, mod, null);
|
||||
return;
|
||||
}
|
||||
if(mod.loadCredentials) mod.loadCredentials(JSON.parse(auth));
|
||||
callback(name, data, mod, auth);
|
||||
});
|
||||
} else {
|
||||
// Hand back the name, the string contents and the compiled module
|
||||
callback(name, data, mod, null);
|
||||
}
|
||||
});
|
||||
} catch(err) {
|
||||
log.error('LM', 'Failed loading module "' + name + '"');
|
||||
}
|
||||
}
|
||||
|
||||
function loadModules(directory, callback) {
|
||||
fs.readdir(path.resolve(__dirname, '..', directory), function (err, list) {
|
||||
if (err) {
|
||||
log.error('LM', 'loading modules directory: ' + err);
|
||||
return;
|
||||
}
|
||||
log.print('LM', 'Loading ' + list.length + ' modules from "' + directory + '"');
|
||||
list.forEach(function (file) {
|
||||
fs.stat(path.resolve(__dirname, '..', directory, file), function (err, stat) {
|
||||
if (stat && stat.isDirectory()) {
|
||||
loadModule(directory, file, callback);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
exports.loadModule = loadModule;
|
||||
exports.loadModules = loadModules;
|
||||
exports.requireFromString = requireFromString;
|
||||
|
||||
100
js/module_manager.js
Normal file
100
js/module_manager.js
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
# Module Manager
|
||||
> The module manager takes care of the module and rules loading in the initialization
|
||||
> phase and on user request.
|
||||
|
||||
> Event and Action modules are loaded as strings and stored in the database,
|
||||
> then compiled into node modules and and rules
|
||||
*/
|
||||
var fs = require('fs'),
|
||||
path = require('path'),
|
||||
log = require('./logging'),
|
||||
ml = require('./module_loader'),
|
||||
db = null, funcLoadAction, funcLoadRule;
|
||||
|
||||
function init(db_link, fLoadAction, fLoadRule) {
|
||||
db = db_link;
|
||||
funcLoadAction = fLoadAction;
|
||||
funcLoadRule = fLoadRule;
|
||||
}
|
||||
/*
|
||||
# A First Level Header
|
||||
|
||||
|
||||
A Second Level Header
|
||||
---------------------
|
||||
|
||||
Now is the time for all good men to come to
|
||||
the aid of their country. This is just a
|
||||
regular paragraph.
|
||||
|
||||
The quick brown fox jumped over the lazy
|
||||
dog's back.
|
||||
|
||||
### Header 3
|
||||
|
||||
> This is a blockquote.
|
||||
>
|
||||
> This is the second paragraph in the blockquote.
|
||||
>
|
||||
> ## This is an H2 in a blockquote
|
||||
|
||||
This is the function documentation
|
||||
@param {Object} [args] the optional arguments
|
||||
@param {String} [args.name] the optional name in the arguments
|
||||
*/
|
||||
function loadRulesFile(args, answHandler) {
|
||||
if(!args) args = {};
|
||||
if(!args.name) args.name = 'rules';
|
||||
if(!funcLoadRule) log.error('ML', 'no rule loader function available');
|
||||
else {
|
||||
fs.readFile(path.resolve(__dirname, '..', 'rules', args.name + '.json'), 'utf8', function (err, data) {
|
||||
if (err) {
|
||||
log.error('ML', 'Loading rules file: ' + args.name + '.json');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var arr = JSON.parse(data), txt = '';
|
||||
log.print('ML', 'Loading ' + arr.length + ' rules:');
|
||||
for(var i = 0; i < arr.length; i++) {
|
||||
txt += arr[i].id + ', ';
|
||||
db.storeRule(arr[i].id, JSON.stringify(arr[i]));
|
||||
funcLoadRule(arr[i]);
|
||||
}
|
||||
answHandler.answerSuccess('Yep, loaded rules: ' + txt);
|
||||
} catch (e) {
|
||||
log.error('ML', 'rules file was corrupt! (' + args.name + '.json)');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Object} name
|
||||
* @param {Object} data
|
||||
* @param {Object} mod
|
||||
* @param {String} [auth] The string representation of the auth json
|
||||
*/
|
||||
function loadActionCallback(name, data, mod, auth) {
|
||||
db.storeActionModule(name, data); // store module in db
|
||||
funcLoadAction(name, mod); // hand compiled module back
|
||||
if(auth) db.storeActionModuleAuth(name, auth);
|
||||
}
|
||||
|
||||
function loadActionModule(args, answHandler) {
|
||||
if(args && args.name) {
|
||||
answHandler.answerSuccess('Loading action module ' + args.name + '...');
|
||||
ml.loadModule('mod_actions', args.name, loadActionCallback);
|
||||
}
|
||||
}
|
||||
|
||||
function loadActionModules(args, answHandler) {
|
||||
answHandler.answerSuccess('Loading action modules...');
|
||||
ml.loadModules('mod_actions', loadActionCallback);
|
||||
}
|
||||
|
||||
exports.init = init;
|
||||
exports.loadRulesFile = loadRulesFile;
|
||||
exports.loadActionModule = loadActionModule;
|
||||
exports.loadActionModules = loadActionModules;
|
||||
57
js/queue.js
Normal file
57
js/queue.js
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
// *(will be replaced by a Redis DB queue)*
|
||||
|
||||
// Queue.js
|
||||
// ========
|
||||
//
|
||||
// *A function to represent a queue*
|
||||
|
||||
// *Created by Stephen Morley - http://code.stephenmorley.org/ - and released under
|
||||
// the terms of the CC0 1.0 Universal legal code:*
|
||||
|
||||
// *http://creativecommons.org/publicdomain/zero/1.0/legalcode*
|
||||
|
||||
// *items are added to the end of the queue and removed from the front.*
|
||||
|
||||
exports.Queue = function(){
|
||||
// initialise the queue and offset
|
||||
var queue = [];
|
||||
var offset = 0;
|
||||
this.getLength = function(){
|
||||
// return the length of the queue
|
||||
return (queue.length - offset);
|
||||
};
|
||||
|
||||
/* Returns true if the queue is empty, and false otherwise.
|
||||
*/
|
||||
this.isEmpty = function(){ return (queue.length == 0); };
|
||||
|
||||
/* Enqueues the specified item. The parameter is:
|
||||
* item - the item to enqueue
|
||||
*/
|
||||
this.enqueue = function(item){ queue.push(item); };
|
||||
|
||||
/* Dequeues an item and returns it. If the queue is empty then undefined is
|
||||
* returned.
|
||||
*/
|
||||
this.dequeue = function(){
|
||||
// if the queue is empty, return undefined
|
||||
if (queue.length == 0) return undefined;
|
||||
// store the item at the front of the queue
|
||||
var item = queue[offset];
|
||||
// increment the offset and remove the free space if necessary
|
||||
if (++ offset * 2 >= queue.length){
|
||||
queue = queue.slice(offset);
|
||||
offset = 0;
|
||||
}
|
||||
// return the dequeued item
|
||||
return item;
|
||||
};
|
||||
|
||||
/* Returns the item at the front of the queue (without dequeuing it). If the
|
||||
* queue is empty then undefined is returned.
|
||||
*/
|
||||
this.peek = function(){
|
||||
// return the item at the front of the queue
|
||||
return (queue.length > 0 ? queue[offset] : undefined);
|
||||
};
|
||||
};
|
||||
81
js/server.js
Normal file
81
js/server.js
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/** @module rules_server */
|
||||
'use strict';
|
||||
/*
|
||||
A First Level Header
|
||||
====================
|
||||
|
||||
A Second Level Header
|
||||
---------------------
|
||||
|
||||
Now is the time for all good men to come to
|
||||
the aid of their country. This is just a
|
||||
regular paragraph.
|
||||
|
||||
The quick brown fox jumped over the lazy
|
||||
dog's back.
|
||||
|
||||
### Header 3
|
||||
|
||||
> This is a blockquote.
|
||||
>
|
||||
> This is the second paragraph in the blockquote.
|
||||
>
|
||||
> ## This is an H2 in a blockquote
|
||||
*/
|
||||
var http_listener = require('./http_listener'),
|
||||
db = require('./db_interface'),
|
||||
engine = require('./engine'),
|
||||
mm = require('./module_manager'),
|
||||
log = require('./logging'),
|
||||
fs = require('fs'),
|
||||
path = require('path'),
|
||||
objCmds = {
|
||||
'loadrules': mm.loadRulesFile,
|
||||
'loadaction': mm.loadActionModule,
|
||||
'loadactions': mm.loadActionModules,
|
||||
'loadevent': engine.loadEventModule,
|
||||
'loadevents': engine.loadEventModules,
|
||||
'shutdown': shutDown,
|
||||
'restart': null //TODO implement
|
||||
};
|
||||
|
||||
function handleAdminCommands(args, answHandler) {
|
||||
if(args && args.cmd) {
|
||||
var func = objCmds[args.cmd];
|
||||
if(func) func(args, answHandler);
|
||||
} else log.print('RS', 'No command in request');
|
||||
setTimeout(function(ah) {
|
||||
answHandler = ah;
|
||||
return function() {
|
||||
if(!answHandler.isAnswered()) answHandler.answerError('Not handeled...');
|
||||
};
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
function shutDown(args, answHandler) {
|
||||
answHandler.answerSuccess('Goodbye!');
|
||||
log.print('RS', 'Received shut down command!');
|
||||
engine.shutDown();
|
||||
http_listener.shutDown();
|
||||
}
|
||||
|
||||
fs.readFile(path.resolve(__dirname, '..', 'config', 'config.json'), 'utf8', function (err, data) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
log.error('RS', 'Loading config file');
|
||||
return;
|
||||
}
|
||||
var config = JSON.parse(data);
|
||||
if(!config.http_port || !config.db_port || !config.crypto_key) {
|
||||
log.error('RS', 'you forgot to define either http_port, db_port, crypto_key, or even all of them!');
|
||||
} else {
|
||||
log.print('RS', 'Initialzing DB');
|
||||
db.init(config.db_port, config.crypto_key, function() {
|
||||
engine.init(db, config.db_port, config.crypto_key);
|
||||
});
|
||||
log.print('RS', 'Initialzing http listener');
|
||||
http_listener.init(config.http_port, handleAdminCommands, engine.pushEvent);
|
||||
log.print('RS', 'Initialzing module manager');
|
||||
mm.init(db, engine.loadActionModule, engine.loadRule);
|
||||
}
|
||||
});
|
||||
185
mod_actions/probinder/probinder.js
Normal file
185
mod_actions/probinder/probinder.js
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
'use strict';
|
||||
|
||||
var needle = require('needle');
|
||||
/**
|
||||
* ProBinder ACTION MODULE
|
||||
*/
|
||||
|
||||
var urlService = 'https://probinder.com/service/',
|
||||
credentials = null;
|
||||
|
||||
function loadCredentials(cred) {
|
||||
if(!cred || !cred.username || !cred.password) {
|
||||
console.error('ERROR: ProBinder AM credentials file corrupt');
|
||||
} else {
|
||||
credentials = cred;
|
||||
console.log('Successfully loaded credentials for ProBinder AM');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset eventually loaded credentials
|
||||
*/
|
||||
function purgeCredentials() {
|
||||
credentials = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify whether the arguments match the existing credentials.
|
||||
* @param {String} username the username
|
||||
* @param {String} password the password
|
||||
*/
|
||||
function verifyCredentials(username, password) {
|
||||
if(!credentials) return false;
|
||||
return credentials.username === username
|
||||
&& credentials.password === password;
|
||||
};
|
||||
|
||||
/**
|
||||
* Call the ProBinder service with the given parameters.
|
||||
* @param {Object} args the required function arguments object
|
||||
* @param {Object} [args.data] the data to be posted
|
||||
* @param {String} args.service the required service identifier to be appended to the url
|
||||
* @param {String} args.method the required method identifier to be appended to the url
|
||||
* @param {function} [args.succes] the function to be called on success,
|
||||
* receives the response body String or Buffer.
|
||||
* @param {function} [args.error] the function to be called on error,
|
||||
* receives an error, an http.ClientResponse object and a response body
|
||||
* String or Buffer.
|
||||
*/
|
||||
function call(args) {
|
||||
if(!args || !args.service || !args.method) {
|
||||
console.error('ERROR in ProBinder AM call: Too few arguments!');
|
||||
return null;
|
||||
}
|
||||
if(credentials){
|
||||
needle.post(urlService + args.service + '/' + args.method,
|
||||
args.data,
|
||||
credentials,
|
||||
function(error, response, body) { // The callback
|
||||
if(!error && response && response.statusCode == 200) {
|
||||
if(args && args.success) args.success(body);
|
||||
} else {
|
||||
if(args && args.error) args.error(error, response, body);
|
||||
else console.error('Error during ProBinder AM call: ' + error.message);
|
||||
}
|
||||
}
|
||||
);
|
||||
} else console.error('ERROR ProBinder AM: request or credentials object not ready!');
|
||||
};
|
||||
|
||||
/**
|
||||
* Calls the user's unread content service.
|
||||
* @param {Object} [args] the optional object containing the success
|
||||
* and error callback methods
|
||||
* @param {function} [args.succes] refer to call function
|
||||
* @param {function} [args.error] refer to call function
|
||||
*/
|
||||
function getUnreadContents(args) {
|
||||
if(!args) args = {};
|
||||
call({
|
||||
service: '36',
|
||||
method: 'unreadcontent',
|
||||
success: args.success,
|
||||
error: args.error
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Calls the content get service with the content id and the service id provided.
|
||||
* @param {Object} args the object containing the service id and the content id,
|
||||
* success and error callback methods
|
||||
* @param {String} args.serviceid the service id that is able to process this content
|
||||
* @param {String} args.contentid the content id
|
||||
* @param {function} [args.succes] to be called on success, receives the service, content
|
||||
* and user id's along with the content
|
||||
* @param {function} [args.error] refer to call function
|
||||
*/
|
||||
function getContent(args){
|
||||
if(!args || !args.serviceid || !args.contentid) {
|
||||
console.error('ERROR in ProBinder AM getContent: Too few arguments!');
|
||||
return null;
|
||||
}
|
||||
call({
|
||||
service: '2',
|
||||
method: 'get',
|
||||
data: { id: args.contentid, service: args.serviceid },
|
||||
success: args.success,
|
||||
error: args.error
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Does everything to post something in a binder
|
||||
* @param {Object} args the object containing the content
|
||||
* @param {String} args.content the content to be posted
|
||||
*/
|
||||
function newContent(args){
|
||||
if(!args) args = {};
|
||||
if(!args.content) args.content = 'Rule#0 says you received a new mail!';
|
||||
call({
|
||||
service: '27',
|
||||
method: 'save',
|
||||
data: {
|
||||
companyId: '961',
|
||||
context: '17936',
|
||||
text: args.content
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Does everything to post a file info in a binder tabe
|
||||
* @param {Object} args the object containing the content
|
||||
* @param {String} args.service the content service
|
||||
* @param {String} args.id the content id
|
||||
*/
|
||||
function makeFileEntry(args){
|
||||
if(!args || !args.service || !args.id) {
|
||||
console.error('ERROR in ProBinder AM makeFileEntry: Too few arguments!');
|
||||
return null;
|
||||
}
|
||||
getContent({
|
||||
serviceid: args.service,
|
||||
contentid: args.id,
|
||||
success: function(data) {
|
||||
call({
|
||||
service: '27',
|
||||
method: 'save',
|
||||
data: {
|
||||
companyId: '961',
|
||||
context: '17936',
|
||||
text: 'New file (' + data.title + ') in tab \"' + data.context[0].name
|
||||
+ '\", find it <a href=\"https://probinder.com/file/' + data.fileIds[0] + '\">here</a>!'
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Does everything to post something in a binder
|
||||
* @param {Object} args the object containing the content
|
||||
* @param {String} args.content the content to be posted
|
||||
*/
|
||||
function setRead(args){
|
||||
call({
|
||||
service: '2',
|
||||
method: 'setread',
|
||||
data: {
|
||||
id: args.id
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
exports.loadCredentials = loadCredentials;
|
||||
exports.purgeCredentials = purgeCredentials;
|
||||
exports.verifyCredentials = verifyCredentials;
|
||||
exports.call = call;
|
||||
exports.getUnreadContents = getUnreadContents;
|
||||
// exports.getBinderTabContents = getBinderTabContents;
|
||||
exports.getContent = getContent;
|
||||
exports.newContent = newContent;
|
||||
exports.makeFileEntry = makeFileEntry;
|
||||
exports.setRead = setRead;
|
||||
|
||||
3
mod_actions/testing/test.json
Normal file
3
mod_actions/testing/test.json
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"test": "should stay"
|
||||
}
|
||||
3
mod_actions/testing/test.json_BACKUP
Normal file
3
mod_actions/testing/test.json_BACKUP
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"test": "should stay"
|
||||
}
|
||||
18
mod_actions/testing/testing.js
Normal file
18
mod_actions/testing/testing.js
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
var fs = require('fs'),
|
||||
path = require('path');
|
||||
|
||||
/*
|
||||
// Hacking my own system...
|
||||
console.log(module.parent.parent.children[0].exports.getEventModuleAuth('probinder',
|
||||
function(err, obj) {console.log(obj);}));
|
||||
*/
|
||||
|
||||
//FIXME do not try to delete a file and rely on it to exist, rather try to create it first! o check for its existance and then delete it
|
||||
try {
|
||||
fs.unlinkSync(path.resolve(__dirname, 'event_modules', 'malicious', 'test.json'));
|
||||
console.error('VERY BAD! NEVER START THIS SERVER WITH A USER THAT HAS WRITE RIGHTS ANYWHERE!!!');
|
||||
} catch (err) {
|
||||
console.log('VERY GOOD! USERS CANNOT WRITE ON YOUR DISK!');
|
||||
|
||||
}
|
||||
throw new Error('Testing your error handling');
|
||||
31
mod_actions/webapi/webapi.js
Normal file
31
mod_actions/webapi/webapi.js
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
'use strict';
|
||||
|
||||
var needle = require('needle');
|
||||
|
||||
/**
|
||||
* Call any arbitrary webAPI.
|
||||
* @param {Object} args the required function arguments object
|
||||
* @param {String} args.url the required webAPI url
|
||||
* @param {Object} [args.data] the data to be posted
|
||||
* @param {Object} [args.credentials] optional credentials
|
||||
* @param {String} [args.credentials.username] optional username
|
||||
* @param {String} [args.credentials.password] optional password
|
||||
*/
|
||||
function call(args) {
|
||||
if(!args || !args.url) {
|
||||
console.error('ERROR in WebAPI AM call: Too few arguments!');
|
||||
return null;
|
||||
}
|
||||
needle.post(args.url,
|
||||
args.data,
|
||||
args.credentials,
|
||||
function(error, response, body) {
|
||||
if (!error) console.log('Successful webAPI AM call to ' + args.url);
|
||||
else console.error('Error during webAPI AM call to ' + args.url
|
||||
+ ': ' + error.message);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
exports.call = call;
|
||||
|
||||
51
mod_actions/wikipedia/wikipedia.js
Normal file
51
mod_actions/wikipedia/wikipedia.js
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
'use strict';
|
||||
|
||||
var needle = require('needle');
|
||||
|
||||
var urlService = 'http://en.wikipedia.org/w/api.php?format=json&action=query&prop=extracts&exintro&exchars=200&explaintext&titles=Computer%20science';
|
||||
//http://www.mediawiki.org/wiki/API:Search
|
||||
/*
|
||||
* 1. try to get title right away: http://en.wikipedia.org/w/api.php?format=json&action=query&prop=extracts&exintro&titles=Computer%20science
|
||||
* if success add it in comment, else issue search:
|
||||
* 2. http://en.wikipedia.org/w/api.php?format=json&action=query&list=search&srwhat=text&srsearch=cs&srlimit=3
|
||||
* get snippets and add comments
|
||||
*
|
||||
*/
|
||||
|
||||
function search(text) {
|
||||
var ret = requestTitle(text);
|
||||
if(!ret) ret = searchText(text);
|
||||
}
|
||||
|
||||
function requestTitle(title, cbDone, cbFail) {
|
||||
needle.get('http://en.wikipedia.org/w/api.php?format=json&action=query&prop=extracts&exintro&exchars=200&explaintext&titles=' + encodeURI(title),
|
||||
function handleResponse(error, response, body) {
|
||||
obj = JSON.parse(body);
|
||||
if(error || response.statusCode != 200 || !obj || !obj.query || !obj.query.pages || obj.query.pages['-1']) {
|
||||
searchText(title, cbDone, cbFail);
|
||||
} else {
|
||||
var pgs = obj.query.pages;
|
||||
for(var el in pgs) console.log('found: ' + pgs[el].title);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function searchText(text, cbDone, cbFail) {
|
||||
needle.get('http://en.wikipedia.org/w/api.php?format=json&action=query&list=search&srwhat=text&srlimit=3&srsearch=' + encodeURI(text),
|
||||
function handleResponse(error, response, body) {
|
||||
obj = JSON.parse(body);
|
||||
if(error || response.statusCode != 200 || !obj || !obj.query || !obj.query.search || obj.query.search.length == 0) {
|
||||
console.log('nothing found for this tag');
|
||||
if(cbFail) cbFail('nothing found for this tag');
|
||||
} else {
|
||||
var srch = obj.query.search;
|
||||
for(var i = 0; i < srch.length; i++) {
|
||||
console.log('found: ' + srch[i].title + ' [' + srch[i].snippet + ']');
|
||||
if(cbDone) cbDone('found: ' + srch[i].title + ' [' + srch[i].snippet + ']');
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
57
mod_events/emailyak/emailyak.js
Normal file
57
mod_events/emailyak/emailyak.js
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
'use strict';
|
||||
|
||||
var needle = require('request');
|
||||
|
||||
/*
|
||||
* EmailYak EVENT MODULE
|
||||
*/
|
||||
|
||||
var credentials = null;
|
||||
|
||||
function loadCredentials(cred) {
|
||||
if(!cred || !cred.key) {
|
||||
console.error('ERROR in EmailYak EM: credentials file corrupt');
|
||||
} else {
|
||||
credentials = cred;
|
||||
console.log('Successfully loaded EmailYak EM credentials');
|
||||
}
|
||||
}
|
||||
|
||||
//FIXME every second mail gets lost?
|
||||
// 1) for http.request options, set Connection:keep-alive
|
||||
// 2) set Agent.maxSockets = 1024 (so more connection to play around
|
||||
// with )
|
||||
// 3) very critical: DO a timeout for the http.request.
|
||||
//
|
||||
// e.g.
|
||||
// var responseHdr = function (clientResponse) {
|
||||
// if (clientResposne) {
|
||||
//
|
||||
// } else {
|
||||
// clientRequest.abort();
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// var timeoutHdr = setTimeout(function() {
|
||||
// clientRequest.emit('req-timeout');
|
||||
// }, 5000); // timeout after 5 secs
|
||||
//
|
||||
// clientRequest.on("req-timeout", responseHdr);
|
||||
// clientRequest.on('error', function(e) {
|
||||
// clearTimeout(timeoutHdr);
|
||||
// console.error('Ok.. clientrequest error' + myCounter);
|
||||
// next({err:JSON.stringify(e)});
|
||||
// });
|
||||
function newMail(callback) { //FIXME not beautiful to have to set prop each time here
|
||||
needle.get('https://api.emailyak.com/v1/' + credentials.key + '/json/get/new/email/',
|
||||
function (error, response, body){
|
||||
if (!error && response.statusCode == 200) {
|
||||
var mails = JSON.parse(body).Emails;
|
||||
for(var i = 0; i < mails.length; i++) callback(mails[i]);
|
||||
} else console.error('ERROR in EmailYak EM newMail: ' + error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
exports.loadCredentials = loadCredentials;
|
||||
exports.newMail = newMail;
|
||||
75
mod_events/probinder/probinder.js
Normal file
75
mod_events/probinder/probinder.js
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
'use strict';
|
||||
|
||||
var needle = require('needle');
|
||||
|
||||
/*
|
||||
* ProBinder EVENT MODULE
|
||||
*/
|
||||
|
||||
var request = require('needle'),
|
||||
urlService = 'https://probinder.com/service/',
|
||||
credentials = null;
|
||||
|
||||
function loadCredentials(cred) {
|
||||
if(!cred || !cred.username || !cred.password) {
|
||||
console.error('ERROR: ProBinder EM credentials file corrupt');
|
||||
} else {
|
||||
credentials = cred;
|
||||
console.log('Successfully loaded credentials for ProBinder EM');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the ProBinder service with the given parameters.
|
||||
* @param {Object} args the required function arguments object
|
||||
* @param {Object} [args.data] the data to be posted
|
||||
* @param {String} args.service the required service identifier to be appended to the url
|
||||
* @param {String} args.method the required method identifier to be appended to the url
|
||||
* @param {function} [args.succes] the function to be called on success,
|
||||
* receives the response body String or Buffer.
|
||||
* @param {function} [args.error] the function to be called on error,
|
||||
* receives an error, an http.ClientResponse object and a response body
|
||||
* String or Buffer.
|
||||
*/
|
||||
function call(args) {
|
||||
if(!args || !args.service || !args.method) {
|
||||
console.error('ERROR in ProBinder EM call: Too few arguments!');
|
||||
return null;
|
||||
}
|
||||
if(credentials){
|
||||
needle.post(urlService + args.service + '/' + args.method,
|
||||
args.data,
|
||||
credentials,
|
||||
function(error, response, body) { // The callback
|
||||
if (!error) { //) && response.statusCode == 200) {
|
||||
if(args && args.success) args.success(body);
|
||||
} else {
|
||||
if(args && args.error) args.error(error, response, body);
|
||||
else console.error('ERROR during ProBinder EM call: ' + error.message);
|
||||
}
|
||||
}
|
||||
);
|
||||
} else console.error('ProBinder EM request or credentials object not ready!');
|
||||
};
|
||||
|
||||
/**
|
||||
* Calls the user's unread content service.
|
||||
* @param {Object} [args] the optional object containing the success
|
||||
* and error callback methods
|
||||
*/
|
||||
function unread(callback) { //FIXME ugly prop in here
|
||||
|
||||
call({
|
||||
service: '36',
|
||||
method: 'unreadcontent',
|
||||
success: function(data) {
|
||||
for(var i = 0; i < data.length; i++) callback(null, data[i]);
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
exports.loadCredentials = loadCredentials;
|
||||
exports.call = call;
|
||||
exports.unread = unread;
|
||||
|
||||
3
mod_events/testing/test.json
Normal file
3
mod_events/testing/test.json
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"test": "should stay"
|
||||
}
|
||||
3
mod_events/testing/test.json_BACKUP
Normal file
3
mod_events/testing/test.json_BACKUP
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"test": "should stay"
|
||||
}
|
||||
20
mod_events/testing/testing.js
Normal file
20
mod_events/testing/testing.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
var fs = require('fs'),
|
||||
path = require('path');
|
||||
|
||||
/*
|
||||
// Hacking my own system...
|
||||
console.log(module.parent.parent.children[0].exports.getEventModuleAuth('probinder',
|
||||
function(err, obj) {console.log(obj);}));
|
||||
*/
|
||||
|
||||
//FIXME do not try to delete a file and rely on it to exist, rather try to create it first! o check for its existance and then delete it
|
||||
try {
|
||||
fs.unlinkSync(path.resolve(__dirname, 'event_modules', 'malicious', 'test.json'));
|
||||
console.error('VERY BAD! NEVER START THIS SERVER WITH A USER THAT HAS WRITE RIGHTS ANYWHERE!!!');
|
||||
} catch (err) {
|
||||
console.log('VERY GOOD! USERS CANNOT WRITE ON YOUR DISK!');
|
||||
|
||||
}
|
||||
|
||||
//FIXME add several standard methods for testing (also rules to be inserted during testing)
|
||||
throw new Error('Testing your error handling');
|
||||
31
mod_events/weather/weather.js
Normal file
31
mod_events/weather/weather.js
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
var needle = require('needle');
|
||||
|
||||
var urlService = 'http://api.openweathermap.org/data/2.5/weather',
|
||||
credentials,
|
||||
old_temp;
|
||||
|
||||
function loadCredentials(cred) {
|
||||
if(!cred || !cred.key) {
|
||||
console.error('ERROR in Weather EM: Weather event module credentials file corrupt');
|
||||
} else {
|
||||
credentials = cred;
|
||||
console.log('Successfully loaded credentials for Weather EM');
|
||||
}
|
||||
}
|
||||
|
||||
function tempRaisesAbove(prop, degree) {
|
||||
needle.get(urlService + '?APPID=' + credentials.key + '&q=Basel',
|
||||
function(error, response, body) { // The callback
|
||||
if (!error) { //) && response.statusCode == 200) {
|
||||
if(args && args.success) args.success(body);
|
||||
} else {
|
||||
if(args && args.error) args.error(error, response, body);
|
||||
else console.error('Error during Weather EM tempRaisesAbove: ' + error.message);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
exports.tempRaisesAbove = tempRaisesAbove;
|
||||
exports.loadCredentials = loadCredentials;
|
||||
26
package.json
Normal file
26
package.json
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"name": "webapi-eca",
|
||||
"author": "Dominic Bosch",
|
||||
"description": "An ECA inference engine as a middleware between Web API's",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"repository": {
|
||||
"type" : "git",
|
||||
"url" : "https://github.com/dominicbosch/webapi-eca.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"express": "3.4.0",
|
||||
"groc": "0.6.1",
|
||||
"needle": "0.6.1",
|
||||
"nodeunit": "0.8.2",
|
||||
"redis": "0.9.0",
|
||||
"request": "2.27.0"
|
||||
},
|
||||
"__comment": {
|
||||
"dependencies": {
|
||||
"diff": "1.0.5",
|
||||
"socket.io": "0.9.16",
|
||||
"contextify": "0.1.6"
|
||||
}
|
||||
}
|
||||
}
|
||||
73
rules/rules.json
Normal file
73
rules/rules.json
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
[
|
||||
{
|
||||
"id": "rule_1",
|
||||
"event": "mail",
|
||||
"actions": [
|
||||
{
|
||||
"module": "probinder->newContent",
|
||||
"arguments": {
|
||||
"content": "Rule#1: $X.subject"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rule_2",
|
||||
"event": "mail",
|
||||
"condition": { "sender": "sender2" },
|
||||
"actions": [
|
||||
{
|
||||
"module": "probinder->newContent",
|
||||
"arguments": {
|
||||
"content": "Rule#2: $X.subject"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rule_emailyak",
|
||||
"event": "yakmail",
|
||||
"condition": { "FromAddress": "dominic.bosch.db@gmail.com" },
|
||||
"actions": [
|
||||
{
|
||||
"module": "probinder->newContent",
|
||||
"arguments": {
|
||||
"content": "Received from EmailYak: $X.textbody"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rule_pull_emailyak",
|
||||
"event": "emailyak->newMail",
|
||||
"condition": { "FromAddress": "dominic.bosch.db@gmail.com" },
|
||||
"actions": [
|
||||
{
|
||||
"module": "probinder->newContent",
|
||||
"arguments": {
|
||||
"content": "Received from EmailYak: $X.textbody"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "rule_pull_probinder",
|
||||
"event": "probinder->unread",
|
||||
"condition": { "serviceId": "32" },
|
||||
"actions": [
|
||||
{
|
||||
"module": "probinder->makeFileEntry",
|
||||
"arguments": {
|
||||
"service": "$X.serviceId",
|
||||
"id": "$X.id"
|
||||
}
|
||||
},
|
||||
{
|
||||
"module": "probinder->setRead",
|
||||
"arguments": {
|
||||
"id": "$X.id"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
3
run_server.sh
Executable file
3
run_server.sh
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
node $DIR/js/webapi_eca_server > $DIR/server.log 2>&1 &
|
||||
1
run_tests.js
Normal file
1
run_tests.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
require('nodeunit').reporters.default.run(['testing']);
|
||||
270
testing/mod_db_interface.js
Normal file
270
testing/mod_db_interface.js
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
|
||||
exports.testUnit_DB = function(test){
|
||||
test.ok(true, "db");
|
||||
test.done();
|
||||
};
|
||||
|
||||
// // # DB Interface
|
||||
// // Handles the connection to the database and provides functionalities for
|
||||
// // event/action modules, rules and the encrypted storing of authentication tokens.
|
||||
//
|
||||
// // ## General
|
||||
// // General functionality as a wrapper for the module holds initialization,
|
||||
// // encryption/decryption, the retrieval of modules and shut down.
|
||||
// // Modules of the same group, e.g. action modules are registered in an unordered
|
||||
// // set in the database, from where they can be retrieved again. For example a new
|
||||
// // action module has its ID (e.g 'probinder') first registered in the set
|
||||
// // 'action_modules' and then stored in the db with the key 'action\_module\_' + ID
|
||||
// // (e.g. action\_module\_probinder).
|
||||
// 'use strict';
|
||||
// var redis = require('redis'),
|
||||
// crypto = require('crypto'),
|
||||
// log = require('./logging'),
|
||||
// crypto_key, db;
|
||||
//
|
||||
//
|
||||
// // @function init()
|
||||
//
|
||||
// /*
|
||||
// * Initializes the DB connection. Requires a port where the DB listens to requests
|
||||
// * and a key that is used for encryptions.
|
||||
// * @param {int} db_port
|
||||
// */
|
||||
// exports.init = function(db_port, key, cbDone){
|
||||
// if(!db_port || !key) {
|
||||
// log.error('DB', 'No DB port or cipher key defined!');
|
||||
// return null;
|
||||
// }
|
||||
// crypto_key = key;
|
||||
// db = redis.createClient(db_port);
|
||||
// db.on("error", function (err) {
|
||||
// log.error('DB', ' Message from DB: ' + err);
|
||||
// });
|
||||
// if(cbDone) cbDone();
|
||||
// };
|
||||
//
|
||||
// /**
|
||||
// * ### encrypt
|
||||
// */
|
||||
// function encrypt(plainText) {
|
||||
// if(!plainText) return null;
|
||||
// try {
|
||||
// var enciph = crypto.createCipher('aes-256-cbc', crypto_key);
|
||||
// var et = enciph.update(plainText, 'utf8', 'base64') + enciph.final('base64');
|
||||
// log.print('DB', 'Encrypted credentials into: ' + et);
|
||||
// return et;
|
||||
// } catch (err) {
|
||||
// log.error('DB', 'in encrypting: ' + err);
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * ### decrypt
|
||||
// */
|
||||
// function decrypt(crypticText) {
|
||||
// if(!crypticText) return null;
|
||||
// try {
|
||||
// var deciph = crypto.createDecipher('aes-256-cbc', crypto_key);
|
||||
// return deciph.update(crypticText, 'base64', 'utf8') + deciph.final('utf8');
|
||||
// } catch (err) {
|
||||
// log.error('DB', 'in decrypting: ' + err);
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * ### replyHandler
|
||||
// * Abstraction answer handling for simple information replies from the DB.
|
||||
// * @param {String} action the action to be displayed in the output string.
|
||||
// */
|
||||
// function replyHandler(action) {
|
||||
// return function(err, reply) {
|
||||
// if(err) log.error('DB', ' during "' + action + '": ' + err);
|
||||
// else log.print('DB', action + ': ' + reply);
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * ### getSetRecords
|
||||
// * The general structure for modules is that the key is stored in a set.
|
||||
// * By fetching all set entries we can then fetch all modules, which is
|
||||
// * automated in this function.
|
||||
// *
|
||||
// * @param {String} set the set name how it is stored in the DB
|
||||
// * @param {function} funcSingle the function that fetches single entries from the DB
|
||||
// * @param {function} callback the function to be called on success or error, receives
|
||||
// * arguments (err, obj)
|
||||
// */
|
||||
// function getSetRecords(set, funcSingle, callback) {
|
||||
// db.smembers(set, function(err, reply) {
|
||||
// if(err) log.error('DB', 'fetching ' + set + ': ' + err);
|
||||
// else {
|
||||
// if(reply.length === 0) {
|
||||
// callback(null, null);
|
||||
// } else {
|
||||
// var semaphore = reply.length, objReplies = {};
|
||||
// setTimeout(function() {
|
||||
// if(semaphore > 0) {
|
||||
// callback('Timeout fetching ' + set, null);
|
||||
// }
|
||||
// }, 1000);
|
||||
// for(var i = 0; i < reply.length; i++){
|
||||
// funcSingle(reply[i], function(prop) {
|
||||
// return function(err, reply) {
|
||||
// if(err) log.error('DB', ' fetching single element: ' + prop);
|
||||
// else {
|
||||
// objReplies[prop] = reply;
|
||||
// if(--semaphore === 0) callback(null, objReplies);
|
||||
// }
|
||||
// };
|
||||
// }(reply[i]));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// // @method shutDown()
|
||||
//
|
||||
// // Shuts down the db link.
|
||||
// exports.shutDown = function() { db.quit(); };
|
||||
//
|
||||
// // ## Action Modules
|
||||
//
|
||||
// /**
|
||||
// * ### storeActionModule
|
||||
// * Store a string representation of an action module in the DB.
|
||||
// * @param {String} id the unique identifier of the module
|
||||
// * @param {String} data the string representation
|
||||
// */
|
||||
// exports.storeActionModule = function(id, data) {
|
||||
// db.sadd('action_modules', id, replyHandler('storing action module key ' + id));
|
||||
// db.set('action_module_' + id, data, replyHandler('storing action module ' + id));
|
||||
// };
|
||||
//
|
||||
// /**
|
||||
// * ### getActionModule(id, callback)
|
||||
// * Query the DB for an action module.
|
||||
// * @param {String} id the module id
|
||||
// * @param {function} callback the callback to receive the answer (err, obj)
|
||||
// */
|
||||
// exports.getActionModule = function(id, callback) {
|
||||
// if(callback) db.get('action_module_' + id, callback);
|
||||
// };
|
||||
//
|
||||
// /**
|
||||
// * ### getActionModules(callback)
|
||||
// * Fetch all action modules.
|
||||
// * @param {function} callback the callback to receive the answer (err, obj)
|
||||
// */
|
||||
// exports.getActionModules = function(callback) {
|
||||
// getSetRecords('action_modules', exports.getActionModule, callback);
|
||||
// };
|
||||
//
|
||||
// /**
|
||||
// * storeActionModuleAuth(id, data)
|
||||
// * Store a string representation of the authentication parameters for an action module.
|
||||
// * @param {String} id the unique identifier of the module
|
||||
// * @param {String} data the string representation
|
||||
// */
|
||||
// exports.storeActionModuleAuth = function(id, data) {
|
||||
// if(data) {
|
||||
// db.sadd('action_modules_auth', id, replyHandler('storing action module auth key ' + id));
|
||||
// db.set('action_module_' + id +'_auth', encrypt(data), replyHandler('storing action module auth ' + id));
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// /**
|
||||
// * ### getActionModuleAuth(id, callback)
|
||||
// * Query the DB for an action module authentication token.
|
||||
// * @param {String} id the module id
|
||||
// * @param {function} callback the callback to receive the answer (err, obj)
|
||||
// */
|
||||
// exports.getActionModuleAuth = function(id, callback) {
|
||||
// if(callback) db.get('action_module_' + id + '_auth', function(err, txt) { callback(err, decrypt(txt)); });
|
||||
// };
|
||||
//
|
||||
// // ## Event Modules
|
||||
//
|
||||
// /**
|
||||
// * ### storeEventModule(id, data)
|
||||
// * Store a string representation of an event module in the DB.
|
||||
// * @param {String} id the unique identifier of the module
|
||||
// * @param {String} data the string representation
|
||||
// */
|
||||
// exports.storeEventModule = function(id, data) {
|
||||
// db.sadd('event_modules', id, replyHandler('storing event module key ' + id));
|
||||
// db.set('event_module_' + id, data, replyHandler('storing event module ' + id));
|
||||
// };
|
||||
//
|
||||
// /**
|
||||
// * ### getEventModule(id, callback)
|
||||
// * Query the DB for an event module.
|
||||
// * @param {String} id the module id
|
||||
// * @param {function} callback the callback to receive the answer (err, obj)
|
||||
// */
|
||||
// exports.getEventModule = function(id, callback) {
|
||||
// if(callback) db.get('event_module_' + id, callback);
|
||||
// };
|
||||
//
|
||||
// /**
|
||||
// * ### getEventModules(callback)
|
||||
// * Fetch all event modules.
|
||||
// * @param {function} callback the callback that receives the arguments (err, obj)
|
||||
// */
|
||||
// exports.getEventModules = function(callback) {
|
||||
// getSetRecords('event_modules', exports.getEventModule, callback);
|
||||
// };
|
||||
//
|
||||
// /**
|
||||
// * ### storeEventModuleAuth(id, data)
|
||||
// * Store a string representation of he authentication parameters for an event module.
|
||||
// * @param {String} id the unique identifier of the module
|
||||
// * @param {String} data the string representation
|
||||
// */
|
||||
// exports.storeEventModuleAuth = function(id, data) {
|
||||
// if(data) {
|
||||
// db.sadd('event_modules_auth', id, replyHandler('storing event module auth key ' + id));
|
||||
// db.set('event_module_' + id +'_auth', encrypt(data), replyHandler('storing event module auth ' + id));
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// // @method getEventModuleAuth(id, callback)
|
||||
//
|
||||
// // Query the DB for an event module authentication token.
|
||||
// // @param {String} id the module id
|
||||
// // @param {function} callback the callback to receive the answer (err, obj)
|
||||
// exports.getEventModuleAuth = function(id, callback) {
|
||||
// if(callback) db.get('event_module_' + id +'_auth', function(err, txt) { callback(err, decrypt(txt)); });
|
||||
// };
|
||||
//
|
||||
// // ## Rules
|
||||
//
|
||||
// // @method storeRule(id, data)
|
||||
//
|
||||
// // Store a string representation of a rule in the DB.
|
||||
// // @param {String} id the unique identifier of the rule
|
||||
// // @param {String} data the string representation
|
||||
// exports.storeRule = function(id, data) {
|
||||
// db.sadd('rules', id, replyHandler('storing rule key ' + id));
|
||||
// db.set('rule_' + id, data, replyHandler('storing rule ' + id));
|
||||
// };
|
||||
//
|
||||
// // @method getRule(id, callback)
|
||||
//
|
||||
// // Query the DB for a rule.
|
||||
// // @param {String} id the rule id
|
||||
// // @param {function} callback the callback to receive the answer (err, obj)
|
||||
// exports.getRule = function(id, callback) {
|
||||
// db.get('rule_' + id, callback);
|
||||
// };
|
||||
//
|
||||
// // @method getRules(callback)
|
||||
//
|
||||
// // Fetch all rules from the database.
|
||||
// // @param {function} callback the callback to receive the answer (err, obj)
|
||||
// exports.getRules = function(callback) {
|
||||
// getSetRecords('rules', exports.getRule, callback);
|
||||
// };
|
||||
//
|
||||
282
testing/mod_engine.js
Normal file
282
testing/mod_engine.js
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
|
||||
exports.setUp = function (callback) {
|
||||
console.log('setup in module');
|
||||
callback();
|
||||
};
|
||||
exports.testUnit_ENG = function(test){
|
||||
test.ok(true, "ENG");
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.group = {
|
||||
setUp: function (callback) {
|
||||
console.log('setup in group');
|
||||
callback();
|
||||
},
|
||||
test2: function (test) {
|
||||
test.ok(true, "ENG");
|
||||
test.done();
|
||||
},
|
||||
test3: function (test) {
|
||||
test.ok(true, "ENG");
|
||||
test.done();
|
||||
}
|
||||
};
|
||||
|
||||
exports.testUnit_Engine = function(test){
|
||||
test.ok(true, "engine");
|
||||
test.done();
|
||||
};
|
||||
|
||||
|
||||
// 'use strict';
|
||||
//
|
||||
// var cp = require('child_process'), ml = require('./module_loader'),
|
||||
// log = require('./logging'),
|
||||
// poller, db, isRunning = true,
|
||||
// qEvents = new (require('./queue')).Queue(); // export queue into redis
|
||||
//
|
||||
// var regex = /\$X\.[\w\.\[\]]*/g, // find properties of $X
|
||||
// listRules = {},
|
||||
// listActionModules = {},
|
||||
// actionsLoaded = false, eventsLoaded = false;
|
||||
// /*
|
||||
// * Initialize the rules engine which initializes the module loader.
|
||||
// * @param {Object} db_link the link to the db, see [db\_interface](db_interface.html)
|
||||
// * @param {String} db_port the db port
|
||||
// * @param {String} crypto_key the key to be used for encryption on the db, max legnth 256
|
||||
// */
|
||||
// function init(db_link, db_port, crypto_key) {
|
||||
// db = db_link;
|
||||
// loadActions();
|
||||
// poller = cp.fork('./eventpoller', [db_port, crypto_key]);
|
||||
// poller.on('message', function(evt) {
|
||||
// if(evt.event === 'ep_finished_loading') {
|
||||
// eventsLoaded = true;
|
||||
// tryToLoadRules();
|
||||
// } else pushEvent(evt);
|
||||
// });
|
||||
// //start to poll the event queue
|
||||
// pollQueue();
|
||||
// }
|
||||
//
|
||||
// function loadActions() {
|
||||
// db.getActionModules(function(err, obj) {
|
||||
// if(err) log.error('EN', 'retrieving Action Modules from DB!');
|
||||
// else {
|
||||
// if(!obj) {
|
||||
// log.print('EN', 'No Action Modules found in DB!');
|
||||
// actionsLoaded = true;
|
||||
// tryToLoadRules();
|
||||
// } else {
|
||||
// var m, semaphore = 0;
|
||||
// for(var el in obj) {
|
||||
// semaphore++;
|
||||
// log.print('EN', 'Loading Action Module from DB: ' + el);
|
||||
// m = ml.requireFromString(obj[el], el);
|
||||
// db.getActionModuleAuth(el, function(mod) {
|
||||
// return function(err, obj) {
|
||||
// if(--semaphore == 0) {
|
||||
// actionsLoaded = true;
|
||||
// tryToLoadRules();
|
||||
// }
|
||||
// if(obj && mod.loadCredentials) mod.loadCredentials(JSON.parse(obj));
|
||||
// };
|
||||
// }(m));
|
||||
// listActionModules[el] = m;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// function tryToLoadRules() {
|
||||
// if(eventsLoaded && actionsLoaded) {
|
||||
// db.getRules(function(err, obj) {
|
||||
// for(var el in obj) loadRule(JSON.parse(obj[el]));
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Insert an action module into the list of available interfaces.
|
||||
// * @param {Object} objModule the action module object
|
||||
// */
|
||||
// function loadActionModule(name, objModule) {
|
||||
// log.print('EN', 'Action module "' + name + '" loaded');
|
||||
// listActionModules[name] = objModule;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Insert a rule into the eca rules repository
|
||||
// * @param {Object} objRule the rule object
|
||||
// */
|
||||
// function loadRule(objRule) {
|
||||
// //TODO validate rule
|
||||
// log.print('EN', 'Loading Rule: ' + objRule.id);
|
||||
// if(listRules[objRule.id]) log.print('EN', 'Replacing rule: ' + objRule.id);
|
||||
// listRules[objRule.id] = objRule;
|
||||
//
|
||||
// // Notify poller about eventual candidate
|
||||
// try {
|
||||
// poller.send('event|'+objRule.event);
|
||||
// } catch (err) {
|
||||
// log.print('EN', 'Unable to inform poller about new active rule!');
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// function pollQueue() {
|
||||
// if(isRunning) {
|
||||
// var evt = qEvents.dequeue();
|
||||
// if(evt) {
|
||||
// processEvent(evt);
|
||||
// }
|
||||
// setTimeout(pollQueue, 50); //TODO adapt to load
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Stores correctly posted events in the queue
|
||||
// * @param {Object} evt The event object
|
||||
// */
|
||||
// function pushEvent(evt) {
|
||||
// qEvents.enqueue(evt);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Handles correctly posted events
|
||||
// * @param {Object} evt The event object
|
||||
// */
|
||||
// function processEvent(evt) {
|
||||
// log.print('EN', 'processing event: ' + evt.event + '(' + evt.eventid + ')');
|
||||
// var actions = checkEvent(evt);
|
||||
// for(var i = 0; i < actions.length; i++) {
|
||||
// invokeAction(evt, actions[i]);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 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 = [];
|
||||
// for(var rn in listRules) {
|
||||
// //TODO this needs to get depth safe, not only data but eventually also
|
||||
// // on one level above (eventid and other meta)
|
||||
// if(listRules[rn].event === evt.event && validConditions(evt.data, listRules[rn])) {
|
||||
// log.print('EN', 'Rule "' + rn + '" fired');
|
||||
// actions = actions.concat(listRules[rn].actions);
|
||||
// }
|
||||
// }
|
||||
// return actions;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 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.condition){
|
||||
// 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, action) {
|
||||
// var actionargs = {},
|
||||
// arrModule = action.module.split('->');
|
||||
// if(arrModule.length < 2) {
|
||||
// log.error('EN', 'Invalid rule detected!');
|
||||
// return;
|
||||
// }
|
||||
// var srvc = listActionModules[arrModule[0]];
|
||||
// if(srvc && srvc[arrModule[1]]) {
|
||||
// //FIXME preprocessing not only on data
|
||||
// preprocessActionArguments(evt.data, action.arguments, actionargs);
|
||||
// try {
|
||||
// if(srvc[arrModule[1]]) srvc[arrModule[1]](actionargs);
|
||||
// } catch(err) {
|
||||
// log.error('EN', 'during action execution: ' + err);
|
||||
// }
|
||||
// }
|
||||
// else log.print('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;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// function loadEventModule(args, answHandler) {
|
||||
// if(args && args.name) {
|
||||
// answHandler.answerSuccess('Loading event module ' + args.name + '...');
|
||||
// poller.send('cmd|loadevent|'+args.name);
|
||||
// } else if(args) answHandler.answerError(args.name + ' not found');
|
||||
// }
|
||||
//
|
||||
// function loadEventModules(args, answHandler) {
|
||||
// answHandler.answerSuccess('Loading event moules...');
|
||||
// poller.send('cmd|loadevents');
|
||||
// }
|
||||
//
|
||||
// function shutDown() {
|
||||
// log.print('EN', 'Shutting down Poller and DB Link');
|
||||
// isRunning = false;
|
||||
// poller.send('cmd|shutdown');
|
||||
// db.shutDown();
|
||||
// }
|
||||
//
|
||||
// exports.init = init;
|
||||
// exports.loadActionModule = loadActionModule;
|
||||
// exports.loadRule = loadRule;
|
||||
// exports.loadEventModule = loadEventModule;
|
||||
// exports.loadEventModules = loadEventModules;
|
||||
// exports.pushEvent = pushEvent;
|
||||
// exports.shutDown = shutDown;
|
||||
144
testing/mod_eventpoller.js
Normal file
144
testing/mod_eventpoller.js
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
|
||||
exports.testUnit_EventPoller = function(test){
|
||||
test.ok(true, "ep");
|
||||
test.done();
|
||||
};
|
||||
|
||||
// // # Event Poller
|
||||
//
|
||||
// 'use strict';
|
||||
//
|
||||
// if(process.argv.length < 3) {
|
||||
// log.error('EP', 'No DB port defined! Not starting poller...');
|
||||
// } else {
|
||||
// (function() {
|
||||
// var fs = require('fs'),
|
||||
// path = require('path'),
|
||||
// log = require('./logging'),
|
||||
// db = require('./db_interface'),
|
||||
// ml = require('./module_loader'),
|
||||
// listMessageActions = {},
|
||||
// listAdminCommands = {},
|
||||
// listEventModules = {},
|
||||
// listPoll = {}, //TODO this will change in the future because it could have
|
||||
// //several parameterized (user-specific) instances of each event module
|
||||
// isRunning = true,
|
||||
// eId = 0;
|
||||
// //TODO allow different polling intervals (a wrapper together with settimeout per to be polled could be an easy and solution)
|
||||
//
|
||||
// db.init(process.argv[2], process.argv[3]);
|
||||
//
|
||||
// //TODO eventpoller will not load event modules from filesystem, this will be done by
|
||||
// // the moduel manager and the eventpoller receives messages about new/updated active rules
|
||||
//
|
||||
// db.getEventModules(function(err, obj) {
|
||||
// if(err) log.error('EP', 'retrieving Event Modules from DB!');
|
||||
// else {
|
||||
// if(!obj) {
|
||||
// log.print('EP', 'No Event Modules found in DB!');
|
||||
// process.send({ event: 'ep_finished_loading' });
|
||||
// } else {
|
||||
// var m, semaphore = 0;
|
||||
// for(var el in obj) {
|
||||
// semaphore++;
|
||||
// m = ml.requireFromString(obj[el], el);
|
||||
// db.getEventModuleAuth(el, function(mod) {
|
||||
// return function(err, obj) {
|
||||
// if(--semaphore === 0) process.send({ event: 'ep_finished_loading' });
|
||||
// if(obj && mod.loadCredentials) mod.loadCredentials(JSON.parse(obj));
|
||||
// };
|
||||
// }(m));
|
||||
// log.print('EP', 'Loading Event Module: ' + el);
|
||||
// listEventModules[el] = m;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// listMessageActions['event'] = function(args) {
|
||||
// var prop = args[1], arrModule = prop.split('->');
|
||||
// // var arrModule = obj.module.split('->');
|
||||
// if(arrModule.length > 1){
|
||||
// var module = listEventModules[arrModule[0]];
|
||||
// for(var i = 1; i < arrModule.length; i++) {
|
||||
// if(module) module = module[arrModule[i]];
|
||||
// }
|
||||
// if(module) {
|
||||
// log.print('EP', 'Found active event module "' + prop + '", adding it to polling list');
|
||||
// listPoll[prop] = module;
|
||||
// } else {
|
||||
// log.print('EP', 'No property "' + prop + '" found');
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// listAdminCommands['loadevent'] = function(args) {
|
||||
// ml.loadModule('mod_events', args[2], loadEventCallback);
|
||||
// };
|
||||
//
|
||||
// listAdminCommands['loadevents'] = function(args) {
|
||||
// ml.loadModules('mod_events', loadEventCallback);
|
||||
// };
|
||||
//
|
||||
// listAdminCommands['shutdown'] = function(args) {
|
||||
// log.print('EP', 'Shutting down DB Link');
|
||||
// isRunning = false;
|
||||
// db.shutDown();
|
||||
// };
|
||||
//
|
||||
// //TODO this goes into module_manager, this will receive notification about
|
||||
// // new loaded/stored event modules and fetch them from the db
|
||||
// listMessageActions['cmd'] = function(args) {
|
||||
// var func = listAdminCommands[args[1]];
|
||||
// if(typeof(func) === 'function') func(args);
|
||||
// };
|
||||
//
|
||||
// process.on('message', function(strProps) {
|
||||
// var arrProps = strProps.split('|');
|
||||
// if(arrProps.length < 2) log.error('EP', 'too few parameter in message!');
|
||||
// else {
|
||||
// var func = listMessageActions[arrProps[0]];
|
||||
// if(func) func(arrProps);
|
||||
// }
|
||||
// });
|
||||
// function loadEventCallback(name, data, mod, auth) {
|
||||
// db.storeEventModule(name, data); // store module in db
|
||||
// if(auth) db.storeEventModuleAuth(name, auth);
|
||||
// listEventModules[name] = mod; // store compiled module for polling
|
||||
// }
|
||||
//
|
||||
// function checkRemotes() {
|
||||
// var txt = 'Polled active event modules: ';
|
||||
// for(var prop in listPoll) {
|
||||
// txt += prop + ', ';
|
||||
// listPoll[prop](
|
||||
// /*
|
||||
// * what a hack to get prop local :-P
|
||||
// * define and immediately call anonymous function with param prop.
|
||||
// * This places the value of prop into the context of the callback
|
||||
// * and thus doesn't change when the for loop keeps iterating over listPoll
|
||||
// */
|
||||
// (function(p) {
|
||||
// return function(obj) {
|
||||
// process.send({
|
||||
// event: p,
|
||||
// eventid: 'polled_' + eId++,
|
||||
// data: obj
|
||||
// });
|
||||
// };
|
||||
// })(prop)
|
||||
// );
|
||||
// }
|
||||
// log.print('EP', txt);
|
||||
// }
|
||||
//
|
||||
// function pollLoop() {
|
||||
// if(isRunning) {
|
||||
// checkRemotes();
|
||||
// setTimeout(pollLoop, 10000);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// pollLoop();
|
||||
// })();
|
||||
// }
|
||||
110
testing/mod_http_listener.js
Normal file
110
testing/mod_http_listener.js
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
|
||||
exports.testUnit_HL = function(test){
|
||||
test.ok(true, "hl");
|
||||
test.done();
|
||||
};
|
||||
|
||||
// // # HTTP Listener
|
||||
// // Isso
|
||||
// 'use strict';
|
||||
// var express = require('express'),
|
||||
// port = express(),
|
||||
// log = require('./logging'),
|
||||
// qs = require('querystring'),
|
||||
// adminHandler, eventHandler, server;
|
||||
//
|
||||
// function init(http_port, funcAdminHandler, funcEvtHandler) {
|
||||
// if(!http_port || !funcEvtHandler) {
|
||||
// log.error('HL', 'ERROR: either port or eventHandler function not defined!');
|
||||
// return;
|
||||
// }
|
||||
// adminHandler = funcAdminHandler;
|
||||
// eventHandler = funcEvtHandler;
|
||||
// // port.get('/doc*', onDocRequest);
|
||||
// port.use('/doc', express.static(__dirname + '/../doc'));
|
||||
// port.use('/doc', express.static(__dirname + '/../doc-na'));
|
||||
// port.get('/admin', onAdminCommand);
|
||||
// port.post('/pushEvents', onPushEvent);
|
||||
// server = port.listen(http_port); // inbound event channel
|
||||
// log.print('HL', 'Started listening for http requests on port ' + http_port);
|
||||
// }
|
||||
//
|
||||
// function answerHandler(r) {
|
||||
// var response = r, hasBeenAnswered = false;
|
||||
// function postAnswer(msg) {
|
||||
// if(!hasBeenAnswered) {
|
||||
// response.write(msg);
|
||||
// response.end();
|
||||
// hasBeenAnswered = true;
|
||||
// }
|
||||
// }
|
||||
// return {
|
||||
// answerSuccess: function(msg) {
|
||||
// if(!hasBeenAnswered) response.writeHead(200, { "Content-Type": "text/plain" });
|
||||
// postAnswer(msg);
|
||||
// },
|
||||
// answerError: function(msg) {
|
||||
// if(!hasBeenAnswered) response.writeHead(400, { "Content-Type": "text/plain" });
|
||||
// postAnswer(msg);
|
||||
// },
|
||||
// isAnswered: function() { return hasBeenAnswered; }
|
||||
// };
|
||||
// };
|
||||
//
|
||||
// // function onDocRequest(request, response) {
|
||||
// // var pth = request.url;
|
||||
// // pth = pth.substring(4);
|
||||
// // if(pth.substring(pth.length-1) === '/') pth += 'index.html';
|
||||
// // console.log(pth);
|
||||
// // }
|
||||
//
|
||||
// /**
|
||||
// * Handles correct event posts, replies thank you.
|
||||
// */
|
||||
// function answerSuccess(resp, msg){
|
||||
// resp.writeHead(200, { "Content-Type": "text/plain" });
|
||||
// resp.write(msg);
|
||||
// resp.end();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Handles erroneous requests.
|
||||
// * @param {Object} msg the error message to be returned
|
||||
// */
|
||||
// function answerError(resp, msg) {
|
||||
// resp.writeHead(400, { "Content-Type": "text/plain" });
|
||||
// resp.write(msg);
|
||||
// resp.end();
|
||||
// }
|
||||
//
|
||||
// //FIXME this answer handling is a very ugly hack, improve!
|
||||
// function onAdminCommand(request, response) {
|
||||
// var q = request.query;
|
||||
// log.print('HL', 'Received admin request: ' + request.originalUrl);
|
||||
// if(q.cmd) {
|
||||
// adminHandler(q, answerHandler(response));
|
||||
// // answerSuccess(response, 'Thank you, we try our best!');
|
||||
// } else answerError(response, 'I\'m not sure about what you want from me...');
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * If a request is made to the server, this function is used to handle it.
|
||||
// */
|
||||
// function onPushEvent(request, response) {
|
||||
// var body = '';
|
||||
// request.on('data', function (data) { body += data; });
|
||||
// request.on('end', function () {
|
||||
// var obj = qs.parse(body);
|
||||
// /* If required event properties are present we process the event */
|
||||
// if(obj && obj.event && obj.eventid){
|
||||
// answerSuccess(response, 'Thank you for the event (' + obj.event + '[' + obj.eventid + '])!');
|
||||
// eventHandler(obj);
|
||||
// } else answerError(response, 'Your event was missing important parameters!');
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// exports.init = init;
|
||||
// exports.shutDown = function() {
|
||||
// log.print('HL', 'Shutting down HTTP listener');
|
||||
// process.exit(); // This is a bit brute force...
|
||||
// };
|
||||
32
testing/mod_logging.js
Normal file
32
testing/mod_logging.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
|
||||
exports.testUnit_LOG = function(test){
|
||||
test.ok(true, "log");
|
||||
test.done();
|
||||
};
|
||||
|
||||
// /*
|
||||
// * # Logging
|
||||
// * Functions to funnel logging
|
||||
// */
|
||||
//
|
||||
// // @function print(module, msg)
|
||||
//
|
||||
// /*
|
||||
// * Prints a log to stdout.
|
||||
// * @param {String} module
|
||||
// * @param {String} msg
|
||||
// */
|
||||
// exports.print = function(module, msg) {
|
||||
// console.log((new Date()).toISOString() + ' | ' + module + ' | ' + msg);
|
||||
// };
|
||||
//
|
||||
// // @function error(module, msg)
|
||||
//
|
||||
// /*
|
||||
// * Prints a log to stderr.
|
||||
// * @param {String} module
|
||||
// * @param {String} msg
|
||||
// */
|
||||
// exports.error = function(module, msg) {
|
||||
// console.error((new Date()).toISOString() + ' | ' + module + ' | ERROR: ' + msg);
|
||||
// };
|
||||
74
testing/mod_module_loader.js
Normal file
74
testing/mod_module_loader.js
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
|
||||
exports.testUnit_ML = function(test){
|
||||
test.ok(true, "ml");
|
||||
test.done();
|
||||
};
|
||||
|
||||
// var fs = require('fs'),
|
||||
// path = require('path'),
|
||||
// log = require('./logging');
|
||||
//
|
||||
// function requireFromString(src, name, dir) {
|
||||
// if(!dir) dir = __dirname;
|
||||
// // YAH yet another hack, this time to load modules from strings
|
||||
// var id = path.resolve(dir, name, name + '.js');
|
||||
// var m = new module.constructor(id, module);
|
||||
// m.paths = module.paths;
|
||||
// try {
|
||||
// m._compile(src);
|
||||
// } catch(err) {
|
||||
// log.error('LM', ' during compilation of ' + name + ': ' + err);
|
||||
// }
|
||||
// return m.exports;
|
||||
// }
|
||||
//
|
||||
// function loadModule(directory, name, callback) {
|
||||
// //FIXME contextualize and only allow small set of modules for safety reasons
|
||||
// try {
|
||||
// fs.readFile(path.resolve(directory, name, name + '.js'), 'utf8', function (err, data) {
|
||||
// if (err) {
|
||||
// log.error('LM', 'Loading module file!');
|
||||
// return;
|
||||
// }
|
||||
// var mod = requireFromString(data, name, directory);
|
||||
// if(mod && fs.existsSync(path.resolve(directory, name, 'credentials.json'))) {
|
||||
// fs.readFile(path.resolve(directory, name, 'credentials.json'), 'utf8', function (err, auth) {
|
||||
// if (err) {
|
||||
// log.error('LM', 'Loading credentials file for "' + name + '"!');
|
||||
// callback(name, data, mod, null);
|
||||
// return;
|
||||
// }
|
||||
// if(mod.loadCredentials) mod.loadCredentials(JSON.parse(auth));
|
||||
// callback(name, data, mod, auth);
|
||||
// });
|
||||
// } else {
|
||||
// // Hand back the name, the string contents and the compiled module
|
||||
// callback(name, data, mod, null);
|
||||
// }
|
||||
// });
|
||||
// } catch(err) {
|
||||
// log.error('LM', 'Failed loading module "' + name + '"');
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// function loadModules(directory, callback) {
|
||||
// fs.readdir(path.resolve(__dirname, directory), function (err, list) {
|
||||
// if (err) {
|
||||
// log.error('LM', 'loading modules directory: ' + err);
|
||||
// return;
|
||||
// }
|
||||
// log.print('LM', 'Loading ' + list.length + ' modules from "' + directory + '"');
|
||||
// list.forEach(function (file) {
|
||||
// fs.stat(path.resolve(__dirname, directory, file), function (err, stat) {
|
||||
// if (stat && stat.isDirectory()) {
|
||||
// loadModule(path.resolve(__dirname, directory), file, callback);
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// exports.loadModule = loadModule;
|
||||
// exports.loadModules = loadModules;
|
||||
// exports.requireFromString = requireFromString;
|
||||
//
|
||||
103
testing/mod_module_manager.js
Normal file
103
testing/mod_module_manager.js
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
|
||||
exports.testUnit_MM = function(test){
|
||||
test.ok(true, "mm");
|
||||
test.done();
|
||||
};
|
||||
|
||||
// /*
|
||||
// # Module Manager
|
||||
// > The module manager takes care of the module and rules loading in the initialization
|
||||
// > phase and on user request.
|
||||
//
|
||||
// > Event and Action modules are loaded as strings and stored in the database,
|
||||
// > then compiled into node modules and and rules
|
||||
// */
|
||||
// var fs = require('fs'),
|
||||
// path = require('path'),
|
||||
// log = require('./logging'),
|
||||
// ml = require('./module_loader'),
|
||||
// db = null, funcLoadAction, funcLoadRule;
|
||||
//
|
||||
// function init(db_link, fLoadAction, fLoadRule) {
|
||||
// db = db_link;
|
||||
// funcLoadAction = fLoadAction;
|
||||
// funcLoadRule = fLoadRule;
|
||||
// }
|
||||
// /*
|
||||
// # A First Level Header
|
||||
//
|
||||
//
|
||||
// A Second Level Header
|
||||
// ---------------------
|
||||
//
|
||||
// Now is the time for all good men to come to
|
||||
// the aid of their country. This is just a
|
||||
// regular paragraph.
|
||||
//
|
||||
// The quick brown fox jumped over the lazy
|
||||
// dog's back.
|
||||
//
|
||||
// ### Header 3
|
||||
//
|
||||
// > This is a blockquote.
|
||||
// >
|
||||
// > This is the second paragraph in the blockquote.
|
||||
// >
|
||||
// > ## This is an H2 in a blockquote
|
||||
//
|
||||
// This is the function documentation
|
||||
// @param {Object} [args] the optional arguments
|
||||
// @param {String} [args.name] the optional name in the arguments
|
||||
// */
|
||||
// function loadRulesFile(args, answHandler) {
|
||||
// //FIXME if a corrupt rule file is read the system crashes, prevent this also for event and action modules
|
||||
// if(!args) args = {};
|
||||
// if(!args.name) args.name = 'rules';
|
||||
// if(!funcLoadRule) log.error('ML', 'no rule loader function available');
|
||||
// else {
|
||||
// fs.readFile(path.resolve(__dirname, 'rules', args.name + '.json'), 'utf8', function (err, data) {
|
||||
// if (err) {
|
||||
// log.error('ML', 'Loading rules file: ' + args.name + '.json');
|
||||
// return;
|
||||
// }
|
||||
// var arr = JSON.parse(data), txt = '';
|
||||
// log.print('ML', 'Loading ' + arr.length + ' rules:');
|
||||
// for(var i = 0; i < arr.length; i++) {
|
||||
// txt += arr[i].id + ', ';
|
||||
// db.storeRule(arr[i].id, JSON.stringify(arr[i]));
|
||||
// funcLoadRule(arr[i]);
|
||||
// }
|
||||
// answHandler.answerSuccess('Yep, loaded rules: ' + txt);
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// *
|
||||
// * @param {Object} name
|
||||
// * @param {Object} data
|
||||
// * @param {Object} mod
|
||||
// * @param {String} [auth] The string representation of the auth json
|
||||
// */
|
||||
// function loadActionCallback(name, data, mod, auth) {
|
||||
// db.storeActionModule(name, data); // store module in db
|
||||
// funcLoadAction(name, mod); // hand compiled module back
|
||||
// if(auth) db.storeActionModuleAuth(name, auth);
|
||||
// }
|
||||
//
|
||||
// function loadActionModule(args, answHandler) {
|
||||
// if(args && args.name) {
|
||||
// answHandler.answerSuccess('Loading action module ' + args.name + '...');
|
||||
// ml.loadModule('mod_actions', args.name, loadActionCallback);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// function loadActionModules(args, answHandler) {
|
||||
// answHandler.answerSuccess('Loading action modules...');
|
||||
// ml.loadModules('mod_actions', loadActionCallback);
|
||||
// }
|
||||
//
|
||||
// exports.init = init;
|
||||
// exports.loadRulesFile = loadRulesFile;
|
||||
// exports.loadActionModule = loadActionModule;
|
||||
// exports.loadActionModules = loadActionModules;
|
||||
65
testing/mod_server.js
Normal file
65
testing/mod_server.js
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
exports.setUp = function() {
|
||||
// this.srv = require('../js/webapi_eca_server.js');
|
||||
};
|
||||
|
||||
exports.testUnit_SRV = function(test){
|
||||
console.log(this.srv);
|
||||
test.ok(true, "SRV");
|
||||
test.done();
|
||||
};
|
||||
// var http_listener = require('./http_listener'),
|
||||
// db = require('./db_interface'),
|
||||
// engine = require('./engine'),
|
||||
// mm = require('./module_manager'),
|
||||
// log = require('./logging'),
|
||||
// fs = require('fs'),
|
||||
// path = require('path'),
|
||||
// objCmds = {
|
||||
// 'loadrules': mm.loadRulesFile,
|
||||
// 'loadaction': mm.loadActionModule,
|
||||
// 'loadactions': mm.loadActionModules,
|
||||
// 'loadevent': engine.loadEventModule,
|
||||
// 'loadevents': engine.loadEventModules,
|
||||
// 'shutdown': shutDown,
|
||||
// 'restart': null //TODO implement
|
||||
// };
|
||||
//
|
||||
// function handleAdminCommands(args, answHandler) {
|
||||
// if(args && args.cmd) {
|
||||
// var func = objCmds[args.cmd];
|
||||
// if(func) func(args, answHandler);
|
||||
// } else log.print('RS', 'No command in request');
|
||||
// setTimeout(function(ah) {
|
||||
// answHandler = ah;
|
||||
// return function() {
|
||||
// if(!answHandler.isAnswered()) answHandler.answerError('Not handeled...');
|
||||
// };
|
||||
// }, 2000);
|
||||
// }
|
||||
//
|
||||
// function shutDown(args, answHandler) {
|
||||
// answHandler.answerSuccess('Goodbye!');
|
||||
// log.print('RS', 'Received shut down command!');
|
||||
// engine.shutDown();
|
||||
// http_listener.shutDown();
|
||||
// }
|
||||
//
|
||||
// fs.readFile(path.resolve(__dirname, 'config', 'config.json'), 'utf8', function (err, data) {
|
||||
// if (err) {
|
||||
// log.error('RS', 'Loading config file');
|
||||
// return;
|
||||
// }
|
||||
// var config = JSON.parse(data);
|
||||
// if(!config.http_port || !config.db_port || !config.crypto_key) {
|
||||
// log.error('RS', 'you forgot to define either http_port, db_port, crypto_key, or even all of them!');
|
||||
// } else {
|
||||
// log.print('RS', 'Initialzing DB');
|
||||
// db.init(config.db_port, config.crypto_key, function() {
|
||||
// engine.init(db, config.db_port, config.crypto_key);
|
||||
// });
|
||||
// log.print('RS', 'Initialzing http listener');
|
||||
// http_listener.init(config.http_port, handleAdminCommands, engine.pushEvent);
|
||||
// log.print('RS', 'Initialzing module manager');
|
||||
// mm.init(db, engine.loadActionModule, engine.loadRule);
|
||||
// }
|
||||
// });
|
||||
5
testing/unit_integration.js
Normal file
5
testing/unit_integration.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
exports.testUnitIntegration = function(test){
|
||||
test.ok(true, "unit integration");
|
||||
test.done();
|
||||
};
|
||||
5
testing/whole_system.js
Normal file
5
testing/whole_system.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
exports.testSystem = function(test){
|
||||
test.ok(true, "system");
|
||||
test.done();
|
||||
};
|
||||
Loading…
Reference in a new issue