Message Pub

This entire system revolves around "messages". A message can map into a few concepts:

  • an event
  • a function
  • a fact (as in rules)
  • a service call (WS or REST)
  • notification
  • etc

An event or message has the form $msg subject.verb (parm1=value,...). It associates a verb to a subject and a list of attributes.

The subject can be qualified and can be:

  • a subsystem, like email or bell
  • an entity like Person or product
  • prefixed, such as my.system.email - use this for packages or other grouping or hierarchy of entities

The verb is a single word (not qualified) can be:

  • a method of a class, i.e. Person.walked_in
  • a function of a service, i.e. email.create
  • an action of or on an entity i.e. bell.ring

Messages can be received, generated, sent or mocked:

  • received via the Diesel API
  • generated with $when
  • sent by default, when defined with $msg and a known protocol
  • mocked with $mock
  • tested with $expect

Declaring messages

Messages can and should be declared, inside a Spec like so:

$msg home.guest_arrived(name:String ~= "Jane") : (greetings:String)

The type annotations and default values for arguments are optional - they are helpful though and it's good practice to provide them!

If messages are not declared, their respective rules are still applied, but there is less information for the engine or the integration points (receiving messages) to process them, some of the effects may include:

  • wrong type for results
  • not processing the proper results, other than payload
  • etc

Parameters / arguments - definition

The general form of a parameter definition is: @annotation name:<>type[kind]*~=default. Do remember that parameters also can be used in either declarations or rule matching, the syntax looks the same but there are different options in either case.

Here are the annotations you can use for parameters/arguments:

  • name name
  • type name:String
    • optional List indicator names:String*
  • optional indicator name?
  • default / sample value name ~= "Jane"
  • default expression
  • archetypes

Types are generally inferred, but when ingesting data, declaring types for messages is a good idea. studentList : Student* or age:Number

Here are some examples:

$class Student (@key name:String)

$msg calculateGravity (mass:Number, g = 9.8)

The sample values are used when generating the messages automatically, especially in sketch mode, see Flags and modes.

Parameters - matching

When matching arguments to a rule, there is a slightly different set of expressions available, example:

$when caught.skipping.class (students:Student*, when:Date)

$when a.factorial (num is 0)

$when diesel.rest (path ~path "/v1/ems/:env/device/:deviceType/:deviceId", verb == "GET")

$when diesel.rest(path ~= "/myActualServer/create/(?<user>.+)")

Decomposing messages

Messages can be decomposed in a Spec, via matcher rules, with the $when construct:

$when subjectMatch.verbMatch (parmMatch) IF => IF subject.verb (parms)

When a suitable match is found, the resulting message will be created. IF is an optional condition to apply this decomposition rule. You can see more details about Expressions and pattern matching.

$when home.guest_arrived(name) => lights.on

$when home.guest_arrived(name=="Jane") if (isRaining == "true") => chimes.welcome(name="Jane")

$when lights.* => lights.check

$when *.sendtest => (rule12=true)

$when dieseltestsendtest.* => (rule16=true)

$when dieseltest.send.multiple.* => (rule18=true)

$when /dieseltest.*/ => (rule19=true)

$when /dieseltest\..*/ => (rule19a=true)

There's also a multiline version:

$when home.guest_arrived(name) 
=> if (name!="Jane") lights.on
=> if (name=="Jane" && isRaining == "true") => chimes.welcome(name="Jane")

$when is the message implementation, while $mock is the message's mock.

Mocking

Alongside the $when rules, you can define $mock rules - these are used when mocking the message - see Mocking services++ and Flags and modes.

Sending messages

A Story starts with a message. This is either simulated when testing or sampled in an external message stream++.

$msg subject.verb (parms) is used to simulate a message.

$sample subjectMatch.verbMatch (parmMatch) is used to sample messages.

$msg home.guest_arrived(name="Jane")

When either is seen (simulated or sampled), and it meets the conditions of the first message in the story, the story is "triggered" and the expectations are checked.

Messages can be triggered via the Diesel API as well.

Testing stories

If the message was simulated as a result of running the story in test mode, then the following messages are also triggered and tested.

If the message was observed during normal runtime and we only need to test it, the story will progress as further messages are observed during the same runtime (i.e. their appearance is tested).

Testing messages

When a story is executed, its expectations are being tested:

$expect subjectMatch.verbMatch (parmMatch)

Such as:

$expect (greetings=="Greetings, Jane")

$expect chimes.welcome(name=="Jane")

Note the difference between checking for a message with values versus the version to check just for values.

Mocking messages

When running in mockMode, messages can be mocked instead of actually "executed":

$mock subjectMatch.verbMatch (parmMatch) => (parms)

Examples:

$mock lights.check => (lights="bright")

$mock chimes.welcome => (greetings="Greetings, "+name)

Executing messages

When not running in mock mode, messages will be executed. A suitable executor will be found and passed the message.

Some executors are defined by default, such as ctx or wiki, see Default executors.

Otherwise, you can extend the system by creating your own executors, see [The SDK]].

Synchronous vs async execution

Message execution is naturally asynchronous, in the engine's internal implementation. However, logically, the execution is treated as synchronous. The engine uses Akka actors internally, to execute each message individually, in its own separate execution context.

This represents the ultimate in flexibility but also ads some complexity for the users:

  • when is a message really "done" ?

For instance, the message $msg <POST> subDb.createSub is an asynchronous REST call, consisting of a request and then a reply on a different actor/thread.

Thus, when the message is triggered initially, it is not in fact complete, as logically, it should wait for the response.

The executor of the message is responsible for notifying the engine when the message is completed. At this point, the engine will consider the next messages in a sequence.

See more in Concurrency, asynchronous and distributed.


Was this useful?    

By: Razie | 2016-06-21 .. 2022-11-15 | Tags: academy , reference


Viewed 633 times ( | History | Print ) this page.

You need to log in to post a comment!

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