Skip to content

Commit 0183bbd

Browse files
committed
initial human task service proposal
1 parent b4a6d7f commit 0183bbd

File tree

5 files changed

+224
-0
lines changed

5 files changed

+224
-0
lines changed

20240401-CR-human-tasks-apis.md

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
# Human Task Component / Service / APIs
2+
3+
* Author(s): Mauricio Salatino
4+
* State: Ready for Implementation
5+
* Updated: 2024/04/01
6+
7+
## Overview
8+
9+
Following the success of Dapr Workflows, this proposal introduces a set of functionalities to enable tasks created for people to interact with. While Human Tasks can be created as part of workflow instances, as part of the orchestration flow, this component also enable Dapr users to create tasks based on other interactions, for example based on subscriptions.
10+
11+
![simple workflow](resources/20240401-human-tasks/simple-workflow.png)
12+
13+
Workflows enable asynchronous interactions by using the `waitForExternalEvent` semantic. This is extremely flexible as the workflow instance will wait for an event to be `raised` to continue the execution. While this approach provide the ultimate flexiblity, it pushes developers to build a lot of functionalities required when that event is produced as a result of a human interaction. This proposal aims to enable workflows to define human interactions as part of the workflow definition, but still reusing the `waitForExternalEvent` and `raiseEvent` mechanisms under the hood.
14+
15+
To make the life of developers easy new APIs can be added to the existing workflow client APIs:
16+
17+
```java
18+
public class ExampleWorkflow extends Workflow{
19+
20+
@Override
21+
public WorkflowStub create() {
22+
23+
ctx.callActivity(CreateCustomerOrderActivity.class.getName(), workflowPayload).await();
24+
..
25+
var order = ctx.waitForExternalEvent("WarehouseValidOrder", Duration.ofMinutes(5), OrderPayload.class).await();
26+
..
27+
if( order.getValue() > 5000){
28+
...
29+
**ctx.createHumanTask("Call Customer and Validate Order", workflowPayload).await();**
30+
}
31+
...
32+
ctx.callActivity(ShipOrderActivity.class.getName(), workflowPayload).await();
33+
34+
}
35+
}
36+
```
37+
38+
![simple workflow](resources/20240401-human-tasks/simple-workflow-with-human-tasks.png)
39+
40+
By calling this new API, a new Task will be created for a human to interact with and APIs will be provided for those interactions.
41+
42+
This proposal is divided into two sections:
43+
- APIs exposed by the service
44+
- Architectural options
45+
46+
47+
## Background
48+
49+
Human Task-based systems, in other words, systems where the main users are people (humans), in contrast with systems, usually implements an inbox-like interaction pattern to organise work that people needs to do. Tools like [GitHub issues](https://github.com/features/issues), [Jira](https://www.atlassian.com/software/jira/guides/issues/overview#what-is-an-issue) and even team coordination tools like [Asana](https://asana.com/) or [Monday](onday.com) all follow the same approach.
50+
51+
52+
Following the Dapr building blocks concept, this proposal describe the APIs that Dapr can expose to provide these features that can then be implemented by different providers. The APIs described in this proposal aim to reduce the work needed by developers (with a heavy focus on workflow developers) to building cloud native applications where human interactions are organised around tasks.
53+
54+
55+
## Implementation Details
56+
57+
### Design
58+
59+
A set of new APIs are introduced to provide basic functionality to implement Human Task systems. All the APIs work around the `Task` entity which stores the task metadata and status. The following APIs should be provided to implement basic task management features:
60+
61+
- **POST `/tasks/`**: Create a new Task, by default the tasks is unassigned.
62+
- **GET `/tasks/`**: Get All Tasks, this allow filters, for example, getting all the tasks for the logged in user, get all the tasks associated to a workflow instance, or get all the assigned tasks that are not completed yet.
63+
- **PUT `/tasks/<id>`**: Modify task information
64+
- **POST `/tasks/<id>/assign`**: Assign the task to an individual or to a group. Assinging the task to an empty group or user makes the task unassigned
65+
- **POST `/tasks/<id>/start`**: Mark the task as started
66+
- **POST `/tasks/<id>/complete`**: Mark the task as completed
67+
68+
The `Task entity` includes the following information:
69+
- **Task Id**: an unique identifier for the task
70+
- **Task Name**: the name of the task
71+
- **Task Data/Payload**: custom data that is associated with the task. This is usually used to correlated with data stored in external systems. For example, this payload can contain a link to a document that is stored on an external system, or the Customer Id that is associated with this task.
72+
- **(Optional) Workflow Instance Id**: if the task belongs to a workflow instance, the task needs to include a reference to the Workflow Instance Id.
73+
- **(Optional) App Id**: To further improve filtering, including the application id that created the task can be useful.
74+
- **Assignee**: this can be the id of an individual or a group of people. If this property is empty, the task is considered unassigned.
75+
- **Status**: [Created, Assigned, Started, Completed], this list can me made extensible in future interations.
76+
77+
For the service to be useful, all tasks must be stored in a persistent storage. The Statestore building block can be used here, but advanced query capabilities are required for this to work.
78+
79+
![](resources/20240401-human-tasks/task-lifecycle.png)
80+
81+
Tasks, because they have status associated to them, implement a simple lifecycle:
82+
- **Created**: this is the initial status of a task when it gets created.
83+
- **Assigned**: tasks needs to be assigned before they can be worked on. Assigning a task to an empty group/user will set the task back to Created status.
84+
- **Started**: A task must be started, before it is completed. Once the task is started, its payload can change.
85+
- **Completed**: A completed task cannot change it's payload.
86+
87+
### Behavior and expected APIs for developers
88+
89+
As shown in the following example, tasks can be created by our workflow definitions. In this case we want to validate orders that are exceeding values of $5000. When an order that matches this criteria is found a new human task is created for the fraud team to check its details.
90+
91+
```java
92+
public class ExampleWorkflow extends Workflow{
93+
94+
@Override
95+
public WorkflowStub create() {
96+
97+
ctx.callActivity(CreateCustomerOrderActivity.class.getName(), workflowPayload).await();
98+
..
99+
var order = ctx.waitForExternalEvent("WarehouseValidOrder", Duration.ofMinutes(5), OrderPayload.class).await();
100+
..
101+
if( order.getValue() > 5000){
102+
...
103+
**boolean valid = ctx.createHumanTask("Call Customer and Validate Order", "fraud team", workflowPayload).await();**
104+
105+
if(valid){
106+
...
107+
ctx.callActivity(ShipOrderActivity.class.getName(), workflowPayload).await();
108+
}
109+
}
110+
111+
112+
}
113+
}
114+
```
115+
116+
![](resources/20240401-human-tasks/workflow-example-with-human-task.png)
117+
118+
Under the covers, the `ctx.createHumanTask` operation can be implemented by having a workflow Activity (`HumanTaskActivity`) that implements the functionality to create a new Task. I mention this here, to make sure that the inclusion of the new `createHumanTask()` API doesn't involve any change in the underlaying mechanisms that Dapr workflows already have.
119+
120+
Once the task is created, developers will need separate APIs to interact with the Task instance. Similarly to raising events for workflows (`workflowClient.raiseEvent(workflowId, "WarehouseValidOrder", customerEvent.order())`) helper APIs can be provided to interact with the created tasks. This can be provided by the workflowClient or a separate `tasksClient`:
121+
122+
```java
123+
List<Task> tasks = workflowClient.getAllTasks();
124+
...
125+
// assigning or claiming tasks mechanisms can be provided here
126+
workflowClient.assignTask(taskId, "salaboy");
127+
...
128+
129+
// depending on the identity management integration, this needs to validate that "salaboy" is executing the following operations
130+
workflowClient.startTask(taskId);
131+
...
132+
workflowClient.updateTask(taskId, payload);
133+
134+
...
135+
workflowClient.completeTask(taskId, payload);
136+
137+
```
138+
139+
Under the covers, calling the `completeTask(taskId, payload)` API will perform two operations:
140+
- Mark the tasks as completed and update the payload with its final version
141+
- Raise an event so the workflow instance can continue
142+
143+
Going back to the proposed `HumanTaskActivity` internal implementation, it can use the waitForExternalEvent existing functionalty to wait for "HumanTaskCompleted" events.
144+
145+
```java
146+
public class HumanTaskActivity implements WorkflowActivity {
147+
148+
@Override
149+
public Object run(WorkflowActivityContext ctx) {
150+
// First, create a Task by calling the Task APIs
151+
taskClient.createTask("Task Name", taskPayload)
152+
...
153+
ctx.waitForExternalEvent("HumanTaskCompleted", TaskPayload.class).await();
154+
155+
}
156+
}
157+
```
158+
159+
160+
## Different Architectural options
161+
162+
I can see two completely different architectures for this proposal to be implemented. I will be asking the Dapr community for input here, as each option has its own drawbacks too.
163+
164+
### Human Tasks APIs included as other Building Blocks and hosted in the Dapr sidecar
165+
166+
The Human Tasks APIs can be created as a new Building Block that can be configured using Components as the Statestore or PubSub Components.
167+
168+
The component definition should allow the configuration of 3rd party services such as GitHub Issues, Jira or using the Statestore building block to store the tasks data.
169+
170+
Pros:
171+
- This approach follows the same principles already used by other Dapr Building Blocks
172+
173+
Cons:
174+
- It adds up to the Dapr Sidecar complexity
175+
- It requires the service to implement HTTP and GRPC endpoitns
176+
- It requires to have SDK support
177+
- There is no need to have a strong relationship with the application (identity)
178+
179+
180+
### Standalone Service that can be installed as part of the Dapr Control Plane
181+
182+
Creating a Standalone service that can be installed when installing Dapr, enables a separate lifecycle and different scalability approaches to be implemented.
183+
184+
When working with Tasks, it is expected to work with Tasks created by different applications, hence there is no need of an application specific "instance" of the service.
185+
186+
Pros:
187+
- Simple service that can run standalone and can be scaled up independently of the Dapr Sidecars.
188+
- There is one global configuration for the service, each sidecar doesn't need to access those configurations
189+
- A simple version can be implemented using only HTTP to validate the service features
190+
191+
Cons:
192+
- Introduces a new service, with a completely new lifecycle. This service is user facing so it is not part of the Dapr Control Plane component.
193+
194+
195+
## Provider Implementations
196+
197+
Initially, if the Query APIs from the Statestore offer enough functionality to start, I would suggest to implement a simple version of the service using a Statestore as persistent storage. If Query support is added to the `in-memory` Statestore, this would make an in-memory Human Task service possible.
198+
199+
To improve and prove the value of the APIs suggested in this proposal, a second implementation using GitHub issues can be implemented.
200+
201+
202+
## Related Items
203+
204+
Human Tasks systems, because they involve people doing work, most of the time require integration points with:
205+
- **Identity management solutions**: tasks needs to be assigned to people and then filtered by those assignments, integrations with tools like [Keycloak](https://www.keycloak.org/), [Zitadel](https://zitadel.com/) or other identity managers is key to provide the flexiblity needed by the applications consuming these APIs.
206+
- **Scheduled Jobs/Tasks, Alerts and time-based reminders**: there are very common business scenarios where tasks need to be automatically cancelled or reminders needs to be sent if a task hasn't been completed in a fixed amount of time. For such scenarios, integrations with the Dapr scheduler service are extremely good to have.
207+
- **Notifications, Emails**: there are well-known patterns to communicate with people, sending notifications (in app, SMS, or emails) are quite common requirements for the kind of functionalities described in this proposal. This highlight the need for what might become another Dapr building block to provide such functionality as APIs for developers to use.
208+
209+
### Acceptance Criteria
210+
211+
How will success be measured?
212+
213+
A new set of APIs are available and developers can use the Human Task Service APIs (HTTP) to interact with Task Instances.
214+
215+
Followed by the first iteration, GRPC endpoints can be implemented and support for SDKs can be added.
216+
217+
218+
## Completion Checklist
219+
220+
What changes or actions are required to make this proposal complete?
221+
* Code changes
222+
* Tests added (e2e, unit)
223+
* Documentation
224+
97.1 KB
Loading
76.7 KB
Loading
38.1 KB
Loading
187 KB
Loading

0 commit comments

Comments
 (0)