use affinidi_messaging_didcomm::Message;
use affinidi_messaging_didcomm_service::{
DIDCommServiceError, HandlerContext, TspHandler, TspResponse,
};
use async_trait::async_trait;
use serde_json::Value;
use tracing::{debug, warn};
use crate::messaging::dispatch_tsp_message;
use crate::server::AppState;
pub struct ServerTspHandler {
state: AppState,
}
impl ServerTspHandler {
pub fn new(state: AppState) -> Self {
Self { state }
}
}
#[async_trait]
impl TspHandler for ServerTspHandler {
async fn handle(
&self,
_ctx: HandlerContext,
payload: Vec<u8>,
sender_vid: String,
) -> Result<Option<TspResponse>, DIDCommServiceError> {
if let Ok(doc) = serde_json::from_slice::<trust_tasks_rs::TrustTask<Value>>(&payload) {
let type_uri = doc.type_uri.to_string();
if crate::trust_tasks_infra::owns(&type_uri) {
debug!(sender = %sender_vid, %type_uri, "inbound TSP: trust task");
return Ok(
match crate::trust_tasks_infra::dispatch(&self.state, &sender_vid, doc).await {
Some(resp) => Some(TspResponse::new(
serde_json::to_vec(&resp)
.map_err(|e| DIDCommServiceError::Internal(e.to_string()))?,
)),
None => None,
},
);
}
warn!(
sender = %sender_vid,
%type_uri,
"inbound TSP: trust task of a type this server does not implement"
);
return Ok(None);
}
let msg: Message = match serde_json::from_slice(&payload) {
Ok(m) => m,
Err(e) => {
warn!(
sender = %sender_vid,
error = %e,
"TSP: payload is neither a trust task nor a DIDComm Message"
);
return Ok(None);
}
};
debug!(sender = %sender_vid, msg_type = %msg.typ, "inbound TSP: server sync/domain message");
let _ = dispatch_tsp_message(&self.state, &sender_vid, &msg).await;
Ok(None)
}
}
#[cfg(test)]
mod tests {
use super::*;
use did_hosting_common::didcomm_types::{MSG_HEALTH_PING, MSG_SYNC_UPDATE};
use serde_json::json;
#[test]
fn didcomm_message_never_parses_as_a_trust_task() {
let msg = Message::build(
"msg-1".to_string(),
MSG_SYNC_UPDATE.to_string(),
json!({ "mnemonic": "alice", "log_content": "..." }),
)
.finalize();
let bytes = serde_json::to_vec(&msg).expect("message serialises");
let parsed = serde_json::from_slice::<trust_tasks_rs::TrustTask<Value>>(&bytes);
assert!(
parsed.is_err(),
"a DIDComm Message must not deserialise as a TrustTask — the TSP \
sniff depends on it; got {parsed:?}"
);
}
#[test]
fn trust_task_never_parses_as_a_didcomm_message() {
let doc = did_hosting_common::server::trust_tasks::send::build_request(
MSG_HEALTH_PING,
"did:example:control",
"did:example:server",
json!({}),
)
.expect("build request");
let bytes = serde_json::to_vec(&doc).expect("doc serialises");
assert!(
serde_json::from_slice::<Message>(&bytes).is_err(),
"a TrustTask must not deserialise as a DIDComm Message"
);
}
#[test]
fn infra_owns_only_health_ping_and_register_ack() {
use crate::trust_tasks_infra::owns;
use did_hosting_common::didcomm_types::{MSG_DOMAIN_ASSIGN, MSG_SERVER_REGISTER_ACK};
assert!(owns(MSG_HEALTH_PING));
assert!(owns(MSG_SERVER_REGISTER_ACK));
assert!(!owns(MSG_SYNC_UPDATE));
assert!(!owns(MSG_DOMAIN_ASSIGN));
}
}