ScriptingIntroduction to Scripting
The scripting engine is a work in progress and updates are being introduced regularly. If you've somehow ended up on this page (possibly through a direct link sent to you or a search result) then congratulations, you are now in on one of our roadmap secrets. Let's keep it that way, and we'll make sure to announce it with a bang once it's fully up and running.
Scripting is an engine built to freely allow users to extend the possibilities on what can be done with some EVA aspects using a custom language. Areas made scriptable in EVA are called dialects, and as of now there are two such dialects, Order Orchestration and Cookbook. However, some fine-tuning is in progress for those as well.
The new kid on the block is what we call Extension points, and with this you would be able to extend parts of EVA with scripts. Basically, an extension point is a script that receives an input context (with different available variables depending on what you're extending) and that returns an output value. In the process, you invoke it with scripts to extend its purpose or achieve a certain criterion, then return an output to do something with.
At the moment scripting can only be managed through CRUD services, a frontend is in progress.
The scripting language
Our scripting engine uses a custom language however, one that is close to normal English as possible i.e. no weird symbols unless necessary. So here are supported expressions:
- Logical operators:
or, get translated to
- Equality comparison:
- Collection checks:
- Null checks:
has no value
- String literals: single quotes, multi-line strings are supported
- Interpolated strings: Work the same as C#, except single quotes
- Number literals: supports both floating point (10.0) and integers (10), negative and positive.
- Boolean literals:
- Null literal:
- Match expressions: for example
when Foo = 'bar' then 1 else when Foo = 'foo' then 2 else then 3, can be used anywhere an expression is expected
- Braces: any expression can be wrapped in round braces to make precedence clear:
10 * (5 + 2)instead of
10 * 5 + 2.
- Member access: any non-keyword identifier is considered to be a reference to a variable.
- Array literals: a primary (a literal, or a member access) or match expression separated by commas is considered an array literal, for example
1, 2, 3is considered an array.
- Lookup expression: do a look-up into a defined table. Example:
Value from Table where CountryID = 'NL'. It's a core expression, but only works if your dialect allows table definitions.
Extension points, as the name implies, are specified points in EVA which you can extend through scripts.
Here is an example:
| CountryID | Amount | MinOrderAmount |
| 'NL' | 1.95 | 100 |
| 'NL' | 4.95 | 0 |
| 'US' | 2.95 | 100 |
set shippingCosts to (Amount from DefaultShippingCosts
where CountryID = Order.SoldFromOrganizationUnit.CountryID
and Order.TotalAmountInTax >= MinOrderAmount)
An extension point should start with extend and then declare which aspect of EVA it is extending, in this case
CalculateShippingCosts. The available extension points are all classes that implement
IExtensionPoint. The above script makes use of the ability to define an ASCII table and then does a lookup in it.
Every extension point must end with an output statement which determines the result that EVA gets back from the script. Currently, the output statement must come at the end, no early returns are supported.
You can control which extension points a user is allowed to script, by using the scripting check box under the respective user role functionalities.
These statements are currently supported inside extension point scripts:
Sets a variable to a value.
set <name> to <value>.
The variable doesn't have to be defined first, and it can be set multiple times.
The variable used has top-level scoping, when transpiling it to C# it's declared at the start of the function.
if <expression> then
else if <expression> then
Declares a lookup table which can make some logic easier to express, instead of complicated if-then-else constructions.
| <name1> | <name2> |
| <value1> | <value2> |
So you first define a header row with column names, the name of the column is important as you use that to reference to it later. A pipe symbol is used to separate columns. The pipe symbol at the end of each line is optional.
For each loop
For each loop Added mostly as a proof-of-concept for an improvement in the parser in variable binding, but otherwise fully functional.
for each <name> in <expression> do
for each line in Order.Lines do
if line.IsStock then
set hasStockProducts to true