1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6use super::{DomainAuthInfo, DomainContact, HostAttr, XMLNS};
7use crate::common::{NoExtension, ObjectStatus, StringValue};
8use crate::request::{Command, Transaction};
9
10impl<'a> Transaction<NoExtension> for DomainInfo<'a> {}
11
12impl<'a> Command for DomainInfo<'a> {
13 type Response = DomainInfoResponse;
14 const COMMAND: &'static str = "info";
15}
16
17impl<'a> DomainInfo<'a> {
18 pub fn new(name: &'a str, auth_password: Option<&'a str>) -> Self {
19 Self {
20 info: DomainInfoRequestData {
21 xmlns: XMLNS,
22 domain: Domain { hosts: "all", name },
23 auth_info: auth_password.map(|password| DomainAuthInfo {
24 password: password.into(),
25 }),
26 },
27 }
28 }
29}
30
31#[derive(Serialize, Debug)]
35pub struct Domain<'a> {
36 hosts: &'a str,
38 #[serde(rename = "$value")]
40 name: &'a str,
41}
42
43#[derive(Serialize, Debug)]
45pub struct DomainInfoRequestData<'a> {
46 #[serde(rename = "xmlns:domain")]
48 xmlns: &'a str,
49 #[serde(rename = "domain:name")]
51 domain: Domain<'a>,
52 #[serde(rename = "domain:authInfo")]
54 auth_info: Option<DomainAuthInfo<'a>>,
55}
56
57#[derive(Serialize, Debug)]
58pub struct DomainInfo<'a> {
60 #[serde(rename = "domain:info")]
62 info: DomainInfoRequestData<'a>,
63}
64
65#[derive(Deserialize, Debug)]
70pub struct DomainNsList {
71 #[serde(rename = "hostObj")]
73 pub host_obj: Option<Vec<StringValue<'static>>>,
74 pub host_attr: Option<Vec<HostAttr<'static>>>,
76}
77
78#[derive(Deserialize, Debug)]
80pub struct DomainInfoResponseData {
81 pub name: StringValue<'static>,
83 pub roid: StringValue<'static>,
85 #[serde(rename = "status")]
87 pub statuses: Option<Vec<ObjectStatus<'static>>>,
88 pub registrant: Option<StringValue<'static>>,
90 #[serde(rename = "contact")]
92 pub contacts: Option<Vec<DomainContact<'static>>>,
93 #[serde(rename = "ns")]
95 pub ns: Option<DomainNsList>,
96 #[serde(rename = "host")]
98 pub hosts: Option<Vec<StringValue<'static>>>,
99 #[serde(rename = "clID")]
101 pub client_id: StringValue<'static>,
102 #[serde(rename = "crID")]
104 pub creator_id: Option<StringValue<'static>>,
105 #[serde(rename = "crDate")]
107 pub created_at: Option<DateTime<Utc>>,
108 #[serde(rename = "exDate")]
110 pub expiring_at: Option<DateTime<Utc>>,
111 #[serde(rename = "upID")]
113 pub updater_id: Option<StringValue<'static>>,
114 #[serde(rename = "upDate")]
116 pub updated_at: Option<DateTime<Utc>>,
117 #[serde(rename = "trDate")]
119 pub transferred_at: Option<DateTime<Utc>>,
120 #[serde(rename = "authInfo")]
122 pub auth_info: Option<DomainAuthInfo<'static>>,
123}
124
125#[derive(Deserialize, Debug)]
127pub struct DomainInfoResponse {
128 #[serde(rename = "infData")]
130 pub info_data: DomainInfoResponseData,
131}
132
133#[cfg(test)]
134mod tests {
135 use super::DomainInfo;
136 use crate::response::ResultCode;
137 use crate::tests::{assert_serialized, response_from_file, CLTRID, SUCCESS_MSG, SVTRID};
138 use chrono::{TimeZone, Utc};
139
140 #[test]
141 fn command() {
142 let object = DomainInfo::new("eppdev.com", Some("2fooBAR"));
143 assert_serialized("request/domain/info.xml", &object);
144 }
145
146 #[test]
147 fn response() {
148 let object = response_from_file::<DomainInfo>("response/domain/info.xml");
149
150 let result = object.res_data().unwrap();
151 let auth_info = result.info_data.auth_info.as_ref().unwrap();
152 let ns_list = result.info_data.ns.as_ref().unwrap();
153 let ns = ns_list.host_obj.as_ref().unwrap();
154 let hosts = result.info_data.hosts.as_ref().unwrap();
155 let statuses = result.info_data.statuses.as_ref().unwrap();
156 let registrant = result.info_data.registrant.as_ref().unwrap();
157 let contacts = result.info_data.contacts.as_ref().unwrap();
158
159 assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
160 assert_eq!(object.result.message, SUCCESS_MSG.into());
161 assert_eq!(result.info_data.name, "eppdev-1.com".into());
162 assert_eq!(result.info_data.roid, "125899511_DOMAIN_COM-VRSN".into());
163 assert_eq!(statuses[0].status, "ok".to_string());
164 assert_eq!(statuses[1].status, "clientTransferProhibited".to_string());
165 assert_eq!(*registrant, "eppdev-contact-2".into());
166 assert_eq!(contacts[0].id, "eppdev-contact-2".to_string());
167 assert_eq!(contacts[0].contact_type, "admin".to_string());
168 assert_eq!(contacts[1].id, "eppdev-contact-2".to_string());
169 assert_eq!(contacts[1].contact_type, "tech".to_string());
170 assert_eq!(contacts[2].id, "eppdev-contact-2".to_string());
171 assert_eq!(contacts[2].contact_type, "billing".to_string());
172 assert_eq!((*ns)[0], "ns1.eppdev-1.com".into());
173 assert_eq!((*ns)[1], "ns2.eppdev-1.com".into());
174 assert_eq!((*hosts)[0], "ns1.eppdev-1.com".into());
175 assert_eq!((*hosts)[1], "ns2.eppdev-1.com".into());
176 assert_eq!(result.info_data.client_id, "eppdev".into());
177 assert_eq!(
178 *result.info_data.creator_id.as_ref().unwrap(),
179 "SYSTEM".into()
180 );
181 assert_eq!(
182 *result.info_data.created_at.as_ref().unwrap(),
183 Utc.with_ymd_and_hms(2021, 7, 23, 15, 31, 20).unwrap()
184 );
185 assert_eq!(
186 *result.info_data.updater_id.as_ref().unwrap(),
187 "SYSTEM".into()
188 );
189 assert_eq!(
190 *result.info_data.updated_at.as_ref().unwrap(),
191 Utc.with_ymd_and_hms(2021, 7, 23, 15, 31, 21).unwrap()
192 );
193 assert_eq!(
194 *result.info_data.expiring_at.as_ref().unwrap(),
195 Utc.with_ymd_and_hms(2023, 7, 23, 15, 31, 20).unwrap()
196 );
197 assert_eq!(auth_info.password, "epP4uthd#v".into());
198 assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
199 assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
200 }
201
202 #[test]
203 fn response_alt() {
204 response_from_file::<DomainInfo>("response/domain/info_alt.xml");
205 }
206}