instant_epp/domain/
create.rs

1//! Types for EPP domain create request
2
3use chrono::{DateTime, Utc};
4use instant_xml::{FromXml, ToXml};
5
6use super::{DomainAuthInfo, DomainContact, HostInfo, NameServers, Period, XMLNS};
7use crate::common::{NoExtension, EPP_XMLNS};
8use crate::request::{Command, Transaction};
9
10impl<'a> Transaction<NoExtension> for DomainCreate<'a> {}
11
12impl<'a> Command for DomainCreate<'a> {
13    type Response = CreateData;
14    const COMMAND: &'static str = "create";
15}
16
17// Request
18
19/// Type for elements under the domain `<create>` tag
20#[derive(Debug, ToXml)]
21#[xml(rename = "create", ns(XMLNS))]
22pub struct DomainCreateRequestData<'a> {
23    /// The domain name
24    pub name: &'a str,
25    /// The period of registration
26    pub period: Period,
27    /// The list of nameserver hosts
28    /// either of type `HostObjList` or `HostAttrList`
29    pub ns: Option<NameServers<'a>>,
30    /// The domain registrant
31    pub registrant: Option<&'a str>,
32    /// The list of contacts for the domain
33    pub contacts: Option<&'a [DomainContact<'a>]>,
34    /// The auth info for the domain
35    pub auth_info: DomainAuthInfo<'a>,
36}
37
38#[derive(Debug, ToXml)]
39/// Type for EPP XML `<create>` command for domains
40#[xml(rename = "create", ns(EPP_XMLNS))]
41pub struct DomainCreate<'a> {
42    /// The data for the domain to be created with
43    /// T being the type of nameserver list (`HostObjList` or `HostAttrList`)
44    /// to be supplied
45    pub domain: DomainCreateRequestData<'a>,
46}
47
48impl<'a> DomainCreate<'a> {
49    pub fn new(
50        name: &'a str,
51        period: Period,
52        ns: Option<&'a [HostInfo<'a>]>,
53        registrant: Option<&'a str>,
54        auth_password: &'a str,
55        contacts: Option<&'a [DomainContact<'a>]>,
56    ) -> Self {
57        Self {
58            domain: DomainCreateRequestData {
59                name,
60                period,
61                ns: ns.map(|ns| NameServers { ns: ns.into() }),
62                registrant,
63                auth_info: DomainAuthInfo::new(auth_password),
64                contacts,
65            },
66        }
67    }
68}
69
70// Response
71
72/// Type that represents the `<chkData>` tag for domain create response
73#[derive(Debug, FromXml)]
74#[xml(rename = "creData", ns(XMLNS))]
75pub struct CreateData {
76    /// The domain name
77    pub name: String,
78    /// The creation date
79    #[xml(rename = "crDate")]
80    pub created_at: DateTime<Utc>,
81    /// The expiry date
82    #[xml(rename = "exDate")]
83    pub expiring_at: Option<DateTime<Utc>>,
84}
85
86#[cfg(test)]
87mod tests {
88    use std::net::IpAddr;
89
90    use chrono::{TimeZone, Utc};
91
92    use super::{DomainContact, DomainCreate, Period};
93    use crate::domain::{HostAttr, HostInfo, HostObj};
94    use crate::response::ResultCode;
95    use crate::tests::{assert_serialized, response_from_file, CLTRID, SUCCESS_MSG, SVTRID};
96
97    #[test]
98    fn command() {
99        let contacts = &[
100            DomainContact {
101                contact_type: "admin".into(),
102                id: "eppdev-contact-3".into(),
103            },
104            DomainContact {
105                contact_type: "tech".into(),
106                id: "eppdev-contact-3".into(),
107            },
108            DomainContact {
109                contact_type: "billing".into(),
110                id: "eppdev-contact-3".into(),
111            },
112        ];
113
114        let object = DomainCreate::new(
115            "eppdev-1.com",
116            Period::years(1).unwrap(),
117            None,
118            Some("eppdev-contact-3"),
119            "epP4uthd#v",
120            Some(contacts),
121        );
122
123        assert_serialized("request/domain/create.xml", &object);
124    }
125
126    #[test]
127    fn command_with_host_obj() {
128        let contacts = &[
129            DomainContact {
130                contact_type: "admin".into(),
131                id: "eppdev-contact-3".into(),
132            },
133            DomainContact {
134                contact_type: "tech".into(),
135                id: "eppdev-contact-3".into(),
136            },
137            DomainContact {
138                contact_type: "billing".into(),
139                id: "eppdev-contact-3".into(),
140            },
141        ];
142
143        let hosts = &[
144            HostInfo::Obj(HostObj {
145                name: "ns1.test.com".into(),
146            }),
147            HostInfo::Obj(HostObj {
148                name: "ns2.test.com".into(),
149            }),
150        ];
151        let object = DomainCreate::new(
152            "eppdev-1.com",
153            Period::years(1).unwrap(),
154            Some(hosts),
155            Some("eppdev-contact-3"),
156            "epP4uthd#v",
157            Some(contacts),
158        );
159
160        assert_serialized("request/domain/create_with_host_obj.xml", &object);
161    }
162
163    #[test]
164    fn command_with_host_attr() {
165        let contacts = &[
166            DomainContact {
167                contact_type: "admin".into(),
168                id: "eppdev-contact-3".into(),
169            },
170            DomainContact {
171                contact_type: "tech".into(),
172                id: "eppdev-contact-3".into(),
173            },
174            DomainContact {
175                contact_type: "billing".into(),
176                id: "eppdev-contact-3".into(),
177            },
178        ];
179
180        let hosts = &[
181            HostInfo::Attr(HostAttr {
182                name: "ns1.eppdev-1.com".into(),
183                addresses: None,
184            }),
185            HostInfo::Attr(HostAttr {
186                name: "ns2.eppdev-1.com".into(),
187                addresses: Some(vec![
188                    IpAddr::from([177, 232, 12, 58]),
189                    IpAddr::from([0x2404, 0x6800, 0x4001, 0x801, 0, 0, 0, 0x200e]),
190                ]),
191            }),
192        ];
193
194        let object = DomainCreate::new(
195            "eppdev-2.com",
196            Period::years(1).unwrap(),
197            Some(hosts),
198            Some("eppdev-contact-3"),
199            "epP4uthd#v",
200            Some(contacts),
201        );
202
203        assert_serialized("request/domain/create_with_host_attr.xml", &object);
204    }
205
206    #[test]
207    fn response() {
208        let object = response_from_file::<DomainCreate>("response/domain/create.xml");
209
210        let result = object.res_data().unwrap();
211
212        assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
213        assert_eq!(object.result.message, SUCCESS_MSG);
214        assert_eq!(result.name, "eppdev-2.com");
215        assert_eq!(
216            result.created_at,
217            Utc.with_ymd_and_hms(2021, 7, 25, 18, 11, 35).unwrap()
218        );
219        assert_eq!(
220            *result.expiring_at.as_ref().unwrap(),
221            Utc.with_ymd_and_hms(2022, 7, 25, 18, 11, 34).unwrap()
222        );
223        assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
224        assert_eq!(object.tr_ids.server_tr_id, SVTRID);
225    }
226}