1use 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#[derive(Debug, ToXml)]
21#[xml(rename = "create", ns(XMLNS))]
22pub struct DomainCreateRequestData<'a> {
23 pub name: &'a str,
25 pub period: Period,
27 pub ns: Option<NameServers<'a>>,
30 pub registrant: Option<&'a str>,
32 pub contacts: Option<&'a [DomainContact<'a>]>,
34 pub auth_info: DomainAuthInfo<'a>,
36}
37
38#[derive(Debug, ToXml)]
39#[xml(rename = "create", ns(EPP_XMLNS))]
41pub struct DomainCreate<'a> {
42 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#[derive(Debug, FromXml)]
74#[xml(rename = "creData", ns(XMLNS))]
75pub struct CreateData {
76 pub name: String,
78 #[xml(rename = "crDate")]
80 pub created_at: DateTime<Utc>,
81 #[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}