Skip to main content

Introduction

docs image

Scripting

Introduction to Scripting
Under construction

The scripting engine is a work in progress and updates are being introduced regularly.

Scripting is an engine built to enable you 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.

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: and, or, get translated to && and ||
  • Equality comparison: =, <>, <, >, <=, >=
  • Collection checks: in, not in, contains, not contains
  • Null checks: has value and 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: true, false or True, False
  • Null literal: nothing or null.
  • 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, 3 is 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, and will work only if our dialect allows for table definitions, which we do.
  • Regular expressions (AKA regex): you can use regex to validate the format of the input. For example:

Regex example
if SerialNumber.IsMatch with Pattern '[A-Z0-9]{10}' then
set output to true
end
Making strings continue

To prevent strings from being cut off after a ' symbol, use a \ symbol.

For example, the string 'De l'orange' is cut off at De l'. By adding a \ like so: De l\'orange' the entire string is taken into account.

Changes to Arrays

To improve the capabilities of arrays, you can now also type them bracketed, like so: set x to [1, 2, 3] For a short time both the old and new way of writing arrays will be accepted, but the new way will become the EVA standard. A migration will take place before the old (bracketless) way is no longer recognized.

Extension points

note

For a list of available extension points, please use the service GetScriptExtensionPoints. You can also check out samples and learn more about the available and supported extension points for scripting here.

Extension points, as the name implies, are specified points in EVA which you can extend through scripts.

Here is an example of one that uses a lookup expression using a Table statement:

extend CalculateShippingCosts

table DefaultShippingCosts
| CountryID | Amount | MinOrderAmount |
| 'NL' | 1.95 | 100 |
| 'NL' | 4.95 | 0 |
| 'US' | 2.95 | 100 |

set shippingCosts to (from DefaultShippingCosts
where CountryID = `NL`
and MinOrderAmount < 100
select Amount)

output shippingCosts

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.

Permissions for scripts

You can control which extension points a user is allowed to script, by using the scripting check box under the respective user role functionalities.

Available variables

Each extension point has its own set of variables you can use when scripting. Which are available, is subject to change.

That's why we offer the following service, which allows you to check which ones are currently available to include in your script: ParseScript.

    "Source": "extend GenerateUserDiscountCoupon",
"Dialect": "Extension",
"CompletionRequest": {
"Source": "extend GenerateUserDiscountCoupon"
},
"AvailableVariablesRequest": {
"Column": 0,
"Line": 1
}

The extension point mentioned here, GenerateUserDiscountCoupon, is just an example. You can insert any extension point to get an overview of what you can use in the script.

Supported statements

These statements are currently supported inside extension point scripts.

Set

Sets a variable to a value.

Syntax:

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

Syntax:

if <expression> then
<block>
else if <expression> then
<block>
else then
<block>
end

Table

Declares a lookup table which can make some logic easier to express.

Syntax:

table <name>
| <name1> | <name2> |
| <value1> | <value2> |

When using lookup expressions and a table statement, you'll first need to identify a table, so from <table> would be the statements starting point. It should then be followed by the desired filter condition, and end with a desired action thus, select <column> would be the statements ending point.

An example of a table statement was provided under the Extension Points section on this page. You will notice in that example that DefaultShippingCosts was the table name hence, the statement started with from DefaultShippingCosts - it was then followed by the condition, which was CountryID = NL and MinOrderAMount < 100 - and our desired action had to do with the tables Amount column thus, select Amount was the statements end.

For each loop

Syntax:

for each <name> in <expression> do
<block>
end

For example:

for each line in Order.Lines do

if line.IsStock then
set hasStockProducts to true
end
end

Any

Instead of loops, you can also use the Any function, which allows for a filter of sorts. This is best described by means of the following sample.


set CustomType to ''

if (Order.Lines.Any line -> line.OriginalOrder.SoldFromOrganizationUnit.Type = 'Shop') and Order.SoldFromOrganizationUnit.Type = 'Webshop' then
set CustomType to 'Online order, Store Return'
end

output CustomType

In this case line -> line.OriginalOrder.SoldFromOrganizationUnit.Type = 'Shop' is performed for each element in Order.Lines.

To make this even clearer, the second sample in the above table leads to the same result, but by means of a for each loop which is the longer route.

ToNumber

You can use the ToNumber statement to convert the value of a custom field (which might be string value) into a number.

Syntax:


set val to Order.Foo.ToNumber
if val > 100 then
output true
end
output false

Note that this only works if the value contains valid data. In other words: no letters for example.

ParseJson

The function ParseJson lets you convert a string into a JsonObject, which then has the following functions:

  • Object: access an object by the given name
  • String/Number/Boolean: access a string/number/bool on the object by name

set jsonString to '{ "foo" : 123 }'

set json to jsonString.ParseJson

set value to json.String('foo')

GetField

The function GetField is available in all scripts that include the use of custom fields.

This function allows you to call the custom field by means of a string value. At the same time, the use of string lets you use spaces in its value.


Order.CustomFields.GetField('New Black custom field')

Two ways of calling functions

EVA supports two ways of calling functions. The first version we'll describe here is the one EVA started out with; it's relatively easy and very readable, but only so long as the statements you're creating are relatively simple as well.

First version - simple statement
if Order.HasLedgerEntries with Type 'Exported' then
..
end

When the statements you're creating are a tad more complex, the syntax used in the first version actually makes things more complicated, which is why we'd recommend this second way of writing.

The following sample describes a way to convert a string to JSON in the first version's syntax, and shows how that becomes hard to navigate.

First version - more complex statement
set x to (str.ParseJson.Object with Name 'data').String with Name 'value'

The new version is more consistent with how most programming languages manage function calls.

Second version - same complex statement
set x to str.ParseJson.Object('data').String('value')

When using this syntax, it's not necessary to call the function by its name, but it's possible nonetheless. The following sample shows just that:

if Order.HasLedgerEntries(Type: 'Exported') then
end
Examples are shown in the first way

Since the second way of calling statements described here is relatively new, all of our samples included in the extension points documentation are still written using the first version's syntax. You however are free to use either.

Creating scripts

Scripts can be created and managed via Admin Suite or via CRUD services.