tap_msg/message/
relationship.rs

1//! Relationship confirmation message types for the Transaction Authorization Protocol.
2//!
3//! This module defines the ConfirmRelationship message type, which is used to confirm
4//! relationships between agents in the TAP protocol.
5
6use crate::didcomm::PlainMessage;
7use crate::error::{Error, Result};
8use crate::impl_tap_message;
9use crate::message::tap_message_trait::TapMessageBody;
10use chrono::Utc;
11use serde::{Deserialize, Serialize};
12
13/// ConfirmRelationship message body (TAIP-9).
14///
15/// This message type allows confirming a relationship between agents.
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct ConfirmRelationship {
18    /// ID of the transaction related to this message.
19    #[serde(rename = "transfer_id")]
20    pub transaction_id: String,
21
22    /// DID of the agent whose relationship is being confirmed.
23    pub agent_id: String,
24
25    /// DID of the entity that the agent acts on behalf of.
26    #[serde(rename = "for")]
27    pub for_id: String,
28
29    /// Role of the agent in the transaction (optional).
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub role: Option<String>,
32}
33
34impl ConfirmRelationship {
35    /// Creates a new ConfirmRelationship message body.
36    pub fn new(transaction_id: &str, agent_id: &str, for_id: &str, role: Option<String>) -> Self {
37        Self {
38            transaction_id: transaction_id.to_string(),
39            agent_id: agent_id.to_string(),
40            for_id: for_id.to_string(),
41            role,
42        }
43    }
44
45    /// Validates the ConfirmRelationship message body.
46    pub fn validate(&self) -> Result<()> {
47        if self.transaction_id.is_empty() {
48            return Err(Error::Validation(
49                "Transfer ID is required in ConfirmRelationship".to_string(),
50            ));
51        }
52
53        if self.agent_id.is_empty() {
54            return Err(Error::Validation(
55                "Agent ID is required in ConfirmRelationship".to_string(),
56            ));
57        }
58
59        if self.for_id.is_empty() {
60            return Err(Error::Validation(
61                "For ID is required in ConfirmRelationship".to_string(),
62            ));
63        }
64
65        Ok(())
66    }
67}
68
69impl TapMessageBody for ConfirmRelationship {
70    fn message_type() -> &'static str {
71        "https://tap.rsvp/schema/1.0#confirmrelationship"
72    }
73
74    fn validate(&self) -> Result<()> {
75        self.validate()
76    }
77
78    fn to_didcomm(&self, from_did: &str) -> Result<PlainMessage> {
79        // 1. Serialize self to JSON value
80        let mut body_json =
81            serde_json::to_value(self).map_err(|e| Error::SerializationError(e.to_string()))?;
82
83        // 2. Add/ensure '@type' field
84        if let Some(body_obj) = body_json.as_object_mut() {
85            body_obj.insert(
86                "@type".to_string(),
87                serde_json::Value::String(Self::message_type().to_string()),
88            );
89            // Note: serde handles #[serde(rename = "for")] automatically during serialization
90        }
91
92        // 3. Generate ID and timestamp
93        let id = uuid::Uuid::new_v4().to_string();
94        let created_time = Utc::now().timestamp_millis() as u64;
95
96        // The from field is required in our PlainMessage
97        let from = from_did.to_string();
98
99        // 4. Create the Message struct with empty recipients (since test expects empty 'to')
100        let message = PlainMessage {
101            id,
102            typ: "application/didcomm-plain+json".to_string(),
103            type_: Self::message_type().to_string(),
104            from,
105            to: vec![],
106            thid: Some(self.transaction_id.clone()),
107            pthid: None,
108            created_time: Some(created_time),
109            expires_time: None,
110            extra_headers: std::collections::HashMap::new(),
111            from_prior: None,
112            body: body_json,
113            attachments: None,
114        };
115
116        Ok(message)
117    }
118}
119
120impl_tap_message!(ConfirmRelationship);