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:
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, ®istry)?;
+ job_list::register(&mut m, ®istry)?;
job_status::register(&mut m, ®istry)?;
listing_publish::register(&mut m, ®istry)?;
order_request::register(&mut m, ®istry)?;
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\":[]"));
+ }
}