Language Guide

The redmineBOOST Language v1.0 (RBO) is specially designed to describe Redmine tasks as processes. RBO is highly declarative in a way that each task can be described as an item. An item has properties which can be accessed and if they are not readonly can be modified. To get the necessary data from Redmine there are queries which can be stored into fields or directly passed into properties.

The main design goals of the language are readability, error reduction and intuitive handling of compactness.

Notation

The documentation will use the following notations:

This is an important statement.

Important attributes are shown in bold or italics.

The syntax is partially explained by a kind of Backus–Naur Form (BNF) with the following meanings:

(...): A group of elements.

(...)?: An optional group of elements.

*: Repeat from 0 to n times.

+: Repeat from 1 to n times.

<abc>: A placeholder with the name abc.

Item Instantiations

Item Instantiations are the core element of a process description. They represent the tasks which should be executed. They are executed in declaration order (top-down).

The format of an item instantiation is defined as follows:

<predefinedTypeSpecifier> { (<propertyDefinition> | <itemInstantiation> | <fieldDeclaration>)* }

The Item scope <predefinedTypeSpecifier> {...} can contain multiple Property Definitions, Item Instantiations and Local Field Declarations.

The instantiable Items can be defined as follows:

let issue1: Issue = Issue {
    project: GetProject(identifier="project1")
    subject: "Example Subject"
    tracker: GetTracker(name="tracker1")

    Issue {
        project: GetProject(identifier="project1")
        subject: "Example Subject"
        tracker: GetTracker(name="tracker1")
    }
}

In the example the Issue with the identifier issue1 was defined with all required properties (project, subject and tracker). Furthermore it contains a top-level nested Issue. This nesting automatically results in setting the parent property of the nested Issue to issue1. As a result Redmine would create an Issue containing a Child-Issue.

Top-level Item Instantiations result in structural child items, otherwise the items are treated as independent.

Example:

let issue1: Issue = Issue {
    project: GetProject(identifier="project1")
    subject: "Example Subject"
    tracker: GetTracker(name="tracker1")

    let issue2Subject: Issue = Issue {
        project: GetProject(identifier="project1")
        subject: "Example Subject"
        tracker: GetTracker(name="tracker1")
    }.subject
}

In this example, the nested issue is treated as a standalone issue because its subject property is accessed immediately. Therefore, it is not a child Issue of issue1.

Property Definitions

Property Definitions set specific attributes of the containing Item Instantiation.

Each property is beginning with a lowercase letter.

The format of a property definition is defined as follows:

<identifier>: <expression>? (Newline | ;)

identifier: Identifier used to reference the property in the Item.

expression: The property assignment expression can be assigned with Literals, Enumerations constants, Items and Query invocation results.

Newline | ;: Each property definition must end with a line break or a semicolon.

Local Field Declarations

Fields can be assigned with Literals, Enumerations constants, Items and Query invocation results.

A local field declaration is initialized immediately when the containing Item is declared.

Fields have the type specified so the assigning type has to match the defined field type.

Field identifiers must begin with a lowercase letter.

The let keyword changes the visibility of the field for the area that contains it, while the global keyword sets the visibility of the field to the global area. Therefore it is accessible from everywhere.

Fields are declared as follows:

(let | global) <identifier>: <typeSpecifier> = <expression> (Newline | ;)

identifier: Identifier used to reference the field.

Identifiers must begin with a-z, A-Z or _ followed by 0-9, a-z, A-Z or _.

typeSpecifier: The data type of the field.

expression: The field assignment expression can be assigned with Literals, Enumerations constants, Items, Item Instantiations and Query invocation results.

Newline | ;: Each field declaration must end with a line break or a semicolon.

Example:

Issue {
    let subjectText: string = "Example Subject"

    project: GetProject(identifier="project1")
    subject: subjectText
    tracker: GetTracker(name="tracker1")
}

In the example the field with the name subjectText has the type string and is assigned to the string literal "Example Subject".

Global Field Declarations

Global Fields behave exactly as local field declarations except that they are defined at the global scope and are not contained in an Item Instantiation.

A global field declaration is initialized immediately.

Example:

let currentUser: User = GetCurrentUser()

In the example the field with the name currentUser has the type User and is assigned to the result of the query invocation of GetCurrentUser(). The keyword global is unnecessary in this case, because global fields are already defined in the global scope. Therefore, they are already accessible from anywhere.

Scopes

In general a Scope is a concept that refers to where values of properties and fields can be accessed.

There are basically two scope types:

  • Global scope (a value in the global scope can be used anywhere in the entire process)
  • Item block scope (only visible within a { ... } codeblock)

Each property and field is visible in all inner scopes. When the user refers to a property, the property is searched from the referenced location upwards in all scopes and the first property matching the name is chosen.

Example:

let subjectText: string = "Example Subject"
Issue {
    project: GetProject(identifier="project1")
    subject: subjectText
    tracker: GetTracker(name="tracker1")

    Note {
        // Reference the subject of the issue
        text: subject
    }
}

In the example the Issue was defined with all required properties (project, subject and tracker). Furthermore it contains a Sub-Item Note which references the subject of issue1 and assigns it to the text property of the Note. The scope of the Note has no other property or field named subject, so it is searched for in the parent scope and found there. The subject of the issue is set to the field subjectText located in the global scope. As a result Redmine would create an Issue with a Note which contains the subject of the Issue as text.

Queries

Queries are basically getters for Redmine data. Each time someone wants to access access an existing element of Redmine they have to use a Query.

The parameters of a query must always be named explicitly.

All properties of the object returned by the query are readonly. To change data of the item returned by the query, one should use the special update item.

Queries are defined as follows:

<identifier> ( (<argumentIdentifier>=<expression>)? (,<argumentIdentifier>=<expression>)* )

identifier: Identifier of the query.

argumentIdentifier: Identifier of the query argument.

expression: The query argument expression can be assigned with Literals, Enumerations constants, Items and Query invocation results.

Queriess can be used as follows:

GetUser(id=1)
GetUser(login="mmustermann")

In the example the query GetUser is called one time with the parameter id set to 1 and another time with the parameter login set to mmustermann. Each Query in the example returns a User. If the User is not found or another error occurred a diagnostic will be shown.

Item Tracking

redmineBOOST automatically tracks Redmine Items. This means that Redmine Items which are referenced inside properties or fields are automatically removed when they are deleted.

The behaviour is type specific:

  • When the type is an array the deleted item will just be removed from the array.
  • When the type is no array but can be undefined it will be set to undefined.
  • In all other cases an access is forbidden.

String Interpolations

RBO supports basic string interpolation in Single-Line and Multi-Line Strings. As default each string literal is an interpolated string that may contain interpolation expressions. When an interpolated string is resolved to a result string, elements containing interpolation expressions are replaced by the string representations of the expression results. A String Interpolation is defined as follows:

"Date: ${GetCurrentDateTime()}"

In the example the query invocation result of GetCurrentDateTime() is resolved to a result string which is replacing the interpolation expression.

To escape the $ sign you can prefix it with the `.

Array Concatanations

RBO supports basic Array concatanations with the + sign.

The additive expression is defined as follows:

let array1: [int32] = [1, 2] + [3, 4]
let array2: [int32] = [1, 2] + 3

In the example the field array1 is initialized with two arrays ([1, 2], [3, 4]). Therefore array1 contains in the end the values [1, 2, 3, 4]. The second field array2 is however assigned with one array [1, 2] and a single value 3. Therefore the field array2 contains in the end the values [1, 2, 3].

Types

Array

An array is a list of elements of the type T. An array looks like this: [T].

T can be any Type.

Enum

An enumeration is a reusable type that has a name and contains a set of uniquely named constants.

The user can not declare own enums.

Internally, enumerations are treated like Primitive Types. They represent the smallest possible type able to represent all defined Literal constants.

Primitive Types

RBO supports basic data types. The supported primitive types are:

Type Range
bool false / true
date 00:00:00.0000000 UTC, 1. Januar 0001 .. 23:59:59,9999999 UTC, 31. Dezember 9999
float32 -3.40282347E+38 .. 3.40282347E+38
int32 -2147483648 .. 2147483647

Item

Items are the core element of redmineBOOST.

Not all Items are instantiable. The reason is that some can not be created through the provided REST API of Redmine or they are currently not supported.

Some Items can contain Sub-Items and therefore some can only be defined inside of specific Items.

It is forbidden to access a deleted Item. If a deleted Item was in an array, it is removed automatically from the array on property access. Nonetheless, a property which can be set to undefined will be undefined if the referenced item was deleted.

Example:

User {
    login: "mmustermann"
    firstname: "Max"
    lastname: "Mustermann"
    mail: "max.mustermann@example.com"
    status: UserStatus.Locked

    Authentication {
        password: "a1b2c3d4"
    }
}

Generic Item

redmineBOOST supports generic items. A generic item can use its type parameter T as the type of its return value or as the type of one of its properties.

Example:

Choice<string> {
    description: "Matrix"
    options: ["Blue Pill", "Red Pill"]
}

In the example the generic parameter was explicitely set to string. This sets the readonly property value and the element type of options to string. The property options is now of the type [string].

Union

A union type describes a value that can be one of several types. We use the vertical bar (|) to separate each type, so string | undefined is the type of a value that can be a string or undefined.

String

The String type represents text as a sequence of UTF-16 code units.

A string is a sequential collection of characters that’s used to represent text.

The string type can contain Multi-Line aswell as Single-Line text.

Because of string interpolations the $ sign has to be escaped by prefixing it with `.

Restricted

The redmineBOOST Language has two special types, restricted and undefined, that have the values restricted and undefined respectively. The Restricted type can only be the value restricted and occures if the user of the API key does not have the permission to access some properties.

Undefined

The Undefined type can only be the value undefined and occures if the property is of type Union<undefined|...> and does not exist. The result is an undefined value.

Literals

RBO supports the following literal notations by example for each Primitive Type:

Array Literal

An array is a list of elements of the type T.

T can be an Item Type or a Primitive Type.

[T]

Boolean Literal

The Boolean Literal can only have the values true or false.

false
true

Date Literal

The Date literal is always as follows: (d|D)<Four-digit-year>-<two-digit-month>-<two-digit-day>.

d2022-12-31
D2022-12-31

Float32 Literal

4.2
-2.1
+1.05

Int32 Literal

4
-2
+5

Restricted Literal

Item Properties can have the value restricted if the user of the API key does not have the permission to access them.

restricted

String Literal

There are two ways in declaring a string, Single-Line and Multi-Line. A Single-Line String always starts and ends with " and has to be declared in one line. A Multi-Line String however can be declared in multiple lines and always starts with |". Furthermore, Multi-Line Strings will always append a linebreak.

Both string declarations allow String Interpolations.

Because of string interpolations the $ sign has to be escaped by prefixing it with `.

// Single-Line String
"Hello, RBO!"

// Multi-Line String
|"Hello,
|"RBO!

Undefined Literal

Union types which can contain the undefined type can be set to the undefined value.

undefined

Comments

RBO supports Multi-line and Single-line comments. The Comments are ignored when executing the process description.

Comments can be used to explain specific design decisions.

A Multi-line comment is defined as follows:

/*
This is a
multi-line comment.
*/

A Single-line comment is defined as follows:

// This is a single-line comment.