logging finished, together with testing.

This commit is contained in:
Dominic Bosch 2014-02-18 22:34:36 +01:00
parent e4b12b9623
commit 829fa4f641
22 changed files with 1512 additions and 482 deletions

83
coffee/logging.coffee Normal file
View file

@ -0,0 +1,83 @@
# Logging
# =======
# A Helper to handle the logging throughout the application. It uses
# [bunyan](https://github.com/trentm/node-bunyan) which allows for different streams
# to be attached to different log levels. [bunyan](https://github.com/trentm/node-bunyan)
# creates JSON entries for each log action. The bunyan module can then be used as a
# CLI to pretty print these logs, e.g.:
# `node myapp.js | bunyan`
# **Requires:**
# - Node.js Module: [fs](http://nodejs.org/api/fs.html) and [path](http://nodejs.org/api/path.html)
fs = require 'fs'
path = require 'path'
# - External Module: [bunyan](https://github.com/trentm/node-bunyan)
bunyan = require 'bunyan'
###
Returns a bunyan logger according to the given arguments.
@public getLogger( *args* )
@param {Object} args
###
exports.getLogger = ( args ) =>
emptylog =
{
info: () ->
warn: () ->
error: () ->
getLog: () ->
}
# `args` holds the configuration settings for the logging, see either CLI arguments
# in [webapi-eca](webapi-eca.html) or the configuration parameters in [config](config.html).
args = args ? {}
if args.nolog
# if the user doesn't want to have a log at all (e.g. during tests), it can be omitted with
# the nolog flag
emptylog
else
try
opt =
name: "webapi-eca"
# if we are in development mode, we also add information about where the call came from
# this should be turned off in productive mode since it slows down the logging.
if args['mode'] is 'development'
opt.src = true
# if there's a custom path defined for the log, we adopt the setting.
if args['file-path']
@logPath = path.resolve args['file-path']
else
@logPath = path.resolve __dirname, '..', 'logs', 'server.log'
# We try to write a temp file in the same folder to check if the log can be written
try
fs.writeFileSync @logPath + '.temp', 'temp'
fs.unlinkSync @logPath + '.temp'
catch e
console.error "Log folder '#{ @logPath }' is not writable"
return emptylog
# We attach two streams, one for the I/O and one for the log file.
# The log levels are defined per stream according to the CLI args or the configuration.
opt.streams = [
{
level: args['io-level']
stream: process.stdout
},
{
level: args['file-level']
path: @logPath
}
]
# Finally we create the bunyan logger and return it
bunyan.createLogger opt
# If something goes wrong we print the error and return an empty logger.
catch e
console.error e
emptylog

View file

@ -1,59 +0,0 @@
# Logging
# =======
# A Helper to handle logging.
# **Requires:**
# - Node.js Module(s): [path](http://nodejs.org/api/path.html)
path = require 'path'
# - External Module(s): [bunyan](https://github.com/trentm/node-bunyan)
bunyan = require 'bunyan'
###
Module call
-----------
Calling the module as a function will act as a constructor and load the config file.
It is possible to hand an args object with the properties nolog (true if no outputs shall
be generated) and configPath for a custom configuration file path.
@param {Object} args
###
exports = module.exports = ( args ) =>
emptylog =
{
info: () ->
warn: () ->
error: () ->
}
args = args ? {}
if args.nolog
emptylog
else
try
opt =
name: "webapi-eca"
if args['mode'] is 'development'
opt.src = true
if args['file-path']
@logPath = path.resolve __dirname, '..', 'logs', args['file-path']
else
@logPath = path.resolve __dirname, '..', 'logs', 'server.log'
opt.streams = [
{
level: args['io-level']
stream: process.stdout
},
{
level: args['file-level']
type: 'rotating-file'
path: @logPath
period: '1d'
count: 3
}
]
bunyan.createLogger opt
catch e
console.error e
emptylog

View file

@ -21,9 +21,6 @@ Persistence
# **Requires:**
# - [Logging](logging.html)
log = require './logging'
# - External Modules:
# [crypto-js](https://github.com/evanvosberg/crypto-js) and
# [redis](https://github.com/mranney/node_redis)
@ -39,8 +36,8 @@ a db port and a crypto key.
@param {Object} args
###
exports = module.exports = ( args ) =>
args = args ? {}
log args
@log = args.logger
#TODO remove config, do it through args
config = require './config'
config args
@db?.quit()
@ -48,13 +45,13 @@ exports = module.exports = ( args ) =>
@crypto_key = config.getCryptoKey()
@db = redis.createClient config.getDBPort(),
'localhost', { connect_timeout: 2000 }
@db.on 'error', ( err ) ->
@db.on 'error', ( err ) =>
err.addInfo = 'message from DB'
log.error 'DB', err
@ep = new IndexedModules( 'event-poller', @db )
@ai = new IndexedModules( 'action-invoker', @db )
@log.error 'DB', err
@ep = new IndexedModules( 'event-poller', @db, @log )
@ai = new IndexedModules( 'action-invoker', @db, @log )
else
log.error 'DB', 'Initialization failed because of missing config file!'
@log.error 'DB', 'Initialization failed because of missing config file!'
###
Checks whether the db is connected and passes either an error on failure after
@ -69,7 +66,7 @@ exports.isConnected = ( cb ) =>
numAttempts = 0
fCheckConnection = =>
if @db.connected
log.print 'DB', 'Successfully connected to DB!'
@log.info 'DB', 'Successfully connected to DB!'
cb()
else if numAttempts++ < 10
setTimeout fCheckConnection, 100
@ -83,13 +80,13 @@ Abstracts logging for simple action replies from the DB.
@private replyHandler( *action* )
@param {String} action
###
replyHandler = ( action ) ->
( err, reply ) ->
replyHandler = ( action ) =>
( err, reply ) =>
if err
err.addInfo = "during '#{ action }'"
log.error 'DB', err
@log.error 'DB', err
else
log.print 'DB', "#{ action }: #{ reply }"
@log.info 'DB', "#{ action }: #{ reply }"
###
Push an event into the event queue.
@ -99,10 +96,10 @@ Push an event into the event queue.
###
exports.pushEvent = ( oEvent ) =>
if oEvent
log.print 'DB', "Event pushed into the queue: '#{ oEvent.eventid }'"
@log.info 'DB', "Event pushed into the queue: '#{ oEvent.eventid }'"
@db.rpush 'event_queue', JSON.stringify( oEvent )
else
log.error 'DB', 'Why would you give me an empty event...'
@log.error 'DB', 'Why would you give me an empty event...'
###
@ -137,7 +134,7 @@ hash = ( plainText ) =>
( crypto.SHA3 plainText, { outputLength: 512 } ).toString()
catch err
err.addInfo = 'during hashing'
log.error 'DB', err
@log.error 'DB', err
null
@ -153,7 +150,7 @@ encrypt = ( plainText ) =>
crypto.AES.encrypt plainText, @crypto_key
catch err
err.addInfo = 'during encryption'
log.error 'DB', err
@log.error 'DB', err
null
###
@ -169,7 +166,7 @@ decrypt = ( crypticText ) =>
dec.toString(crypto.enc.Utf8)
catch err
err.addInfo = 'during decryption'
log.error 'DB', err
@log.error 'DB', err
null
###
@ -184,13 +181,13 @@ data objects via the provided function and returns the results to cb(err, obj).
the retrieved data or an error
###
getSetRecords = ( set, fSingle, cb ) =>
log.print 'DB', "Fetching set records: '#{ set }'"
@log.info 'DB', "Fetching set records: '#{ set }'"
# Fetch all members of the set
@db.smembers set, ( err, arrReply ) ->
@db.smembers set, ( err, arrReply ) =>
if err
# If an error happens we return it to the callback function
err.addInfo = "fetching '#{ set }'"
log.error 'DB', err
@log.error 'DB', err
cb err
else if arrReply.length == 0
# If the set was empty we return null to the callback
@ -206,18 +203,18 @@ getSetRecords = ( set, fSingle, cb ) =>
if semaphore > 0
cb new Error "Timeout fetching '#{ set }'"
, 2000
fCallback = ( prop ) ->
fCallback = ( prop ) =>
# The callback function is required to preprocess the result before
# handing it to the callback. This especially includes decrementing
# the semaphore
( err, data ) ->
( err, data ) =>
--semaphore
if err
err.addInfo = "fetching single element: '#{ prop }'"
log.error 'DB', err
@log.error 'DB', err
else if not data
# There was no data behind the key
log.error 'DB', new Error "Empty key in DB: '#{ prop }'"
@log.error 'DB', new Error "Empty key in DB: '#{ prop }'"
else
# We found a valid record and add it to the reply object
objReplies[ prop ] = data
@ -231,53 +228,53 @@ getSetRecords = ( set, fSingle, cb ) =>
fSingle reply, fCallback( reply ) for reply in arrReply
class IndexedModules
constructor: ( @setname, @db ) ->
log.print 'DB', "Instantiated indexed modules for '#{ @setname }'"
constructor: ( @setname, @db, @log ) ->
@log.info 'DB', "Instantiated indexed modules for '#{ @setname }'"
storeModule: ( mId, data ) =>
log.print 'DB', "storeModule(#{ @setname }): #{ mId }"
@log.info 'DB', "storeModule(#{ @setname }): #{ mId }"
@db.sadd "#{ @setname }s", mId,
replyHandler "Storing '#{ @setname }' key '#{ mId }'"
@db.set "#{ @setname }:#{ mId }", data,
replyHandler "Storing '#{ @setname }:#{ mId }'"
getModule: ( mId, cb ) =>
log.print 'DB', "getModule('#{ @setname }): #{ mId }'"
@log.info 'DB', "getModule('#{ @setname }): #{ mId }'"
@db.get "#{ @setname }:#{ mId }", cb
getModuleIds: ( cb ) =>
log.print 'DB', "getModuleIds(#{ @setname })"
@log.info 'DB', "getModuleIds(#{ @setname })"
@db.smembers "#{ @setname }s", cb
getModules: ( cb ) =>
log.print 'DB', "getModules(#{ @setname })"
@log.info 'DB', "getModules(#{ @setname })"
getSetRecords "#{ @setname }s", @getModule, cb
deleteModule: ( mId ) =>
log.print 'DB', "deleteModule(#{ @setname }): #{ mId }"
@log.info 'DB', "deleteModule(#{ @setname }): #{ mId }"
@db.srem "#{ @setname }s", mId,
replyHandler "Deleting '#{ @setname }' key '#{ mId }'"
@db.del "#{ @setname }:#{ mId }",
replyHandler "Deleting '#{ @setname }:#{ mId }'"
storeParameters: ( mId, userId, data ) =>
log.print 'DB', "storeParameters(#{ @setname }): '#{ mId }:#{ userId }'"
@log.info 'DB', "storeParameters(#{ @setname }): '#{ mId }:#{ userId }'"
@db.sadd "#{ @setname }-params", "#{ mId }:#{ userId }",
replyHandler "Storing '#{ @setname }' module parameters key '#{ mId }'"
@db.set "#{ @setname }-params:#{ mId }:#{ userId }", encrypt(data),
replyHandler "Storing '#{ @setname }' module parameters '#{ mId }:#{ userId }'"
getParameters: ( mId, userId, cb ) =>
log.print 'DB', "getParameters(#{ @setname }): '#{ mId }:#{ userId }'"
@log.info 'DB', "getParameters(#{ @setname }): '#{ mId }:#{ userId }'"
@db.get "#{ @setname }-params:#{ mId }:#{ userId }", ( err, data ) ->
cb err, decrypt data
getParametersIds: ( cb ) =>
log.print 'DB', "getParametersIds(#{ @setname })"
@log.info 'DB', "getParametersIds(#{ @setname })"
@db.smembers "#{ @setname }-params", cb
deleteParameters: ( mId, userId ) =>
log.print 'DB', "deleteParameters(#{ @setname }): '#{ mId }:#{ userId }'"
@log.info 'DB', "deleteParameters(#{ @setname }): '#{ mId }:#{ userId }'"
@db.srem "#{ @setname }-params", "#{ mId }:#{ userId }",
replyHandler "Deleting '#{ @setname }-params' key '#{ mId }:#{ userId }'"
@db.del "#{ @setname }-params:#{ mId }:#{ userId }",
@ -482,7 +479,7 @@ Query the DB for a rule and pass it to cb(err, obj).
@param {function} cb
###
exports.getRule = ( ruleId, cb ) =>
log.print 'DB', "getRule: '#{ ruleId }'"
@log.info 'DB', "getRule: '#{ ruleId }'"
@db.get "rule:#{ ruleId }", cb
###
@ -491,8 +488,8 @@ Fetch all rules and pass them to cb(err, obj).
@public getRules( *cb* )
@param {function} cb
###
exports.getRules = ( cb ) ->
log.print 'DB', 'Fetching all Rules'
exports.getRules = ( cb ) =>
@log.info 'DB', 'Fetching all Rules'
getSetRecords 'rules', exports.getRule, cb
###
@ -502,7 +499,7 @@ Fetch all rule IDs and hand it to cb(err, obj).
@param {function} cb
###
exports.getRuleIds = ( cb ) =>
log.print 'DB', 'Fetching all Rule IDs'
@log.info 'DB', 'Fetching all Rule IDs'
@db.smembers 'rules', cb
###
@ -513,7 +510,7 @@ Store a string representation of a rule in the DB.
@param {String} data
###
exports.storeRule = ( ruleId, data ) =>
log.print 'DB', "storeRule: '#{ ruleId }'"
@log.info 'DB', "storeRule: '#{ ruleId }'"
@db.sadd 'rules', "#{ ruleId }",
replyHandler "storing rule key '#{ ruleId }'"
@db.set "rule:#{ ruleId }", data,
@ -527,7 +524,7 @@ Delete a string representation of a rule.
@param {String} userId
###
exports.deleteRule = ( ruleId ) =>
log.print 'DB', "deleteRule: '#{ ruleId }'"
@log.info 'DB', "deleteRule: '#{ ruleId }'"
@db.srem "rules", ruleId, replyHandler "Deleting rule key '#{ ruleId }'"
@db.del "rule:#{ ruleId }", replyHandler "Deleting rule '#{ ruleId }'"
@ -555,7 +552,7 @@ Associate a rule to a user.
@param {String} userId
###
exports.linkRule = ( ruleId, userId ) =>
log.print 'DB', "linkRule: '#{ ruleId }' for user '#{ userId }'"
@log.info 'DB', "linkRule: '#{ ruleId }' for user '#{ userId }'"
@db.sadd "rule:#{ ruleId }:users", userId,
replyHandler "storing user '#{ userId }' for rule key '#{ ruleId }'"
@db.sadd "user:#{ userId }:rules", ruleId,
@ -569,7 +566,7 @@ Get rules linked to a user and hand it to cb(err, obj).
@param {function} cb
###
exports.getUserLinkedRules = ( userId, cb ) =>
log.print 'DB', "getUserLinkedRules: for user '#{ userId }'"
@log.info 'DB', "getUserLinkedRules: for user '#{ userId }'"
@db.smembers "user:#{ userId }:rules", cb
###
@ -580,7 +577,7 @@ Get users linked to a rule and hand it to cb(err, obj).
@param {function} cb
###
exports.getRuleLinkedUsers = ( ruleId, cb ) =>
log.print 'DB', "getRuleLinkedUsers: for rule '#{ ruleId }'"
@log.info 'DB', "getRuleLinkedUsers: for rule '#{ ruleId }'"
@db.smembers "rule:#{ ruleId }:users", cb
###
@ -591,7 +588,7 @@ Delete an association of a rule to a user.
@param {String} userId
###
exports.unlinkRule = ( ruleId, userId ) =>
log.print 'DB', "unlinkRule: '#{ ruleId }:#{ userId }'"
@log.info 'DB', "unlinkRule: '#{ ruleId }:#{ userId }'"
@db.srem "rule:#{ ruleId }:users", userId,
replyHandler "removing user '#{ userId }' for rule key '#{ ruleId }'"
@db.srem "user:#{ userId }:rules", ruleId,
@ -605,7 +602,7 @@ Activate a rule.
@param {String} userId
###
exports.activateRule = ( ruleId, userId ) =>
log.print 'DB', "activateRule: '#{ ruleId }' for '#{ userId }'"
@log.info 'DB', "activateRule: '#{ ruleId }' for '#{ userId }'"
@db.sadd "rule:#{ ruleId }:active-users", userId,
replyHandler "storing activated user '#{ userId }' in rule '#{ ruleId }'"
@db.sadd "user:#{ userId }:active-rules", ruleId,
@ -619,7 +616,7 @@ Get rules activated for a user and hand it to cb(err, obj).
@param {function} cb
###
exports.getUserActivatedRules = ( userId, cb ) =>
log.print 'DB', "getUserActivatedRules: for user '#{ userId }'"
@log.info 'DB', "getUserActivatedRules: for user '#{ userId }'"
@db.smembers "user:#{ userId }:active-rules", cb
###
@ -630,7 +627,7 @@ Get users activated for a rule and hand it to cb(err, obj).
@param {function} cb
###
exports.getRuleActivatedUsers = ( ruleId, cb ) =>
log.print 'DB', "getRuleActivatedUsers: for rule '#{ ruleId }'"
@log.info 'DB', "getRuleActivatedUsers: for rule '#{ ruleId }'"
@db.smembers "rule:#{ ruleId }:active-users", cb
###
@ -641,7 +638,7 @@ Deactivate a rule.
@param {String} userId
###
exports.deactivateRule = ( ruleId, userId ) =>
log.print 'DB', "deactivateRule: '#{ ruleId }' for '#{ userId }'"
@log.info 'DB', "deactivateRule: '#{ ruleId }' for '#{ userId }'"
@db.srem "rule:#{ ruleId }:active-users", userId,
replyHandler "removing activated user '#{ userId }' in rule '#{ ruleId }'"
@db.srem "user:#{ userId }:active-rules", ruleId,
@ -654,7 +651,7 @@ Fetch all active ruleIds and pass them to cb(err, obj).
@param {function} cb
###
exports.getAllActivatedRuleIdsPerUser = ( cb ) =>
log.print 'DB', "Fetching all active rules"
@log.info 'DB', "Fetching all active rules"
@db.smembers 'users', ( err, obj ) =>
result = {}
if obj.length is 0
@ -684,7 +681,7 @@ The password should be hashed before it is passed to this function.
exports.storeUser = ( objUser ) =>
#TODO Only store user if not already existing, or at least only then add a private key
#for his encryption. we would want to have one private key per user, right?
log.print 'DB', "storeUser: '#{ objUser.username }'"
@log.info 'DB', "storeUser: '#{ objUser.username }'"
if objUser and objUser.username and objUser.password
@db.sadd 'users', objUser.username,
replyHandler "storing user key '#{ objUser.username }'"
@ -692,7 +689,7 @@ exports.storeUser = ( objUser ) =>
@db.hmset "user:#{ objUser.username }", objUser,
replyHandler "storing user properties '#{ objUser.username }'"
else
log.error 'DB', new Error 'username or password was missing'
@log.error 'DB', new Error 'username or password was missing'
###
Fetch all user IDs and pass them to cb(err, obj).
@ -701,7 +698,7 @@ Fetch all user IDs and pass them to cb(err, obj).
@param {function} cb
###
exports.getUserIds = ( cb ) =>
log.print 'DB', "getUserIds"
@log.info 'DB', "getUserIds"
@db.smembers "users", cb
###
@ -712,7 +709,7 @@ Fetch a user by id and pass it to cb(err, obj).
@param {function} cb
###
exports.getUser = ( userId, cb ) =>
log.print 'DB', "getUser: '#{ userId }'"
@log.info 'DB', "getUser: '#{ userId }'"
@db.hgetall "user:#{ userId }", cb
###
@ -722,7 +719,7 @@ Deletes a user and all his associated linked and active rules.
@param {String} userId
###
exports.deleteUser = ( userId ) =>
log.print 'DB', "deleteUser: '#{ userId }'"
@log.info 'DB', "deleteUser: '#{ userId }'"
@db.srem "users", userId, replyHandler "Deleting user key '#{ userId }'"
@db.del "user:#{ userId }", replyHandler "Deleting user '#{ userId }'"
@ -766,14 +763,14 @@ because we only store hashes of passwords for security6 reasons.
###
#TODO verify and test whole function
exports.loginUser = ( userId, password, cb ) =>
log.print 'DB', "User '#{ userId }' tries to log in"
fCheck = ( pw ) ->
( err, obj ) ->
@log.info 'DB', "User '#{ userId }' tries to log in"
fCheck = ( pw ) =>
( err, obj ) =>
if err
cb err, null
else if obj and obj.password
if pw == obj.password
log.print 'DB', "User '#{ obj.username }' logged in!"
@log.info 'DB', "User '#{ obj.username }' logged in!"
cb null, obj
else
cb (new Error 'Wrong credentials!'), null
@ -796,7 +793,7 @@ Associate a role with a user.
@param {String} role
###
exports.storeUserRole = ( userId, role ) =>
log.print 'DB', "storeUserRole: '#{ userId }:#{ role }'"
@log.info 'DB', "storeUserRole: '#{ userId }:#{ role }'"
@db.sadd 'roles', role, replyHandler "adding role '#{ role }' to role index set"
@db.sadd "user:#{ userId }:roles", role,
replyHandler "adding role '#{ role }' to user '#{ userId }'"
@ -811,7 +808,7 @@ Fetch all roles of a user and pass them to cb(err, obj).
@param {function} cb
###
exports.getUserRoles = ( userId, cb ) =>
log.print 'DB', "getUserRoles: '#{ userId }'"
@log.info 'DB', "getUserRoles: '#{ userId }'"
@db.smembers "user:#{ userId }:roles", cb
###
@ -822,7 +819,7 @@ Fetch all users of a role and pass them to cb(err, obj).
@param {function} cb
###
exports.getRoleUsers = ( role, cb ) =>
log.print 'DB', "getRoleUsers: '#{ role }'"
@log.info 'DB', "getRoleUsers: '#{ role }'"
@db.smembers "role:#{ role }:users", cb
###
@ -833,7 +830,7 @@ Remove a role from a user.
@param {String} userId
###
exports.removeUserRole = ( userId, role ) =>
log.print 'DB', "removeRoleFromUser: role '#{ role }', user '#{ userId }'"
@log.info 'DB', "removeRoleFromUser: role '#{ role }', user '#{ userId }'"
@db.srem "user:#{ userId }:roles", role,
replyHandler "Removing role '#{ role }' from user '#{ userId }'"
@db.srem "role:#{ role }:users", userId,
@ -845,4 +842,4 @@ Shuts down the db link.
@public shutDown()
###
exports.shutDown = () => @db.quit()
exports.shutDown = () => @db?.quit()

16
coffee/sandbox.coffee Normal file
View file

@ -0,0 +1,16 @@
bunyan = require 'bunyan'
opt =
name: "webapi-eca"
opt.streams = [
{
level: 'info'
stream: process.stdout
},
{
level: 'info'
path: 'logs/server.log'
}
]
# Finally we create the bunyan logger
logger = bunyan.createLogger opt
logger.info 'weeee'

View file

@ -1,19 +1,19 @@
###
Server
============
WebAPI-ECA Engine
=================
>This is the main module that is used to run the whole server:
>This is the main module that is used to run the whole application:
>
> node server [opt]
> node webapi-eca [opt]
>
> See below in the optimist CLI preparation for allowed optional parameters
> See below in the optimist CLI preparation for allowed optional parameters `[opt]`.
###
# **Requires:**
# - [Logging](logging.html)
logger = require './new-logging'
logger = require './logging'
# - [Configuration](config.html)
conf = require './config'
@ -40,37 +40,48 @@ optimist = require 'optimist'
procCmds = {}
###
Let's prepare the optimist CLI
Let's prepare the optimist CLI optional arguments `[opt]`:
###
usage = 'This runs your webapi-based ECA engine'
opt =
# `-h`, `--help`: Display the help
'h':
alias : 'help',
describe: 'Display this'
# `-c`, `--config-path`: Specify a path to a custom configuration file, other than "config/config.json"
'c':
alias : 'config-path',
describe: 'Specify a path to a custom configuration file, other than "config/config.json"'
# `-w`, `--http-port`: Specify a HTTP port for the web server
'w':
alias : 'http-port',
describe: 'Specify a HTTP port for the web server'
# `-d`, `--db-port`: Specify a port for the redis DB
'd':
alias : 'db-port',
describe: 'Specify a port for the redis DB'
# `-m`, `--log-mode`: Specify a log mode: [development|productive]
'm':
alias : 'log-mode',
describe: 'Specify a log mode: [development|productive]'
# `-i`, `--log-io-level`: Specify the log level for the I/O
'i':
alias : 'log-io-level',
describe: 'Specify the log level for the I/O'
# `-f`, `--log-file-level`: Specify the log level for the log file
'f':
alias : 'log-file-level',
describe: 'Specify the log level for the log file'
# `-p`, `--log-file-path`: Specify the path to the log file within the "logs" folder
'p':
alias : 'log-file-path',
describe: 'Specify the path to the log file within the "logs" folder'
# `-n`, `--nolog`: Set this if no output shall be generated
'n':
alias : 'nolog',
describe: 'Set this if no output shall be generated'
# now fetch the CLI arguments and exit if the help has been called.
argv = optimist.usage( usage ).options( opt ).argv
if argv.help
console.log optimist.help()

View file

@ -1,4 +1,7 @@
\documentclass{article}
\usepackage{cite}
\usepackage{courier}
\usepackage[toc,page]{appendix}
\newcommand*{\createTitlePage}{\begingroup
\centering
@ -23,29 +26,6 @@
\clearpage\createTitlePage
\thispagestyle{empty}
% \title{\huge WebAPI-ECA Engine\vspace*{15 mm}}
% \subtitle{Master Thesis Report}
% %\date{}
% %\author{Dominic Bosch \\ Departement Mathematics and Computer Science \\ University of Basel}
% \author{
% Technical Documentation\\
% \date{\today}
% \vspace*{50 mm}\\
% \fontsize{10}{9}\selectfont
% Dominic Bosch\\
% \fontsize{10}{9}\selectfont
% Departement Mathematics and Computer Science\\
% \fontsize{10}{9}\selectfont
% University of Basel
% }
% \maketitle
% \renewcommand{\abstractname}{}
% \begin{abstract}
% %\textbf{Abstract.}
% This docuemnt describes the technical features of the WebAPI-ECA engine reference implementation.
% \end{abstract}
\newpage
\tableofcontents
@ -56,5 +36,97 @@
\subsection{section}
\subsubsection{subsection}
t.b.d.
Introduction
%Documentation can be found on localhost:[http_port]/doc/
% TODO
% Key files within the application. This may include files created by the development team, databases accessed during the program's operation, and third-party utility programs.
% Functions and subroutines. This includes an explanation of what each function or subroutine does, including its range of input values and output values.
% Program variables and constants, and how they're used in the application.
% The overall program structure. For a disc-based application, this may mean describing the program's individual modules and libraries, while for a Web application, this may mean describing which pages use which files.
\section{Prerequisites}
Redis or write own DB Interface
Node.js
\section{Installation}
Crossplatform
\section{Configuration}
\section{Application Architecture}
The application is started through the webapi-eca module, which loads other modules such as the logging module, the configuration file handler, the persistence interface, the listener to HTTP requests and finally the ECA engine.
% TODO Architecture picture goes here!
\subsection{Modules}
\subsubsection{Webapi-ECA}
starting point
reads cli arguments
Initializes:
config
engine
persistence
http listener
logging
forks the event poller and sends him information about new rules so he can fetch the appropriate event poller modules.
\subsubsection{Persistence}
The persistence module is an interface to a persistent storage.
It stores the events in a queue, action invoker modules, event poller modules, rules, users and roles.
Event Queue
\texttt{event\_queue} (List): The event queue for all incoming events to be processed by the engine.
Action Invokers
\texttt{action-invokers} (Set of [aiId] keys): A set of all existing action invokers.
\texttt{action-invoker:[aiId]} (String): A stringified action invoker.
\texttt{action-params} (Set of [aiId]:[userId] keys): All existing action invoker parameters associated with a user.
\texttt{action-params:[aiId]:[userId]} (String): A stringified parameter object associated to an action invoker and a user.
Event Pollers
\texttt{event-pollers} (Set of [epId] keys): A set of all existing event pollers.
\texttt{event-poller:[epId]} (String): A stringified event poller.
\texttt{event-params} (Set of [epId]:[userId] keys): All existing event poller parameters associated with a user.
\texttt{event-params:[epId]:[userId]} (String): A stringified parameter object associated to an event poller and a user.
Rules
\texttt{rules} (Set of [ruleId] keys): A set of all existing rules.
\texttt{rule:[ruleId]:users} (Set of [userId] keys): Associated users to a rule.
\texttt{rule:[ruleId]} (String): Stringified rule object.
\texttt{rule:[ruleId]:active-users} (Set of [userId] keys): Users that have this rule activated.
Users
\texttt{users} (Set of [userId] keys): A set of all existing users.
\texttt{user:[userId]} (Hashmap): The flat user object.
\texttt{user:[userId]:rules} (Set of [ruleId] keys): Associated rules to a user.
\texttt{user:[userId]:active-rules} (Set of [ruleId] keys): Active rules.
\texttt{user:[userId]:roles} (Set of [roleId] keys): All roles a certain user is associated with.
Roles
\texttt{roles} (Set of [roleId] keys): A set of all existing roles.
\texttt{role:[roleId]:users} (Set of [userId] keys): All users associated to this role.
\subsection{Views/Webpages}
user interfaces
login
credentials entered in a login form are encrypted using a random key and only then sent to the server.
we fetch the google crypto-js module in the browser from
% \<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/sha3.js"></script>
and the engine fetches the same but modularized code from the npm repository via the package manager. Now we have the same crypto-js code in both modules
this also allows us to send privately stored modules and rules encrypted to the user, which will then see it decrypted after it arrived at the browser
\bibliography{user-manual}
\bibliographystyle{beast}
\newpage
\renewcommand*\appendixpagename{APPENDIX}
\renewcommand*\appendixtocname{APPENDIX}
\begin{appendices}
\section{Things}
\subsection{Important things}
% \subsubsection{ecaserver.js}
Some appendix content
\end{appendices}
\end{document}

View file

@ -1,4 +1,6 @@
\documentclass{article}
\usepackage{cite}
\usepackage[toc,page]{appendix}
\newcommand*{\createTitlePage}{\begingroup
\centering
@ -23,28 +25,6 @@
\clearpage\createTitlePage
\thispagestyle{empty}
% \title{\huge WebAPI-ECA Engine\vspace*{15 mm}}
% \subtitle{Master Thesis Report}
% %\date{}
% %\author{Dominic Bosch \\ Departement Mathematics and Computer Science \\ University of Basel}
% \author{
% Technical Documentation\\
% \date{\today}
% \vspace*{50 mm}\\
% \fontsize{10}{9}\selectfont
% Dominic Bosch\\
% \fontsize{10}{9}\selectfont
% Departement Mathematics and Computer Science\\
% \fontsize{10}{9}\selectfont
% University of Basel
% }
% \maketitle
% \renewcommand{\abstractname}{}
% \begin{abstract}
% %\textbf{Abstract.}
% This docuemnt describes the technical features of the WebAPI-ECA engine reference implementation.
% \end{abstract}
\newpage
@ -57,4 +37,32 @@
\subsubsection{subsection}
t.b.d.
\section{Administrator}
Start the server
Event \& Action Module Management
For All: CRUD, The view shows all existing modules per user and allows editing
Rules
For All: CRUD
\section{User}
Event \& Action Module Management
Per User: CRUD \& Credentials
Rule Management\cite{2005-Patranjan-TLE.pdf}
Per User: CRUD
\bibliography{user-manual}
\bibliographystyle{beast}
\newpage
\renewcommand*\appendixpagename{APPENDIX}
\renewcommand*\appendixtocname{APPENDIX}
\begin{appendices}
\section{Things}
\subsection{Important things}
% \subsubsection{ecaserver.js}
Some appendix content
\end{appendices}
\end{document}

View file

@ -2,7 +2,7 @@
'use strict';
var logger = require('./new-logging'),
var logger = require('./logging'),
listMessageActions = {},
listAdminCommands = {},
listEventModules = {},

View file

@ -1,107 +1,70 @@
/*
* Logging
* =======
* Functions to handle logging and errors.
*
* Valid log types are:
*
* - 0 standard I/O
* - 1 file
* - 2 silent
*/
//TODO dynamic log file names (especially to track unit test logs)
var fs = require('fs'),
wst = require('winston'),
logTypes = [ flushToConsole, flushToFile, null],
logFile = require('path').resolve(__dirname, '..', 'server.log'),
logType = 0;
// Generated by CoffeeScript 1.6.3
(function() {
var bunyan, fs, path,
_this = this;
exports = module.exports = function(args) {
args = args || {};
if(args.logType) logType = parseInt(args.logType) || 0;
if(logType == 1) fs.truncateSync(logFile, 0);
if(logType > logTypes.length - 1) logType = 0;
// winston.add(winston.transports.File, { filename: 'somefile.log' });
// winston.remove(winston.transports.Console);
return module.exports;
};
fs = require('fs');
exports.getLogType = function() { return logType; };
path = require('path');
function flush(err, msg) {
if(typeof logTypes[logType] === 'function') logTypes[logType](err, msg);
}
bunyan = require('bunyan');
function flushToConsole(err, msg) {
if(err) console.error("\033[31m" + msg + "\033[0m");
else console.log(msg);
// if(err) console.error(msg);
// else console.log(msg);
}
/*
Returns a bunyan logger according to the given arguments.
@public getLogger( *args* )
@param {Object} args
*/
function flushToFile(err, msg) {
fs.appendFile(logFile, msg + '\n', function (err) {});
}
// @function print(module, msg)
exports.getLogger = function(args) {
var e, emptylog, opt;
emptylog = {
info: function() {},
warn: function() {},
error: function() {},
getLog: function() {}
};
args = args != null ? args : {};
if (args.nolog) {
return emptylog;
} else {
try {
opt = {
name: "webapi-eca"
};
if (args['mode'] === 'development') {
opt.src = true;
}
if (args['file-path']) {
_this.logPath = path.resolve(args['file-path']);
} else {
_this.logPath = path.resolve(__dirname, '..', 'logs', 'server.log');
}
try {
fs.writeFileSync(_this.logPath + '.temp', 'temp');
fs.unlinkSync(_this.logPath + '.temp');
} catch (_error) {
e = _error;
console.error("Log folder '" + _this.logPath + "' is not writable");
return emptylog;
}
opt.streams = [
{
level: args['io-level'],
stream: process.stdout
}, {
level: args['file-level'],
path: _this.logPath
}
];
return bunyan.createLogger(opt);
} catch (_error) {
e = _error;
console.error(e);
return emptylog;
}
}
};
/*
* Prints a log to stdout.
* @param {String} module
* @param {String} msg
*/
exports.print = function(module, msg) {
flush(false, (new Date()).toISOString() + ' | ' + module + ' | ' + msg);
};
/**
* Prints a log to stderr.
* @param {String} module
* @param {Error} err
*/
function printError(module, err, isSevere) {
var ts = (new Date()).toISOString() + ' | ', ai = '';
if(!err) {
err = new Error('Unexpected error');
isSevere = true;
}
if(typeof err === 'string') err = new Error(err);
// if(module) flush(true, ts + module + ' | ERROR AND BAD HANDLING: ' + err + '\n' + e.stack);
// else flush(true, ts + '!N/A! | ERROR, BAD HANDLING AND NO MODULE NAME: ' + err + '\n' + e.stack);
// } else if(err) {
if(err.addInfo) ai = ' (' + err.addInfo + ')';
if(!err.message) err.message = 'UNKNOWN REASON!\n' + err.stack;
if(module) {
var msg = ts + module + ' | ERROR'+ai+': ' + err.message;
if(isSevere) msg += '\n' + err.stack;
flush(true, msg);
} else flush(true, ts + '!N/A! | ERROR AND NO MODULE NAME'+ai+': ' + err.message + '\n' + err.stack);
// } else {
// var e = new Error('Unexpected error');
// flush(true, e.message + ': \n' + e.stack);
// }
};
/**
* Prints a message to stderr.
* @param {String} module
* @param {Error} err
*/
exports.error = function(module, err) {
printError(module, err, false);
};
/**
* Prints a message with error stack to stderr
* @param {String} module
* @param {Error} err
*/
exports.severe = function(module, err) {
printError(module, err, true);
};
exports.obj = function (varname, obj) {
var arrS = (new Error).stack.split('\n');
console.log('Dumping object "' + varname + '"' + arrS[2]);
console.log(obj);
};
}).call(this);

View file

@ -1,66 +0,0 @@
// Generated by CoffeeScript 1.6.3
(function() {
var bunyan, exports, path,
_this = this;
path = require('path');
bunyan = require('bunyan');
/*
Module call
-----------
Calling the module as a function will act as a constructor and load the config file.
It is possible to hand an args object with the properties nolog (true if no outputs shall
be generated) and configPath for a custom configuration file path.
@param {Object} args
*/
exports = module.exports = function(args) {
var e, emptylog, opt;
emptylog = {
info: function() {},
warn: function() {},
error: function() {}
};
args = args != null ? args : {};
if (args.nolog) {
return emptylog;
} else {
try {
opt = {
name: "webapi-eca"
};
if (args['mode'] === 'development') {
opt.src = true;
}
if (args['file-path']) {
_this.logPath = path.resolve(__dirname, '..', 'logs', args['file-path']);
} else {
_this.logPath = path.resolve(__dirname, '..', 'logs', 'server.log');
}
opt.streams = [
{
level: args['io-level'],
stream: process.stdout
}, {
level: args['file-level'],
type: 'rotating-file',
path: _this.logPath,
period: '1d',
count: 3
}
];
return bunyan.createLogger(opt);
} catch (_error) {
e = _error;
console.error(e);
return emptylog;
}
}
};
}).call(this);

View file

@ -21,12 +21,10 @@ Persistence
(function() {
var IndexedModules, crypto, decrypt, encrypt, exports, getSetRecords, hash, log, redis, replyHandler,
var IndexedModules, crypto, decrypt, encrypt, exports, getSetRecords, hash, redis, replyHandler,
_this = this,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
log = require('./logging');
crypto = require('crypto-js');
redis = require('redis');
@ -43,8 +41,7 @@ Persistence
exports = module.exports = function(args) {
var config, _ref;
args = args != null ? args : {};
log(args);
_this.log = args.logger;
config = require('./config');
config(args);
if ((_ref = _this.db) != null) {
@ -57,12 +54,12 @@ Persistence
});
_this.db.on('error', function(err) {
err.addInfo = 'message from DB';
return log.error('DB', err);
return _this.log.error('DB', err);
});
_this.ep = new IndexedModules('event-poller', _this.db);
return _this.ai = new IndexedModules('action-invoker', _this.db);
_this.ep = new IndexedModules('event-poller', _this.db, _this.log);
return _this.ai = new IndexedModules('action-invoker', _this.db, _this.log);
} else {
return log.error('DB', 'Initialization failed because of missing config file!');
return _this.log.error('DB', 'Initialization failed because of missing config file!');
}
};
@ -83,7 +80,7 @@ Persistence
numAttempts = 0;
fCheckConnection = function() {
if (_this.db.connected) {
log.print('DB', 'Successfully connected to DB!');
_this.log.info('DB', 'Successfully connected to DB!');
return cb();
} else if (numAttempts++ < 10) {
return setTimeout(fCheckConnection, 100);
@ -107,9 +104,9 @@ Persistence
return function(err, reply) {
if (err) {
err.addInfo = "during '" + action + "'";
return log.error('DB', err);
return _this.log.error('DB', err);
} else {
return log.print('DB', "" + action + ": " + reply);
return _this.log.info('DB', "" + action + ": " + reply);
}
};
};
@ -124,10 +121,10 @@ Persistence
exports.pushEvent = function(oEvent) {
if (oEvent) {
log.print('DB', "Event pushed into the queue: '" + oEvent.eventid + "'");
_this.log.info('DB', "Event pushed into the queue: '" + oEvent.eventid + "'");
return _this.db.rpush('event_queue', JSON.stringify(oEvent));
} else {
return log.error('DB', 'Why would you give me an empty event...');
return _this.log.error('DB', 'Why would you give me an empty event...');
}
};
@ -180,7 +177,7 @@ Persistence
} catch (_error) {
err = _error;
err.addInfo = 'during hashing';
log.error('DB', err);
_this.log.error('DB', err);
return null;
}
};
@ -203,7 +200,7 @@ Persistence
} catch (_error) {
err = _error;
err.addInfo = 'during encryption';
log.error('DB', err);
_this.log.error('DB', err);
return null;
}
};
@ -227,7 +224,7 @@ Persistence
} catch (_error) {
err = _error;
err.addInfo = 'during decryption';
log.error('DB', err);
_this.log.error('DB', err);
return null;
}
};
@ -246,12 +243,12 @@ Persistence
getSetRecords = function(set, fSingle, cb) {
log.print('DB', "Fetching set records: '" + set + "'");
_this.log.info('DB', "Fetching set records: '" + set + "'");
return _this.db.smembers(set, function(err, arrReply) {
var fCallback, objReplies, reply, semaphore, _i, _len, _results;
if (err) {
err.addInfo = "fetching '" + set + "'";
log.error('DB', err);
_this.log.error('DB', err);
return cb(err);
} else if (arrReply.length === 0) {
return cb();
@ -268,9 +265,9 @@ Persistence
--semaphore;
if (err) {
err.addInfo = "fetching single element: '" + prop + "'";
log.error('DB', err);
_this.log.error('DB', err);
} else if (!data) {
log.error('DB', new Error("Empty key in DB: '" + prop + "'"));
_this.log.error('DB', new Error("Empty key in DB: '" + prop + "'"));
} else {
objReplies[prop] = data;
}
@ -290,9 +287,10 @@ Persistence
};
IndexedModules = (function() {
function IndexedModules(setname, db) {
function IndexedModules(setname, db, log) {
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);
@ -302,56 +300,56 @@ Persistence
this.getModuleIds = __bind(this.getModuleIds, this);
this.getModule = __bind(this.getModule, this);
this.storeModule = __bind(this.storeModule, this);
log.print('DB', "Instantiated indexed modules for '" + this.setname + "'");
this.log.info('DB', "Instantiated indexed modules for '" + this.setname + "'");
}
IndexedModules.prototype.storeModule = function(mId, data) {
log.print('DB', "storeModule(" + this.setname + "): " + mId);
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 + "'"));
};
IndexedModules.prototype.getModule = function(mId, cb) {
log.print('DB', "getModule('" + this.setname + "): " + mId + "'");
this.log.info('DB', "getModule('" + this.setname + "): " + mId + "'");
return this.db.get("" + this.setname + ":" + mId, cb);
};
IndexedModules.prototype.getModuleIds = function(cb) {
log.print('DB', "getModuleIds(" + this.setname + ")");
this.log.info('DB', "getModuleIds(" + this.setname + ")");
return this.db.smembers("" + this.setname + "s", cb);
};
IndexedModules.prototype.getModules = function(cb) {
log.print('DB', "getModules(" + this.setname + ")");
this.log.info('DB', "getModules(" + this.setname + ")");
return getSetRecords("" + this.setname + "s", this.getModule, cb);
};
IndexedModules.prototype.deleteModule = function(mId) {
log.print('DB', "deleteModule(" + this.setname + "): " + mId);
this.log.info('DB', "deleteModule(" + this.setname + "): " + mId);
this.db.srem("" + this.setname + "s", mId, replyHandler("Deleting '" + this.setname + "' key '" + mId + "'"));
return this.db.del("" + this.setname + ":" + mId, replyHandler("Deleting '" + this.setname + ":" + mId + "'"));
};
IndexedModules.prototype.storeParameters = function(mId, userId, data) {
log.print('DB', "storeParameters(" + this.setname + "): '" + mId + ":" + userId + "'");
this.log.info('DB', "storeParameters(" + 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) {
log.print('DB', "getParameters(" + this.setname + "): '" + mId + ":" + userId + "'");
this.log.info('DB', "getParameters(" + 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) {
log.print('DB', "getParametersIds(" + this.setname + ")");
this.log.info('DB', "getParametersIds(" + this.setname + ")");
return this.db.smembers("" + this.setname + "-params", cb);
};
IndexedModules.prototype.deleteParameters = function(mId, userId) {
log.print('DB', "deleteParameters(" + this.setname + "): '" + mId + ":" + userId + "'");
this.log.info('DB', "deleteParameters(" + 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 + "'"));
};
@ -615,7 +613,7 @@ Persistence
exports.getRule = function(ruleId, cb) {
log.print('DB', "getRule: '" + ruleId + "'");
_this.log.info('DB', "getRule: '" + ruleId + "'");
return _this.db.get("rule:" + ruleId, cb);
};
@ -628,7 +626,7 @@ Persistence
exports.getRules = function(cb) {
log.print('DB', 'Fetching all Rules');
_this.log.info('DB', 'Fetching all Rules');
return getSetRecords('rules', exports.getRule, cb);
};
@ -641,7 +639,7 @@ Persistence
exports.getRuleIds = function(cb) {
log.print('DB', 'Fetching all Rule IDs');
_this.log.info('DB', 'Fetching all Rule IDs');
return _this.db.smembers('rules', cb);
};
@ -655,7 +653,7 @@ Persistence
exports.storeRule = function(ruleId, data) {
log.print('DB', "storeRule: '" + ruleId + "'");
_this.log.info('DB', "storeRule: '" + ruleId + "'");
_this.db.sadd('rules', "" + ruleId, replyHandler("storing rule key '" + ruleId + "'"));
return _this.db.set("rule:" + ruleId, data, replyHandler("storing rule '" + ruleId + "'"));
};
@ -670,7 +668,7 @@ Persistence
exports.deleteRule = function(ruleId) {
log.print('DB', "deleteRule: '" + ruleId + "'");
_this.log.info('DB', "deleteRule: '" + ruleId + "'");
_this.db.srem("rules", ruleId, replyHandler("Deleting rule key '" + ruleId + "'"));
_this.db.del("rule:" + ruleId, replyHandler("Deleting rule '" + ruleId + "'"));
_this.db.smembers("rule:" + ruleId + ":users", function(err, obj) {
@ -711,7 +709,7 @@ Persistence
exports.linkRule = function(ruleId, userId) {
log.print('DB', "linkRule: '" + ruleId + "' for user '" + userId + "'");
_this.log.info('DB', "linkRule: '" + ruleId + "' for user '" + userId + "'");
_this.db.sadd("rule:" + ruleId + ":users", userId, replyHandler("storing user '" + userId + "' for rule key '" + ruleId + "'"));
return _this.db.sadd("user:" + userId + ":rules", ruleId, replyHandler("storing rule key '" + ruleId + "' for user '" + userId + "'"));
};
@ -726,7 +724,7 @@ Persistence
exports.getUserLinkedRules = function(userId, cb) {
log.print('DB', "getUserLinkedRules: for user '" + userId + "'");
_this.log.info('DB', "getUserLinkedRules: for user '" + userId + "'");
return _this.db.smembers("user:" + userId + ":rules", cb);
};
@ -740,7 +738,7 @@ Persistence
exports.getRuleLinkedUsers = function(ruleId, cb) {
log.print('DB', "getRuleLinkedUsers: for rule '" + ruleId + "'");
_this.log.info('DB', "getRuleLinkedUsers: for rule '" + ruleId + "'");
return _this.db.smembers("rule:" + ruleId + ":users", cb);
};
@ -754,7 +752,7 @@ Persistence
exports.unlinkRule = function(ruleId, userId) {
log.print('DB', "unlinkRule: '" + ruleId + ":" + userId + "'");
_this.log.info('DB', "unlinkRule: '" + ruleId + ":" + userId + "'");
_this.db.srem("rule:" + ruleId + ":users", userId, replyHandler("removing user '" + userId + "' for rule key '" + ruleId + "'"));
return _this.db.srem("user:" + userId + ":rules", ruleId, replyHandler("removing rule key '" + ruleId + "' for user '" + userId + "'"));
};
@ -769,7 +767,7 @@ Persistence
exports.activateRule = function(ruleId, userId) {
log.print('DB', "activateRule: '" + ruleId + "' for '" + userId + "'");
_this.log.info('DB', "activateRule: '" + ruleId + "' for '" + userId + "'");
_this.db.sadd("rule:" + ruleId + ":active-users", userId, replyHandler("storing activated user '" + userId + "' in rule '" + ruleId + "'"));
return _this.db.sadd("user:" + userId + ":active-rules", ruleId, replyHandler("storing activated rule '" + ruleId + "' in user '" + userId + "'"));
};
@ -784,7 +782,7 @@ Persistence
exports.getUserActivatedRules = function(userId, cb) {
log.print('DB', "getUserActivatedRules: for user '" + userId + "'");
_this.log.info('DB', "getUserActivatedRules: for user '" + userId + "'");
return _this.db.smembers("user:" + userId + ":active-rules", cb);
};
@ -798,7 +796,7 @@ Persistence
exports.getRuleActivatedUsers = function(ruleId, cb) {
log.print('DB', "getRuleActivatedUsers: for rule '" + ruleId + "'");
_this.log.info('DB', "getRuleActivatedUsers: for rule '" + ruleId + "'");
return _this.db.smembers("rule:" + ruleId + ":active-users", cb);
};
@ -812,7 +810,7 @@ Persistence
exports.deactivateRule = function(ruleId, userId) {
log.print('DB', "deactivateRule: '" + ruleId + "' for '" + userId + "'");
_this.log.info('DB', "deactivateRule: '" + ruleId + "' for '" + userId + "'");
_this.db.srem("rule:" + ruleId + ":active-users", userId, replyHandler("removing activated user '" + userId + "' in rule '" + ruleId + "'"));
return _this.db.srem("user:" + userId + ":active-rules", ruleId, replyHandler("removing activated rule '" + ruleId + "' in user '" + userId + "'"));
};
@ -826,7 +824,7 @@ Persistence
exports.getAllActivatedRuleIdsPerUser = function(cb) {
log.print('DB', "Fetching all active rules");
_this.log.info('DB', "Fetching all active rules");
return _this.db.smembers('users', function(err, obj) {
var fFetchActiveUserRules, result, semaphore, user, _i, _len, _results;
result = {};
@ -869,13 +867,13 @@ Persistence
exports.storeUser = function(objUser) {
log.print('DB', "storeUser: '" + objUser.username + "'");
_this.log.info('DB', "storeUser: '" + objUser.username + "'");
if (objUser && objUser.username && objUser.password) {
_this.db.sadd('users', objUser.username, replyHandler("storing user key '" + objUser.username + "'"));
objUser.password = objUser.password;
return _this.db.hmset("user:" + objUser.username, objUser, replyHandler("storing user properties '" + objUser.username + "'"));
} else {
return log.error('DB', new Error('username or password was missing'));
return _this.log.error('DB', new Error('username or password was missing'));
}
};
@ -888,7 +886,7 @@ Persistence
exports.getUserIds = function(cb) {
log.print('DB', "getUserIds");
_this.log.info('DB', "getUserIds");
return _this.db.smembers("users", cb);
};
@ -902,7 +900,7 @@ Persistence
exports.getUser = function(userId, cb) {
log.print('DB', "getUser: '" + userId + "'");
_this.log.info('DB', "getUser: '" + userId + "'");
return _this.db.hgetall("user:" + userId, cb);
};
@ -915,7 +913,7 @@ Persistence
exports.deleteUser = function(userId) {
log.print('DB', "deleteUser: '" + userId + "'");
_this.log.info('DB', "deleteUser: '" + userId + "'");
_this.db.srem("users", userId, replyHandler("Deleting user key '" + userId + "'"));
_this.db.del("user:" + userId, replyHandler("Deleting user '" + userId + "'"));
_this.db.smembers("user:" + userId + ":rules", function(err, obj) {
@ -974,14 +972,14 @@ Persistence
exports.loginUser = function(userId, password, cb) {
var fCheck;
log.print('DB', "User '" + userId + "' tries to log in");
_this.log.info('DB', "User '" + userId + "' tries to log in");
fCheck = function(pw) {
return function(err, obj) {
if (err) {
return cb(err, null);
} else if (obj && obj.password) {
if (pw === obj.password) {
log.print('DB', "User '" + obj.username + "' logged in!");
_this.log.info('DB', "User '" + obj.username + "' logged in!");
return cb(null, obj);
} else {
return cb(new Error('Wrong credentials!'), null);
@ -1009,7 +1007,7 @@ Persistence
exports.storeUserRole = function(userId, role) {
log.print('DB', "storeUserRole: '" + userId + ":" + role + "'");
_this.log.info('DB', "storeUserRole: '" + userId + ":" + role + "'");
_this.db.sadd('roles', role, replyHandler("adding role '" + role + "' to role index set"));
_this.db.sadd("user:" + userId + ":roles", role, replyHandler("adding role '" + role + "' to user '" + userId + "'"));
return _this.db.sadd("role:" + role + ":users", userId, replyHandler("adding user '" + userId + "' to role '" + role + "'"));
@ -1025,7 +1023,7 @@ Persistence
exports.getUserRoles = function(userId, cb) {
log.print('DB', "getUserRoles: '" + userId + "'");
_this.log.info('DB', "getUserRoles: '" + userId + "'");
return _this.db.smembers("user:" + userId + ":roles", cb);
};
@ -1039,7 +1037,7 @@ Persistence
exports.getRoleUsers = function(role, cb) {
log.print('DB', "getRoleUsers: '" + role + "'");
_this.log.info('DB', "getRoleUsers: '" + role + "'");
return _this.db.smembers("role:" + role + ":users", cb);
};
@ -1053,7 +1051,7 @@ Persistence
exports.removeUserRole = function(userId, role) {
log.print('DB', "removeRoleFromUser: role '" + role + "', user '" + userId + "'");
_this.log.info('DB', "removeRoleFromUser: role '" + role + "', user '" + userId + "'");
_this.db.srem("user:" + userId + ":roles", role, replyHandler("Removing role '" + role + "' from user '" + userId + "'"));
return _this.db.srem("role:" + role + ":users", userId, replyHandler("Removing user '" + userId + "' from role '" + role + "'"));
};
@ -1066,7 +1064,8 @@ Persistence
exports.shutDown = function() {
return _this.db.quit();
var _ref;
return (_ref = _this.db) != null ? _ref.quit() : void 0;
};
}).call(this);

25
js-coffee/sandbox.js Normal file
View file

@ -0,0 +1,25 @@
// Generated by CoffeeScript 1.6.3
(function() {
var bunyan, logger, opt;
bunyan = require('bunyan');
opt = {
name: "webapi-eca"
};
opt.streams = [
{
level: 'info',
stream: process.stdout
}, {
level: 'info',
path: 'logs/server.log'
}
];
logger = bunyan.createLogger(opt);
logger.info('weeee');
}).call(this);

View file

@ -1,21 +1,21 @@
// Generated by CoffeeScript 1.6.3
/*
Server
============
WebAPI-ECA Engine
=================
>This is the main module that is used to run the whole server:
>This is the main module that is used to run the whole application:
>
> node server [opt]
> node webapi-eca [opt]
>
> See below in the optimist CLI preparation for allowed optional parameters
> See below in the optimist CLI preparation for allowed optional parameters `[opt]`.
*/
(function() {
var argv, conf, cp, db, engine, fs, http, init, logger, opt, optimist, path, procCmds, shutDown, usage;
logger = require('./new-logging');
logger = require('./logging');
conf = require('./config');
@ -36,7 +36,7 @@ Server
procCmds = {};
/*
Let's prepare the optimist CLI
Let's prepare the optimist CLI optional arguments `[opt]`:
*/

1
testing/files/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
test.log

93
testing/js/test.js Normal file
View file

@ -0,0 +1,93 @@
// Generated by CoffeeScript 1.6.3
(function() {
({
"name": "webapi-eca",
"hostname": "dominic-HPdv6",
"pid": 3378,
"level": 30,
"msg": "TL | custom path test 1",
"time": "2014-02-18T17:16:04.664Z",
"v": 0
});
({
"name": "webapi-eca",
"hostname": "dominic-HPdv6",
"pid": 3401,
"level": 30,
"msg": "TL | custom path test 1",
"time": "2014-02-18T17:16:54.083Z",
"v": 0
});
({
"name": "webapi-eca",
"hostname": "dominic-HPdv6",
"pid": 3419,
"level": 30,
"msg": "TL | custom path test 1",
"time": "2014-02-18T17:18:01.073Z",
"v": 0
});
({
"name": "webapi-eca",
"hostname": "dominic-HPdv6",
"pid": 3426,
"level": 30,
"msg": "TL | custom path test 1",
"time": "2014-02-18T17:18:37.414Z",
"v": 0
});
({
"name": "webapi-eca",
"hostname": "dominic-HPdv6",
"pid": 3468,
"level": 30,
"msg": "TL | custom path test 1",
"time": "2014-02-18T17:21:37.843Z",
"v": 0
});
({
"name": "webapi-eca",
"hostname": "dominic-HPdv6",
"pid": 3477,
"level": 30,
"msg": "TL | custom path test 1",
"time": "2014-02-18T17:22:08.602Z",
"v": 0
});
({
"name": "webapi-eca",
"hostname": "dominic-HPdv6",
"pid": 6213,
"level": 30,
"msg": "TL | custom path test 1",
"time": "2014-02-18T20:23:53.091Z",
"v": 0
});
({
"name": "webapi-eca",
"hostname": "dominic-HPdv6",
"pid": 6236,
"level": 30,
"msg": "TL | custom path test 1",
"time": "2014-02-18T20:24:45.295Z",
"v": 0
});
({
"name": "webapi-eca",
"hostname": "dominic-HPdv6",
"pid": 6260,
"level": 30,
"msg": "TL | custom path test 1",
"time": "2014-02-18T20:25:27.587Z",
"v": 0
});
}).call(this);

View file

@ -6,9 +6,14 @@
path = require('path');
exports.setUp = function(cb) {
var log, logger;
logger = require(path.join('..', 'js-coffee', 'logging'));
log = logger.getLogger({
nolog: true
});
_this.conf = require(path.join('..', 'js-coffee', 'config'));
_this.conf({
logType: 2
logger: log
});
return cb();
};
@ -20,17 +25,25 @@
};
exports.testParameters = function(test) {
test.expect(4);
var logconf, prop, reqProp, _i, _len;
reqProp = ['mode', 'io-level', 'file-level', 'file-path'];
test.expect(4 + reqProp.length);
test.ok(_this.conf.getHttpPort(), 'HTTP port does not exist!');
test.ok(_this.conf.getDBPort(), 'DB port does not exist!');
test.ok(_this.conf.getCryptoKey(), 'Crypto key does not exist!');
test.ok(_this.conf.getSessionSecret(), 'Session Secret does not exist!');
logconf = _this.conf.getLogConf();
test.ok(logconf, 'Log config does not exist!');
for (_i = 0, _len = reqProp.length; _i < _len; _i++) {
prop = reqProp[_i];
test.ok(logconf[prop], "Log conf property " + prop + " does not exist!");
}
return test.done();
};
exports.testDifferentConfigFile = function(test) {
test.expect(1);
_this.conf({
nolog: true,
configPath: path.join('testing', 'files', 'jsonWrongConfig.json')
});
test.ok(_this.conf.isReady(), 'Different path not loaded!');
@ -40,6 +53,7 @@
exports.testNoConfigFile = function(test) {
test.expect(1);
_this.conf({
nolog: true,
configPath: 'wrongpath.file'
});
test.strictEqual(_this.conf.isReady(), false, 'Wrong path still loaded!');

View file

@ -1,5 +1,121 @@
// Generated by CoffeeScript 1.6.3
(function() {
var fs, getLog, path,
_this = this;
path = require('path');
fs = require('fs');
getLog = function(strPath, cb) {
var fWait,
_this = this;
fWait = function() {
var arrStr, fConvertRow, i, row, str, _i, _len;
str = fs.readFileSync(path.resolve(strPath), 'utf-8');
arrStr = str.split("\n");
fConvertRow = function(row) {
try {
return JSON.parse(row);
} catch (_error) {}
};
for (i = _i = 0, _len = arrStr.length; _i < _len; i = ++_i) {
row = arrStr[i];
arrStr[i] = fConvertRow(row);
}
return cb(arrStr.slice(0, arrStr.length - 1));
};
return setTimeout(fWait, 100);
};
exports.setUp = function(cb) {
_this.stdPath = path.resolve(__dirname, '..', 'logs', 'server.log');
try {
fs.unlinkSync(_this.stdPath);
} catch (_error) {}
_this.logger = require(path.join('..', 'js-coffee', 'logging'));
return cb();
};
exports.testCreate = function(test) {
var args, arrLogs, log;
test.expect(2);
arrLogs = ['TL | testInitIO - info', 'TL | testInitIO - warn', 'TL | testInitIO - error'];
args = {};
args['io-level'] = 'error';
log = _this.logger.getLogger(args);
log.info(arrLogs[0]);
log.warn(arrLogs[1]);
log.error(arrLogs[2]);
test.ok(fs.existsSync(_this.stdPath), 'Log file does not exist!');
return getLog(_this.stdPath, function(arr) {
var allCorrect, i, o, _i, _len;
allCorrect = true;
for (i = _i = 0, _len = arr.length; _i < _len; i = ++_i) {
o = arr[i];
if (o.msg === !arrLogs[i]) {
allCorrect = false;
}
}
test.ok(allCorrect, 'Log file does not contain the correct entries!');
return test.done();
});
};
exports.testNoLog = function(test) {
var fWait, log;
test.expect(1);
log = _this.logger.getLogger({
nolog: true
});
log.info('TL | test 1');
fWait = function() {
test.ok(!fs.existsSync(_this.stdPath), 'Log file does still exist!');
return test.done();
};
return setTimeout(fWait, 100);
};
exports.testCustomPath = function(test) {
var args, fWait, log, strInfo, strPath;
test.expect(2);
strInfo = 'TL | custom path test 1';
strPath = 'testing/test.log';
args = {};
args['file-path'] = strPath;
args['io-level'] = 'error';
log = _this.logger.getLogger(args);
log.info(strInfo);
fWait = function() {
test.ok(fs.existsSync(strPath), 'Custom log file does not exist!');
return getLog(strPath, function(arr) {
test.ok(arr[0].msg === strInfo, 'Custom log file not correct!');
return test.done();
});
};
return setTimeout(fWait, 100);
};
exports.testWrongPath = function(test) {
var args, fWait, log, strInfo, strPath;
test.expect(1);
strInfo = 'TL | custom path test 1';
strPath = 'strange/path/to/test.log';
args = {};
args['file-path'] = strPath;
args['io-level'] = 'error';
console.log('getting logger');
log = _this.logger.getLogger(args);
console.log('logging');
log.info(strInfo);
console.log('logged');
fWait = function() {
console.log('waited');
test.ok(true, 'Custom log file does not exist!');
return test.done();
};
console.log('timing');
return setTimeout(fWait, 100);
};
}).call(this);

View file

@ -1,20 +1,27 @@
// Generated by CoffeeScript 1.6.3
(function() {
var _this = this;
var _this = this,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
exports.setUp = function(cb) {
var logger;
_this.path = require('path');
_this.db = require(path.join('..', 'js-coffee', 'persistence'));
console.log('setup');
console.log(_this.db);
logger = require(_this.path.join('..', 'js-coffee', 'logging'));
_this.log = logger.getLogger({
nolog: true
});
_this.db = require(_this.path.join('..', 'js-coffee', 'persistence'));
_this.db({
logType: 2
logger: _this.log
});
return cb();
};
exports.tearDown = function(cb) {
_this.db.shutDown();
var _ref;
if ((_ref = _this.db) != null) {
_ref.shutDown();
}
return cb();
};
@ -24,19 +31,8 @@
exports.Availability = {
setUp: function(cb) {
_this.path = require('path');
_this.db = require(path.join('..', 'js-coffee', 'persistence'));
console.log('setup');
console.log(_this.db);
_this.db({
logType: 2
});
return cb();
},
testRequire: function(test) {
test.expect(1);
console.log('setup');
test.ok(_this.db, 'DB interface loaded');
return test.done();
},
@ -50,6 +46,7 @@
testNoConfig: function(test) {
test.expect(1);
_this.db({
logger: _this.log,
configPath: 'nonexistingconf.file'
});
return _this.db.isConnected(function(err) {
@ -60,6 +57,7 @@
testWrongConfig: function(test) {
test.expect(1);
_this.db({
logger: _this.log,
configPath: _this.path.join('testing', 'jsonWrongConfig.json')
});
return _this.db.isConnected(function(err) {
@ -84,4 +82,721 @@
}
};
/*
# Test EVENT QUEUE
*/
exports.EventQueue = {
setUp: function(cb) {
_this.evt1 = {
eventid: '1',
event: 'mail'
};
_this.evt2 = {
eventid: '2',
event: 'mail'
};
_this.db.purgeEventQueue();
return cb();
},
testEmptyPopping: function(test) {
test.expect(2);
return _this.db.popEvent(function(err, obj) {
test.ifError(err, 'Error during pop after purging!');
test.strictEqual(obj, null, 'There was an event in the queue!?');
return test.done();
});
},
testEmptyPushing: function(test) {
test.expect(2);
_this.db.pushEvent(null);
return _this.db.popEvent(function(err, obj) {
test.ifError(err, 'Error during non-empty pushing!');
test.strictEqual(obj, null, 'There was an event in the queue!?');
return test.done();
});
},
testNonEmptyPopping: function(test) {
test.expect(3);
_this.db.pushEvent(_this.evt1);
return _this.db.popEvent(function(err, obj) {
test.ifError(err, 'Error during non-empty popping!');
test.notStrictEqual(obj, null, 'There was no event in the queue!');
test.deepEqual(_this.evt1, obj, 'Wrong event in queue!');
return test.done();
});
},
testMultiplePushAndPops: function(test) {
var forkEnds, semaphore;
test.expect(6);
semaphore = 2;
forkEnds = function() {
if (--semaphore === 0) {
return test.done();
}
};
_this.db.pushEvent(_this.evt1);
_this.db.pushEvent(_this.evt2);
_this.db.popEvent(function(err, obj) {
test.ifError(err, 'Error during multiple push and pop!');
test.notStrictEqual(obj, null, 'There was no event in the queue!');
test.deepEqual(_this.evt1, obj, 'Wrong event in queue!');
return forkEnds();
});
return _this.db.popEvent(function(err, obj) {
test.ifError(err, 'Error during multiple push and pop!');
test.notStrictEqual(obj, null, 'There was no event in the queue!');
test.deepEqual(_this.evt2, obj, 'Wrong event in queue!');
return forkEnds();
});
}
};
/*
# Test ACTION INVOKER
*/
exports.ActionInvoker = {
testCreateAndRead: function(test) {
var action, id;
test.expect(3);
id = 'test-action-invoker';
action = 'unit-test action invoker content';
_this.db.storeActionInvoker(id, action);
return _this.db.getActionInvokerIds(function(err, obj) {
test.ok(__indexOf.call(obj, id) >= 0, 'Expected key not in action-invokers set');
return _this.db.getActionInvoker(id, function(err, obj) {
test.strictEqual(obj, action, 'Retrieved Action Invoker is not what we expected');
return _this.db.getActionInvokers(function(err, obj) {
test.deepEqual(action, obj[id], 'Action Invoker ist not in result set');
_this.db.deleteActionInvoker(id);
return test.done();
});
});
});
},
testUpdate: function(test) {
var action, actionNew, id;
test.expect(2);
id = 'test-action-invoker';
action = 'unit-test action invoker content';
actionNew = 'unit-test action invoker new content';
_this.db.storeActionInvoker(id, action);
_this.db.storeActionInvoker(id, actionNew);
return _this.db.getActionInvoker(id, function(err, obj) {
test.strictEqual(obj, actionNew, 'Retrieved Action Invoker is not what we expected');
return _this.db.getActionInvokers(function(err, obj) {
test.deepEqual(actionNew, obj[id], 'Action Invoker ist not in result set');
_this.db.deleteActionInvoker(id);
return test.done();
});
});
},
testDelete: function(test) {
var action, id;
test.expect(2);
id = 'test-action-invoker';
action = 'unit-test action invoker content';
_this.db.storeActionInvoker(id, action);
_this.db.deleteActionInvoker(id);
return _this.db.getActionInvoker(id, function(err, obj) {
test.strictEqual(obj, null, 'Action Invoker still exists');
return _this.db.getActionInvokerIds(function(err, obj) {
test.ok(__indexOf.call(obj, id) < 0, 'Action Invoker key still exists in set');
return test.done();
});
});
},
testFetchSeveral: function(test) {
var action1, action1name, action2, action2name, fCheckInvoker, semaphore;
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 = function(modname, mod) {
var forkEnds, myTest;
myTest = test;
forkEnds = function() {
if (--semaphore === 0) {
return myTest.done();
}
};
return function(err, obj) {
myTest.strictEqual(mod, obj, "Invoker " + modname + " does not equal the expected one");
_this.db.deleteActionInvoker(modname);
return forkEnds();
};
};
_this.db.storeActionInvoker(action1name, action1);
_this.db.storeActionInvoker(action2name, action2);
return _this.db.getActionInvokerIds(function(err, obj) {
test.ok(__indexOf.call(obj, action1name) >= 0 && __indexOf.call(obj, action2name) >= 0, 'Not all action invoker Ids in set');
_this.db.getActionInvoker(action1name, fCheckInvoker(action1name, action1));
return _this.db.getActionInvoker(action2name, fCheckInvoker(action2name, action2));
});
}
};
/*
# Test ACTION INVOKER PARAMS
*/
exports.ActionInvokerParams = {
testCreateAndRead: function(test) {
var actionId, params, userId;
test.expect(2);
userId = 'tester1';
actionId = 'test-action-invoker_1';
params = 'shouldn\'t this be an object?';
_this.db.storeActionParams(actionId, userId, params);
return _this.db.getActionParamsIds(function(err, obj) {
var _ref;
test.ok((_ref = actionId + ':' + userId, __indexOf.call(obj, _ref) >= 0), 'Expected key not in action-params set');
return _this.db.getActionParams(actionId, userId, function(err, obj) {
test.strictEqual(obj, params, 'Retrieved action params is not what we expected');
_this.db.deleteActionParams(actionId, userId);
return test.done();
});
});
},
testUpdate: function(test) {
var actionId, params, paramsNew, userId;
test.expect(1);
userId = 'tester1';
actionId = 'test-action-invoker_1';
params = 'shouldn\'t this be an object?';
paramsNew = 'shouldn\'t this be a new object?';
_this.db.storeActionParams(actionId, userId, params);
_this.db.storeActionParams(actionId, userId, paramsNew);
return _this.db.getActionParams(actionId, userId, function(err, obj) {
test.strictEqual(obj, paramsNew, 'Retrieved action params is not what we expected');
_this.db.deleteActionParams(actionId, userId);
return test.done();
});
},
testDelete: function(test) {
var actionId, params, userId;
test.expect(2);
userId = 'tester1';
actionId = 'test-action-invoker_1';
params = 'shouldn\'t this be an object?';
_this.db.storeActionParams(actionId, userId, params);
_this.db.deleteActionParams(actionId, userId);
return _this.db.getActionParams(actionId, userId, function(err, obj) {
test.strictEqual(obj, null, 'Action params still exists');
return _this.db.getActionParamsIds(function(err, obj) {
var _ref;
test.ok((_ref = actionId + ':' + userId, __indexOf.call(obj, _ref) < 0), 'Action Params key still exists in set');
return test.done();
});
});
}
};
/*
# Test EVENT POLLER
*/
exports.EventPoller = {
testCreateAndRead: function(test) {
var event, id;
test.expect(3);
id = 'test-event-poller';
event = 'unit-test event poller content';
_this.db.storeEventPoller(id, event);
return _this.db.getEventPollerIds(function(err, obj) {
test.ok(__indexOf.call(obj, id) >= 0, 'Expected key not in event-pollers set');
return _this.db.getEventPoller(id, function(err, obj) {
test.strictEqual(obj, event, 'Retrieved Event Poller is not what we expected');
return _this.db.getEventPollers(function(err, obj) {
test.deepEqual(event, obj[id], 'Event Poller ist not in result set');
_this.db.deleteEventPoller(id);
return test.done();
});
});
});
},
testUpdate: function(test) {
var event, eventNew, id;
test.expect(2);
id = 'test-event-poller';
event = 'unit-test event poller content';
eventNew = 'unit-test event poller new content';
_this.db.storeEventPoller(id, event);
_this.db.storeEventPoller(id, eventNew);
return _this.db.getEventPoller(id, function(err, obj) {
test.strictEqual(obj, eventNew, 'Retrieved Event Poller is not what we expected');
return _this.db.getEventPollers(function(err, obj) {
test.deepEqual(eventNew, obj[id], 'Event Poller ist not in result set');
_this.db.deleteEventPoller(id);
return test.done();
});
});
},
testDelete: function(test) {
var event, id;
test.expect(2);
id = 'test-event-poller';
event = 'unit-test event poller content';
_this.db.storeEventPoller(id, event);
_this.db.deleteEventPoller(id);
return _this.db.getEventPoller(id, function(err, obj) {
test.strictEqual(obj, null, 'Event Poller still exists');
return _this.db.getEventPollerIds(function(err, obj) {
test.ok(__indexOf.call(obj, id) < 0, 'Event Poller key still exists in set');
return test.done();
});
});
},
testFetchSeveral: function(test) {
var event1, event1name, event2, event2name, fCheckPoller, semaphore;
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 = function(modname, mod) {
var forkEnds, myTest;
myTest = test;
forkEnds = function() {
if (--semaphore === 0) {
return myTest.done();
}
};
return function(err, obj) {
myTest.strictEqual(mod, obj, "Invoker " + modname + " does not equal the expected one");
_this.db.deleteEventPoller(modname);
return forkEnds();
};
};
_this.db.storeEventPoller(event1name, event1);
_this.db.storeEventPoller(event2name, event2);
return _this.db.getEventPollerIds(function(err, obj) {
test.ok(__indexOf.call(obj, event1name) >= 0 && __indexOf.call(obj, event2name) >= 0, 'Not all event poller Ids in set');
_this.db.getEventPoller(event1name, fCheckPoller(event1name, event1));
return _this.db.getEventPoller(event2name, fCheckPoller(event2name, event2));
});
}
};
/*
# Test EVENT POLLER PARAMS
*/
exports.EventPollerParams = {
testCreateAndRead: function(test) {
var eventId, params, userId;
test.expect(2);
userId = 'tester1';
eventId = 'test-event-poller_1';
params = 'shouldn\'t this be an object?';
_this.db.storeEventParams(eventId, userId, params);
return _this.db.getEventParamsIds(function(err, obj) {
var _ref;
test.ok((_ref = eventId + ':' + userId, __indexOf.call(obj, _ref) >= 0), 'Expected key not in event-params set');
return _this.db.getEventParams(eventId, userId, function(err, obj) {
test.strictEqual(obj, params, 'Retrieved event params is not what we expected');
_this.db.deleteEventParams(eventId, userId);
return test.done();
});
});
},
testUpdate: function(test) {
var eventId, params, paramsNew, userId;
test.expect(1);
userId = 'tester1';
eventId = 'test-event-poller_1';
params = 'shouldn\'t this be an object?';
paramsNew = 'shouldn\'t this be a new object?';
_this.db.storeEventParams(eventId, userId, params);
_this.db.storeEventParams(eventId, userId, paramsNew);
return _this.db.getEventParams(eventId, userId, function(err, obj) {
test.strictEqual(obj, paramsNew, 'Retrieved event params is not what we expected');
_this.db.deleteEventParams(eventId, userId);
return test.done();
});
},
testDelete: function(test) {
var eventId, params, userId;
test.expect(2);
userId = 'tester1';
eventId = 'test-event-poller_1';
params = 'shouldn\'t this be an object?';
_this.db.storeEventParams(eventId, userId, params);
_this.db.deleteEventParams(eventId, userId);
return _this.db.getEventParams(eventId, userId, function(err, obj) {
test.strictEqual(obj, null, 'Event params still exists');
return _this.db.getEventParamsIds(function(err, obj) {
var _ref;
test.ok((_ref = eventId + ':' + userId, __indexOf.call(obj, _ref) < 0), 'Event Params key still exists in set');
return test.done();
});
});
}
};
/*
# Test RULES
*/
exports.Rules = {
setUp: function(cb) {
_this.userId = 'tester-1';
_this.ruleId = 'test-rule_1';
_this.rule = {
"id": "rule_id",
"event": "custom",
"condition": {
"property": "yourValue"
},
"actions": []
};
_this.ruleNew = {
"id": "rule_new",
"event": "custom",
"condition": {
"property": "yourValue"
},
"actions": []
};
return cb();
},
tearDown: function(cb) {
_this.db.deleteRule(_this.ruleId);
return cb();
},
testCreateAndRead: function(test) {
test.expect(3);
_this.db.storeRule(_this.ruleId, JSON.stringify(_this.rule));
return _this.db.getRuleIds(function(err, obj) {
var _ref;
test.ok((_ref = _this.ruleId, __indexOf.call(obj, _ref) >= 0), 'Expected key not in rule key set');
return _this.db.getRule(_this.ruleId, function(err, obj) {
test.deepEqual(JSON.parse(obj), _this.rule, 'Retrieved rule is not what we expected');
return _this.db.getRules(function(err, obj) {
test.deepEqual(_this.rule, JSON.parse(obj[_this.ruleId]), 'Rule not in result set');
_this.db.deleteRule(_this.ruleId);
return test.done();
});
});
});
},
testUpdate: function(test) {
test.expect(1);
_this.db.storeRule(_this.ruleId, JSON.stringify(_this.rule));
_this.db.storeRule(_this.ruleId, JSON.stringify(_this.ruleNew));
return _this.db.getRule(_this.ruleId, function(err, obj) {
test.deepEqual(JSON.parse(obj), _this.ruleNew, 'Retrieved rule is not what we expected');
_this.db.deleteRule(_this.ruleId);
return test.done();
});
},
testDelete: function(test) {
test.expect(2);
_this.db.storeRule(_this.ruleId, JSON.stringify(_this.rule));
_this.db.deleteRule(_this.ruleId);
return _this.db.getRule(_this.ruleId, function(err, obj) {
test.strictEqual(obj, null, 'Rule still exists');
return _this.db.getRuleIds(function(err, obj) {
var _ref;
test.ok((_ref = _this.ruleId, __indexOf.call(obj, _ref) < 0), 'Rule key still exists in set');
return test.done();
});
});
},
testLink: function(test) {
test.expect(2);
_this.db.linkRule(_this.ruleId, _this.userId);
return _this.db.getRuleLinkedUsers(_this.ruleId, function(err, obj) {
var _ref;
test.ok((_ref = _this.userId, __indexOf.call(obj, _ref) >= 0), "Rule not linked to user " + _this.userId);
return _this.db.getUserLinkedRules(_this.userId, function(err, obj) {
var _ref1;
test.ok((_ref1 = _this.ruleId, __indexOf.call(obj, _ref1) >= 0), "User not linked to rule " + _this.ruleId);
return test.done();
});
});
},
testUnlink: function(test) {
test.expect(2);
_this.db.linkRule(_this.ruleId, _this.userId);
_this.db.unlinkRule(_this.ruleId, _this.userId);
return _this.db.getRuleLinkedUsers(_this.ruleId, function(err, obj) {
var _ref;
test.ok((_ref = _this.userId, __indexOf.call(obj, _ref) < 0), "Rule still linked to user " + _this.userId);
return _this.db.getUserLinkedRules(_this.userId, function(err, obj) {
var _ref1;
test.ok((_ref1 = _this.ruleId, __indexOf.call(obj, _ref1) < 0), "User still linked to rule " + _this.ruleId);
return test.done();
});
});
},
testActivate: function(test) {
var usr;
test.expect(4);
usr = {
username: "tester-1",
password: "tester-1"
};
_this.db.storeUser(usr);
_this.db.activateRule(_this.ruleId, _this.userId);
return _this.db.getRuleActivatedUsers(_this.ruleId, function(err, obj) {
var _ref;
test.ok((_ref = _this.userId, __indexOf.call(obj, _ref) >= 0), "Rule not activated for user " + _this.userId);
return _this.db.getUserActivatedRules(_this.userId, function(err, obj) {
var _ref1;
test.ok((_ref1 = _this.ruleId, __indexOf.call(obj, _ref1) >= 0), "User not activated for rule " + _this.ruleId);
return _this.db.getAllActivatedRuleIdsPerUser(function(err, obj) {
var _ref2;
test.notStrictEqual(obj[_this.userId], void 0, "User " + _this.userId + " not in activated rules set");
if (obj[_this.userId]) {
test.ok((_ref2 = _this.ruleId, __indexOf.call(obj[_this.userId], _ref2) >= 0), "Rule " + _this.ruleId + " not in activated rules set");
}
return test.done();
});
});
});
},
testDeactivate: function(test) {
test.expect(3);
_this.db.activateRule(_this.ruleId, _this.userId);
_this.db.deactivateRule(_this.ruleId, _this.userId);
return _this.db.getRuleActivatedUsers(_this.ruleId, function(err, obj) {
var _ref;
test.ok((_ref = _this.userId, __indexOf.call(obj, _ref) < 0), "Rule still activated for user " + _this.userId);
return _this.db.getUserActivatedRules(_this.userId, function(err, obj) {
var _ref1;
test.ok((_ref1 = _this.ruleId, __indexOf.call(obj, _ref1) < 0), "User still activated for rule " + _this.ruleId);
return _this.db.getAllActivatedRuleIdsPerUser(function(err, obj) {
var _ref2;
if (obj[_this.userId]) {
test.ok((_ref2 = _this.ruleId, __indexOf.call(obj[_this.userId], _ref2) < 0), "Rule " + _this.ruleId + " still in activated rules set");
} else {
test.ok(true, "We are fine since there are no entries for this user anymore");
}
return test.done();
});
});
});
},
testUnlinkAndDeactivateAfterDeletion: function(test) {
var fWaitForDeletion, fWaitForTest;
test.expect(2);
_this.db.storeRule(_this.ruleId, JSON.stringify(_this.rule));
_this.db.linkRule(_this.ruleId, _this.userId);
_this.db.activateRule(_this.ruleId, _this.userId);
fWaitForTest = function() {
return _this.db.getUserLinkedRules(_this.userId, function(err, obj) {
var _ref;
test.ok((_ref = _this.ruleId, __indexOf.call(obj, _ref) < 0), "Rule " + _this.ruleId + " still linked to user " + _this.userId);
return _this.db.getUserActivatedRules(_this.userId, function(err, obj) {
var _ref1;
test.ok((_ref1 = _this.ruleId, __indexOf.call(obj, _ref1) < 0), "Rule " + _this.ruleId + " still activated for user " + _this.userId);
return test.done();
});
});
};
fWaitForDeletion = function() {
_this.db.deleteRule(_this.ruleId);
return setTimeout(fWaitForTest, 100);
};
return setTimeout(fWaitForDeletion, 100);
}
};
/*
# Test USER
*/
exports.User = {
setUp: function(cb) {
_this.oUser = {
username: "tester-1",
password: "password"
};
return cb();
},
tearDown: function(cb) {
_this.db.deleteUser(_this.oUser.username);
return cb();
},
testCreateInvalid: function(test) {
var oUserInvOne, oUserInvTwo;
test.expect(4);
oUserInvOne = {
username: "tester-1-invalid"
};
oUserInvTwo = {
password: "password"
};
_this.db.storeUser(oUserInvOne);
_this.db.storeUser(oUserInvTwo);
return _this.db.getUser(oUserInvOne.username, function(err, obj) {
test.strictEqual(obj, null, 'User One was stored!?');
return _this.db.getUser(oUserInvTwo.username, function(err, obj) {
test.strictEqual(obj, null, 'User Two was stored!?');
return _this.db.getUserIds(function(err, obj) {
var _ref, _ref1;
test.ok((_ref = oUserInvOne.username, __indexOf.call(obj, _ref) < 0), 'User key was stored!?');
test.ok((_ref1 = oUserInvTwo.username, __indexOf.call(obj, _ref1) < 0), 'User key was stored!?');
return test.done();
});
});
});
},
testDelete: function(test) {
test.expect(2);
_this.db.storeUser(_this.oUser);
return _this.db.getUser(_this.oUser.username, function(err, obj) {
test.deepEqual(obj, _this.oUser, "User " + _this.oUser.username + " is not what we expect!");
return _this.db.getUserIds(function(err, obj) {
var _ref;
test.ok((_ref = _this.oUser.username, __indexOf.call(obj, _ref) >= 0), 'User key was not stored!?');
return test.done();
});
});
},
testUpdate: function(test) {
var oUserOne;
test.expect(2);
oUserOne = {
username: "tester-1-update",
password: "password"
};
_this.db.storeUser(oUserOne);
oUserOne.password = "password-update";
_this.db.storeUser(oUserOne);
return _this.db.getUser(oUserOne.username, function(err, obj) {
test.deepEqual(obj, oUserOne, "User " + _this.oUser.username + " is not what we expect!");
return _this.db.getUserIds(function(err, obj) {
var _ref;
test.ok((_ref = oUserOne.username, __indexOf.call(obj, _ref) >= 0), 'User key was not stored!?');
_this.db.deleteUser(oUserOne.username);
return test.done();
});
});
},
testDelete: function(test) {
var fWaitForDeletion;
test.expect(2);
fWaitForDeletion = function() {
return _this.db.getUserIds(function(err, obj) {
var _ref;
test.ok((_ref = _this.oUser.username, __indexOf.call(obj, _ref) < 0), 'User key still in set!');
return _this.db.getUser(_this.oUser.username, function(err, obj) {
test.strictEqual(obj, null, 'User key still exists!');
return test.done();
});
});
};
_this.db.storeUser(_this.oUser);
_this.db.deleteUser(_this.oUser.username);
return setTimeout(fWaitForDeletion, 100);
},
testDeleteLinks: function(test) {
var fWaitForDeletion, fWaitForPersistence;
test.expect(4);
fWaitForPersistence = function() {
_this.db.deleteUser(_this.oUser.username);
return setTimeout(fWaitForDeletion, 200);
};
fWaitForDeletion = function() {
return _this.db.getRoleUsers('tester', function(err, obj) {
var _ref;
test.ok((_ref = _this.oUser.username, __indexOf.call(obj, _ref) < 0), 'User key still in role tester!');
return _this.db.getUserRoles(_this.oUser.username, function(err, obj) {
test.ok(obj.length === 0, 'User still associated to roles!');
return _this.db.getUserLinkedRules(_this.oUser.username, function(err, obj) {
test.ok(obj.length === 0, 'User still associated to rules!');
return _this.db.getUserActivatedRules(_this.oUser.username, function(err, obj) {
test.ok(obj.length === 0, 'User still associated to activated rules!');
return test.done();
});
});
});
});
};
_this.db.storeUser(_this.oUser);
_this.db.linkRule('rule-1', _this.oUser.username);
_this.db.linkRule('rule-2', _this.oUser.username);
_this.db.linkRule('rule-3', _this.oUser.username);
_this.db.activateRule('rule-1', _this.oUser.username);
_this.db.storeUserRole(_this.oUser.username, 'tester');
return setTimeout(fWaitForPersistence, 100);
},
testLogin: function(test) {
test.expect(3);
_this.db.storeUser(_this.oUser);
return _this.db.loginUser(_this.oUser.username, _this.oUser.password, function(err, obj) {
test.deepEqual(obj, _this.oUser, 'User not logged in!');
return _this.db.loginUser('dummyname', _this.oUser.password, function(err, obj) {
test.strictEqual(obj, null, 'User logged in?!');
return _this.db.loginUser(_this.oUser.username, 'wrongpass', function(err, obj) {
test.strictEqual(obj, null, 'User logged in?!');
return test.done();
});
});
});
}
};
/*
# Test ROLES
*/
exports.Roles = {
setUp: function(cb) {
_this.db({
logger: _this.log
});
_this.oUser = {
username: "tester-1",
password: "password"
};
return cb();
},
tearDown: function(cb) {
_this.db.deleteUser(_this.oUser.username);
return cb();
},
testStore: function(test) {
test.expect(2);
_this.db.storeUser(_this.oUser);
_this.db.storeUserRole(_this.oUser.username, 'tester');
return _this.db.getUserRoles(_this.oUser.username, function(err, obj) {
test.ok(__indexOf.call(obj, 'tester') >= 0, 'User role tester not stored!');
return _this.db.getRoleUsers('tester', function(err, obj) {
var _ref;
test.ok((_ref = _this.oUser.username, __indexOf.call(obj, _ref) >= 0), "User " + _this.oUser.username + " not stored in role tester!");
return test.done();
});
});
},
testDelete: function(test) {
test.expect(2);
_this.db.storeUser(_this.oUser);
_this.db.storeUserRole(_this.oUser.username, 'tester');
_this.db.removeUserRole(_this.oUser.username, 'tester');
return _this.db.getUserRoles(_this.oUser.username, function(err, obj) {
test.ok(__indexOf.call(obj, 'tester') < 0, 'User role tester not stored!');
return _this.db.getRoleUsers('tester', function(err, obj) {
var _ref;
test.ok((_ref = _this.oUser.username, __indexOf.call(obj, _ref) < 0), "User " + _this.oUser.username + " not stored in role tester!");
return test.done();
});
});
}
};
}).call(this);

View file

@ -1,9 +1,12 @@
path = require 'path'
exports.setUp = ( cb ) =>
logger = require path.join '..', 'js-coffee', 'logging'
log = logger.getLogger
nolog: true
@conf = require path.join '..', 'js-coffee', 'config'
@conf
nolog: true
logger: log
cb()
exports.testRequire = ( test ) =>

View file

@ -1,68 +1,99 @@
path = require 'path'
fs = require 'fs'
getLog = ( strPath, cb ) ->
fWait = =>
# cb fs.readFileSync path, 'utf-8'
str = fs.readFileSync path.resolve( strPath ), 'utf-8'
arrStr = str.split "\n"
fConvertRow = ( row ) ->
try
JSON.parse row
arrStr[i] = fConvertRow row for row, i in arrStr
cb arrStr.slice 0, arrStr.length - 1
setTimeout fWait, 100
exports.setUp = ( cb ) =>
@fs = require 'fs'
@path = require 'path'
@stdPath = @path.resolve __dirname, '..', 'logs', 'server.log'
@stdPath = path.resolve __dirname, '..', 'logs', 'server.log'
try
@fs.unlinkSync @stdPath
catch e
@log = require @path.join '..', 'js-coffee', 'new-logging'
fs.unlinkSync @stdPath
@logger = require path.join '..', 'js-coffee', 'logging'
cb()
exports.tearDown = ( cb ) =>
cb()
# exports.tearDown = ( cb ) =>
# cb()
exports.testInitIO = ( test ) =>
test.expect 1
conf =
logType: 0
@log.configure conf
@log.info 'TL', 'testInitIO - info'
@log.warn 'TL', 'testInitIO - warn'
@log.error 'TL', 'testInitIO - error'
test.ok !@fs.existsSync @stdPath
test.done()
exports.testCreate = ( test ) =>
test.expect 2
arrLogs = [
'TL | testInitIO - info'
'TL | testInitIO - warn'
'TL | testInitIO - error'
]
args = {}
args[ 'io-level' ] = 'error'
log = @logger.getLogger args
log.info arrLogs[0]
log.warn arrLogs[1]
log.error arrLogs[2]
test.ok fs.existsSync( @stdPath ), 'Log file does not exist!'
getLog @stdPath, ( arr ) ->
allCorrect = true
for o,i in arr
if o.msg is not arrLogs[i]
allCorrect = false
test.ok allCorrect, 'Log file does not contain the correct entries!'
test.done()
exports.testInitFile = ( test ) =>
exports.testNoLog = ( test ) =>
test.expect 1
conf =
logType: 1
@log.configure conf
@log.info 'UT', 'test 1'
log = @logger.getLogger
nolog: true
log.info 'TL | test 1'
fWait = () =>
@log.info 'UT', 'test 2'
test.ok @fs.existsSync @stdPath
test.ok !fs.existsSync( @stdPath ), 'Log file does still exist!'
test.done()
setTimeout fWait, 100
# exports.testInitSilent = ( test ) =>
# test.expect 1
exports.testCustomPath = ( test ) =>
test.expect 2
# conf =
# logType: 2
# @log.configure conf
# @log.info 'test 3'
strInfo = 'TL | custom path test 1'
strPath = 'testing/files/test.log'
args = {}
args[ 'file-path' ] = strPath
args[ 'io-level' ] = 'error'
# test.ok true, 'yay'
# test.done()
log = @logger.getLogger args
log.info strInfo
# exports.testInitPath = ( test ) =>
# test.expect 1
fWait = () =>
test.ok fs.existsSync( strPath ), 'Custom log file does not exist!'
getLog strPath, ( arr ) ->
test.ok arr[0].msg is strInfo, 'Custom log file not correct!'
try
fs.unlinkSync strPath
test.done()
# conf =
# logType: 1
# logPath: 'testing/log-initPath.log'
# @log.configure conf
# @log.info 'test 3'
setTimeout fWait, 100
# test.ok true, 'yay'
# test.done()
exports.testWrongPath = ( test ) =>
test.expect 1
# exports.testPrint = ( test ) =>
# test.expect 1
# test.ok true, 'yay'
# @log.info 'test 3'
# test.done()
strInfo = 'TL | custom path test 1'
strPath = 'strange/path/to/test.log'
args = {}
args[ 'file-path' ] = strPath
args[ 'io-level' ] = 'error'
log = @logger.getLogger args
log.info strInfo
fWait = () =>
test.ok !fs.existsSync( path.resolve ( strPath ) ), 'Custom log file does exist!?'
test.done()
setTimeout fWait, 1000

View file

@ -1,12 +1,16 @@
exports.setUp = ( cb ) =>
@path = require 'path'
logger = require @path.join '..', 'js-coffee', 'logging'
@log = logger.getLogger
nolog: true
@db = require @path.join '..', 'js-coffee', 'persistence'
@db logType: 2
@db
logger: @log
cb()
exports.tearDown = ( cb ) =>
@db.shutDown()
@db?.shutDown()
cb()
@ -31,6 +35,7 @@ exports.Availability =
test.expect 1
@db
logger: @log
configPath: 'nonexistingconf.file'
@db.isConnected ( err ) ->
test.ok err, 'Still connected!?'
@ -39,7 +44,9 @@ exports.Availability =
testWrongConfig: ( test ) =>
test.expect 1
@db { configPath: @path.join 'testing', 'jsonWrongConfig.json' }
@db
logger: @log
configPath: @path.join 'testing', 'jsonWrongConfig.json'
@db.isConnected ( err ) ->
test.ok err, 'Still connected!?'
test.done()
@ -836,7 +843,8 @@ exports.User =
###
exports.Roles =
setUp: ( cb ) =>
@db logType: 1
@db
logger: @log
@oUser =
username: "tester-1"
password: "password"