mirror of https://github.com/dapr/docs.git
scaffold out docs for workflow java sdk
Signed-off-by: Hannah Hunter <hannahhunter@microsoft.com>
This commit is contained in:
parent
4d444bd12a
commit
0211dfef6e
|
@ -30,7 +30,25 @@ The Dapr sidecar doesn’t load any workflow definitions. Rather, the sidecar si
|
||||||
|
|
||||||
[Workflow activities]({{< ref "workflow-features-concepts.md#workflow-activites" >}}) are the basic unit of work in a workflow and are the tasks that get orchestrated in the business process.
|
[Workflow activities]({{< ref "workflow-features-concepts.md#workflow-activites" >}}) are the basic unit of work in a workflow and are the tasks that get orchestrated in the business process.
|
||||||
|
|
||||||
{{< tabs ".NET" Python >}}
|
{{< tabs Python ".NET" Java >}}
|
||||||
|
|
||||||
|
{{% codetab %}}
|
||||||
|
|
||||||
|
<!--python-->
|
||||||
|
|
||||||
|
Define the workflow activities you'd like your workflow to perform. Activities are a function definition and can take inputs and outputs. The following example creates a counter (activity) called `hello_act` that notifies users of the current counter value. `hello_act` is a function derived from a class called `WorkflowActivityContext`.
|
||||||
|
|
||||||
|
```python
|
||||||
|
def hello_act(ctx: WorkflowActivityContext, input):
|
||||||
|
global counter
|
||||||
|
counter += input
|
||||||
|
print(f'New counter value is: {counter}!', flush=True)
|
||||||
|
```
|
||||||
|
|
||||||
|
[See the `hello_act` workflow activity in context.](https://github.com/dapr/python-sdk/blob/master/examples/demo_workflow/app.py#LL40C1-L43C59)
|
||||||
|
|
||||||
|
|
||||||
|
{{% /codetab %}}
|
||||||
|
|
||||||
{{% codetab %}}
|
{{% codetab %}}
|
||||||
|
|
||||||
|
@ -102,29 +120,67 @@ public class ProcessPaymentActivity : WorkflowActivity<PaymentRequest, object>
|
||||||
|
|
||||||
{{% codetab %}}
|
{{% codetab %}}
|
||||||
|
|
||||||
<!--python-->
|
<!--java-->
|
||||||
|
|
||||||
Define the workflow activities you'd like your workflow to perform. Activities are a function definition and can take inputs and outputs. The following example creates a counter (activity) called `hello_act` that notifies users of the current counter value. `hello_act` is a function derived from a class called `WorkflowActivityContext`.
|
Define the workflow activities you'd like your workflow to perform.
|
||||||
|
|
||||||
```python
|
The activities called in the example below are:
|
||||||
def hello_act(ctx: WorkflowActivityContext, input):
|
- `need`: Receive notification of a new order.
|
||||||
global counter
|
|
||||||
counter += input
|
### [activity]
|
||||||
print(f'New counter value is: {counter}!', flush=True)
|
|
||||||
|
```java
|
||||||
|
todo
|
||||||
```
|
```
|
||||||
|
|
||||||
[See the `hello_act` workflow activity in context.](https://github.com/dapr/python-sdk/blob/master/examples/demo_workflow/app.py#LL40C1-L43C59)
|
[See the full `todo` workflow activity example.](todo)
|
||||||
|
|
||||||
|
### [activity]
|
||||||
|
|
||||||
|
```java
|
||||||
|
todo
|
||||||
|
```
|
||||||
|
[See the full `todo` workflow activity example.](todo)
|
||||||
|
|
||||||
|
### [todo]
|
||||||
|
|
||||||
|
```java
|
||||||
|
todo
|
||||||
|
```
|
||||||
|
|
||||||
|
[See the full `todo` workflow activity example.](todo)
|
||||||
|
|
||||||
{{% /codetab %}}
|
{{% /codetab %}}
|
||||||
|
|
||||||
|
|
||||||
{{< /tabs >}}
|
{{< /tabs >}}
|
||||||
|
|
||||||
## Write the workflow
|
## Write the workflow
|
||||||
|
|
||||||
Next, register and call the activites in a workflow.
|
Next, register and call the activites in a workflow.
|
||||||
|
|
||||||
{{< tabs ".NET" Python >}}
|
{{< tabs Python ".NET" Java >}}
|
||||||
|
|
||||||
|
{{% codetab %}}
|
||||||
|
|
||||||
|
<!--python-->
|
||||||
|
|
||||||
|
The `hello_world_wf` function is derived from a class called `DaprWorkflowContext` with input and output parameter types. It also includes a `yield` statement that does the heavy lifting of the workflow and calls the workflow activities.
|
||||||
|
|
||||||
|
```python
|
||||||
|
def hello_world_wf(ctx: DaprWorkflowContext, input):
|
||||||
|
print(f'{input}')
|
||||||
|
yield ctx.call_activity(hello_act, input=1)
|
||||||
|
yield ctx.call_activity(hello_act, input=10)
|
||||||
|
yield ctx.wait_for_external_event("event1")
|
||||||
|
yield ctx.call_activity(hello_act, input=100)
|
||||||
|
yield ctx.call_activity(hello_act, input=1000)
|
||||||
|
```
|
||||||
|
|
||||||
|
[See the `hello_world_wf` workflow in context.](https://github.com/dapr/python-sdk/blob/master/examples/demo_workflow/app.py#LL32C1-L38C51)
|
||||||
|
|
||||||
|
|
||||||
|
{{% /codetab %}}
|
||||||
|
|
||||||
{{% codetab %}}
|
{{% codetab %}}
|
||||||
|
|
||||||
|
@ -171,21 +227,15 @@ The `OrderProcessingWorkflow` class is derived from a base class called `Workflo
|
||||||
|
|
||||||
{{% codetab %}}
|
{{% codetab %}}
|
||||||
|
|
||||||
<!--python-->
|
<!--java-->
|
||||||
|
|
||||||
The `hello_world_wf` function is derived from a class called `DaprWorkflowContext` with input and output parameter types. It also includes a `yield` statement that does the heavy lifting of the workflow and calls the workflow activities.
|
Intro
|
||||||
|
|
||||||
```python
|
```java
|
||||||
def hello_world_wf(ctx: DaprWorkflowContext, input):
|
todo
|
||||||
print(f'{input}')
|
|
||||||
yield ctx.call_activity(hello_act, input=1)
|
|
||||||
yield ctx.call_activity(hello_act, input=10)
|
|
||||||
yield ctx.wait_for_external_event("event1")
|
|
||||||
yield ctx.call_activity(hello_act, input=100)
|
|
||||||
yield ctx.call_activity(hello_act, input=1000)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
[See the `hello_world_wf` workflow in context.](https://github.com/dapr/python-sdk/blob/master/examples/demo_workflow/app.py#LL32C1-L38C51)
|
[See the `todo` workflow in context.](todo)
|
||||||
|
|
||||||
|
|
||||||
{{% /codetab %}}
|
{{% /codetab %}}
|
||||||
|
@ -196,78 +246,7 @@ def hello_world_wf(ctx: DaprWorkflowContext, input):
|
||||||
|
|
||||||
Finally, compose the application using the workflow.
|
Finally, compose the application using the workflow.
|
||||||
|
|
||||||
{{< tabs ".NET" Python >}}
|
{{< tabs Python ".NET" Java >}}
|
||||||
|
|
||||||
{{% codetab %}}
|
|
||||||
|
|
||||||
<!--csharp-->
|
|
||||||
|
|
||||||
[In the following `Program.cs` example](https://github.com/dapr/dotnet-sdk/blob/master/examples/Workflow/WorkflowConsoleApp/Program.cs), for a basic ASP.NET order processing application using the .NET SDK, your project code would include:
|
|
||||||
|
|
||||||
- A NuGet package called `Dapr.Workflow` to receive the .NET SDK capabilities
|
|
||||||
- A builder with an extension method called `AddDaprWorkflow`
|
|
||||||
- This will allow you to register workflows and workflow activities (tasks that workflows can schedule)
|
|
||||||
- HTTP API calls
|
|
||||||
- One for submitting a new order
|
|
||||||
- One for checking the status of an existing order
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
using Dapr.Workflow;
|
|
||||||
//...
|
|
||||||
|
|
||||||
// Dapr Workflows are registered as part of the service configuration
|
|
||||||
builder.Services.AddDaprWorkflow(options =>
|
|
||||||
{
|
|
||||||
// Note that it's also possible to register a lambda function as the workflow
|
|
||||||
// or activity implementation instead of a class.
|
|
||||||
options.RegisterWorkflow<OrderProcessingWorkflow>();
|
|
||||||
|
|
||||||
// These are the activities that get invoked by the workflow(s).
|
|
||||||
options.RegisterActivity<NotifyActivity>();
|
|
||||||
options.RegisterActivity<ReserveInventoryActivity>();
|
|
||||||
options.RegisterActivity<ProcessPaymentActivity>();
|
|
||||||
});
|
|
||||||
|
|
||||||
WebApplication app = builder.Build();
|
|
||||||
|
|
||||||
// POST starts new order workflow instance
|
|
||||||
app.MapPost("/orders", async (WorkflowEngineClient client, [FromBody] OrderPayload orderInfo) =>
|
|
||||||
{
|
|
||||||
if (orderInfo?.Name == null)
|
|
||||||
{
|
|
||||||
return Results.BadRequest(new
|
|
||||||
{
|
|
||||||
message = "Order data was missing from the request",
|
|
||||||
example = new OrderPayload("Paperclips", 99.95),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//...
|
|
||||||
});
|
|
||||||
|
|
||||||
// GET fetches state for order workflow to report status
|
|
||||||
app.MapGet("/orders/{orderId}", async (string orderId, WorkflowEngineClient client) =>
|
|
||||||
{
|
|
||||||
WorkflowState state = await client.GetWorkflowStateAsync(orderId, true);
|
|
||||||
if (!state.Exists)
|
|
||||||
{
|
|
||||||
return Results.NotFound($"No order with ID = '{orderId}' was found.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var httpResponsePayload = new
|
|
||||||
{
|
|
||||||
details = state.ReadInputAs<OrderPayload>(),
|
|
||||||
status = state.RuntimeStatus.ToString(),
|
|
||||||
result = state.ReadOutputAs<OrderResult>(),
|
|
||||||
};
|
|
||||||
|
|
||||||
//...
|
|
||||||
}).WithName("GetOrderInfoEndpoint");
|
|
||||||
|
|
||||||
app.Run();
|
|
||||||
```
|
|
||||||
|
|
||||||
{{% /codetab %}}
|
|
||||||
|
|
||||||
{{% codetab %}}
|
{{% codetab %}}
|
||||||
|
|
||||||
|
@ -356,6 +335,91 @@ if __name__ == '__main__':
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
{{% /codetab %}}
|
||||||
|
|
||||||
|
{{% codetab %}}
|
||||||
|
|
||||||
|
<!--csharp-->
|
||||||
|
|
||||||
|
[In the following `Program.cs` example](https://github.com/dapr/dotnet-sdk/blob/master/examples/Workflow/WorkflowConsoleApp/Program.cs), for a basic ASP.NET order processing application using the .NET SDK, your project code would include:
|
||||||
|
|
||||||
|
- A NuGet package called `Dapr.Workflow` to receive the .NET SDK capabilities
|
||||||
|
- A builder with an extension method called `AddDaprWorkflow`
|
||||||
|
- This will allow you to register workflows and workflow activities (tasks that workflows can schedule)
|
||||||
|
- HTTP API calls
|
||||||
|
- One for submitting a new order
|
||||||
|
- One for checking the status of an existing order
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Dapr.Workflow;
|
||||||
|
//...
|
||||||
|
|
||||||
|
// Dapr Workflows are registered as part of the service configuration
|
||||||
|
builder.Services.AddDaprWorkflow(options =>
|
||||||
|
{
|
||||||
|
// Note that it's also possible to register a lambda function as the workflow
|
||||||
|
// or activity implementation instead of a class.
|
||||||
|
options.RegisterWorkflow<OrderProcessingWorkflow>();
|
||||||
|
|
||||||
|
// These are the activities that get invoked by the workflow(s).
|
||||||
|
options.RegisterActivity<NotifyActivity>();
|
||||||
|
options.RegisterActivity<ReserveInventoryActivity>();
|
||||||
|
options.RegisterActivity<ProcessPaymentActivity>();
|
||||||
|
});
|
||||||
|
|
||||||
|
WebApplication app = builder.Build();
|
||||||
|
|
||||||
|
// POST starts new order workflow instance
|
||||||
|
app.MapPost("/orders", async (WorkflowEngineClient client, [FromBody] OrderPayload orderInfo) =>
|
||||||
|
{
|
||||||
|
if (orderInfo?.Name == null)
|
||||||
|
{
|
||||||
|
return Results.BadRequest(new
|
||||||
|
{
|
||||||
|
message = "Order data was missing from the request",
|
||||||
|
example = new OrderPayload("Paperclips", 99.95),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//...
|
||||||
|
});
|
||||||
|
|
||||||
|
// GET fetches state for order workflow to report status
|
||||||
|
app.MapGet("/orders/{orderId}", async (string orderId, WorkflowEngineClient client) =>
|
||||||
|
{
|
||||||
|
WorkflowState state = await client.GetWorkflowStateAsync(orderId, true);
|
||||||
|
if (!state.Exists)
|
||||||
|
{
|
||||||
|
return Results.NotFound($"No order with ID = '{orderId}' was found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var httpResponsePayload = new
|
||||||
|
{
|
||||||
|
details = state.ReadInputAs<OrderPayload>(),
|
||||||
|
status = state.RuntimeStatus.ToString(),
|
||||||
|
result = state.ReadOutputAs<OrderResult>(),
|
||||||
|
};
|
||||||
|
|
||||||
|
//...
|
||||||
|
}).WithName("GetOrderInfoEndpoint");
|
||||||
|
|
||||||
|
app.Run();
|
||||||
|
```
|
||||||
|
|
||||||
|
{{% /codetab %}}
|
||||||
|
|
||||||
|
{{% codetab %}}
|
||||||
|
|
||||||
|
<!--java-->
|
||||||
|
|
||||||
|
[In the following example](todo), for a basic Java hello world application using the Java SDK, your project code would include:
|
||||||
|
|
||||||
|
- A Java package called `todo` to receive the Java SDK capabilities.
|
||||||
|
|
||||||
|
```java
|
||||||
|
todo
|
||||||
|
```
|
||||||
|
|
||||||
{{% /codetab %}}
|
{{% /codetab %}}
|
||||||
|
|
||||||
|
|
||||||
|
@ -377,5 +441,6 @@ Now that you've authored a workflow, learn how to manage it.
|
||||||
- [Workflow overview]({{< ref workflow-overview.md >}})
|
- [Workflow overview]({{< ref workflow-overview.md >}})
|
||||||
- [Workflow API reference]({{< ref workflow_api.md >}})
|
- [Workflow API reference]({{< ref workflow_api.md >}})
|
||||||
- Try out the full SDK examples:
|
- Try out the full SDK examples:
|
||||||
- [.NET example](https://github.com/dapr/dotnet-sdk/tree/master/examples/Workflow)
|
|
||||||
- [Python example](https://github.com/dapr/python-sdk/tree/master/examples/demo_workflow)
|
- [Python example](https://github.com/dapr/python-sdk/tree/master/examples/demo_workflow)
|
||||||
|
- [.NET example](https://github.com/dapr/dotnet-sdk/tree/master/examples/Workflow)
|
||||||
|
- [Java example](todo)
|
||||||
|
|
|
@ -8,43 +8,7 @@ description: Manage and run workflows
|
||||||
|
|
||||||
Now that you've [authored the workflow and its activities in your application]({{< ref howto-author-workflow.md >}}), you can start, terminate, and get information about the workflow using HTTP API calls. For more information, read the [workflow API reference]({{< ref workflow_api.md >}}).
|
Now that you've [authored the workflow and its activities in your application]({{< ref howto-author-workflow.md >}}), you can start, terminate, and get information about the workflow using HTTP API calls. For more information, read the [workflow API reference]({{< ref workflow_api.md >}}).
|
||||||
|
|
||||||
{{< tabs ".NET" Python HTTP >}}
|
{{< tabs Python ".NET" Java HTTP >}}
|
||||||
|
|
||||||
<!--NET-->
|
|
||||||
{{% codetab %}}
|
|
||||||
|
|
||||||
Manage your workflow within your code. In the `OrderProcessingWorkflow` example from the [Author a workflow]({{< ref "howto-author-workflow.md#write-the-application" >}}) guide, the workflow is registered in the code. You can now start, terminate, and get information about a running workflow:
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
string orderId = "exampleOrderId";
|
|
||||||
string workflowComponent = "dapr";
|
|
||||||
string workflowName = "OrderProcessingWorkflow";
|
|
||||||
OrderPayload input = new OrderPayload("Paperclips", 99.95);
|
|
||||||
Dictionary<string, string> workflowOptions; // This is an optional parameter
|
|
||||||
|
|
||||||
// Start the workflow. This returns back a "StartWorkflowResponse" which contains the instance ID for the particular workflow instance.
|
|
||||||
StartWorkflowResponse startResponse = await daprClient.StartWorkflowAsync(orderId, workflowComponent, workflowName, input, workflowOptions);
|
|
||||||
|
|
||||||
// Get information on the workflow. This response contains information such as the status of the workflow, when it started, and more!
|
|
||||||
GetWorkflowResponse getResponse = await daprClient.GetWorkflowAsync(orderId, workflowComponent, workflowName);
|
|
||||||
|
|
||||||
// Terminate the workflow
|
|
||||||
await daprClient.TerminateWorkflowAsync(orderId, workflowComponent);
|
|
||||||
|
|
||||||
// Raise an event (an incoming purchase order) that your workflow will wait for. This returns the item waiting to be purchased.
|
|
||||||
await daprClient.RaiseWorkflowEventAsync(orderId, workflowComponent, workflowName, input);
|
|
||||||
|
|
||||||
// Pause
|
|
||||||
await daprClient.PauseWorkflowAsync(orderId, workflowComponent);
|
|
||||||
|
|
||||||
// Resume
|
|
||||||
await daprClient.ResumeWorkflowAsync(orderId, workflowComponent);
|
|
||||||
|
|
||||||
// Purge
|
|
||||||
await daprClient.PurgeWorkflowAsync(orderId, workflowComponent);
|
|
||||||
```
|
|
||||||
|
|
||||||
{{% /codetab %}}
|
|
||||||
|
|
||||||
<!--Python-->
|
<!--Python-->
|
||||||
{{% codetab %}}
|
{{% codetab %}}
|
||||||
|
@ -95,6 +59,53 @@ d.terminate_workflow(instance_id=instanceId, workflow_component=workflowComponen
|
||||||
|
|
||||||
{{% /codetab %}}
|
{{% /codetab %}}
|
||||||
|
|
||||||
|
<!--NET-->
|
||||||
|
{{% codetab %}}
|
||||||
|
|
||||||
|
Manage your workflow within your code. In the `OrderProcessingWorkflow` example from the [Author a workflow]({{< ref "howto-author-workflow.md#write-the-application" >}}) guide, the workflow is registered in the code. You can now start, terminate, and get information about a running workflow:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
string orderId = "exampleOrderId";
|
||||||
|
string workflowComponent = "dapr";
|
||||||
|
string workflowName = "OrderProcessingWorkflow";
|
||||||
|
OrderPayload input = new OrderPayload("Paperclips", 99.95);
|
||||||
|
Dictionary<string, string> workflowOptions; // This is an optional parameter
|
||||||
|
|
||||||
|
// Start the workflow. This returns back a "StartWorkflowResponse" which contains the instance ID for the particular workflow instance.
|
||||||
|
StartWorkflowResponse startResponse = await daprClient.StartWorkflowAsync(orderId, workflowComponent, workflowName, input, workflowOptions);
|
||||||
|
|
||||||
|
// Get information on the workflow. This response contains information such as the status of the workflow, when it started, and more!
|
||||||
|
GetWorkflowResponse getResponse = await daprClient.GetWorkflowAsync(orderId, workflowComponent, workflowName);
|
||||||
|
|
||||||
|
// Terminate the workflow
|
||||||
|
await daprClient.TerminateWorkflowAsync(orderId, workflowComponent);
|
||||||
|
|
||||||
|
// Raise an event (an incoming purchase order) that your workflow will wait for. This returns the item waiting to be purchased.
|
||||||
|
await daprClient.RaiseWorkflowEventAsync(orderId, workflowComponent, workflowName, input);
|
||||||
|
|
||||||
|
// Pause
|
||||||
|
await daprClient.PauseWorkflowAsync(orderId, workflowComponent);
|
||||||
|
|
||||||
|
// Resume
|
||||||
|
await daprClient.ResumeWorkflowAsync(orderId, workflowComponent);
|
||||||
|
|
||||||
|
// Purge
|
||||||
|
await daprClient.PurgeWorkflowAsync(orderId, workflowComponent);
|
||||||
|
```
|
||||||
|
|
||||||
|
{{% /codetab %}}
|
||||||
|
|
||||||
|
<!--Python-->
|
||||||
|
{{% codetab %}}
|
||||||
|
|
||||||
|
Manage your workflow within your code. In the workflow example from the [Author a workflow]({{< ref "howto-author-workflow.md#write-the-application" >}}) guide, the workflow is registered in the code using the following APIs:
|
||||||
|
|
||||||
|
```java
|
||||||
|
todo
|
||||||
|
```
|
||||||
|
|
||||||
|
{{% /codetab %}}
|
||||||
|
|
||||||
|
|
||||||
<!--HTTP-->
|
<!--HTTP-->
|
||||||
{{% codetab %}}
|
{{% codetab %}}
|
||||||
|
@ -172,6 +183,8 @@ Learn more about these HTTP calls in the [workflow API reference guide]({{< ref
|
||||||
## Next steps
|
## Next steps
|
||||||
- [Try out the Workflow quickstart]({{< ref workflow-quickstart.md >}})
|
- [Try out the Workflow quickstart]({{< ref workflow-quickstart.md >}})
|
||||||
- Try out the full SDK examples:
|
- Try out the full SDK examples:
|
||||||
- [.NET example](https://github.com/dapr/dotnet-sdk/tree/master/examples/Workflow)
|
|
||||||
- [Python example](https://github.com/dapr/python-sdk/blob/master/examples/demo_workflow/app.py)
|
- [Python example](https://github.com/dapr/python-sdk/blob/master/examples/demo_workflow/app.py)
|
||||||
|
- [.NET example](https://github.com/dapr/dotnet-sdk/tree/master/examples/Workflow)
|
||||||
|
- [Java example](todo)
|
||||||
|
|
||||||
- [Workflow API reference]({{< ref workflow_api.md >}})
|
- [Workflow API reference]({{< ref workflow_api.md >}})
|
||||||
|
|
|
@ -83,9 +83,9 @@ You can use the following SDKs to author a workflow.
|
||||||
|
|
||||||
| Language stack | Package |
|
| Language stack | Package |
|
||||||
| - | - |
|
| - | - |
|
||||||
| .NET | [Dapr.Workflow](https://www.nuget.org/profiles/dapr.io) |
|
|
||||||
| Python | [dapr-ext-workflow](https://github.com/dapr/python-sdk/tree/master/ext/dapr-ext-workflow) |
|
| Python | [dapr-ext-workflow](https://github.com/dapr/python-sdk/tree/master/ext/dapr-ext-workflow) |
|
||||||
|
| .NET | [Dapr.Workflow](https://www.nuget.org/profiles/dapr.io) |
|
||||||
|
| Java | need |
|
||||||
|
|
||||||
## Try out workflows
|
## Try out workflows
|
||||||
|
|
||||||
|
@ -96,8 +96,9 @@ Want to put workflows to the test? Walk through the following quickstart and tut
|
||||||
| Quickstart/tutorial | Description |
|
| Quickstart/tutorial | Description |
|
||||||
| ------------------- | ----------- |
|
| ------------------- | ----------- |
|
||||||
| [Workflow quickstart]({{< ref workflow-quickstart.md >}}) | Run a .NET workflow application with four workflow activities to see Dapr Workflow in action |
|
| [Workflow quickstart]({{< ref workflow-quickstart.md >}}) | Run a .NET workflow application with four workflow activities to see Dapr Workflow in action |
|
||||||
| [Workflow .NET SDK example](https://github.com/dapr/dotnet-sdk/tree/master/examples/Workflow) | Learn how to create a Dapr Workflow and invoke it using ASP.NET Core web APIs. |
|
|
||||||
| [Workflow Python SDK example](https://github.com/dapr/python-sdk/tree/master/examples/demo_workflow) | Learn how to create a Dapr Workflow and invoke it using the Python `DaprClient` package. |
|
| [Workflow Python SDK example](https://github.com/dapr/python-sdk/tree/master/examples/demo_workflow) | Learn how to create a Dapr Workflow and invoke it using the Python `DaprClient` package. |
|
||||||
|
| [Workflow .NET SDK example](https://github.com/dapr/dotnet-sdk/tree/master/examples/Workflow) | Learn how to create a Dapr Workflow and invoke it using ASP.NET Core web APIs. |
|
||||||
|
| [Workflow Java SDK example](todo) | Learn how to create a Dapr Workflow and invoke it using the Java `need` package. |
|
||||||
|
|
||||||
|
|
||||||
### Start using workflows directly in your app
|
### Start using workflows directly in your app
|
||||||
|
|
|
@ -21,242 +21,9 @@ In this guide, you'll:
|
||||||
<img src="/images/workflow-quickstart-overview.png" width=800 style="padding-bottom:15px;">
|
<img src="/images/workflow-quickstart-overview.png" width=800 style="padding-bottom:15px;">
|
||||||
|
|
||||||
|
|
||||||
{{< tabs ".NET" "Python" >}}
|
{{< tabs "Python" ".NET" "Java" >}}
|
||||||
|
|
||||||
<!-- .NET -->
|
<!-- Python -->
|
||||||
{{% codetab %}}
|
|
||||||
|
|
||||||
The `order-processor` console app starts and manages the lifecycle of an order processing workflow that stores and retrieves data in a state store. The workflow consists of four workflow activities, or tasks:
|
|
||||||
- `NotifyActivity`: Utilizes a logger to print out messages throughout the workflow
|
|
||||||
- `ReserveInventoryActivity`: Checks the state store to ensure that there is enough inventory for the purchase
|
|
||||||
- `ProcessPaymentActivity`: Processes and authorizes the payment
|
|
||||||
- `UpdateInventoryActivity`: Removes the requested items from the state store and updates the store with the new remaining inventory value
|
|
||||||
|
|
||||||
|
|
||||||
### Step 1: Pre-requisites
|
|
||||||
|
|
||||||
For this example, you will need:
|
|
||||||
|
|
||||||
- [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started).
|
|
||||||
- [.NET SDK or .NET 6 SDK installed](https://dotnet.microsoft.com/download).
|
|
||||||
<!-- IGNORE_LINKS -->
|
|
||||||
- [Docker Desktop](https://www.docker.com/products/docker-desktop)
|
|
||||||
<!-- END_IGNORE -->
|
|
||||||
|
|
||||||
### Step 2: Set up the environment
|
|
||||||
|
|
||||||
Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/workflows).
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/dapr/quickstarts.git
|
|
||||||
```
|
|
||||||
|
|
||||||
In a new terminal window, navigate to the `order-processor` directory:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd workflows/csharp/sdk/order-processor
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 3: Run the order processor app
|
|
||||||
|
|
||||||
In the terminal, start the order processor app alongside a Dapr sidecar:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
dapr run --app-id order-processor dotnet run
|
|
||||||
```
|
|
||||||
|
|
||||||
This starts the `order-processor` app with unique workflow ID and runs the workflow activities.
|
|
||||||
|
|
||||||
Expected output:
|
|
||||||
|
|
||||||
```
|
|
||||||
== APP == Starting workflow 6d2abcc9 purchasing 10 Cars
|
|
||||||
|
|
||||||
== APP == info: Microsoft.DurableTask.Client.Grpc.GrpcDurableTaskClient[40]
|
|
||||||
== APP == Scheduling new OrderProcessingWorkflow orchestration with instance ID '6d2abcc9' and 47 bytes of input data.
|
|
||||||
== APP == info: WorkflowConsoleApp.Activities.NotifyActivity[0]
|
|
||||||
== APP == Received order 6d2abcc9 for 10 Cars at $15000
|
|
||||||
== APP == info: WorkflowConsoleApp.Activities.ReserveInventoryActivity[0]
|
|
||||||
== APP == Reserving inventory for order 6d2abcc9 of 10 Cars
|
|
||||||
== APP == info: WorkflowConsoleApp.Activities.ReserveInventoryActivity[0]
|
|
||||||
== APP == There are: 100, Cars available for purchase
|
|
||||||
|
|
||||||
== APP == Your workflow has started. Here is the status of the workflow: Dapr.Workflow.WorkflowState
|
|
||||||
|
|
||||||
== APP == info: WorkflowConsoleApp.Activities.ProcessPaymentActivity[0]
|
|
||||||
== APP == Processing payment: 6d2abcc9 for 10 Cars at $15000
|
|
||||||
== APP == info: WorkflowConsoleApp.Activities.ProcessPaymentActivity[0]
|
|
||||||
== APP == Payment for request ID '6d2abcc9' processed successfully
|
|
||||||
== APP == info: WorkflowConsoleApp.Activities.UpdateInventoryActivity[0]
|
|
||||||
== APP == Checking Inventory for: Order# 6d2abcc9 for 10 Cars
|
|
||||||
== APP == info: WorkflowConsoleApp.Activities.UpdateInventoryActivity[0]
|
|
||||||
== APP == There are now: 90 Cars left in stock
|
|
||||||
== APP == info: WorkflowConsoleApp.Activities.NotifyActivity[0]
|
|
||||||
== APP == Order 6d2abcc9 has completed!
|
|
||||||
|
|
||||||
== APP == Workflow Status: Completed
|
|
||||||
```
|
|
||||||
|
|
||||||
### (Optional) Step 4: View in Zipkin
|
|
||||||
|
|
||||||
If you have Zipkin configured for Dapr locally on your machine, you can view the workflow trace spans in the Zipkin web UI (typically at `http://localhost:9411/zipkin/`).
|
|
||||||
|
|
||||||
<img src="/images/workflow-trace-spans-zipkin.png" width=800 style="padding-bottom:15px;">
|
|
||||||
|
|
||||||
### What happened?
|
|
||||||
|
|
||||||
When you ran `dapr run --app-id order-processor dotnet run`:
|
|
||||||
|
|
||||||
1. A unique order ID for the workflow is generated (in the above example, `6d2abcc9`) and the workflow is scheduled.
|
|
||||||
1. The `NotifyActivity` workflow activity sends a notification saying an order for 10 cars has been received.
|
|
||||||
1. The `ReserveInventoryActivity` workflow activity checks the inventory data, determines if you can supply the ordered item, and responds with the number of cars in stock.
|
|
||||||
1. Your workflow starts and notifies you of its status.
|
|
||||||
1. The `ProcessPaymentActivity` workflow activity begins processing payment for order `6d2abcc9` and confirms if successful.
|
|
||||||
1. The `UpdateInventoryActivity` workflow activity updates the inventory with the current available cars after the order has been processed.
|
|
||||||
1. The `NotifyActivity` workflow activity sends a notification saying that order `6d2abcc9` has completed.
|
|
||||||
1. The workflow terminates as completed.
|
|
||||||
|
|
||||||
#### `order-processor/Program.cs`
|
|
||||||
|
|
||||||
In the application's program file:
|
|
||||||
- The unique workflow order ID is generated
|
|
||||||
- The workflow is scheduled
|
|
||||||
- The workflow status is retrieved
|
|
||||||
- The workflow and the workflow activities it invokes are registered
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
using Dapr.Client;
|
|
||||||
using Dapr.Workflow;
|
|
||||||
//...
|
|
||||||
|
|
||||||
{
|
|
||||||
services.AddDaprWorkflow(options =>
|
|
||||||
{
|
|
||||||
// Note that it's also possible to register a lambda function as the workflow
|
|
||||||
// or activity implementation instead of a class.
|
|
||||||
options.RegisterWorkflow<OrderProcessingWorkflow>();
|
|
||||||
|
|
||||||
// These are the activities that get invoked by the workflow(s).
|
|
||||||
options.RegisterActivity<NotifyActivity>();
|
|
||||||
options.RegisterActivity<ReserveInventoryActivity>();
|
|
||||||
options.RegisterActivity<ProcessPaymentActivity>();
|
|
||||||
options.RegisterActivity<UpdateInventoryActivity>();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
//...
|
|
||||||
|
|
||||||
// Generate a unique ID for the workflow
|
|
||||||
string orderId = Guid.NewGuid().ToString()[..8];
|
|
||||||
string itemToPurchase = "Cars";
|
|
||||||
int ammountToPurchase = 10;
|
|
||||||
|
|
||||||
// Construct the order
|
|
||||||
OrderPayload orderInfo = new OrderPayload(itemToPurchase, 15000, ammountToPurchase);
|
|
||||||
|
|
||||||
// Start the workflow
|
|
||||||
Console.WriteLine("Starting workflow {0} purchasing {1} {2}", orderId, ammountToPurchase, itemToPurchase);
|
|
||||||
|
|
||||||
await daprClient.StartWorkflowAsync(
|
|
||||||
workflowComponent: DaprWorkflowComponent,
|
|
||||||
workflowName: nameof(OrderProcessingWorkflow),
|
|
||||||
input: orderInfo,
|
|
||||||
instanceId: orderId);
|
|
||||||
|
|
||||||
// Wait for the workflow to start and confirm the input
|
|
||||||
GetWorkflowResponse state = await daprClient.WaitForWorkflowStartAsync(
|
|
||||||
instanceId: orderId,
|
|
||||||
workflowComponent: DaprWorkflowComponent);
|
|
||||||
|
|
||||||
Console.WriteLine("Your workflow has started. Here is the status of the workflow: {0}", state.RuntimeStatus);
|
|
||||||
|
|
||||||
// Wait for the workflow to complete
|
|
||||||
state = await daprClient.WaitForWorkflowCompletionAsync(
|
|
||||||
instanceId: orderId,
|
|
||||||
workflowComponent: DaprWorkflowComponent);
|
|
||||||
|
|
||||||
Console.WriteLine("Workflow Status: {0}", state.RuntimeStatus);
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `order-processor/Workflows/OrderProcessingWorkflow.cs`
|
|
||||||
|
|
||||||
In `OrderProcessingWorkflow.cs`, the workflow is defined as a class with all of its associated tasks (determined by workflow activities).
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
using Dapr.Workflow;
|
|
||||||
//...
|
|
||||||
|
|
||||||
class OrderProcessingWorkflow : Workflow<OrderPayload, OrderResult>
|
|
||||||
{
|
|
||||||
public override async Task<OrderResult> RunAsync(WorkflowContext context, OrderPayload order)
|
|
||||||
{
|
|
||||||
string orderId = context.InstanceId;
|
|
||||||
|
|
||||||
// Notify the user that an order has come through
|
|
||||||
await context.CallActivityAsync(
|
|
||||||
nameof(NotifyActivity),
|
|
||||||
new Notification($"Received order {orderId} for {order.Quantity} {order.Name} at ${order.TotalCost}"));
|
|
||||||
|
|
||||||
string requestId = context.InstanceId;
|
|
||||||
|
|
||||||
// Determine if there is enough of the item available for purchase by checking the inventory
|
|
||||||
InventoryResult result = await context.CallActivityAsync<InventoryResult>(
|
|
||||||
nameof(ReserveInventoryActivity),
|
|
||||||
new InventoryRequest(RequestId: orderId, order.Name, order.Quantity));
|
|
||||||
|
|
||||||
// If there is insufficient inventory, fail and let the user know
|
|
||||||
if (!result.Success)
|
|
||||||
{
|
|
||||||
// End the workflow here since we don't have sufficient inventory
|
|
||||||
await context.CallActivityAsync(
|
|
||||||
nameof(NotifyActivity),
|
|
||||||
new Notification($"Insufficient inventory for {order.Name}"));
|
|
||||||
return new OrderResult(Processed: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// There is enough inventory available so the user can purchase the item(s). Process their payment
|
|
||||||
await context.CallActivityAsync(
|
|
||||||
nameof(ProcessPaymentActivity),
|
|
||||||
new PaymentRequest(RequestId: orderId, order.Name, order.Quantity, order.TotalCost));
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// There is enough inventory available so the user can purchase the item(s). Process their payment
|
|
||||||
await context.CallActivityAsync(
|
|
||||||
nameof(UpdateInventoryActivity),
|
|
||||||
new PaymentRequest(RequestId: orderId, order.Name, order.Quantity, order.TotalCost));
|
|
||||||
}
|
|
||||||
catch (TaskFailedException)
|
|
||||||
{
|
|
||||||
// Let them know their payment was processed
|
|
||||||
await context.CallActivityAsync(
|
|
||||||
nameof(NotifyActivity),
|
|
||||||
new Notification($"Order {orderId} Failed! You are now getting a refund"));
|
|
||||||
return new OrderResult(Processed: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Let them know their payment was processed
|
|
||||||
await context.CallActivityAsync(
|
|
||||||
nameof(NotifyActivity),
|
|
||||||
new Notification($"Order {orderId} has completed!"));
|
|
||||||
|
|
||||||
// End the workflow with a success result
|
|
||||||
return new OrderResult(Processed: true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `order-processor/Activities` directory
|
|
||||||
|
|
||||||
The `Activities` directory holds the four workflow activities used by the workflow, defined in the following files:
|
|
||||||
- `NotifyActivity.cs`
|
|
||||||
- `ReserveInventoryActivity.cs`
|
|
||||||
- `ProcessPaymentActivity.cs`
|
|
||||||
- `UpdateInventoryActivity.cs`
|
|
||||||
|
|
||||||
{{% /codetab %}}
|
|
||||||
|
|
||||||
<!-- Python -->
|
|
||||||
{{% codetab %}}
|
{{% codetab %}}
|
||||||
|
|
||||||
The `order-processor` console app starts and manages the `order_processing_workflow`, which simulates purchasing items from a store. The workflow consists of five unique workflow activities, or tasks:
|
The `order-processor` console app starts and manages the `order_processing_workflow`, which simulates purchasing items from a store. The workflow consists of five unique workflow activities, or tasks:
|
||||||
|
@ -494,6 +261,334 @@ In `workflow.py`, the workflow is defined as a class with all of its associated
|
||||||
```
|
```
|
||||||
{{% /codetab %}}
|
{{% /codetab %}}
|
||||||
|
|
||||||
|
<!-- .NET -->
|
||||||
|
{{% codetab %}}
|
||||||
|
|
||||||
|
The `order-processor` console app starts and manages the lifecycle of an order processing workflow that stores and retrieves data in a state store. The workflow consists of four workflow activities, or tasks:
|
||||||
|
- `NotifyActivity`: Utilizes a logger to print out messages throughout the workflow
|
||||||
|
- `ReserveInventoryActivity`: Checks the state store to ensure that there is enough inventory for the purchase
|
||||||
|
- `ProcessPaymentActivity`: Processes and authorizes the payment
|
||||||
|
- `UpdateInventoryActivity`: Removes the requested items from the state store and updates the store with the new remaining inventory value
|
||||||
|
|
||||||
|
|
||||||
|
### Step 1: Pre-requisites
|
||||||
|
|
||||||
|
For this example, you will need:
|
||||||
|
|
||||||
|
- [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started).
|
||||||
|
- [.NET SDK or .NET 6 SDK installed](https://dotnet.microsoft.com/download).
|
||||||
|
<!-- IGNORE_LINKS -->
|
||||||
|
- [Docker Desktop](https://www.docker.com/products/docker-desktop)
|
||||||
|
<!-- END_IGNORE -->
|
||||||
|
|
||||||
|
### Step 2: Set up the environment
|
||||||
|
|
||||||
|
Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/workflows).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/dapr/quickstarts.git
|
||||||
|
```
|
||||||
|
|
||||||
|
In a new terminal window, navigate to the `order-processor` directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd workflows/csharp/sdk/order-processor
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Run the order processor app
|
||||||
|
|
||||||
|
In the terminal, start the order processor app alongside a Dapr sidecar:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dapr run --app-id order-processor dotnet run
|
||||||
|
```
|
||||||
|
|
||||||
|
This starts the `order-processor` app with unique workflow ID and runs the workflow activities.
|
||||||
|
|
||||||
|
Expected output:
|
||||||
|
|
||||||
|
```
|
||||||
|
== APP == Starting workflow 6d2abcc9 purchasing 10 Cars
|
||||||
|
|
||||||
|
== APP == info: Microsoft.DurableTask.Client.Grpc.GrpcDurableTaskClient[40]
|
||||||
|
== APP == Scheduling new OrderProcessingWorkflow orchestration with instance ID '6d2abcc9' and 47 bytes of input data.
|
||||||
|
== APP == info: WorkflowConsoleApp.Activities.NotifyActivity[0]
|
||||||
|
== APP == Received order 6d2abcc9 for 10 Cars at $15000
|
||||||
|
== APP == info: WorkflowConsoleApp.Activities.ReserveInventoryActivity[0]
|
||||||
|
== APP == Reserving inventory for order 6d2abcc9 of 10 Cars
|
||||||
|
== APP == info: WorkflowConsoleApp.Activities.ReserveInventoryActivity[0]
|
||||||
|
== APP == There are: 100, Cars available for purchase
|
||||||
|
|
||||||
|
== APP == Your workflow has started. Here is the status of the workflow: Dapr.Workflow.WorkflowState
|
||||||
|
|
||||||
|
== APP == info: WorkflowConsoleApp.Activities.ProcessPaymentActivity[0]
|
||||||
|
== APP == Processing payment: 6d2abcc9 for 10 Cars at $15000
|
||||||
|
== APP == info: WorkflowConsoleApp.Activities.ProcessPaymentActivity[0]
|
||||||
|
== APP == Payment for request ID '6d2abcc9' processed successfully
|
||||||
|
== APP == info: WorkflowConsoleApp.Activities.UpdateInventoryActivity[0]
|
||||||
|
== APP == Checking Inventory for: Order# 6d2abcc9 for 10 Cars
|
||||||
|
== APP == info: WorkflowConsoleApp.Activities.UpdateInventoryActivity[0]
|
||||||
|
== APP == There are now: 90 Cars left in stock
|
||||||
|
== APP == info: WorkflowConsoleApp.Activities.NotifyActivity[0]
|
||||||
|
== APP == Order 6d2abcc9 has completed!
|
||||||
|
|
||||||
|
== APP == Workflow Status: Completed
|
||||||
|
```
|
||||||
|
|
||||||
|
### (Optional) Step 4: View in Zipkin
|
||||||
|
|
||||||
|
If you have Zipkin configured for Dapr locally on your machine, you can view the workflow trace spans in the Zipkin web UI (typically at `http://localhost:9411/zipkin/`).
|
||||||
|
|
||||||
|
<img src="/images/workflow-trace-spans-zipkin.png" width=800 style="padding-bottom:15px;">
|
||||||
|
|
||||||
|
### What happened?
|
||||||
|
|
||||||
|
When you ran `dapr run --app-id order-processor dotnet run`:
|
||||||
|
|
||||||
|
1. A unique order ID for the workflow is generated (in the above example, `6d2abcc9`) and the workflow is scheduled.
|
||||||
|
1. The `NotifyActivity` workflow activity sends a notification saying an order for 10 cars has been received.
|
||||||
|
1. The `ReserveInventoryActivity` workflow activity checks the inventory data, determines if you can supply the ordered item, and responds with the number of cars in stock.
|
||||||
|
1. Your workflow starts and notifies you of its status.
|
||||||
|
1. The `ProcessPaymentActivity` workflow activity begins processing payment for order `6d2abcc9` and confirms if successful.
|
||||||
|
1. The `UpdateInventoryActivity` workflow activity updates the inventory with the current available cars after the order has been processed.
|
||||||
|
1. The `NotifyActivity` workflow activity sends a notification saying that order `6d2abcc9` has completed.
|
||||||
|
1. The workflow terminates as completed.
|
||||||
|
|
||||||
|
#### `order-processor/Program.cs`
|
||||||
|
|
||||||
|
In the application's program file:
|
||||||
|
- The unique workflow order ID is generated
|
||||||
|
- The workflow is scheduled
|
||||||
|
- The workflow status is retrieved
|
||||||
|
- The workflow and the workflow activities it invokes are registered
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Dapr.Client;
|
||||||
|
using Dapr.Workflow;
|
||||||
|
//...
|
||||||
|
|
||||||
|
{
|
||||||
|
services.AddDaprWorkflow(options =>
|
||||||
|
{
|
||||||
|
// Note that it's also possible to register a lambda function as the workflow
|
||||||
|
// or activity implementation instead of a class.
|
||||||
|
options.RegisterWorkflow<OrderProcessingWorkflow>();
|
||||||
|
|
||||||
|
// These are the activities that get invoked by the workflow(s).
|
||||||
|
options.RegisterActivity<NotifyActivity>();
|
||||||
|
options.RegisterActivity<ReserveInventoryActivity>();
|
||||||
|
options.RegisterActivity<ProcessPaymentActivity>();
|
||||||
|
options.RegisterActivity<UpdateInventoryActivity>();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//...
|
||||||
|
|
||||||
|
// Generate a unique ID for the workflow
|
||||||
|
string orderId = Guid.NewGuid().ToString()[..8];
|
||||||
|
string itemToPurchase = "Cars";
|
||||||
|
int ammountToPurchase = 10;
|
||||||
|
|
||||||
|
// Construct the order
|
||||||
|
OrderPayload orderInfo = new OrderPayload(itemToPurchase, 15000, ammountToPurchase);
|
||||||
|
|
||||||
|
// Start the workflow
|
||||||
|
Console.WriteLine("Starting workflow {0} purchasing {1} {2}", orderId, ammountToPurchase, itemToPurchase);
|
||||||
|
|
||||||
|
await daprClient.StartWorkflowAsync(
|
||||||
|
workflowComponent: DaprWorkflowComponent,
|
||||||
|
workflowName: nameof(OrderProcessingWorkflow),
|
||||||
|
input: orderInfo,
|
||||||
|
instanceId: orderId);
|
||||||
|
|
||||||
|
// Wait for the workflow to start and confirm the input
|
||||||
|
GetWorkflowResponse state = await daprClient.WaitForWorkflowStartAsync(
|
||||||
|
instanceId: orderId,
|
||||||
|
workflowComponent: DaprWorkflowComponent);
|
||||||
|
|
||||||
|
Console.WriteLine("Your workflow has started. Here is the status of the workflow: {0}", state.RuntimeStatus);
|
||||||
|
|
||||||
|
// Wait for the workflow to complete
|
||||||
|
state = await daprClient.WaitForWorkflowCompletionAsync(
|
||||||
|
instanceId: orderId,
|
||||||
|
workflowComponent: DaprWorkflowComponent);
|
||||||
|
|
||||||
|
Console.WriteLine("Workflow Status: {0}", state.RuntimeStatus);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `order-processor/Workflows/OrderProcessingWorkflow.cs`
|
||||||
|
|
||||||
|
In `OrderProcessingWorkflow.cs`, the workflow is defined as a class with all of its associated tasks (determined by workflow activities).
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Dapr.Workflow;
|
||||||
|
//...
|
||||||
|
|
||||||
|
class OrderProcessingWorkflow : Workflow<OrderPayload, OrderResult>
|
||||||
|
{
|
||||||
|
public override async Task<OrderResult> RunAsync(WorkflowContext context, OrderPayload order)
|
||||||
|
{
|
||||||
|
string orderId = context.InstanceId;
|
||||||
|
|
||||||
|
// Notify the user that an order has come through
|
||||||
|
await context.CallActivityAsync(
|
||||||
|
nameof(NotifyActivity),
|
||||||
|
new Notification($"Received order {orderId} for {order.Quantity} {order.Name} at ${order.TotalCost}"));
|
||||||
|
|
||||||
|
string requestId = context.InstanceId;
|
||||||
|
|
||||||
|
// Determine if there is enough of the item available for purchase by checking the inventory
|
||||||
|
InventoryResult result = await context.CallActivityAsync<InventoryResult>(
|
||||||
|
nameof(ReserveInventoryActivity),
|
||||||
|
new InventoryRequest(RequestId: orderId, order.Name, order.Quantity));
|
||||||
|
|
||||||
|
// If there is insufficient inventory, fail and let the user know
|
||||||
|
if (!result.Success)
|
||||||
|
{
|
||||||
|
// End the workflow here since we don't have sufficient inventory
|
||||||
|
await context.CallActivityAsync(
|
||||||
|
nameof(NotifyActivity),
|
||||||
|
new Notification($"Insufficient inventory for {order.Name}"));
|
||||||
|
return new OrderResult(Processed: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// There is enough inventory available so the user can purchase the item(s). Process their payment
|
||||||
|
await context.CallActivityAsync(
|
||||||
|
nameof(ProcessPaymentActivity),
|
||||||
|
new PaymentRequest(RequestId: orderId, order.Name, order.Quantity, order.TotalCost));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// There is enough inventory available so the user can purchase the item(s). Process their payment
|
||||||
|
await context.CallActivityAsync(
|
||||||
|
nameof(UpdateInventoryActivity),
|
||||||
|
new PaymentRequest(RequestId: orderId, order.Name, order.Quantity, order.TotalCost));
|
||||||
|
}
|
||||||
|
catch (TaskFailedException)
|
||||||
|
{
|
||||||
|
// Let them know their payment was processed
|
||||||
|
await context.CallActivityAsync(
|
||||||
|
nameof(NotifyActivity),
|
||||||
|
new Notification($"Order {orderId} Failed! You are now getting a refund"));
|
||||||
|
return new OrderResult(Processed: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let them know their payment was processed
|
||||||
|
await context.CallActivityAsync(
|
||||||
|
nameof(NotifyActivity),
|
||||||
|
new Notification($"Order {orderId} has completed!"));
|
||||||
|
|
||||||
|
// End the workflow with a success result
|
||||||
|
return new OrderResult(Processed: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `order-processor/Activities` directory
|
||||||
|
|
||||||
|
The `Activities` directory holds the four workflow activities used by the workflow, defined in the following files:
|
||||||
|
- `NotifyActivity.cs`
|
||||||
|
- `ReserveInventoryActivity.cs`
|
||||||
|
- `ProcessPaymentActivity.cs`
|
||||||
|
- `UpdateInventoryActivity.cs`
|
||||||
|
|
||||||
|
{{% /codetab %}}
|
||||||
|
|
||||||
|
<!-- Java -->
|
||||||
|
{{% codetab %}}
|
||||||
|
|
||||||
|
The `order-processor` console app starts and manages the lifecycle of an order processing workflow that stores and retrieves data in a state store. The workflow consists of four workflow activities, or tasks:
|
||||||
|
-
|
||||||
|
|
||||||
|
|
||||||
|
### Step 1: Pre-requisites
|
||||||
|
|
||||||
|
For this example, you will need:
|
||||||
|
|
||||||
|
- [Dapr CLI and initialized environment](https://docs.dapr.io/getting-started).
|
||||||
|
- Java JDK 11 (or greater):
|
||||||
|
- [Oracle JDK](https://www.oracle.com/java/technologies/downloads), or
|
||||||
|
- OpenJDK
|
||||||
|
- [Apache Maven](https://maven.apache.org/install.html), version 3.x.
|
||||||
|
<!-- IGNORE_LINKS -->
|
||||||
|
- [Docker Desktop](https://www.docker.com/products/docker-desktop)
|
||||||
|
<!-- END_IGNORE -->
|
||||||
|
|
||||||
|
### Step 2: Set up the environment
|
||||||
|
|
||||||
|
Clone the [sample provided in the Quickstarts repo](https://github.com/dapr/quickstarts/tree/master/workflows).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/dapr/quickstarts.git
|
||||||
|
```
|
||||||
|
|
||||||
|
In a new terminal window, navigate to the `order-processor` directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
need
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Run the order processor app
|
||||||
|
|
||||||
|
In the terminal, start the order processor app alongside a Dapr sidecar:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
need
|
||||||
|
```
|
||||||
|
|
||||||
|
This starts the `order-processor` app with unique workflow ID and runs the workflow activities.
|
||||||
|
|
||||||
|
Expected output:
|
||||||
|
|
||||||
|
```
|
||||||
|
need
|
||||||
|
```
|
||||||
|
|
||||||
|
### (Optional) Step 4: View in Zipkin
|
||||||
|
|
||||||
|
If you have Zipkin configured for Dapr locally on your machine, you can view the workflow trace spans in the Zipkin web UI (typically at `http://localhost:9411/zipkin/`).
|
||||||
|
|
||||||
|
<img src="/images/workflow-trace-spans-zipkin.png" width=800 style="padding-bottom:15px;">
|
||||||
|
|
||||||
|
### What happened?
|
||||||
|
|
||||||
|
When you ran `need`:
|
||||||
|
|
||||||
|
1. A unique order ID for the workflow is generated (in the above example, `need`) and the workflow is scheduled.
|
||||||
|
1. The `need` workflow activity sends a notification saying an order for 10 cars has been received.
|
||||||
|
1. The `need` workflow activity checks the inventory data, determines if you can supply the ordered item, and responds with the number of cars in stock.
|
||||||
|
1. Your workflow starts and notifies you of its status.
|
||||||
|
1. The `need` workflow activity begins processing payment for order `need` and confirms if successful.
|
||||||
|
1. The `need` workflow activity updates the inventory with the current available cars after the order has been processed.
|
||||||
|
1. The `need` workflow activity sends a notification saying that order `need` has completed.
|
||||||
|
1. The workflow terminates as completed.
|
||||||
|
|
||||||
|
#### `order-processor/... need`
|
||||||
|
|
||||||
|
In the application's program file:
|
||||||
|
- The unique workflow order ID is generated
|
||||||
|
- The workflow is scheduled
|
||||||
|
- The workflow status is retrieved
|
||||||
|
- The workflow and the workflow activities it invokes are registered
|
||||||
|
|
||||||
|
```java
|
||||||
|
need
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `order-processor/... need`
|
||||||
|
|
||||||
|
In `need`, the workflow is defined as a class with all of its associated tasks (determined by workflow activities).
|
||||||
|
|
||||||
|
```java
|
||||||
|
need
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `order-processor/... need` directory
|
||||||
|
|
||||||
|
The `Activities` directory holds the four workflow activities used by the workflow, defined in the following files:
|
||||||
|
- `need`
|
||||||
|
|
||||||
|
{{% /codetab %}}
|
||||||
|
|
||||||
{{< /tabs >}}
|
{{< /tabs >}}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue