Default executors Pub

The default executors are built-in functionality that it's always available, from accessing the context, to flow control to communication.

There are few services/categories of messages:

Special flow control messages

Please read Engine

These messages are triggered internally before and after running an engine. You can use them to set environment variables, constants etc.

Before and after

Each flow will be wrapped in a set of engine-generated messages:

  • diesel.vals
  • diesel.before
  • ... // all rules and messages
  • diesel.after
msg diesel.vals 

This is an internally generated and processed message. This will collect all $val declarations at the global scope and execute them, so that all global values and variables are defined before any other rules are triggered.

You cannot handle or intercept this message.

When inspecting a trace, this message is logged at "trace" level.

msg diesel.before 

This is a message that is internally generated as the first message of each flow. You can use this to setup say constants and other common statics across all flows.

You can typically use it to define constants:

$when diesel.before
=> ctx.set (
    INV_CANCELED="666400011",
    INV_APPROVED="666400016",
    INV_DRAFT="666400002",
    INV_POSTED="666400009",
    INV_FAILED_POSTING="666400010"
    )
msg diesel.after 

This is an internally generated message after any flow has fininshed. You can intercept it and run some logic.

Exception handling, try-catch-throw

msg diesel.try 

msg diesel.catch 

Exception handling works somewhat different from a programming language, and it still in progress, here are some principles (see examples in engine story and engine spec):

  • each message is executed asynchronously in a separate context
    • any exception does not cause other side effectrs in the flow, it simply results in an EError node being added to the flow and the payload set to an Exception object.
  • demarcate a block with try-catch, to have all exceptions caught inside.
  • an exception (like divide by zero) does not cascade up or stop anything. the payload is set to the exception, but the flow continues
  • it is up to you to either catch this and do something (like diesel.return) or check the type of payload payload is exception or such

Examples of handling errors and exceptions:

$when my.lookup.list
=> snakk.thirdparty(...) // assume this returns exception
=> $if (payload is exception) (payload=[])

In a story, when you want to ignore exceptions in a block (exceptions are reported as failures) you can surround that block with a try-catch:

$send diesel.try
$send ctx.set(oops = 1/0)
$send diesel.catch
msg diesel.throw 

This will throw a diesel exception within the flow. It will behave much like throwing exceptions in code:

  • other siblings are stopped from processing, only within the parent scope
  • any input values passed to diesel.throw will be evaluated and added to the context

Other

msg diesel.return 

Stop the current flow and return. You can include a return code, headers etc - if the flow will return as an HTTP server.

Example:

$mock diesel.rest (path ~path "/account2/404/id") 
=> diesel.return(
    diesel.http.response.status=404,
    diesel.http.response.header.myHeader = "mine",
  )
msg diesel.debug 

msg diesel.later 

msg diesel.engine.sync 

The engine is by default asynchronous - meaning that each message is executed in a separate actor message/context, on potentially separate threads.

diesel.engine.sync will cause the engine to process the rest of the current flow in synchronous mode - this means that even if multiple paths are available at one point, they will be processed in sequence, on the same thread, as opposed to being processed as separate messages.

Advantages are making the engine a bit faster (the engine uses this mode in very few scenarios), but disadvantages include potentially impacting the engine if exceptions appear or timeouts etc.

When to use it? No good reason that I can think of, other than looping through collections or such.

msg diesel.engine.async 

Like diesel.engine.sync but will have the opposite effect: switch the enging back to the default asynchronous mode

ctx

This is the default executor for messages to the current context (data).

msg ctx.log 

Print the current context to log.

msg ctx.info 

Insert an EInfo - if you click on it, it will dump the value to the browser's console.

msg ctx.test 

msg ctx.debug 

Dump the contents of the current context - you can use it to see what values are there.

msg ctx.echo 

Echo a value - you can easily see a value on the screen - use it to debug expressions.

msg ctx.setVal  (name, value)

Set a value with the given name and value.

msg ctx.set 

Set values: one for each input argument.

msg ctx.sleep  (duration)

Sleep - duration is in millis. Good to simulate timeouts etc.

msg ctx.timer  (duration)

msg ctx.base64encode  (result)

msg ctx.base64decode  (result)

Encode or decode BASE64. Each input value is decoded into payload. If result is passed in, then a value with that name will also be populated.

msg ctx.sha1  (result)

msg ctx.sha256  (result)

Like base64 but sha1 or sha256 encoded as hex. You can pass in many parameters like parm1 and each will result in an parm1_sha1 or a parm_sha256 respectively. Additionally, you can pass in a result="outputParm1" if you want a specific output value, otherwise the payload will be used.

msg ctx.persisted  (kind, id)

Persist the current context between calls / executions - see State Machines for examples:

msg ctx.clear 

Clear the current context:

Story teller instructions

msg ctx.storySync 

msg ctx.storyAsync 

Story values are available for use in story constructs

msg test.diesel.storyVal 

=> (theValue=storyValue1)

Authorization and authentication

Run tests in the context of a user, configurable per domain. Normally, an automated test runs in the background, so without a user context. This will pose many issues to testing APIs that are meant to work in the context of a user - so this is where this message comes in handy.

Invoke it at the beginning of a story that's meant to run in the context of a user. And configure this special user in the reactor properties, as diesel.testUserEmail=someemail where the ID is a user id.

msg ctx.setValAuthUser 

Make sure there is an user. This will prevent public messages from being invoked by the "public" or index engines etc.

msg ctx.authUser 

TODO document

msg ctx.csv  (list, separator)

Use this to convert a list of documents/objects into a list of strings CSV style, first row will be the headers.

msg ctx.mkString  (separator)

Convert a payload of list of strings into a string, using the separator.

Databases

The document database model is used to keep state and there are a few kinds available:

  • memdb - in memory, per user
  • shareddb - in memory, per app
  • col - persisted, available for paid accounts, depending on volume

The generic operations are:

  • upsert(collection, id, document) => (id) - update or create document with given value
  • getsert(collection,id,default) => (document) - get or create, if default given. If no default and nothing found, it will return an Undefined
  • query(collection,parmA,parmB...) => (documents) - query documents, based on document properties
  • clear(collection) - delete all entries from one collection / one document type... *careful with this one...

Generic DB operations

These are implemented by all DB types and instances:

msg diesel.db.INST.upsert  (collection, id, document)

msg diesel.db.INST.getsert  (collection, id, default)

msg diesel.db.INST.query  (collection)

msg diesel.db.INST.clear  (collection)

Mem DB

The in-memory database is good to mock up functions that require a bit of state. You should not rely on it being available or persisted for too long :).

msg diesel.db.memdb.upsert  (collection, id, document)

msg diesel.db.memdb.getsert  (collection, id, default)

msg diesel.db.memdb.query  (collection)

msg diesel.db.memdb.log 

msg diesel.db.memdb.clear 

Shared DB

The shared database is good to mock up functions that require a bit of state. This one is persisted and available in a cluster (in case of transparent restarts of processing nodes etc).

msg diesel.db.shareddb.upsert  (collection, id, document)

msg diesel.db.shareddb.getsert  (collection, id, default)

msg diesel.db.shareddb.query  (collection)

msg diesel.db.shareddb.log 

msg diesel.db.shareddb.clear 

Persisted DB

This is an actual persisted DB - avaialble for paid member accounts.

msg diesel.db.col.upsert  (collection, id, document)

msg diesel.db.col.getsert  (collection, id, default)

msg diesel.db.col.query  (collection)

msg diesel.db.col.clear  (collection)

Wiki functions (EEWiki)

The wiki executor deals with wiki commands.

msg wiki.follow  (userName, wpath, how)

User follows a wiki (for instance following the club's calendar when joining a club).

msg wiki.content  (wpath, result, type)

Set a value with the name contained by result to the content referenced by wpath. The content is not formatted or pre-processed. This is useful to get schemas, sample data etc - all of these can be saved as topics and loaded into variables like this.

If there is no result specified, the payload is set to the respective contents.

The optional type is used to coerce the wiki content string into a given type, also parsing it etc.

msg diesel.wiki.updated  (wpath, realm, event)

Generated automatically when a topic is updated. You can attach rules to it and handle it.

def

JS script executor

This is an automatic executor which will execute blocks of code.

If you define a function with $def like so:

$def func.haha(p1,p2) {{
p1+p2
}}

Then this is executed for the func.haha message:

$msg func.haha (p1="a", p2="b")

$when ha.ha => func.haha (p1,p2)

$msg ha.ha (p1="a", p2="b")

REST (snakk)

This one can make REST calls - it works by defining templates for the calls, see REST and HTTP templates - as you can see, the templates mirror the actual HTTP calls, so you can configure header attributes or content or both.

You can also snakk directly by calling these:

msg snakk.json  (url, verb, body, headers, result)

msg snakk.xml  (url, verb, body, headers, result)

Here's a sample usage (using oauth to get a token):

$when api.getAccessToken
=> snakk.json(
    url="${AUTH_URL}", 
    verb="POST", 
    'Content-type'="application/x-www-form-urlencoded",
    body="grant_type=client_credentials&client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET_ENC}&resource=${RESOURCE}/"
    )
=> (accessToken=payload.access_token)

The result of snakk.json is of type JSON and you can see the last expression extracts something from it.

See more details in Snakking REST.

REST APIs

msg diesel.rest 

This is the default message generated for an incoming HTTP event. It will be passed these parameters:

  • path the request path
    • you can match the path to an extraction pattern with the special ~path operator
    • it will not only match but also extract, in the example below:
    • :env a single path segment
    • *elkpath the rest of the path from that point on
  • other parameters passed in context:
    • path the path of the incoming request
    • verb the verb: GET, POST, PUT, PATCH etc
    • queryString decoded query string
    • queryStringEncoded non-decoded query string, as it came in
    • dieselQuery as a JSON object containing all the parsed query params
    • all the query parameters are also flattened and passed in - note that you should not overwrite any of the reserved parameters in this list - the behaviour is undefined
$when diesel.rest (path ~path "/v1/:env/elk/*elkpath", verb == "GET")
=> elk.query.passthrough(path=elkpath, query=queryString)

Scope

You can push and pop scopes - this is important to define independent sub-scopes.

msg diesel.scope.push 

msg diesel.scope.pop 

Lifecycle events

When a realm is loaded in a node (this will be called once per node, many times in a cluster). To enable this message, just map it to something, in the Environment settings.

msg diesel.realm.configure  (realm)

When the settings for an environment and user combination are needed, this message will be called - you have to configure it in Environment settings and set any global variables needed by any of the flows.

This is called multiple times as EnvironmentSettings is updated - careful with resource leaks etc.

msg diesel.realm.loaded  (realm)

This is called when a reactor is loaded on a node. This is generally when the node starts. Set any globals here.

msg diesel.setEnv  (env, user)

For instance:

$when diesel.setEnv(env == "sandbox")
=> ctx.set (
    HOST = "https://sandbox1.cloudhub.io",
    URL = "https://sandbox1.cloudhub.io/myService",
    PING_URL = "https://sandbox1.cloudhub.io/status"
    )

Guardian messages

The guardian is a utility for automated testing and executing flows. See Guardian for more details and examples.

Create a guardian schedule for a specific environment - you have to call this on diesel.realm.loaded:

msg diesel.guardian.schedule  (schedule, env, inLocal:Boolean<-false)

A poll is executed every time on the schedule - you need to implement this, in Environment settings. Read more in Guardian.

msg diesel.guardian.poll  (realm, env)

After a poll, this decides to start a run, read more in Guardian.

$msg diesel.guardian.polled msg diesel.guardian.run 

msg diesel.guardian.starts  (realm, env)

msg diesel.guardian.ends 

msg diesel.guardian.notify 

    EMsg(DG, "report") ::
    EMsg(DG, "stats") ::
    EMsg(DG, "clear") :: Nil

Cron functionality

The crons are best configured in Environment settings. Every time you issue a diesel.cron.set on a given name, it will reset that job and re-start it (if there was a counter, the counter is reset).

Note that these jobs fire off in every node in the cluster. If you only want one, like a singleton, use the Diesel Singleton++.

msg diesel.cron.set  (name, schedule<-"30 seconds", env, count, tquery, singleton, collectCount)

Start a cron schedule with the given name and frequenccy (schedule).

  • Parameters:
    • name - he name is mandatory and you can identify different schedules by name.
    • schedule - in natural language , example 30 seconds. The units are:
      • "d day",
      • "h hour",
      • "min minute",
      • "s sec second",
      • "ms milli millisecond",
      • "µs micro microsecond",
      • "ns nano nanosecond"
    • count - optional: it will stop after this many occurences
    • tquery - this is an important one: every time this fires, a new engine is created and it will load all the specs that match this tquery (these tags), see tag query++
    • singleton - TBD, by default all cron jobs are singletons (i.e. only one per environment) but on-demand, there is an ability to have a job per node (i.e. load some configuration etc)
    • collectCount - by default, cron job traces are not equal to others, so only the last 10 (system configuration) are collected - you can overwrite this if they're important
      • 0 will not keep any. A larger number may or may not work, depending on other constraints

It's best to set this up when the realm is loaded - this is executed upon startup:

$when diesel.realm.loaded
=> diesel.cron.set (name="keealive", env="sandbox", schedule="5 minutes")
msg diesel.cron.list 

This will list all the current cron jobs / schedules.

msg diesel.cron.tick  (name, env)

This is fired off every time - you need to intercept it and do something, in Environment settings, example:

$when diesel.cron.tick(name == "keepalive")
=> snakk.json(url="http://this.is.keptalive.com/keepalive")
msg diesel.cron.stop  (name, env, count)

This is fired off when a count is reached and the timer stops.


Was this useful?    

By: Razie | 2016-10-23 .. 2020-11-22 | Tags: spec , engine , academy , reference , dsl


Viewed 103 times ( | Print ) this page.

You need to log in to post a comment!

© Copyright DieselApps, 2012-2020, all rights reserved.