const BASE: &str = "http://127.0.0.1:3000/api/v1";
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let client = reqwest::Client::new();
let start = client
.post(format!("{}/process-instances", BASE))
.json(&serde_json::json!({
"process_def_id": "payment-flow",
"variables": { "amount": "100" }
}))
.send()
.await?;
if !start.status().is_success() {
let body = start.text().await?;
eprintln!("start failed: {}", body);
std::process::exit(1);
}
let start_body: serde_json::Value = start.json().await?;
let instance_id = start_body["instance_id"].as_str().unwrap();
println!("Started instance: {}", instance_id);
let fetch = client
.post(format!("{}/external-tasks/fetch-and-lock", BASE))
.json(&serde_json::json!({
"worker_id": "worker-1",
"task_types": ["payment"],
"max_tasks": 5,
"lock_duration_ms": 30_000
}))
.send()
.await?;
if !fetch.status().is_success() {
eprintln!("fetch_and_lock failed: {}", fetch.text().await?);
std::process::exit(1);
}
let tasks: Vec<serde_json::Value> = fetch.json().await?;
let task_id = tasks.first().and_then(|t| t["task_id"].as_str()).unwrap();
println!("Locked task: {}", task_id);
for i in 1..=2 {
let fail = client
.post(format!("{}/external-tasks/{}/fail", BASE, task_id))
.json(&serde_json::json!({
"worker_id": "worker-1",
"error": format!("retry {}", i)
}))
.send()
.await?;
if !fail.status().is_success() {
eprintln!("fail {} failed: {}", i, fail.text().await?);
std::process::exit(1);
}
println!("Failed (retry {}); task goes back to READY", i);
let fetch2 = client
.post(format!("{}/external-tasks/fetch-and-lock", BASE))
.json(&serde_json::json!({
"worker_id": "worker-1",
"task_types": ["payment"],
"max_tasks": 5,
"lock_duration_ms": 30_000
}))
.send()
.await?;
if !fetch2.status().is_success()
|| fetch2.json::<Vec<serde_json::Value>>().await?.is_empty()
{
eprintln!("fetch after fail {} failed or empty", i);
std::process::exit(1);
}
println!("Locked again after retry {}", i);
}
let complete = client
.post(format!("{}/external-tasks/{}/complete", BASE, task_id))
.json(&serde_json::json!({
"worker_id": "worker-1",
"variables": { "status": "PAID", "amount": "100" }
}))
.send()
.await?;
if !complete.status().is_success() {
eprintln!("complete failed: {}", complete.text().await?);
std::process::exit(1);
}
println!("Completed task.");
let trace = client
.get(format!("{}/process-instances/{}/trace", BASE, instance_id))
.send()
.await?;
if !trace.status().is_success() {
eprintln!("trace failed: {}", trace.text().await?);
std::process::exit(1);
}
let trace_body: serde_json::Value = trace.json().await?;
println!("\n--- Trace (external_task_history) ---");
let ext = trace_body["external_task_history"].as_array().unwrap();
for entry in ext {
let task_id = entry["task_id"].as_str().unwrap_or("");
let events = entry["events"].as_array().unwrap();
println!("Task {}: {} events", task_id, events.len());
for ev in events {
println!(" - {} at {}", ev["event_type"], ev["occurred_at"]);
}
}
println!("\nInstance status: {}", trace_body["instance"]["status"]);
Ok(())
}