mirror of https://github.com/dapr/quickstarts.git
Merge branch 'release-1.13' into kaibocai/add-js-quickstart
Signed-off-by: Paul Yuknewicz <paulyuk@microsoft.com>
This commit is contained in:
commit
1b8a451a40
|
@ -1,5 +1,5 @@
|
|||
DAPR_CLI_VERSION: 1.12.0
|
||||
DAPR_RUNTIME_VERSION: 1.12.0
|
||||
DAPR_RUNTIME_VERSION: 1.13.0-rc.6
|
||||
DAPR_INSTALL_URL: https://raw.githubusercontent.com/dapr/cli/v${DAPR_CLI_VERSION}/install/
|
||||
DAPR_DEFAULT_IMAGE_REGISTRY: ghcr
|
||||
MACOS_PYTHON_VERSION: 3.10
|
||||
|
|
|
@ -32,7 +32,7 @@ jobs:
|
|||
const commentBody = payload.comment.body;
|
||||
if (!isFromPulls && commentBody && commentBody.indexOf("/assign") == 0) {
|
||||
if (!issue.assignees || issue.assignees.length === 0) {
|
||||
await github.issues.addAssignees({
|
||||
await github.rest.issues.addAssignees({
|
||||
owner: issue.owner,
|
||||
repo: issue.repo,
|
||||
issue_number: issue.number,
|
||||
|
|
|
@ -79,7 +79,8 @@ jobs:
|
|||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: |
|
||||
7.0.x
|
||||
6.0.x
|
||||
8.0.x
|
||||
- name: Set up Dapr CLI - Mac/Linux
|
||||
if: matrix.os != 'windows-latest'
|
||||
run: wget -q ${{ env.DAPR_INSTALL_URL }}/install.sh -O - | /bin/bash -s ${{ env.DAPR_CLI_VERSION }}
|
||||
|
|
|
@ -19,6 +19,7 @@ mvnw
|
|||
packages
|
||||
**/__pycache__/
|
||||
**/dist/
|
||||
Debug/
|
||||
|
||||
# IDE generated files and directories
|
||||
*.iml
|
||||
|
|
|
@ -34,15 +34,15 @@ Run the `SmartDevice.Service`, which will start service itself and the Dapr side
|
|||
<!-- STEP
|
||||
name: Run actor service
|
||||
expected_stdout_lines:
|
||||
- "Request finished HTTP/1.1 GET http://127.0.0.1:5001/healthz - - - 200"
|
||||
- "Request finished HTTP/1.1 GET http://127.0.0.1:5001/healthz - 200"
|
||||
expected_stderr_lines:
|
||||
working_dir: ./service
|
||||
working_dir: .
|
||||
output_match_mode: substring
|
||||
background: true
|
||||
sleep: 30
|
||||
-->
|
||||
```bash
|
||||
cd actors/csharp/sdk/service
|
||||
cd service
|
||||
dapr run --app-id actorservice --app-port 5001 --app-protocol http --dapr-http-port 56001 --resources-path ../../../resources -- dotnet run --urls=http://localhost:5001/
|
||||
```
|
||||
<!-- END_STEP -->
|
||||
|
@ -70,13 +70,13 @@ name: Run actor client
|
|||
expected_stdout_lines:
|
||||
- "Device 2 state: Location: Second Floor, Status: Ready"
|
||||
expected_stderr_lines:
|
||||
working_dir: ./client
|
||||
working_dir: .
|
||||
output_match_mode: substring
|
||||
background: true
|
||||
sleep: 60
|
||||
-->
|
||||
```bash
|
||||
cd ./actors/csharp/sdk/client
|
||||
cd client
|
||||
dapr run --app-id actorclient -- dotnet run
|
||||
```
|
||||
<!-- END_STEP -->
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<OutputType>Exe</OutputType>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6;net7</TargetFrameworks>
|
||||
<TargetFrameworks>net8.0</TargetFrameworks>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.5.002.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SmartDevice.Service", "service\SmartDevice.Service.csproj", "{3E9760D4-A707-487F-8B73-3ECB080749C0}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SmartDevice.Client", "client\SmartDevice.Client.csproj", "{044F6311-35DC-4A94-A99A-35CB59913AFF}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SmartDevice.Interfaces", "interfaces\SmartDevice.Interfaces.csproj", "{D5B9AFF1-D7F3-42E5-8E8E-06593F91323F}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{3E9760D4-A707-487F-8B73-3ECB080749C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3E9760D4-A707-487F-8B73-3ECB080749C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3E9760D4-A707-487F-8B73-3ECB080749C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3E9760D4-A707-487F-8B73-3ECB080749C0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{044F6311-35DC-4A94-A99A-35CB59913AFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{044F6311-35DC-4A94-A99A-35CB59913AFF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{044F6311-35DC-4A94-A99A-35CB59913AFF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{044F6311-35DC-4A94-A99A-35CB59913AFF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D5B9AFF1-D7F3-42E5-8E8E-06593F91323F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D5B9AFF1-D7F3-42E5-8E8E-06593F91323F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D5B9AFF1-D7F3-42E5-8E8E-06593F91323F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D5B9AFF1-D7F3-42E5-8E8E-06593F91323F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {409D8B04-7025-42BC-BC2A-AF3F6A089623}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -2,13 +2,9 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -2,13 +2,12 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
|
||||
<PackageReference Include="Dapr.AspNetCore" Version="1.12.*-*" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -22,7 +22,7 @@ app.use(bodyParser.json());
|
|||
const daprPort = process.env.DAPR_HTTP_PORT ?? "3500";
|
||||
const daprGRPCPort = process.env.DAPR_GRPC_PORT ?? "50001";
|
||||
|
||||
const stateStoreName = `statestore`;
|
||||
const stateStoreName = process.env.STATE_STORE_NAME ?? "statestore";
|
||||
const stateUrl = `http://localhost:${daprPort}/v1.0/state/${stateStoreName}`;
|
||||
const port = process.env.APP_PORT ?? "3000";
|
||||
|
||||
|
@ -76,4 +76,4 @@ app.get('/ports', (_req, res) => {
|
|||
res.status(200).send({DAPR_HTTP_PORT: daprPort, DAPR_GRPC_PORT: daprGRPCPort })
|
||||
});
|
||||
|
||||
app.listen(port, () => console.log(`Node App listening on port ${port}!`));
|
||||
app.listen(port, () => console.log(`Node App listening on port ${port}!`));
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<NoWarn>612,618</NoWarn>
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
# Dapr workflows
|
||||
|
||||
In this quickstart, you'll create a simple console application to demonstrate Dapr's workflow programming model and the workflow authoring client. The console app
|
||||
starts and manages an order processing workflow.
|
||||
The workflow management API provided by the Dapr client can also be used interchangeably with minor adjustments
|
||||
|
||||
This quickstart includes one project:
|
||||
|
||||
- Go app `order-processor`
|
||||
|
||||
The quickstart contains 1 workflow (OrderProcessingWorkflow) which simulates purchasing items from a store, and 5 unique activities within the workflow. These 5
|
||||
activities are as follows:
|
||||
|
||||
- NotifyActivity: This activity utilizes a logger to print out messages throughout the workflow. These messages notify the user when there is insufficient
|
||||
§inventory, their payment couldn't be processed, and more.
|
||||
- ProcessPaymentActivity: This activity is responsible for processing and authorizing the payment.
|
||||
- VerifyInventoryActivity: This activity checks the state store to ensure that there is enough inventory present for purchase.
|
||||
- UpdateInventoryActivity: This activity removes the requested items from the state store and updates the store with the new remaining inventory value.
|
||||
- RequestApprovalActivity: This activity seeks approval from Manager, if payment is greater than 50000 USD.
|
||||
|
||||
### Run the order processor workflow
|
||||
|
||||
1. Open a new terminal window and navigate to `order-processor` directory.
|
||||
2. Run the console app with Dapr:
|
||||
<!-- STEP
|
||||
name: Running this example
|
||||
expected_stdout_lines:
|
||||
- "for 10 cars - $150000"
|
||||
- "There are 100 cars available for purchase"
|
||||
- "Requesting approval for payment of 150000USD for 10 cars"
|
||||
- "has been approved!"
|
||||
- "There are now 90 cars left in stock"
|
||||
- "Workflow completed - result: COMPLETED"
|
||||
output_match_mode: substring
|
||||
background: true
|
||||
timeout_seconds: 120
|
||||
sleep: 30
|
||||
-->
|
||||
|
||||
```sh
|
||||
|
||||
dapr run -f .
|
||||
```
|
||||
|
||||
3. Expected output
|
||||
|
||||
```
|
||||
== APP - order-processor == *** Welcome to the Dapr Workflow console app sample!
|
||||
== APP - order-processor == *** Using this app, you can place orders that start workflows.
|
||||
== APP - order-processor == dapr client initializing for: 127.0.0.1:50056
|
||||
== APP - order-processor == adding base stock item: paperclip
|
||||
== APP - order-processor == 2024/02/01 12:59:52 work item listener started
|
||||
== APP - order-processor == INFO: 2024/02/01 12:59:52 starting background processor
|
||||
== APP - order-processor == adding base stock item: cars
|
||||
== APP - order-processor == adding base stock item: computers
|
||||
== APP - order-processor == ==========Begin the purchase of item:==========
|
||||
== APP - order-processor == NotifyActivity: Received order 48ee83b7-5d80-48d5-97f9-6b372f5480a5 for 10 cars - $150000
|
||||
== APP - order-processor == VerifyInventoryActivity: Verifying inventory for order 48ee83b7-5d80-48d5-97f9-6b372f5480a5 of 10 cars
|
||||
== APP - order-processor == VerifyInventoryActivity: There are 100 cars available for purchase
|
||||
== APP - order-processor == RequestApprovalActivity: Requesting approval for payment of 150000USD for 10 cars
|
||||
== APP - order-processor == NotifyActivity: Payment for order 48ee83b7-5d80-48d5-97f9-6b372f5480a5 has been approved!
|
||||
== APP - order-processor == ProcessPaymentActivity: 48ee83b7-5d80-48d5-97f9-6b372f5480a5 for 10 - cars (150000USD)
|
||||
== APP - order-processor == UpdateInventoryActivity: Checking Inventory for order 48ee83b7-5d80-48d5-97f9-6b372f5480a5 for 10 * cars
|
||||
== APP - order-processor == UpdateInventoryActivity: There are now 90 cars left in stock
|
||||
== APP - order-processor == NotifyActivity: Order 48ee83b7-5d80-48d5-97f9-6b372f5480a5 has completed!
|
||||
== APP - order-processor == Workflow completed - result: COMPLETED
|
||||
== APP - order-processor == Purchase of item is complete
|
||||
```
|
||||
|
||||
4. Stop Dapr workflow with CTRL-C or:
|
||||
<!-- END_STEP -->
|
||||
|
||||
```sh
|
||||
dapr stop -f .
|
||||
```
|
||||
|
||||
|
||||
|
||||
### View workflow output with Zipkin
|
||||
|
||||
For a more detailed view of the workflow activities (duration, progress etc.), try using Zipkin.
|
||||
|
||||
1. View Traces in Zipkin UI - In your browser go to http://localhost:9411 to view the workflow trace spans in the Zipkin web UI. The order-processor workflow
|
||||
should be viewable with the following output in the Zipkin web UI. Note: the [openzipkin/zipkin](https://hub.docker.com/r/openzipkin/zipkin/) docker container is
|
||||
launched on running `dapr init`.
|
||||
|
||||
<img src="img/workflow-trace-spans-zipkin.png">
|
||||
|
||||
### What happened?
|
||||
|
||||
When you ran the above comands:
|
||||
|
||||
1. First the "user" inputs an order for 10 cars into the concole app.
|
||||
2. A unique order ID for the workflow is generated (in the above example, `b903d749cd814e099f06ebf4a56a2f90`) and the workflow is scheduled.
|
||||
3. The `NotifyActivity` workflow activity sends a notification saying an order for 10 cars has been received.
|
||||
4. The `VerifyInventoryActivity` workflow activity checks the inventory data, determines if you can supply the ordered item, and responds with the number of cars
|
||||
in stock.
|
||||
5. The `RequestApprovalActivity` workflow activity is triggered due to buisness logic for orders exceeding $50k and user is prompted to manually approve the
|
||||
purchase before continuing the order.
|
||||
6. The workflow starts and notifies you of its status.
|
||||
7. The `ProcessPaymentActivity` workflow activity begins processing payment for order `b903d749cd814e099f06ebf4a56a2f90` and confirms if successful.
|
||||
8. The `UpdateInventoryActivity` workflow activity updates the inventory with the current available cars after the order has been processed.
|
||||
9. The `NotifyActivity` workflow activity sends a notification saying that order `b903d749cd814e099f06ebf4a56a2f90` has completed.
|
||||
10. The workflow terminates as completed.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
version: 1
|
||||
common:
|
||||
resourcesPath: ../../components
|
||||
apps:
|
||||
- appDirPath: ./order-processor/
|
||||
appID: order-processor
|
||||
command: ["go", "run", "."]
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 544 KiB |
|
@ -0,0 +1,2 @@
|
|||
include ../../../docker.mk
|
||||
include ../../../validate.mk
|
|
@ -0,0 +1,29 @@
|
|||
module dapr_example
|
||||
|
||||
go 1.21
|
||||
|
||||
toolchain go1.21.6
|
||||
|
||||
require github.com/dapr/go-sdk v1.6.1-0.20240209153236-ac26e622c4a6
|
||||
|
||||
require (
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||
github.com/dapr/dapr v1.13.0-rc.2 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/marusama/semaphore/v2 v2.5.0 // indirect
|
||||
github.com/microsoft/durabletask-go v0.4.1-0.20240122160106-fb5c4c05729d // indirect
|
||||
go.opentelemetry.io/otel v1.23.1 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.23.1 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.23.1 // indirect
|
||||
golang.org/x/net v0.21.0 // indirect
|
||||
golang.org/x/sys v0.17.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014 // indirect
|
||||
google.golang.org/grpc v1.61.0 // indirect
|
||||
google.golang.org/protobuf v1.32.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
|
@ -0,0 +1,65 @@
|
|||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/dapr/dapr v1.13.0-rc.2 h1:Y5tQ07KB856aSWXxVjb/Lob4AT8Gy/hJxZtwODI21CI=
|
||||
github.com/dapr/dapr v1.13.0-rc.2/go.mod h1:QvxJ5htwv17PeRfFMGkHznEVRkpnt35re7TpF4CsCc8=
|
||||
github.com/dapr/go-sdk v1.6.1-0.20240209153236-ac26e622c4a6 h1:YXOqOgB6RslXup0R10ZPc/hcQBD+LmUXXRuohb9wrjs=
|
||||
github.com/dapr/go-sdk v1.6.1-0.20240209153236-ac26e622c4a6/go.mod h1:DEftCAXK4mAt2OY7B5+9TcFQyo7lxI+OA85vyKNR01s=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/marusama/semaphore/v2 v2.5.0 h1:o/1QJD9DBYOWRnDhPwDVAXQn6mQYD0gZaS1Tpx6DJGM=
|
||||
github.com/marusama/semaphore/v2 v2.5.0/go.mod h1:z9nMiNUekt/LTpTUQdpp+4sJeYqUGpwMHfW0Z8V8fnQ=
|
||||
github.com/microsoft/durabletask-go v0.4.1-0.20240122160106-fb5c4c05729d h1:CVjystOHucBzKExLHD8E96D4KUNbehP0ozgue/6Tq/Y=
|
||||
github.com/microsoft/durabletask-go v0.4.1-0.20240122160106-fb5c4c05729d/go.mod h1:OSZ4K7SgqBEsaouk3lAVdDzvanIzsdj7angZ0FTeSAU=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
go.opentelemetry.io/otel v1.23.1 h1:Za4UzOqJYS+MUczKI320AtqZHZb7EqxO00jAHE0jmQY=
|
||||
go.opentelemetry.io/otel v1.23.1/go.mod h1:Td0134eafDLcTS4y+zQ26GE8u3dEuRBiBCTUIRHaikA=
|
||||
go.opentelemetry.io/otel/metric v1.23.1 h1:PQJmqJ9u2QaJLBOELl1cxIdPcpbwzbkjfEyelTl2rlo=
|
||||
go.opentelemetry.io/otel/metric v1.23.1/go.mod h1:mpG2QPlAfnK8yNhNJAxDZruU9Y1/HubbC+KyH8FaCWI=
|
||||
go.opentelemetry.io/otel/trace v1.23.1 h1:4LrmmEd8AU2rFvU1zegmvqW7+kWarxtNOPyeL6HmYY8=
|
||||
go.opentelemetry.io/otel/trace v1.23.1/go.mod h1:4IpnpJFwr1mo/6HL8XIPJaE9y0+u1KcVmuW7dwFSVrI=
|
||||
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA=
|
||||
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014 h1:FSL3lRCkhaPFxqi0s9o+V4UI2WTzAVOvkgbd4kVV4Wg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014/go.mod h1:SaPjaZGWb0lPqs6Ittu0spdfrOArqji4ZdeP5IC/9N4=
|
||||
google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0=
|
||||
google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
|
||||
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -0,0 +1,144 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/dapr/go-sdk/client"
|
||||
"github.com/dapr/go-sdk/workflow"
|
||||
)
|
||||
|
||||
var (
|
||||
stateStoreName = "statestore"
|
||||
workflowComponent = "dapr"
|
||||
workflowName = "OrderProcessingWorkflow"
|
||||
defaultItemName = "cars"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("*** Welcome to the Dapr Workflow console app sample!")
|
||||
fmt.Println("*** Using this app, you can place orders that start workflows.")
|
||||
|
||||
w, err := workflow.NewWorker()
|
||||
if err != nil {
|
||||
log.Fatalf("failed to start worker: %v", err)
|
||||
}
|
||||
|
||||
if err := w.RegisterWorkflow(OrderProcessingWorkflow); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := w.RegisterActivity(NotifyActivity); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := w.RegisterActivity(RequestApprovalActivity); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := w.RegisterActivity(VerifyInventoryActivity); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := w.RegisterActivity(ProcessPaymentActivity); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := w.RegisterActivity(UpdateInventoryActivity); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := w.Start(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
daprClient, err := client.NewClient()
|
||||
if err != nil {
|
||||
log.Fatalf("failed to initialise dapr client: %v", err)
|
||||
}
|
||||
wfClient, err := workflow.NewClient(workflow.WithDaprClient(daprClient))
|
||||
if err != nil {
|
||||
log.Fatalf("failed to initialise workflow client: %v", err)
|
||||
}
|
||||
|
||||
inventory := []InventoryItem{
|
||||
{ItemName: "paperclip", PerItemCost: 5, Quantity: 100},
|
||||
{ItemName: "cars", PerItemCost: 15000, Quantity: 100},
|
||||
{ItemName: "computers", PerItemCost: 500, Quantity: 100},
|
||||
}
|
||||
if err := restockInventory(daprClient, inventory); err != nil {
|
||||
log.Fatalf("failed to restock: %v", err)
|
||||
}
|
||||
|
||||
fmt.Println("==========Begin the purchase of item:==========")
|
||||
|
||||
itemName := defaultItemName
|
||||
orderQuantity := 10
|
||||
|
||||
totalCost := inventory[1].PerItemCost * orderQuantity
|
||||
|
||||
orderPayload := OrderPayload{
|
||||
ItemName: itemName,
|
||||
Quantity: orderQuantity,
|
||||
TotalCost: totalCost,
|
||||
}
|
||||
|
||||
id, err := wfClient.ScheduleNewWorkflow(context.Background(), workflowName, workflow.WithInput(orderPayload))
|
||||
if err != nil {
|
||||
log.Fatalf("failed to start workflow: %v", err)
|
||||
}
|
||||
|
||||
approvalSought := false
|
||||
|
||||
startTime := time.Now()
|
||||
|
||||
for {
|
||||
timeDelta := time.Since(startTime)
|
||||
metadata, err := wfClient.FetchWorkflowMetadata(context.Background(), id)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to fetch workflow: %v", err)
|
||||
}
|
||||
if (metadata.RuntimeStatus == workflow.StatusCompleted) || (metadata.RuntimeStatus == workflow.StatusFailed) || (metadata.RuntimeStatus == workflow.StatusTerminated) {
|
||||
fmt.Printf("Workflow completed - result: %v\n", metadata.RuntimeStatus.String())
|
||||
break
|
||||
}
|
||||
if timeDelta.Seconds() >= 10 {
|
||||
metadata, err := wfClient.FetchWorkflowMetadata(context.Background(), id)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to fetch workflow: %v", err)
|
||||
}
|
||||
if totalCost > 50000 && !approvalSought && ((metadata.RuntimeStatus != workflow.StatusCompleted) || (metadata.RuntimeStatus != workflow.StatusFailed) || (metadata.RuntimeStatus != workflow.StatusTerminated)) {
|
||||
approvalSought = true
|
||||
promptForApproval(id)
|
||||
}
|
||||
}
|
||||
// Sleep before the next iteration
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
|
||||
fmt.Println("Purchase of item is complete")
|
||||
}
|
||||
|
||||
// promptForApproval is an example case. There is no user input required here due to this being for testing purposes only.
|
||||
// It would be perfectly valid to add a wait here or display a prompt to continue the process.
|
||||
func promptForApproval(id string) {
|
||||
wfClient, err := workflow.NewClient()
|
||||
if err != nil {
|
||||
log.Fatalf("failed to initialise wfClient: %v", err)
|
||||
}
|
||||
if err := wfClient.RaiseEvent(context.Background(), id, "manager_approval"); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func restockInventory(daprClient client.Client, inventory []InventoryItem) error {
|
||||
for _, item := range inventory {
|
||||
itemSerialized, err := json.Marshal(item)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("adding base stock item: %s\n", item.ItemName)
|
||||
if err := daprClient.SaveState(context.Background(), stateStoreName, item.ItemName, itemSerialized, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package main
|
||||
|
||||
type OrderPayload struct {
|
||||
ItemName string `json:"item_name"`
|
||||
TotalCost int `json:"total_cost"`
|
||||
Quantity int `json:"quanity"`
|
||||
}
|
||||
|
||||
type OrderResult struct {
|
||||
Processed bool `json:"processed"`
|
||||
}
|
||||
|
||||
type InventoryItem struct {
|
||||
ItemName string `json:"item_name"`
|
||||
PerItemCost int `json:"per_item_cost"`
|
||||
Quantity int `json:"quanity"`
|
||||
}
|
||||
|
||||
type InventoryRequest struct {
|
||||
RequestID string `json:"request_id"`
|
||||
ItemName string `json:"item_name"`
|
||||
Quantity int `json:"quanity"`
|
||||
}
|
||||
|
||||
type InventoryResult struct {
|
||||
Success bool `json:"success"`
|
||||
InventoryItem InventoryItem `json:"inventory_item"`
|
||||
}
|
||||
|
||||
type PaymentRequest struct {
|
||||
RequestID string `json:"request_id"`
|
||||
ItemBeingPurchased string `json:"item_being_purchased"`
|
||||
Amount int `json:"amount"`
|
||||
Quantity int `json:"quantity"`
|
||||
}
|
||||
|
||||
type ApprovalRequired struct {
|
||||
Approval bool `json:"approval"`
|
||||
}
|
||||
|
||||
type Notification struct {
|
||||
Message string `json:"message"`
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/dapr/go-sdk/client"
|
||||
"github.com/dapr/go-sdk/workflow"
|
||||
)
|
||||
|
||||
// OrderProcessingWorkflow is the main workflow for orchestrating activities in the order process.
|
||||
func OrderProcessingWorkflow(ctx *workflow.WorkflowContext) (any, error) {
|
||||
orderID := ctx.InstanceID()
|
||||
var orderPayload OrderPayload
|
||||
if err := ctx.GetInput(&orderPayload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err := ctx.CallActivity(NotifyActivity, workflow.ActivityInput(Notification{
|
||||
Message: fmt.Sprintf("Received order %s for %d %s - $%d", orderID, orderPayload.Quantity, orderPayload.ItemName, orderPayload.TotalCost),
|
||||
})).Await(nil)
|
||||
if err != nil {
|
||||
return OrderResult{Processed: false}, err
|
||||
}
|
||||
|
||||
var verifyInventoryResult InventoryResult
|
||||
if err := ctx.CallActivity(VerifyInventoryActivity, workflow.ActivityInput(InventoryRequest{
|
||||
RequestID: orderID,
|
||||
ItemName: orderPayload.ItemName,
|
||||
Quantity: orderPayload.Quantity,
|
||||
})).Await(&verifyInventoryResult); err != nil {
|
||||
return OrderResult{Processed: false}, err
|
||||
}
|
||||
|
||||
if !verifyInventoryResult.Success {
|
||||
notification := Notification{Message: fmt.Sprintf("Insufficient inventory for %s", orderPayload.ItemName)}
|
||||
err := ctx.CallActivity(NotifyActivity, workflow.ActivityInput(notification)).Await(nil)
|
||||
return OrderResult{Processed: false}, err
|
||||
}
|
||||
|
||||
if orderPayload.TotalCost > 50000 {
|
||||
var approvalRequired ApprovalRequired
|
||||
if err := ctx.CallActivity(RequestApprovalActivity, workflow.ActivityInput(orderPayload)).Await(&approvalRequired); err != nil {
|
||||
return OrderResult{Processed: false}, err
|
||||
}
|
||||
if err := ctx.WaitForExternalEvent("manager_approval", time.Second*200).Await(nil); err != nil {
|
||||
return OrderResult{Processed: false}, err
|
||||
}
|
||||
// TODO: Confirm timeout flow - this will be in the form of an error.
|
||||
if approvalRequired.Approval {
|
||||
if err := ctx.CallActivity(NotifyActivity, workflow.ActivityInput(Notification{Message: fmt.Sprintf("Payment for order %s has been approved!", orderID)})).Await(nil); err != nil {
|
||||
log.Printf("failed to notify of a successful order: %v\n", err)
|
||||
}
|
||||
} else {
|
||||
if err := ctx.CallActivity(NotifyActivity, workflow.ActivityInput(Notification{Message: fmt.Sprintf("Payment for order %s has been rejected!", orderID)})).Await(nil); err != nil {
|
||||
log.Printf("failed to notify of an unsuccessful order :%v\n", err)
|
||||
}
|
||||
return OrderResult{Processed: false}, err
|
||||
}
|
||||
}
|
||||
err = ctx.CallActivity(ProcessPaymentActivity, workflow.ActivityInput(PaymentRequest{
|
||||
RequestID: orderID,
|
||||
ItemBeingPurchased: orderPayload.ItemName,
|
||||
Amount: orderPayload.TotalCost,
|
||||
Quantity: orderPayload.Quantity,
|
||||
})).Await(nil)
|
||||
if err != nil {
|
||||
if err := ctx.CallActivity(NotifyActivity, workflow.ActivityInput(Notification{Message: fmt.Sprintf("Order %s failed!", orderID)})).Await(nil); err != nil {
|
||||
log.Printf("failed to notify of a failed order: %v", err)
|
||||
}
|
||||
return OrderResult{Processed: false}, err
|
||||
}
|
||||
|
||||
err = ctx.CallActivity(UpdateInventoryActivity, workflow.ActivityInput(PaymentRequest{
|
||||
RequestID: orderID,
|
||||
ItemBeingPurchased: orderPayload.ItemName,
|
||||
Amount: orderPayload.TotalCost,
|
||||
Quantity: orderPayload.Quantity,
|
||||
})).Await(nil)
|
||||
if err != nil {
|
||||
if err := ctx.CallActivity(NotifyActivity, workflow.ActivityInput(Notification{Message: fmt.Sprintf("Order %s failed!", orderID)})).Await(nil); err != nil {
|
||||
log.Printf("failed to notify of a failed order: %v", err)
|
||||
}
|
||||
return OrderResult{Processed: false}, err
|
||||
}
|
||||
|
||||
if err := ctx.CallActivity(NotifyActivity, workflow.ActivityInput(Notification{Message: fmt.Sprintf("Order %s has completed!", orderID)})).Await(nil); err != nil {
|
||||
log.Printf("failed to notify of a successful order: %v", err)
|
||||
}
|
||||
return OrderResult{Processed: true}, err
|
||||
}
|
||||
|
||||
// NotifyActivity outputs a notification message
|
||||
func NotifyActivity(ctx workflow.ActivityContext) (any, error) {
|
||||
var input Notification
|
||||
if err := ctx.GetInput(&input); err != nil {
|
||||
return "", err
|
||||
}
|
||||
fmt.Printf("NotifyActivity: %s\n", input.Message)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// ProcessPaymentActivity is used to process a payment
|
||||
func ProcessPaymentActivity(ctx workflow.ActivityContext) (any, error) {
|
||||
var input PaymentRequest
|
||||
if err := ctx.GetInput(&input); err != nil {
|
||||
return "", err
|
||||
}
|
||||
fmt.Printf("ProcessPaymentActivity: %s for %d - %s (%dUSD)\n", input.RequestID, input.Quantity, input.ItemBeingPurchased, input.Amount)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// VerifyInventoryActivity is used to verify if an item is available in the inventory
|
||||
func VerifyInventoryActivity(ctx workflow.ActivityContext) (any, error) {
|
||||
var input InventoryRequest
|
||||
if err := ctx.GetInput(&input); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Printf("VerifyInventoryActivity: Verifying inventory for order %s of %d %s\n", input.RequestID, input.Quantity, input.ItemName)
|
||||
dClient, err := client.NewClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
item, err := dClient.GetState(context.Background(), stateStoreName, input.ItemName, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if item == nil {
|
||||
return InventoryResult{
|
||||
Success: false,
|
||||
InventoryItem: InventoryItem{},
|
||||
}, nil
|
||||
}
|
||||
var result InventoryItem
|
||||
if err := json.Unmarshal(item.Value, &result); err != nil {
|
||||
log.Fatalf("failed to parse inventory result %v", err)
|
||||
}
|
||||
fmt.Printf("VerifyInventoryActivity: There are %d %s available for purchase\n", result.Quantity, result.ItemName)
|
||||
if result.Quantity >= input.Quantity {
|
||||
return InventoryResult{Success: true, InventoryItem: result}, nil
|
||||
}
|
||||
return InventoryResult{Success: false, InventoryItem: InventoryItem{}}, nil
|
||||
}
|
||||
|
||||
// UpdateInventoryActivity modifies the inventory.
|
||||
func UpdateInventoryActivity(ctx workflow.ActivityContext) (any, error) {
|
||||
var input PaymentRequest
|
||||
if err := ctx.GetInput(&input); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Printf("UpdateInventoryActivity: Checking Inventory for order %s for %d * %s\n", input.RequestID, input.Quantity, input.ItemBeingPurchased)
|
||||
dClient, err := client.NewClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
item, err := dClient.GetState(context.Background(), stateStoreName, input.ItemBeingPurchased, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var result InventoryItem
|
||||
err = json.Unmarshal(item.Value, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newQuantity := result.Quantity - input.Quantity
|
||||
if newQuantity < 0 {
|
||||
return nil, fmt.Errorf("insufficient inventory for: %s", input.ItemBeingPurchased)
|
||||
}
|
||||
result.Quantity = newQuantity
|
||||
newState, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to marshal new state: %v", err)
|
||||
}
|
||||
dClient.SaveState(context.Background(), stateStoreName, input.ItemBeingPurchased, newState, nil)
|
||||
fmt.Printf("UpdateInventoryActivity: There are now %d %s left in stock\n", result.Quantity, result.ItemName)
|
||||
return InventoryResult{Success: true, InventoryItem: result}, nil
|
||||
}
|
||||
|
||||
// RequestApprovalActivity requests approval for the order
|
||||
func RequestApprovalActivity(ctx workflow.ActivityContext) (any, error) {
|
||||
var input OrderPayload
|
||||
if err := ctx.GetInput(&input); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Printf("RequestApprovalActivity: Requesting approval for payment of %dUSD for %d %s\n", input.TotalCost, input.Quantity, input.ItemName)
|
||||
return ApprovalRequired{Approval: true}, nil
|
||||
}
|
|
@ -49,16 +49,11 @@ dapr run -f .
|
|||
|
||||
```
|
||||
==========Begin the purchase of item:==========
|
||||
To restock items, type 'restock'.
|
||||
To exit workflow console app, type 'exit'.
|
||||
Enter the name of one of the following items to order: paperclip, cars, computers: cars
|
||||
How many cars would you like to purchase? 10
|
||||
Starting order workflow, purchasing 10 of cars
|
||||
INFO:NotifyActivity:Received order b903d749cd814e099f06ebf4a56a2f90 for 10 cars at $150000 !
|
||||
INFO:VerifyInventoryActivity:Verifying inventory for order b903d749cd814e099f06ebf4a56a2f90 of 10 cars
|
||||
INFO:VerifyInventoryActivity:There are 100 Cars available for purchase
|
||||
INFO:RequestApprovalActivity:Requesting approval for payment of 150000 USD for 10 cars
|
||||
(ID = b903d749cd814e099f06ebf4a56a2f90) requires approval. Approve? [Y/N] y
|
||||
INFO:NotifyActivity:Payment for order b903d749cd814e099f06ebf4a56a2f90 has been approved!
|
||||
INFO:ProcessPaymentActivity:Processing payment: b903d749cd814e099f06ebf4a56a2f90 for 10 cars at 150000 USD
|
||||
INFO:ProcessPaymentActivity:Payment for request ID b903d749cd814e099f06ebf4a56a2f90 processed successfully
|
||||
|
|
Loading…
Reference in New Issue