Here's the minimal solution for the AI Pizza Shop Dapr Workflow Challenge.
The following diagram illustrates the high-level flow of the OrderProcessingWorkflow
and its child KitchenPreparationWorkflow
:
graph TD
%% Main API entry point
Start[API: POST /order_pizza] --> OrderFlow
%% Main Order Processing Workflow
subgraph OrderFlow[Order Processing Workflow]
direction TB
O_Start([Start]) --> ValidateOrder[1. Validate Order]
ValidateOrder --> SuggestToppings[2. AI Topping Suggestions]
SuggestToppings --> ProcessPayment[3. Process Payment<br>retries: 3 times, 1s delay]
ProcessPayment --> CallKitchen[4. Start Kitchen Workflow]
CallKitchen --> NotifyCustomer[5. Send Confirmation<br>to Customer]
NotifyCustomer --> O_End([Order Complete])
%% Error paths
ValidateOrder -.-> OrderValidationError[❌ Invalid Order<br>Return 400]
ProcessPayment -.-> PaymentError[❌ Payment Failed<br>Return 402]
end
%% Kitchen Workflow - connected to the main flow
CallKitchen --> KitchenFlow
%% Kitchen Preparation Workflow
subgraph KitchenFlow[Kitchen Preparation Workflow]
direction TB
K_Start([Start]) --> ForEachPizza{{For each pizza<br>in order}}
ForEachPizza --> MakeDough[1. Prepare Dough]
MakeDough --> AddToppings[2. Add Toppings]
AddToppings --> StartBaking["3. Start Baking<br>(Log: START_BAKING)"]
StartBaking --> BakeTimer["4. Wait for Baking<br>(Timer: ~12 mins)"]
BakeTimer --> FinishBaking["5. Finish Baking<br>(Log: FINISH_BAKING)"]
FinishBaking --> NextPizza{More pizzas?}
NextPizza -- Yes --> ForEachPizza
NextPizza -- No --> K_End([All Pizzas Ready])
end
%% Styling
classDef process fill:#d4f1f9,stroke:#05386b,stroke-width:1px
classDef special fill:#f9d4f1,stroke:#4b0082,stroke-width:2px
classDef timer fill:#d4f9d4,stroke:#228b22,stroke-width:1px
classDef error fill:#f9d4d4,stroke:#8b0000,stroke-width:1px
classDef loop fill:#f9f9d4,stroke:#8b8b00,stroke-width:1px
%% Apply styles
class ValidateOrder,SuggestToppings,NotifyCustomer,MakeDough,AddToppings,StartBaking,FinishBaking process
class ProcessPayment special
class BakeTimer timer
class OrderValidationError,PaymentError error
class ForEachPizza,NextPizza loop
Get K8 Cluster and Tilt Setup locally. Ensure you are using containerd runtime - if it's docker then open Tiltfile
and comment the nerdctl command at start. It by default uses docker. Now clone and Run the Code
tilt up
Now in Browser Open:
- Tilt UI: http://localhost:10350
- Pizza Workflows Interface: http://localhost:30080/docs
Jaegor can be optionally setup - for it just enable it at end of Tilt File. The config is already there and interface will be at http://localhost:16686/
You can test these either through UI at /docs or cli i.e curl.
Step 1: Start the Pizza Order Workflow
-
Action: Send a
POST
request to the/order_pizza
endpoint. -
Method:
POST
-
URL:
http://localhost:30080/order_pizza
-
Body (JSON):
{ "customer_name": "Ada Lovelace", "pizza_type": "Margherita", "quantity": 1 }
You can also try
quantity: 2
to see the kitchen workflow process multiple items. -
Example using
curl
:curl -X POST -H "Content-Type: application/json" \ -d '{"customer_name": "Ada Lovelace", "pizza_type": "Margherita", "quantity": 1}' \ http://localhost:30080/order_pizza
-
Expected Response: You should receive a JSON response indicating the workflow was scheduled successfully, including an
instance_id
. Note thisinstance_id
(it will look something likepizza-order-xxxxxxxxxxxx
).{ "message": "Pizza order workflow scheduled successfully.", "instance_id": "pizza-order-abcdef123456" }
Step 2: Observe Application Logs
- Action: Monitor the console where you ran the
dapr run ... python main.py
command. - What to look for:
Order WF '<instance_id>': Starting for Ada Lovelace, 1 x Margherita.
- Logs from
ValidateOrderActivity
. - Logs from
AIToppingSuggestionActivity
. Activity (WF '<instance_id>'): Payment attempt 1 failed (simulated).
(and attempt 2)Activity (WF '<instance_id>'): Payment attempt 3 successful...
Kitchen WF '<child_instance_id>': Starting preparation...
- Logs for
MakeDoughActivity
,AddToppingsActivity
. Kitchen WF '<child_instance_id>': Baking Margherita (item 1) for 10s...
- Logs from
BakePizzaActivity
(START_BAKING and FINISH_BAKING). Kitchen WF '<child_instance_id>': All 1 Margherita(s) prepared successfully.
- Logs from
NotifyCustomerActivity
. Order WF '<instance_id>': Order for Ada Lovelace (1x Margherita w/ Fresh Basil) processed successfully.
- The workflow setting custom statuses at various stages.
Step 3: Check Workflow Status
You can check the workflow status using the application's endpoint or the Dapr API directly. Replace <YOUR_INSTANCE_ID>
with the actual ID from Step 1.
-
Method:
GET
- URL:
http://localhost:30080/workflows/<YOUR_INSTANCE_ID>/status
- Example
curl
:curl http://localhost:30080/workflows/pizza-order-abcdef123456/status
- URL:
-
What to look for:
- Initially,
runtimeStatus
might beRUNNING
. - Eventually, it should become
COMPLETED
if everything succeeds. - If an unhandled error occurs (e.g., if validation fails and you send an invalid order), it might be
FAILED
. - You will also see the
customStatus
messages set by the workflow.
- Initially,
Step 4: Observe Payment Retries in Logs
- As noted in Step 2, the logs for
ProcessPaymentActivity
will show the first two attempts failing due to the simulation inmain.py
. - You'll see the Dapr Workflow engine automatically retrying the activity based on the defined
RetryPolicy
. - The third attempt will succeed.
Step 5: Observe Child Workflow and Timer in Logs
- Look for log entries indicating the start and completion of the
KitchenPreparationWorkflow
(the child workflow). It will have its own instance ID, typically related to the parent's ID. - Inside the kitchen workflow logs:
- Observe
MakeDoughActivity
andAddToppingsActivity
logs. - Pay attention to the "Baking ... for 10s..." message. The workflow will pause here due to
ctx.create_timer()
. - After 10 seconds, you'll see the "Finished baking" log.
- If
quantity
was greater than 1, you'll see this dough-toppings-bake sequence repeat.
- Observe
Step 6: (Optional) Test Invalid Order
Try starting a workflow with invalid data to see the validation fail:
- Body (JSON):
{ "customer_name": "Test User", "pizza_type": "", "quantity": 0 }
- Observe:
- The
OrderProcessingWorkflow
should start. ValidateOrderActivity
will raise an error.- The workflow should transition to a
FAILED
state. Check the logs and status endpoint.
- The
Step 7: (Optional) Using Other Management Endpoints
The application includes generic endpoints for managing workflows. You can use these with your instance_id
:
- Pause:
POST /workflows/<YOUR_INSTANCE_ID>/pause
- Resume:
POST /workflows/<YOUR_INSTANCE_ID>/resume
- Terminate:
POST /workflows/<YOUR_INSTANCE_ID>/terminate
- Purge:
POST /workflows/<YOUR_INSTANCE_ID>/purge
(after completion/failure)
While the pizza workflow runs relatively quickly, you could try pausing it during the 10-second baking timer in the KitchenPreparationWorkflow
to see the effect.