- Implemented get_error() method to fetch job error messages from Redis - Mirrors get_result() pattern for consistency - Used by supervisor to retrieve job errors without manual Redis queries - Cleanup: removed old runner_osis directory
249 lines
9.9 KiB
Rust
249 lines
9.9 KiB
Rust
/// 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<dyn std::error::Error>> {
|
|
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(())
|
|
}
|