use std::sync::Arc;
use std::time::Duration;
use affinidi_messaging_core::MessageTransport;
use affinidi_messaging_delivery::{MessagingService, OutboxStore};
use affinidi_tdk::messaging::{ATM, profiles::ATMProfile};
use serde_json::Value;
use tracing::warn;
use vti_common::error::AppError;
use vti_common::store::KeyspaceHandle;
use vti_common::trust_task_push::{self, PushContext, PushMessaging};
use crate::server::AppState;
const SWEEP_INTERVAL: Duration = Duration::from_secs(30);
const DRAIN_INTERVAL: Duration = Duration::from_secs(2);
#[cfg(test)]
pub(crate) type PushLog = Arc<std::sync::Mutex<Vec<(String, Value)>>>;
#[cfg(test)]
pub(crate) struct Pushed {
pub recipient_did: String,
pub body: Value,
}
#[cfg(test)]
pub(crate) fn take_pushes(state: &AppState) -> Vec<Pushed> {
std::mem::take(&mut *state.push_log.lock().expect("push log"))
.into_iter()
.map(|(recipient_did, body)| Pushed {
recipient_did,
body,
})
.collect()
}
pub async fn push_trust_task(
state: &AppState,
recipient: &str,
document: Value,
deliver_by: Duration,
) -> Result<String, AppError> {
#[cfg(test)]
state
.push_log
.lock()
.expect("push log")
.push((recipient.to_string(), document.clone()));
let wiring = state.didcomm_bridge.push_wiring();
trust_task_push::push_trust_task(&context(state, &wiring), recipient, document, deliver_by)
.await
}
pub async fn sweep(state: &AppState) -> Result<(), AppError> {
let wiring = state.didcomm_bridge.push_wiring();
trust_task_push::sweep(&context(state, &wiring)).await
}
pub async fn sweep_loop(state: AppState) {
let mut tick = tokio::time::interval(SWEEP_INTERVAL);
loop {
tick.tick().await;
if let Err(e) = sweep(&state).await {
warn!(error = %e, "trust-task push sweep failed; retrying next tick");
}
}
}
fn context<'a>(
state: &'a AppState,
wiring: &'a Option<(Arc<MessagingService>, ATM, String)>,
) -> PushContext<'a> {
PushContext {
records: &state.trust_task_pushes_ks,
outbox: &state.outbox_ks,
resolver: state.did_resolver.as_ref(),
messaging: wiring
.as_ref()
.map(|(service, atm, own_did)| PushMessaging {
service,
atm,
own_did,
}),
tsp: cfg!(feature = "tsp"),
#[cfg(feature = "tsp")]
learned_tsp: Some(&state.tsp_reach),
#[cfg(not(feature = "tsp"))]
learned_tsp: None,
}
}
pub(crate) fn register_transports(
service: &MessagingService,
outbox: Arc<dyn OutboxStore>,
pushes: KeyspaceHandle,
#[cfg_attr(not(feature = "tsp"), allow(unused))] atm: &Arc<ATM>,
#[cfg_attr(not(feature = "tsp"), allow(unused))] profile: &Arc<ATMProfile>,
#[cfg_attr(not(feature = "tsp"), allow(unused))] mediator_did: &str,
) {
#[cfg(feature = "tsp")]
if let Some(primary) = service.primary_transport() {
let tsp: Arc<dyn MessageTransport> = Arc::new(trust_task_push::TspPushTransport {
atm: atm.clone(),
profile: profile.clone(),
mediator_did: mediator_did.to_string(),
pushes: pushes.clone(),
conn: primary.connection_state(),
});
service.add_transport(trust_task_push::TSP_TRANSPORT_ID.into(), tsp.clone());
tokio::spawn(affinidi_messaging_delivery::drain_loop_via(
outbox.clone(),
trust_task_push::TSP_TRANSPORT_ID.into(),
tsp,
DRAIN_INTERVAL,
));
}
let rest: Arc<dyn MessageTransport> = Arc::new(trust_task_push::RestPushTransport::new(
pushes,
vta_sdk::http::foreign_fetch_client(),
));
service.add_transport(trust_task_push::REST_TRANSPORT_ID.into(), rest.clone());
tokio::spawn(affinidi_messaging_delivery::drain_loop_via(
outbox,
trust_task_push::REST_TRANSPORT_ID.into(),
rest,
DRAIN_INTERVAL,
));
}