mirror of https://github.com/dapr/quickstarts.git
refactor: update python workflow example with daprWorkflowClient (#1101)
Signed-off-by: Eileen Yu <eileenylj@gmail.com>
This commit is contained in:
parent
9e9cb5e862
commit
c32f421eae
|
|
@ -34,7 +34,7 @@ cd ..
|
||||||
name: Running this example
|
name: Running this example
|
||||||
expected_stdout_lines:
|
expected_stdout_lines:
|
||||||
- "There are now 90 cars left in stock"
|
- "There are now 90 cars left in stock"
|
||||||
- "Workflow completed! Result: Completed"
|
- "Workflow completed!"
|
||||||
output_match_mode: substring
|
output_match_mode: substring
|
||||||
background: true
|
background: true
|
||||||
timeout_seconds: 120
|
timeout_seconds: 120
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,12 @@ from time import sleep
|
||||||
|
|
||||||
from dapr.clients import DaprClient
|
from dapr.clients import DaprClient
|
||||||
from dapr.conf import settings
|
from dapr.conf import settings
|
||||||
from dapr.ext.workflow import WorkflowRuntime
|
from dapr.ext.workflow import DaprWorkflowClient, WorkflowStatus
|
||||||
|
|
||||||
from workflow import order_processing_workflow, notify_activity, process_payment_activity, \
|
from workflow import wfr, order_processing_workflow
|
||||||
verify_inventory_activity, update_inventory_activity, requst_approval_activity
|
|
||||||
from model import InventoryItem, OrderPayload
|
from model import InventoryItem, OrderPayload
|
||||||
|
|
||||||
store_name = "statestore"
|
store_name = "statestore"
|
||||||
workflow_component = "dapr"
|
|
||||||
workflow_name = "order_processing_workflow"
|
workflow_name = "order_processing_workflow"
|
||||||
default_item_name = "cars"
|
default_item_name = "cars"
|
||||||
|
|
||||||
|
|
@ -19,39 +17,35 @@ class WorkflowConsoleApp:
|
||||||
def main(self):
|
def main(self):
|
||||||
print("*** Welcome to the Dapr Workflow console app sample!", flush=True)
|
print("*** Welcome to the Dapr Workflow console app sample!", flush=True)
|
||||||
print("*** Using this app, you can place orders that start workflows.", flush=True)
|
print("*** Using this app, you can place orders that start workflows.", flush=True)
|
||||||
|
|
||||||
|
wfr.start()
|
||||||
# Wait for the sidecar to become available
|
# Wait for the sidecar to become available
|
||||||
sleep(5)
|
sleep(5)
|
||||||
|
|
||||||
workflowRuntime = WorkflowRuntime(settings.DAPR_RUNTIME_HOST, settings.DAPR_GRPC_PORT)
|
wfClient = DaprWorkflowClient()
|
||||||
workflowRuntime.register_workflow(order_processing_workflow)
|
|
||||||
workflowRuntime.register_activity(notify_activity)
|
baseInventory = {
|
||||||
workflowRuntime.register_activity(requst_approval_activity)
|
"paperclip": InventoryItem("Paperclip", 5, 100),
|
||||||
workflowRuntime.register_activity(verify_inventory_activity)
|
"cars": InventoryItem("Cars", 15000, 100),
|
||||||
workflowRuntime.register_activity(process_payment_activity)
|
"computers": InventoryItem("Computers", 500, 100),
|
||||||
workflowRuntime.register_activity(update_inventory_activity)
|
}
|
||||||
workflowRuntime.start()
|
|
||||||
|
|
||||||
daprClient = DaprClient(address=f'{settings.DAPR_RUNTIME_HOST}:{settings.DAPR_GRPC_PORT}')
|
daprClient = DaprClient(address=f'{settings.DAPR_RUNTIME_HOST}:{settings.DAPR_GRPC_PORT}')
|
||||||
baseInventory = {}
|
|
||||||
baseInventory["paperclip"] = InventoryItem("Paperclip", 5, 100)
|
|
||||||
baseInventory["cars"] = InventoryItem("Cars", 15000, 100)
|
|
||||||
baseInventory["computers"] = InventoryItem("Computers", 500, 100)
|
|
||||||
|
|
||||||
self.restock_inventory(daprClient, baseInventory)
|
self.restock_inventory(daprClient, baseInventory)
|
||||||
|
|
||||||
print("==========Begin the purchase of item:==========", flush=True)
|
print("==========Begin the purchase of item:==========", flush=True)
|
||||||
item_name = default_item_name
|
item_name = default_item_name
|
||||||
order_quantity = 10
|
order_quantity = 10
|
||||||
|
|
||||||
total_cost = int(order_quantity) * baseInventory[item_name].per_item_cost
|
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)
|
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)
|
|
||||||
start_resp = daprClient.start_workflow(workflow_component=workflow_component,
|
|
||||||
workflow_name=workflow_name,
|
|
||||||
input=order)
|
|
||||||
_id = start_resp.instance_id
|
|
||||||
|
|
||||||
def prompt_for_approval(daprClient: DaprClient):
|
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.
|
"""This is a helper function to prompt for approval.
|
||||||
Not using the prompt here ACTUALLY, as quickstart validation is required to be automated.
|
Not using the prompt here ACTUALLY, as quickstart validation is required to be automated.
|
||||||
|
|
||||||
|
|
@ -65,9 +59,9 @@ class WorkflowConsoleApp:
|
||||||
if state.runtime_status.name == "COMPLETED":
|
if state.runtime_status.name == "COMPLETED":
|
||||||
return
|
return
|
||||||
if approved.lower() == "y":
|
if approved.lower() == "y":
|
||||||
client.raise_workflow_event(instance_id=_id, event_name="manager_approval", data={'approval': True})
|
wfClient.raise_workflow_event(instance_id=_id, event_name="manager_approval", data={'approval': True})
|
||||||
else:
|
else:
|
||||||
client.raise_workflow_event(instance_id=_id, event_name="manager_approval", data={'approval': False})
|
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:
|
## Additionally, you would need to import signal and define timeout_error:
|
||||||
# import signal
|
# import signal
|
||||||
|
|
@ -76,32 +70,32 @@ class WorkflowConsoleApp:
|
||||||
|
|
||||||
# signal.signal(signal.SIGALRM, timeout_error)
|
# signal.signal(signal.SIGALRM, timeout_error)
|
||||||
"""
|
"""
|
||||||
daprClient.raise_workflow_event(instance_id=_id, workflow_component=workflow_component,
|
wfClient.raise_workflow_event(instance_id=_id, event_name="manager_approval", data={'approval': True})
|
||||||
event_name="manager_approval", event_data={'approval': True})
|
|
||||||
|
|
||||||
approval_seeked = False
|
approval_seeked = False
|
||||||
start_time = datetime.now()
|
start_time = datetime.now()
|
||||||
while True:
|
while True:
|
||||||
time_delta = datetime.now() - start_time
|
time_delta = datetime.now() - start_time
|
||||||
state = daprClient.get_workflow(instance_id=_id, workflow_component=workflow_component)
|
state = wfClient.get_workflow_state(instance_id=_id)
|
||||||
|
|
||||||
if not state:
|
if not state:
|
||||||
print("Workflow not found!") # not expected
|
print("Workflow not found!") # not expected
|
||||||
elif state.runtime_status == "Completed" or\
|
break
|
||||||
state.runtime_status == "Failed" or\
|
|
||||||
state.runtime_status == "Terminated":
|
if state.runtime_status in {WorkflowStatus.COMPLETED, WorkflowStatus.FAILED, WorkflowStatus.TERMINATED}:
|
||||||
print(f'Workflow completed! Result: {state.runtime_status}', flush=True)
|
print(f'Workflow completed! Result: {state.runtime_status}', flush=True)
|
||||||
break
|
break
|
||||||
if time_delta.total_seconds() >= 10:
|
|
||||||
state = daprClient.get_workflow(instance_id=_id, workflow_component=workflow_component)
|
|
||||||
if total_cost > 50000 and (
|
|
||||||
state.runtime_status != "Completed" or
|
|
||||||
state.runtime_status != "Failed" or
|
|
||||||
state.runtime_status != "Terminated"
|
|
||||||
) and not approval_seeked:
|
|
||||||
approval_seeked = True
|
|
||||||
threading.Thread(target=prompt_for_approval(daprClient), daemon=True).start()
|
|
||||||
|
|
||||||
print("Purchase of item is ", state.runtime_status, flush=True)
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
wfr.shutdown()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def restock_inventory(self, daprClient: DaprClient, baseInventory):
|
def restock_inventory(self, daprClient: DaprClient, baseInventory):
|
||||||
for key, item in baseInventory.items():
|
for key, item in baseInventory.items():
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from dapr.ext.workflow import DaprWorkflowContext, WorkflowActivityContext, when_any
|
from dapr.ext.workflow import DaprWorkflowContext, WorkflowActivityContext, WorkflowRuntime, when_any
|
||||||
from dapr.clients import DaprClient
|
from dapr.clients import DaprClient
|
||||||
from dapr.conf import settings
|
from dapr.conf import settings
|
||||||
|
|
||||||
|
|
@ -12,10 +11,13 @@ from model import InventoryItem, Notification, InventoryRequest, OrderPayload, O
|
||||||
|
|
||||||
store_name = "statestore"
|
store_name = "statestore"
|
||||||
|
|
||||||
|
wfr = WorkflowRuntime()
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
|
||||||
def order_processing_workflow(ctx: DaprWorkflowContext, order_payload_str: OrderPayload):
|
@wfr.workflow(name="order_processing_workflow")
|
||||||
|
def order_processing_workflow(ctx: DaprWorkflowContext, order_payload_str: str):
|
||||||
"""Defines the order processing workflow.
|
"""Defines the order processing workflow.
|
||||||
When the order is received, the inventory is checked to see if there is enough inventory to
|
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
|
fulfill the order. If there is enough inventory, the payment is processed and the inventory is
|
||||||
|
|
@ -39,7 +41,7 @@ def order_processing_workflow(ctx: DaprWorkflowContext, order_payload_str: Order
|
||||||
return OrderResult(processed=False)
|
return OrderResult(processed=False)
|
||||||
|
|
||||||
if order_payload["total_cost"] > 50000:
|
if order_payload["total_cost"] > 50000:
|
||||||
yield ctx.call_activity(requst_approval_activity, input=order_payload)
|
yield ctx.call_activity(request_approval_activity, input=order_payload)
|
||||||
approval_task = ctx.wait_for_external_event("manager_approval")
|
approval_task = ctx.wait_for_external_event("manager_approval")
|
||||||
timeout_event = ctx.create_timer(timedelta(seconds=200))
|
timeout_event = ctx.create_timer(timedelta(seconds=200))
|
||||||
winner = yield when_any([approval_task, timeout_event])
|
winner = yield when_any([approval_task, timeout_event])
|
||||||
|
|
@ -76,7 +78,7 @@ def order_processing_workflow(ctx: DaprWorkflowContext, order_payload_str: Order
|
||||||
message=f'Order {order_id} has completed!'))
|
message=f'Order {order_id} has completed!'))
|
||||||
return OrderResult(processed=True)
|
return OrderResult(processed=True)
|
||||||
|
|
||||||
|
@wfr.activity(name="notify_activity")
|
||||||
def notify_activity(ctx: WorkflowActivityContext, input: Notification):
|
def notify_activity(ctx: WorkflowActivityContext, input: Notification):
|
||||||
"""Defines Notify Activity. This is used by the workflow to send out a notification"""
|
"""Defines Notify Activity. This is used by the workflow to send out a notification"""
|
||||||
# Create a logger
|
# Create a logger
|
||||||
|
|
@ -84,7 +86,7 @@ def notify_activity(ctx: WorkflowActivityContext, input: Notification):
|
||||||
logger.info(input.message)
|
logger.info(input.message)
|
||||||
|
|
||||||
|
|
||||||
|
@wfr.activity(name="process_payment_activity")
|
||||||
def process_payment_activity(ctx: WorkflowActivityContext, input: PaymentRequest):
|
def process_payment_activity(ctx: WorkflowActivityContext, input: PaymentRequest):
|
||||||
"""Defines Process Payment Activity.This is used by the workflow to process a payment"""
|
"""Defines Process Payment Activity.This is used by the workflow to process a payment"""
|
||||||
logger = logging.getLogger('ProcessPaymentActivity')
|
logger = logging.getLogger('ProcessPaymentActivity')
|
||||||
|
|
@ -94,6 +96,7 @@ def process_payment_activity(ctx: WorkflowActivityContext, input: PaymentRequest
|
||||||
logger.info(f'Payment for request ID {input.request_id} processed successfully')
|
logger.info(f'Payment for request ID {input.request_id} processed successfully')
|
||||||
|
|
||||||
|
|
||||||
|
@wfr.activity(name="verify_inventory_activity")
|
||||||
def verify_inventory_activity(ctx: WorkflowActivityContext,
|
def verify_inventory_activity(ctx: WorkflowActivityContext,
|
||||||
input: InventoryRequest) -> InventoryResult:
|
input: InventoryRequest) -> InventoryResult:
|
||||||
"""Defines Verify Inventory Activity. This is used by the workflow to verify if inventory
|
"""Defines Verify Inventory Activity. This is used by the workflow to verify if inventory
|
||||||
|
|
@ -117,6 +120,8 @@ def verify_inventory_activity(ctx: WorkflowActivityContext,
|
||||||
return InventoryResult(False, None)
|
return InventoryResult(False, None)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@wfr.activity(name="update_inventory_activity")
|
||||||
def update_inventory_activity(ctx: WorkflowActivityContext,
|
def update_inventory_activity(ctx: WorkflowActivityContext,
|
||||||
input: PaymentRequest) -> InventoryResult:
|
input: PaymentRequest) -> InventoryResult:
|
||||||
"""Defines Update Inventory Activity. This is used by the workflow to check if inventory
|
"""Defines Update Inventory Activity. This is used by the workflow to check if inventory
|
||||||
|
|
@ -139,7 +144,9 @@ def update_inventory_activity(ctx: WorkflowActivityContext,
|
||||||
logger.info(f'There are now {new_quantity} {input.item_being_purchased} left in stock')
|
logger.info(f'There are now {new_quantity} {input.item_being_purchased} left in stock')
|
||||||
|
|
||||||
|
|
||||||
def requst_approval_activity(ctx: WorkflowActivityContext,
|
|
||||||
|
@wfr.activity(name="request_approval_activity")
|
||||||
|
def request_approval_activity(ctx: WorkflowActivityContext,
|
||||||
input: OrderPayload):
|
input: OrderPayload):
|
||||||
"""Defines Request Approval Activity. This is used by the workflow to request approval
|
"""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
|
for payment of an order. This activity is used only if the order total cost is greater than
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue