/// Complete End-to-End OSIRIS Example /// /// This example demonstrates the complete OSIRIS workflow: /// 1. Starting the runner_osiris daemon /// 2. Creating jobs with JobBuilder /// 3. Running jobs with run_job() (synchronous) /// 4. Creating OSIRIS objects (Note, Event) /// 5. Storing objects in contexts /// 6. Querying and retrieving data /// 7. Proper cleanup /// /// Prerequisites: /// - Redis running on localhost:6379 /// /// Run with: /// ```bash /// cargo run --example osiris_complete /// ``` use runner_rust::job::{JobBuilder, Client}; use std::time::Duration; use tokio::process::Command; use tokio::time::sleep; #[tokio::main] async fn main() -> Result<(), Box> { println!("šŸš€ OSIRIS Complete End-to-End Example"); println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"); // ======================================================================== // STEP 1: Start the runner daemon // ======================================================================== println!("Step 1: Starting runner_osiris daemon"); println!("─────────────────────────────────────────────────────────────\n"); let mut runner = Command::new("cargo") .args(&[ "run", "--bin", "runner_osiris", "--", "demo_runner", "--db-path", "/tmp/osiris_complete.db", "--redis-url", "redis://localhost:6379", ]) .spawn()?; println!("āœ“ Runner started (PID: {})", runner.id().unwrap_or(0)); println!(" Runner ID: demo_runner"); println!(" Queue: demo_runner"); println!(" DB: /tmp/osiris_complete.db"); // Give the runner time to initialize sleep(Duration::from_secs(2)).await; println!("\n"); // ======================================================================== // STEP 2: Create job client // ======================================================================== println!("Step 2: Creating job client"); println!("─────────────────────────────────────────────────────────────\n"); let client = Client::builder() .redis_url("redis://localhost:6379") .build() .await?; println!("āœ“ Job client created\n"); // ======================================================================== // STEP 3: Create and store a Note // ======================================================================== println!("Step 3: Creating and Storing a Note"); println!("─────────────────────────────────────────────────────────────\n"); let create_note_script = std::fs::read_to_string("examples/osiris/note.rhai")?; let job1 = JobBuilder::new() .caller_id("example_client") .context_id("demo_context") .payload(&create_note_script) .runner("demo_runner") .executor("rhai") .timeout(30) .signature("alice", "") .signature("bob", "") .signature("charlie", "") .build()?; println!("Running job: Create Note"); println!("Job ID: {}", job1.id); match client.run_job(&job1, "demo_runner", 60).await { Ok(result) => { println!("\nāœ… {}\n", result); } Err(e) => { println!("\nāŒ Job failed: {}\n", e); let _ = runner.kill().await; return Err(format!("Job failed: {}", e).into()); } } // ======================================================================== // STEP 4: Create and store an Event // ======================================================================== println!("Step 4: Creating and Storing an Event"); println!("─────────────────────────────────────────────────────────────\n"); let create_event_script = std::fs::read_to_string("examples/osiris/event.rhai")?; let job2 = JobBuilder::new() .caller_id("example_client") .context_id("demo_context") .payload(&create_event_script) .runner("demo_runner") .executor("rhai") .timeout(30) .signature("alice", "") .signature("bob", "") .signature("charlie", "") .build()?; println!("Running job: Create Event"); println!("Job ID: {}", job2.id); match client.run_job(&job2, "demo_runner", 60).await { Ok(result) => { println!("\nāœ… {}\n", result); } Err(e) => { println!("\nāŒ Job failed: {}\n", e); let _ = runner.kill().await; return Err(format!("Job failed: {}", e).into()); } } // ======================================================================== // STEP 5: Query context data // ======================================================================== println!("Step 5: Querying Context Data"); println!("─────────────────────────────────────────────────────────────\n"); let query_script = std::fs::read_to_string("examples/osiris/query.rhai")?; let job3 = JobBuilder::new() .caller_id("example_client") .context_id("demo_context") .payload(&query_script) .runner("demo_runner") .executor("rhai") .timeout(30) .signature("alice", "") .signature("bob", "") .signature("charlie", "") .build()?; println!("Running job: Query Data"); println!("Job ID: {}", job3.id); match client.run_job(&job3, "demo_runner", 60).await { Ok(result) => { println!("\nāœ… {}\n", result); } Err(e) => { println!("\nāŒ Job failed: {}\n", e); let _ = runner.kill().await; return Err(format!("Job failed: {}", e).into()); } } // ======================================================================== // STEP 6: Demonstrate access control // ======================================================================== println!("Step 6: Testing Access Control"); println!("─────────────────────────────────────────────────────────────\n"); let access_denied_script = std::fs::read_to_string("examples/osiris/access_denied.rhai")?; let job4 = JobBuilder::new() .caller_id("example_client") .context_id("demo_context") .payload(&access_denied_script) .runner("demo_runner") .executor("rhai") .timeout(30) .signature("alice", "") .signature("bob", "") .signature("charlie", "") .build()?; println!("Running job: Access Control Test"); println!("Job ID: {}", job4.id); println!("Expected: Access denied\n"); match client.run_job(&job4, "demo_runner", 60).await { Ok(result) => { println!("āŒ Unexpected success: {}\n", result); } Err(e) => { println!("āœ… Access denied as expected"); println!(" Error: {}\n", e); } } // ======================================================================== // STEP 7: Cleanup // ======================================================================== println!("Step 7: Cleanup"); println!("─────────────────────────────────────────────────────────────\n"); println!("Stopping runner..."); runner.kill().await?; println!("āœ“ Runner stopped\n"); // ======================================================================== // Summary // ======================================================================== println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"); println!("šŸŽ‰ Complete Example Finished!\n"); println!("šŸ“ What We Demonstrated:"); println!(" āœ“ Started runner_osiris daemon"); println!(" āœ“ Created job client with builder pattern"); println!(" āœ“ Built jobs with JobBuilder"); println!(" āœ“ Used run_job() for synchronous execution"); println!(" āœ“ Created OSIRIS Note object"); println!(" āœ“ Created OSIRIS Event object"); println!(" āœ“ Stored objects in contexts"); println!(" āœ“ Queried and retrieved data"); println!(" āœ“ Demonstrated participant-based access control"); println!(" āœ“ Proper cleanup and shutdown"); println!("\nšŸ—ļø Architecture:"); println!(" • Contexts defined by participant public keys"); println!(" • At least one participant must be a signatory"); println!(" • No state tracking - contexts created on demand"); println!(" • Jobs executed via Redis queue"); println!(" • Results returned synchronously"); println!("\nšŸ’” Key Features:"); println!(" • Simple API: client.run_job()"); println!(" • Fluent builders: JobBuilder, note(), event()"); println!(" • Automatic waiting and result extraction"); println!(" • Proper error handling and timeouts"); println!(" • Production-ready job infrastructure"); Ok(()) }