Skip to content

New example - filling up a glass of water #279

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 120 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Provides Serverless Workflow language examples
- [Accumulate room readings and create timely reports (ExecTimeout and KeepActive)](#Accumulate-room-readings)
- [Car vitals checks (SubFlow state Repeat)](#Car-Vitals-Checks)
- [Book Lending Workflow](#Book-Lending)
- [Filling a glass of water (Expression functions)](#Filling-a-glass-of-water)

### Hello World Example

Expand Down Expand Up @@ -3753,3 +3754,122 @@ events: file://books/lending/events.json
</td>
</tr>
</table>

### Filling a glass of water

#### Description

In this example we showcase the power of [expression functions](../specification.md#Using-Functions-For-Expression-Evaluation).
Our workflow definition is assumed to have the following data input:

```json
{
"counts": {
"current": 0,
"max": 10
}
}
```

Our workflow simulates filling up a glass of water one "count" at a time until "max" count is reached which
represents our glass is full.
Each time we increment the current count, the workflow checks if we need to keep refilling the glass.
If the current count reaches the max count, the workflow execution ends.
To increment the current count, the workflow invokes the "IncrementCurrent" expression function.
Its results are then merged back into the state data according to the "toStateData" property of the event data filter.

#### Workflow Diagram

<p align="center">
<img src="../media/examples/examples-fill-glass.png" height="300px" alt="Fill Glass of Water Example"/>
</p>

#### Workflow Definition

<table>
<tr>
<th>JSON</th>
<th>YAML</th>
</tr>
<tr>
<td valign="top">

```json
{
"id": "fillgrassofwater",
"name": "Fill glass of water workflow",
"start": "Check if full",
"functions": [
{
"name": "Increment Current Count Function",
"type": "expression",
"operation": ".counts.current += 1 | .counts.current"
}
],
"states": [
{
"name": "Check if full",
"type": "switch",
"dataConditions": [
{
"name": "Need to fill more",
"condition": "${ .counts.current < .counts.max }",
"transition": "Add Water"
},
{
"name": "Glass full",
"condition": ".counts.current >= .counts.max",
"end": true
}
]
},
{
"name": "Add Water",
"type": "operation",
"actions": [
{
"functionRef": "Increment Current Count Function",
"actionDataFilter": {
"toStateData": ".counts.current"
}
}
],
"transition": "Check If Full"
}
]
}
```

</td>
<td valign="top">

```yaml
id: fillgrassofwater
name: Fill glass of water workflow
start: Check if full
functions:
- name: Increment Current Count Function
type: expression
operation: ".counts.current += 1 | .counts.current"
states:
- name: Check if full
type: switch
dataConditions:
- name: Need to fill more
condition: "${ .counts.current < .counts.max }"
transition: Add Water
- name: Glass full
condition: ".counts.current >= .counts.max"
end: true
- name: Add Water
type: operation
actions:
- functionRef: Increment Current Count Function
actionDataFilter:
toStateData: ".counts.current"
transition: Check If Full
```

</td>
</tr>
</table>
Binary file added media/examples/examples-fill-glass.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
66 changes: 60 additions & 6 deletions specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -1081,10 +1081,9 @@ In addition to defining RESTful and RPC services and their operations, workflow
can also be used to define expressions that should be evaluated during workflow execution.

Defining expressions as part of function definitions has the benefit of being able to reference
them by their logical name through workflow states where expression evaluation is required, thus making them
reusable definitions.
them by their logical name through workflow states where expression evaluation is required.

Expression expression functions must declare their `type` parameter to be `expression`.
Expression functions must declare their `type` parameter to be `expression`.

Let's take at an example of such definitions:

Expand All @@ -1106,9 +1105,9 @@ Let's take at an example of such definitions:
```

Here we define two reusable expression functions. Expressions in Serverless Workflow
are evaluated against the workflow data. Note that different data filters play a big role as to which parts of the
workflow data are selected. Reference the
[State Data Filtering](#State-data-filters) section for more information on this.
can be evaluated against the workflow, or workflow state data. Note that different data filters play a big role as to which parts of the
workflow data are being evaluated by the expressions. Reference the
[State Data Filtering](#State-Data-Filtering) section for more information on this.

Our expression function definitions can now be referenced by workflow states when they need to be evaluated. For example:

Expand Down Expand Up @@ -1138,6 +1137,61 @@ Our expression function definitions can now be referenced by workflow states whe
}
```

Our expression functions can also be referenced and executed as part of state [action](#Action-Definition) execution.
Let's say we have the following workflow definition:

```json
{
"name": "simpleadd",
"functions": [
{
"name": "Increment Count Function",
"type": "expression",
"operation": ".count += 1 | .count"
}
],
"start": "Initialize Count",
"states": [
{
"name": "Initialize Count",
"type": "inject",
"data": {
"count": 0
},
"transition": "Increment Count"
},
{
"name": "Increment Count",
"type": "operation",
"actions": [
{
"functionRef": "Increment Count Function",
"actionFilter": {
"toStateData": "${ .count }"
}
}
],
"end": true
}
]
}
```

The starting [inject state](#Inject-State) "Initialize Count" injects the count element into our state data,
which then becomes the state data input of our "Increment Count" [operation state](#Operation-State).
This state defines an invocation of the "Increment Count Function" expression function defined in our workflow definition.

This triggers the evaluation of the defined expression. The input of this expression is by default the current state data.
Just like with "rest", and "rpc" type functions, expression functions also produce a result. In this case
the result of the expression is just the number 1.
The actions filter then assigns this result to the state data element "count" and the state data becomes:

``` json
{
"count": 1
}
```

Note that the used function definition type in this case must be `expression`.

For more information about functions, reference the [Functions definitions](#Function-Definition) section.
Expand Down