epp_client/message/
poll.rs

1use crate::common::NoExtension;
2use crate::domain::transfer::DomainTransferResponseData;
3use crate::extensions::low_balance::LowBalance;
4use crate::host::info::HostInfoResponseData;
5use crate::request::{Command, Transaction};
6use serde::{Deserialize, Serialize};
7
8impl<'a> Transaction<NoExtension> for MessagePoll<'a> {}
9
10impl<'a> Command for MessagePoll<'a> {
11    type Response = MessagePollResponse;
12    const COMMAND: &'static str = "poll";
13}
14
15// Request
16
17#[derive(Serialize, Debug)]
18/// Type for EPP XML &lt;poll&gt; command for message poll
19pub struct MessagePoll<'a> {
20    /// The type of operation to perform
21    /// The value is "req" for message polling
22    op: &'a str,
23}
24
25impl Default for MessagePoll<'static> {
26    fn default() -> Self {
27        Self { op: "req" }
28    }
29}
30
31// Response
32
33/// Type that represents the &lt;trnData&gt; tag for message poll response
34#[non_exhaustive]
35#[derive(Deserialize, Debug)]
36pub enum MessageData {
37    /// Data under the &lt;domain:trnData&gt; tag
38    #[serde(rename = "trnData")]
39    DomainTransfer(DomainTransferResponseData),
40    /// Data under the &lt;host:infData&gt; tag
41    #[serde(rename = "infData")]
42    HostInfo(HostInfoResponseData),
43    /// Data under the &lt;lowbalance&gt; tag
44    #[serde(rename = "pollData")]
45    LowBalance(LowBalance),
46}
47
48/// Type that represents the &lt;resData&gt; tag for message poll response
49#[derive(Deserialize, Debug)]
50pub struct MessagePollResponse {
51    /// Data under the &lt;trnData&gt; tag
52    #[serde(rename = "trnData", alias = "infData", alias = "pollData")]
53    pub message_data: MessageData,
54}
55
56#[cfg(test)]
57mod tests {
58    use super::MessagePoll;
59    use crate::message::poll::MessageData;
60    use crate::response::ResultCode;
61    use crate::tests::{assert_serialized, response_from_file, CLTRID, SVTRID};
62
63    use chrono::{TimeZone, Utc};
64    use std::net::IpAddr;
65
66    #[test]
67    fn command() {
68        let object = MessagePoll::default();
69        assert_serialized("request/message/poll.xml", &object);
70    }
71
72    #[test]
73    fn domain_transfer_response() {
74        let object = response_from_file::<MessagePoll>("response/message/poll_domain_transfer.xml");
75        let result = object.res_data().unwrap();
76        let msg = object.message_queue().unwrap();
77
78        assert_eq!(
79            object.result.code,
80            ResultCode::CommandCompletedSuccessfullyAckToDequeue
81        );
82        assert_eq!(
83            object.result.message,
84            "Command completed successfully; ack to dequeue".into()
85        );
86        assert_eq!(msg.count, 5);
87        assert_eq!(msg.id, "12345".to_string());
88        assert_eq!(
89            msg.date,
90            Utc.with_ymd_and_hms(2021, 7, 23, 19, 12, 43).single()
91        );
92        assert_eq!(
93            *(msg.message.as_ref().unwrap()),
94            "Transfer requested.".into()
95        );
96
97        if let MessageData::DomainTransfer(tr) = &result.message_data {
98            assert_eq!(tr.name, "eppdev-transfer.com".into());
99            assert_eq!(tr.transfer_status, "pending".into());
100            assert_eq!(tr.requester_id, "eppdev".into());
101            assert_eq!(
102                tr.requested_at,
103                Utc.with_ymd_and_hms(2021, 7, 23, 15, 31, 21).unwrap()
104            );
105            assert_eq!(tr.ack_id, "ClientY".into());
106            assert_eq!(
107                tr.ack_by,
108                Utc.with_ymd_and_hms(2021, 7, 28, 15, 31, 21).unwrap()
109            );
110            assert_eq!(
111                tr.expiring_at,
112                Utc.with_ymd_and_hms(2022, 7, 2, 14, 53, 19).single()
113            );
114        } else {
115            panic!("Wrong type");
116        }
117
118        assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
119        assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
120    }
121
122    #[test]
123    fn host_info_response() {
124        let object = response_from_file::<MessagePoll>("response/message/poll_host_info.xml");
125        let result = object.res_data().unwrap();
126        let msg = object.message_queue().unwrap();
127
128        assert_eq!(
129            object.result.code,
130            ResultCode::CommandCompletedSuccessfullyAckToDequeue
131        );
132        assert_eq!(
133            object.result.message,
134            "Command completed successfully; ack to dequeue".into()
135        );
136        assert_eq!(msg.count, 4);
137        assert_eq!(msg.id, "12345".to_string());
138        assert_eq!(
139            msg.date,
140            Utc.with_ymd_and_hms(2022, 1, 2, 11, 30, 45).single()
141        );
142        assert_eq!(
143            *(msg.message.as_ref().unwrap()),
144            "Unused objects policy".into()
145        );
146
147        if let MessageData::HostInfo(host) = &result.message_data {
148            assert_eq!(host.name, "ns.test.com".into());
149
150            assert_eq!(host.roid, "1234".into());
151            assert!(host.statuses.iter().any(|s| s.status == "ok"));
152            assert!(host
153                .addresses
154                .iter()
155                .any(|a| a == &IpAddr::from([1, 1, 1, 1])));
156            assert_eq!(host.client_id, "1234".into());
157            assert_eq!(host.creator_id, "user".into());
158            assert_eq!(
159                host.created_at,
160                Utc.with_ymd_and_hms(2021, 12, 1, 22, 40, 48).unwrap()
161            );
162            assert_eq!(host.updater_id, Some("user".into()));
163            assert_eq!(
164                host.updated_at,
165                Utc.with_ymd_and_hms(2021, 12, 1, 22, 40, 48).single()
166            );
167        } else {
168            panic!("Wrong type");
169        }
170
171        assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
172        assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
173    }
174
175    #[test]
176    fn message_only_response() {
177        let object = response_from_file::<MessagePoll>("response/message/poll_message_only.xml");
178        let msg = object.message_queue().unwrap();
179
180        assert_eq!(
181            object.result.code,
182            ResultCode::CommandCompletedSuccessfullyAckToDequeue
183        );
184        assert_eq!(
185            object.result.message,
186            "Command completed successfully; ack to dequeue".into()
187        );
188
189        assert_eq!(msg.count, 4);
190        assert_eq!(msg.id, "12346".to_string());
191        assert_eq!(
192            msg.date,
193            Utc.with_ymd_and_hms(2000, 6, 8, 22, 10, 0).single()
194        );
195        assert_eq!(
196            *(msg.message.as_ref().unwrap()),
197            "Credit balance low.".into()
198        );
199
200        assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
201        assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
202    }
203
204    #[test]
205    fn empty_queue_response() {
206        let object = response_from_file::<MessagePoll>("response/message/poll_empty_queue.xml");
207
208        assert_eq!(
209            object.result.code,
210            ResultCode::CommandCompletedSuccessfullyNoMessages
211        );
212        assert_eq!(
213            object.result.message,
214            "Command completed successfully; no messages".into()
215        );
216
217        assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
218        assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
219    }
220}