diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..5dc46e6b --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +* text=auto eol=lf +*.{cmd,[cC][mM][dD]} text eol=crlf +*.{bat,[bB][aA][tT]} text eol=crlf \ No newline at end of file diff --git a/conversation/python/http/README.md b/conversation/python/http/README.md index 2c013903..a935f02d 100644 --- a/conversation/python/http/README.md +++ b/conversation/python/http/README.md @@ -28,13 +28,15 @@ pip3 install -r requirements.txt cd .. ``` + + 2. Open a new terminal window and run the multi app run template: ```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 == dapr client initializing for: 127.0.0.1:46533 +== APP - order-processor == INFO: 2025/02/13 13:18:33 connecting work item listener stream +== APP - order-processor == 2025/02/13 13:18:33 work item listener started +== APP - order-processor == INFO: 2025/02/13 13:18:33 starting background processor == 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 == NotifyActivity: Received order b4cb2687-1af0-4f8d-9659-eb6389c07ade for 1 cars - $5000 +== APP - order-processor == VerifyInventoryActivity: Verifying inventory for order b4cb2687-1af0-4f8d-9659-eb6389c07ade of 1 cars +== APP - order-processor == VerifyInventoryActivity: There are 10 cars available for purchase +== APP - order-processor == ProcessPaymentActivity: b4cb2687-1af0-4f8d-9659-eb6389c07ade for 1 - cars (5000USD) +== APP - order-processor == UpdateInventoryActivity: Checking Inventory for order b4cb2687-1af0-4f8d-9659-eb6389c07ade for 1 * cars +== APP - order-processor == UpdateInventoryActivity: There are now 9 cars left in stock +== APP - order-processor == NotifyActivity: Order b4cb2687-1af0-4f8d-9659-eb6389c07ade has completed! +== APP - order-processor == workflow status: COMPLETED == APP - order-processor == Purchase of item is complete ``` 4. Stop Dapr workflow with CTRL-C or: - - - ```sh dapr stop -f . ``` - - - - ### View workflow output with Zipkin For a more detailed view of the workflow activities (duration, progress etc.), try using Zipkin. @@ -97,21 +85,14 @@ launched on running `dapr init`. 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. +1. An OrderPayload is made containing one car. +2. A unique order ID for the workflow is generated (in the above example, `b4cb2687-1af0-4f8d-9659-eb6389c07ade`) 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. - - - - - +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 total cost of the order is 5000, so the workflow will not call the `RequestApprovalActivity` activity. +6. The `ProcessPaymentActivity` workflow activity begins processing payment for order `b4cb2687-1af0-4f8d-9659-eb6389c07ade` and confirms if successful. +7. The `UpdateInventoryActivity` workflow activity updates the inventory with the current available cars after the order has been processed. +8. The `NotifyActivity` workflow activity sends a notification saying that order `b4cb2687-1af0-4f8d-9659-eb6389c07ade` has completed. +9. The workflow terminates as completed and the OrderResult is set to processed. +> **Note:** This quickstart uses an OrderPayload of one car with a total cost of $5000. Since the total order cost is not over 5000, the workflow will not call the `RequestApprovalActivity` activity nor wait for an approval event. Since the quickstart is a console application, it can't accept incoming events easily. If you want to test this scenario, convert the console app to a service and use the [raise event API](https://v1-15.docs.dapr.io/reference/api/workflow_api/#raise-event-request) via HTTP/gRPC or via the Dapr Workflow client to send an event to the workflow instance. \ No newline at end of file diff --git a/workflows/go/sdk/order-processor/main.go b/workflows/go/sdk/order-processor/main.go index 26baf781..e1f0e59e 100644 --- a/workflows/go/sdk/order-processor/main.go +++ b/workflows/go/sdk/order-processor/main.go @@ -61,7 +61,7 @@ func main() { inventory := []InventoryItem{ {ItemName: "paperclip", PerItemCost: 5, Quantity: 100}, - {ItemName: "cars", PerItemCost: 15000, Quantity: 100}, + {ItemName: "cars", PerItemCost: 5000, Quantity: 10}, {ItemName: "computers", PerItemCost: 500, Quantity: 100}, } if err := restockInventory(daprClient, inventory); err != nil { @@ -71,7 +71,7 @@ func main() { fmt.Println("==========Begin the purchase of item:==========") itemName := defaultItemName - orderQuantity := 10 + orderQuantity := 1 totalCost := inventory[1].PerItemCost * orderQuantity @@ -86,49 +86,23 @@ func main() { 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) + waitCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + _, err = wfClient.WaitForWorkflowCompletion(waitCtx, id) + cancel() + if err != nil { + log.Fatalf("failed to wait for workflow: %v", err) } + respFetch, err := wfClient.FetchWorkflowMetadata(context.Background(), id, workflow.WithFetchPayloads(true)) + if err != nil { + log.Fatalf("failed to get workflow: %v", err) + } + + fmt.Printf("workflow status: %v\n", respFetch.RuntimeStatus) + 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) diff --git a/workflows/go/sdk/order-processor/workflow.go b/workflows/go/sdk/order-processor/workflow.go index 50753990..6c05c4e5 100644 --- a/workflows/go/sdk/order-processor/workflow.go +++ b/workflows/go/sdk/order-processor/workflow.go @@ -40,7 +40,7 @@ func OrderProcessingWorkflow(ctx *workflow.WorkflowContext) (any, error) { return OrderResult{Processed: false}, err } - if orderPayload.TotalCost > 50000 { + if orderPayload.TotalCost > 5000 { var approvalRequired ApprovalRequired if err := ctx.CallActivity(RequestApprovalActivity, workflow.ActivityInput(orderPayload)).Await(&approvalRequired); err != nil { return OrderResult{Processed: false}, err diff --git a/workflows/java/sdk/README.md b/workflows/java/sdk/README.md index 0e60c6c3..71c22b1d 100644 --- a/workflows/java/sdk/README.md +++ b/workflows/java/sdk/README.md @@ -1,6 +1,6 @@ # Dapr workflows -In this quickstart, you'll create a simple console application to demonstrate Dapr's workflow programming model and the workflow management API. The console app starts and manages the lifecycle of a workflow that stores and retrieves data in a state store. +In this quickstart, you'll run console application to demonstrate Dapr's workflow programming model and the workflow management API. The console app starts and manages the lifecycle of a workflow that stores and retrieves data in a state store. This quickstart includes one project: @@ -9,8 +9,8 @@ This quickstart includes one project: The quickstart contains 1 workflow to simulate 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. -- ReserveInventoryActivity: This activity checks the state store to ensure that there is enough inventory present for purchase. -- RequestApprovalActivity: This activity requests approval for orders over a certain threshold +- VerifyInventoryActivity: This activity checks the state store to ensure that there is enough inventory present for purchase. +- RequestApprovalActivity: This activity requests approval for orders over a certain threshold. - ProcessPaymentActivity: This activity is responsible for processing and authorizing the payment. - UpdateInventoryActivity: This activity updates the state store with the new remaining inventory value. @@ -34,7 +34,7 @@ cd .. + 3. Expected output ``` -==========Begin the purchase of item:========== -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 -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 -INFO:UpdateInventoryActivity:Checking inventory for order b903d749cd814e099f06ebf4a56a2f90 for 10 cars -INFO:UpdateInventoryActivity:There are now 90 cars left in stock -INFO:NotifyActivity:Order b903d749cd814e099f06ebf4a56a2f90 has completed! -Workflow completed! Result: Completed - Purchase of item is Completed +== 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 == 2025-02-13 11:44:11.357 durabletask-worker INFO: Starting gRPC worker that connects to dns:127.0.0.1:38891 +== APP - order-processor == 2025-02-13 11:44:11.361 durabletask-worker INFO: Successfully connected to dns:127.0.0.1:38891. Waiting for work items... +== APP - order-processor == INFO:NotifyActivity:Received order 6830cb00174544a0b062ba818e14fddc for 1 cars at $5000 ! +== APP - order-processor == 2025-02-13 11:44:14.157 durabletask-worker INFO: 6830cb00174544a0b062ba818e14fddc: Orchestrator yielded with 1 task(s) and 0 event(s) outstanding. +== APP - order-processor == INFO:VerifyInventoryActivity:Verifying inventory for order 6830cb00174544a0b062ba818e14fddc of 1 cars +== APP - order-processor == INFO:VerifyInventoryActivity:There are 10 Cars available for purchase +== APP - order-processor == 2025-02-13 11:44:14.171 durabletask-worker INFO: 6830cb00174544a0b062ba818e14fddc: Orchestrator yielded with 1 task(s) and 0 event(s) outstanding. +== APP - order-processor == INFO:ProcessPaymentActivity:Processing payment: 6830cb00174544a0b062ba818e14fddc for 1 cars at 5000 USD +== APP - order-processor == INFO:ProcessPaymentActivity:Payment for request ID 6830cb00174544a0b062ba818e14fddc processed successfully +== APP - order-processor == 2025-02-13 11:44:14.177 durabletask-worker INFO: 6830cb00174544a0b062ba818e14fddc: Orchestrator yielded with 1 task(s) and 0 event(s) outstanding. +== APP - order-processor == INFO:UpdateInventoryActivity:Checking inventory for order 6830cb00174544a0b062ba818e14fddc for 1 cars +== APP - order-processor == INFO:UpdateInventoryActivity:There are now 9 cars left in stock +== APP - order-processor == 2025-02-13 11:44:14.189 durabletask-worker INFO: 6830cb00174544a0b062ba818e14fddc: Orchestrator yielded with 1 task(s) and 0 event(s) outstanding. +== APP - order-processor == INFO:NotifyActivity:Order 6830cb00174544a0b062ba818e14fddc has completed! +== APP - order-processor == 2025-02-13 11:44:14.195 durabletask-worker INFO: 6830cb00174544a0b062ba818e14fddc: Orchestration completed with status: COMPLETED +== APP - order-processor == item: InventoryItem(item_name=Paperclip, per_item_cost=5, quantity=100) +== APP - order-processor == item: InventoryItem(item_name=Cars, per_item_cost=5000, quantity=10) +== APP - order-processor == item: InventoryItem(item_name=Computers, per_item_cost=500, quantity=100) +== APP - order-processor == ==========Begin the purchase of item:========== +== APP - order-processor == Starting order workflow, purchasing 1 of cars +== APP - order-processor == 2025-02-13 11:44:16.363 durabletask-client INFO: Starting new 'order_processing_workflow' instance with ID = 'fc8a507e4a2246d2917d3ad4e3111240'. +== APP - order-processor == 2025-02-13 11:44:16.366 durabletask-client INFO: Waiting 30s for instance 'fc8a507e4a2246d2917d3ad4e3111240' to complete. +== APP - order-processor == 2025-02-13 11:44:16.366 durabletask-worker INFO: fc8a507e4a2246d2917d3ad4e3111240: Orchestrator yielded with 1 task(s) and 0 event(s) outstanding. +== APP - order-processor == INFO:NotifyActivity:Received order fc8a507e4a2246d2917d3ad4e3111240 for 1 cars at $5000 ! +== APP - order-processor == 2025-02-13 11:44:16.373 durabletask-worker INFO: fc8a507e4a2246d2917d3ad4e3111240: Orchestrator yielded with 1 task(s) and 0 event(s) outstanding. +== APP - order-processor == INFO:VerifyInventoryActivity:Verifying inventory for order fc8a507e4a2246d2917d3ad4e3111240 of 1 cars +== APP - order-processor == INFO:VerifyInventoryActivity:There are 10 Cars available for purchase +== APP - order-processor == 2025-02-13 11:44:16.383 durabletask-worker INFO: fc8a507e4a2246d2917d3ad4e3111240: Orchestrator yielded with 1 task(s) and 0 event(s) outstanding. +== APP - order-processor == INFO:ProcessPaymentActivity:Processing payment: fc8a507e4a2246d2917d3ad4e3111240 for 1 cars at 5000 USD +== APP - order-processor == INFO:ProcessPaymentActivity:Payment for request ID fc8a507e4a2246d2917d3ad4e3111240 processed successfully +== APP - order-processor == 2025-02-13 11:44:16.390 durabletask-worker INFO: fc8a507e4a2246d2917d3ad4e3111240: Orchestrator yielded with 1 task(s) and 0 event(s) outstanding. +== APP - order-processor == INFO:UpdateInventoryActivity:Checking inventory for order fc8a507e4a2246d2917d3ad4e3111240 for 1 cars +== APP - order-processor == INFO:UpdateInventoryActivity:There are now 9 cars left in stock +== APP - order-processor == 2025-02-13 11:44:16.403 durabletask-worker INFO: fc8a507e4a2246d2917d3ad4e3111240: Orchestrator yielded with 1 task(s) and 0 event(s) outstanding. +== APP - order-processor == INFO:NotifyActivity:Order fc8a507e4a2246d2917d3ad4e3111240 has completed! +== APP - order-processor == 2025-02-13 11:44:16.411 durabletask-worker INFO: fc8a507e4a2246d2917d3ad4e3111240: Orchestration completed with status: COMPLETED +== APP - order-processor == 2025-02-13 11:44:16.425 durabletask-client INFO: Instance 'fc8a507e4a2246d2917d3ad4e3111240' completed. +== APP - order-processor == 2025-02-13 11:44:16.425 durabletask-worker INFO: Stopping gRPC worker... +== APP - order-processor == 2025-02-13 11:44:16.426 durabletask-worker INFO: Disconnected from dns:127.0.0.1:38891 +== APP - order-processor == 2025-02-13 11:44:16.426 durabletask-worker INFO: No longer listening for work items +== APP - order-processor == 2025-02-13 11:44:16.426 durabletask-worker INFO: Worker shutdown completed +== APP - order-processor == Workflow completed! Result: {"processed": true, "__durabletask_autoobject__": true} ``` 4. Stop Dapr workflow with CTRL-C or: - ```sh dapr stop -f . ``` - - ### View workflow output with Zipkin For a more detailed view of the workflow activities (duration, progress etc.), try using Zipkin. @@ -83,21 +112,16 @@ For a more detailed view of the workflow activities (duration, progress etc.), t ### What happened? -When you ran `dapr run --app-id order-processor --resources-path ../../../components/ -- python3 app.py` - -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 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. - - - - +When you ran `dapr run -f .` +1. An OrderPayload is made containing one car. +2. A unique order ID for the workflow is generated (in the above example, `fc8a507e4a2246d2917d3ad4e3111240`) and the workflow is scheduled. +3. The `notify_activity` workflow activity sends a notification saying an order for one car has been received. +4. The `verify_inventory_activity` workflow activity checks the inventory data, determines if you can supply the ordered item, and responds with the number of cars in stock. The inventory is sufficient so the workflow continues. +5. The total cost of the order is 5000, so the workflow will not call the `request_approval_activity` activity. +6. The `process_payment_activity` workflow activity begins processing payment for order `fc8a507e4a2246d2917d3ad4e3111240` and confirms if successful. +7. The `update_inventory_activity` workflow activity updates the inventory with the current available cars after the order has been processed. +8. The `notify_activity` workflow activity sends a notification saying that order `fc8a507e4a2246d2917d3ad4e3111240` has completed. +9. The workflow terminates as completed and the OrderResult is set to processed. +> **Note:** This quickstart uses an OrderPayload of one car with a total cost of $5000. Since the total order cost is not over 5000, the workflow will not call the `request_approval_activity` activity nor wait for an approval event. Since the quickstart is a console application, it can't accept incoming events easily. If you want to test this scenario, convert the console app to a service and use the [raise event API](https://v1-15.docs.dapr.io/reference/api/workflow_api/#raise-event-request) via HTTP/gRPC or via the Dapr Workflow client to send an event to the workflow instance. \ No newline at end of file diff --git a/workflows/python/sdk/order-processor/app.py b/workflows/python/sdk/order-processor/app.py index b4678728..c0587440 100644 --- a/workflows/python/sdk/order-processor/app.py +++ b/workflows/python/sdk/order-processor/app.py @@ -1,5 +1,4 @@ from datetime import datetime -import threading from time import sleep from dapr.clients import DaprClient @@ -26,7 +25,7 @@ class WorkflowConsoleApp: baseInventory = { "paperclip": InventoryItem("Paperclip", 5, 100), - "cars": InventoryItem("Cars", 15000, 100), + "cars": InventoryItem("Cars", 5000, 10), "computers": InventoryItem("Computers", 500, 100), } @@ -36,67 +35,27 @@ class WorkflowConsoleApp: print("==========Begin the purchase of item:==========", flush=True) item_name = default_item_name - order_quantity = 10 + order_quantity = 1 total_cost = int(order_quantity) * baseInventory[item_name].per_item_cost order = OrderPayload(item_name=item_name, quantity=int(order_quantity), total_cost=total_cost) print(f'Starting order workflow, purchasing {order_quantity} of {item_name}', flush=True) instance_id = wfClient.schedule_new_workflow( workflow=order_processing_workflow, input=order.to_json()) - _id = instance_id - - def prompt_for_approval(wfClient: DaprWorkflowClient): - """This is a helper function to prompt for approval. - Not using the prompt here ACTUALLY, as quickstart validation is required to be automated. - - But, in case you may want to run this sample manually, you can uncomment the following lines: - try: - signal.alarm(15) - approved = input(f'(ID = {_id}) requires approval. Approve? [Y/N] ') - signal.alarm(0) # cancel the alarm - except TimeoutError: - approved = "y" - if state.runtime_status.name == "COMPLETED": - return - if approved.lower() == "y": - wfClient.raise_workflow_event(instance_id=_id, event_name="manager_approval", data={'approval': True}) - else: - wfClient.raise_workflow_event(instance_id=_id, event_name="manager_approval", data={'approval': False}) - - ## Additionally, you would need to import signal and define timeout_error: - # import signal - # def timeout_error(*_): - # raise TimeoutError - - # signal.signal(signal.SIGALRM, timeout_error) - """ - wfClient.raise_workflow_event(instance_id=_id, event_name="manager_approval", data={'approval': True}) - - approval_seeked = False - start_time = datetime.now() - while True: - time_delta = datetime.now() - start_time - state = wfClient.get_workflow_state(instance_id=_id) + try: + state = wfClient.wait_for_workflow_completion(instance_id=instance_id, timeout_in_seconds=30) if not state: - print("Workflow not found!") # not expected - break - - if state.runtime_status in {WorkflowStatus.COMPLETED, WorkflowStatus.FAILED, WorkflowStatus.TERMINATED}: - print(f'Workflow completed! Result: {state.runtime_status}', flush=True) - break - - - if time_delta.total_seconds() >= 10: - state = wfClient.get_workflow_state(instance_id=_id) - if total_cost > 50000 and state not in {WorkflowStatus.COMPLETED, WorkflowStatus.FAILED, WorkflowStatus.TERMINATED} and not approval_seeked: - approval_seeked = True - threading.Thread(target=prompt_for_approval(wfClient), daemon=True).start() + print("Workflow not found!") + elif state.runtime_status.name == 'COMPLETED': + print(f'Workflow completed! Result: {state.serialized_output}') + else: + print(f'Workflow failed! Status: {state.runtime_status.name}') # not expected + except TimeoutError: + print('*** Workflow timed out!') wfr.shutdown() - - def restock_inventory(self, daprClient: DaprClient, baseInventory): for key, item in baseInventory.items(): print(f'item: {item}') diff --git a/workflows/python/sdk/order-processor/workflow.py b/workflows/python/sdk/order-processor/workflow.py index 8df23e63..c0c9c05c 100644 --- a/workflows/python/sdk/order-processor/workflow.py +++ b/workflows/python/sdk/order-processor/workflow.py @@ -22,7 +22,7 @@ def order_processing_workflow(ctx: DaprWorkflowContext, order_payload_str: str): When the order is received, the inventory is checked to see if there is enough inventory to fulfill the order. If there is enough inventory, the payment is processed and the inventory is updated. If there is not enough inventory, the order is rejected. - If the total order is greater than $50,000, the order is sent to a manager for approval. + If the total order is greater than $5,000, the order is sent to a manager for approval. """ order_id = ctx.instance_id order_payload=json.loads(order_payload_str) @@ -40,23 +40,20 @@ def order_processing_workflow(ctx: DaprWorkflowContext, order_payload_str: str): +f'{order_payload["item_name"]}'+'!')) return OrderResult(processed=False) - if order_payload["total_cost"] > 50000: + if order_payload["total_cost"] > 5000: yield ctx.call_activity(request_approval_activity, input=order_payload) - approval_task = ctx.wait_for_external_event("manager_approval") - timeout_event = ctx.create_timer(timedelta(seconds=200)) + approval_task = ctx.wait_for_external_event("approval_event") + timeout_event = ctx.create_timer(timedelta(seconds=30)) winner = yield when_any([approval_task, timeout_event]) if winner == timeout_event: yield ctx.call_activity(notify_activity, - input=Notification(message='Payment for order '+order_id - +' has been cancelled due to timeout!')) + input=Notification(message='Order '+order_id + +' has been cancelled due to approval timeout.')) return OrderResult(processed=False) approval_result = yield approval_task - if approval_result["approval"]: + if approval_result == False: yield ctx.call_activity(notify_activity, input=Notification( - message=f'Payment for order {order_id} has been approved!')) - else: - yield ctx.call_activity(notify_activity, input=Notification( - message=f'Payment for order {order_id} has been rejected!')) + message=f'Order {order_id} was not approved')) return OrderResult(processed=False) yield ctx.call_activity(process_payment_activity, input=PaymentRequest( @@ -137,7 +134,7 @@ def update_inventory_activity(ctx: WorkflowActivityContext, new_quantity = res_json['quantity'] - input.quantity per_item_cost = res_json['per_item_cost'] if new_quantity < 0: - raise ValueError('Payment for request ID '+f'{input.item_being_purchased}' + raise ValueError('Inventory update for request ID '+f'{input.item_being_purchased}' +' could not be processed. Insufficient inventory.') new_val = f'{{"name": "{input.item_being_purchased}", "quantity": {str(new_quantity)}, "per_item_cost": {str(per_item_cost)}}}' client.save_state(store_name, input.item_being_purchased, new_val) @@ -150,7 +147,7 @@ def request_approval_activity(ctx: WorkflowActivityContext, input: OrderPayload): """Defines Request Approval Activity. This is used by the workflow to request approval for payment of an order. This activity is used only if the order total cost is greater than - a particular threshold, currently 50000 USD""" + a particular threshold""" logger = logging.getLogger('RequestApprovalActivity') logger.info('Requesting approval for payment of '+f'{input["total_cost"]}'+' USD for '