Multi-line rule executor
A need often exists to build multi-line rules to compute a wanted value for an attribute. IBM® Verify offers the ability to configure both simple single-line and more advanced multi-line rules.
For single-line syntax rules, see Attribute functions.
Elements of a multi-line rule
The multi-line rule is written in YAML format. It is constrained by the formatting needs of a YAML document. The YAML parser that is used supports most of YAML 1.1 and 1.2.
- Statement
- A statement is the building block of the rule and can be of different types. For example, the return statement computes the expression that was provided and returns the expression as the rule value.
- Block
- A block is a bounded collection of statements. The rule starts with a top-level block called
statements. Blocks can exist within certain statement types. For example, theif.blockis the collection of statements to be executed when the correspondingif.matchstatement evaluates to true. - Block context
- A block context is a collection of variables that are available within that block. Subblocks can access the context of the parent blocks. Parent blocks cannot access the context of the subblocks.
- Expression
- An expression is a snippet that uses the same syntax as single-lined expressions. See Attribute functions.
Supported statement types
- Context
- Return
- If
Context statement
context statement is used to initialize and assign values to named variables.
It supports two operators.:=- This operator is used to initialize a new variable within the context block where the statement
is evaluated. For example, if a variable is initialized within an
if.block, it is not available outside this block in the subsequent statements.
=- This operator is used to assign a value to an existing variable. The variable can exist in the current context block or a parent block.
context: {{varname}} := {{expression}}. Use is
context.{{varname}} to access {{varname}} in subsequent statements
in the same block or subblocks.Return statement
The return statement is used to indicate to the rule engine to end the execution and to immediately return the value that was computed.
The format is return: {{expression}}.
If statement
The if statement is used to build conditional blocks. It consists of several
properties, unlike the previous two statements.
| Name | Description | Format |
|---|---|---|
match |
If this condition evaluates to true, the corresponding block is
executed. |
match: 1 == 2 |
block |
Bounded block of statements that must be executed if match evaluates to
true. |
A standard YAML array of statements |
elsifs |
Array of else-if conditions that are evaluated if match fails to evaluate to
true. |
YAML arrays that are nested if statements |
else |
Block of statements that are executed if match and all
elseifs.match fail to evaluate to true. |
A YAML array of statements |
Trace logging
Trace logging can be added to a multi-line rule to send debug logs. The trace logging properties
are executed if a rule is executed with enabled trace mode. Trace logging is supported through the
debug and debugx properties.
debugstatement-
The
debugstatement is used to indicate to execute the expression that was provided and log the value that was returned. The expression should always evaluate to a string.Format -
debug: {{expression}}
debugxblock-
The
debugxblock consists of two properties:logandfields.Name Description Format log The expression provided is evaluated and the value returned is logged. The expression should always evaluate to a string. log: {{expression}}fields Custom metadata fields that are sent with the log. Provided as map of key-value pairs. The value of each pair is an expression, which must evaluate to a string. {{field_key}}: {{expression}}Refer to the section for a sample function that illustrates trace logging.
Snippets
This list of snippets is not exhaustive and does not cover all possible uses of rules. However, this list provides some samples that can be used as is or can be extended.
Get the manager's display name
statements:
- context: manager := user.getManager()
- context: userExists := has(context.manager.name)
- context: >
givenNameExists := context.userExists
&& has(context.manager.name.givenName)
&& context.manager.name.givenName != ""
- context: familyNameExists := context.userExists && has(context.manager.name.familyName) && context.manager.name.familyName != ""
- context: formattedExists := context.userExists && has(context.manager.name.formatted) && context.manager.name.formatted != ""
- if:
match: context.formattedExists
block:
- return: context.manager.name.formatted
elseifs:
- match: context.givenNameExists
block:
- if:
match: context.familyNameExists
block:
- context: managerName := context.manager.name.familyName + ", " + context.manager.name.givenName
- return: context.managerName
else:
- return: context.manager.name.givenName
- match: context.familyNameExists
block:
- return: context.manager.name.familyName
- return: string("Not Available")
Transform the email domain
statements:
- context: "workEmails := has(user.emails) ? user.emails.filter(e, e.type == 'work') : []"
- context: "workEmail := size(context.workEmails) > 0 ? context.workEmails[0].value : ''"
- if:
match: context.workEmail != ""
block:
- context: cn := context.workEmail.substring(0, context.workEmail.lastIndexOf('@'))
- if:
match: context.cn != ""
block:
- return: context.cn + "@github.com"
- return: ""
Scoping of variables
statements:
- context: ret := 3 + 4
- if:
match: 2 > 1block:
- context: ret := 5
- if:
match: 3 > 2block:
- context: ret = 0
- return: context.ret
The result is 7. This result is because line 6 reinitializes ret within the
if.block. The subsequent nested statements see this variable. However, after the
rule engine exits the if.block, it can access the variable that was initialized at
line 2.
Trace logging example
This example illustrates how to use trace logging statements.
statements:
- context: uid := user.id
- context: userEmail := "user@test.com"
- debug: '"This is an example of a trace log at time " + string(now)'
- debugx:
log: '"The user id obtained: " + context.uid'
fields:
flow: '"login"'
time: 'string(now)'
userEmail: context.userEmail
- return: context.uid + ", " + context.userEmail
For the debug statement, the expression provided will be evaluated and logged as
"This is an example of a trace log at time <timestamp>".
debugx block, the expression provided will be evaluated and logged as
"The user id obtained: <user id>". The following custom metadata fields will be
sent along with the log.
| Key | Value |
|---|---|
"flow" |
"login" |
"time" |
"<timestamp>" |
"userEmail" |
"user@test.com" |
Important restrictions
- The variables that are initialized by the
contextframe can live only within that frame or subframes within it. In the previous rule example, the variablemanagerNameis not accessible outside theifframe that it was initialized in. elseifsandelseframes can be used only if anifframe is on the same level.- All the
ifandelseifsframe must have amatchexpression that always returns a Boolean value. - If the rule evaluation flow does not encounter a
returnstatement, a null value is returned. No errors are thrown nor are syntax check performed to make sure that all possible flows have areturn. The author of the rule must ensure that all flows of the rule return an acceptable value.