epp_client/domain/
create.rs

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