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#[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#[derive(Debug)]
44pub struct Ack<'a> {
45 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#[derive(Debug, FromXml)]
66#[xml(forward)]
67pub enum PollData {
68 DomainTransfer(TransferData),
70 HostInfo(InfoData),
72 LowBalance(LowBalance),
74 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}