use std::sync::Arc;
use affinidi_tdk::didcomm::Message;
use affinidi_tdk::messaging::messages::compat::UnpackMetadata;
use async_trait::async_trait;
use serde::Serialize;
use serde_json::Value;
use tracing::{error, warn};
use trust_tasks_didcomm::ENVELOPE_TYPE;
use trust_tasks_rs::{ErrorResponse, TrustTask};
use uuid::Uuid;
use crate::didcomm::error::DIDCommError;
use crate::didcomm::handlers::{HandlerContext, ProtocolHandler};
use crate::trust_tasks::TaskHandler;
pub struct TrustTasksHandler {
tasks: TaskHandler,
}
impl TrustTasksHandler {
pub fn new(tasks: TaskHandler) -> Self {
Self { tasks }
}
}
fn new_id() -> String {
Uuid::new_v4().to_string()
}
pub async fn route_envelope_body(
tasks: &TaskHandler,
body: Value,
sender_did: Option<&str>,
) -> Option<Result<TrustTask<Value>, ErrorResponse>> {
let doc: TrustTask<Value> = match serde_json::from_value(body) {
Ok(doc) => doc,
Err(e) => {
warn!(
"Dropping malformed Trust Task envelope from {}: {e}",
sender_did.unwrap_or("an unauthenticated sender")
);
return None;
}
};
Some(tasks.handle(doc, sender_did).await)
}
#[async_trait]
impl ProtocolHandler for TrustTasksHandler {
fn get_supported_inbound_message_types(&self) -> Vec<String> {
vec![ENVELOPE_TYPE.to_string()]
}
async fn handle(
&self,
ctx: &Arc<HandlerContext>,
message: Message,
_meta: UnpackMetadata,
) -> Result<(), Box<dyn std::error::Error>> {
let Some(outcome) = route_envelope_body(
&self.tasks,
message.body,
ctx.authenticated_sender.as_deref(),
)
.await
else {
return Ok(());
};
match outcome {
Ok(response) => self.send(ctx, &response).await,
Err(err) => self.send(ctx, &err).await,
}
Ok(())
}
}
impl TrustTasksHandler {
async fn send<T: Serialize>(&self, ctx: &Arc<HandlerContext>, doc: &T) {
if let Err(e) = self.try_send(ctx, doc).await {
error!(
"[profile = {}] Failed to send Trust Task response to {}: {}",
ctx.profile.inner.alias, ctx.sender_did, e
);
}
}
async fn try_send<T: Serialize>(
&self,
ctx: &Arc<HandlerContext>,
doc: &T,
) -> Result<(), Box<dyn std::error::Error>> {
let body = serde_json::to_value(doc)?;
let thread_id = body
.get("threadId")
.and_then(Value::as_str)
.map(str::to_string);
let message_id = new_id();
let mut builder = Message::build(message_id.clone(), ENVELOPE_TYPE.to_string(), body)
.from(ctx.profile.inner.did.clone())
.to(ctx.sender_did.clone());
if let Some(thid) = thread_id {
builder = builder.thid(thid);
}
let envelope = builder.finalize();
let packed = ctx
.atm
.pack_encrypted(
&envelope,
&ctx.sender_did,
Some(&ctx.profile.inner.did),
Some(&ctx.profile.inner.did),
)
.await?;
let mediator = ctx
.profile
.to_tdk_profile()
.mediator
.clone()
.ok_or(DIDCommError::MissingMediator)?;
ctx.atm
.forward_and_send_message(
&ctx.profile,
false,
&packed.0,
Some(&message_id),
&mediator,
&ctx.sender_did,
None,
None,
false,
)
.await?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn envelope_type_is_the_binding_envelope() {
let handler_types = vec![ENVELOPE_TYPE.to_string()];
assert_eq!(
handler_types[0],
"https://trusttasks.org/binding/didcomm/0.1/envelope"
);
}
}