use affinidi_messaging_didcomm_service::{
DIDCommServiceError, HandlerContext, TspHandler, TspResponse,
};
use async_trait::async_trait;
use serde_json::Value;
use tracing::{debug, warn};
use did_hosting_common::server::tsp_binding;
use crate::server::AppState;
pub struct ServerTspHandler {
state: AppState,
}
impl ServerTspHandler {
pub fn new(state: AppState) -> Self {
Self { state }
}
}
pub async fn run_tsp_trust_task(
state: &AppState,
sender_vid: &str,
payload: &[u8],
) -> Result<Option<Vec<u8>>, DIDCommServiceError> {
let (document, carriage) = tsp_binding::open(payload);
let doc = match serde_json::from_slice::<trust_tasks_rs::TrustTask<Value>>(&document) {
Ok(doc) => doc,
Err(e) => {
warn!(
sender = %sender_vid,
error = %e,
"inbound TSP: payload is not a trust-task document — dropped"
);
return Ok(None);
}
};
debug!(sender = %sender_vid, type_uri = %doc.type_uri, "inbound TSP: trust task");
match crate::messaging::dispatch_inbound_document(state, Some(sender_vid), doc).await {
Some(reply) => Ok(Some(tsp_binding::frame(
serde_json::to_vec(&reply).map_err(|e| DIDCommServiceError::Internal(e.to_string()))?,
carriage,
))),
None => Ok(None),
}
}
#[async_trait]
impl TspHandler for ServerTspHandler {
async fn handle(
&self,
_ctx: HandlerContext,
payload: Vec<u8>,
sender_vid: String,
) -> Result<Option<TspResponse>, DIDCommServiceError> {
match run_tsp_trust_task(&self.state, &sender_vid, &payload).await? {
Some(frame) => Ok(Some(TspResponse::new(frame))),
None => Ok(None),
}
}
async fn handle_control(
&self,
ctx: HandlerContext,
control: affinidi_tsp::message::control::ControlMessage,
sender_vid: String,
thread_digest: [u8; 32],
) {
did_hosting_common::server::tsp_relationship::answer_inbound_control(
&ctx,
&control,
&sender_vid,
thread_digest,
)
.await
}
}
#[cfg(test)]
mod tests {
use did_hosting_common::didcomm_types::{
MSG_HEALTH_PING, MSG_REPLICA_DOMAIN_ASSIGN, MSG_SERVER_REGISTER_ACK, MSG_STATS_ACK,
MSG_STATS_SYNC, MSG_SYNC_UPDATE,
};
#[test]
fn infra_and_control_plane_ops_are_disjoint() {
use crate::messaging::CONTROL_PLANE_OPS;
use crate::trust_tasks_infra::owns;
assert!(owns(MSG_HEALTH_PING));
assert!(owns(MSG_SERVER_REGISTER_ACK));
assert!(owns(MSG_STATS_ACK));
assert!(!owns(MSG_STATS_SYNC));
for op in CONTROL_PLANE_OPS {
assert!(!owns(op), "{op} is a control-plane op, not an infra op");
}
assert!(CONTROL_PLANE_OPS.contains(&MSG_SYNC_UPDATE));
assert!(CONTROL_PLANE_OPS.contains(&MSG_REPLICA_DOMAIN_ASSIGN));
}
}