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:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user