diff --git a/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-patterns.md b/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-patterns.md index 7657273f1..34679a88a 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-patterns.md +++ b/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-patterns.md @@ -164,7 +164,49 @@ public class ChainWorkflow extends Workflow { ```go - +func TaskChainWorkflow(ctx *workflow.WorkflowContext) (any, error) { + var input int + if err := ctx.GetInput(&input); err != nil { + return "", err + } + var result1 int + if err := ctx.CallActivity(Step1, workflow.ActivityInput(input)).Await(&result1); err != nil { + return nil, err + } + var result2 int + if err := ctx.CallActivity(Step1, workflow.ActivityInput(input)).Await(&result2); err != nil { + return nil, err + } + var result3 int + if err := ctx.CallActivity(Step1, workflow.ActivityInput(input)).Await(&result3); err != nil { + return nil, err + } + return []int{result1, result2, result3}, nil +} +func Step1(ctx workflow.ActivityContext) (any, error) { + var input int + if err := ctx.GetInput(&input); err != nil { + return "", err + } + fmt.Printf("Step 1: Received input: %s", input) + return input + 1, nil +} +func Step2(ctx workflow.ActivityContext) (any, error) { + var input int + if err := ctx.GetInput(&input); err != nil { + return "", err + } + fmt.Printf("Step 2: Received input: %s", input) + return input * 2, nil +} +func Step3(ctx workflow.ActivityContext) (any, error) { + var input int + if err := ctx.GetInput(&input); err != nil { + return "", err + } + fmt.Printf("Step 3: Received input: %s", input) + return int(math.Pow(float64(input), 2)), nil +} ``` {{% /codetab %}} diff --git a/daprdocs/content/en/getting-started/quickstarts/workflow-quickstart.md b/daprdocs/content/en/getting-started/quickstarts/workflow-quickstart.md index 03fc13433..d20bc49cf 100644 --- a/daprdocs/content/en/getting-started/quickstarts/workflow-quickstart.md +++ b/daprdocs/content/en/getting-started/quickstarts/workflow-quickstart.md @@ -20,6 +20,7 @@ In this guide, you'll: +Select your preferred language-specific Dapr SDK before proceeding with the Quickstart. {{< tabs "Python" ".NET" "Java" "Go" >}} @@ -68,14 +69,12 @@ pip3 install -r requirements.txt ### Step 3: Run the order processor app -In the terminal, start the order processor app alongside a Dapr sidecar: +In the terminal, start the order processor app alongside a Dapr sidecar using [Multi-App Run]({{< ref multi-app-dapr-run >}}): ```bash -dapr run --app-id order-processor --resources-path ../../../components/ -- python3 app.py +dapr run -f . ``` -> **Note:** Since Python3.exe is not defined in Windows, you may need to use `python app.py` instead of `python3 app.py`. - This starts the `order-processor` app with unique workflow ID and runs the workflow activities. Expected output: @@ -303,10 +302,10 @@ 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: +In the terminal, start the order processor app alongside a Dapr sidecar using [Multi-App Run]({{< ref multi-app-dapr-run >}}): ```bash -dapr run --app-id order-processor dotnet run +dapr run -f . ``` This starts the `order-processor` app with unique workflow ID and runs the workflow activities. @@ -559,10 +558,10 @@ mvn clean install ### Step 3: Run the order processor app -In the terminal, start the order processor app alongside a Dapr sidecar: +In the terminal, start the order processor app alongside a Dapr sidecar using [Multi-App Run]({{< ref multi-app-dapr-run >}}): ```bash -dapr run --app-id WorkflowConsoleApp --resources-path ../../../components/ --dapr-grpc-port 50001 -- java -jar target/OrderProcessingService-0.0.1-SNAPSHOT.jar io.dapr.quickstarts.workflows.WorkflowConsoleApp +dapr run -f . ``` This starts the `order-processor` app with unique workflow ID and runs the workflow activities. @@ -856,7 +855,7 @@ The `Activities` directory holds the four workflow activities used by the workfl {{% codetab %}} -The `order-processor` console app starts and manages the `OrderProcessingWorkflow`, 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 `OrderProcessingWorkflow` workflow, which simulates purchasing items from a store. The workflow consists of five unique workflow activities, or tasks: - `NotifyActivity`: Utilizes a logger to print out messages throughout the workflow. These messages notify you when: - You have insufficient inventory @@ -890,15 +889,9 @@ In a new terminal window, navigate to the `order-processor` directory: cd workflows/go/sdk/order-processor ``` -Install the Dapr Go SDK dependencies: - -```bash -go build . -``` - ### Step 3: Run the order processor app -In the terminal, start the order processor app alongside a Dapr sidecar: +In the terminal, start the order processor app alongside a Dapr sidecar using [Multi-App Run]({{< ref multi-app-dapr-run >}}): ```bash dapr run -f . @@ -1097,177 +1090,8 @@ func restockInventory(daprClient client.Client, inventory []InventoryItem) error } ``` -#### `order-processor/workflow.go` +Meanwhile, the `OrderProcessingWorkflow` and its activities are defined as methods in [`workflow.go`](https://github.com/dapr/quickstarts/workflows/go/sdk/order-processor/workflow.go) -In `main.go`, the workflow is defined as a method with all of its associated tasks (determined by workflow activities). - -```go -// 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 - } - if 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); 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 { - if err := ctx.CallActivity(NotifyActivity, workflow.ActivityInput(Notification{Message: fmt.Sprintf("Insufficient inventory for %s", orderPayload.ItemName)})).Await(nil); err != 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}, nil - } - } - if err := ctx.CallActivity(ProcessPaymentActivity, workflow.ActivityInput(PaymentRequest{ - RequestID: orderID, - ItemBeingPurchased: orderPayload.ItemName, - Amount: orderPayload.TotalCost, - Quantity: orderPayload.Quantity, - })).Await(nil); 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(UpdateInventoryActivity, workflow.ActivityInput(PaymentRequest{RequestID: orderID, ItemBeingPurchased: orderPayload.ItemName, Amount: orderPayload.TotalCost, Quantity: orderPayload.Quantity})).Await(nil); 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}, nil -} - -// 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 -} -``` {{% /codetab %}}