feat: reorganize examples and add signature support to JobBuilder

- Reorganized examples into osiris/, sal/, and utils/ folders
- Moved hardcoded scripts to separate .rhai files
- Added signature() method to JobBuilder for job signing
- Updated OSIRIS context to use block_in_place instead of runtime
- Removed runtime field from OsirisContext
- Added typed save() methods for Note and Event objects
- Updated all examples to use new structure and APIs
This commit is contained in:
Timur Gordon
2025-10-27 13:49:39 +01:00
parent 90754cc4ac
commit 14ff6cae67
92 changed files with 904 additions and 268 deletions

View File

@@ -346,4 +346,97 @@ impl Client {
Ok(())
}
/// Run a job: dispatch it, wait for completion, and return the result
///
/// This is a convenience method that:
/// 1. Stores the job in Redis
/// 2. Dispatches it to the runner's queue
/// 3. Waits for the job to complete (polls status)
/// 4. Returns the result or error
///
/// # Arguments
/// * `job` - The job to run
/// * `runner_name` - The name of the runner to dispatch to
/// * `timeout_secs` - Maximum time to wait for job completion (in seconds)
///
/// # Returns
/// * `Ok(String)` - The job result if successful
/// * `Err(JobError)` - If the job fails, times out, or encounters an error
pub async fn run_job(
&self,
job: &crate::job::Job,
runner_name: &str,
timeout_secs: u64,
) -> Result<String, JobError> {
use tokio::time::{Duration, timeout};
// Store the job in Redis
self.store_job_in_redis(job).await?;
// Dispatch to runner queue
self.dispatch_job(&job.id, runner_name).await?;
// Wait for job to complete with timeout
let result = timeout(
Duration::from_secs(timeout_secs),
self.wait_for_job_completion(&job.id)
).await;
match result {
Ok(Ok(job_result)) => Ok(job_result),
Ok(Err(e)) => Err(e),
Err(_) => Err(JobError::Timeout(format!(
"Job {} did not complete within {} seconds",
job.id, timeout_secs
))),
}
}
/// Wait for a job to complete by polling its status
///
/// This polls the job status every 500ms until it reaches a terminal state
/// (Finished or Error), then returns the result or error.
async fn wait_for_job_completion(&self, job_id: &str) -> Result<String, JobError> {
use tokio::time::{sleep, Duration};
loop {
// Check job status
let status = self.get_status(job_id).await?;
match status {
JobStatus::Finished => {
// Job completed successfully, get the result
let result = self.get_result(job_id).await?;
return result.ok_or_else(|| {
JobError::InvalidData(format!("Job {} finished but has no result", job_id))
});
}
JobStatus::Error => {
// Job failed, get the error message
let mut conn = self.redis_client
.get_multiplexed_async_connection()
.await
.map_err(|e| JobError::Redis(e))?;
let error_msg: Option<String> = conn
.hget(&self.job_key(job_id), "error")
.await
.map_err(|e| JobError::Redis(e))?;
return Err(JobError::InvalidData(
error_msg.unwrap_or_else(|| format!("Job {} failed with unknown error", job_id))
));
}
JobStatus::Stopping => {
return Err(JobError::InvalidData(format!("Job {} was stopped", job_id)));
}
// Job is still running (Dispatched, WaitingForPrerequisites, Started)
_ => {
// Wait before polling again
sleep(Duration::from_millis(500)).await;
}
}
}
}
}