Skip to content

[WIP] feat: [#1096] While, Do-while loop implementation for fetching paginated data from an API #1097

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
64 changes: 63 additions & 1 deletion dsl-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
- [Switch](#switch)
- [Try](#try)
- [Wait](#wait)
- [While](#while)
+ [Flow Directive](#flow-directive)
+ [Lifecycle Events](#lifecycle-events)
- [Workflow Lifecycle Events](#workflow-lifecycle-events)
Expand Down Expand Up @@ -274,6 +275,7 @@ The Serverless Workflow DSL defines a list of [tasks](#task) that **must be** su
- [Set](#set), used to dynamically set the [workflow](#workflow)'s data during the its execution.
- [Try](#try), used to attempt executing a specified [task](#task), and to handle any resulting [errors](#error) gracefully, allowing the [workflow](#workflow) to continue without interruption.
- [Wait](#wait), used to pause or wait for a specified duration before proceeding to the next task.
- [While](#while), used to iterate over a collection of items based on a condition, supporting both pre-test (while) and post-test (do-while) loops.

#### Properties

Expand Down Expand Up @@ -781,7 +783,7 @@ Provides the capability to execute external [containers](#container-process), [s
| run.script | [`script`](#script-process) | `no` | The definition of the script to run.<br>*Required if `container`, `shell` and `workflow` have not been set.* |
| run.shell | [`shell`](#shell-process) | `no` | The definition of the shell command to run.<br>*Required if `container`, `script` and `workflow` have not been set.* |
| run.workflow | [`workflow`](#workflow-process) | `no` | The definition of the workflow to run.<br>*Required if `container`, `script` and `shell` have not been set.* |
| await | `boolean` | `no` | Determines whether or not the process to run should be awaited for.<br>*When set to `false`, the task cannot wait for the process to complete and thus cannot output the processs result. In this case, it should simply output its transformed input.*<br>*Defaults to `true`.* |
| await | `boolean` | `no` | Determines whether or not the process to run should be awaited for.<br>*When set to `false`, the task cannot wait for the process to complete and thus cannot output the process's result. In this case, it should simply output its transformed input.*<br>*Defaults to `true`.* |
| return | `string` | `no` | Configures the output of the process.<br>*Supported values are:*<br>*- `stdout`: Outputs the content of the process **STDOUT**.*<br>*- `stderr`: Outputs the content of the process **STDERR**.*<br>*- `code`: Outputs the process's **exit code**.*<br>*- `all`: Outputs the **exit code**, the **STDOUT** content and the **STDERR** content, wrapped into a new [processResult](#process-result) object.*<br>*- `none`: Does not output anything.*<br>*Defaults to `stdout`.* |

##### Examples
Expand Down Expand Up @@ -1148,6 +1150,66 @@ do:
seconds: 10
```

#### While

Enables iterative execution of tasks based on a condition, supporting both pre-test (while) and post-test (do-while) loops. This task type is essential for scenarios requiring repetitive execution until a specific condition is met.

##### Properties

| Name | Type | Required | Description|
|:--|:---:|:---:|:---|
| while | `string` | `yes` | A [runtime expression](dsl.md#runtime-expressions) that represents the condition that must be met for the iteration to continue. |
| postConditionCheck | `boolean` | `no` | Determines whether the condition is evaluated after (do-while) or before (while) executing the task.<br>*If set to `true`, implements a do-while loop (post-test).*<br>*If set to `false`, implements a while loop (pre-test).*<br>*Defaults to `false`.* |
| maxIterations | `integer` | `no` | The maximum number of iterations allowed to prevent infinite loops.<br>*If not set, implementation-specific default limits apply.* |
| do | [`map[string, task]`](#task) | `yes` | The [task(s)](#task) to perform while the condition is true. |

##### Examples

```yaml
document:
dsl: '1.0.0'
namespace: test
name: while-example
version: '0.1.0'
do:
# While loop example (pre-test)
- fetchPaginatedData:
while: .hasNextPage == true
maxIterations: 100
do:
- fetchPage:
call: getPageData
input:
pageToken: .nextPageToken
output: .fetchedData
- accumulateData:
run: mergeResults
input:
newData: .fetchedData
existingData: .accumulatedResults
output: .accumulatedResults

# Do-while loop example (post-test)
- retryOperation:
while: .retryNeeded == true
postConditionCheck: true
maxIterations: 3
do:
- attemptOperation:
call: performOperation
output:
as: |
if .status == "failed" then
{ retryNeeded: true }
else
{ retryNeeded: false }
end
```

In the examples above:
- The first task demonstrates a while loop that fetches paginated data until there are no more pages, with a maximum of 100 iterations.
- The second task shows a do-while loop that retries an operation up to 3 times, evaluating the retry condition after each attempt.

### Flow Directive

Flow Directives are commands within a workflow that dictate its progression.
Expand Down
47 changes: 47 additions & 0 deletions examples/while.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
document:
dsl: '1.0.0'
namespace: test
name: while-examples
version: '0.1.0'

do:
# Example of a while loop (condition evaluated before execution)
- fetchPaginatedData:
while: .hasNextPage == true
maxIterations: 100
do:
- fetchPage:
call: getPageData
input:
pageToken: .nextPageToken
output: .fetchedData
- accumulateData:
run: mergeResults
input:
newData: .fetchedData
existingData: .accumulatedResults
output: .accumulatedResults

# Example of a do-while loop (condition evaluated after execution)
- retryOperation:
while: .retryCount < 3 && .success == false
postConditionCheck: true
do:
- attempt:
call: someOperation
output:
as: .success
- updateRetry:
set:
retryCount: '${ .retryCount + 1 }'

# Example using iteration counter
- countToMax:
while: .counter < .maxValue
at: iteration
maxIterations: 10
do:
- increment:
set:
counter: '${ .counter + 1 }'
iterations: '${ .iterations + [.iteration] }'
32 changes: 32 additions & 0 deletions schema/workflow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ $defs:
- $ref: '#/$defs/forkTask'
- $ref: '#/$defs/emitTask'
- $ref: '#/$defs/forTask'
- $ref: '#/$defs/whileTask'
- $ref: '#/$defs/listenTask'
- $ref: '#/$defs/raiseTask'
- $ref: '#/$defs/runTask'
Expand Down Expand Up @@ -544,6 +545,37 @@ $defs:
do:
$ref: '#/$defs/taskList'
title: ForTaskDo
whileTask:
type: object
$ref: '#/$defs/taskBase'
title: WhileTask
description: A task that enables conditional looping with support for both while and do-while behavior.
required: [ while, do ]
unevaluatedProperties: false
properties:
while:
type: string
title: WhileCondition
description: A runtime expression that represents the condition that must be met for the iteration to continue.
postConditionCheck:
type: boolean
title: PostConditionCheck
description: Controls whether the condition is evaluated after (do-while) or before (while) executing the tasks. When true, enables do-while behavior.
default: false
maxIterations:
type: integer
title: MaxIterations
description: The maximum number of iterations to perform, if any.
minimum: 1
at:
type: string
title: WhileAt
description: The name of the variable used to store the index of the current iteration.
default: index
do:
$ref: '#/$defs/taskList'
title: WhileTaskDo
description: The tasks to execute in each iteration.
listenTask:
type: object
$ref: '#/$defs/taskBase'
Expand Down
Loading