Skip to content

OrderFlow Scripting Guide

Realtime Despatch Software Ltd

Document Version: 4.0.6

Document Built: 2020-10-12

This document and its content is copyright of Realtime Despatch Software Limited. All rights reserved.
You may not, except with our express written permission, distribute, publish or commercially exploit the content.
Any reproduction of part or all of the contents in any form is prohibited.

Groovy Scripting

Groovy Scripting

The main scripting language in use in OrderFlow is Groovy.

The advantages of using Groovy are as follows:

  • Groovy is a Java Virtual Machine (JVM) based language, and integrates seamlessly with the rest of OrderFlow, which is built using Java.
  • Groovy is both immensely powerful but also simple to learn.

Groovy Basics

There are plenty of online tutorials on Groovy. For OrderFlow scripting we tend to rely only on its most basic features, in areas such as variable assignment, logical expressions, conditional expressions, and looping.

Variable Assignment

Done using the def keywork:

def count = 0; //integer

def state = 'ready'; //string

def map = ['key1':'value1', 'key2':'value2'];
map['key3'] = 'value3';

def list = [1,2];
list.add(3);

Note the format used for map and list literals.

Note that map values can also be retrieved using 'dot' notation, as in the example below.

def map = ['key1':'value1', 'key2':'value2'];
map.key3 = 'value3';
print map.key3;

Note that there are restrictions in the names of keys that can be used in dot notation.

Consider the example:

def map = ['two word key':'value'];
print map['two word key'];

You cannot use as a substitute for the second line:

print map.two word key;

However, the example below with the single quotes will work:

print map.'two word key';  

Logical expression

Logical expressions are usually done using the if statement, followed by a conditional expression.

if (count > 10 || state == 'ready') {
    //do something
} else if (count <= 10 && state == 'ready') {
    //do another thing
} else {
    //do something else
}

The conditional expression can be a compound statement, with elements joined by && (and) or || (or), operators.

Note that it is also possible to use a switch statement for:

switch (input["storeId"]) {
    case "1": return "Default Store";
    case "2": return "US Store";
    case "3": return "European Store";
    default:  throw new IllegalArgumentException("Unrecognised Store Id");
}

In general, our recommendation is to use if statements if the number of outcomes is no more than three. The switch statement is more intuitive if there are a large number of potential outcomes.

Looping

There are different ways to do looping. The most commonly used looping construct is with the for keyword, as shown below:

for (line in shipment.orderLines) {
    println line.product.externalReference;
}

Return values

The value returned from a script execution is best controlled using a return statement, as below:

return 'somevalue';

Note that if no return statement is contained, then the script will return the value of the most recently evaluated expression. As it is not always very easy to identify what this will be, it is best practice to explicitly use return statement when relying on the value returned from a script.

Null Values

Groovy can access information from the OrderFlow data model using compound expressions such as:

shipment.site.externalReference

Note that if the site has not yet been set on the site, the code above will fail with a NullPointerException (NPE).

Groovy provides a very simple and useful way to write 'NPE safe' expressions, using the ? operator. In the case above, the following code will not fail with a NullPointerException, but will instead will return a null value.

shipment.site?.externalReference

Formatting and Parseing

Groovy has some useful formatting functionality which can be useful, for example, for formatting dates and numbers:

Date Formatting

Dates can be formatted using code such as the following:

def dateString = new java.text.SimpleDateFormat('yyyy-MM-dd hh:mm:ss').format(shipment.created);
println date;

The date format can obviously be set to the required value. There are many tutorials on the internet on how to use SimpleDateFormat for this purpose.

Date Parseing

The class java.text.SimpleDateFormat can also parse a date from a text string. This is useful for converting dates from a different format.

def date = new java.text.SimpleDateFormat('dd/MM/yy hh:mm:ss').parse('30/10/15 23:11:15');
def newDateString = new java.text.SimpleDateFormat('yyyy-MM-dd hh:mm:ss').format(date);
println newDateString;

Number Formatting

Groovy scripts can be used for number formatting in a number of ways. A simple example is shown below, which formats a decimal with two decimal character.

println String.format('%.2f', 1.345)

which will output the value 1.35.

Scripting Context

An important concept to understand with scripting is the scripting context.

Sometimes you may see scripts that variables that don't appear to have been 'defined'. An example is shown below:

if (input['key'] == 'testvalue') {
    ...
}

In the above snippet, there is no declaration of the input variable. Instead, the input variable has been added to the scripting context independently of the script itself.

Scripts in OrderFlow almost always rely on some scripting context to achieve a result. For example, for a script that need to operate on or extract data from a shipment, the shipment will invariably be present in the scripting context.

The details of the scripting context for the different types of scripts run in OrderFlow are covered in the different sections of this document.

Sandboxing

The Groovy that can be executed within OrderFlow environments is tightly controlled to avoid the risk of undesirable or risky code being added to scripts.

However, by default, the following applies:

  • methods or constructors cannot be called in any of the 'system' classes in the java.lang package, including System, ClassLoader, Thread, Runtime or Security.
  • objects can only be instantiated from the packages java.lang, java.util and java.text.

For certain scripting context, these limitations are relaxed slightly to allow for access to methods in specific packages to support functionality likely to be required in that scripting environment.