diff --git a/dsl-reference.md b/dsl-reference.md
index a6a9e9e3..5c71158e 100644
--- a/dsl-reference.md
+++ b/dsl-reference.md
@@ -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)
@@ -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
@@ -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.
*Required if `container`, `shell` and `workflow` have not been set.* |
| run.shell | [`shell`](#shell-process) | `no` | The definition of the shell command to run.
*Required if `container`, `script` and `workflow` have not been set.* |
| run.workflow | [`workflow`](#workflow-process) | `no` | The definition of the workflow to run.
*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.
*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.*
*Defaults to `true`.* |
+| await | `boolean` | `no` | Determines whether or not the process to run should be awaited for.
*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.*
*Defaults to `true`.* |
| return | `string` | `no` | Configures the output of the process.
*Supported values are:*
*- `stdout`: Outputs the content of the process **STDOUT**.*
*- `stderr`: Outputs the content of the process **STDERR**.*
*- `code`: Outputs the process's **exit code**.*
*- `all`: Outputs the **exit code**, the **STDOUT** content and the **STDERR** content, wrapped into a new [processResult](#process-result) object.*
*- `none`: Does not output anything.*
*Defaults to `stdout`.* |
##### Examples
@@ -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.
*If set to `true`, implements a do-while loop (post-test).*
*If set to `false`, implements a while loop (pre-test).*
*Defaults to `false`.* |
+| maxIterations | `integer` | `no` | The maximum number of iterations allowed to prevent infinite loops.
*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.
diff --git a/examples/while.yaml b/examples/while.yaml
new file mode 100644
index 00000000..6de9a253
--- /dev/null
+++ b/examples/while.yaml
@@ -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] }'
\ No newline at end of file
diff --git a/schema/workflow.yaml b/schema/workflow.yaml
index 5473f059..96dce2a8 100644
--- a/schema/workflow.yaml
+++ b/schema/workflow.yaml
@@ -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'
@@ -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'