instant_epp/
poll.rs

1use instant_xml::{FromXml, ToXml};
2
3use crate::common::{NoExtension, EPP_XMLNS};
4use crate::domain::transfer::TransferData;
5use crate::extensions::low_balance::LowBalance;
6use crate::extensions::rgp::poll::RgpPollData;
7use crate::host::info::InfoData;
8use crate::request::{Command, Transaction};
9
10impl Transaction<NoExtension> for Poll {}
11
12impl Command for Poll {
13    type Response = PollData;
14    const COMMAND: &'static str = "poll";
15}
16
17impl Transaction<NoExtension> for Ack<'_> {}
18
19impl Command for Ack<'_> {
20    type Response = String;
21    const COMMAND: &'static str = "poll";
22}
23
24// Request
25
26/// Type for EPP XML `<poll>` command with `op="req"`
27#[derive(Debug)]
28pub struct Poll;
29
30impl ToXml for Poll {
31    fn serialize<W: std::fmt::Write + ?Sized>(
32        &self,
33        _: Option<instant_xml::Id<'_>>,
34        serializer: &mut instant_xml::Serializer<W>,
35    ) -> Result<(), instant_xml::Error> {
36        serializer.write_start("poll", EPP_XMLNS)?;
37        serializer.write_attr("op", EPP_XMLNS, &"req")?;
38        serializer.end_empty()
39    }
40}
41
42/// Type for EPP XML `<poll>` command with `op="ack"`
43#[derive(Debug)]
44pub struct Ack<'a> {
45    /// The ID of the message to be acknowledged
46    pub message_id: &'a str,
47}
48
49impl ToXml for Ack<'_> {
50    fn serialize<W: std::fmt::Write + ?Sized>(
51        &self,
52        _: Option<instant_xml::Id<'_>>,
53        serializer: &mut instant_xml::Serializer<W>,
54    ) -> Result<(), instant_xml::Error> {
55        serializer.write_start("poll", EPP_XMLNS)?;
56        serializer.write_attr("op", EPP_XMLNS, &"ack")?;
57        serializer.write_attr("msgID", EPP_XMLNS, &self.message_id)?;
58        serializer.end_empty()
59    }
60}
61
62// Response
63
64/// Type that represents the `<trnData>` tag for message poll response
65#[derive(Debug, FromXml)]
66#[xml(forward)]
67pub enum PollData {
68    /// Data under the `<domain:trnData>` tag
69    DomainTransfer(TransferData),
70    /// Data under the `<host:infData>` tag
71    HostInfo(InfoData),
72    /// Data under the `<lowbalance>` tag
73    LowBalance(LowBalance),
74    /// Data under the `<rgp-poll:pollData>` tag
75    RgpPoll(RgpPollData),
76}
77
78#[cfg(test)]
79mod tests {
80    use super::{Ack, Poll, PollData};
81    use crate::host::Status;
82    use crate::response::ResultCode;
83    use crate::tests::{assert_serialized, response_from_file, CLTRID, SUCCESS_MSG, SVTRID};
84
85    use chrono::{TimeZone, Utc};
86    use std::net::IpAddr;
87
88    #[test]
89    fn ack_command() {
90        let object = Ack {
91            message_id: "12345",
92        };
93        assert_serialized("request/poll/ack.xml", &object);
94    }
95
96    #[test]
97    fn response() {
98        let object = response_from_file::<Ack>("response/poll/ack.xml");
99        let msg = object.message_queue().unwrap();
100
101        assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
102        assert_eq!(object.result.message, SUCCESS_MSG);
103        assert_eq!(msg.count, 4);
104        assert_eq!(msg.id, "12345".to_string());
105        assert_eq!(object.tr_ids.server_tr_id, SVTRID);
106    }
107
108    #[test]
109    fn poll_command() {
110        let object = Poll;
111        assert_serialized("request/poll/poll.xml", &object);
112    }
113
114    #[test]
115    fn domain_transfer_response() {
116        let object = response_from_file::<Poll>("response/poll/poll_domain_transfer.xml");
117        let result = object.res_data().unwrap();
118        let msg = object.message_queue().unwrap();
119
120        assert_eq!(
121            object.result.code,
122            ResultCode::CommandCompletedSuccessfullyAckToDequeue
123        );
124        assert_eq!(
125            object.result.message,
126            "Command completed successfully; ack to dequeue"
127        );
128        assert_eq!(msg.count, 5);
129        assert_eq!(msg.id, "12345".to_string());
130        assert_eq!(
131            msg.date,
132            Utc.with_ymd_and_hms(2021, 7, 23, 19, 12, 43).single()
133        );
134        assert_eq!(msg.message.as_ref().unwrap().text, "Transfer requested.");
135
136        if let PollData::DomainTransfer(tr) = &result {
137            assert_eq!(tr.name, "eppdev-transfer.com");
138            assert_eq!(tr.transfer_status, "pending");
139            assert_eq!(tr.requester_id, "eppdev");
140            assert_eq!(
141                tr.requested_at,
142                Utc.with_ymd_and_hms(2021, 7, 23, 15, 31, 21).unwrap()
143            );
144            assert_eq!(tr.ack_id, "ClientY");
145            assert_eq!(
146                tr.ack_by,
147                Utc.with_ymd_and_hms(2021, 7, 28, 15, 31, 21).unwrap()
148            );
149            assert_eq!(
150                tr.expiring_at,
151                Utc.with_ymd_and_hms(2022, 7, 2, 14, 53, 19).single()
152            );
153        } else {
154            panic!("Wrong type");
155        }
156
157        assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
158        assert_eq!(object.tr_ids.server_tr_id, SVTRID);
159    }
160
161    #[test]
162    fn host_info_response() {
163        let object = response_from_file::<Poll>("response/poll/poll_host_info.xml");
164        let result = object.res_data().unwrap();
165        let msg = object.message_queue().unwrap();
166
167        assert_eq!(
168            object.result.code,
169            ResultCode::CommandCompletedSuccessfullyAckToDequeue
170        );
171        assert_eq!(
172            object.result.message,
173            "Command completed successfully; ack to dequeue"
174        );
175        assert_eq!(msg.count, 4);
176        assert_eq!(msg.id, "12345".to_string());
177        assert_eq!(
178            msg.date,
179            Utc.with_ymd_and_hms(2022, 1, 2, 11, 30, 45).single()
180        );
181        assert_eq!(msg.message.as_ref().unwrap().text, "Unused objects policy");
182
183        if let PollData::HostInfo(host) = &result {
184            assert_eq!(host.name, "ns.test.com");
185
186            assert_eq!(host.roid, "1234");
187            assert!(host.statuses.iter().any(|&s| s == Status::Ok));
188            assert!(host
189                .addresses
190                .iter()
191                .any(|a| a == &IpAddr::from([1, 1, 1, 1])));
192            assert_eq!(host.client_id, "1234");
193            assert_eq!(host.creator_id, "user");
194            assert_eq!(
195                host.created_at,
196                Utc.with_ymd_and_hms(2021, 12, 1, 22, 40, 48).unwrap()
197            );
198            assert_eq!(host.updater_id, Some("user".into()));
199            assert_eq!(
200                host.updated_at,
201                Utc.with_ymd_and_hms(2021, 12, 1, 22, 40, 48).single()
202            );
203        } else {
204            panic!("Wrong type");
205        }
206
207        assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
208        assert_eq!(object.tr_ids.server_tr_id, SVTRID);
209    }
210
211    #[test]
212    fn message_only_response() {
213        let object = response_from_file::<Poll>("response/poll/poll_message_only.xml");
214        let msg = object.message_queue().unwrap();
215        dbg!(&msg);
216
217        assert_eq!(
218            object.result.code,
219            ResultCode::CommandCompletedSuccessfullyAckToDequeue
220        );
221        assert_eq!(
222            object.result.message,
223            "Command completed successfully; ack to dequeue"
224        );
225
226        assert_eq!(msg.count, 4);
227        assert_eq!(msg.id, "12346".to_string());
228        assert_eq!(
229            msg.date,
230            Utc.with_ymd_and_hms(2000, 6, 8, 22, 10, 0).single()
231        );
232        assert_eq!(msg.message.as_ref().unwrap().text, "Credit balance low.");
233
234        assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
235        assert_eq!(object.tr_ids.server_tr_id, SVTRID);
236    }
237
238    #[test]
239    fn empty_queue_response() {
240        let object = response_from_file::<Poll>("response/poll/poll_empty_queue.xml");
241
242        assert_eq!(
243            object.result.code,
244            ResultCode::CommandCompletedSuccessfullyNoMessages
245        );
246        assert_eq!(
247            object.result.message,
248            "Command completed successfully; no messages"
249        );
250
251        assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
252        assert_eq!(object.tr_ids.server_tr_id, SVTRID);
253    }
254}