radrootsd

JSON-RPC bridge for Radroots event publishing
git clone https://radroots.dev/git/radrootsd.git
Log | Files | Refs | README | LICENSE

commit 5dec0c01c8184e04b96ff3eec544d90a1df1880d
parent 007cc6f4fe32e4571b8ddd8f7f953a7b99af5286
Author: triesap <tyson@radroots.org>
Date:   Tue,  7 Apr 2026 06:29:36 +0000

add bridge job list rpc for cli inspection

Diffstat:
Msrc/core/bridge/store.rs | 36++++++++++++++++++++++++++++++++++++
Asrc/transport/jsonrpc/methods/bridge/job_list.rs | 22++++++++++++++++++++++
Msrc/transport/jsonrpc/methods/bridge/mod.rs | 2++
Msrc/transport/jsonrpc/methods/mod.rs | 17+++++++++++++++++
4 files changed, 77 insertions(+), 0 deletions(-)

diff --git a/src/core/bridge/store.rs b/src/core/bridge/store.rs @@ -245,6 +245,16 @@ impl BridgeJobStore { .cloned() } + pub fn list(&self) -> Vec<BridgeJobRecord> { + let inner = self.inner.read().unwrap_or_else(|e| e.into_inner()); + inner + .order + .iter() + .rev() + .filter_map(|job_id| inner.jobs.get(job_id).cloned()) + .collect() + } + pub fn snapshot(&self) -> BridgeJobStoreSnapshot { let inner = self.inner.read().unwrap_or_else(|e| e.into_inner()); let mut accepted_jobs = 0usize; @@ -619,6 +629,32 @@ mod tests { } #[test] + fn list_returns_jobs_in_reverse_insertion_order() { + let store = BridgeJobStore::new(8); + for (job_id, fingerprint) in [("job-1", "fingerprint-1"), ("job-2", "fingerprint-2")] { + let job = new_listing_publish_job( + job_id.to_string(), + None, + "embedded_service_identity".to_string(), + 30402, + Some(format!("event-{job_id}")), + "30402:author:listing".to_string(), + BridgeDeliveryPolicy::Any, + None, + ); + store + .reserve(job, fingerprint.to_string()) + .expect("reserve job"); + } + + let jobs = store.list(); + + assert_eq!(jobs.len(), 2); + assert_eq!(jobs[0].job_id, "job-2"); + assert_eq!(jobs[1].job_id, "job-1"); + } + + #[test] fn reserve_prunes_oldest_jobs_when_capacity_is_exceeded() { let store = BridgeJobStore::new(1); let first = new_listing_publish_job( diff --git a/src/transport/jsonrpc/methods/bridge/job_list.rs b/src/transport/jsonrpc/methods/bridge/job_list.rs @@ -0,0 +1,22 @@ +use anyhow::Result; +use jsonrpsee::server::RpcModule; + +use crate::transport::jsonrpc::auth::require_bridge_auth; +use crate::transport::jsonrpc::methods::bridge::shared::BridgeJobView; +use crate::transport::jsonrpc::{MethodRegistry, RpcContext, RpcError}; + +pub fn register(m: &mut RpcModule<RpcContext>, registry: &MethodRegistry) -> Result<()> { + registry.track("bridge.job.list"); + m.register_async_method("bridge.job.list", |_params, ctx, extensions| async move { + require_bridge_auth(&extensions)?; + let jobs = ctx + .state + .bridge_jobs + .list() + .into_iter() + .map(BridgeJobView::from) + .collect::<Vec<_>>(); + Ok::<Vec<BridgeJobView>, RpcError>(jobs) + })?; + Ok(()) +} diff --git a/src/transport/jsonrpc/methods/bridge/mod.rs b/src/transport/jsonrpc/methods/bridge/mod.rs @@ -3,6 +3,7 @@ use jsonrpsee::server::RpcModule; use crate::transport::jsonrpc::{MethodRegistry, RpcContext}; +mod job_list; mod job_status; mod listing_publish; mod order_request; @@ -13,6 +14,7 @@ mod status; pub fn module(ctx: RpcContext, registry: MethodRegistry) -> Result<RpcModule<RpcContext>> { let mut m = RpcModule::new(ctx); status::register(&mut m, &registry)?; + job_list::register(&mut m, &registry)?; job_status::register(&mut m, &registry)?; listing_publish::register(&mut m, &registry)?; order_request::register(&mut m, &registry)?; diff --git a/src/transport/jsonrpc/methods/mod.rs b/src/transport/jsonrpc/methods/mod.rs @@ -58,6 +58,7 @@ mod tests { register_all(&mut root, ctx, registry).expect("register"); assert!(root.method("bridge.status").is_some()); + assert!(root.method("bridge.job.list").is_some()); assert!(root.method("bridge.job.status").is_some()); assert!(root.method("bridge.listing.publish").is_some()); assert!(root.method("bridge.order.request").is_some()); @@ -139,4 +140,20 @@ mod tests { assert!(response.get().contains("\"failed_jobs\":0")); assert!(response.get().contains("\"recovered_failed_jobs\":0")); } + + #[tokio::test] + async fn bridge_job_list_accepts_authenticated_requests() { + let registry = MethodRegistry::default(); + let ctx = RpcContext::new(state(true, false), registry.clone()); + let mut root = RpcModule::new(ctx.clone()); + root.extensions_mut() + .insert(BridgeAuthorization::Authorized); + register_all(&mut root, ctx, registry).expect("register"); + + let (response, _stream) = root + .raw_json_request(r#"{"jsonrpc":"2.0","method":"bridge.job.list","id":1}"#, 1) + .await + .expect("request"); + assert!(response.get().contains("\"result\":[]")); + } }