270 lines
8.3 KiB
Rust
270 lines
8.3 KiB
Rust
//! Examples demonstrating the new job API workflows
|
|
//!
|
|
//! This example shows how to use the new job API methods:
|
|
//! - jobs.create: Create a job without queuing
|
|
//! - jobs.list: List all jobs
|
|
//! - job.run: Run a job and get result immediately
|
|
//! - job.start: Start a created job
|
|
//! - job.status: Get job status (non-blocking)
|
|
//! - job.result: Get job result (blocking)
|
|
|
|
use hero_supervisor_openrpc_client::{SupervisorClient, JobBuilder, JobResult};
|
|
use std::time::Duration;
|
|
use tokio::time::sleep;
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
// Initialize logging
|
|
env_logger::init();
|
|
|
|
println!("🚀 Hero Supervisor Job API Examples");
|
|
println!("===================================\n");
|
|
|
|
// Create client
|
|
let client = SupervisorClient::new("http://localhost:3030")?;
|
|
let secret = "user-secret-456"; // Use a user secret for job operations
|
|
|
|
// Test connection
|
|
println!("📡 Testing connection...");
|
|
match client.discover().await {
|
|
Ok(_) => println!("✅ Connected to supervisor\n"),
|
|
Err(e) => {
|
|
println!("❌ Failed to connect: {}", e);
|
|
println!("Make sure the supervisor is running with: ./supervisor --config examples/supervisor/config.toml\n");
|
|
return Ok(());
|
|
}
|
|
}
|
|
|
|
// Example 1: Fire-and-forget job execution
|
|
println!("🔥 Example 1: Fire-and-forget job execution");
|
|
println!("--------------------------------------------");
|
|
|
|
let job = JobBuilder::new()
|
|
.caller_id("example_client")
|
|
.context_id("fire_and_forget")
|
|
.payload("echo 'Hello from fire-and-forget job!'")
|
|
.executor("osis")
|
|
.runner("osis_runner_1")
|
|
.timeout(30)
|
|
.build()?;
|
|
|
|
println!("Running job immediately...");
|
|
match client.job_run(secret, job).await {
|
|
Ok(JobResult::Success { success }) => {
|
|
println!("✅ Job completed successfully:");
|
|
println!(" Output: {}", success);
|
|
},
|
|
Ok(JobResult::Error { error }) => {
|
|
println!("❌ Job failed:");
|
|
println!(" Error: {}", error);
|
|
},
|
|
Err(e) => {
|
|
println!("❌ API call failed: {}", e);
|
|
}
|
|
}
|
|
println!();
|
|
|
|
// Example 2: Asynchronous job processing
|
|
println!("⏰ Example 2: Asynchronous job processing");
|
|
println!("------------------------------------------");
|
|
|
|
let job = JobBuilder::new()
|
|
.caller_id("example_client")
|
|
.context_id("async_processing")
|
|
.payload("sleep 2 && echo 'Hello from async job!'")
|
|
.executor("osis")
|
|
.runner("osis_runner_1")
|
|
.timeout(60)
|
|
.build()?;
|
|
|
|
// Step 1: Create the job
|
|
println!("1. Creating job...");
|
|
let job_id = match client.jobs_create(secret, job).await {
|
|
Ok(id) => {
|
|
println!("✅ Job created with ID: {}", id);
|
|
id
|
|
},
|
|
Err(e) => {
|
|
println!("❌ Failed to create job: {}", e);
|
|
return Ok(());
|
|
}
|
|
};
|
|
|
|
// Step 2: Start the job
|
|
println!("2. Starting job...");
|
|
match client.job_start(secret, &job_id).await {
|
|
Ok(_) => println!("✅ Job started"),
|
|
Err(e) => {
|
|
println!("❌ Failed to start job: {}", e);
|
|
return Ok(());
|
|
}
|
|
}
|
|
|
|
// Step 3: Poll for completion (non-blocking)
|
|
println!("3. Monitoring job progress...");
|
|
let mut attempts = 0;
|
|
let max_attempts = 30; // 30 seconds max
|
|
|
|
loop {
|
|
attempts += 1;
|
|
|
|
match client.job_status(&job_id).await {
|
|
Ok(status) => {
|
|
println!(" Status: {} (attempt {})", status.status, attempts);
|
|
|
|
if status.status == "completed" || status.status == "failed" || status.status == "timeout" {
|
|
break;
|
|
}
|
|
|
|
if attempts >= max_attempts {
|
|
println!(" ⏰ Timeout waiting for job completion");
|
|
break;
|
|
}
|
|
|
|
sleep(Duration::from_secs(1)).await;
|
|
},
|
|
Err(e) => {
|
|
println!(" ❌ Failed to get job status: {}", e);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Step 4: Get the result
|
|
println!("4. Getting job result...");
|
|
match client.job_result(&job_id).await {
|
|
Ok(JobResult::Success { success }) => {
|
|
println!("✅ Job completed successfully:");
|
|
println!(" Output: {}", success);
|
|
},
|
|
Ok(JobResult::Error { error }) => {
|
|
println!("❌ Job failed:");
|
|
println!(" Error: {}", error);
|
|
},
|
|
Err(e) => {
|
|
println!("❌ Failed to get job result: {}", e);
|
|
}
|
|
}
|
|
println!();
|
|
|
|
// Example 3: Batch job processing
|
|
println!("📦 Example 3: Batch job processing");
|
|
println!("-----------------------------------");
|
|
|
|
let job_specs = vec![
|
|
("echo 'Batch job 1'", "batch_1"),
|
|
("echo 'Batch job 2'", "batch_2"),
|
|
("echo 'Batch job 3'", "batch_3"),
|
|
];
|
|
|
|
let mut job_ids = Vec::new();
|
|
|
|
// Create all jobs
|
|
println!("Creating batch jobs...");
|
|
for (i, (payload, context)) in job_specs.iter().enumerate() {
|
|
let job = JobBuilder::new()
|
|
.caller_id("example_client")
|
|
.context_id(context)
|
|
.payload(payload)
|
|
.executor("osis")
|
|
.runner("osis_runner_1")
|
|
.timeout(30)
|
|
.build()?;
|
|
|
|
match client.jobs_create(secret, job).await {
|
|
Ok(job_id) => {
|
|
println!("✅ Created job {}: {}", i + 1, job_id);
|
|
job_ids.push(job_id);
|
|
},
|
|
Err(e) => {
|
|
println!("❌ Failed to create job {}: {}", i + 1, e);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Start all jobs
|
|
println!("Starting all batch jobs...");
|
|
for (i, job_id) in job_ids.iter().enumerate() {
|
|
match client.job_start(secret, job_id).await {
|
|
Ok(_) => println!("✅ Started job {}", i + 1),
|
|
Err(e) => println!("❌ Failed to start job {}: {}", i + 1, e),
|
|
}
|
|
}
|
|
|
|
// Collect results
|
|
println!("Collecting results...");
|
|
for (i, job_id) in job_ids.iter().enumerate() {
|
|
match client.job_result(job_id).await {
|
|
Ok(JobResult::Success { success }) => {
|
|
println!("✅ Job {} result: {}", i + 1, success);
|
|
},
|
|
Ok(JobResult::Error { error }) => {
|
|
println!("❌ Job {} failed: {}", i + 1, error);
|
|
},
|
|
Err(e) => {
|
|
println!("❌ Failed to get result for job {}: {}", i + 1, e);
|
|
}
|
|
}
|
|
}
|
|
println!();
|
|
|
|
// Example 4: List all jobs
|
|
println!("📋 Example 4: Listing all jobs");
|
|
println!("-------------------------------");
|
|
|
|
match client.jobs_list().await {
|
|
Ok(job_ids) => {
|
|
println!("✅ Found {} jobs in the system:", job_ids.len());
|
|
for (i, job_id) in job_ids.iter().take(10).enumerate() {
|
|
println!(" {}. {}", i + 1, job_id);
|
|
}
|
|
if job_ids.len() > 10 {
|
|
println!(" ... and {} more", job_ids.len() - 10);
|
|
}
|
|
},
|
|
Err(e) => {
|
|
println!("❌ Failed to list jobs: {}", e);
|
|
}
|
|
}
|
|
println!();
|
|
|
|
println!("🎉 All examples completed!");
|
|
println!("\nAPI Convention Summary:");
|
|
println!("- jobs.create: Create job without queuing");
|
|
println!("- jobs.list: List all job IDs");
|
|
println!("- job.run: Run job and return result immediately");
|
|
println!("- job.start: Start a created job");
|
|
println!("- job.status: Get job status (non-blocking)");
|
|
println!("- job.result: Get job result (blocking)");
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_job_builder() {
|
|
let job = JobBuilder::new()
|
|
.caller_id("test")
|
|
.context_id("test")
|
|
.payload("echo 'test'")
|
|
.executor("osis")
|
|
.runner("test_runner")
|
|
.build();
|
|
|
|
assert!(job.is_ok());
|
|
let job = job.unwrap();
|
|
assert_eq!(job.caller_id, "test");
|
|
assert_eq!(job.context_id, "test");
|
|
assert_eq!(job.payload, "echo 'test'");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_client_creation() {
|
|
let client = SupervisorClient::new("http://localhost:3030");
|
|
assert!(client.is_ok());
|
|
}
|
|
}
|