chore: import upstream snapshot with attribution
CI / unit-test (push) Has been cancelled
CI / detect-changes (push) Has been cancelled
CI / build (push) Has been cancelled
Publish docs via GitHub Pages / Deploy docs (push) Has been cancelled
CI / test-harness (push) Has been cancelled
CI / generate-e2e-matrix (push) Has been cancelled
CI / e2e (push) Has been cancelled
CI / build-ui (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
UI v2 Integration CI / E2E (Integration) (push) Has been cancelled
UI v2 CI / Lint, Format & Test (push) Has been cancelled
UI v2 CI / E2E (Mocked) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:37:56 +08:00
commit a9cd7750f4
3727 changed files with 627706 additions and 0 deletions
+177
View File
@@ -0,0 +1,177 @@
---
description: "Event Handlers Lab — hands-on tutorial for publishing events and triggering Conductor workflows with event handlers."
---
# Events and Event Handlers
In this exercise, we shall:
* Publish an Event to Conductor using `Event` task.
* Subscribe to Events, and perform actions:
* Start a Workflow
* Complete Task
Conductor supports eventing with two Interfaces:
* [Event Task](../../documentation/configuration/workflowdef/systemtasks/event-task.md)
* [Event Handlers](../../documentation/configuration/eventhandlers.md)
## Create Workflow Definitions
Let's create two workflows:
* `test_workflow_for_eventHandler` which will have an `Event` task to start another workflow, and a `WAIT` System task that will be completed by an event.
* `test_workflow_startedBy_eventHandler` which will have an `Event` task to generate an event to complete `WAIT` task in the above workflow.
Send `POST` requests to `/metadata/workflow` endpoint with below payloads:
```json
{
"name": "test_workflow_for_eventHandler",
"description": "A test workflow to start another workflow with EventHandler",
"version": 1,
"tasks": [
{
"name": "test_start_workflow_event",
"taskReferenceName": "start_workflow_with_event",
"type": "EVENT",
"sink": "conductor"
},
{
"name": "test_task_tobe_completed_by_eventHandler",
"taskReferenceName": "test_task_tobe_completed_by_eventHandler",
"type": "WAIT"
}
]
}
```
```json
{
"name": "test_workflow_startedBy_eventHandler",
"description": "A test workflow which is started by EventHandler, and then goes on to complete task in another workflow.",
"version": 1,
"tasks": [
{
"name": "test_complete_task_event",
"taskReferenceName": "complete_task_with_event",
"inputParameters": {
"sourceWorkflowId": "${workflow.input.sourceWorkflowId}"
},
"type": "EVENT",
"sink": "conductor"
}
]
}
```
### Event Tasks in Workflow
`EVENT` task is a System task, and we shall define it just like other Tasks in Workflow, with `sink` parameter. Also, `EVENT` task doesn't have to be registered before using in Workflow. This is also true for the `WAIT` task.
Hence, we will not be registering any tasks for these workflows.
### Events are sent, but they're not handled (yet)
Once you try to start `test_workflow_for_eventHandler` workflow, you would notice that the event is sent successfully, but the second worflow `test_workflow_startedBy_eventHandler` is not started. We have sent the Events, but we also need to define `Event Handlers` for Conductor to take any `actions` based on the Event. Let's create `Event Handlers`.
## Create Event Handlers
Event Handler definitions are pretty much like Task or Workflow definitions. We start by name:
```json
{
"name": "test_start_workflow"
}
```
Event Handler should know the Queue it has to listen to. This should be defined in `event` parameter.
When using Conductor queues, define `event` with format:
```conductor:{workflow_name}:{taskReferenceName}```
And when using SQS, define with format:
```sqs:{my_sqs_queue_name}```
```json
{
"name": "test_start_workflow",
"event": "conductor:test_workflow_for_eventHandler:start_workflow_with_event"
}
```
Event Handler can perform a list of actions defined in `actions` array parameter, for this particular `event` queue.
```json
{
"name": "test_start_workflow",
"event": "conductor:test_workflow_for_eventHandler:start_workflow_with_event",
"actions": [
"<insert-actions-here>"
],
"active": true
}
```
Let's define `start_workflow` action. We shall pass the name of workflow we would like to start. The `start_workflow` parameter can use any of the values from the general [Start Workflow Request](../../documentation/api/startworkflow.md). Here we are passing in the workflowId, so that the Complete Task Event Handler can use it.
```json
{
"action": "start_workflow",
"start_workflow": {
"name": "test_workflow_startedBy_eventHandler",
"input": {
"sourceWorkflowId": "${workflowInstanceId}"
}
}
}
```
Send a `POST` request to `/event` endpoint:
```json
{
"name": "test_start_workflow",
"event": "conductor:test_workflow_for_eventHandler:start_workflow_with_event",
"actions": [
{
"action": "start_workflow",
"start_workflow": {
"name": "test_workflow_startedBy_eventHandler",
"input": {
"sourceWorkflowId": "${workflowInstanceId}"
}
}
}
],
"active": true
}
```
Similarly, create another Event Handler to complete task.
```json
{
"name": "test_complete_task_event",
"event": "conductor:test_workflow_startedBy_eventHandler:complete_task_with_event",
"actions": [
{
"action": "complete_task",
"complete_task": {
"workflowId": "${sourceWorkflowId}",
"taskRefName": "test_task_tobe_completed_by_eventHandler"
}
}
],
"active": true
}
```
## Summary
After wiring all of the above, starting the `test_workflow_for_eventHandler` should:
1. Start `test_workflow_startedBy_eventHandler` workflow.
2. Sets `test_task_tobe_completed_by_eventHandler` WAIT task `IN_PROGRESS`.
3. `test_workflow_startedBy_eventHandler` event task would publish an Event to complete the WAIT task above.
4. Both the workflows would move to `COMPLETED` state.
+151
View File
@@ -0,0 +1,151 @@
---
description: "First Workflow Lab — step-by-step tutorial to create and run your first Conductor workflow using built-in HTTP tasks."
---
# A First Workflow
In this article we will explore how we can run a really simple workflow that runs without deploying any new microservice.
Conductor can orchestrate HTTP services out of the box without implementing any code. We will use that to create and run the first workflow.
See [System Task](../../documentation/configuration/workflowdef/systemtasks/index.md) for the list of such built-in tasks.
Using system tasks is a great way to run a lot of our code in production.
## Configuring our First Workflow
This is a sample workflow that we can leverage for our test.
```json
{
"name": "first_sample_workflow",
"description": "First Sample Workflow",
"version": 1,
"tasks": [
{
"name": "get_population_data",
"taskReferenceName": "get_population_data",
"inputParameters": {
"http_request": {
"uri": "https://datausa.io/api/data?drilldowns=Nation&measures=Population",
"method": "GET"
}
},
"type": "HTTP"
}
],
"inputParameters": [],
"outputParameters": {
"data": "${get_population_data.output.response.body.data}",
"source": "${get_population_data.output.response.body.source}"
},
"schemaVersion": 2,
"restartable": true,
"workflowStatusListenerEnabled": false,
"ownerEmail": "example@email.com",
"timeoutPolicy": "ALERT_ONLY",
"timeoutSeconds": 0
}
```
This is an example workflow that queries a publicly available JSON API to retrieve some data. This workflow doesnt
require any worker implementation as the tasks in this workflow are managed by the system itself. This is an awesome
feature of Conductor. For a lot of typical work, we wont have to write any code at all.
Let's talk about this workflow a little more so that we can gain some context.
```json
"name" : "first_sample_workflow"
```
This line here is how we name our workflow. In this case our workflow name is `first_sample_workflow`
This workflow contains just one worker. The workers are defined under the key `tasks`. Here is the worker definition
with the most important values:
```json
{
"name": "get_population_data",
"taskReferenceName": "get_population_data",
"inputParameters": {
"http_request": {
"uri": "https://datausa.io/api/data?drilldowns=Nation&measures=Population",
"method": "GET"
}
},
"type": "HTTP"
}
```
Here is a list of fields and what it does:
1. `"name"` : Name of our worker
2. `"taskReferenceName"` : This is a reference to this worker in this specific workflow implementation. We can have multiple
workers of the same name in our workflow, but we will need a unique task reference name for each of them. Task
reference name should be unique across our entire workflow.
3. `"inputParameters"` : These are the inputs into our worker. We can hard code inputs as we have done here. We can
also provide dynamic inputs such as from the workflow input or based on the output of another worker. We can find
examples of this in our documentation.
4. `"type"` : This is what defines what the type of worker is. In our example - this is `HTTP`. There are more task
types which we can find in the Conductor documentation.
5. `"http_request"` : This is an input that is required for tasks of type `HTTP`. In our example we have provided a well
known internet JSON API url and the type of HTTP method to invoke - `GET`
We haven't talked about the other fields that we can use in our definitions as these are either just
metadata or more advanced concepts which we can learn more in the detailed documentation.
Ok, now that we have walked through our workflow details, let's run this and see how it works.
To configure the workflow, head over to the swagger API of conductor server and access the metadata workflow create API:
[http://{{ server_host }}/swagger-ui/index.html?configUrl=/api-docs/swagger-config#/metadata-resource/create](http://{{ server_host }}/swagger-ui/index.html?configUrl=/api-docs/swagger-config#/metadata-resource/create)
If the link doesnt open the right Swagger section, we can navigate to Metadata-Resource
`POST {{ api_prefix }}/metadata/workflow`
![Swagger UI - Metadata - Workflow](metadataWorkflowPost.png)
Paste the workflow payload into the Swagger API and hit Execute.
Now if we head over to the UI, we can see this workflow definition created:
![Conductor UI - Workflow Definition](uiWorkflowDefinition.png)
If we click through we can see a visual representation of the workflow:
![Conductor UI - Workflow Definition - Visual Flow](uiWorkflowDefinitionVisual.png)
## Running our First Workflow
Lets run this workflow. To do that we can use the swagger API under the workflow-resources
[http://{{ server_host }}/swagger-ui/index.html?configUrl=/api-docs/swagger-config#/workflow-resource/startWorkflow_1](http://{{ server_host }}/swagger-ui/index.html?configUrl=/api-docs/swagger-config#/workflow-resource/startWorkflow_1)
![Swagger UI - Metadata - Workflow - Run](metadataWorkflowRun.png)
Hit **Execute**!
Conductor will return a workflow id. We will need to use this id to load this up on the UI. If our UI installation has
search enabled we wouldn't need to copy this. If we don't have search enabled (using Elasticsearch) copy it from the
Swagger UI.
![Swagger UI - Metadata - Workflow - Run](workflowRunIdCopy.png)
Ok, we should see this running and get completed soon. Lets go to the UI to see what happened.
To load the workflow directly, use this URL format:
```
http://localhost:5000/execution/<WORKFLOW_ID>
```
Replace `<WORKFLOW_ID>` with our workflow id from the previous step. We should see a screen like below. Click on the
different tabs to see all inputs and outputs and task list etc. Explore away!
![Conductor UI - Workflow Run](workflowLoaded.png)
## Summary
In this article — we learned how to run a sample workflow in our Conductor installation. Concepts we touched on:
1. Workflow creation
2. System tasks such as HTTP
3. Running a workflow via API
Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

+26
View File
@@ -0,0 +1,26 @@
---
description: "Guided Tutorial — learn Conductor step by step with hands-on labs covering task workers, definitions, and workflows."
---
# Guided Tutorial
## High Level Steps
Generally, these are the steps necessary in order to put Conductor to work for your business workflow:
1. Create task worker(s) that poll for scheduled tasks at regular interval
2. Create task definitions for these workers and register them.
3. Create the workflow definition
## Before We Begin
Ensure you have a Conductor instance up and running. This includes both the Server and the UI. We recommend following the [Docker Instructions](../running/deploy.md).
## Tools
For the purpose of testing and issuing API calls, the following tools are useful
- Linux cURL command
- [Postman](https://www.postman.com) or similar REST client
## Let's Go
We will begin by defining a simple workflow that utilizes System Tasks.
[Next](first-workflow.md)
+280
View File
@@ -0,0 +1,280 @@
---
description: "Run the Conductor kitchen sink example workflow that demonstrates forks, sub-workflows, decisions, dynamic tasks, and HTTP tasks in one definition."
---
# Kitchen Sink
An example kitchensink workflow that demonstrates the usage of all the schema constructs.
### Definition
```json
{
"name": "kitchensink",
"description": "kitchensink workflow",
"version": 1,
"tasks": [
{
"name": "task_1",
"taskReferenceName": "task_1",
"inputParameters": {
"mod": "${workflow.input.mod}",
"oddEven": "${workflow.input.oddEven}"
},
"type": "SIMPLE"
},
{
"name": "event_task",
"taskReferenceName": "event_0",
"inputParameters": {
"mod": "${workflow.input.mod}",
"oddEven": "${workflow.input.oddEven}"
},
"type": "EVENT",
"sink": "conductor"
},
{
"name": "dyntask",
"taskReferenceName": "task_2",
"inputParameters": {
"taskToExecute": "${workflow.input.task2Name}"
},
"type": "DYNAMIC",
"dynamicTaskNameParam": "taskToExecute"
},
{
"name": "oddEvenDecision",
"taskReferenceName": "oddEvenDecision",
"inputParameters": {
"oddEven": "${task_2.output.oddEven}"
},
"type": "DECISION",
"caseValueParam": "oddEven",
"decisionCases": {
"0": [
{
"name": "task_4",
"taskReferenceName": "task_4",
"inputParameters": {
"mod": "${task_2.output.mod}",
"oddEven": "${task_2.output.oddEven}"
},
"type": "SIMPLE"
},
{
"name": "dynamic_fanout",
"taskReferenceName": "fanout1",
"inputParameters": {
"dynamicTasks": "${task_4.output.dynamicTasks}",
"input": "${task_4.output.inputs}"
},
"type": "FORK_JOIN_DYNAMIC",
"dynamicForkTasksParam": "dynamicTasks",
"dynamicForkTasksInputParamName": "input"
},
{
"name": "dynamic_join",
"taskReferenceName": "join1",
"type": "JOIN"
}
],
"1": [
{
"name": "fork_join",
"taskReferenceName": "forkx",
"type": "FORK_JOIN",
"forkTasks": [
[
{
"name": "task_10",
"taskReferenceName": "task_10",
"type": "SIMPLE"
},
{
"name": "sub_workflow_x",
"taskReferenceName": "wf3",
"inputParameters": {
"mod": "${task_1.output.mod}",
"oddEven": "${task_1.output.oddEven}"
},
"type": "SUB_WORKFLOW",
"subWorkflowParam": {
"name": "sub_flow_1",
"version": 1
}
}
],
[
{
"name": "task_11",
"taskReferenceName": "task_11",
"type": "SIMPLE"
},
{
"name": "sub_workflow_x",
"taskReferenceName": "wf4",
"inputParameters": {
"mod": "${task_1.output.mod}",
"oddEven": "${task_1.output.oddEven}"
},
"type": "SUB_WORKFLOW",
"subWorkflowParam": {
"name": "sub_flow_1",
"version": 1
}
}
]
]
},
{
"name": "join",
"taskReferenceName": "join2",
"type": "JOIN",
"joinOn": [
"wf3",
"wf4"
]
}
]
}
},
{
"name": "search_elasticsearch",
"taskReferenceName": "get_es_1",
"inputParameters": {
"http_request": {
"uri": "http://localhost:9200/conductor/_search?size=10",
"method": "GET"
}
},
"type": "HTTP"
},
{
"name": "task_30",
"taskReferenceName": "task_30",
"inputParameters": {
"statuses": "${get_es_1.output..status}",
"workflowIds": "${get_es_1.output..workflowId}"
},
"type": "SIMPLE"
}
],
"outputParameters": {
"statues": "${get_es_1.output..status}",
"workflowIds": "${get_es_1.output..workflowId}"
},
"ownerEmail": "example@email.com",
"schemaVersion": 2
}
```
### Visual Flow
![img](kitchensink.png)
### Running Kitchensink Workflow
1. If you are running Conductor locally, use the `-DloadSample=true` Java system property when launching the server. This will create a kitchensink workflow,
related task definitions and kick off an instance of kitchensink workflow. Otherwise, you can create a new Workflow Definition in the UI by copying the sample above.
2. Once the workflow has started, the first task remains in the `SCHEDULED` state. This is because no workers are currently polling for the task.
3. We will use the REST endpoints directly to poll for tasks and updating the status.
#### Start workflow execution
Start the execution of the kitchensink workflow:
```bash
conductor workflow start -w kitchensink -i '{"task2Name": "task_5"}'
```
The response is a text string identifying the workflow instance id.
??? note "Using cURL"
```shell
curl -X POST --header 'Content-Type: application/json' --header 'Accept: text/plain' '{{ server_host }}{{ api_prefix }}/workflow/kitchensink' -d '
{
"task2Name": "task_5"
}
'
```
#### Poll for the first task:
```bash
conductor task poll task_1
```
??? note "Using cURL"
```shell
curl {{ server_host }}{{ api_prefix }}/tasks/poll/task_1
```
The response should look something like:
```json
{
"taskType": "task_1",
"status": "IN_PROGRESS",
"inputData": {
"mod": null,
"oddEven": null
},
"referenceTaskName": "task_1",
"retryCount": 0,
"seq": 1,
"pollCount": 1,
"taskDefName": "task_1",
"scheduledTime": 1486580932471,
"startTime": 1486580933869,
"endTime": 0,
"updateTime": 1486580933902,
"startDelayInSeconds": 0,
"retried": false,
"callbackFromWorker": true,
"responseTimeoutSeconds": 3600,
"workflowInstanceId": "b0d1a935-3d74-46fd-92b2-0ca1e388659f",
"taskId": "b9eea7dd-3fbd-46b9-a9ff-b00279459476",
"callbackAfterSeconds": 0,
"polledTime": 1486580933902,
"queueWaitTime": 1398
}
```
#### Update the task status
* Note the values for ```taskId``` and ```workflowInstanceId``` fields from the poll response
* Update the status of the task as ```COMPLETED``` as below:
```bash
conductor task update-execution --workflow-id b0d1a935-3d74-46fd-92b2-0ca1e388659f --task-ref-name task_1 --status COMPLETED --output '{"mod":5,"taskToExecute":"task_1","oddEven":0,"dynamicTasks":[{"name":"task_1","taskReferenceName":"task_1_1","type":"SIMPLE"},{"name":"sub_workflow_4","taskReferenceName":"wf_dyn","type":"SUB_WORKFLOW","subWorkflowParam":{"name":"sub_flow_1"}}],"inputs":{"task_1_1":{},"wf_dyn":{}}}'
```
??? note "Using cURL"
```json
curl -H 'Content-Type:application/json' -H 'Accept:application/json' -X POST {{ server_host }}{{ api_prefix }}/tasks/ -d '
{
"taskId": "b9eea7dd-3fbd-46b9-a9ff-b00279459476",
"workflowInstanceId": "b0d1a935-3d74-46fd-92b2-0ca1e388659f",
"status": "COMPLETED",
"outputData": {
"mod": 5,
"taskToExecute": "task_1",
"oddEven": 0,
"dynamicTasks": [
{
"name": "task_1",
"taskReferenceName": "task_1_1",
"type": "SIMPLE"
},
{
"name": "sub_workflow_4",
"taskReferenceName": "wf_dyn",
"type": "SUB_WORKFLOW",
"subWorkflowParam": {
"name": "sub_flow_1"
}
}
],
"inputs": {
"task_1_1": {},
"wf_dyn": {}
}
}
}'
```
This will mark the task_1 as completed and schedule ```task_5``` as the next task.
Repeat the same process for the subsequently scheduled tasks until the completion.
Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB