1use 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#[derive(Serialize, Debug)]
21pub struct DomainCreateRequestData<'a> {
22 #[serde(rename = "xmlns:domain")]
24 pub xmlns: &'a str,
25 #[serde(rename = "domain:name")]
27 pub name: StringValue<'a>,
28 #[serde(rename = "domain:period")]
30 pub period: Period,
31 #[serde(rename = "domain:ns")]
34 pub ns: Option<HostList<'a>>,
35 #[serde(rename = "domain:registrant")]
37 pub registrant: Option<StringValue<'a>>,
38 #[serde(rename = "domain:contact")]
40 pub contacts: Option<&'a [DomainContact<'a>]>,
41 #[serde(rename = "domain:authInfo")]
43 pub auth_info: DomainAuthInfo<'a>,
44}
45
46#[derive(Serialize, Debug)]
47pub struct DomainCreate<'a> {
49 #[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#[derive(Deserialize, Debug)]
83pub struct DomainCreateResponseData {
84 #[serde(rename = "xmlns:domain")]
86 pub xmlns: String,
87 pub name: StringValue<'static>,
89 #[serde(rename = "crDate")]
91 pub created_at: DateTime<Utc>,
92 #[serde(rename = "exDate")]
94 pub expiring_at: Option<DateTime<Utc>>,
95}
96
97#[derive(Deserialize, Debug)]
99pub struct DomainCreateResponse {
100 #[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}