Here's the big Dieselapps overview. Each section links to detailed topics.
Dieselapps is based on a Markdown Wiki with DSL (Domain Specific Language). Every "page" is a wiki topic, with a category, content and tags. The category and the tags are meaningful, see stories and specs below.
Basically, the wiki pages are written in markdown and can contain specific DSL instructions, for example this one declares a message
## This is an example topic
With some floating text, and...
$msg home.guest_arrived(name:String) : (welcome)
Read more details at Markup and DSL
If you're wondering what the DSL is used for, among the few capabilities like domain modelling etc, the most important is defining and using rules for the embedded rules engine. See DSL Reference for details on the current DSL capabilities (it is an extensible framework).
A rule defines the behavior for a specific message
or event
- in this case generates another message (chimes.welcome
) and derives a new value in context (welcome
), which are the typical things you do:
$when home.guest_arrived(name == "Jane")
=> chimes.welcome(name="Jane")
=> (welcome = "Welcome, ${name}")
A message looks much like a function, it has a qualified name and a list of attributes or parameters as well as a resulting list of attributes - you can declare a message's signature:
$msg home.guest_arrived(name) : (welcome)
$msg elk.devices.query(name) : (deviceList, totalCount)
The attributes can be typed, have default values etc, see more in Message. The actual rules to handle these messages, those defined with the $when
or $mock
keywords usually add conditions on these attributes, for instance this rule only welcomes Jane
:
$when home.guest_arrived(name == "Jane")
=> chimes.welcome(name="Dear Jane")
So what are these messages? They could be things like
executor
like snakk.json
(there are many built in Default executors)The rules engine decomposes messages based on these rules. A flow can be started in several ways, the most common is via an API call and diesel.rest
, for instance if you want a simple API:
$mock diesel.rest (path ~path "/account2/404/id")
=> diesel.flow.return(
payload="Nope, 404 is mine...",
diesel.http.response.status=404,
diesel.http.response.header.myHeader = "mine",
)
During a flow's execution, the flow will look like nodes in a tree, based on the rules that were found applicable, which decomposed into other messages and so on. The nodes are generally messages, values/variables, or just other information (logs etc).
When you see a trace, you can hover on the colored tags for additional details and info and click them, if the cursor changes to a hand, to see where that element was generated from.
You can see these traces if you select one flow in the flow viewer, or when editing a Story in either the fiddle or the main editor with the "trace" checkbox enabled.
Besides messages, in a flow you can set and read values of attributes. These are stored in a "context" much like a function's variables, see above the lights
value being set to "bright" or the example above, setting => (welcome=...)
.
These contexts are fairly intuitive, work close to what you'd expect scopes to work in regular programming languages, but there may be some gotchas as you progress to more and more complex constructs, you'll see.
The payload
is a special value which is passed from message to message, you can think of it as the "current result" or the "currently returned value". Each message can set the value of payload and it is the invisible hand-off from message to message, unless the message declares a specific returned value like $msg this.one.returns : (myValue)
.
For instance in this sequence, the result of snakk.json is populated in the payload
and implicitely passed on to ctx.echo
:
$send snakk.json(url="...")
$send ctx.echo
Or this more explicit version:
$send ctx.echo(x=payload)
Built-in or custom Executors can handle specific messages and take action on them, these plug into the rules engine and use specific prefixes, which you should not reuse or redefine, to avoid unexpected behavior.
There are several predefined executors - these prefixes are reserved:
snakk
- HTTP requestsdiesel
- special engine functions (like return, exceptions etc)ctx
- context functions (set, transform values etc)wiki
- interact with the wiki engine (read content, receive change events etc)Read about these at Default executors - some of the most common are:
ctx.set(name=expr)
to set a valuesnakk.json(url,verb)
to read a URLdiesel.flow.return
to stop a flow and return a valuediesel.rest
entry point for an APIctx.echo
to echo any values into the traceThe main entry-point to trigger workflows is /diesel/rest
which internally raises a diesel.rest
event, which can be handled with rules like:
$when diesel.rest (path ~path "/v1/ems/:env/device/:deviceType/:deviceId/config/:configRole/*configPath")
=> ems.cfg.query(env, deviceId, deviceType, configRole)
The ~path
operator is special for handling URL paths and will not only match the URL but also extract the values like :configRole
and *configPath
which are then populated as values in the context - this notation is typical for URL router frameworks. Path elements like :env
are parsed and populated as attributes in context etc - see more details in Default executors.
To reach out to other services, the snakk
executor is used, the most used messages are:
snakk.text
- read test from an URLsnakk.json
- read json from an URLThe result is placed in payload
.
While all wiki topics are equal, some are special when using rules, these two categories:
Spec
- specs can specify messages and rulesStory
- stories can trigger messages and rules (i.e. are tests, active acceptance criteria etc)So, the typical constructs for a spec are $mock
and $when
, while for stories, they would typically use $send
and $expect
.
These two rules below would both trigger for the .../sync
url, so to make only the first one trigger ang ignore the second, we'll make the first one exclusive
- a rule tagged as exclusive will dominate all other applicable rules and exclude them:
$when <exclusive> diesel.rest (path ~path "/v1/ems/:env/device/:deviceType/:deviceId/config/sync")
=> ems.cfg.sync(env, deviceType, deviceId)
$when diesel.rest (path ~path "/v1/ems/:env/device/:deviceType/:deviceId/config/:configRole")
=> ems.cfg.query(env, deviceId, deviceType, configRole)
The regex operator ~=
can decompose the input based on the named matching groups, i.e. below the correlationId will be available as a value inside the rule:
$when::
timer.ticked (name ~= "check(?<correlationId>.+)"
)
ctx.echo (msg=correlatio...)
See Diesel Fiddle Guide for details on using the fiddle.
Dieselapps is ran in the cloud and a local environment. The cloud deployment cannot reach the internal network, so for development you need to use a local environment (on premises), unless all other components are avaialble from the internet.
In the cloud, there are two projects (app is your app's name):
devapp
which is the development project, includes storiesapp
which is the production project, no test stories, just specsWhen developing on the cloud, you work in the devapp
project and when ready, sync only the specs to the app
production project. When you go to User/Diff menu from the devapp
project, it will compare to the app
project by default.
When running local, say on mybox1
, only the production project is cloned, for production.
Typical development process:
You need to log in to post a comment!