major implementations to get a running server

This commit is contained in:
Dominic Bosch 2014-02-24 00:59:49 +01:00
parent a8f85d74c3
commit 8a6d357439
30 changed files with 2262 additions and 784 deletions

View file

@ -0,0 +1,323 @@
###
Components Manager
==================
> The components manager takes care of the dynamic JS modules and the rules.
> Event Poller and Action Invoker modules are loaded as strings and stored in the database,
> then compiled into node modules and rules and used in the engine and event poller.
###
# **Loads Modules:**
# - [Persistence](persistence.html)
db = require './persistence'
# - [Dynamic Modules](dynamic-modules.html)
dynmod = require './dynamic-modules'
# - Node.js Modules: [fs](http://nodejs.org/api/fs.html),
# [vm](http://nodejs.org/api/vm.html) and
# [path](http://nodejs.org/api/path.html),
# [events](http://nodejs.org/api/events.html)
fs = require 'fs'
vm = require 'vm'
path = require 'path'
events = require 'events'
###
Module call
-----------
Initializes the HTTP listener and its request handler.
@param {Object} args
###
exports = module.exports = ( args ) =>
@log = args.logger
@ee = new events.EventEmitter()
db args
dynmod args
module.exports
exports.addListener = ( evt, eh ) =>
@ee.addListener evt, eh
#TODO as soon as an event handler is added it needs to receive the
#full list of existing and activated rules
# cb ( obj ) where obj should contain at least the HTTP response code and a message
exports.processRequest = ( user, obj, cb ) =>
if commandFunctions[obj.command]
answ = commandFunctions[obj.command] user, obj, cb
else
cb
code: 404
message: 'Strange request!'
commandFunctions =
forge_event_poller: ( user, obj, cb ) =>
answ =
code: 200
db.getEventPoller obj.id, ( err, mod ) =>
if mod
answ.code = 409
answ.message = 'Event Poller module name already existing: ' + obj.id
else
src = obj.data
cm = dynmod.compileString src, obj.id, {}, obj.lang
answ = cm.answ
if answ.code is 200
events = []
events.push name for name, id of cm.module
@log.info "CM | Storing new eventpoller with events #{ events }"
answ.message =
"Event Poller module successfully stored! Found following event(s): #{ events }"
db.storeEventPoller obj.id, user.username,
code: obj.data
lang: obj.lang
params: obj.params
events: events
if obj.public is 'true'
db.publishEventPoller obj.id
cb answ
get_event_pollers: ( user, obj, cb ) ->
db.getAvailableEventPollerIds user.username, ( err, obj ) ->
oRes = {}
sem = obj.length
fGetEvents = ( id ) ->
db.getEventPoller id, ( err, obj ) ->
oRes[id] = obj.events
if --sem is 0
cb
code: 200
message: oRes
fGetEvents id for id in obj
get_event_poller_params: ( user, obj, cb ) ->
db.getEventPollerRequiredParams obj.id, ( err, obj ) ->
cb
code: 200
message: obj
get_action_invokers: ( user, obj, cb ) ->
db.getAvailableActionInvokerIds user.username, ( err, obj ) ->
oRes = {}
sem = obj.length
fGetActions = ( id ) ->
db.getActionInvoker id, ( err, obj ) ->
oRes[id] = obj.actions
if --sem is 0
cb
code: 200
message: oRes
fGetActions id for id in obj
get_action_invoker_params: ( user, obj, cb ) ->
db.getActionInvokerRequiredParams obj.id, ( err, obj ) ->
cb
code: 200
message: obj
forge_action_invoker: ( user, obj, cb ) =>
answ =
code: 200
db.getActionInvoker obj.id, ( err, mod ) =>
if mod
answ.code = 409
answ.message = 'Action Invoker module name already existing: ' + obj.id
else
src = obj.data
cm = dynmod.compileString src, obj.id, {}, obj.lang
answ = cm.answ
if answ.code is 200
actions = []
actions.push name for name, id of cm.module
@log.info "CM | Storing new eventpoller with actions #{ actions }"
answ.message =
"Action Invoker module successfully stored! Found following action(s): #{ actions }"
db.storeActionInvoker obj.id, user.username,
code: obj.data
lang: obj.lang
params: obj.params
actions: actions
if obj.public is 'true'
db.publishActionInvoker obj.id
cb answ
get_rules: ( user, obj, cb ) ->
console.log 'CM | Implement get_rules'
forge_rule: ( user, obj, cb ) =>
obj.event = JSON.parse obj.event
console.log obj
db.getRule obj.id, ( err, objRule ) =>
if objRule isnt null
answ =
code: 409
message: 'Rule name already existing!'
else
answ =
code: 200
message: 'Rule stored and activated!'
rule =
id: obj.id
event: "#{ obj.event.module } -> #{ obj.event.function }"
conditions: JSON.parse obj.conditions
actions: JSON.parse obj.actions
console.log rule
modules = JSON.parse obj.event.action_params
console.log 'store rule'
db.storeRule rule.id, JSON.stringify rule
console.log 'link rule'
db.linkRule rule.id, user.username
console.log 'activate rule'
db.activateRule rule.id, user.username
console.log 'store event params'
db.storeEventUserParams obj.event.module, user.username, obj.event_params
console.log 'store action params'
db.storeActionUserParams id, user.username, params for id, params of modules
#TODO implement ID approach, check for existing
@ee.emit 'newRule', rule
cb answ
# exports.loadModule = function(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 = exports.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 + '"');
# }
# };
# exports.loadModules = function(directory, callback) {
# fs.readdir(path.resolve(__dirname, '..', directory), function (err, list) {
# if (err) {
# log.error('LM', 'loading modules directory: ' + err);
# return;
# }
# log.info('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()) {
# exports.loadModule(directory, file, callback);
# }
# });
# });
# });
# };
# exports.storeEventModule = function (objUser, obj, answHandler) {
# try {
# // TODO in the future we might want to link the modules close to the user
# // and allow for e.g. private modules
# // we need a child process to run this code and kill it after invocation
# var m = exports.requireFromString(obj.data, obj.id);
# obj.methods = Object.keys(m);
# answHandler.answerSuccess('Thank you for the event module!');
# db.storeEventModule(obj.id, obj);
# } catch (err) {
# answHandler.answerError(err.message);
# console.error(err);
# }
# };
# exports.getAllEventModules = function ( objUser, obj, answHandler ) {
# db.getEventModules(function(err, obj) {
# if(err) answHandler.answerError('Failed fetching event modules: ' + err.message);
# else answHandler.answerSuccess(obj);
# });
# };
# exports.storeActionModule = function (objUser, obj, answHandler) {
# var m = exports.requireFromString(obj.data, obj.id);
# obj.methods = Object.keys(m);
# answHandler.answerSuccess('Thank you for the action module!');
# db.storeActionModule(obj.id, obj);
# };
# exports.getAllActionModules = function ( objUser, obj, answHandler ) {
# db.getActionModules(function(err, obj) {
# if(err) answHandler.answerError('Failed fetching action modules: ' + err.message);
# else answHandler.answerSuccess(obj);
# });
# };
# exports.storeRule = function (objUser, obj, answHandler) {
# //TODO fix, twice same logic
# var cbEventModule = function (lstParams) {
# return function(err, data) {
# if(err) {
# err.addInfo = 'fetching event module';
# log.error('MM', err);
# }
# if(!err && data) {
# if(data.params) {
# lstParams.eventmodules[data.id] = data.params;
# }
# }
# if(--semaphore === 0) answHandler.answerSuccess(lstParams);
# };
# };
# var cbActionModule = function (lstParams) {
# return function(err, data) {
# if(err) {
# err.addInfo = 'fetching action module';
# log.error('MM', err);
# }
# if(!err && data) {
# if(data.params) {
# lstParams.actionmodules[data.id] = data.params;
# }
# }
# if(--semaphore === 0) answHandler.answerSuccess(lstParams);
# };
# };
# var semaphore = 1;
# var lst = {
# eventmodules: {},
# actionmodules: {}
# };
# try {
# var objRule = JSON.parse(obj.data);
# for(var i = 0; i < objRule.actions.length; i++) {
# semaphore++;
# db.getActionModule(objRule.actions[i].module.split('->')[0], cbActionModule(lst));
# }
# db.getEventModule(objRule.event.split('->')[0], cbEventModule(lst));
# db.storeRule(objRule.id, objUser.username, obj.data);
# ee.emit('newRule', objRule);
# // for( var i = 0; i < eventHandlers.length; i++ ) {
# // eventHandlers[i]( objRule );
# // }
# } catch(err) {
# answHandler.answerError(err.message);
# log.error('MM', err);
# }
# };

View file

@ -0,0 +1,71 @@
###
Dynamic Modules
===============
> Compiles CoffeeScript modules and loads JS modules in a VM, together
> with only a few allowed node.js modules.
###
# - Node.js Modules: [vm](http://nodejs.org/api/vm.html) and
# [events](http://nodejs.org/api/events.html)
vm = require 'vm'
needle = require 'needle'
# - External Modules: [coffee-script](http://coffeescript.org/)
cs = require 'coffee-script'
###
Module call
-----------
Initializes the dynamic module handler.
@param {Object} args
###
exports = module.exports = ( args ) =>
@log = args.logger
module.exports
###
Try to run a JS module from a string, together with the
given parameters. If it is written in CoffeeScript we
compile it first into JS.
@public compileString ( *src, id, params, lang* )
@param {String} src
@param {String} id
@param {Object} params
@param {String} lang
###
exports.compileString = ( src, id, params, lang ) =>
answ =
code: 200
message: 'Successfully compiled'
src = "'use strict;'\n" + src
if lang is '0'
try
src = cs.compile src
catch err
answ.code = 400
answ.message = 'Compilation of CoffeeScript failed at line ' +
err.location.first_line
#FIXME not log but debug module is required to provide information to the user
sandbox =
id: id
params: params
needle: needle
log: @log
exports: {}
#TODO child_process to run module!
#Define max runtime per loop as 10 seconds, after that the child will be killed
#it can still be active after that if there was a timing function or a callback used...
#kill the child each time? how to determine whether there's still a token in the module?
try
vm.runInNewContext src, sandbox, id + '.vm'
catch err
answ.code = 400
answ.message = 'Loading Module failed: ' + err.message
ret =
answ: answ
module: sandbox.exports

View file

@ -44,7 +44,7 @@ exports = module.exports = ( args ) =>
# Eventually we try to connect to the wrong port, redis will emit an error that we
# need to catch and take into account when answering the isConnected function call
@db.on 'error', ( err ) =>
if err.message.indexOf 'ECONNREFUSED' > -1
if err.message.indexOf( 'ECONNREFUSED' ) > -1
@connRefused = true
@log.error err, 'DB | Wrong port?'
@ep = new IndexedModules( 'event-poller', @db, @log )
@ -161,7 +161,7 @@ decrypt = ( crypticText ) =>
if !crypticText? then return null;
try
dec = crypto.AES.decrypt crypticText, @crypto_key
dec.toString(crypto.enc.Utf8)
dec.toString crypto.enc.Utf8
catch err
@log.warn err, 'DB | during decryption'
null
@ -220,8 +220,9 @@ getSetRecords = ( set, fSingle, cb ) =>
# Since we retrieved an array of keys, we now execute the fSingle function
# on each of them, to retrieve the ata behind the key. Our fCallback function
# is used to preprocess the answer to determine correct execution
fSingle reply, fCallback( reply ) for reply in arrReply
fSingle reply, fCallback reply for reply in arrReply
# TODO remove specific functions and allow direct access to instances of this class
class IndexedModules
constructor: ( @setname, @db, @log ) ->
@log.info "DB | Instantiated indexed modules for '#{ @setname }'"
@ -230,12 +231,50 @@ class IndexedModules
@log.info "DB | storeModule(#{ @setname }): #{ mId }"
@db.sadd "#{ @setname }s", mId,
replyHandler "Storing '#{ @setname }' key '#{ mId }'"
@db.set "#{ @setname }:#{ mId }", data,
@db.hmset "#{ @setname }:#{ mId }", data,
replyHandler "Storing '#{ @setname }:#{ mId }'"
#TODO add testing
linkModule: ( mId, userId ) =>
@log.info "DB | linkModule(#{ @setname }): #{ mId } to #{ userId }"
@db.sadd "#{ @setname }:#{ mId }:users", userId,
replyHandler "Linking '#{ @setname }:#{ mId }:users' #{ userId }"
@db.sadd "user:#{ userId }:#{ @setname }s", mId,
replyHandler "Linking 'user:#{ userId }:#{ @setname }s' #{ mId }"
#TODO add testing
unlinkModule: ( mId, userId ) =>
@log.info "DB | unlinkModule(#{ @setname }): #{ mId } to #{ userId }"
@db.srem "#{ @setname }:#{ mId }:users", userId,
replyHandler "Unlinking '#{ @setname }:#{ mId }:users' #{ userId }"
@db.srem "user:#{ userId }:#{ @setname }s", mId,
replyHandler "Unlinking 'user:#{ userId }:#{ @setname }s' #{ mId }"
#TODO add testing
publish: ( mId ) =>
@log.info "DB | publish(#{ @setname }): #{ mId }"
@db.sadd "public-#{ @setname }s", mId,
replyHandler "Publishing '#{ @setname }' key '#{ mId }'"
#TODO add testing
unpublish: ( mId ) =>
@log.info "DB | unpublish(#{ @setname }): #{ mId }"
@db.srem "public-#{ @setname }s", mId,
replyHandler "Unpublishing '#{ @setname }' key '#{ mId }'"
getModule: ( mId, cb ) =>
@log.info "DB | getModule('#{ @setname }): #{ mId }'"
@db.get "#{ @setname }:#{ mId }", cb
@db.hgetall "#{ @setname }:#{ mId }", cb
#TODO add testing
getModuleParams: ( mId, cb ) =>
@log.info "DB | getModule('#{ @setname }): #{ mId }'"
@db.hget "#{ @setname }:#{ mId }", "params", cb
#TODO add testing
getAvailableModuleIds: ( userId, cb ) =>
@log.info "DB | getPublicModuleIds(#{ @setname })"
@db.sunion "public-#{ @setname }s", "user:#{ userId }:#{ @setname }s", cb
getModuleIds: ( cb ) =>
@log.info "DB | getModuleIds(#{ @setname })"
@ -251,25 +290,27 @@ class IndexedModules
replyHandler "Deleting '#{ @setname }' key '#{ mId }'"
@db.del "#{ @setname }:#{ mId }",
replyHandler "Deleting '#{ @setname }:#{ mId }'"
#TODO remove published ids
#TODO remove from linked users
storeParameters: ( mId, userId, data ) =>
@log.info "DB | storeParameters(#{ @setname }): '#{ mId }:#{ userId }'"
storeUserParameters: ( mId, userId, data ) =>
@log.info "DB | storeUserParameters(#{ @setname }): '#{ mId }:#{ userId }'"
@db.sadd "#{ @setname }-params", "#{ mId }:#{ userId }",
replyHandler "Storing '#{ @setname }' module parameters key '#{ mId }'"
@db.set "#{ @setname }-params:#{ mId }:#{ userId }", encrypt(data),
@db.set "#{ @setname }-params:#{ mId }:#{ userId }", encrypt( data ),
replyHandler "Storing '#{ @setname }' module parameters '#{ mId }:#{ userId }'"
getParameters: ( mId, userId, cb ) =>
@log.info "DB | getParameters(#{ @setname }): '#{ mId }:#{ userId }'"
getUserParameters: ( mId, userId, cb ) =>
@log.info "DB | getUserParameters(#{ @setname }): '#{ mId }:#{ userId }'"
@db.get "#{ @setname }-params:#{ mId }:#{ userId }", ( err, data ) ->
cb err, decrypt data
getParametersIds: ( cb ) =>
@log.info "DB | getParametersIds(#{ @setname })"
getUserParametersIds: ( cb ) =>
@log.info "DB | getUserParametersIds(#{ @setname })"
@db.smembers "#{ @setname }-params", cb
deleteParameters: ( mId, userId ) =>
@log.info "DB | deleteParameters(#{ @setname }): '#{ mId }:#{ userId }'"
deleteUserParameters: ( mId, userId ) =>
@log.info "DB | deleteUserParameters(#{ @setname }): '#{ mId }:#{ userId }'"
@db.srem "#{ @setname }-params", "#{ mId }:#{ userId }",
replyHandler "Deleting '#{ @setname }-params' key '#{ mId }:#{ userId }'"
@db.del "#{ @setname }-params:#{ mId }:#{ userId }",
@ -283,12 +324,35 @@ class IndexedModules
###
Store a string representation of an action invoker in the DB.
@public storeActionInvoker ( *aiId, data* )
@public storeActionInvoker ( *aiId, userId, data* )
@param {String} aiId
@param {String} userId
@param {String} data
###
exports.storeActionInvoker = ( aiId, data ) =>
@ai.storeModule( aiId, data )
#TODO adapt testing
exports.storeActionInvoker = ( aiId, userId, data ) =>
@ai.storeModule aiId, data
@ai.linkModule aiId, userId
###
Make an action invoker public.
@public publishActionInvoker ( *aiId* )
@param {String} aiId
###
exports.publishActionInvoker = ( aiId ) =>
@ai.publish aiId
###
Make an action invoker private.
@public unpublishActionInvoker ( *aiId* )
@param {String} aiId
###
exports.unpublishActionInvoker = ( aiId ) =>
@ai.unpublish aiId
###
Query the DB for an action invoker and pass it to cb(err, obj).
@ -300,6 +364,16 @@ Query the DB for an action invoker and pass it to cb(err, obj).
exports.getActionInvoker = ( aiId, cb ) =>
@ai.getModule aiId, cb
###
Query the DB for action invoker required params and pass it to cb(err, obj).
@public getActionInvokerEventPollerRequiredParams( *epId, cb* )
@param {String} epId
@param {function} cb
###
exports.getActionInvokerRequiredParams = ( epId, cb ) =>
@ai.getModuleParams epId, cb
###
Fetch all action invoker IDs and hand them to cb(err, obj).
@ -309,6 +383,25 @@ Fetch all action invoker IDs and hand them to cb(err, obj).
exports.getActionInvokerIds = ( cb ) =>
@ai.getModuleIds cb
###
Fetch all available actin invoker IDs for a user and
hand them to cb(err, obj).
@public getAvailableActionInvokerIds( *userId, cb* )
@param {function} cb
###
exports.getAvailableActionInvokerIds = ( userId, cb ) =>
@ai.getAvailableModuleIds userId, cb
###
Fetch all public action invoker IDs and hand them to cb(err, obj).
@public getPublicActionInvokerIds( *cb* )
@param {function} cb
###
exports.getPublicActionInvokerIds = ( cb ) =>
@ai.getPublicModuleIds cb
###
Fetch all action invokers and hand them to cb(err, obj).
@ -330,43 +423,43 @@ exports.deleteActionInvoker = ( aiId ) =>
###
Store user-specific action invoker parameters .
@public storeActionParams( *userId, aiId, data* )
@public storeActionUserParams( *userId, aiId, data* )
@param {String} userId
@param {String} aiId
@param {String} data
###
exports.storeActionParams = ( aiId, userId, data ) =>
@ai.storeParameters aiId, userId, data
exports.storeActionUserParams = ( aiId, userId, data ) =>
@ai.storeUserParameters aiId, userId, data
###
Query the DB for user-specific action module parameters,
and pass it to cb(err, obj).
@public getActionParams( *userId, aiId, cb* )
@public getActionUserParams( *userId, aiId, cb* )
@param {String} userId
@param {String} aiId
@param {function} cb
###
exports.getActionParams = ( aiId, userId, cb ) =>
@ai.getParameters aiId, userId, cb
exports.getActionUserParams = ( aiId, userId, cb ) =>
@ai.getUserParameters aiId, userId, cb
###
Fetch all action params IDs and hand them to cb(err, obj).
@public getActionParamsIds( *cb* )
@public getActionUserParamsIds( *cb* )
@param {function} cb
###
exports.getActionParamsIds = ( cb ) =>
@ai.getParametersIds cb
exports.getActionUserParamsIds = ( cb ) =>
@ai.getUserParametersIds cb
###
Fetch all action modules and hand them to cb(err, obj).
@public deleteActionParams( *cb* )
@public deleteActionUserParams( *cb* )
@param {function} cb
###
exports.deleteActionParams = ( aiId, userId ) =>
@ai.deleteParameters aiId, userId
exports.deleteActionUserParams = ( aiId, userId ) =>
@ai.deleteUserParameters aiId, userId
###
@ -376,12 +469,35 @@ exports.deleteActionParams = ( aiId, userId ) =>
###
Store a string representation of an event poller in the DB.
@public storeEventPoller ( *epId, data* )
@public storeEventPoller ( *epId, userId, data* )
@param {String} epId
@param {String} userId
@param {String} data
###
exports.storeEventPoller = ( epId, data ) =>
@ep.storeModule( epId, data )
#TODO adapt testing
exports.storeEventPoller = ( epId, userId, data ) =>
@ep.storeModule epId, data
@ep.linkModule epId, userId
###
Make an event poller public.
@public publishEventPoller ( *epId* )
@param {String} epId
###
exports.publishEventPoller = ( epId ) =>
@ep.publish epId
###
Make an event poller private.
@public unpublishEventPoller ( *epId* )
@param {String} epId
###
exports.unpublishEventPoller = ( epId ) =>
@ep.unpublish epId
###
Query the DB for an event poller and pass it to cb(err, obj).
@ -393,6 +509,16 @@ Query the DB for an event poller and pass it to cb(err, obj).
exports.getEventPoller = ( epId, cb ) =>
@ep.getModule epId, cb
###
Query the DB for event poller required params and pass it to cb(err, obj).
@public getEventPollerRequiredParams( *epId, cb* )
@param {String} epId
@param {function} cb
###
exports.getEventPollerRequiredParams = ( epId, cb ) =>
@ep.getModuleParams epId, cb
###
Fetch all event poller IDs and hand them to cb(err, obj).
@ -402,6 +528,25 @@ Fetch all event poller IDs and hand them to cb(err, obj).
exports.getEventPollerIds = ( cb ) =>
@ep.getModuleIds cb
###
Fetch all available event poller IDs for a user and
hand them to cb(err, obj).
@public getAvailableEventPollerIds( *userId, cb* )
@param {function} cb
###
exports.getAvailableEventPollerIds = ( userId, cb ) =>
@ep.getAvailableModuleIds userId, cb
###
Fetch all public event poller IDs and hand them to cb(err, obj).
@public getPublicEventPollerIds( *cb* )
@param {function} cb
###
exports.getPublicEventPollerIds = ( cb ) =>
@ep.getPublicModuleIds cb
###
Fetch all event pollers and hand them to cb(err, obj).
@ -418,48 +563,51 @@ Fetch all event pollers and hand them to cb(err, obj).
@param {function} cb
###
exports.deleteEventPoller = ( epId ) =>
# TODO remove from public modules
# TODO remove parameters
# TODO also do this for action invokers
@ep.deleteModule epId
###
Store user-specific event poller parameters .
@public storeEventParams( *userId, epId, data* )
@public storeEventUserParams( *userId, epId, data* )
@param {String} userId
@param {String} epId
@param {String} data
###
exports.storeEventParams = ( epId, userId, data ) =>
@ep.storeParameters epId, userId, data
exports.storeEventUserParams = ( epId, userId, data ) =>
@ep.storeUserParameters epId, userId, data
###
Query the DB for user-specific event module parameters,
and pass it to cb(err, obj).
@public getEventParams( *userId, epId, cb* )
@public getEventUserParams( *userId, epId, cb* )
@param {String} userId
@param {String} epId
@param {function} cb
###
exports.getEventParams = ( epId, userId, cb ) =>
@ep.getParameters epId, userId, cb
exports.getEventUserParams = ( epId, userId, cb ) =>
@ep.getUserParameters epId, userId, cb
###
Fetch all event params IDs and hand them to cb(err, obj).
@public getEventParamsIds( *cb* )
@public getEventUserParamsIds( *cb* )
@param {function} cb
###
exports.getEventParamsIds = ( cb ) =>
@ep.getParametersIds cb
exports.getEventUserParamsIds = ( cb ) =>
@ep.getUserParametersIds cb
###
Fetch all event modules and hand them to cb(err, obj).
@public deleteEventParams( *cb* )
@public deleteEventUserParams( *cb* )
@param {function} cb
###
exports.deleteEventParams = ( epId, userId ) =>
@ep.deleteParameters epId, userId
exports.deleteEventUserParams = ( epId, userId ) =>
@ep.deleteUserParameters epId, userId
###
@ -528,14 +676,14 @@ exports.deleteRule = ( ruleId ) =>
delLinkedUserRule = ( userId ) =>
@db.srem "user:#{ userId }:rules", ruleId,
replyHandler "Deleting rule key '#{ ruleId }' in linked user '#{ userId }'"
delLinkedUserRule( id ) for id in obj
delLinkedUserRule id for id in obj
@db.del "rule:#{ ruleId }:users", replyHandler "Deleting rule '#{ ruleId }' users"
@db.smembers "rule:#{ ruleId }:active-users", ( err, obj ) =>
delActiveUserRule = ( userId ) =>
@db.srem "user:#{ userId }:active-rules", ruleId,
replyHandler "Deleting rule key '#{ ruleId }' in active user '#{ userId }'"
delActiveUserRule( id ) for id in obj
delActiveUserRule id for id in obj
@db.del "rule:#{ ruleId }:active-users",
replyHandler "Deleting rule '#{ ruleId }' active users"
@ -659,7 +807,7 @@ exports.getAllActivatedRuleIdsPerUser = ( cb ) =>
result[userId] = obj
if --semaphore is 0
cb null, result
fFetchActiveUserRules(user) for user in obj
fFetchActiveUserRules user for user in obj
###
@ -723,7 +871,7 @@ exports.deleteUser = ( userId ) =>
delLinkedRuleUser = ( ruleId ) =>
@db.srem "rule:#{ ruleId }:users", userId,
replyHandler "Deleting user key '#{ userId }' in linked rule '#{ ruleId }'"
delLinkedRuleUser( id ) for id in obj
delLinkedRuleUser id for id in obj
@db.del "user:#{ userId }:rules",
replyHandler "Deleting user '#{ userId }' rules"
@ -732,7 +880,7 @@ exports.deleteUser = ( userId ) =>
delActivatedRuleUser = ( ruleId ) =>
@db.srem "rule:#{ ruleId }:active-users", userId,
replyHandler "Deleting user key '#{ userId }' in active rule '#{ ruleId }'"
delActivatedRuleUser( id ) for id in obj
delActivatedRuleUser id for id in obj
@db.del "user:#{ userId }:active-rules",
replyHandler "Deleting user '#{ userId }' rules"
@ -741,7 +889,7 @@ exports.deleteUser = ( userId ) =>
delRoleUser = ( roleId ) =>
@db.srem "role:#{ roleId }:users", userId,
replyHandler "Deleting user key '#{ userId }' in role '#{ roleId }'"
delRoleUser( id ) for id in obj
delRoleUser id for id in obj
@db.del "user:#{ userId }:roles",
replyHandler "Deleting user '#{ userId }' roles"

View file

@ -36,9 +36,12 @@ exports = module.exports = ( args ) =>
# Register the shutdown handler to the admin command.
@objAdminCmds =
shutdown: ( args, answerHandler ) ->
answerHandler.answerSuccess 'Shutting down... BYE!'
shutdown: ( obj, cb ) ->
data =
code: 200
message: 'Shutting down... BYE!'
setTimeout args[ 'shutdown-function' ], 500
cb null, data
db args
# Load the standard users from the user config file
@ -63,13 +66,22 @@ exports.handleEvent = ( req, resp ) ->
req.on 'data', ( data ) ->
body += data
req.on 'end', ->
obj = qs.parse body
# If required event properties are present we process the event #
if obj and obj.event and obj.eventid
resp.send 'Thank you for the event: ' + obj.event + ' (' + obj.eventid + ')!'
db.pushEvent obj
if req.session and req.session.user
obj = qs.parse body
# If required event properties are present we process the event #
if obj and obj.event
timestamp = ( new Date ).toISOString()
rand = ( Math.floor Math.random() * 10e9 ).toString( 16 ).toUpperCase()
obj.eventid = "#{ obj.event }_#{ timestamp }_#{ rand }"
answ =
code: 200
message: "Thank you for the event: #{ obj.eventid }"
resp.send answ.code, answ
db.pushEvent obj
else
resp.send 400, 'Your event was missing important parameters!'
else
resp.send 400, 'Your event was missing important parameters!'
resp.send 401, 'Please login!'
###
@ -170,6 +182,7 @@ renderPage = ( name, req, resp, msg ) ->
code = 200
data =
message: msg
user: req.session.user
# Try to grab the script belonging to this page. But don't bother if it's not existing
try
@ -187,21 +200,22 @@ renderPage = ( name, req, resp, msg ) ->
content = getTemplate 'error'
script = getScript 'error'
code = 404
data =
message: 'Invalid Page!'
content = mustache.render content, data
data.message = 'Invalid Page!'
if req.session.user
menubar = getTemplate 'menubar'
view =
user: req.session.user,
content: content,
script: script,
remote_scripts: remote_scripts,
pageElements =
content: content
script: script
remote_scripts: remote_scripts
menubar: menubar
resp.send code, mustache.render skeleton, view
# First we render the page by including all page elements into the skeleton
page = mustache.render skeleton, pageElements
# Then we complete the rendering by adding the data, and send the result to the user
resp.send code, mustache.render page, data
###
Present the desired forge page to the user.
@ -230,19 +244,17 @@ objects.*
@public handleUser( *req, resp* )
###
exports.handleUserCommand = ( req, resp ) =>
if not req.session or not req.session.user
resp.send 401, 'Login first!'
else
if req.session and req.session.user
body = ''
req.on 'data', ( data ) ->
body += data
req.on 'end', =>
obj = qs.parse body
@userRequestHandler req.session.user, obj, ( err, obj ) ->
if err
resp.send 404, 'Rethink your request!'
else
resp.send obj
@userRequestHandler req.session.user, obj, ( obj ) ->
resp.send obj.code, obj
else
resp.send 401, 'Login first!'
###
Present the admin console to the user if he's allowed to see it.
@ -276,46 +288,20 @@ objects.*
@public handleAdminCommand( *req, resp* )
###
exports.handleAdminCommand = ( req, resp ) =>
if req.session and req.session.user
if req.session.user.isAdmin is "true"
q = req.query
@log.info 'RH | Received admin request: ' + req.originalUrl
if q.cmd
@objAdminCmds[q.cmd]? q, answerHandler req, resp, true
if req.session and
req.session.user and
req.session.user.isAdmin is "true"
body = ''
req.on 'data', ( data ) ->
body += data
req.on 'end', =>
obj = qs.parse body
@log.info 'RH | Received admin request: ' + obj.command
if obj.command and @objAdminCmds[obj.command]
@objAdminCmds[obj.command] obj, ( err, obj ) ->
resp.send obj.code, obj
else
resp.send 404, 'Command unknown!'
else
resp.send renderPage 'unauthorized', req.session
else
resp.sendfile getHandlerPath 'login'
#TODO remove this ugly legacy blob
answerHandler = (req, resp, ntbr) ->
request = req
response = resp
needsToBeRendered = ntbr
hasBeenAnswered = false
ret =
answerSuccess: (msg) ->
if not hasBeenAnswered
if needsToBeRendered
response.send renderPage 'command_answer', request.session, msg
else
response.send msg
hasBeenAnswered = true
,
answerError: (msg) ->
if not hasBeenAnswered
if needsToBeRendered
response.send 400, renderPage 'error', request.session, msg
else
response.send 400, msg
hasBeenAnswered = true
,
isAnswered: -> hasBeenAnswered
setTimeout(() ->
ret.answerError 'Strange... maybe try again?'
, 5000)
ret
resp.send 401, 'You need to be logged in as admin!'

View file

@ -1,204 +1,246 @@
// Generated by CoffeeScript 1.6.3
/*
# 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 rules
*/
Components Manager
==================
> The components manager takes care of the dynamic JS modules and the rules.
> Event Poller and Action Invoker modules are loaded as strings and stored in the database,
> then compiled into node modules and rules and used in the engine and event poller.
*/
// # **Loads Modules:**
(function() {
var commandFunctions, db, dynmod, events, exports, fs, path, vm,
_this = this;
// # - [Persistence](persistence.html)
var fs = require('fs'),
path = require('path'),
db = require('./persistence'),
events = require('events'),
log, ee,
eventHandlers = [],
funcLoadAction, funcLoadRule;
db = require('./persistence');
exports = module.exports = function(args) {
args = args || {};
ee = new events.EventEmitter();
log = args.logger;
db(args);
return module.exports;
};
dynmod = require('./dynamic-modules');
exports.addListener = function( evt, eh ) {
ee.addListener( evt, eh );
//TODO as soon as an event handler is added it needs to receive the full list of existing and activated rules
}
fs = require('fs');
exports.processRequest = function( user, obj, cb ) {
console.log('module manager needs to process request: ');
console.log(obj.command);
var answ = {
test: 'object',
should: 'work'
}
cb(null, answ);
}
vm = require('vm');
exports.requireFromString = function(src, name, dir) {
if(!dir) dir = __dirname;
var id = path.resolve(dir, name, name + '.vm');
var vm = require('vm'),
// FIXME not log but debug module is required to provide information to the user
sandbox = {
id: id, // use this to gather kill info
needle: require('needle'), //https://github.com/tomas/needle
log: log,
exports: {}
};
//TODO child_process to run module!
// Define max runtime per loop as 10 seconds, after that the child will be killed
// it can still be active after that if there was a timing function or a callback used...
// kill the child each time? how to determine whether there's still a token in the module?
try {
var mod = vm.runInNewContext(src, sandbox, id);
} catch (err) {
log.error('ML', 'Error running module in sandbox: ' + err.message);
}
return sandbox.exports;
};
path = require('path');
exports.loadModule = function(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 = exports.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 + '"');
}
};
events = require('events');
exports.loadModules = function(directory, callback) {
fs.readdir(path.resolve(__dirname, '..', directory), function (err, list) {
if (err) {
log.error('LM', 'loading modules directory: ' + err);
return;
}
log.info('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()) {
exports.loadModule(directory, file, callback);
}
});
});
});
};
exports.storeEventModule = function (objUser, obj, answHandler) {
try {
// TODO in the future we might want to link the modules close to the user
// and allow for e.g. private modules
// we need a child process to run this code and kill it after invocation
var m = exports.requireFromString(obj.data, obj.id);
obj.methods = Object.keys(m);
answHandler.answerSuccess('Thank you for the event module!');
db.storeEventModule(obj.id, obj);
} catch (err) {
answHandler.answerError(err.message);
console.error(err);
}
};
exports.getAllEventModules = function ( objUser, obj, answHandler ) {
db.getEventModules(function(err, obj) {
if(err) answHandler.answerError('Failed fetching event modules: ' + err.message);
else answHandler.answerSuccess(obj);
});
};
exports.storeActionModule = function (objUser, obj, answHandler) {
var m = exports.requireFromString(obj.data, obj.id);
obj.methods = Object.keys(m);
answHandler.answerSuccess('Thank you for the action module!');
db.storeActionModule(obj.id, obj);
};
exports.getAllActionModules = function ( objUser, obj, answHandler ) {
db.getActionModules(function(err, obj) {
if(err) answHandler.answerError('Failed fetching action modules: ' + err.message);
else answHandler.answerSuccess(obj);
});
};
exports.storeRule = function (objUser, obj, answHandler) {
//TODO fix, twice same logic
var cbEventModule = function (lstParams) {
return function(err, data) {
if(err) {
err.addInfo = 'fetching event module';
log.error('MM', err);
}
if(!err && data) {
if(data.params) {
lstParams.eventmodules[data.id] = data.params;
}
}
if(--semaphore === 0) answHandler.answerSuccess(lstParams);
};
};
var cbActionModule = function (lstParams) {
return function(err, data) {
if(err) {
err.addInfo = 'fetching action module';
log.error('MM', err);
}
if(!err && data) {
if(data.params) {
lstParams.actionmodules[data.id] = data.params;
}
}
if(--semaphore === 0) answHandler.answerSuccess(lstParams);
};
};
/*
Module call
-----------
Initializes the HTTP listener and its request handler.
var semaphore = 1;
var lst = {
eventmodules: {},
actionmodules: {}
};
try {
var objRule = JSON.parse(obj.data);
for(var i = 0; i < objRule.actions.length; i++) {
semaphore++;
db.getActionModule(objRule.actions[i].module.split('->')[0], cbActionModule(lst));
}
db.getEventModule(objRule.event.split('->')[0], cbEventModule(lst));
db.storeRule(objRule.id, objUser.username, obj.data);
ee.emit('newRule', objRule);
// for( var i = 0; i < eventHandlers.length; i++ ) {
// eventHandlers[i]( objRule );
// }
} catch(err) {
answHandler.answerError(err.message);
log.error('MM', err);
}
@param {Object} args
*/
};
exports = module.exports = function(args) {
_this.log = args.logger;
_this.ee = new events.EventEmitter();
db(args);
dynmod(args);
return module.exports;
};
exports.addListener = function(evt, eh) {
return _this.ee.addListener(evt, eh);
};
exports.processRequest = function(user, obj, cb) {
var answ;
if (commandFunctions[obj.command]) {
return answ = commandFunctions[obj.command](user, obj, cb);
} else {
return cb({
code: 404,
message: 'Strange request!'
});
}
};
commandFunctions = {
forge_event_poller: function(user, obj, cb) {
var answ;
answ = {
code: 200
};
return db.getEventPoller(obj.id, function(err, mod) {
var cm, id, name, src, _ref;
if (mod) {
answ.code = 409;
answ.message = 'Event Poller module name already existing: ' + obj.id;
} else {
src = obj.data;
cm = dynmod.compileString(src, obj.id, {}, obj.lang);
answ = cm.answ;
if (answ.code === 200) {
events = [];
_ref = cm.module;
for (name in _ref) {
id = _ref[name];
events.push(name);
}
_this.log.info("CM | Storing new eventpoller with events " + events);
answ.message = "Event Poller module successfully stored! Found following event(s): " + events;
db.storeEventPoller(obj.id, user.username, {
code: obj.data,
lang: obj.lang,
params: obj.params,
events: events
});
if (obj["public"] === 'true') {
db.publishEventPoller(obj.id);
}
}
}
return cb(answ);
});
},
get_event_pollers: function(user, obj, cb) {
return db.getAvailableEventPollerIds(user.username, function(err, obj) {
var fGetEvents, id, oRes, sem, _i, _len, _results;
oRes = {};
sem = obj.length;
fGetEvents = function(id) {
return db.getEventPoller(id, function(err, obj) {
oRes[id] = obj.events;
if (--sem === 0) {
return cb({
code: 200,
message: oRes
});
}
});
};
_results = [];
for (_i = 0, _len = obj.length; _i < _len; _i++) {
id = obj[_i];
_results.push(fGetEvents(id));
}
return _results;
});
},
get_event_poller_params: function(user, obj, cb) {
return db.getEventPollerRequiredParams(obj.id, function(err, obj) {
return cb({
code: 200,
message: obj
});
});
},
get_action_invokers: function(user, obj, cb) {
return db.getAvailableActionInvokerIds(user.username, function(err, obj) {
var fGetActions, id, oRes, sem, _i, _len, _results;
oRes = {};
sem = obj.length;
fGetActions = function(id) {
return db.getActionInvoker(id, function(err, obj) {
oRes[id] = obj.actions;
if (--sem === 0) {
return cb({
code: 200,
message: oRes
});
}
});
};
_results = [];
for (_i = 0, _len = obj.length; _i < _len; _i++) {
id = obj[_i];
_results.push(fGetActions(id));
}
return _results;
});
},
get_action_invoker_params: function(user, obj, cb) {
return db.getActionInvokerRequiredParams(obj.id, function(err, obj) {
return cb({
code: 200,
message: obj
});
});
},
forge_action_invoker: function(user, obj, cb) {
var answ;
answ = {
code: 200
};
return db.getActionInvoker(obj.id, function(err, mod) {
var actions, cm, id, name, src, _ref;
if (mod) {
answ.code = 409;
answ.message = 'Action Invoker module name already existing: ' + obj.id;
} else {
src = obj.data;
cm = dynmod.compileString(src, obj.id, {}, obj.lang);
answ = cm.answ;
if (answ.code === 200) {
actions = [];
_ref = cm.module;
for (name in _ref) {
id = _ref[name];
actions.push(name);
}
_this.log.info("CM | Storing new eventpoller with actions " + actions);
answ.message = "Action Invoker module successfully stored! Found following action(s): " + actions;
db.storeActionInvoker(obj.id, user.username, {
code: obj.data,
lang: obj.lang,
params: obj.params,
actions: actions
});
if (obj["public"] === 'true') {
db.publishActionInvoker(obj.id);
}
}
}
return cb(answ);
});
},
get_rules: function(user, obj, cb) {
return console.log('CM | Implement get_rules');
},
forge_rule: function(user, obj, cb) {
obj.event = JSON.parse(obj.event);
console.log(obj);
return db.getRule(obj.id, function(err, objRule) {
var answ, id, modules, params, rule;
if (objRule !== null) {
answ = {
code: 409,
message: 'Rule name already existing!'
};
} else {
answ = {
code: 200,
message: 'Rule stored and activated!'
};
rule = {
id: obj.id,
event: "" + obj.event.module + " -> " + obj.event["function"],
conditions: JSON.parse(obj.conditions),
actions: JSON.parse(obj.actions)
};
console.log(rule);
modules = JSON.parse(obj.event.action_params);
console.log('store rule');
db.storeRule(rule.id, JSON.stringify(rule));
console.log('link rule');
db.linkRule(rule.id, user.username);
console.log('activate rule');
db.activateRule(rule.id, user.username);
console.log('store event params');
db.storeEventUserParams(obj.event.module, user.username, obj.event_params);
console.log('store action params');
for (id in modules) {
params = modules[id];
db.storeActionUserParams(id, user.username, params);
}
_this.ee.emit('newRule', rule);
}
return cb(answ);
});
}
};
}).call(this);

View file

@ -0,0 +1,84 @@
// Generated by CoffeeScript 1.6.3
/*
Dynamic Modules
===============
> Compiles CoffeeScript modules and loads JS modules in a VM, together
> with only a few allowed node.js modules.
*/
(function() {
var cs, exports, needle, vm,
_this = this;
vm = require('vm');
needle = require('needle');
cs = require('coffee-script');
/*
Module call
-----------
Initializes the dynamic module handler.
@param {Object} args
*/
exports = module.exports = function(args) {
_this.log = args.logger;
return module.exports;
};
/*
Try to run a JS module from a string, together with the
given parameters. If it is written in CoffeeScript we
compile it first into JS.
@public compileString ( *src, id, params, lang* )
@param {String} src
@param {String} id
@param {Object} params
@param {String} lang
*/
exports.compileString = function(src, id, params, lang) {
var answ, err, ret, sandbox;
answ = {
code: 200,
message: 'Successfully compiled'
};
src = "'use strict;'\n" + src;
if (lang === '0') {
try {
src = cs.compile(src);
} catch (_error) {
err = _error;
answ.code = 400;
answ.message = 'Compilation of CoffeeScript failed at line ' + err.location.first_line;
}
}
sandbox = {
id: id,
params: params,
needle: needle,
log: _this.log,
exports: {}
};
try {
vm.runInNewContext(src, sandbox, id + '.vm');
} catch (_error) {
err = _error;
answ.code = 400;
answ.message = 'Loading Module failed: ' + err.message;
}
return ret = {
answ: answ,
module: sandbox.exports
};
};
}).call(this);

View file

@ -49,7 +49,7 @@ Persistence
connect_timeout: 2000
});
_this.db.on('error', function(err) {
if (err.message.indexOf('ECONNREFUSED' > -1)) {
if (err.message.indexOf('ECONNREFUSED') > -1) {
_this.connRefused = true;
return _this.log.error(err, 'DB | Wrong port?');
}
@ -284,14 +284,20 @@ Persistence
this.setname = setname;
this.db = db;
this.log = log;
this.deleteParameters = __bind(this.deleteParameters, this);
this.getParametersIds = __bind(this.getParametersIds, this);
this.getParameters = __bind(this.getParameters, this);
this.storeParameters = __bind(this.storeParameters, this);
this.deleteUserParameters = __bind(this.deleteUserParameters, this);
this.getUserParametersIds = __bind(this.getUserParametersIds, this);
this.getUserParameters = __bind(this.getUserParameters, this);
this.storeUserParameters = __bind(this.storeUserParameters, this);
this.deleteModule = __bind(this.deleteModule, this);
this.getModules = __bind(this.getModules, this);
this.getModuleIds = __bind(this.getModuleIds, this);
this.getAvailableModuleIds = __bind(this.getAvailableModuleIds, this);
this.getModuleParams = __bind(this.getModuleParams, this);
this.getModule = __bind(this.getModule, this);
this.unpublish = __bind(this.unpublish, this);
this.publish = __bind(this.publish, this);
this.unlinkModule = __bind(this.unlinkModule, this);
this.linkModule = __bind(this.linkModule, this);
this.storeModule = __bind(this.storeModule, this);
this.log.info("DB | Instantiated indexed modules for '" + this.setname + "'");
}
@ -299,12 +305,44 @@ Persistence
IndexedModules.prototype.storeModule = function(mId, data) {
this.log.info("DB | storeModule(" + this.setname + "): " + mId);
this.db.sadd("" + this.setname + "s", mId, replyHandler("Storing '" + this.setname + "' key '" + mId + "'"));
return this.db.set("" + this.setname + ":" + mId, data, replyHandler("Storing '" + this.setname + ":" + mId + "'"));
return this.db.hmset("" + this.setname + ":" + mId, data, replyHandler("Storing '" + this.setname + ":" + mId + "'"));
};
IndexedModules.prototype.linkModule = function(mId, userId) {
this.log.info("DB | linkModule(" + this.setname + "): " + mId + " to " + userId);
this.db.sadd("" + this.setname + ":" + mId + ":users", userId, replyHandler("Linking '" + this.setname + ":" + mId + ":users' " + userId));
return this.db.sadd("user:" + userId + ":" + this.setname + "s", mId, replyHandler("Linking 'user:" + userId + ":" + this.setname + "s' " + mId));
};
IndexedModules.prototype.unlinkModule = function(mId, userId) {
this.log.info("DB | unlinkModule(" + this.setname + "): " + mId + " to " + userId);
this.db.srem("" + this.setname + ":" + mId + ":users", userId, replyHandler("Unlinking '" + this.setname + ":" + mId + ":users' " + userId));
return this.db.srem("user:" + userId + ":" + this.setname + "s", mId, replyHandler("Unlinking 'user:" + userId + ":" + this.setname + "s' " + mId));
};
IndexedModules.prototype.publish = function(mId) {
this.log.info("DB | publish(" + this.setname + "): " + mId);
return this.db.sadd("public-" + this.setname + "s", mId, replyHandler("Publishing '" + this.setname + "' key '" + mId + "'"));
};
IndexedModules.prototype.unpublish = function(mId) {
this.log.info("DB | unpublish(" + this.setname + "): " + mId);
return this.db.srem("public-" + this.setname + "s", mId, replyHandler("Unpublishing '" + this.setname + "' key '" + mId + "'"));
};
IndexedModules.prototype.getModule = function(mId, cb) {
this.log.info("DB | getModule('" + this.setname + "): " + mId + "'");
return this.db.get("" + this.setname + ":" + mId, cb);
return this.db.hgetall("" + this.setname + ":" + mId, cb);
};
IndexedModules.prototype.getModuleParams = function(mId, cb) {
this.log.info("DB | getModule('" + this.setname + "): " + mId + "'");
return this.db.hget("" + this.setname + ":" + mId, "params", cb);
};
IndexedModules.prototype.getAvailableModuleIds = function(userId, cb) {
this.log.info("DB | getPublicModuleIds(" + this.setname + ")");
return this.db.sunion("public-" + this.setname + "s", "user:" + userId + ":" + this.setname + "s", cb);
};
IndexedModules.prototype.getModuleIds = function(cb) {
@ -323,26 +361,26 @@ Persistence
return this.db.del("" + this.setname + ":" + mId, replyHandler("Deleting '" + this.setname + ":" + mId + "'"));
};
IndexedModules.prototype.storeParameters = function(mId, userId, data) {
this.log.info("DB | storeParameters(" + this.setname + "): '" + mId + ":" + userId + "'");
IndexedModules.prototype.storeUserParameters = function(mId, userId, data) {
this.log.info("DB | storeUserParameters(" + this.setname + "): '" + mId + ":" + userId + "'");
this.db.sadd("" + this.setname + "-params", "" + mId + ":" + userId, replyHandler("Storing '" + this.setname + "' module parameters key '" + mId + "'"));
return this.db.set("" + this.setname + "-params:" + mId + ":" + userId, encrypt(data), replyHandler("Storing '" + this.setname + "' module parameters '" + mId + ":" + userId + "'"));
};
IndexedModules.prototype.getParameters = function(mId, userId, cb) {
this.log.info("DB | getParameters(" + this.setname + "): '" + mId + ":" + userId + "'");
IndexedModules.prototype.getUserParameters = function(mId, userId, cb) {
this.log.info("DB | getUserParameters(" + this.setname + "): '" + mId + ":" + userId + "'");
return this.db.get("" + this.setname + "-params:" + mId + ":" + userId, function(err, data) {
return cb(err, decrypt(data));
});
};
IndexedModules.prototype.getParametersIds = function(cb) {
this.log.info("DB | getParametersIds(" + this.setname + ")");
IndexedModules.prototype.getUserParametersIds = function(cb) {
this.log.info("DB | getUserParametersIds(" + this.setname + ")");
return this.db.smembers("" + this.setname + "-params", cb);
};
IndexedModules.prototype.deleteParameters = function(mId, userId) {
this.log.info("DB | deleteParameters(" + this.setname + "): '" + mId + ":" + userId + "'");
IndexedModules.prototype.deleteUserParameters = function(mId, userId) {
this.log.info("DB | deleteUserParameters(" + this.setname + "): '" + mId + ":" + userId + "'");
this.db.srem("" + this.setname + "-params", "" + mId + ":" + userId, replyHandler("Deleting '" + this.setname + "-params' key '" + mId + ":" + userId + "'"));
return this.db.del("" + this.setname + "-params:" + mId + ":" + userId, replyHandler("Deleting '" + this.setname + "-params:" + mId + ":" + userId + "'"));
};
@ -359,14 +397,40 @@ Persistence
/*
Store a string representation of an action invoker in the DB.
@public storeActionInvoker ( *aiId, data* )
@public storeActionInvoker ( *aiId, userId, data* )
@param {String} aiId
@param {String} userId
@param {String} data
*/
exports.storeActionInvoker = function(aiId, data) {
return _this.ai.storeModule(aiId, data);
exports.storeActionInvoker = function(aiId, userId, data) {
_this.ai.storeModule(aiId, data);
return _this.ai.linkModule(aiId, userId);
};
/*
Make an action invoker public.
@public publishActionInvoker ( *aiId* )
@param {String} aiId
*/
exports.publishActionInvoker = function(aiId) {
return _this.ai.publish(aiId);
};
/*
Make an action invoker private.
@public unpublishActionInvoker ( *aiId* )
@param {String} aiId
*/
exports.unpublishActionInvoker = function(aiId) {
return _this.ai.unpublish(aiId);
};
/*
@ -382,6 +446,19 @@ Persistence
return _this.ai.getModule(aiId, cb);
};
/*
Query the DB for action invoker required params and pass it to cb(err, obj).
@public getActionInvokerEventPollerRequiredParams( *epId, cb* )
@param {String} epId
@param {function} cb
*/
exports.getActionInvokerRequiredParams = function(epId, cb) {
return _this.ai.getModuleParams(epId, cb);
};
/*
Fetch all action invoker IDs and hand them to cb(err, obj).
@ -394,6 +471,31 @@ Persistence
return _this.ai.getModuleIds(cb);
};
/*
Fetch all available actin invoker IDs for a user and
hand them to cb(err, obj).
@public getAvailableActionInvokerIds( *userId, cb* )
@param {function} cb
*/
exports.getAvailableActionInvokerIds = function(userId, cb) {
return _this.ai.getAvailableModuleIds(userId, cb);
};
/*
Fetch all public action invoker IDs and hand them to cb(err, obj).
@public getPublicActionInvokerIds( *cb* )
@param {function} cb
*/
exports.getPublicActionInvokerIds = function(cb) {
return _this.ai.getPublicModuleIds(cb);
};
/*
Fetch all action invokers and hand them to cb(err, obj).
@ -421,54 +523,54 @@ Persistence
/*
Store user-specific action invoker parameters .
@public storeActionParams( *userId, aiId, data* )
@public storeActionUserParams( *userId, aiId, data* )
@param {String} userId
@param {String} aiId
@param {String} data
*/
exports.storeActionParams = function(aiId, userId, data) {
return _this.ai.storeParameters(aiId, userId, data);
exports.storeActionUserParams = function(aiId, userId, data) {
return _this.ai.storeUserParameters(aiId, userId, data);
};
/*
Query the DB for user-specific action module parameters,
and pass it to cb(err, obj).
@public getActionParams( *userId, aiId, cb* )
@public getActionUserParams( *userId, aiId, cb* )
@param {String} userId
@param {String} aiId
@param {function} cb
*/
exports.getActionParams = function(aiId, userId, cb) {
return _this.ai.getParameters(aiId, userId, cb);
exports.getActionUserParams = function(aiId, userId, cb) {
return _this.ai.getUserParameters(aiId, userId, cb);
};
/*
Fetch all action params IDs and hand them to cb(err, obj).
@public getActionParamsIds( *cb* )
@public getActionUserParamsIds( *cb* )
@param {function} cb
*/
exports.getActionParamsIds = function(cb) {
return _this.ai.getParametersIds(cb);
exports.getActionUserParamsIds = function(cb) {
return _this.ai.getUserParametersIds(cb);
};
/*
Fetch all action modules and hand them to cb(err, obj).
@public deleteActionParams( *cb* )
@public deleteActionUserParams( *cb* )
@param {function} cb
*/
exports.deleteActionParams = function(aiId, userId) {
return _this.ai.deleteParameters(aiId, userId);
exports.deleteActionUserParams = function(aiId, userId) {
return _this.ai.deleteUserParameters(aiId, userId);
};
/*
@ -479,14 +581,40 @@ Persistence
/*
Store a string representation of an event poller in the DB.
@public storeEventPoller ( *epId, data* )
@public storeEventPoller ( *epId, userId, data* )
@param {String} epId
@param {String} userId
@param {String} data
*/
exports.storeEventPoller = function(epId, data) {
return _this.ep.storeModule(epId, data);
exports.storeEventPoller = function(epId, userId, data) {
_this.ep.storeModule(epId, data);
return _this.ep.linkModule(epId, userId);
};
/*
Make an event poller public.
@public publishEventPoller ( *epId* )
@param {String} epId
*/
exports.publishEventPoller = function(epId) {
return _this.ep.publish(epId);
};
/*
Make an event poller private.
@public unpublishEventPoller ( *epId* )
@param {String} epId
*/
exports.unpublishEventPoller = function(epId) {
return _this.ep.unpublish(epId);
};
/*
@ -502,6 +630,19 @@ Persistence
return _this.ep.getModule(epId, cb);
};
/*
Query the DB for event poller required params and pass it to cb(err, obj).
@public getEventPollerRequiredParams( *epId, cb* )
@param {String} epId
@param {function} cb
*/
exports.getEventPollerRequiredParams = function(epId, cb) {
return _this.ep.getModuleParams(epId, cb);
};
/*
Fetch all event poller IDs and hand them to cb(err, obj).
@ -514,6 +655,31 @@ Persistence
return _this.ep.getModuleIds(cb);
};
/*
Fetch all available event poller IDs for a user and
hand them to cb(err, obj).
@public getAvailableEventPollerIds( *userId, cb* )
@param {function} cb
*/
exports.getAvailableEventPollerIds = function(userId, cb) {
return _this.ep.getAvailableModuleIds(userId, cb);
};
/*
Fetch all public event poller IDs and hand them to cb(err, obj).
@public getPublicEventPollerIds( *cb* )
@param {function} cb
*/
exports.getPublicEventPollerIds = function(cb) {
return _this.ep.getPublicModuleIds(cb);
};
/*
Fetch all event pollers and hand them to cb(err, obj).
@ -541,54 +707,54 @@ Persistence
/*
Store user-specific event poller parameters .
@public storeEventParams( *userId, epId, data* )
@public storeEventUserParams( *userId, epId, data* )
@param {String} userId
@param {String} epId
@param {String} data
*/
exports.storeEventParams = function(epId, userId, data) {
return _this.ep.storeParameters(epId, userId, data);
exports.storeEventUserParams = function(epId, userId, data) {
return _this.ep.storeUserParameters(epId, userId, data);
};
/*
Query the DB for user-specific event module parameters,
and pass it to cb(err, obj).
@public getEventParams( *userId, epId, cb* )
@public getEventUserParams( *userId, epId, cb* )
@param {String} userId
@param {String} epId
@param {function} cb
*/
exports.getEventParams = function(epId, userId, cb) {
return _this.ep.getParameters(epId, userId, cb);
exports.getEventUserParams = function(epId, userId, cb) {
return _this.ep.getUserParameters(epId, userId, cb);
};
/*
Fetch all event params IDs and hand them to cb(err, obj).
@public getEventParamsIds( *cb* )
@public getEventUserParamsIds( *cb* )
@param {function} cb
*/
exports.getEventParamsIds = function(cb) {
return _this.ep.getParametersIds(cb);
exports.getEventUserParamsIds = function(cb) {
return _this.ep.getUserParametersIds(cb);
};
/*
Fetch all event modules and hand them to cb(err, obj).
@public deleteEventParams( *cb* )
@public deleteEventUserParams( *cb* )
@param {function} cb
*/
exports.deleteEventParams = function(epId, userId) {
return _this.ep.deleteParameters(epId, userId);
exports.deleteEventUserParams = function(epId, userId) {
return _this.ep.deleteUserParameters(epId, userId);
};
/*

View file

@ -11,7 +11,7 @@ Request Handler
(function() {
var answerHandler, crypto, db, dirHandlers, exports, fs, getHandlerPath, getRemoteScripts, getScript, getTemplate, mustache, path, qs, renderPage,
var crypto, db, dirHandlers, exports, fs, getHandlerPath, getRemoteScripts, getScript, getTemplate, mustache, path, qs, renderPage,
_this = this;
db = require('./persistence');
@ -33,9 +33,14 @@ Request Handler
_this.log = args.logger;
_this.userRequestHandler = args['request-service'];
_this.objAdminCmds = {
shutdown: function(args, answerHandler) {
answerHandler.answerSuccess('Shutting down... BYE!');
return setTimeout(args['shutdown-function'], 500);
shutdown: function(obj, cb) {
var data;
data = {
code: 200,
message: 'Shutting down... BYE!'
};
setTimeout(args['shutdown-function'], 500);
return cb(null, data);
}
};
db(args);
@ -67,13 +72,24 @@ Request Handler
return body += data;
});
return req.on('end', function() {
var obj;
obj = qs.parse(body);
if (obj && obj.event && obj.eventid) {
resp.send('Thank you for the event: ' + obj.event + ' (' + obj.eventid + ')!');
return db.pushEvent(obj);
var answ, obj, rand, timestamp;
if (req.session && req.session.user) {
obj = qs.parse(body);
if (obj && obj.event) {
timestamp = (new Date).toISOString();
rand = (Math.floor(Math.random() * 10e9)).toString(16).toUpperCase();
obj.eventid = "" + obj.event + "_" + timestamp + "_" + rand;
answ = {
code: 200,
message: "Thank you for the event: " + obj.eventid
};
resp.send(answ.code, answ);
return db.pushEvent(obj);
} else {
return resp.send(400, 'Your event was missing important parameters!');
}
} else {
return resp.send(400, 'Your event was missing important parameters!');
return resp.send(401, 'Please login!');
}
});
};
@ -199,12 +215,13 @@ Request Handler
renderPage = function(name, req, resp, msg) {
var code, content, data, err, menubar, pathSkel, remote_scripts, script, skeleton, view;
var code, content, data, err, menubar, page, pageElements, pathSkel, remote_scripts, script, skeleton;
pathSkel = path.join(dirHandlers, 'skeleton.html');
skeleton = fs.readFileSync(pathSkel, 'utf8');
code = 200;
data = {
message: msg
message: msg,
user: req.session.user
};
try {
script = getScript(name);
@ -219,22 +236,19 @@ Request Handler
content = getTemplate('error');
script = getScript('error');
code = 404;
data = {
message: 'Invalid Page!'
};
data.message = 'Invalid Page!';
}
content = mustache.render(content, data);
if (req.session.user) {
menubar = getTemplate('menubar');
}
view = {
user: req.session.user,
pageElements = {
content: content,
script: script,
remote_scripts: remote_scripts,
menubar: menubar
};
return resp.send(code, mustache.render(skeleton, view));
page = mustache.render(skeleton, pageElements);
return resp.send(code, mustache.render(page, data));
};
/*
@ -272,9 +286,7 @@ Request Handler
exports.handleUserCommand = function(req, resp) {
var body;
if (!req.session || !req.session.user) {
return resp.send(401, 'Login first!');
} else {
if (req.session && req.session.user) {
body = '';
req.on('data', function(data) {
return body += data;
@ -282,14 +294,12 @@ Request Handler
return req.on('end', function() {
var obj;
obj = qs.parse(body);
return _this.userRequestHandler(req.session.user, obj, function(err, obj) {
if (err) {
return resp.send(404, 'Rethink your request!');
} else {
return resp.send(obj);
}
return _this.userRequestHandler(req.session.user, obj, function(obj) {
return resp.send(obj.code, obj);
});
});
} else {
return resp.send(401, 'Login first!');
}
};
@ -331,59 +341,27 @@ Request Handler
exports.handleAdminCommand = function(req, resp) {
var q, _base, _name;
if (req.session && req.session.user) {
if (req.session.user.isAdmin === "true") {
q = req.query;
_this.log.info('RH | Received admin request: ' + req.originalUrl);
if (q.cmd) {
return typeof (_base = _this.objAdminCmds)[_name = q.cmd] === "function" ? _base[_name](q, answerHandler(req, resp, true)) : void 0;
var body;
if (req.session && req.session.user && req.session.user.isAdmin === "true") {
body = '';
req.on('data', function(data) {
return body += data;
});
return req.on('end', function() {
var obj;
obj = qs.parse(body);
_this.log.info('RH | Received admin request: ' + obj.command);
if (obj.command && _this.objAdminCmds[obj.command]) {
return _this.objAdminCmds[obj.command](obj, function(err, obj) {
return resp.send(obj.code, obj);
});
} else {
return resp.send(404, 'Command unknown!');
}
} else {
return resp.send(renderPage('unauthorized', req.session));
}
});
} else {
return resp.sendfile(getHandlerPath('login'));
return resp.send(401, 'You need to be logged in as admin!');
}
};
answerHandler = function(req, resp, ntbr) {
var hasBeenAnswered, needsToBeRendered, request, response, ret;
request = req;
response = resp;
needsToBeRendered = ntbr;
hasBeenAnswered = false;
ret = {
answerSuccess: function(msg) {
if (!hasBeenAnswered) {
if (needsToBeRendered) {
response.send(renderPage('command_answer', request.session, msg));
} else {
response.send(msg);
}
}
return hasBeenAnswered = true;
},
answerError: function(msg) {
if (!hasBeenAnswered) {
if (needsToBeRendered) {
response.send(400, renderPage('error', request.session, msg));
} else {
response.send(400, msg);
}
}
return hasBeenAnswered = true;
},
isAnswered: function() {
return hasBeenAnswered;
}
};
setTimeout(function() {
return ret.answerError('Strange... maybe try again?');
}, 5000);
return ret;
};
}).call(this);

View file

@ -139,73 +139,76 @@ exports.EventQueue =
# Test ACTION INVOKER
###
exports.ActionInvoker =
setUp: ( cb ) =>
@action1id = 'test-action-invoker_1'
@action2id = 'test-action-invoker_2'
@action1 =
code: 'unit-test action invoker 1 content'
reqparams:'[param11,param12]'
@action2 =
code: 'unit-test action invoker 2 content'
reqparams:'[param21,param22]'
cb()
tearDown: ( cb ) =>
@db.deleteActionInvoker @action1id
@db.deleteActionInvoker @action2id
cb()
testCreateAndRead: ( test ) =>
test.expect 3
id = 'test-action-invoker'
action = 'unit-test action invoker content'
# store an entry to start with
@db.storeActionInvoker id, action
@db.storeActionInvoker @action1id, @action1
# test that the ID shows up in the set
@db.getActionInvokerIds ( err , obj ) =>
test.ok id in obj,
test.ok @action1id in obj,
'Expected key not in action-invokers set'
# the retrieved object really is the one we expected
@db.getActionInvoker id, ( err , obj ) =>
test.strictEqual obj, action,
@db.getActionInvoker @action1id, ( err , obj ) =>
test.deepEqual obj, @action1,
'Retrieved Action Invoker is not what we expected'
# Ensure the action invoker is in the list of all existing ones
@db.getActionInvokers ( err , obj ) =>
test.deepEqual action, obj[id],
test.deepEqual @action1, obj[@action1id],
'Action Invoker ist not in result set'
@db.deleteActionInvoker id
test.done()
testUpdate: ( test ) =>
test.expect 2
id = 'test-action-invoker'
action = 'unit-test action invoker content'
actionNew = 'unit-test action invoker new content'
# store an entry to start with
@db.storeActionInvoker id, action
@db.storeActionInvoker id, actionNew
@db.storeActionInvoker @action1id, @action1
@db.storeActionInvoker @action1id, @action2
# the retrieved object really is the one we expected
@db.getActionInvoker id, ( err , obj ) =>
test.strictEqual obj, actionNew,
@db.getActionInvoker @action1id, ( err , obj ) =>
test.deepEqual obj, @action2,
'Retrieved Action Invoker is not what we expected'
# Ensure the action invoker is in the list of all existing ones
@db.getActionInvokers ( err , obj ) =>
test.deepEqual actionNew, obj[id],
test.deepEqual @action2, obj[@action1id],
'Action Invoker ist not in result set'
@db.deleteActionInvoker id
test.done()
testDelete: ( test ) =>
test.expect 2
id = 'test-action-invoker'
action = 'unit-test action invoker content'
# store an entry to start with
@db.storeActionInvoker id, action
@db.storeActionInvoker @action1id, @action1
# Ensure the action invoker has been deleted
@db.deleteActionInvoker id
@db.getActionInvoker id, ( err , obj ) =>
@db.deleteActionInvoker @action1id
@db.getActionInvoker @action1id, ( err , obj ) =>
test.strictEqual obj, null,
'Action Invoker still exists'
# Ensure the ID has been removed from the set
@db.getActionInvokerIds ( err , obj ) =>
test.ok id not in obj,
test.ok @action1id not in obj,
'Action Invoker key still exists in set'
test.done()
@ -214,33 +217,29 @@ exports.ActionInvoker =
test.expect 3
semaphore = 2
action1name = 'test-action-invoker_1'
action2name = 'test-action-invoker_2'
action1 = 'unit-test action invoker 1 content'
action2 = 'unit-test action invoker 2 content'
fCheckInvoker = ( modname, mod ) =>
myTest = test
forkEnds = () ->
myTest.done() if --semaphore is 0
( err, obj ) =>
myTest.strictEqual mod, obj,
myTest.deepEqual mod, obj,
"Invoker #{ modname } does not equal the expected one"
@db.deleteActionInvoker modname
forkEnds()
@db.storeActionInvoker action1name, action1
@db.storeActionInvoker action2name, action2
@db.storeActionInvoker @action1id, @action1
@db.storeActionInvoker @action2id, @action2
@db.getActionInvokerIds ( err, obj ) =>
test.ok action1name in obj and action2name in obj,
test.ok @action1id in obj and @action2id in obj,
'Not all action invoker Ids in set'
@db.getActionInvoker action1name, fCheckInvoker action1name, action1
@db.getActionInvoker action2name, fCheckInvoker action2name, action2
@db.getActionInvoker @action1id, fCheckInvoker @action1id, @action1
@db.getActionInvoker @action2id, fCheckInvoker @action2id, @action2
###
# Test ACTION INVOKER PARAMS
###
#TODO add tests for required parameters per module
exports.ActionInvokerParams =
testCreateAndRead: ( test ) =>
test.expect 2
@ -250,18 +249,18 @@ exports.ActionInvokerParams =
params = 'shouldn\'t this be an object?'
# store an entry to start with
@db.storeActionParams actionId, userId, params
@db.storeActionUserParams actionId, userId, params
# test that the ID shows up in the set
@db.getActionParamsIds ( err, obj ) =>
@db.getActionUserParamsIds ( err, obj ) =>
test.ok actionId+':'+userId in obj,
'Expected key not in action-params set'
# the retrieved object really is the one we expected
@db.getActionParams actionId, userId, ( err, obj ) =>
@db.getActionUserParams actionId, userId, ( err, obj ) =>
test.strictEqual obj, params,
'Retrieved action params is not what we expected'
@db.deleteActionParams actionId, userId
@db.deleteActionUserParams actionId, userId
test.done()
testUpdate: ( test ) =>
@ -273,14 +272,14 @@ exports.ActionInvokerParams =
paramsNew = 'shouldn\'t this be a new object?'
# store an entry to start with
@db.storeActionParams actionId, userId, params
@db.storeActionParams actionId, userId, paramsNew
@db.storeActionUserParams actionId, userId, params
@db.storeActionUserParams actionId, userId, paramsNew
# the retrieved object really is the one we expected
@db.getActionParams actionId, userId, ( err, obj ) =>
@db.getActionUserParams actionId, userId, ( err, obj ) =>
test.strictEqual obj, paramsNew,
'Retrieved action params is not what we expected'
@db.deleteActionParams actionId, userId
@db.deleteActionUserParams actionId, userId
test.done()
testDelete: ( test ) =>
@ -291,15 +290,15 @@ exports.ActionInvokerParams =
params = 'shouldn\'t this be an object?'
# store an entry to start with and delte it right away
@db.storeActionParams actionId, userId, params
@db.deleteActionParams actionId, userId
@db.storeActionUserParams actionId, userId, params
@db.deleteActionUserParams actionId, userId
# Ensure the action params have been deleted
@db.getActionParams actionId, userId, ( err, obj ) =>
@db.getActionUserParams actionId, userId, ( err, obj ) =>
test.strictEqual obj, null,
'Action params still exists'
# Ensure the ID has been removed from the set
@db.getActionParamsIds ( err, obj ) =>
@db.getActionUserParamsIds ( err, obj ) =>
test.ok actionId+':'+userId not in obj,
'Action Params key still exists in set'
test.done()
@ -309,73 +308,76 @@ exports.ActionInvokerParams =
# Test EVENT POLLER
###
exports.EventPoller =
setUp: ( cb ) =>
@event1id = 'test-event-poller_1'
@event2id = 'test-event-poller_2'
@event1 =
code: 'unit-test event poller 1 content'
reqparams:'[param11,param12]'
@event2 =
code: 'unit-test event poller 2 content'
reqparams:'[param21,param22]'
cb()
tearDown: ( cb ) =>
@db.deleteEventPoller @event1id
@db.deleteEventPoller @event2id
cb()
testCreateAndRead: ( test ) =>
test.expect 3
id = 'test-event-poller'
event = 'unit-test event poller content'
# store an entry to start with
@db.storeEventPoller id, event
@db.storeEventPoller @event1id, @event1
# test that the ID shows up in the set
@db.getEventPollerIds ( err , obj ) =>
test.ok id in obj,
test.ok @event1id in obj,
'Expected key not in event-pollers set'
# the retrieved object really is the one we expected
@db.getEventPoller id, ( err , obj ) =>
test.strictEqual obj, event,
@db.getEventPoller @event1id, ( err , obj ) =>
test.deepEqual obj, @event1,
'Retrieved Event Poller is not what we expected'
# Ensure the event poller is in the list of all existing ones
@db.getEventPollers ( err , obj ) =>
test.deepEqual event, obj[id],
test.deepEqual @event1, obj[@event1id],
'Event Poller ist not in result set'
@db.deleteEventPoller id
test.done()
testUpdate: ( test ) =>
test.expect 2
id = 'test-event-poller'
event = 'unit-test event poller content'
eventNew = 'unit-test event poller new content'
# store an entry to start with
@db.storeEventPoller id, event
@db.storeEventPoller id, eventNew
@db.storeEventPoller @event1id, @event1
@db.storeEventPoller @event1id, @event2
# the retrieved object really is the one we expected
@db.getEventPoller id, ( err , obj ) =>
test.strictEqual obj, eventNew,
@db.getEventPoller @event1id, ( err , obj ) =>
test.deepEqual obj, @event2,
'Retrieved Event Poller is not what we expected'
# Ensure the event poller is in the list of all existing ones
@db.getEventPollers ( err , obj ) =>
test.deepEqual eventNew, obj[id],
test.deepEqual @event2, obj[@event1id],
'Event Poller ist not in result set'
@db.deleteEventPoller id
test.done()
testDelete: ( test ) =>
test.expect 2
id = 'test-event-poller'
event = 'unit-test event poller content'
# store an entry to start with
@db.storeEventPoller id, event
@db.storeEventPoller @event1id, @event1
# Ensure the event poller has been deleted
@db.deleteEventPoller id
@db.getEventPoller id, ( err , obj ) =>
@db.deleteEventPoller @event1id
@db.getEventPoller @event1id, ( err , obj ) =>
test.strictEqual obj, null,
'Event Poller still exists'
# Ensure the ID has been removed from the set
@db.getEventPollerIds ( err , obj ) =>
test.ok id not in obj,
test.ok @event1id not in obj,
'Event Poller key still exists in set'
test.done()
@ -384,28 +386,23 @@ exports.EventPoller =
test.expect 3
semaphore = 2
event1name = 'test-event-poller_1'
event2name = 'test-event-poller_2'
event1 = 'unit-test event poller 1 content'
event2 = 'unit-test event poller 2 content'
fCheckPoller = ( modname, mod ) =>
fCheckInvoker = ( modname, mod ) =>
myTest = test
forkEnds = () ->
myTest.done() if --semaphore is 0
( err, obj ) =>
myTest.strictEqual mod, obj,
myTest.deepEqual mod, obj,
"Invoker #{ modname } does not equal the expected one"
@db.deleteEventPoller modname
forkEnds()
@db.storeEventPoller event1name, event1
@db.storeEventPoller event2name, event2
@db.storeEventPoller @event1id, @event1
@db.storeEventPoller @event2id, @event2
@db.getEventPollerIds ( err, obj ) =>
test.ok event1name in obj and event2name in obj,
test.ok @event1id in obj and @event2id in obj,
'Not all event poller Ids in set'
@db.getEventPoller event1name, fCheckPoller event1name, event1
@db.getEventPoller event2name, fCheckPoller event2name, event2
@db.getEventPoller @event1id, fCheckInvoker @event1id, @event1
@db.getEventPoller @event2id, fCheckInvoker @event2id, @event2
###
@ -420,18 +417,18 @@ exports.EventPollerParams =
params = 'shouldn\'t this be an object?'
# store an entry to start with
@db.storeEventParams eventId, userId, params
@db.storeEventUserParams eventId, userId, params
# test that the ID shows up in the set
@db.getEventParamsIds ( err, obj ) =>
@db.getEventUserParamsIds ( err, obj ) =>
test.ok eventId+':'+userId in obj,
'Expected key not in event-params set'
# the retrieved object really is the one we expected
@db.getEventParams eventId, userId, ( err, obj ) =>
@db.getEventUserParams eventId, userId, ( err, obj ) =>
test.strictEqual obj, params,
'Retrieved event params is not what we expected'
@db.deleteEventParams eventId, userId
@db.deleteEventUserParams eventId, userId
test.done()
testUpdate: ( test ) =>
@ -443,14 +440,14 @@ exports.EventPollerParams =
paramsNew = 'shouldn\'t this be a new object?'
# store an entry to start with
@db.storeEventParams eventId, userId, params
@db.storeEventParams eventId, userId, paramsNew
@db.storeEventUserParams eventId, userId, params
@db.storeEventUserParams eventId, userId, paramsNew
# the retrieved object really is the one we expected
@db.getEventParams eventId, userId, ( err, obj ) =>
@db.getEventUserParams eventId, userId, ( err, obj ) =>
test.strictEqual obj, paramsNew,
'Retrieved event params is not what we expected'
@db.deleteEventParams eventId, userId
@db.deleteEventUserParams eventId, userId
test.done()
testDelete: ( test ) =>
@ -461,15 +458,15 @@ exports.EventPollerParams =
params = 'shouldn\'t this be an object?'
# store an entry to start with and delete it right away
@db.storeEventParams eventId, userId, params
@db.deleteEventParams eventId, userId
@db.storeEventUserParams eventId, userId, params
@db.deleteEventUserParams eventId, userId
# Ensure the event params have been deleted
@db.getEventParams eventId, userId, ( err, obj ) =>
@db.getEventUserParams eventId, userId, ( err, obj ) =>
test.strictEqual obj, null,
'Event params still exists'
# Ensure the ID has been removed from the set
@db.getEventParamsIds ( err, obj ) =>
@db.getEventUserParamsIds ( err, obj ) =>
test.ok eventId+':'+userId not in obj,
'Event Params key still exists in set'
test.done()

View file

@ -0,0 +1,20 @@
fOnLoad = () ->
document.title = 'Administrate'
$( '#pagetitle' ).text 'Hi {{{user.username}}}, issue your commands please:'
$( '#but_submit' ).click () ->
data =
command: $( '#inp_command' ).val()
$.post( 'admincommand', data )
.done ( data ) ->
$( '#info' ).text data.message
$( '#info' ).attr 'class', 'success'
.fail ( err ) ->
if err.responseText is ''
err.responseText = 'No Response from Server!'
$( '#info' ).text 'Error: ' + err.responseText
$( '#info' ).attr 'class', 'error'
if err.status is 401
window.location.href = 'admin'
window.addEventListener 'load', fOnLoad, true

View file

@ -1,7 +0,0 @@
fOnLoad = () ->
document.title = 'Error!'
$( '#pagetitle' ).text 'Error processing your request!'
$( '#pagetitle' ).attr 'class', 'error'
window.addEventListener 'load', fOnLoad, true

View file

@ -0,0 +1,80 @@
fOnLoad = () ->
document.title = 'Forge Action Invoker'
$( '#pagetitle' ).text "{{{user.username}}}, forge your custom action invoker!"
# Setup the ACE editor
editor = ace.edit "editor"
editor.setTheme "ace/theme/monokai"
editor.getSession().setMode "ace/mode/coffee"
editor.setShowPrintMargin false
$( '#editor_mode' ).change ( el ) ->
if $( this ).val() is '0'
editor.getSession().setMode "ace/mode/coffee"
else
editor.getSession().setMode "ace/mode/javascript"
# Add parameter list functionality
fChangeCrosses = () ->
$( '#tableParams img' ).each ( id ) ->
par = $( this ).closest 'tr'
if par.is ':last-child' or par.is ':only-child'
$( this ).hide()
else
$( this ).show()
$( '#tableParams' ).on 'click', 'img', () ->
par = $( this ).closest 'tr'
if not par.is ':last-child'
par.remove()
fChangeCrosses()
$( '#tableParams' ).on 'keyup', 'input', () ->
par = $( this ).closest( 'tr' )
if par.is ':last-child'
tr = $ '<tr>'
img = $( '<img>' ).attr 'src', 'red_cross_small.png'
inp = $( '<input>' ).attr 'type', 'text'
tr.append( $( '<td>' ).append img )
tr.append( $( '<td>' ).append inp )
par.parent().append tr
fChangeCrosses()
else if $( this ).val() is '' and not par.is ':only-child'
par.remove()
fChangeCrosses()
# Add submit button logic
$( '#but_submit' ).click () ->
if $( '#input_id' ).val() is ''
alert 'Please enter an action invoker name!'
else
listParams = []
$( '#tableParams input' ).each () ->
val = $( this ).val()
if val isnt ""
listParams.push val
obj =
id: $( '#input_id' ).val()
command: 'forge_action_invoker'
lang: $( '#editor_mode' ).val()
public: $( '#is_public' ).is ':checked'
data: editor.getValue()
params: JSON.stringify listParams
$.post( '/usercommand', obj )
.done ( data ) ->
$( '#info' ).text data.message
$( '#info' ).attr 'class', 'success'
.fail ( err ) ->
if err.responseText is ''
msg = 'No Response from Server!'
else
try
oErr = JSON.parse err.responseText
msg = oErr.message
$( '#info' ).text 'Action Invoker not stored! ' + msg
$( '#info' ).attr 'class', 'error'
if err.status is 401
window.location.href = 'forge?page=forge_action_invoker'
window.addEventListener 'load', fOnLoad, true

View file

@ -8,16 +8,25 @@ fOnLoad = () ->
editor.setTheme "ace/theme/monokai"
editor.getSession().setMode "ace/mode/json"
editor.setShowPrintMargin false
$( '#editor' ).css 'height', '400px'
$( '#editor' ).css 'width', '600px'
$('#but_submit').click () ->
$( '#but_submit' ).click () ->
try
data = JSON.parse editor.getValue()
$.post('/event', data)
$.post( '/event', data )
.done ( data ) ->
alert data
.fail (err) ->
alert 'Posting of event failed: ' + err.responseText
$( '#info' ).text data.message
$( '#info' ).attr 'class', 'success'
.fail ( err ) ->
if err.responseText is ''
err.responseText = 'No Response from Server!'
$( '#info' ).text 'Error in upload: ' + err.responseText
$( '#info' ).attr 'class', 'error'
if err.status is 401
window.location.href = 'forge?page=forge_event'
catch err
alert 'You have errors in your JSON object!'
$( '#info' ).text 'You have errors in your JSON object! ' + err
$( '#info' ).attr 'class', 'error'
window.addEventListener 'load', fOnLoad, true

View file

@ -1,9 +1,9 @@
fOnLoad = () ->
document.title = 'Forge Event Poller'
$( '#pagetitle' ).text 'Forge your custom event poller!'
$( '#pagetitle' ).text "{{{user.username}}}, forge your custom event poller!"
# Setup the ACE editor
editor = ace.edit "editor"
editor.setTheme "ace/theme/monokai"
editor.getSession().setMode "ace/mode/coffee"
@ -14,41 +14,67 @@ fOnLoad = () ->
editor.getSession().setMode "ace/mode/coffee"
else
editor.getSession().setMode "ace/mode/javascript"
$( '#but_submit' ).click () ->
try
data = JSON.parse editor.getValue()
$.post('/event', data)
.done ( data ) ->
alert data
.fail (err) ->
alert 'Posting of event failed: ' + err.responseText
catch err
alert 'You have errors in your JSON object!'
# Add parameter list functionality
fChangeCrosses = () ->
$( '#listParams img' ).each ( id ) ->
par = $( this ).parent 'tr'
$( '#tableParams img' ).each ( id ) ->
par = $( this ).closest 'tr'
if par.is ':last-child' or par.is ':only-child'
$( this ).hide()
else
$( this ).show()
$( '#listParams' ).on 'click', 'img', () ->
par = $( this ).parent 'tr'
$( '#tableParams' ).on 'click', 'img', () ->
par = $( this ).closest 'tr'
if not par.is ':last-child'
par.remove()
fChangeCrosses()
$( '#listParams' ).on 'keyup', 'input', ->
console.log 'weees'
if $( this ).parent( 'tr' ).is ':last-child'
$( '#tableParams' ).on 'keyup', 'input', () ->
par = $( this ).closest( 'tr' )
if par.is ':last-child'
tr = $ '<tr>'
td = tr.append 'td'
td.append $( '<img>' ).attr 'src', 'red_cross_small.png'
td.append $( '<input>' ).attr 'type', 'text'
$( '#listParams' ).append tr
img = $( '<img>' ).attr 'src', 'red_cross_small.png'
inp = $( '<input>' ).attr 'type', 'text'
tr.append( $( '<td>' ).append img )
tr.append( $( '<td>' ).append inp )
par.parent().append tr
fChangeCrosses()
else if $( this ).val() is '' and not par.is ':only-child'
par.remove()
fChangeCrosses()
# Add submit button logic
$( '#but_submit' ).click () ->
if $( '#input_id' ).val() is ''
alert 'Please enter an event poller name!'
else
listParams = []
$( '#tableParams input' ).each () ->
val = $( this ).val()
if val isnt ""
listParams.push val
obj =
id: $( '#input_id' ).val()
command: 'forge_event_poller'
lang: $( '#editor_mode' ).val()
public: $( '#is_public' ).is ':checked'
data: editor.getValue()
params: JSON.stringify listParams
$.post( '/usercommand', obj )
.done ( data ) ->
$( '#info' ).text data.message
$( '#info' ).attr 'class', 'success'
.fail ( err ) ->
if err.responseText is ''
msg = 'No Response from Server!'
else
try
oErr = JSON.parse err.responseText
msg = oErr.message
$( '#info' ).text 'Event Poller not stored! ' + msg
$( '#info' ).attr 'class', 'error'
if err.status is 401
window.location.href = 'forge?page=forge_event_poller'
window.addEventListener 'load', fOnLoad, true

View file

@ -0,0 +1,188 @@
fOnLoad = () ->
document.title = 'Rule Forge!'
$( '#pagetitle' ).text '{{{user.username}}}, forge your custom rule!'
# Fetch Event Poller user-specific parameters
fFetchEventParams = ( name ) ->
arr = name.split ' -> '
obj =
command: 'get_event_poller_params'
id: arr[0]
$.post( '/usercommand', obj )
.done ( data ) ->
if data.message
arrParams = JSON.parse data.message
$( '#event_poller_params table' ).remove()
if arrParams.length > 0
$( '#event_poller_params' ).text 'Required user-specific params:'
table = $ '<table>'
$( '#event_poller_params' ).append table
fAppendParam = ( name ) ->
tr = $( '<tr>' )
tr.append $( '<td>' ).css 'width', '20px'
tr.append $( '<td>' ).text name
inp = $( '<input>' ).attr( 'type', 'password' ).attr 'id', "#{ name }"
tr.append $( '<td>' ).text( ' :' ).append inp
table.append tr
fAppendParam name for name in arrParams
.fail ( err ) ->
console.log err
$( '#info' ).text 'Error fetching event poller params'
$( '#info' ).attr 'class', 'error'
#FIXME Add possibility for custom event via text input
#FIXME Add conditions
# Init Event Pollers
obj =
command: 'get_event_pollers'
$.post( '/usercommand', obj )
.done ( data ) ->
fAppendEvent = ( id, name ) ->
$( '#select_event' ).append $( '<option>' ).text id + ' -> ' + name
fAppendEvent id, name for id, name of data.message
fFetchEventParams $( '#select_event option:selected' ).text()
.fail ( err ) ->
console.log err
$( '#info' ).text 'Error fetching event poller'
$( '#info' ).attr 'class', 'error'
$( '#select_event' ).change () ->
fFetchEventParams $( this ).val()
# Init Action Invoker
arrActionInvoker = []
obj =
command: 'get_action_invokers'
$.post( '/usercommand', obj )
.done ( data ) ->
i = 0
fAppendAction = ( id, name ) ->
$( '#select_actions' ).append $( '<option>' ).attr( 'id', i++ ).text id + ' -> ' + name
arrActionInvoker.push id + ' -> ' + name
fAppendAction id, name for id, name of data.message
.fail ( err ) ->
console.log err
$( '#info' ).text 'Error fetching event poller'
$( '#info' ).attr 'class', 'error'
# Fetch Action Invoker user-specific parameters
fFetchActionParams = ( div, name ) ->
obj =
command: 'get_action_invoker_params'
id: name
$.post( '/usercommand', obj )
.done ( data ) ->
if data.message
arrParams = JSON.parse data.message
if arrParams.length > 0
table = $ '<table>'
div.append table
fAppendActionParam = ( name ) ->
tr = $( '<tr>' )
tr.append $( '<td>' ).css 'width', '20px'
tr.append $( '<td>' ).attr( 'class', 'key').text name
tr.append $( '<td>' ).text(' :').append $( '<input>' ).attr( 'type', 'password' )
table.append tr
fAppendActionParam name for name in arrParams
.fail ( err ) ->
console.log err
$( '#info' ).text 'Error fetching action invoker params'
$( '#info' ).attr 'class', 'error'
$( '#select_actions' ).on 'change', () ->
opt = $ 'option:selected', this
arrAI = opt.val().split ' -> '
table = $( '#selected_actions' )
tr = $( '<tr>' ).attr( 'id', 'title_' + opt.attr 'id')
img = $( '<img>' ).attr 'src', 'red_cross_small.png'
tr.append $( '<td>' ).css( 'width', '20px' ).append img
tr.append $( '<td>' ).attr( 'class', 'title').text( opt.val() )
table.append tr
if $( '#ap_' + arrAI[0] ).length is 0
div = $( '<div>' ).attr( 'id', 'ap_' + arrAI[0] ).html "<i>#{ arrAI[0] }</i>"
$( '#action_params' ).append div
fFetchActionParams div, arrAI[0]
opt.remove()
$( '#selected_actions' ).on 'click', 'img', () ->
id = $( this ).closest( 'tr' ).attr( 'id' ).substring 6
name = arrActionInvoker[id]
arrName = name.split ' -> '
$( '#title_' + id ).remove()
$( '#params_' + id ).remove()
opt = $( '<option>' ).attr( 'id', id ).text name
$( '#select_actions' ).append opt
isSelected = false
$( '#selected_actions td' ).each () ->
if $( this ).text().indexOf( arrName[0] ) > -1
isSelected = true
if not isSelected
$( '#ap_' + arrName[0] ).remove()
$( '#but_submit' ).click () ->
arrEP = $( '#select_event option:selected' ).val().split ' -> '
try
if $( '#input_id' ).val() is ''
throw new Error 'Please enter a module name!'
ep = {}
$( "#event_poller_params tr" ).each () ->
val = $( 'input', this ).val()
name = $( 'td:nth-child(2)', this ).text()
if val is ''
throw new Error "Please enter a value for '#{ name }' in the event module!"
ep[name] = val
if $( '#selected_actions tr' ).length is 0
throw new Error 'Please select at least one action or create one!'
ap = {}
$( '#action_params div' ).each () ->
id = $( this ).attr( 'id' ).substring 3
params = {}
$( 'tr', this ).each () ->
key = $( '.key', this ).text()
val = $( 'input', this ).val()
if val is ''
throw new Error "'#{ key }' missing for '#{ id }'"
params[key] = val
ap[id] = params
acts = []
$( '#selected_actions .title' ).each () ->
acts.push $( this ).text()
obj =
command: 'forge_rule'
id: $( '#input_id' ).val()
event: JSON.stringify
module: arrEP[0]
function: arrEP[1]
event_params: JSON.stringify ep
conditions: JSON.stringify {} #TODO Add conditions!
actions: JSON.stringify acts
action_params: JSON.stringify ap
$.post( '/usercommand', obj )
.done ( data ) ->
$( '#info' ).text data.message
$( '#info' ).attr 'class', 'success'
.fail ( err ) ->
if err.responseText is ''
msg = 'No Response from Server!'
else
try
oErr = JSON.parse err.responseText
msg = oErr.message
$( '#info' ).text 'Error in upload: ' + msg
$( '#info' ).attr 'class', 'error'
if err.status is 401
window.location.href = 'forge?page=forge_rule'
catch err
alert err.message
window.addEventListener 'load', fOnLoad, true

View file

@ -0,0 +1,31 @@
// Generated by CoffeeScript 1.6.3
(function() {
var fOnLoad;
fOnLoad = function() {
document.title = 'Administrate';
$('#pagetitle').text('Hi {{{user.username}}}, issue your commands please:');
return $('#but_submit').click(function() {
var data;
data = {
command: $('#inp_command').val()
};
return $.post('admincommand', data).done(function(data) {
$('#info').text(data.message);
return $('#info').attr('class', 'success');
}).fail(function(err) {
if (err.responseText === '') {
err.responseText = 'No Response from Server!';
}
$('#info').text('Error: ' + err.responseText);
$('#info').attr('class', 'error');
if (err.status === 401) {
return window.location.href = 'admin';
}
});
});
};
window.addEventListener('load', fOnLoad, true);
}).call(this);

View file

@ -1,13 +0,0 @@
// Generated by CoffeeScript 1.6.3
(function() {
var fOnLoad;
fOnLoad = function() {
document.title = 'Error!';
$('#pagetitle').text('Error processing your request!');
return $('#pagetitle').attr('class', 'error');
};
window.addEventListener('load', fOnLoad, true);
}).call(this);

View file

@ -1,5 +1,101 @@
// Generated by CoffeeScript 1.6.3
(function() {
var fOnLoad;
fOnLoad = function() {
var editor, fChangeCrosses;
document.title = 'Forge Action Invoker';
$('#pagetitle').text("{{{user.username}}}, forge your custom action invoker!");
editor = ace.edit("editor");
editor.setTheme("ace/theme/monokai");
editor.getSession().setMode("ace/mode/coffee");
editor.setShowPrintMargin(false);
$('#editor_mode').change(function(el) {
if ($(this).val() === '0') {
return editor.getSession().setMode("ace/mode/coffee");
} else {
return editor.getSession().setMode("ace/mode/javascript");
}
});
fChangeCrosses = function() {
return $('#tableParams img').each(function(id) {
var par;
par = $(this).closest('tr');
if (par.is(':last-child' || par.is(':only-child'))) {
return $(this).hide();
} else {
return $(this).show();
}
});
};
$('#tableParams').on('click', 'img', function() {
var par;
par = $(this).closest('tr');
if (!par.is(':last-child')) {
par.remove();
}
return fChangeCrosses();
});
$('#tableParams').on('keyup', 'input', function() {
var img, inp, par, tr;
par = $(this).closest('tr');
if (par.is(':last-child')) {
tr = $('<tr>');
img = $('<img>').attr('src', 'red_cross_small.png');
inp = $('<input>').attr('type', 'text');
tr.append($('<td>').append(img));
tr.append($('<td>').append(inp));
par.parent().append(tr);
return fChangeCrosses();
} else if ($(this).val() === '' && !par.is(':only-child')) {
return par.remove();
}
});
fChangeCrosses();
return $('#but_submit').click(function() {
var listParams, obj;
if ($('#input_id').val() === '') {
return alert('Please enter an action invoker name!');
} else {
listParams = [];
$('#tableParams input').each(function() {
var val;
val = $(this).val();
if (val !== "") {
return listParams.push(val);
}
});
obj = {
id: $('#input_id').val(),
command: 'forge_action_invoker',
lang: $('#editor_mode').val(),
"public": $('#is_public').is(':checked'),
data: editor.getValue(),
params: JSON.stringify(listParams)
};
return $.post('/usercommand', obj).done(function(data) {
$('#info').text(data.message);
return $('#info').attr('class', 'success');
}).fail(function(err) {
var msg, oErr;
if (err.responseText === '') {
msg = 'No Response from Server!';
} else {
try {
oErr = JSON.parse(err.responseText);
msg = oErr.message;
} catch (_error) {}
}
$('#info').text('Action Invoker not stored! ' + msg);
$('#info').attr('class', 'error');
if (err.status === 401) {
return window.location.href = 'forge?page=forge_action_invoker';
}
});
}
});
};
window.addEventListener('load', fOnLoad, true);
}).call(this);

View file

@ -10,18 +10,29 @@
editor.setTheme("ace/theme/monokai");
editor.getSession().setMode("ace/mode/json");
editor.setShowPrintMargin(false);
$('#editor').css('height', '400px');
$('#editor').css('width', '600px');
return $('#but_submit').click(function() {
var data, err;
try {
data = JSON.parse(editor.getValue());
return $.post('/event', data).done(function(data) {
return alert(data);
$('#info').text(data.message);
return $('#info').attr('class', 'success');
}).fail(function(err) {
return alert('Posting of event failed: ' + err.responseText);
if (err.responseText === '') {
err.responseText = 'No Response from Server!';
}
$('#info').text('Error in upload: ' + err.responseText);
$('#info').attr('class', 'error');
if (err.status === 401) {
return window.location.href = 'forge?page=forge_event';
}
});
} catch (_error) {
err = _error;
return alert('You have errors in your JSON object!');
$('#info').text('You have errors in your JSON object! ' + err);
return $('#info').attr('class', 'error');
}
});
};

View file

@ -5,7 +5,7 @@
fOnLoad = function() {
var editor, fChangeCrosses;
document.title = 'Forge Event Poller';
$('#pagetitle').text('Forge your custom event poller!');
$('#pagetitle').text("{{{user.username}}}, forge your custom event poller!");
editor = ace.edit("editor");
editor.setTheme("ace/theme/monokai");
editor.getSession().setMode("ace/mode/coffee");
@ -17,24 +17,10 @@
return editor.getSession().setMode("ace/mode/javascript");
}
});
$('#but_submit').click(function() {
var data, err;
try {
data = JSON.parse(editor.getValue());
return $.post('/event', data).done(function(data) {
return alert(data);
}).fail(function(err) {
return alert('Posting of event failed: ' + err.responseText);
});
} catch (_error) {
err = _error;
return alert('You have errors in your JSON object!');
}
});
fChangeCrosses = function() {
return $('#listParams img').each(function(id) {
return $('#tableParams img').each(function(id) {
var par;
par = $(this).parent('tr');
par = $(this).closest('tr');
if (par.is(':last-child' || par.is(':only-child'))) {
return $(this).hide();
} else {
@ -42,27 +28,72 @@
}
});
};
$('#listParams').on('click', 'img', function() {
$('#tableParams').on('click', 'img', function() {
var par;
par = $(this).parent('tr');
par = $(this).closest('tr');
if (!par.is(':last-child')) {
par.remove();
}
return fChangeCrosses();
});
$('#listParams').on('keyup', 'input', function() {
var td, tr;
console.log('weees');
if ($(this).parent('tr').is(':last-child')) {
$('#tableParams').on('keyup', 'input', function() {
var img, inp, par, tr;
par = $(this).closest('tr');
if (par.is(':last-child')) {
tr = $('<tr>');
td = tr.append('td');
td.append($('<img>').attr('src', 'red_cross_small.png'));
td.append($('<input>').attr('type', 'text'));
$('#listParams').append(tr);
img = $('<img>').attr('src', 'red_cross_small.png');
inp = $('<input>').attr('type', 'text');
tr.append($('<td>').append(img));
tr.append($('<td>').append(inp));
par.parent().append(tr);
return fChangeCrosses();
} else if ($(this).val() === '' && !par.is(':only-child')) {
return par.remove();
}
});
fChangeCrosses();
return $('#but_submit').click(function() {
var listParams, obj;
if ($('#input_id').val() === '') {
return alert('Please enter an event poller name!');
} else {
listParams = [];
$('#tableParams input').each(function() {
var val;
val = $(this).val();
if (val !== "") {
return listParams.push(val);
}
});
obj = {
id: $('#input_id').val(),
command: 'forge_event_poller',
lang: $('#editor_mode').val(),
"public": $('#is_public').is(':checked'),
data: editor.getValue(),
params: JSON.stringify(listParams)
};
return $.post('/usercommand', obj).done(function(data) {
$('#info').text(data.message);
return $('#info').attr('class', 'success');
}).fail(function(err) {
var msg, oErr;
if (err.responseText === '') {
msg = 'No Response from Server!';
} else {
try {
oErr = JSON.parse(err.responseText);
msg = oErr.message;
} catch (_error) {}
}
$('#info').text('Event Poller not stored! ' + msg);
$('#info').attr('class', 'error');
if (err.status === 401) {
return window.location.href = 'forge?page=forge_event_poller';
}
});
}
});
return fChangeCrosses();
};
window.addEventListener('load', fOnLoad, true);

View file

@ -1,5 +1,243 @@
// Generated by CoffeeScript 1.6.3
(function() {
var fOnLoad;
fOnLoad = function() {
var arrActionInvoker, fFetchActionParams, fFetchEventParams, obj;
document.title = 'Rule Forge!';
$('#pagetitle').text('{{{user.username}}}, forge your custom rule!');
fFetchEventParams = function(name) {
var arr, obj;
arr = name.split(' -> ');
obj = {
command: 'get_event_poller_params',
id: arr[0]
};
return $.post('/usercommand', obj).done(function(data) {
var arrParams, fAppendParam, table, _i, _len, _results;
if (data.message) {
arrParams = JSON.parse(data.message);
$('#event_poller_params table').remove();
if (arrParams.length > 0) {
$('#event_poller_params').text('Required user-specific params:');
table = $('<table>');
$('#event_poller_params').append(table);
fAppendParam = function(name) {
var inp, tr;
tr = $('<tr>');
tr.append($('<td>').css('width', '20px'));
tr.append($('<td>').text(name));
inp = $('<input>').attr('type', 'password').attr('id', "" + name);
tr.append($('<td>').text(' :').append(inp));
return table.append(tr);
};
_results = [];
for (_i = 0, _len = arrParams.length; _i < _len; _i++) {
name = arrParams[_i];
_results.push(fAppendParam(name));
}
return _results;
}
}
}).fail(function(err) {
console.log(err);
$('#info').text('Error fetching event poller params');
return $('#info').attr('class', 'error');
});
};
obj = {
command: 'get_event_pollers'
};
$.post('/usercommand', obj).done(function(data) {
var fAppendEvent, id, name, _ref;
fAppendEvent = function(id, name) {
return $('#select_event').append($('<option>').text(id + ' -> ' + name));
};
_ref = data.message;
for (id in _ref) {
name = _ref[id];
fAppendEvent(id, name);
}
return fFetchEventParams($('#select_event option:selected').text());
}).fail(function(err) {
console.log(err);
$('#info').text('Error fetching event poller');
return $('#info').attr('class', 'error');
});
$('#select_event').change(function() {
return fFetchEventParams($(this).val());
});
arrActionInvoker = [];
obj = {
command: 'get_action_invokers'
};
$.post('/usercommand', obj).done(function(data) {
var fAppendAction, i, id, name, _ref, _results;
i = 0;
fAppendAction = function(id, name) {
$('#select_actions').append($('<option>').attr('id', i++).text(id + ' -> ' + name));
return arrActionInvoker.push(id + ' -> ' + name);
};
_ref = data.message;
_results = [];
for (id in _ref) {
name = _ref[id];
_results.push(fAppendAction(id, name));
}
return _results;
}).fail(function(err) {
console.log(err);
$('#info').text('Error fetching event poller');
return $('#info').attr('class', 'error');
});
fFetchActionParams = function(div, name) {
obj = {
command: 'get_action_invoker_params',
id: name
};
return $.post('/usercommand', obj).done(function(data) {
var arrParams, fAppendActionParam, table, _i, _len, _results;
if (data.message) {
arrParams = JSON.parse(data.message);
if (arrParams.length > 0) {
table = $('<table>');
div.append(table);
fAppendActionParam = function(name) {
var tr;
tr = $('<tr>');
tr.append($('<td>').css('width', '20px'));
tr.append($('<td>').attr('class', 'key').text(name));
tr.append($('<td>').text(' :').append($('<input>').attr('type', 'password')));
return table.append(tr);
};
_results = [];
for (_i = 0, _len = arrParams.length; _i < _len; _i++) {
name = arrParams[_i];
_results.push(fAppendActionParam(name));
}
return _results;
}
}
}).fail(function(err) {
console.log(err);
$('#info').text('Error fetching action invoker params');
return $('#info').attr('class', 'error');
});
};
$('#select_actions').on('change', function() {
var arrAI, div, img, opt, table, tr;
opt = $('option:selected', this);
arrAI = opt.val().split(' -> ');
table = $('#selected_actions');
tr = $('<tr>').attr('id', 'title_' + opt.attr('id'));
img = $('<img>').attr('src', 'red_cross_small.png');
tr.append($('<td>').css('width', '20px').append(img));
tr.append($('<td>').attr('class', 'title').text(opt.val()));
table.append(tr);
if ($('#ap_' + arrAI[0]).length === 0) {
div = $('<div>').attr('id', 'ap_' + arrAI[0]).html("<i>" + arrAI[0] + "</i>");
$('#action_params').append(div);
fFetchActionParams(div, arrAI[0]);
}
return opt.remove();
});
$('#selected_actions').on('click', 'img', function() {
var arrName, id, isSelected, name, opt;
id = $(this).closest('tr').attr('id').substring(6);
name = arrActionInvoker[id];
arrName = name.split(' -> ');
$('#title_' + id).remove();
$('#params_' + id).remove();
opt = $('<option>').attr('id', id).text(name);
$('#select_actions').append(opt);
isSelected = false;
$('#selected_actions td').each(function() {
if ($(this).text().indexOf(arrName[0]) > -1) {
return isSelected = true;
}
});
if (!isSelected) {
return $('#ap_' + arrName[0]).remove();
}
});
return $('#but_submit').click(function() {
var acts, ap, arrEP, ep, err;
arrEP = $('#select_event option:selected').val().split(' -> ');
try {
if ($('#input_id').val() === '') {
throw new Error('Please enter a module name!');
}
ep = {};
$("#event_poller_params tr").each(function() {
var name, val;
val = $('input', this).val();
name = $('td:nth-child(2)', this).text();
if (val === '') {
throw new Error("Please enter a value for '" + name + "' in the event module!");
}
return ep[name] = val;
});
if ($('#selected_actions tr').length === 0) {
throw new Error('Please select at least one action or create one!');
}
ap = {};
$('#action_params div').each(function() {
var id, params;
id = $(this).attr('id').substring(3);
params = {};
$('tr', this).each(function() {
var key, val;
key = $('.key', this).text();
val = $('input', this).val();
if (val === '') {
throw new Error("'" + key + "' missing for '" + id + "'");
}
return params[key] = val;
});
return ap[id] = params;
});
acts = [];
$('#selected_actions .title').each(function() {
return acts.push($(this).text());
});
obj = {
command: 'forge_rule',
id: $('#input_id').val(),
event: JSON.stringify({
module: arrEP[0],
"function": arrEP[1]
}),
event_params: JSON.stringify(ep),
conditions: JSON.stringify({}),
actions: JSON.stringify(acts),
action_params: JSON.stringify(ap)
};
return $.post('/usercommand', obj).done(function(data) {
$('#info').text(data.message);
return $('#info').attr('class', 'success');
}).fail(function(err) {
var msg, oErr;
if (err.responseText === '') {
msg = 'No Response from Server!';
} else {
try {
oErr = JSON.parse(err.responseText);
msg = oErr.message;
} catch (_error) {}
}
$('#info').text('Error in upload: ' + msg);
$('#info').attr('class', 'error');
if (err.status === 401) {
return window.location.href = 'forge?page=forge_rule';
}
});
} catch (_error) {
err = _error;
return alert(err.message);
}
});
};
window.addEventListener('load', fOnLoad, true);
}).call(this);

View file

@ -1 +1,2 @@
admin page yay
<input type="text" id="inp_command" style="width:300px" />
<button type="button" id="but_submit">command</button>

View file

@ -1 +0,0 @@
<div class="error">Error: {{{message}}}</div>

View file

@ -0,0 +1,62 @@
Action Invoker Name: <input id="input_id" type="text" />
<select id="editor_mode">
<option value="0">CoffeeScript</option>
<option value="1">JavaScript</option>
</select> is public: <input type="checkbox" id="is_public" />
<table id="editor_table">
<tr>
<td id="editor_col" valign="top">
<div id="editor">
#
# ProBinder Action Invoker
#
# Action Invoker requires user params:
# - username: The ProBinder login username
# - password: The ProBinder login password
#
#
# createBinderEntry function requires arguments:
#
# - company: The ProBidner company of the binder
# - context: The ProBinder context (the binder ID)
#
exports.createBinderEntry = ( args ) ->
url = 'https://probinder.com/service/27/save'
credentials =
username: params.username
password: params.password
data =
companyId: args.company
context: args.context
text: args.content
needle.post url, data, credentials, ( err, resp, body ) ->
if err
log err
if resp.statusCode isnt 200
log "Request not successful: #{ body }"
</div>
<button id="but_submit">save</button>
</td>
<td id="params_col" valign="top">
This action invoker requires user-specific properties:
<table id="tableParams">
<tr>
<td><img src="red_cross_small.png"></td>
<td><input type="text" value="username" /></td>
</tr>
<tr>
<td><img src="red_cross_small.png"></td>
<td><input type="text" value="password" /></td>
</tr>
<tr>
<td><img src="red_cross_small.png"></td>
<td><input type="text" /></td>
</tr>
</table>
</td>
</tr>
</table>

View file

@ -1,14 +1,13 @@
<p>
<div id="editor">
<div style="width:100%;float:left;">
<div id="editor">
{
"event": "mail",
"eventid": "mail_0",
"payload": {
"subject": "hello"
}
}
</div>
</div>
</p>
<p>
<button id="but_submit">invoke</button>
</p>
<button id="but_submit">invoke</button>

View file

@ -1,17 +1,20 @@
Event Poller Name: <input id="input_id" type="text" />
<select id="editor_mode">
<option value="0">CoffeeScript</option>
<option value="1">JavaScript</option>
</select>
</select> is public: <input type="checkbox" id="is_public" />
<table id="editor_table">
<tr>
<td id="editor_col" valign="top">
<p>
<div id="editor">
#
# EmailYak EVENT POLLER
#
url = 'https://api.emailyak.com/v1/' + credentials.key + '/json/get/new/email/'
# Requires user params:
# - apikey: The user's EmailYak API key
#
url = 'https://api.emailyak.com/v1/' + params.apikey + '/json/get/new/email/'
exports.newMail = ( pushEvent ) ->
needle.get url, ( err, resp, body ) ->
@ -22,14 +25,15 @@ exports.newMail = ( pushEvent ) ->
log.error 'Error in EmailYak EM newMail: ' + err.message
</div>
</p>
<p>
<button id="but_submit">save</button>
</p>
</td>
<td id="params_col" valign="top">
This event poller requires user-specific properties:
<table id="tableParams">
<tr>
<td><img src="red_cross_small.png"></td>
<td><input type="text" value="apikey" /></td>
</tr>
<tr>
<td><img src="red_cross_small.png"></td>
<td><input type="text" /></td>

View file

@ -0,0 +1,14 @@
<b>Rule Name: </b><input id="input_id" type="text" /><br><br>
<b>Event: </b><select id="select_event"></select>
<div id="event_poller_params"></div>
<br><br>
<b>Actions: </b><select id="select_actions"><option></option></select>
<br><br>
<b>Selected Actions:</b>
<table id="selected_actions"></table>
<br><br>
<b>Required Params:</b>
<br><br>
<div id="action_params"></div>
<br><br>
<button id="but_submit">save</button>

View file

@ -1,26 +1,35 @@
<div id="menubar">
<div id="menubar_event">invoke events</div>
<div id="menubar_events">forge event poller</div>
<div id="menubar_actions">forge action invoker</div>
<div id="menubar_rules">forge rules</div>
<div id="menubar_logout">logout</div>
<script>
$( '#menubar_event' ).click( function() {
window.location.href = 'forge?page=forge_event';
});
$( '#menubar_events' ).click( function() {
window.location.href = 'forge?page=forge_event_poller';
});
$( '#menubar_actions' ).click( function() {
window.location.href = 'forge?page=forge_action_invoker';
});
$( '#menubar_rules' ).click( function() {
window.location.href = 'forge?page=forge_rule';
});
$( '#menubar_logout' ).click( function() {
$.post( '/logout' ).done( function() {
window.location.href = document.URL;
});
var menubar = $( '#menubar' );
var fRedirect = function( url ) {
return function() {
window.location.href = url;
}
};
var fCreateLink = function( text, fAction ) {
var link = $( '<div>' ).text( text );
link.click( fAction );
menubar.append(link);
};
fCreateLink( 'invoke event',
fRedirect( 'forge?page=forge_event' )
);
fCreateLink( 'forge event poller',
fRedirect( 'forge?page=forge_event_poller' )
);
fCreateLink( 'forge action invoker',
fRedirect( 'forge?page=forge_action_invoker' )
);
fCreateLink( 'forge rule',
fRedirect( 'forge?page=forge_rule' )
);
fCreateLink( 'admin', fRedirect( 'admin' ) );
fCreateLink( 'logout', function() {
$.post( '/logout' ).done( fRedirect( document.URL ) );
});
</script>
</div>

View file

@ -1,125 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Forge A Module</title>
{{{head_requires}}}
<script type = "text/template" id="templ_action">
exports.myOwnActionFunction = function( args ) {
var data = {
companyId: '961',
context: '17936',
text: 'Binder entry based on event: ' + args.info
};
needle.post('https://probinder.com/service/27/save', data);
};
</script>
<script type = "text/template" id="templ_event">
exports.myOwnEventFunction = function( callback ) {
var myEvent = {
event: 'mail',
eventid: 'mail_0',
payload: {
subject: 'My mail event subject',
body: 'Complex text body'
}
};
callback( myEvent );
};
</script>
<script type = "text/template" id="templ_params">
{
"username": {
"description": "The username for the Web API"
},
"password": {
"description": "The password for the Web API"
}
}
</script>
</head>
<body>
{{{div_menubar}}}
<div id="mainbody">
<div id="pagetitle">Hi {{user.username}}, forge your own modules!</div>
<p>
<select id="select_type">
<option value="0">Action Invoker</option>
<option value="1">Event Poller</option>
</select>
</p>
<p>
Module name: <input type="text" id="input_id" />
</p>
<p>
<textarea id="textarea_module" rows="20" cols="100"> </textarea>
</p>
<!-- <p>
Description of module:<br />
<textarea id="textarea_description" rows="2" cols="100">This module does...</textarea>
</p> -->
<p>
<!-- <input id="checkbox_public" type="checkbox"> Module is public, reusable for everybody<br />-->
<input id="checkbox_params" type="checkbox"> Module requires user-specific parameters
</p>
<p>
<textarea id="textarea_params" rows="10" cols="40">
</textarea>
</p>
<p>
<button id="but_submit">save</button>
</p>
</div>
<div id="info"></div>
<script type="text/javascript">
$('#select_type').change(function(e) {
if($(this).val() === '0') {
$('#textarea_module').val($('#templ_action').html());
} else {
$('#textarea_module').val($('#templ_event').html());
}
});
$('#but_submit').click(function() {
if($('#input_id').val() === '') alert('Please enter a module name!');
else {
var obj = {
id: $('#input_id').val(),
data: $('#textarea_module').val()
// description: $('#textarea_description').val(),
// ispublic: $('#checkbox_public').is(':checked')
};
try {
if($('#checkbox_params').is(':checked')) {
//TODO if syntax error we should make better info's
JSON.parse($('#textarea_params').val());
obj.params = $('#textarea_params').val();
}
if($('#select_type').val() === '0') obj.command = 'store_action';
else obj.command = 'store_event';
$.post('/usercommand', obj)
.done(function(data) {
console.log(data);
})
.fail(function(err) {
console.error(err);
alert('Posting of module failed: ' + err.responseText);
});
} catch(err) {
alert(err);
}
}
});
$('#checkbox_params').click(function() {
$('#textarea_params').toggle();
});
$('#textarea_module').val($('#templ_action').html());
$('#textarea_params').val($('#templ_params').html());
$('#textarea_params').hide();
</script>
</body>
</html>

View file

@ -3,6 +3,7 @@ body {
font-family: sans-serif, "Times New Roman", Georgia, Serif;
font-size: 80%;
margin: 0px;
background-color: #EEF;
}
input[type=text] {
@ -25,7 +26,6 @@ input[type=text]:focus {
#menubar {
font-size: 0.75em;
width: 100%;
padding: 2px;
height: 1em;
background-color: #DDD;
@ -50,10 +50,21 @@ input[type=text]:focus {
}
#info {
padding: 10px;
padding-left: 20px;
line-height: 2em;
font-family: Tahoma, sans-serif;
font-size: 0.75em;
color: #F33;
font-weight: bold;
}
.success {
background-color: #CFC;
color: #050;
}
.error {
background-color: #FCC;
color: #800;
}
#mainbody {
@ -64,6 +75,12 @@ input[type=text]:focus {
#pagetitle {
font-size: 1.5em;
font-weight: bold;
padding-top: 5px;
padding-bottom: 5px;
}
#editor_mode {
margin-right: 15px;
}
#editor_table {
@ -71,7 +88,7 @@ input[type=text]:focus {
}
#editor_col {
width: 80%;
width: 70%;
}
#editor {
@ -93,14 +110,7 @@ input[type=text]:focus {
}
#tableParams input {
width: 100px;
width: 150px;
margin-left: 10px;
}
.error {
padding: 10px;
font-family: Tahoma, sans-serif;
font-size: 1em;
font-weight: bold;
color: #F33;
}