diff --git a/coffee/components-manager.coffee b/coffee/components-manager.coffee index c7185d9..f6817a6 100644 --- a/coffee/components-manager.coffee +++ b/coffee/components-manager.coffee @@ -240,11 +240,11 @@ storeRule = ( user, oPayload, callback ) => oParams = oPayload.action_params for id, params of oParams db.actionInvokers.storeUserParams id, user.username, JSON.stringify params - oParams = oPayload.action_functions + oFuncArgs = oPayload.action_functions # if action function arguments were send, store them - for id, params of oParams + for id, args of oFuncArgs arr = id.split ' -> ' - db.actionInvokers.storeUserArguments user.username, rule.id, arr[ 0 ], arr[ 1 ], JSON.stringify params + db.actionInvokers.storeUserArguments user.username, rule.id, arr[ 0 ], arr[ 1 ], JSON.stringify args # Initialize the rule log db.resetLog user.username, rule.id diff --git a/coffee/dynamic-modules.coffee b/coffee/dynamic-modules.coffee index 23fbce3..290a266 100644 --- a/coffee/dynamic-modules.coffee +++ b/coffee/dynamic-modules.coffee @@ -141,15 +141,17 @@ fTryToLoadModule = ( userId, ruleId, modId, src, dbMod, params, cb ) => if dbMod oFuncArgs = {} - for func of oFuncParams - dbMod.getUserArguments userId, ruleId, modId, func, ( err, obj ) => + fRegisterArguments = ( fName ) => + ( err, obj ) => if obj try - oFuncArgs[ func ] = JSON.parse encryption.decrypt obj - @log.info "DM | Found and attached user-specific arguments to #{ userId }, #{ ruleId }, #{ modId }" + oFuncArgs[ fName ] = JSON.parse obj + @log.info "DM | Found and attached user-specific arguments to #{ userId }, #{ ruleId }, #{ modId }: #{ obj }" catch err @log.warn "DM | Error during parsing of user-specific arguments for #{ userId }, #{ ruleId }, #{ modId }" @log.warn err + for func of oFuncParams + dbMod.getUserArguments userId, ruleId, modId, func, fRegisterArguments func cb answ: answ module: sandbox.exports diff --git a/coffee/engine.coffee b/coffee/engine.coffee index 288d4a8..9342284 100644 --- a/coffee/engine.coffee +++ b/coffee/engine.coffee @@ -160,7 +160,7 @@ updateActionModules = ( updatedRuleId ) => else @log.error "EN | Compilation of code failed! #{ userName }, #{ oMyRule.rule.id }, #{ moduleName }: #{ result.answ.message }" - oMyRule.actions[moduleName] = result.module + oMyRule.actions[moduleName] = result else @log.warn "EN | #{ moduleName } not found for #{ oMyRule.rule.id }!" @@ -209,7 +209,16 @@ processEvent = ( evt ) => try numExecutingFunctions++ @log.info "EN | #{ funcName } executes..." - node[funcName] evt.payload + arrArgs = [] + if node.funcArgs[ funcName ] + for oArg in node.funcArgs[ funcName ] + if oArg.jsselector + arrArgs.push jsonQuery( evt.payload, oArg.value ).nodes()[ 0 ] + else + arrArgs.push oArg.value + else + @log.warn "EN | Weird! arguments not loaded for function '#{ funcName }'!" + node.module[ funcName ].apply null, arrArgs @log.info "EN | #{ funcName } finished execution" catch err @log.info "EN | ERROR IN ACTION INVOKER: " + err.message diff --git a/coffee/event-poller.coffee b/coffee/event-poller.coffee index 32b98df..00cd60c 100644 --- a/coffee/event-poller.coffee +++ b/coffee/event-poller.coffee @@ -93,33 +93,40 @@ fLoadModule = ( msg ) -> if not listUserModules[msg.user] listUserModules[msg.user] = {} - iv = msg.rule.event_interval * 60 * 1000 + ts = new Date() # We open up a new object for the rule it listUserModules[msg.user][msg.rule.id] = id: msg.rule.event pollfunc: arrName[1] - event_interval: iv + event_interval: msg.rule.event_interval * 60 * 1000 module: result.module logger: result.logger + timestamp: ts log.info "EP | New event module '#{ arrName[0] }' loaded for user #{ msg.user }, - in rule #{ msg.rule.id }, polling every #{ msg.rule.event_interval } minutes" - setTimeout fCheckAndRun( msg.user, msg.rule.id ), iv + in rule #{ msg.rule.id }, starting at #{ ts.toISOString() } + and polling every #{ msg.rule.event_interval } minutes" + fCheckAndRun( msg.user, msg.rule.id, ts )() if msg.event is 'new' or not listUserModules[msg.user] or not listUserModules[msg.user][msg.rule.id] fAnonymous() -fCheckAndRun = ( userId, ruleId ) -> +fCheckAndRun = ( userId, ruleId, timestamp ) -> () -> log.info "EP | Check and run user #{ userId }, rule #{ ruleId }" if isRunning and listUserModules[userId] and listUserModules[userId][ruleId] - oRule = listUserModules[userId][ruleId] - fCallFunction userId, ruleId, oRule - setTimeout fCheckAndRun( userId, ruleId ), oRule.event_interval + # If there was a rule update we only continue the latest setTimeout execution + if listUserModules[userId][ruleId].timestamp is timestamp + oRule = listUserModules[userId][ruleId] + fCallFunction userId, ruleId, oRule + setTimeout fCheckAndRun( userId, ruleId, timestamp ), oRule.event_interval + else + log.info "EP | We found a newer polling interval and discontinue this one which + was created at #{ timestamp.toISOString() }" # We have to register the poll function in belows anonymous function # because we're fast iterating through the listUserModules and references will @@ -129,7 +136,7 @@ fCallFunction = ( userId, ruleId, oRule ) -> oRule.module[oRule.pollfunc] ( obj ) -> db.pushEvent event: oRule.id - eventid: "polled #{ oRule.id } #{ userId }_#{ ( new Date ).toISOString() }" + eventid: "polled #{ oRule.id } #{ userId }_#{ ( new Date() ).toISOString() }" payload: obj catch err log.info "EP | ERROR in module when polled: #{ oRule.id } #{ userId }: #{err.message}" diff --git a/coffee/http-listener.coffee b/coffee/http-listener.coffee index 4cd5933..45bff7e 100644 --- a/coffee/http-listener.coffee +++ b/coffee/http-listener.coffee @@ -43,7 +43,7 @@ exports = module.exports = ( args ) => indexEvent = ( event, body, resp ) -> try obj = JSON.parse body - timestamp = ( new Date ).toISOString() + timestamp = ( new Date() ).toISOString() rand = ( Math.floor Math.random() * 10e9 ).toString( 16 ).toUpperCase() obj.event = event obj.eventid = "#{ obj.event }_#{ timestamp }_#{ rand }" diff --git a/coffee/persistence.coffee b/coffee/persistence.coffee index 2d0e36b..defd3c3 100644 --- a/coffee/persistence.coffee +++ b/coffee/persistence.coffee @@ -349,8 +349,6 @@ class IndexedModules @log.info "DB | (IdxedMods) #{ @setname }.getAllModuleUserArguments( #{ userId }, #{ ruleId }, #{ mId } )" @db.smembers "#{ @setname }:#{ userId }:#{ ruleId }:#{ mId }:functions", ( err, obj ) => sem = obj.length - console.log 'getAllModuleUserArguments' - console.log obj oAnswer = {} for func in obj fRegisterFunction = ( func ) => @@ -388,7 +386,7 @@ Appends a log entry. ### exports.appendLog = ( userId, ruleId, moduleId, message ) => @db.append "#{ userId }:#{ ruleId }:log", - "[#{ ( new Date ).toISOString() }] {#{ moduleId }} #{ message }\n" + "[#{ ( new Date() ).toISOString() }] {#{ moduleId }} #{ message }\n" ### Retrieves a log entry. diff --git a/coffee/request-handler.coffee b/coffee/request-handler.coffee index 5f3d373..545a04d 100644 --- a/coffee/request-handler.coffee +++ b/coffee/request-handler.coffee @@ -77,7 +77,7 @@ exports.handleEvent = ( req, resp ) -> resp.send 400, 'Badly formed event!' # If required event properties are present we process the event # if obj and obj.event and not err - timestamp = ( new Date ).toISOString() + timestamp = ( new Date() ).toISOString() rand = ( Math.floor Math.random() * 10e9 ).toString( 16 ).toUpperCase() obj.eventid = "#{ obj.event }_#{ timestamp }_#{ rand }" answ = diff --git a/examples/action-invokers/aiThree.coffee b/examples/action-invokers/aiThree.coffee deleted file mode 100644 index d17bc8c..0000000 --- a/examples/action-invokers/aiThree.coffee +++ /dev/null @@ -1,2 +0,0 @@ -exports.printUserParamToLog = ( evt ) -> - log params.password \ No newline at end of file diff --git a/examples/event-pollers/importio.coffee b/examples/event-pollers/importio.coffee new file mode 100644 index 0000000..4626335 --- /dev/null +++ b/examples/event-pollers/importio.coffee @@ -0,0 +1,31 @@ +### +Import.io allows to capture data from the web +required module params: + +- apikey +- queryGuid +### + +params.apikey = "Cc8AX35d4B89ozzmn5bpm7k70HRon5rrfUxZvOwkVRj31/oBGHzVfQSRp5mEvlOgxyh7xi+tFSL66iAFo1W/sQ==" +params.userGuid = "d19f0d08-bf73-4115-90a8-ac045ad4f225" +params.queryGuid = "caff10dc-3bf8-402e-b1b8-c799a77c3e8c" + +exports.queryData = ( pushEvent ) -> + debug params.apikey + debug params.queryGuid + debug params.userGuid + io = new importio params.userGuid, params.apikey, "query.import.io" + + io.connect ( connected ) -> + if not connected + log "ERROR: Unable to connect" + else + log "Connected!" + data = [] + io.query "input": "input": "query", "connectorGuids": [ params.queryGuid ], ( finished, msg ) -> + if msg.type is "MESSAGE" + log "Adding #{ msg.data.results.length } results" + data = data.concat msg.data.results + if finished + log "Done" + log JSON.stringify data diff --git a/js/components-manager.js b/js/components-manager.js index 544908e..0686ff7 100644 --- a/js/components-manager.js +++ b/js/components-manager.js @@ -311,7 +311,7 @@ Components Manager storeRule = (function(_this) { return function(user, oPayload, callback) { - var arr, epModId, id, oParams, params, rule, strRule; + var args, arr, epModId, id, oFuncArgs, oParams, params, rule, strRule; rule = { id: oPayload.id, event: oPayload.event, @@ -332,11 +332,11 @@ Components Manager params = oParams[id]; db.actionInvokers.storeUserParams(id, user.username, JSON.stringify(params)); } - oParams = oPayload.action_functions; - for (id in oParams) { - params = oParams[id]; + oFuncArgs = oPayload.action_functions; + for (id in oFuncArgs) { + args = oFuncArgs[id]; arr = id.split(' -> '); - db.actionInvokers.storeUserArguments(user.username, rule.id, arr[0], arr[1], JSON.stringify(params)); + db.actionInvokers.storeUserArguments(user.username, rule.id, arr[0], arr[1], JSON.stringify(args)); } db.resetLog(user.username, rule.id); db.appendLog(user.username, rule.id, "INIT", "Rule '" + rule.id + "' initialized"); diff --git a/js/dynamic-modules.js b/js/dynamic-modules.js index 264ec10..79cf400 100644 --- a/js/dynamic-modules.js +++ b/js/dynamic-modules.js @@ -120,7 +120,7 @@ Dynamic Modules fTryToLoadModule = (function(_this) { return function(userId, ruleId, modId, src, dbMod, params, cb) { - var answ, err, fName, func, logFunc, msg, oFuncArgs, oFuncParams, sandbox, _ref; + var answ, err, fName, fRegisterArguments, func, logFunc, msg, oFuncArgs, oFuncParams, sandbox, _ref; if (!params) { params = {}; } @@ -162,19 +162,22 @@ Dynamic Modules } if (dbMod) { oFuncArgs = {}; - for (func in oFuncParams) { - dbMod.getUserArguments(userId, ruleId, modId, func, function(err, obj) { + fRegisterArguments = function(fName) { + return function(err, obj) { if (obj) { try { - oFuncArgs[func] = JSON.parse(encryption.decrypt(obj)); - return _this.log.info("DM | Found and attached user-specific arguments to " + userId + ", " + ruleId + ", " + modId); + oFuncArgs[fName] = JSON.parse(obj); + return _this.log.info("DM | Found and attached user-specific arguments to " + userId + ", " + ruleId + ", " + modId + ": " + obj); } catch (_error) { err = _error; _this.log.warn("DM | Error during parsing of user-specific arguments for " + userId + ", " + ruleId + ", " + modId); return _this.log.warn(err); } } - }); + }; + }; + for (func in oFuncParams) { + dbMod.getUserArguments(userId, ruleId, modId, func, fRegisterArguments(func)); } } return cb({ diff --git a/js/engine.js b/js/engine.js index ecbb94f..4366ed1 100644 --- a/js/engine.js +++ b/js/engine.js @@ -172,7 +172,7 @@ Engine } else { _this.log.error("EN | Compilation of code failed! " + userName + ", " + oMyRule.rule.id + ", " + moduleName + ": " + result.answ.message); } - return oMyRule.actions[moduleName] = result.module; + return oMyRule.actions[moduleName] = result; }); } else { return _this.log.warn("EN | " + moduleName + " not found for " + oMyRule.rule.id + "!"); @@ -253,7 +253,7 @@ Engine return function(evt) { var action, arr, fSearchAndInvokeAction, oMyRule, oUser, ruleName, userName, _results; fSearchAndInvokeAction = function(node, arrPath, funcName, evt, depth) { - var err; + var arrArgs, err, oArg, _i, _len, _ref; if (!node) { _this.log.error("EN | Didn't find property in user rule list: " + arrPath.join(', ') + " at depth " + depth); return; @@ -262,7 +262,21 @@ Engine try { numExecutingFunctions++; _this.log.info("EN | " + funcName + " executes..."); - node[funcName](evt.payload); + arrArgs = []; + if (node.funcArgs[funcName]) { + _ref = node.funcArgs[funcName]; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + oArg = _ref[_i]; + if (oArg.jsselector) { + arrArgs.push(jsonQuery(evt.payload, oArg.value).nodes()[0]); + } else { + arrArgs.push(oArg.value); + } + } + } else { + _this.log.warn("EN | Weird! arguments not loaded for function '" + funcName + "'!"); + } + node.module[funcName].apply(null, arrArgs); _this.log.info("EN | " + funcName + " finished execution"); } catch (_error) { err = _error; diff --git a/js/event-poller.js b/js/event-poller.js index eb36613..5aef363 100644 --- a/js/event-poller.js +++ b/js/event-poller.js @@ -84,23 +84,24 @@ Dynamic Modules return log.warn("EP | Strange... no module retrieved: " + arrName[0]); } else { return dynmod.compileString(obj.data, msg.user, msg.rule.id, arrName[0], obj.lang, db.eventPollers, function(result) { - var iv; + var ts; if (!result.answ === 200) { log.error("EP | Compilation of code failed! " + msg.user + ", " + msg.rule.id + ", " + arrName[0]); } if (!listUserModules[msg.user]) { listUserModules[msg.user] = {}; } - iv = msg.rule.event_interval * 60 * 1000; + ts = new Date(); listUserModules[msg.user][msg.rule.id] = { id: msg.rule.event, pollfunc: arrName[1], - event_interval: iv, + event_interval: msg.rule.event_interval * 60 * 1000, module: result.module, - logger: result.logger + logger: result.logger, + timestamp: ts }; - log.info("EP | New event module '" + arrName[0] + "' loaded for user " + msg.user + ", in rule " + msg.rule.id + ", polling every " + msg.rule.event_interval + " minutes"); - return setTimeout(fCheckAndRun(msg.user, msg.rule.id), iv); + log.info("EP | New event module '" + arrName[0] + "' loaded for user " + msg.user + ", in rule " + msg.rule.id + ", starting at " + (ts.toISOString()) + " and polling every " + msg.rule.event_interval + " minutes"); + return fCheckAndRun(msg.user, msg.rule.id, ts)(); }); } }); @@ -110,14 +111,18 @@ Dynamic Modules } }; - fCheckAndRun = function(userId, ruleId) { + fCheckAndRun = function(userId, ruleId, timestamp) { return function() { var oRule; log.info("EP | Check and run user " + userId + ", rule " + ruleId); if (isRunning && listUserModules[userId] && listUserModules[userId][ruleId]) { - oRule = listUserModules[userId][ruleId]; - fCallFunction(userId, ruleId, oRule); - return setTimeout(fCheckAndRun(userId, ruleId), oRule.event_interval); + if (listUserModules[userId][ruleId].timestamp === timestamp) { + oRule = listUserModules[userId][ruleId]; + fCallFunction(userId, ruleId, oRule); + return setTimeout(fCheckAndRun(userId, ruleId, timestamp), oRule.event_interval); + } else { + return log.info("EP | We found a newer polling interval and discontinue this one which was created at " + (timestamp.toISOString())); + } } }; }; @@ -128,7 +133,7 @@ Dynamic Modules return oRule.module[oRule.pollfunc](function(obj) { return db.pushEvent({ event: oRule.id, - eventid: "polled " + oRule.id + " " + userId + "_" + ((new Date).toISOString()), + eventid: "polled " + oRule.id + " " + userId + "_" + ((new Date()).toISOString()), payload: obj }); }); diff --git a/js/http-listener.js b/js/http-listener.js index 15690d2..3e5400d 100644 --- a/js/http-listener.js +++ b/js/http-listener.js @@ -46,7 +46,7 @@ HTTP Listener var err, obj, rand, timestamp; try { obj = JSON.parse(body); - timestamp = (new Date).toISOString(); + timestamp = (new Date()).toISOString(); rand = (Math.floor(Math.random() * 10e9)).toString(16).toUpperCase(); obj.event = event; obj.eventid = "" + obj.event + "_" + timestamp + "_" + rand; diff --git a/js/persistence.js b/js/persistence.js index b618715..718cfa2 100644 --- a/js/persistence.js +++ b/js/persistence.js @@ -445,8 +445,6 @@ Persistence return function(err, obj) { var fRegisterFunction, func, oAnswer, sem, _i, _len, _results; sem = obj.length; - console.log('getAllModuleUserArguments'); - console.log(obj); oAnswer = {}; _results = []; for (_i = 0, _len = obj.length; _i < _len; _i++) { @@ -509,7 +507,7 @@ Persistence exports.appendLog = (function(_this) { return function(userId, ruleId, moduleId, message) { - return _this.db.append("" + userId + ":" + ruleId + ":log", "[" + ((new Date).toISOString()) + "] {" + moduleId + "} " + message + "\n"); + return _this.db.append("" + userId + ":" + ruleId + ":log", "[" + ((new Date()).toISOString()) + "] {" + moduleId + "} " + message + "\n"); }; })(this); diff --git a/js/request-handler.js b/js/request-handler.js index e73d07a..5762ffc 100644 --- a/js/request-handler.js +++ b/js/request-handler.js @@ -85,7 +85,7 @@ Request Handler resp.send(400, 'Badly formed event!'); } if (obj && obj.event && !err) { - timestamp = (new Date).toISOString(); + timestamp = (new Date()).toISOString(); rand = (Math.floor(Math.random() * 10e9)).toString(16).toUpperCase(); obj.eventid = "" + obj.event + "_" + timestamp + "_" + rand; answ = { diff --git a/testing/files/testObjects.json b/testing/files/testObjects.json index 163ce7d..5bb808f 100644 --- a/testing/files/testObjects.json +++ b/testing/files/testObjects.json @@ -80,18 +80,30 @@ "ruleOne": { "id": "ruleReal", "event": "test_1", + "event_interval": 1, "conditions": [".more:val(\"really nested\")"], - "actions": ["aiOne -> printToLog"] + "actions": ["aiOne -> printToLog"], + "action_functions": { + "aiOne -> printToLog": [ + { + "argument": "evt", + "value": "*", + "jsselector": true + } + ] + } }, "ruleTwo": { "id": "ruleRealTwo", "event": "test_2", + "event_interval": 1, "conditions": [], "actions": ["aiTwo -> otherEvent"] }, "ruleThree": { "id": "ruleRealThree", "event": "epOne -> newMail", + "event_interval": 1, "conditions": [], "actions": ["aiThree -> printUserParamToLog"] } diff --git a/testing/test_components-manager.coffee b/testing/test_components-manager.coffee index 4880942..600973f 100644 --- a/testing/test_components-manager.coffee +++ b/testing/test_components-manager.coffee @@ -35,6 +35,9 @@ cm.addRuleListener engine.internalEvent db = require path.join '..', 'js', 'persistence' db opts +encryption = require path.join '..', 'js', 'encryption' +encryption opts + oUser = objects.users.userOne oRuleOne = objects.rules.ruleOne oRuleTwo = objects.rules.ruleTwo @@ -196,10 +199,12 @@ exports.ruleForge = db.actionInvokers.storeModule oUser.username, oAiThree pw = 'This password should come out cleartext' - userparams = JSON.stringify password: pw - oEncrypted = cryptico.encrypt userparams, strPublicKey + oEncrypted = cryptico.encrypt pw, strPublicKey + userparams = JSON.stringify password: + shielded: false + value: oEncrypted.cipher - db.actionInvokers.storeUserParams oAiThree.id, oUser.username, oEncrypted.cipher + db.actionInvokers.storeUserParams oAiThree.id, oUser.username, userparams request = command: 'forge_rule' @@ -230,7 +235,7 @@ exports.ruleForge = test.strictEqual 200, answ.code, "Deleting Rule returned #{ answ.code }: #{ answ.message }" setTimeout test.done, 200 - setTimeout fWaitAgain, 200 + setTimeout fWaitAgain, 500 setTimeout fWaitForPersistence, 200 diff --git a/webpages/handlers/coffee/forge_rule.coffee b/webpages/handlers/coffee/forge_rule.coffee index 2d6a1bf..2d94b31 100644 --- a/webpages/handlers/coffee/forge_rule.coffee +++ b/webpages/handlers/coffee/forge_rule.coffee @@ -42,35 +42,7 @@ fOnLoad = () -> editor.setShowPrintMargin false # editor.session.setUseSoftTabs false - # Fetch Event Poller user-specific parameters - fFetchEventParams = ( name ) -> - $( '#event_poller_params *' ).remove() - if name - arr = name.split ' -> ' - obj = - command: 'get_event_poller_params' - payload: JSON.stringify - id: arr[ 0 ] - $.post( '/usercommand', obj ) - .done ( data ) -> - if data.message - oParams = JSON.parse data.message - $( '#event_poller_params' ).html '
Required Parameters:' - table = $ '' - $( '#event_poller_params' ).append table - fAppendParam = ( name, shielded ) -> - tr = $( '' ) - tr.append $( '
' ).css 'width', '20px' - tr.append $( '' ).attr( 'class', 'key' ).text name - inp = $( '' ) - if shielded - inp.attr( 'type', 'password' ) - tr.append $( '' ).text( ' : ' ).append inp - table.append tr - fAppendParam name, shielded for name, shielded of oParams - .fail fFailedRequest 'Error fetching event poller params' - - # Init Event Pollers +# EVENT $.post( '/usercommand', command: 'get_event_pollers' ) .done ( data ) -> try @@ -89,12 +61,13 @@ fOnLoad = () -> .fail fFailedRequest 'Error fetching event poller' $( '#select_event' ).change () -> - if $( this ).val() is '' + evtFunc = $( this ).val() + if evtFunc is '' $( '#input_interval' ).html '' else fPlaceAndPaintInterval() - $( '#input_event' ).val $( this ).val() - fFetchEventParams $( this ).val() + $( '#input_event' ).val evtFunc + fFetchEventParams evtFunc $( '#input_event' ).change () -> $( '#select_event' ).val '' @@ -105,8 +78,54 @@ fOnLoad = () -> else fPlaceAndPaintInterval() + fFetchEventParams = ( name ) -> + $( '#event_poller_params *' ).remove() + if name + arr = name.split ' -> ' + obj = + command: 'get_event_poller_params' + payload: JSON.stringify + id: arr[ 0 ] + $.post( '/usercommand', obj ) + .done fAddEventParams arr[ 0 ] + .fail fFailedRequest 'Error fetching event poller params' - # Init Action Invoker + fAddEventParams = ( id ) -> + ( data ) -> + if data.message + oParams = JSON.parse data.message + $( '#event_poller_params' ).html '
Required Parameters:' + table = $ '' + $( '#event_poller_params' ).append table + fAppendParam = ( name, shielded ) -> + tr = $( '' ) + tr.append $( '' ).appendTo table + img = $( '' ).attr 'src', 'red_cross_small.png' + tr.append $( '' ).appendTo table - img = $( '' ).attr 'src', 'red_cross_small.png' - tr.append $( '
' ).css 'width', '20px' + tr.append $( '' ).attr( 'class', 'key' ).text name + inp = $( '' ) + if shielded + inp.attr( 'type', 'password' ) + tr.append $( '' ).text( ' : ' ).append inp + table.append tr + fAppendParam name, shielded for name, shielded of oParams + fFillEventParams id + + fFillEventParams = ( moduleId ) -> + obj = + command: 'get_event_poller_user_params' + payload: JSON.stringify + id: moduleId + $.post( '/usercommand', obj ) + .done ( data ) -> + oParams = JSON.parse data.message + for param, oParam of oParams + par = $( "#event_poller_params tr" ).filter () -> + $( 'td.key', this ).text() is param + $( 'input', par ).val oParam.value + $( 'input', par ).attr 'unchanged', 'true' + $( 'input', par ).change () -> + $( this ).attr 'unchanged', 'false' + +# ACTIONS obj = command: 'get_action_invokers' $.post( '/usercommand', obj ) @@ -119,11 +138,37 @@ fOnLoad = () -> return fAppendActions = ( module, actions ) -> for act in actions - if $( "#action_params div:contains(#{ module } -> #{ act })" ).length is 0 + arrEls = $( "#action_params div" ).filter () -> + $( this ).text() is "#{ module } -> #{ act }" + # It could have been loaded async before through the rules ito the action params + if arrEls.length is 0 $( '#select_actions' ).append $( '
' ).css( 'width', '20px' ).append img + tr.append $( '' ).attr( 'class', 'title').text name + td = $( '' ).attr( 'class', 'funcMappings').appendTo tr + fFetchActionFunctionArgs td, arrName + if arrName[ 0 ] not in arrEls + div = $( '
' ).appendTo $( '#action_params' ) + subdiv = $( '
').appendTo div + subdiv.append $( '
' ) + .attr( 'class', 'modName underlined' ).text arrName[ 0 ] + fFetchActionParams div, arrName[ 0 ] + $( "#select_actions option" ).each () -> + if $( this ).text() is name + $( this ).remove() + fFillActionFunction arrName[ 0 ] + fFetchActionParams = ( div, modName ) -> obj = command: 'get_action_invoker_params' @@ -149,7 +194,7 @@ fOnLoad = () -> fAppendActionParam name, sh for name, sh of oParams .fail fFailedRequest 'Error fetching action invoker params' - fFetchActionFunctionParams = ( tag, arrName ) -> + fFetchActionFunctionArgs = ( tag, arrName ) -> obj = command: 'get_action_invoker_function_arguments' payload: JSON.stringify @@ -174,25 +219,44 @@ fOnLoad = () -> .attr 'title', 'js-select expression to be resolved on event?' .fail fFailedRequest 'Error fetching action invoker function params' - fAddSelectedAction = ( name ) -> - arrName = name.split ' -> ' - arrEls = $( "#action_params div.modName" ).map( () -> - $( this ).text() - ).get() - table = $( '#selected_actions' ) - tr = $( '
' ).css( 'width', '20px' ).append img - tr.append $( '' ).attr( 'class', 'title').text name - td = $( '' ).attr( 'class', 'funcMappings').appendTo tr - fFetchActionFunctionParams td, arrName - if arrName[ 0 ] not in arrEls - div = $( '
' ).appendTo $( '#action_params' ) - subdiv = $( '
').appendTo div - subdiv.append $( '
' ) - .attr( 'class', 'modName underlined' ).text arrName[ 0 ] - fFetchActionParams div, arrName[ 0 ] - $( "#select_actions option:contains(#{ name })" ).remove() + fFillActionFunction = ( name ) -> + obj = + command: 'get_action_invoker_user_params' + payload: JSON.stringify + id: name + $.post( '/usercommand', obj ) + .done fAddActionUserParams name + + obj.command = 'get_action_invoker_user_arguments' + obj.payload = JSON.stringify + ruleId: $( '#input_id' ).val() + moduleId: name + $.post( '/usercommand', obj ) + .done fAddActionUserArgs name + + fAddActionUserParams = ( name ) -> + ( data ) -> + oParams = JSON.parse data.message + domMod = $( "#action_params div" ).filter () -> + $( 'div.modName', this ).text() is name + for param, oParam of oParams + par = $( "tr", domMod ).filter () -> + $( 'td.key', this ).text() is param + $( 'input', par ).val oParam.value + $( 'input', par ).attr 'unchanged', 'true' + $( 'input', par ).change () -> + $( this ).attr 'unchanged', 'false' + + fAddActionUserArgs = ( name ) -> + ( data ) -> + for key, arrFuncs of data.message + par = $( "#selected_actions tr" ).filter () -> + $( 'td.title', this ).text() is "#{ name } -> #{ key }" + for oFunc in JSON.parse arrFuncs + tr = $( "tr", par ).filter () -> + $( '.funcarg', this ).text() is "#{ oFunc.argument }" + $( "input[type=text]", tr ).val oFunc.value + $( "input[type=checkbox]", tr ).prop 'checked', oFunc.jsselector $( '#select_actions' ).on 'change', () -> opt = $ 'option:selected', this @@ -217,6 +281,8 @@ fOnLoad = () -> $( this ).closest( 'tr' ).remove() +# SUBMIT + $( '#but_submit' ).click () -> window.scrollTo 0, 0 $( '#info' ).text '' @@ -240,11 +306,12 @@ fOnLoad = () -> shielded = $( 'input', this ).attr( 'type' ) is 'password' ep[ key ] = shielded: shielded - if $( 'input', this ).attr( 'unchanged' ) is 'true' - ep[ key ].value = val - else + if not shielded or $( 'input', this ).attr( 'unchanged' ) isnt 'true' encryptedParam = cryptico.encrypt val, strPublicKey - ep[ key ].value = encryptedParam.cipher + ep[ key ].value = encryptedParam.cipher + else + ep[ key ].value = val + if $( '#selected_actions tr' ).length is 0 throw new Error 'Please select at least one action or create one!' @@ -262,11 +329,12 @@ fOnLoad = () -> throw new Error "'#{ key }' missing for '#{ modName }'" params[ key ] = shielded: shielded - if $( 'input', this ).attr( 'unchanged' ) is 'true' - params[ key ].value = val - else + + if not shielded or $( 'input', this ).attr( 'unchanged' ) isnt 'true' encryptedParam = cryptico.encrypt val, strPublicKey - params[ key ].value = encryptedParam.cipher + params[ key ].value = encryptedParam.cipher + else + params[ key ].value = val ap[ modName ] = params acts = [] actParams = {} @@ -286,7 +354,7 @@ fOnLoad = () -> actParams[ actionName ].push argument: $( 'div.funcarg', this ).text() value: $( 'input[type=text]', this ).val() - regexp: $( 'input[type=checkbox]', this ).is( ':checked' ) + jsselector: $( 'input[type=checkbox]', this ).is( ':checked' ) try conds = JSON.parse editor.getValue() @@ -379,19 +447,7 @@ fOnLoad = () -> if $( '#select_event' ).val() isnt '' fFetchEventParams oRule.event fPlaceAndPaintInterval() - obj = - command: 'get_event_poller_user_params' - payload: JSON.stringify - id: oRule.event.split( ' -> ' )[ 0 ] - $.post( '/usercommand', obj ) - .done ( data ) -> - oParams = JSON.parse data.message - for param, oParam of oParams - par = $( "#event_poller_params tr:contains(#{ param })" ).parent() - $( 'input', par ).val oParam.value - $( 'input', par ).attr 'unchanged', 'true' - $( 'input', par ).change () -> - $( this ).attr 'unchanged', 'false' + $( '#input_event' ).val oRule.event $( '#event_interval' ).val oRule.event_interval @@ -399,47 +455,9 @@ fOnLoad = () -> editor.setValue JSON.stringify oRule.conditions # Actions - oActions = {} for action in oRule.actions - fAddSelectedAction action arrName = action.split ' -> ' - if not oActions[ arrName[ 0 ] ] - oActions[ arrName[ 0 ] ] = [] - oActions[ arrName[ 0 ] ].push arrName[ 1 ] - fAddActionModuleParams = ( name ) -> - ( data ) -> - oParams = JSON.parse data.message - domMod = $( "#action_params div.modName:contains(#{ name })" ).parent() - for param, oParam of oParams - par = $( "td.key:contains(#{ param })", domMod ).parent() - $( 'input', par ).val oParam.value - $( 'input', par ).attr 'unchanged', 'true' - $( 'input', par ).change () -> - $( this ).attr 'unchanged', 'false' - - fAddActionModuleArgs = ( name ) -> - ( data ) -> - for key, arrFuncs of data.message - par = $( "#selected_actions td.title:contains(#{ name } -> #{ key })" ).parent() - for oFunc in JSON.parse arrFuncs - tr = $( ".funcarg:contains(#{ oFunc.argument })", par ).parent().parent() - $( "input[type=text]", tr ).val oFunc.value - $( "input[type=checkbox]", tr ).prop 'checked', oFunc.regexp - - for mod, arrMod of oActions - obj = - command: 'get_action_invoker_user_params' - payload: JSON.stringify - id: mod - $.post( '/usercommand', obj ) - .done fAddActionModuleParams mod - - obj.command = 'get_action_invoker_user_arguments' - obj.payload = JSON.stringify - ruleId: oRule.id - moduleId: mod - $.post( '/usercommand', obj ) - .done fAddActionModuleArgs mod + fAddSelectedAction action .fail ( err ) -> if err.responseText is '' diff --git a/webpages/handlers/js/forge_rule.js b/webpages/handlers/js/forge_rule.js index 29b36b8..fdf0154 100644 --- a/webpages/handlers/js/forge_rule.js +++ b/webpages/handlers/js/forge_rule.js @@ -48,53 +48,13 @@ }); fOnLoad = function() { - var editor, fAddSelectedAction, fFetchActionFunctionParams, fFetchActionParams, fFetchEventParams, obj; + var editor, fAddActionUserArgs, fAddActionUserParams, fAddEventParams, fAddSelectedAction, fFetchActionFunctionArgs, fFetchActionParams, fFetchEventParams, fFillActionFunction, fFillEventParams, obj; document.title = 'Rule Forge!'; $('#pagetitle').text('{{{user.username}}}, forge your ECA Rule!'); editor = ace.edit("editor_conditions"); editor.setTheme("ace/theme/monokai"); editor.getSession().setMode("ace/mode/json"); editor.setShowPrintMargin(false); - fFetchEventParams = function(name) { - var arr, obj; - $('#event_poller_params *').remove(); - if (name) { - arr = name.split(' -> '); - obj = { - command: 'get_event_poller_params', - payload: JSON.stringify({ - id: arr[0] - }) - }; - return $.post('/usercommand', obj).done(function(data) { - var fAppendParam, shielded, table, _results; - if (data.message) { - oParams = JSON.parse(data.message); - $('#event_poller_params').html('
Required Parameters:'); - table = $(''); - $('#event_poller_params').append(table); - fAppendParam = function(name, shielded) { - var inp, tr; - tr = $(''); - tr.append($('
').css('width', '20px')); - tr.append($('').attr('class', 'key').text(name)); - inp = $(''); - if (shielded) { - inp.attr('type', 'password'); - } - tr.append($('').text(' : ').append(inp)); - return table.append(tr); - }; - _results = []; - for (name in oParams) { - shielded = oParams[name]; - _results.push(fAppendParam(name, shielded)); - } - return _results; - } - }).fail(fFailedRequest('Error fetching event poller params')); - } - }; $.post('/usercommand', { command: 'get_event_pollers' }).done(function(data) { @@ -126,13 +86,15 @@ return fFetchEventParams($('#select_event option:selected').text()); }).fail(fFailedRequest('Error fetching event poller')); $('#select_event').change(function() { - if ($(this).val() === '') { + var evtFunc; + evtFunc = $(this).val(); + if (evtFunc === '') { $('#input_interval').html(''); } else { fPlaceAndPaintInterval(); } - $('#input_event').val($(this).val()); - return fFetchEventParams($(this).val()); + $('#input_event').val(evtFunc); + return fFetchEventParams(evtFunc); }); $('#input_event').change(function() { $('#select_event').val(''); @@ -144,6 +106,74 @@ return fPlaceAndPaintInterval(); } }); + fFetchEventParams = function(name) { + var arr, obj; + $('#event_poller_params *').remove(); + if (name) { + arr = name.split(' -> '); + obj = { + command: 'get_event_poller_params', + payload: JSON.stringify({ + id: arr[0] + }) + }; + return $.post('/usercommand', obj).done(fAddEventParams(arr[0])).fail(fFailedRequest('Error fetching event poller params')); + } + }; + fAddEventParams = function(id) { + return function(data) { + var fAppendParam, name, shielded, table; + if (data.message) { + oParams = JSON.parse(data.message); + $('#event_poller_params').html('
Required Parameters:'); + table = $(''); + $('#event_poller_params').append(table); + fAppendParam = function(name, shielded) { + var inp, tr; + tr = $(''); + tr.append($('').appendTo(table); + img = $('').attr('src', 'red_cross_small.png'); + tr.append($('').appendTo(table); - img = $('').attr('src', 'red_cross_small.png'); - tr.append($('
').css('width', '20px')); + tr.append($('').attr('class', 'key').text(name)); + inp = $(''); + if (shielded) { + inp.attr('type', 'password'); + } + tr.append($('').text(' : ').append(inp)); + return table.append(tr); + }; + for (name in oParams) { + shielded = oParams[name]; + fAppendParam(name, shielded); + } + return fFillEventParams(id); + } + }; + }; + fFillEventParams = function(moduleId) { + var obj; + obj = { + command: 'get_event_poller_user_params', + payload: JSON.stringify({ + id: moduleId + }) + }; + return $.post('/usercommand', obj).done(function(data) { + var oParam, par, _results; + oParams = JSON.parse(data.message); + _results = []; + for (param in oParams) { + oParam = oParams[param]; + par = $("#event_poller_params tr").filter(function() { + return $('td.key', this).text() === param; + }); + $('input', par).val(oParam.value); + $('input', par).attr('unchanged', 'true'); + _results.push($('input', par).change(function() { + return $(this).attr('unchanged', 'false'); + })); + } + return _results; + }); + }; obj = { command: 'get_action_invokers' }; @@ -157,11 +187,14 @@ return; } fAppendActions = function(module, actions) { - var act, _j, _len1, _results; + var act, arrEls, _j, _len1, _results; _results = []; for (_j = 0, _len1 = actions.length; _j < _len1; _j++) { act = actions[_j]; - if ($("#action_params div:contains(" + module + " -> " + act + ")").length === 0) { + arrEls = $("#action_params div").filter(function() { + return $(this).text() === ("" + module + " -> " + act); + }); + if (arrEls.length === 0) { _results.push($('#select_actions').append($('
').css('width', '20px').append(img)); + tr.append($('').attr('class', 'title').text(name)); + td = $('').attr('class', 'funcMappings').appendTo(tr); + fFetchActionFunctionArgs(td, arrName); + if (_ref = arrName[0], __indexOf.call(arrEls, _ref) < 0) { + div = $('
').appendTo($('#action_params')); + subdiv = $('
').appendTo(div); + subdiv.append($('
')).attr('class', 'modName underlined').text(arrName[0]); + fFetchActionParams(div, arrName[0]); + } + $("#select_actions option").each(function() { + if ($(this).text() === name) { + return $(this).remove(); + } + }); + return fFillActionFunction(arrName[0]); + }; fFetchActionParams = function(div, modName) { obj = { command: 'get_action_invoker_params', @@ -212,7 +271,7 @@ } }).fail(fFailedRequest('Error fetching action invoker params')); }; - fFetchActionFunctionParams = function(tag, arrName) { + fFetchActionFunctionArgs = function(tag, arrName) { obj = { command: 'get_action_invoker_function_arguments', payload: JSON.stringify({ @@ -245,26 +304,70 @@ } }).fail(fFailedRequest('Error fetching action invoker function params')); }; - fAddSelectedAction = function(name) { - var arrEls, arrName, div, img, subdiv, table, td, tr, _ref; - arrName = name.split(' -> '); - arrEls = $("#action_params div.modName").map(function() { - return $(this).text(); - }).get(); - table = $('#selected_actions'); - tr = $('
').css('width', '20px').append(img)); - tr.append($('').attr('class', 'title').text(name)); - td = $('').attr('class', 'funcMappings').appendTo(tr); - fFetchActionFunctionParams(td, arrName); - if (_ref = arrName[0], __indexOf.call(arrEls, _ref) < 0) { - div = $('
').appendTo($('#action_params')); - subdiv = $('
').appendTo(div); - subdiv.append($('
')).attr('class', 'modName underlined').text(arrName[0]); - fFetchActionParams(div, arrName[0]); - } - return $("#select_actions option:contains(" + name + ")").remove(); + fFillActionFunction = function(name) { + obj = { + command: 'get_action_invoker_user_params', + payload: JSON.stringify({ + id: name + }) + }; + $.post('/usercommand', obj).done(fAddActionUserParams(name)); + obj.command = 'get_action_invoker_user_arguments'; + obj.payload = JSON.stringify({ + ruleId: $('#input_id').val(), + moduleId: name + }); + return $.post('/usercommand', obj).done(fAddActionUserArgs(name)); + }; + fAddActionUserParams = function(name) { + return function(data) { + var domMod, oParam, par, _results; + oParams = JSON.parse(data.message); + domMod = $("#action_params div").filter(function() { + return $('div.modName', this).text() === name; + }); + _results = []; + for (param in oParams) { + oParam = oParams[param]; + par = $("tr", domMod).filter(function() { + return $('td.key', this).text() === param; + }); + $('input', par).val(oParam.value); + $('input', par).attr('unchanged', 'true'); + _results.push($('input', par).change(function() { + return $(this).attr('unchanged', 'false'); + })); + } + return _results; + }; + }; + fAddActionUserArgs = function(name) { + return function(data) { + var arrFuncs, key, oFunc, par, tr, _ref, _results; + _ref = data.message; + _results = []; + for (key in _ref) { + arrFuncs = _ref[key]; + par = $("#selected_actions tr").filter(function() { + return $('td.title', this).text() === ("" + name + " -> " + key); + }); + _results.push((function() { + var _j, _len1, _ref1, _results1; + _ref1 = JSON.parse(arrFuncs); + _results1 = []; + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + oFunc = _ref1[_j]; + tr = $("tr", par).filter(function() { + return $('.funcarg', this).text() === ("" + oFunc.argument); + }); + $("input[type=text]", tr).val(oFunc.value); + _results1.push($("input[type=checkbox]", tr).prop('checked', oFunc.jsselector)); + } + return _results1; + })()); + } + return _results; + }; }; $('#select_actions').on('change', function() { var opt; @@ -320,11 +423,11 @@ ep[key] = { shielded: shielded }; - if ($('input', this).attr('unchanged') === 'true') { - return ep[key].value = val; - } else { + if (!shielded || $('input', this).attr('unchanged') !== 'true') { encryptedParam = cryptico.encrypt(val, strPublicKey); return ep[key].value = encryptedParam.cipher; + } else { + return ep[key].value = val; } }); if ($('#selected_actions tr').length === 0) { @@ -347,11 +450,11 @@ params[key] = { shielded: shielded }; - if ($('input', this).attr('unchanged') === 'true') { - return params[key].value = val; - } else { + if (!shielded || $('input', this).attr('unchanged') !== 'true') { encryptedParam = cryptico.encrypt(val, strPublicKey); return params[key].value = encryptedParam.cipher; + } else { + return params[key].value = val; } }); return ap[modName] = params; @@ -368,7 +471,7 @@ return actParams[actionName].push({ argument: $('div.funcarg', this).text(), value: $('input[type=text]', this).val(), - regexp: $('input[type=checkbox]', this).is(':checked') + jsselector: $('input[type=checkbox]', this).is(':checked') }); }); }); @@ -461,7 +564,7 @@ }) }; return $.post('/usercommand', obj).done(function(data) { - var action, arrMod, arrName, fAddActionModuleArgs, fAddActionModuleParams, mod, oActions, oRule, _j, _len1, _ref, _results; + var action, arrName, oRule, _j, _len1, _ref, _results; oRule = JSON.parse(data.message); if (oRule) { $('#input_id').val(oRule.id); @@ -469,96 +572,16 @@ if ($('#select_event').val() !== '') { fFetchEventParams(oRule.event); fPlaceAndPaintInterval(); - obj = { - command: 'get_event_poller_user_params', - payload: JSON.stringify({ - id: oRule.event.split(' -> ')[0] - }) - }; - $.post('/usercommand', obj).done(function(data) { - var oParam, par; - oParams = JSON.parse(data.message); - for (param in oParams) { - oParam = oParams[param]; - par = $("#event_poller_params tr:contains(" + param + ")").parent(); - $('input', par).val(oParam.value); - $('input', par).attr('unchanged', 'true'); - } - return $('input', par).change(function() { - return $(this).attr('unchanged', 'false'); - }); - }); } $('#input_event').val(oRule.event); $('#event_interval').val(oRule.event_interval); editor.setValue(JSON.stringify(oRule.conditions)); - oActions = {}; _ref = oRule.actions; + _results = []; for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { action = _ref[_j]; - fAddSelectedAction(action); arrName = action.split(' -> '); - if (!oActions[arrName[0]]) { - oActions[arrName[0]] = []; - } - oActions[arrName[0]].push(arrName[1]); - } - fAddActionModuleParams = function(name) { - return function(data) { - var domMod, oParam, par; - oParams = JSON.parse(data.message); - domMod = $("#action_params div.modName:contains(" + name + ")").parent(); - for (param in oParams) { - oParam = oParams[param]; - par = $("td.key:contains(" + param + ")", domMod).parent(); - $('input', par).val(oParam.value); - $('input', par).attr('unchanged', 'true'); - } - return $('input', par).change(function() { - return $(this).attr('unchanged', 'false'); - }); - }; - }; - fAddActionModuleArgs = function(name) { - return function(data) { - var arrFuncs, key, oFunc, par, tr, _ref1, _results; - _ref1 = data.message; - _results = []; - for (key in _ref1) { - arrFuncs = _ref1[key]; - par = $("#selected_actions td.title:contains(" + name + " -> " + key + ")").parent(); - _results.push((function() { - var _k, _len2, _ref2, _results1; - _ref2 = JSON.parse(arrFuncs); - _results1 = []; - for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { - oFunc = _ref2[_k]; - tr = $(".funcarg:contains(" + oFunc.argument + ")", par).parent().parent(); - $("input[type=text]", tr).val(oFunc.value); - _results1.push($("input[type=checkbox]", tr).prop('checked', oFunc.regexp)); - } - return _results1; - })()); - } - return _results; - }; - }; - _results = []; - for (mod in oActions) { - arrMod = oActions[mod]; - obj = { - command: 'get_action_invoker_user_params', - payload: JSON.stringify({ - id: mod - }) - }; - $.post('/usercommand', obj).done(fAddActionModuleParams(mod)); - obj.command = 'get_action_invoker_user_arguments'; - obj.payload = JSON.stringify({ - ruleId: oRule.id, - moduleId: mod - }); - _results.push($.post('/usercommand', obj).done(fAddActionModuleArgs(mod))); + _results.push(fAddSelectedAction(action)); } return _results; } diff --git a/webpages/handlers/remote-scripts/forge_module.html b/webpages/handlers/remote-scripts/forge_module.html index 32c04bb..1a3fa99 100644 --- a/webpages/handlers/remote-scripts/forge_module.html +++ b/webpages/handlers/remote-scripts/forge_module.html @@ -18,14 +18,40 @@ exports.newMail = ( pushEvent ) -> # Syntax: needle.request method, url, data, [options], callback # needle.request 'get', url, null, null, ( err, resp, body ) -> + log 'Poll function executed' if err log 'Error in EmailYak EM newMail: ' + err.message else if resp.statusCode is 200 - for mail in body.Emails - pushEvent - subject: mail.Subject - content: mail.TextBody + if body.Emails.length > 0 + log JSON.stringify body.Emails[0] + log 'Poll function executed' + pushEvent mail for mail in body.Emails + + ### + This will emit events of the form: + ( Refer to http://docs.emailyak.com/get-new-email.html for more information. ) + + { + "EmailID": "xquukd5z", + "Received": "2014-04-19T11:27:11", + "ToAddress": "test@mscliveweb.simpleyak.com", + "ParsedData": [ + { + "Data": "Best Regards\nTest User", + "Part": 0, + "Type": "Email" + } + ], + "FromName": "Test User", + "ToAddressList": "test@mscliveweb.simpleyak.com", + "FromAddress": "test.address@provider.com", + "HtmlBody": "Best Regards\nTest User", + "CcAddressList": "", + "TextBody": "Best Regards\nTest User", + "Subject": "test subject" + } + ###