Switch from quick-xml to instant-xml
This commit is contained in:
parent
75f1dfe03e
commit
3829ed77fa
|
@ -14,7 +14,7 @@ default = ["tokio-rustls"]
|
||||||
async-trait = "0.1.52"
|
async-trait = "0.1.52"
|
||||||
celes = "2.1"
|
celes = "2.1"
|
||||||
chrono = { version = "0.4.23", features = ["serde"] }
|
chrono = { version = "0.4.23", features = ["serde"] }
|
||||||
quick-xml = { version = "0.26", features = [ "serialize" ] }
|
instant-xml = { version = "0.1", features = ["chrono"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
tokio = { version = "1.0", features = ["io-util", "net", "time"] }
|
tokio = { version = "1.0", features = ["io-util", "net", "time"] }
|
||||||
tokio-rustls = { version = "0.23", optional = true }
|
tokio-rustls = { version = "0.23", optional = true }
|
||||||
|
@ -26,3 +26,4 @@ regex = "1.5"
|
||||||
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
|
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
|
||||||
tokio-test = "0.4"
|
tokio-test = "0.4"
|
||||||
tracing-subscriber = "0.3.3"
|
tracing-subscriber = "0.3.3"
|
||||||
|
similar-asserts = "1.4.2"
|
||||||
|
|
|
@ -18,9 +18,9 @@ use crate::common::{Certificate, NoExtension, PrivateKey};
|
||||||
pub use crate::connection::Connector;
|
pub use crate::connection::Connector;
|
||||||
use crate::connection::{self, EppConnection};
|
use crate::connection::{self, EppConnection};
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::hello::{Greeting, GreetingDocument, HelloDocument};
|
use crate::hello::{Greeting, Hello};
|
||||||
use crate::request::{Command, CommandDocument, Extension, Transaction};
|
use crate::request::{Command, CommandWrapper, Extension, Transaction};
|
||||||
use crate::response::{Response, ResponseDocument, ResponseStatus};
|
use crate::response::{Response, ResponseStatus};
|
||||||
use crate::xml;
|
use crate::xml;
|
||||||
|
|
||||||
/// An `EppClient` provides an interface to sending EPP requests to a registry
|
/// An `EppClient` provides an interface to sending EPP requests to a registry
|
||||||
|
@ -35,9 +35,9 @@ use crate::xml;
|
||||||
/// # use std::net::ToSocketAddrs;
|
/// # use std::net::ToSocketAddrs;
|
||||||
/// # use std::time::Duration;
|
/// # use std::time::Duration;
|
||||||
/// #
|
/// #
|
||||||
/// use epp_client::EppClient;
|
/// use instant_epp::EppClient;
|
||||||
/// use epp_client::domain::DomainCheck;
|
/// use instant_epp::domain::DomainCheck;
|
||||||
/// use epp_client::common::NoExtension;
|
/// use instant_epp::common::NoExtension;
|
||||||
///
|
///
|
||||||
/// # #[tokio::main]
|
/// # #[tokio::main]
|
||||||
/// # async fn main() {
|
/// # async fn main() {
|
||||||
|
@ -55,9 +55,12 @@ use crate::xml;
|
||||||
/// // Execute an EPP Command against the registry with distinct request and response objects
|
/// // Execute an EPP Command against the registry with distinct request and response objects
|
||||||
/// let domain_check = DomainCheck { domains: &["eppdev.com", "eppdev.net"] };
|
/// let domain_check = DomainCheck { domains: &["eppdev.com", "eppdev.net"] };
|
||||||
/// let response = client.transact(&domain_check, "transaction-id").await.unwrap();
|
/// let response = client.transact(&domain_check, "transaction-id").await.unwrap();
|
||||||
/// response.res_data.unwrap().list
|
/// response
|
||||||
|
/// .res_data()
|
||||||
|
/// .unwrap()
|
||||||
|
/// .list
|
||||||
/// .iter()
|
/// .iter()
|
||||||
/// .for_each(|chk| println!("Domain: {}, Available: {}", chk.id, chk.available));
|
/// .for_each(|chk| println!("Domain: {}, Available: {}", chk.inner.id, chk.inner.available));
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
@ -103,13 +106,13 @@ impl<C: Connector> EppClient<C> {
|
||||||
|
|
||||||
/// Executes an EPP Hello call and returns the response as a `Greeting`
|
/// Executes an EPP Hello call and returns the response as a `Greeting`
|
||||||
pub async fn hello(&mut self) -> Result<Greeting, Error> {
|
pub async fn hello(&mut self) -> Result<Greeting, Error> {
|
||||||
let xml = xml::serialize(&HelloDocument::default())?;
|
let xml = xml::serialize(Hello)?;
|
||||||
|
|
||||||
debug!("{}: hello: {}", self.connection.registry, &xml);
|
debug!("{}: hello: {}", self.connection.registry, &xml);
|
||||||
let response = self.connection.transact(&xml)?.await?;
|
let response = self.connection.transact(&xml)?.await?;
|
||||||
debug!("{}: greeting: {}", self.connection.registry, &response);
|
debug!("{}: greeting: {}", self.connection.registry, &response);
|
||||||
|
|
||||||
Ok(xml::deserialize::<GreetingDocument>(&response)?.data)
|
xml::deserialize::<Greeting>(&response)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn transact<'c, 'e, Cmd, Ext>(
|
pub async fn transact<'c, 'e, Cmd, Ext>(
|
||||||
|
@ -122,29 +125,28 @@ impl<C: Connector> EppClient<C> {
|
||||||
Ext: Extension + 'e,
|
Ext: Extension + 'e,
|
||||||
{
|
{
|
||||||
let data = data.into();
|
let data = data.into();
|
||||||
let document = CommandDocument::new(data.command, data.extension, id);
|
let document = CommandWrapper::new(data.command, data.extension, id);
|
||||||
let xml = xml::serialize(&document)?;
|
let xml = xml::serialize(&document)?;
|
||||||
|
|
||||||
debug!("{}: request: {}", self.connection.registry, &xml);
|
debug!("{}: request: {}", self.connection.registry, &xml);
|
||||||
let response = self.connection.transact(&xml)?.await?;
|
let response = self.connection.transact(&xml)?.await?;
|
||||||
debug!("{}: response: {}", self.connection.registry, &response);
|
debug!("{}: response: {}", self.connection.registry, &response);
|
||||||
|
|
||||||
let rsp =
|
let rsp = match xml::deserialize::<Response<Cmd::Response, Ext::Response>>(&response) {
|
||||||
match xml::deserialize::<ResponseDocument<Cmd::Response, Ext::Response>>(&response) {
|
Ok(rsp) => rsp,
|
||||||
Ok(rsp) => rsp,
|
Err(e) => {
|
||||||
Err(e) => {
|
error!(%response, "failed to deserialize response for transaction: {e}");
|
||||||
error!(%response, "failed to deserialize response for transaction: {e}");
|
return Err(e);
|
||||||
return Err(e);
|
}
|
||||||
}
|
};
|
||||||
};
|
|
||||||
|
|
||||||
if rsp.data.result.code.is_success() {
|
if rsp.result.code.is_success() {
|
||||||
return Ok(rsp.data);
|
return Ok(rsp);
|
||||||
}
|
}
|
||||||
|
|
||||||
let err = crate::error::Error::Command(Box::new(ResponseStatus {
|
let err = crate::error::Error::Command(Box::new(ResponseStatus {
|
||||||
result: rsp.data.result,
|
result: rsp.result,
|
||||||
tr_ids: rsp.data.tr_ids,
|
tr_ids: rsp.tr_ids,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
error!(%response, "Failed to deserialize response for transaction: {}", err);
|
error!(%response, "Failed to deserialize response for transaction: {}", err);
|
||||||
|
@ -164,7 +166,7 @@ impl<C: Connector> EppClient<C> {
|
||||||
|
|
||||||
/// Returns the greeting received on establishment of the connection as an `Greeting`
|
/// Returns the greeting received on establishment of the connection as an `Greeting`
|
||||||
pub fn greeting(&self) -> Result<Greeting, Error> {
|
pub fn greeting(&self) -> Result<Greeting, Error> {
|
||||||
xml::deserialize::<GreetingDocument>(&self.connection.greeting).map(|obj| obj.data)
|
xml::deserialize::<Greeting>(&self.connection.greeting)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn reconnect(&mut self) -> Result<(), Error> {
|
pub async fn reconnect(&mut self) -> Result<(), Error> {
|
||||||
|
|
205
src/common.rs
205
src/common.rs
|
@ -1,135 +1,45 @@
|
||||||
//! Common data types included in EPP Requests and Responses
|
//! Common data types included in EPP Requests and Responses
|
||||||
|
|
||||||
use std::ops::Deref;
|
use std::borrow::Cow;
|
||||||
use std::{borrow::Cow, fmt::Display, net::IpAddr};
|
|
||||||
|
|
||||||
use serde::ser::SerializeSeq;
|
use instant_xml::{FromXml, ToXml};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::request::Extension;
|
use crate::request::Extension;
|
||||||
|
|
||||||
pub(crate) const EPP_XMLNS: &str = "urn:ietf:params:xml:ns:epp-1.0";
|
pub(crate) const EPP_XMLNS: &str = "urn:ietf:params:xml:ns:epp-1.0";
|
||||||
|
|
||||||
/// Wraps String for easier serialization to and from values that are inner text
|
#[derive(Debug, Eq, PartialEq, ToXml)]
|
||||||
/// for tags rather than attributes
|
|
||||||
#[derive(Default, Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
|
|
||||||
pub struct StringValue<'a>(Cow<'a, str>);
|
|
||||||
|
|
||||||
impl Deref for StringValue<'_> {
|
|
||||||
type Target = str;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
self.0.as_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> AsRef<str> for StringValue<'a> {
|
|
||||||
fn as_ref(&self) -> &str {
|
|
||||||
self.0.as_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for StringValue<'_> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "{}", self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a str> for StringValue<'a> {
|
|
||||||
fn from(s: &'a str) -> Self {
|
|
||||||
Self(s.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<String> for StringValue<'static> {
|
|
||||||
fn from(s: String) -> Self {
|
|
||||||
Self(s.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
|
||||||
/// An empty placeholder tag. To be refactored to something more compliant later.
|
|
||||||
pub struct NoExtension;
|
pub struct NoExtension;
|
||||||
|
|
||||||
|
impl<'xml> FromXml<'xml> for NoExtension {
|
||||||
|
fn matches(_: instant_xml::Id<'_>, _: Option<instant_xml::Id<'_>>) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<'cx>(
|
||||||
|
_: &mut Self::Accumulator,
|
||||||
|
_: &'static str,
|
||||||
|
_: &mut instant_xml::Deserializer<'cx, 'xml>,
|
||||||
|
) -> Result<(), instant_xml::Error> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
type Accumulator = Option<Self>;
|
||||||
|
const KIND: instant_xml::Kind = instant_xml::Kind::Element;
|
||||||
|
}
|
||||||
|
|
||||||
impl Extension for NoExtension {
|
impl Extension for NoExtension {
|
||||||
type Response = NoExtension;
|
type Response = NoExtension;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type that represents the <name> tag for host check response
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
struct Available {
|
|
||||||
/// The resource name
|
|
||||||
#[serde(rename = "$value")]
|
|
||||||
pub id: StringValue<'static>,
|
|
||||||
/// The resource (un)availability
|
|
||||||
#[serde(rename = "avail")]
|
|
||||||
pub available: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Type that represents the <cd> tag for domain check response
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
struct CheckResponseDataItem {
|
|
||||||
/// Data under the <name> tag
|
|
||||||
#[serde(rename = "name", alias = "id")]
|
|
||||||
pub resource: Available,
|
|
||||||
/// The reason for (un)availability
|
|
||||||
pub reason: Option<StringValue<'static>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Type that represents the <chkData> tag for host check response
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
struct CheckData {
|
|
||||||
/// Data under the <cd> tag
|
|
||||||
#[serde(rename = "cd")]
|
|
||||||
pub list: Vec<CheckResponseDataItem>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Type that represents the <resData> tag for host check response
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
struct DeserializedCheckResponse {
|
|
||||||
/// Data under the <chkData> tag
|
|
||||||
#[serde(rename = "chkData")]
|
|
||||||
pub check_data: CheckData,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Checked {
|
|
||||||
pub id: String,
|
|
||||||
pub available: bool,
|
|
||||||
pub reason: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
#[serde(from = "DeserializedCheckResponse")]
|
|
||||||
pub struct CheckResponse {
|
|
||||||
pub list: Vec<Checked>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<DeserializedCheckResponse> for CheckResponse {
|
|
||||||
fn from(rsp: DeserializedCheckResponse) -> Self {
|
|
||||||
Self {
|
|
||||||
list: rsp
|
|
||||||
.check_data
|
|
||||||
.list
|
|
||||||
.into_iter()
|
|
||||||
.map(|item| Checked {
|
|
||||||
id: item.resource.id.0.into_owned(),
|
|
||||||
available: item.resource.available,
|
|
||||||
reason: item.reason.map(|r| r.0.into_owned()),
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The <option> type in EPP XML login requests
|
/// The <option> type in EPP XML login requests
|
||||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq, ToXml)]
|
||||||
#[serde(rename = "options")]
|
#[xml(rename = "options", ns(EPP_XMLNS))]
|
||||||
pub struct Options<'a> {
|
pub struct Options<'a> {
|
||||||
/// The EPP version being used
|
/// The EPP version being used
|
||||||
pub version: StringValue<'a>,
|
pub version: Cow<'a, str>,
|
||||||
/// The language that will be used during EPP transactions
|
/// The language that will be used during EPP transactions
|
||||||
pub lang: StringValue<'a>,
|
pub lang: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Options<'a> {
|
impl<'a> Options<'a> {
|
||||||
|
@ -143,73 +53,26 @@ impl<'a> Options<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The <svcExtension> type in EPP XML
|
/// The <svcExtension> type in EPP XML
|
||||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq, ToXml)]
|
||||||
#[serde(rename = "svcExtension")]
|
#[xml(rename = "svcExtension", ns(EPP_XMLNS))]
|
||||||
pub struct ServiceExtension<'a> {
|
pub struct ServiceExtension<'a> {
|
||||||
/// The service extension URIs being represented by <extURI> in EPP XML
|
/// The service extension URIs being represented by <extURI> in EPP XML
|
||||||
#[serde(rename = "extURI")]
|
#[xml(rename = "extURI")]
|
||||||
pub ext_uris: Option<Vec<StringValue<'a>>>,
|
pub ext_uris: Option<Vec<Cow<'a, str>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The <svcs> type in EPP XML
|
/// The <svcs> type in EPP XML
|
||||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq, ToXml)]
|
||||||
|
#[xml(rename = "svcs", ns(EPP_XMLNS))]
|
||||||
pub struct Services<'a> {
|
pub struct Services<'a> {
|
||||||
/// The service URIs being used by this EPP session represented by <objURI> in EPP XML
|
/// The service URIs being used by this EPP session represented by <objURI> in EPP XML
|
||||||
#[serde(rename = "objURI")]
|
#[xml(rename = "objURI")]
|
||||||
pub obj_uris: Vec<StringValue<'a>>,
|
pub obj_uris: Vec<Cow<'a, str>>,
|
||||||
/// The <svcExtention> being used in this EPP session
|
// The <svcExtension> being used in this EPP session
|
||||||
#[serde(rename = "svcExtension")]
|
#[xml(rename = "svcExtension")]
|
||||||
pub svc_ext: Option<ServiceExtension<'a>>,
|
pub svc_ext: Option<ServiceExtension<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The <hostAddr> types domain or host transactions
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
pub(crate) struct HostAddr<'a> {
|
|
||||||
#[serde(rename = "ip")]
|
|
||||||
pub ip_version: Option<Cow<'a, str>>,
|
|
||||||
#[serde(rename = "$value")]
|
|
||||||
pub address: Cow<'a, str>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&IpAddr> for HostAddr<'static> {
|
|
||||||
fn from(addr: &IpAddr) -> Self {
|
|
||||||
Self {
|
|
||||||
ip_version: Some(match addr {
|
|
||||||
IpAddr::V4(_) => "v4".into(),
|
|
||||||
IpAddr::V6(_) => "v6".into(),
|
|
||||||
}),
|
|
||||||
address: addr.to_string().into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn serialize_host_addrs_option<T: AsRef<[IpAddr]>, S>(
|
|
||||||
addrs: &Option<T>,
|
|
||||||
ser: S,
|
|
||||||
) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: serde::ser::Serializer,
|
|
||||||
{
|
|
||||||
let addrs = match addrs {
|
|
||||||
Some(addrs) => addrs.as_ref(),
|
|
||||||
None => return ser.serialize_none(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut seq = ser.serialize_seq(Some(addrs.len()))?;
|
|
||||||
for addr in addrs {
|
|
||||||
seq.serialize_element(&HostAddr::from(addr))?;
|
|
||||||
}
|
|
||||||
seq.end()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The <status> type on contact transactions
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
pub struct ObjectStatus<'a> {
|
|
||||||
/// The status name, represented by the 's' attr on <status> tags
|
|
||||||
#[serde(rename = "s")]
|
|
||||||
pub status: Cow<'a, str>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This type contains a single DER-encoded X.509 certificate.
|
/// This type contains a single DER-encoded X.509 certificate.
|
||||||
///
|
///
|
||||||
/// The rustls-pemfile crate can be used to parse a PEM file.
|
/// The rustls-pemfile crate can be used to parse a PEM file.
|
||||||
|
|
135
src/contact.rs
135
src/contact.rs
|
@ -1,9 +1,8 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use instant_xml::{display_to_xml, from_xml_str, FromXml, ToXml};
|
||||||
|
|
||||||
use crate::common::StringValue;
|
|
||||||
|
|
||||||
pub mod check;
|
pub mod check;
|
||||||
pub use check::ContactCheck;
|
pub use check::ContactCheck;
|
||||||
|
@ -22,9 +21,39 @@ pub use update::ContactUpdate;
|
||||||
|
|
||||||
pub const XMLNS: &str = "urn:ietf:params:xml:ns:contact-1.0";
|
pub const XMLNS: &str = "urn:ietf:params:xml:ns:contact-1.0";
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Country(celes::Country);
|
pub struct Country(celes::Country);
|
||||||
|
|
||||||
|
impl<'xml> FromXml<'xml> for Country {
|
||||||
|
fn matches(id: instant_xml::Id<'_>, _: Option<instant_xml::Id<'_>>) -> bool {
|
||||||
|
id == instant_xml::Id {
|
||||||
|
ns: XMLNS,
|
||||||
|
name: "cc",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize<'cx>(
|
||||||
|
into: &mut Self::Accumulator,
|
||||||
|
_: &'static str,
|
||||||
|
deserializer: &mut instant_xml::Deserializer<'cx, 'xml>,
|
||||||
|
) -> Result<(), instant_xml::Error> {
|
||||||
|
from_xml_str(deserializer, into)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Accumulator = Option<Self>;
|
||||||
|
const KIND: instant_xml::Kind = instant_xml::Kind::Scalar;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToXml for Country {
|
||||||
|
fn serialize<W: fmt::Write + ?Sized>(
|
||||||
|
&self,
|
||||||
|
field: Option<instant_xml::Id<'_>>,
|
||||||
|
serializer: &mut instant_xml::Serializer<W>,
|
||||||
|
) -> Result<(), instant_xml::Error> {
|
||||||
|
display_to_xml(&self.0.alpha2, field, serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FromStr for Country {
|
impl FromStr for Country {
|
||||||
type Err = <celes::Country as FromStr>::Err;
|
type Err = <celes::Country as FromStr>::Err;
|
||||||
|
|
||||||
|
@ -42,11 +71,12 @@ impl std::ops::Deref for Country {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The <authInfo> tag for domain and contact transactions
|
/// The <authInfo> tag for domain and contact transactions
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Debug, Clone, FromXml, ToXml)]
|
||||||
|
#[xml(rename = "authInfo", ns(XMLNS))]
|
||||||
pub struct ContactAuthInfo<'a> {
|
pub struct ContactAuthInfo<'a> {
|
||||||
/// The <pw> tag under <authInfo>
|
/// The <pw> tag under <authInfo>
|
||||||
#[serde(rename = "contact:pw", alias = "pw")]
|
#[xml(rename = "pw")]
|
||||||
pub password: StringValue<'a>,
|
pub password: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ContactAuthInfo<'a> {
|
impl<'a> ContactAuthInfo<'a> {
|
||||||
|
@ -58,18 +88,46 @@ impl<'a> ContactAuthInfo<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The data for <voice> and <fax> types on domain transactions
|
/// The data for <voice> types on domain transactions
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Debug, Clone, FromXml, ToXml)]
|
||||||
pub struct Phone<'a> {
|
#[xml(rename = "voice", ns(XMLNS))]
|
||||||
/// The inner text on the <voice> and <fax> tags
|
pub struct Voice<'a> {
|
||||||
#[serde(rename = "$value")]
|
|
||||||
pub number: Cow<'a, str>,
|
|
||||||
/// The value of the 'x' attr on <voice> and <fax> tags
|
/// The value of the 'x' attr on <voice> and <fax> tags
|
||||||
#[serde(rename = "x")]
|
#[xml(rename = "x", attribute)]
|
||||||
pub extension: Option<Cow<'a, str>>,
|
pub extension: Option<Cow<'a, str>>,
|
||||||
|
/// The inner text on the <voice> and <fax> tags
|
||||||
|
#[xml(direct)]
|
||||||
|
pub number: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Phone<'a> {
|
impl<'a> Voice<'a> {
|
||||||
|
/// Creates a new Phone instance with a given phone number
|
||||||
|
pub fn new(number: &'a str) -> Self {
|
||||||
|
Self {
|
||||||
|
extension: None,
|
||||||
|
number: number.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the extension value of the Phone type
|
||||||
|
pub fn set_extension(&mut self, ext: &'a str) {
|
||||||
|
self.extension = Some(ext.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The data for <voice> and <fax> types on domain transactions
|
||||||
|
#[derive(Debug, Clone, FromXml, ToXml)]
|
||||||
|
#[xml(rename = "fax", ns(XMLNS))]
|
||||||
|
pub struct Fax<'a> {
|
||||||
|
/// The value of the 'x' attr on <voice> and <fax> tags
|
||||||
|
#[xml(rename = "x", attribute)]
|
||||||
|
pub extension: Option<Cow<'a, str>>,
|
||||||
|
/// The inner text on the <voice> and <fax> tags
|
||||||
|
#[xml(direct)]
|
||||||
|
pub number: Cow<'a, str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Fax<'a> {
|
||||||
/// Creates a new Phone instance with a given phone number
|
/// Creates a new Phone instance with a given phone number
|
||||||
pub fn new(number: &'a str) -> Self {
|
pub fn new(number: &'a str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -85,22 +143,21 @@ impl<'a> Phone<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The <addr> type on contact transactions
|
/// The <addr> type on contact transactions
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Debug, Clone, FromXml, ToXml)]
|
||||||
|
#[xml(rename = "addr", ns(XMLNS))]
|
||||||
pub struct Address<'a> {
|
pub struct Address<'a> {
|
||||||
/// The <street> tags under <addr>
|
/// The <street> tags under <addr>
|
||||||
#[serde(rename = "contact:street", alias = "street")]
|
pub street: Vec<Cow<'a, str>>,
|
||||||
pub street: Vec<StringValue<'a>>,
|
|
||||||
/// The <city> tag under <addr>
|
/// The <city> tag under <addr>
|
||||||
#[serde(rename = "contact:city", alias = "city")]
|
pub city: Cow<'a, str>,
|
||||||
pub city: StringValue<'a>,
|
|
||||||
/// The <sp> tag under <addr>
|
/// The <sp> tag under <addr>
|
||||||
#[serde(rename = "contact:sp", alias = "sp")]
|
#[xml(rename = "sp")]
|
||||||
pub province: StringValue<'a>,
|
pub province: Cow<'a, str>,
|
||||||
/// The <pc> tag under <addr>
|
/// The <pc> tag under <addr>
|
||||||
#[serde(rename = "contact:pc", alias = "pc")]
|
#[xml(rename = "pc")]
|
||||||
pub postal_code: StringValue<'a>,
|
pub postal_code: Cow<'a, str>,
|
||||||
/// The <cc> tag under <addr>
|
/// The <cc> tag under <addr>
|
||||||
#[serde(rename = "contact:cc", alias = "cc")]
|
#[xml(rename = "cc")]
|
||||||
pub country: Country,
|
pub country: Country,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,35 +183,43 @@ impl<'a> Address<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The <postalInfo> type on contact transactions
|
/// The <postalInfo> type on contact transactions
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Debug, Clone, FromXml, ToXml)]
|
||||||
|
#[xml(rename = "postalInfo", ns(XMLNS))]
|
||||||
pub struct PostalInfo<'a> {
|
pub struct PostalInfo<'a> {
|
||||||
/// The 'type' attr on <postalInfo>
|
/// The 'type' attr on <postalInfo>
|
||||||
#[serde(rename = "type")]
|
#[xml(rename = "type", attribute)]
|
||||||
pub info_type: String,
|
pub info_type: Cow<'a, str>,
|
||||||
/// The <name> tag under <postalInfo>
|
/// The <name> tag under <postalInfo>
|
||||||
#[serde(rename = "contact:name", alias = "name")]
|
pub name: Cow<'a, str>,
|
||||||
pub name: StringValue<'a>,
|
|
||||||
/// The <org> tag under <postalInfo>
|
/// The <org> tag under <postalInfo>
|
||||||
#[serde(rename = "contact:org", alias = "org")]
|
#[xml(rename = "org")]
|
||||||
pub organization: StringValue<'a>,
|
pub organization: Cow<'a, str>,
|
||||||
/// The <addr> tag under <postalInfo>
|
/// The <addr> tag under <postalInfo>
|
||||||
#[serde(rename = "contact:addr", alias = "addr")]
|
|
||||||
pub address: Address<'a>,
|
pub address: Address<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PostalInfo<'a> {
|
impl<'a> PostalInfo<'a> {
|
||||||
/// Creates a new PostalInfo instance
|
/// Creates a new PostalInfo instance
|
||||||
pub fn new(
|
pub fn new(
|
||||||
info_type: &str,
|
info_type: &'a str,
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
organization: &'a str,
|
organization: &'a str,
|
||||||
address: Address<'a>,
|
address: Address<'a>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
info_type: info_type.to_string(),
|
info_type: info_type.into(),
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
organization: organization.into(),
|
organization: organization.into(),
|
||||||
address,
|
address,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The <status> type on contact transactions
|
||||||
|
#[derive(Debug, FromXml, ToXml)]
|
||||||
|
#[xml(rename = "status", ns(XMLNS))]
|
||||||
|
pub struct Status<'a> {
|
||||||
|
/// The status name, represented by the 's' attr on <status> tags
|
||||||
|
#[xml(attribute, rename = "s")]
|
||||||
|
pub status: Cow<'a, str>,
|
||||||
|
}
|
||||||
|
|
|
@ -1,57 +1,72 @@
|
||||||
use std::fmt::Debug;
|
//! Types for EPP contact check request
|
||||||
|
|
||||||
|
use std::fmt::{self, Debug};
|
||||||
|
|
||||||
|
use instant_xml::{FromXml, Serializer, ToXml};
|
||||||
|
|
||||||
/// Types for EPP contact check request
|
|
||||||
use super::XMLNS;
|
use super::XMLNS;
|
||||||
use crate::common::{CheckResponse, NoExtension, StringValue};
|
use crate::common::{NoExtension, EPP_XMLNS};
|
||||||
use crate::request::{Command, Transaction};
|
use crate::request::{Command, Transaction};
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
impl<'a> Transaction<NoExtension> for ContactCheck<'a> {}
|
impl<'a> Transaction<NoExtension> for ContactCheck<'a> {}
|
||||||
|
|
||||||
impl<'a> Command for ContactCheck<'a> {
|
impl<'a> Command for ContactCheck<'a> {
|
||||||
type Response = CheckResponse;
|
type Response = CheckData;
|
||||||
const COMMAND: &'static str = "check";
|
const COMMAND: &'static str = "check";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request
|
// Request
|
||||||
|
|
||||||
/// Type that represents the <check> command for contact transactions
|
/// Type that represents the <check> command for contact transactions
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "check", ns(XMLNS))]
|
||||||
struct ContactList<'a> {
|
struct ContactList<'a> {
|
||||||
/// The XML namespace for the contact <check>
|
|
||||||
#[serde(rename = "xmlns:contact")]
|
|
||||||
xmlns: &'a str,
|
|
||||||
/// The list of contact ids to check for availability
|
/// The list of contact ids to check for availability
|
||||||
#[serde(rename = "contact:id")]
|
id: &'a [&'a str],
|
||||||
contact_ids: Vec<StringValue<'a>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
fn serialize_contacts<W: fmt::Write + ?Sized>(
|
||||||
struct SerializeContactCheck<'a> {
|
ids: &[&str],
|
||||||
/// The <check> tag for the contact check command
|
serializer: &mut Serializer<W>,
|
||||||
#[serde(rename = "contact:check")]
|
) -> Result<(), instant_xml::Error> {
|
||||||
list: ContactList<'a>,
|
ContactList { id: ids }.serialize(None, serializer)
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<ContactCheck<'a>> for SerializeContactCheck<'a> {
|
|
||||||
fn from(check: ContactCheck<'a>) -> Self {
|
|
||||||
Self {
|
|
||||||
list: ContactList {
|
|
||||||
xmlns: XMLNS,
|
|
||||||
contact_ids: check.contact_ids.iter().map(|&id| id.into()).collect(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The EPP `check` command for contacts
|
/// The EPP `check` command for contacts
|
||||||
#[derive(Clone, Debug, Serialize)]
|
#[derive(Clone, Debug, ToXml)]
|
||||||
#[serde(into = "SerializeContactCheck")]
|
#[xml(rename = "check", ns(EPP_XMLNS))]
|
||||||
pub struct ContactCheck<'a> {
|
pub struct ContactCheck<'a> {
|
||||||
/// The list of contact IDs to be checked
|
#[xml(serialize_with = "serialize_contacts")]
|
||||||
pub contact_ids: &'a [&'a str],
|
pub contact_ids: &'a [&'a str],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Response
|
||||||
|
|
||||||
|
#[derive(Debug, FromXml)]
|
||||||
|
#[xml(rename = "id", ns(XMLNS))]
|
||||||
|
pub struct Checked {
|
||||||
|
#[xml(attribute, rename = "avail")]
|
||||||
|
pub available: bool,
|
||||||
|
#[xml(attribute)]
|
||||||
|
pub reason: Option<String>,
|
||||||
|
#[xml(direct)]
|
||||||
|
pub id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, FromXml)]
|
||||||
|
#[xml(rename = "cd", ns(XMLNS))]
|
||||||
|
pub struct CheckedContact {
|
||||||
|
/// Data under the <cd> tag
|
||||||
|
pub inner: Checked,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type that represents the <chkData> tag for host check response
|
||||||
|
#[derive(Debug, FromXml)]
|
||||||
|
#[xml(rename = "chkData", ns(XMLNS))]
|
||||||
|
pub struct CheckData {
|
||||||
|
pub list: Vec<CheckedContact>,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::ContactCheck;
|
use super::ContactCheck;
|
||||||
|
@ -72,12 +87,12 @@ mod tests {
|
||||||
let results = object.res_data().unwrap();
|
let results = object.res_data().unwrap();
|
||||||
|
|
||||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||||
assert_eq!(results.list[0].id, "eppdev-contact-1");
|
assert_eq!(results.list[0].inner.id, "eppdev-contact-1");
|
||||||
assert!(!results.list[0].available);
|
assert!(!results.list[0].inner.available);
|
||||||
assert_eq!(results.list[1].id, "eppdev-contact-2");
|
assert_eq!(results.list[1].inner.id, "eppdev-contact-2");
|
||||||
assert!(results.list[1].available);
|
assert!(results.list[1].inner.available);
|
||||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,53 +1,45 @@
|
||||||
//! Types for EPP contact create request
|
//! Types for EPP contact create request
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use instant_xml::{FromXml, ToXml};
|
||||||
|
|
||||||
use super::{ContactAuthInfo, Phone, PostalInfo, XMLNS};
|
use super::{ContactAuthInfo, Fax, PostalInfo, Voice, XMLNS};
|
||||||
use crate::common::{NoExtension, StringValue};
|
use crate::common::{NoExtension, EPP_XMLNS};
|
||||||
use crate::request::{Command, Transaction};
|
use crate::request::{Command, Transaction};
|
||||||
|
|
||||||
impl<'a> Transaction<NoExtension> for ContactCreate<'a> {}
|
impl<'a> Transaction<NoExtension> for ContactCreate<'a> {}
|
||||||
|
|
||||||
impl<'a> Command for ContactCreate<'a> {
|
impl<'a> Command for ContactCreate<'a> {
|
||||||
type Response = ContactCreateResponse;
|
type Response = ContactCreateData;
|
||||||
const COMMAND: &'static str = "create";
|
const COMMAND: &'static str = "create";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request
|
// Request
|
||||||
|
|
||||||
/// Type for elements under the contact <create> tag
|
/// Type for elements under the contact <create> tag
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
pub struct Contact<'a> {
|
#[xml(rename = "create", ns(XMLNS))]
|
||||||
/// XML namespace for contact commands
|
pub struct ContactCreateRequest<'a> {
|
||||||
#[serde(rename = "xmlns:contact")]
|
|
||||||
xmlns: &'a str,
|
|
||||||
/// Contact <id> tag
|
/// Contact <id> tag
|
||||||
#[serde(rename = "contact:id")]
|
id: &'a str,
|
||||||
id: StringValue<'a>,
|
|
||||||
/// Contact <postalInfo> tag
|
/// Contact <postalInfo> tag
|
||||||
#[serde(rename = "contact:postalInfo")]
|
|
||||||
postal_info: PostalInfo<'a>,
|
postal_info: PostalInfo<'a>,
|
||||||
/// Contact <voice> tag
|
/// Contact <voice> tag
|
||||||
#[serde(rename = "contact:voice")]
|
voice: Voice<'a>,
|
||||||
voice: Phone<'a>,
|
/// Contact <fax> tag,]
|
||||||
/// Contact <fax> tag,
|
fax: Option<Fax<'a>>,
|
||||||
#[serde(rename = "contact:fax")]
|
|
||||||
fax: Option<Phone<'a>>,
|
|
||||||
/// Contact <email> tag
|
/// Contact <email> tag
|
||||||
#[serde(rename = "contact:email")]
|
email: &'a str,
|
||||||
email: StringValue<'a>,
|
|
||||||
/// Contact <authInfo> tag
|
/// Contact <authInfo> tag
|
||||||
#[serde(rename = "contact:authInfo")]
|
|
||||||
auth_info: ContactAuthInfo<'a>,
|
auth_info: ContactAuthInfo<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
|
||||||
/// Type for EPP XML <create> command for contacts
|
/// Type for EPP XML <create> command for contacts
|
||||||
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "create", ns(EPP_XMLNS))]
|
||||||
pub struct ContactCreate<'a> {
|
pub struct ContactCreate<'a> {
|
||||||
/// Data for <create> command for contact
|
/// Data for <create> command for contact
|
||||||
#[serde(rename = "contact:create")]
|
pub contact: ContactCreateRequest<'a>,
|
||||||
pub contact: Contact<'a>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ContactCreate<'a> {
|
impl<'a> ContactCreate<'a> {
|
||||||
|
@ -55,24 +47,23 @@ impl<'a> ContactCreate<'a> {
|
||||||
id: &'a str,
|
id: &'a str,
|
||||||
email: &'a str,
|
email: &'a str,
|
||||||
postal_info: PostalInfo<'a>,
|
postal_info: PostalInfo<'a>,
|
||||||
voice: Phone<'a>,
|
voice: Voice<'a>,
|
||||||
auth_password: &'a str,
|
auth_password: &'a str,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
contact: Contact {
|
contact: ContactCreateRequest {
|
||||||
xmlns: XMLNS,
|
id,
|
||||||
id: id.into(),
|
|
||||||
postal_info,
|
postal_info,
|
||||||
voice,
|
voice,
|
||||||
fax: None,
|
fax: None,
|
||||||
email: email.into(),
|
email,
|
||||||
auth_info: ContactAuthInfo::new(auth_password),
|
auth_info: ContactAuthInfo::new(auth_password),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the <fax> data for the request
|
/// Sets the <fax> data for the request
|
||||||
pub fn set_fax(&mut self, fax: Phone<'a>) {
|
pub fn set_fax(&mut self, fax: Fax<'a>) {
|
||||||
self.contact.fax = Some(fax);
|
self.contact.fax = Some(fax);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,28 +71,21 @@ impl<'a> ContactCreate<'a> {
|
||||||
// Response
|
// Response
|
||||||
|
|
||||||
/// Type that represents the <creData> tag for contact create response
|
/// Type that represents the <creData> tag for contact create response
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Debug, FromXml)]
|
||||||
|
#[xml(rename = "creData", ns(XMLNS))]
|
||||||
pub struct ContactCreateData {
|
pub struct ContactCreateData {
|
||||||
/// The contact id
|
/// The contact id
|
||||||
pub id: StringValue<'static>,
|
pub id: String,
|
||||||
#[serde(rename = "crDate")]
|
#[xml(rename = "crDate")]
|
||||||
/// The contact creation date
|
/// The contact creation date
|
||||||
pub created_at: DateTime<Utc>,
|
pub created_at: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type that represents the <resData> tag for contact create response
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
pub struct ContactCreateResponse {
|
|
||||||
/// Data under the <creData> tag
|
|
||||||
#[serde(rename = "creData")]
|
|
||||||
pub create_data: ContactCreateData,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use chrono::{TimeZone, Utc};
|
use chrono::{TimeZone, Utc};
|
||||||
|
|
||||||
use super::{ContactCreate, Phone, PostalInfo};
|
use super::{ContactCreate, Fax, PostalInfo, Voice};
|
||||||
use crate::contact::Address;
|
use crate::contact::Address;
|
||||||
use crate::response::ResultCode;
|
use crate::response::ResultCode;
|
||||||
use crate::tests::{assert_serialized, response_from_file, CLTRID, SUCCESS_MSG, SVTRID};
|
use crate::tests::{assert_serialized, response_from_file, CLTRID, SUCCESS_MSG, SVTRID};
|
||||||
|
@ -111,9 +95,9 @@ mod tests {
|
||||||
let street = &["58", "Orchid Road"];
|
let street = &["58", "Orchid Road"];
|
||||||
let address = Address::new(street, "Paris", "Paris", "392374", "FR".parse().unwrap());
|
let address = Address::new(street, "Paris", "Paris", "392374", "FR".parse().unwrap());
|
||||||
let postal_info = PostalInfo::new("int", "John Doe", "Acme Widgets", address);
|
let postal_info = PostalInfo::new("int", "John Doe", "Acme Widgets", address);
|
||||||
let mut voice = Phone::new("+33.47237942");
|
let mut voice = Voice::new("+33.47237942");
|
||||||
voice.set_extension("123");
|
voice.set_extension("123");
|
||||||
let mut fax = Phone::new("+33.86698799");
|
let mut fax = Fax::new("+33.86698799");
|
||||||
fax.set_extension("677");
|
fax.set_extension("677");
|
||||||
|
|
||||||
let mut object = ContactCreate::new(
|
let mut object = ContactCreate::new(
|
||||||
|
@ -134,13 +118,13 @@ mod tests {
|
||||||
let results = object.res_data().unwrap();
|
let results = object.res_data().unwrap();
|
||||||
|
|
||||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||||
assert_eq!(results.create_data.id, "eppdev-contact-4".into());
|
assert_eq!(results.id, "eppdev-contact-4");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
results.create_data.created_at,
|
results.created_at,
|
||||||
Utc.with_ymd_and_hms(2021, 7, 25, 16, 5, 32).unwrap(),
|
Utc.with_ymd_and_hms(2021, 7, 25, 16, 5, 32).unwrap(),
|
||||||
);
|
);
|
||||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
//! Types for EPP contact delete request
|
//! Types for EPP contact delete request
|
||||||
|
|
||||||
|
use instant_xml::ToXml;
|
||||||
|
|
||||||
use super::XMLNS;
|
use super::XMLNS;
|
||||||
use crate::common::{NoExtension, StringValue};
|
use crate::common::{NoExtension, EPP_XMLNS};
|
||||||
use crate::request::{Command, Transaction};
|
use crate::request::{Command, Transaction};
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
impl<'a> Transaction<NoExtension> for ContactDelete<'a> {}
|
impl<'a> Transaction<NoExtension> for ContactDelete<'a> {}
|
||||||
|
|
||||||
|
@ -13,31 +14,25 @@ impl<'a> Command for ContactDelete<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type containing the data for the <delete> tag for contacts
|
/// Type containing the data for the <delete> tag for contacts
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
pub struct ContactDeleteRequestData<'a> {
|
#[xml(rename = "delete", ns(XMLNS))]
|
||||||
/// XML namespace for the <delete> command for contacts
|
pub struct ContactDeleteRequest<'a> {
|
||||||
#[serde(rename = "xmlns:contact")]
|
|
||||||
xmlns: &'a str,
|
|
||||||
/// The id of the contact to be deleted
|
/// The id of the contact to be deleted
|
||||||
#[serde(rename = "contact:id")]
|
id: &'a str,
|
||||||
id: StringValue<'a>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
|
||||||
/// The <delete> type for the contact delete EPP command
|
/// The <delete> type for the contact delete EPP command
|
||||||
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "delete", ns(EPP_XMLNS))]
|
||||||
pub struct ContactDelete<'a> {
|
pub struct ContactDelete<'a> {
|
||||||
#[serde(rename = "contact:delete")]
|
|
||||||
/// The data for the <delete> tag for a contact delete command
|
/// The data for the <delete> tag for a contact delete command
|
||||||
contact: ContactDeleteRequestData<'a>,
|
contact: ContactDeleteRequest<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ContactDelete<'a> {
|
impl<'a> ContactDelete<'a> {
|
||||||
pub fn new(id: &'a str) -> Self {
|
pub fn new(id: &'a str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
contact: ContactDeleteRequestData {
|
contact: ContactDeleteRequest { id },
|
||||||
xmlns: XMLNS,
|
|
||||||
id: id.into(),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,8 +53,8 @@ mod tests {
|
||||||
fn response() {
|
fn response() {
|
||||||
let object = response_from_file::<ContactDelete>("response/contact/delete.xml");
|
let object = response_from_file::<ContactDelete>("response/contact/delete.xml");
|
||||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,49 +1,44 @@
|
||||||
//! Types for EPP contact info request
|
//! Types for EPP contact info request
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use instant_xml::{FromXml, ToXml};
|
||||||
|
|
||||||
use super::{ContactAuthInfo, Phone, PostalInfo, XMLNS};
|
use super::{ContactAuthInfo, Fax, PostalInfo, Status, Voice, XMLNS};
|
||||||
use crate::common::{NoExtension, ObjectStatus, StringValue};
|
use crate::common::{NoExtension, EPP_XMLNS};
|
||||||
use crate::request::{Command, Transaction};
|
use crate::request::{Command, Transaction};
|
||||||
|
|
||||||
impl<'a> Transaction<NoExtension> for ContactInfo<'a> {}
|
impl<'a> Transaction<NoExtension> for ContactInfo<'a> {}
|
||||||
|
|
||||||
impl<'a> Command for ContactInfo<'a> {
|
impl<'a> Command for ContactInfo<'a> {
|
||||||
type Response = ContactInfoResponse;
|
type Response = ContactInfoData;
|
||||||
const COMMAND: &'static str = "info";
|
const COMMAND: &'static str = "info";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request
|
// Request
|
||||||
|
|
||||||
/// Type for elements under the contact <info> tag
|
/// Type for elements under the contact <info> tag
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
pub struct ContactInfoRequestData<'a> {
|
#[xml(rename = "info", ns(XMLNS))]
|
||||||
/// XML namespace for contact commands
|
pub struct ContactInfoRequest<'a> {
|
||||||
#[serde(rename = "xmlns:contact")]
|
|
||||||
xmlns: &'a str,
|
|
||||||
/// The contact id for the info command
|
/// The contact id for the info command
|
||||||
#[serde(rename = "contact:id")]
|
id: &'a str,
|
||||||
id: StringValue<'a>,
|
|
||||||
/// The <authInfo> data
|
/// The <authInfo> data
|
||||||
#[serde(rename = "contact:authInfo")]
|
|
||||||
auth_info: ContactAuthInfo<'a>,
|
auth_info: ContactAuthInfo<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
|
||||||
/// Type for EPP XML <info> command for contacts
|
/// Type for EPP XML <info> command for contacts
|
||||||
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "info", ns(EPP_XMLNS))]
|
||||||
pub struct ContactInfo<'a> {
|
pub struct ContactInfo<'a> {
|
||||||
/// Data for <info> command for contact
|
/// Data for <info> command for contact
|
||||||
#[serde(rename = "contact:info")]
|
info: ContactInfoRequest<'a>,
|
||||||
info: ContactInfoRequestData<'a>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ContactInfo<'a> {
|
impl<'a> ContactInfo<'a> {
|
||||||
pub fn new(id: &'a str, auth_password: &'a str) -> ContactInfo<'a> {
|
pub fn new(id: &'a str, auth_password: &'a str) -> ContactInfo<'a> {
|
||||||
Self {
|
Self {
|
||||||
info: ContactInfoRequestData {
|
info: ContactInfoRequest {
|
||||||
xmlns: XMLNS,
|
id,
|
||||||
id: id.into(),
|
|
||||||
auth_info: ContactAuthInfo::new(auth_password),
|
auth_info: ContactAuthInfo::new(auth_password),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -53,53 +48,44 @@ impl<'a> ContactInfo<'a> {
|
||||||
// Response
|
// Response
|
||||||
|
|
||||||
/// Type that represents the <infData> tag for contact check response
|
/// Type that represents the <infData> tag for contact check response
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Debug, FromXml)]
|
||||||
pub struct ContactInfoData<'a> {
|
#[xml(rename = "infData", ns(XMLNS))]
|
||||||
|
pub struct ContactInfoData {
|
||||||
/// The contact id
|
/// The contact id
|
||||||
pub id: StringValue<'a>,
|
pub id: String,
|
||||||
/// The contact ROID
|
/// The contact ROID
|
||||||
pub roid: StringValue<'a>,
|
pub roid: String,
|
||||||
/// The list of contact statuses
|
/// The list of contact statuses
|
||||||
#[serde(rename = "status")]
|
pub statuses: Vec<Status<'static>>,
|
||||||
pub statuses: Vec<ObjectStatus<'a>>,
|
|
||||||
/// The postal info for the contact
|
/// The postal info for the contact
|
||||||
#[serde(rename = "postalInfo")]
|
pub postal_info: PostalInfo<'static>,
|
||||||
pub postal_info: PostalInfo<'a>,
|
|
||||||
/// The voice data for the contact
|
/// The voice data for the contact
|
||||||
pub voice: Phone<'a>,
|
pub voice: Voice<'static>,
|
||||||
/// The fax data for the contact
|
/// The fax data for the contact
|
||||||
pub fax: Option<Phone<'a>>,
|
pub fax: Option<Fax<'static>>,
|
||||||
/// The email for the contact
|
/// The email for the contact
|
||||||
pub email: StringValue<'a>,
|
pub email: String,
|
||||||
/// The epp user to whom the contact belongs
|
/// The epp user to whom the contact belongs
|
||||||
#[serde(rename = "clID")]
|
#[xml(rename = "clID")]
|
||||||
pub client_id: StringValue<'a>,
|
pub client_id: String,
|
||||||
/// The epp user who created the contact
|
/// The epp user who created the contact
|
||||||
#[serde(rename = "crID")]
|
#[xml(rename = "crID")]
|
||||||
pub creator_id: StringValue<'a>,
|
pub creator_id: String,
|
||||||
/// The creation date
|
/// The creation date
|
||||||
#[serde(rename = "crDate")]
|
#[xml(rename = "crDate")]
|
||||||
pub created_at: DateTime<Utc>,
|
pub created_at: DateTime<Utc>,
|
||||||
/// The epp user who last updated the contact
|
/// The epp user who last updated the contact
|
||||||
#[serde(rename = "upID")]
|
#[xml(rename = "upID")]
|
||||||
pub updater_id: Option<StringValue<'a>>,
|
pub updater_id: Option<String>,
|
||||||
/// The last update date
|
/// The last update date
|
||||||
#[serde(rename = "upDate")]
|
#[xml(rename = "upDate")]
|
||||||
pub updated_at: Option<DateTime<Utc>>,
|
pub updated_at: Option<DateTime<Utc>>,
|
||||||
/// The contact transfer date
|
/// The contact transfer date
|
||||||
#[serde(rename = "trDate")]
|
#[xml(rename = "trDate")]
|
||||||
pub transferred_at: Option<DateTime<Utc>>,
|
pub transferred_at: Option<DateTime<Utc>>,
|
||||||
/// The contact auth info
|
/// The contact auth info
|
||||||
#[serde(rename = "authInfo")]
|
#[xml(rename = "authInfo")]
|
||||||
pub auth_info: Option<ContactAuthInfo<'a>>,
|
pub auth_info: Option<ContactAuthInfo<'static>>,
|
||||||
}
|
|
||||||
|
|
||||||
/// Type that represents the <resData> tag for contact info response
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
pub struct ContactInfoResponse {
|
|
||||||
/// Data under the <infData> tag
|
|
||||||
#[serde(rename = "infData")]
|
|
||||||
pub info_data: ContactInfoData<'static>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -121,58 +107,43 @@ mod tests {
|
||||||
let object = response_from_file::<ContactInfo>("response/contact/info.xml");
|
let object = response_from_file::<ContactInfo>("response/contact/info.xml");
|
||||||
|
|
||||||
let result = object.res_data().unwrap();
|
let result = object.res_data().unwrap();
|
||||||
let fax = result.info_data.fax.as_ref().unwrap();
|
let fax = result.fax.as_ref().unwrap();
|
||||||
let voice_ext = result.info_data.voice.extension.as_ref().unwrap();
|
let voice_ext = result.voice.extension.as_ref().unwrap();
|
||||||
let fax_ext = fax.extension.as_ref().unwrap();
|
let fax_ext = fax.extension.as_ref().unwrap();
|
||||||
let auth_info = result.info_data.auth_info.as_ref().unwrap();
|
let auth_info = result.auth_info.as_ref().unwrap();
|
||||||
|
|
||||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||||
assert_eq!(result.info_data.id, "eppdev-contact-3".into());
|
assert_eq!(result.id, "eppdev-contact-3");
|
||||||
assert_eq!(result.info_data.roid, "UNDEF-ROID".into());
|
assert_eq!(result.roid, "UNDEF-ROID");
|
||||||
assert_eq!(result.info_data.statuses[0].status, "ok".to_string());
|
assert_eq!(result.statuses[0].status, "ok");
|
||||||
assert_eq!(result.info_data.postal_info.info_type, "loc".to_string());
|
assert_eq!(result.postal_info.info_type, "loc");
|
||||||
assert_eq!(result.info_data.postal_info.name, "John Doe".into());
|
assert_eq!(result.postal_info.name, "John Doe");
|
||||||
assert_eq!(
|
assert_eq!(result.postal_info.organization, "Acme Widgets");
|
||||||
result.info_data.postal_info.organization,
|
assert_eq!(result.postal_info.address.street[0], "58");
|
||||||
"Acme Widgets".into()
|
assert_eq!(result.postal_info.address.street[1], "Orchid Road");
|
||||||
);
|
assert_eq!(result.postal_info.address.city, "Paris");
|
||||||
assert_eq!(result.info_data.postal_info.address.street[0], "58".into());
|
assert_eq!(result.postal_info.address.province, "Paris");
|
||||||
assert_eq!(
|
assert_eq!(result.postal_info.address.postal_code, "392374");
|
||||||
result.info_data.postal_info.address.street[1],
|
assert_eq!(result.postal_info.address.country.alpha2, "FR");
|
||||||
"Orchid Road".into()
|
assert_eq!(result.voice.number, "+33.47237942".to_string());
|
||||||
);
|
|
||||||
assert_eq!(result.info_data.postal_info.address.city, "Paris".into());
|
|
||||||
assert_eq!(
|
|
||||||
result.info_data.postal_info.address.province,
|
|
||||||
"Paris".into()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
result.info_data.postal_info.address.postal_code,
|
|
||||||
"392374".into()
|
|
||||||
);
|
|
||||||
assert_eq!(result.info_data.postal_info.address.country.alpha2, "FR");
|
|
||||||
assert_eq!(result.info_data.voice.number, "+33.47237942".to_string());
|
|
||||||
assert_eq!(*voice_ext, "123".to_string());
|
assert_eq!(*voice_ext, "123".to_string());
|
||||||
assert_eq!(fax.number, "+33.86698799".to_string());
|
assert_eq!(fax.number, "+33.86698799".to_string());
|
||||||
assert_eq!(*fax_ext, "243".to_string());
|
assert_eq!(*fax_ext, "243".to_string());
|
||||||
assert_eq!(result.info_data.email, "contact@eppdev.net".into());
|
assert_eq!(result.email, "contact@eppdev.net");
|
||||||
assert_eq!(result.info_data.client_id, "eppdev".into());
|
assert_eq!(result.client_id, "eppdev");
|
||||||
assert_eq!(result.info_data.creator_id, "SYSTEM".into());
|
assert_eq!(result.creator_id, "SYSTEM");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result.info_data.created_at,
|
result.created_at,
|
||||||
Utc.with_ymd_and_hms(2021, 7, 23, 13, 9, 9).unwrap(),
|
Utc.with_ymd_and_hms(2021, 7, 23, 13, 9, 9).unwrap(),
|
||||||
);
|
);
|
||||||
|
assert_eq!(*(result.updater_id.as_ref().unwrap()), "SYSTEM");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*(result.info_data.updater_id.as_ref().unwrap()),
|
result.updated_at,
|
||||||
"SYSTEM".into()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
result.info_data.updated_at,
|
|
||||||
Utc.with_ymd_and_hms(2021, 7, 23, 13, 9, 9).single()
|
Utc.with_ymd_and_hms(2021, 7, 23, 13, 9, 9).single()
|
||||||
);
|
);
|
||||||
assert_eq!(auth_info.password, "eppdev-387323".into());
|
assert_eq!(auth_info.password, "eppdev-387323");
|
||||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
//! Types for EPP contact create request
|
//! Types for EPP contact create request
|
||||||
|
|
||||||
use super::{ContactAuthInfo, Phone, PostalInfo, XMLNS};
|
use instant_xml::ToXml;
|
||||||
use crate::common::{NoExtension, ObjectStatus, StringValue};
|
|
||||||
|
use super::{ContactAuthInfo, Fax, PostalInfo, Status, Voice, XMLNS};
|
||||||
|
use crate::common::{NoExtension, EPP_XMLNS};
|
||||||
use crate::request::{Command, Transaction};
|
use crate::request::{Command, Transaction};
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
impl<'a> Transaction<NoExtension> for ContactUpdate<'a> {}
|
impl<'a> Transaction<NoExtension> for ContactUpdate<'a> {}
|
||||||
|
|
||||||
|
@ -15,9 +16,8 @@ impl<'a> Command for ContactUpdate<'a> {
|
||||||
impl<'a> ContactUpdate<'a> {
|
impl<'a> ContactUpdate<'a> {
|
||||||
pub fn new(id: &'a str) -> ContactUpdate {
|
pub fn new(id: &'a str) -> ContactUpdate {
|
||||||
Self {
|
Self {
|
||||||
contact: ContactUpdateRequestData {
|
contact: ContactUpdateRequest {
|
||||||
xmlns: XMLNS,
|
id,
|
||||||
id: id.into(),
|
|
||||||
add_statuses: None,
|
add_statuses: None,
|
||||||
remove_statuses: None,
|
remove_statuses: None,
|
||||||
change_info: None,
|
change_info: None,
|
||||||
|
@ -30,11 +30,11 @@ impl<'a> ContactUpdate<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
email: &'a str,
|
email: &'a str,
|
||||||
postal_info: PostalInfo<'a>,
|
postal_info: PostalInfo<'a>,
|
||||||
voice: Phone<'a>,
|
voice: Voice<'a>,
|
||||||
auth_password: &'a str,
|
auth_password: &'a str,
|
||||||
) {
|
) {
|
||||||
self.contact.change_info = Some(ContactChangeInfo {
|
self.contact.change_info = Some(ContactChangeInfo {
|
||||||
email: Some(email.into()),
|
email: Some(email),
|
||||||
postal_info: Some(postal_info),
|
postal_info: Some(postal_info),
|
||||||
voice: Some(voice),
|
voice: Some(voice),
|
||||||
auth_info: Some(ContactAuthInfo::new(auth_password)),
|
auth_info: Some(ContactAuthInfo::new(auth_password)),
|
||||||
|
@ -43,72 +43,74 @@ impl<'a> ContactUpdate<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the data for the <fax> tag under <chg> for the contact update request
|
/// Sets the data for the <fax> tag under <chg> for the contact update request
|
||||||
pub fn set_fax(&mut self, fax: Phone<'a>) {
|
pub fn set_fax(&mut self, fax: Fax<'a>) {
|
||||||
if let Some(info) = &mut self.contact.change_info {
|
if let Some(info) = &mut self.contact.change_info {
|
||||||
info.fax = Some(fax)
|
info.fax = Some(fax)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the data for the <add> tag for the contact update request
|
/// Sets the data for the <add> tag for the contact update request
|
||||||
pub fn add(&mut self, status: &'a [ObjectStatus]) {
|
pub fn add(&mut self, statuses: &'a [Status]) {
|
||||||
self.contact.add_statuses = Some(StatusList { status });
|
self.contact.add_statuses = Some(AddStatuses { statuses });
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the data for the <rem> tag for the contact update request
|
/// Sets the data for the <rem> tag for the contact update request
|
||||||
pub fn remove(&mut self, status: &'a [ObjectStatus]) {
|
pub fn remove(&mut self, statuses: &'a [Status]) {
|
||||||
self.contact.remove_statuses = Some(StatusList { status });
|
self.contact.remove_statuses = Some(RemoveStatuses { statuses });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type for elements under the <chg> tag for contact update request
|
/// Type for elements under the <chg> tag for contact update request
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "chg", ns(XMLNS))]
|
||||||
pub struct ContactChangeInfo<'a> {
|
pub struct ContactChangeInfo<'a> {
|
||||||
#[serde(rename = "contact:postalInfo")]
|
|
||||||
postal_info: Option<PostalInfo<'a>>,
|
postal_info: Option<PostalInfo<'a>>,
|
||||||
#[serde(rename = "contact:voice")]
|
voice: Option<Voice<'a>>,
|
||||||
voice: Option<Phone<'a>>,
|
fax: Option<Fax<'a>>,
|
||||||
#[serde(rename = "contact:fax")]
|
email: Option<&'a str>,
|
||||||
fax: Option<Phone<'a>>,
|
|
||||||
#[serde(rename = "contact:email")]
|
|
||||||
email: Option<StringValue<'a>>,
|
|
||||||
#[serde(rename = "contact:authInfo")]
|
|
||||||
auth_info: Option<ContactAuthInfo<'a>>,
|
auth_info: Option<ContactAuthInfo<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type for list of elements of the <status> tag for contact update request
|
/// Type for list of elements of the <status> tag for contact update request
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
pub struct StatusList<'a> {
|
pub struct StatusList<'a> {
|
||||||
#[serde(rename = "contact:status")]
|
status: &'a [Status<'a>],
|
||||||
status: &'a [ObjectStatus<'a>],
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "add", ns(XMLNS))]
|
||||||
|
struct AddStatuses<'a> {
|
||||||
|
statuses: &'a [Status<'a>],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "rem", ns(XMLNS))]
|
||||||
|
struct RemoveStatuses<'a> {
|
||||||
|
statuses: &'a [Status<'a>],
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type for elements under the contact <update> tag
|
/// Type for elements under the contact <update> tag
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
pub struct ContactUpdateRequestData<'a> {
|
#[xml(rename = "update", ns(XMLNS))]
|
||||||
#[serde(rename = "xmlns:contact")]
|
pub struct ContactUpdateRequest<'a> {
|
||||||
xmlns: &'a str,
|
id: &'a str,
|
||||||
#[serde(rename = "contact:id")]
|
add_statuses: Option<AddStatuses<'a>>,
|
||||||
id: StringValue<'a>,
|
#[xml(rename = "rem")]
|
||||||
#[serde(rename = "contact:add")]
|
remove_statuses: Option<RemoveStatuses<'a>>,
|
||||||
add_statuses: Option<StatusList<'a>>,
|
|
||||||
#[serde(rename = "contact:rem")]
|
|
||||||
remove_statuses: Option<StatusList<'a>>,
|
|
||||||
#[serde(rename = "contact:chg")]
|
|
||||||
change_info: Option<ContactChangeInfo<'a>>,
|
change_info: Option<ContactChangeInfo<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
|
||||||
/// Type for EPP XML <update> command for contacts
|
/// Type for EPP XML <update> command for contacts
|
||||||
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "update", ns(EPP_XMLNS))]
|
||||||
pub struct ContactUpdate<'a> {
|
pub struct ContactUpdate<'a> {
|
||||||
/// The data under the <update> tag for the contact update
|
/// The data under the <update> tag for the contact update
|
||||||
#[serde(rename = "contact:update")]
|
contact: ContactUpdateRequest<'a>,
|
||||||
contact: ContactUpdateRequestData<'a>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{ContactUpdate, Phone, PostalInfo};
|
use super::{ContactUpdate, PostalInfo, Status, Voice};
|
||||||
use crate::common::ObjectStatus;
|
|
||||||
use crate::contact::Address;
|
use crate::contact::Address;
|
||||||
use crate::response::ResultCode;
|
use crate::response::ResultCode;
|
||||||
use crate::tests::{assert_serialized, response_from_file, CLTRID, SUCCESS_MSG, SVTRID};
|
use crate::tests::{assert_serialized, response_from_file, CLTRID, SUCCESS_MSG, SVTRID};
|
||||||
|
@ -120,14 +122,14 @@ mod tests {
|
||||||
let street = &["58", "Orchid Road"];
|
let street = &["58", "Orchid Road"];
|
||||||
let address = Address::new(street, "Paris", "Paris", "392374", "FR".parse().unwrap());
|
let address = Address::new(street, "Paris", "Paris", "392374", "FR".parse().unwrap());
|
||||||
let postal_info = PostalInfo::new("loc", "John Doe", "Acme Widgets", address);
|
let postal_info = PostalInfo::new("loc", "John Doe", "Acme Widgets", address);
|
||||||
let voice = Phone::new("+33.47237942");
|
let voice = Voice::new("+33.47237942");
|
||||||
|
|
||||||
object.set_info("newemail@eppdev.net", postal_info, voice, "eppdev-387323");
|
object.set_info("newemail@eppdev.net", postal_info, voice, "eppdev-387323");
|
||||||
let add_statuses = &[ObjectStatus {
|
let add_statuses = &[Status {
|
||||||
status: "clientTransferProhibited".into(),
|
status: "clientTransferProhibited".into(),
|
||||||
}];
|
}];
|
||||||
object.add(add_statuses);
|
object.add(add_statuses);
|
||||||
let remove_statuses = &[ObjectStatus {
|
let remove_statuses = &[Status {
|
||||||
status: "clientDeleteProhibited".into(),
|
status: "clientDeleteProhibited".into(),
|
||||||
}];
|
}];
|
||||||
object.remove(remove_statuses);
|
object.remove(remove_statuses);
|
||||||
|
@ -139,8 +141,8 @@ mod tests {
|
||||||
fn contact_update() {
|
fn contact_update() {
|
||||||
let object = response_from_file::<ContactUpdate>("response/contact/update.xml");
|
let object = response_from_file::<ContactUpdate>("response/contact/update.xml");
|
||||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
158
src/domain.rs
158
src/domain.rs
|
@ -1,10 +1,11 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::fmt;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use instant_xml::OptionAccumulator;
|
||||||
|
use instant_xml::{Accumulate, Deserializer, FromXml, Serializer, ToXml};
|
||||||
|
|
||||||
use crate::common::{serialize_host_addrs_option, HostAddr, StringValue};
|
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
pub mod check;
|
pub mod check;
|
||||||
|
@ -31,85 +32,128 @@ pub use update::DomainUpdate;
|
||||||
pub const XMLNS: &str = "urn:ietf:params:xml:ns:domain-1.0";
|
pub const XMLNS: &str = "urn:ietf:params:xml:ns:domain-1.0";
|
||||||
|
|
||||||
/// The <hostAttr> type for domain transactions
|
/// The <hostAttr> type for domain transactions
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Clone, Debug, Eq, FromXml, PartialEq, ToXml)]
|
||||||
|
#[xml(rename = "hostAttr", ns(XMLNS))]
|
||||||
pub struct HostAttr<'a> {
|
pub struct HostAttr<'a> {
|
||||||
/// The <hostName> tag
|
/// The <hostName> tag
|
||||||
#[serde(rename = "domain:hostName", alias = "hostName")]
|
#[xml(rename = "hostName")]
|
||||||
pub name: StringValue<'a>,
|
pub name: Cow<'a, str>,
|
||||||
/// The <hostAddr> tags
|
/// The <hostAddr> tags
|
||||||
#[serde(
|
#[xml(
|
||||||
rename = "domain:hostAddr",
|
rename = "hostAddr",
|
||||||
alias = "hostAddr",
|
|
||||||
serialize_with = "serialize_host_addrs_option",
|
serialize_with = "serialize_host_addrs_option",
|
||||||
deserialize_with = "deserialize_host_addrs_option"
|
deserialize_with = "deserialize_host_addrs_option"
|
||||||
)]
|
)]
|
||||||
pub addresses: Option<Vec<IpAddr>>,
|
pub addresses: Option<Vec<IpAddr>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_host_addrs_option<'de, D>(de: D) -> Result<Option<Vec<IpAddr>>, D::Error>
|
fn deserialize_host_addrs_option<'xml>(
|
||||||
where
|
into: &mut OptionAccumulator<Vec<IpAddr>, Vec<IpAddr>>,
|
||||||
D: serde::de::Deserializer<'de>,
|
field: &'static str,
|
||||||
{
|
deserializer: &mut Deserializer<'_, 'xml>,
|
||||||
let addrs = Option::<Vec<HostAddr<'static>>>::deserialize(de)?;
|
) -> Result<(), instant_xml::Error> {
|
||||||
let addrs = match addrs {
|
let mut value = <Option<Vec<HostAddr<'static>>> as FromXml<'xml>>::Accumulator::default();
|
||||||
Some(addrs) => addrs,
|
<Option<Vec<HostAddr<'static>>>>::deserialize(&mut value, field, deserializer)?;
|
||||||
None => return Ok(None),
|
let new = match value.try_done(field)? {
|
||||||
|
Some(new) => new,
|
||||||
|
None => return Ok(()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = addrs
|
let into = into.get_mut();
|
||||||
.into_iter()
|
for addr in new {
|
||||||
.map(|addr| IpAddr::from_str(&addr.address))
|
match IpAddr::from_str(&addr.address) {
|
||||||
.collect::<Result<_, _>>();
|
Ok(ip) => into.push(ip),
|
||||||
|
Err(_) => {
|
||||||
|
return Err(instant_xml::Error::UnexpectedValue(format!(
|
||||||
|
"invalid IP address '{}'",
|
||||||
|
&addr.address
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match result {
|
Ok(())
|
||||||
Ok(addrs) => Ok(Some(addrs)),
|
}
|
||||||
Err(e) => Err(serde::de::Error::custom(format!("{}", e))),
|
|
||||||
|
/// The <hostAddr> types domain or host transactions
|
||||||
|
#[derive(Debug, FromXml, ToXml)]
|
||||||
|
#[xml(rename = "hostAddr", ns(super::domain::XMLNS))]
|
||||||
|
pub(crate) struct HostAddr<'a> {
|
||||||
|
#[xml(attribute, rename = "ip")]
|
||||||
|
pub ip_version: Option<Cow<'a, str>>,
|
||||||
|
#[xml(direct)]
|
||||||
|
pub address: Cow<'a, str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&IpAddr> for HostAddr<'static> {
|
||||||
|
fn from(addr: &IpAddr) -> Self {
|
||||||
|
Self {
|
||||||
|
ip_version: Some(match addr {
|
||||||
|
IpAddr::V4(_) => "v4".into(),
|
||||||
|
IpAddr::V6(_) => "v6".into(),
|
||||||
|
}),
|
||||||
|
address: addr.to_string().into(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The list of <hostAttr> types for domain transactions. Typically under an <ns> tag
|
pub(crate) fn serialize_host_addrs_option<T: AsRef<[IpAddr]>, W: fmt::Write + ?Sized>(
|
||||||
#[derive(Serialize, Debug)]
|
addrs: &Option<T>,
|
||||||
pub struct HostAttrList<'a> {
|
serializer: &mut Serializer<'_, W>,
|
||||||
/// The list of <hostAttr> tags
|
) -> Result<(), instant_xml::Error> {
|
||||||
#[serde(rename = "domain:hostAttr", alias = "hostAttr")]
|
let addrs = match addrs {
|
||||||
pub hosts: &'a [HostAttr<'a>],
|
Some(addrs) => addrs.as_ref(),
|
||||||
|
None => return Ok(()),
|
||||||
|
};
|
||||||
|
|
||||||
|
for addr in addrs {
|
||||||
|
HostAddr::from(addr).serialize(None, serializer)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The list of <hostObj> types for domain transactions. Typically under an <ns> tag
|
#[derive(Clone, Debug, Eq, FromXml, PartialEq, ToXml)]
|
||||||
#[derive(Serialize, Debug)]
|
#[xml(rename = "hostObj", ns(XMLNS))]
|
||||||
pub struct HostObjList<'a> {
|
pub struct HostObj<'a> {
|
||||||
/// The list of <hostObj> tags
|
#[xml(direct)]
|
||||||
#[serde(rename = "domain:hostObj", alias = "hostObj")]
|
pub name: Cow<'a, str>,
|
||||||
pub hosts: &'a [StringValue<'a>],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enum that can accept one type which corresponds to either the <hostObj> or <hostAttr>
|
#[derive(Clone, Debug, Eq, FromXml, PartialEq, ToXml)]
|
||||||
/// list of tags
|
#[xml(forward)]
|
||||||
#[derive(Serialize, Debug)]
|
pub enum HostInfo<'a> {
|
||||||
#[serde(untagged)]
|
Attr(HostAttr<'a>),
|
||||||
pub enum HostList<'a> {
|
Obj(HostObj<'a>),
|
||||||
HostObjList(HostObjList<'a>),
|
}
|
||||||
HostAttrList(HostAttrList<'a>),
|
|
||||||
|
#[derive(Debug, FromXml, ToXml)]
|
||||||
|
#[xml(rename = "ns", ns(XMLNS))]
|
||||||
|
pub struct NameServers<'a> {
|
||||||
|
pub ns: Vec<HostInfo<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The <contact> type on domain creation and update requests
|
/// The <contact> type on domain creation and update requests
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Debug, FromXml, ToXml)]
|
||||||
|
#[xml(rename = "contact", ns(XMLNS))]
|
||||||
pub struct DomainContact<'a> {
|
pub struct DomainContact<'a> {
|
||||||
/// The contact id
|
|
||||||
#[serde(rename = "$value")]
|
|
||||||
pub id: Cow<'a, str>,
|
|
||||||
/// The contact type attr (usually admin, billing, or tech in most registries)
|
/// The contact type attr (usually admin, billing, or tech in most registries)
|
||||||
#[serde(rename = "type")]
|
#[xml(attribute, rename = "type")]
|
||||||
pub contact_type: Cow<'a, str>,
|
pub contact_type: Cow<'a, str>,
|
||||||
|
/// The contact id
|
||||||
|
#[xml(direct)]
|
||||||
|
pub id: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The <period> type for registration, renewal or transfer on domain transactions
|
/// The <period> type for registration, renewal or transfer on domain transactions
|
||||||
#[derive(Clone, Copy, Debug, Serialize)]
|
#[derive(Clone, Copy, Debug, ToXml)]
|
||||||
|
#[xml(rename = "period", ns(XMLNS))]
|
||||||
pub struct Period {
|
pub struct Period {
|
||||||
/// The interval (usually 'y' indicating years)
|
/// The interval (usually 'y' indicating years)
|
||||||
|
#[xml(attribute)]
|
||||||
unit: char,
|
unit: char,
|
||||||
/// The length of the registration, renewal or transfer period (usually in years)
|
/// The length of the registration, renewal or transfer period (usually in years)
|
||||||
#[serde(rename = "$value")]
|
#[xml(direct)]
|
||||||
length: u8,
|
length: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,11 +202,12 @@ pub const SIX_MONTHS: Period = Period {
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The <authInfo> tag for domain and contact transactions
|
/// The <authInfo> tag for domain and contact transactions
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Clone, Debug, FromXml, ToXml)]
|
||||||
|
#[xml(rename = "authInfo", ns(XMLNS))]
|
||||||
pub struct DomainAuthInfo<'a> {
|
pub struct DomainAuthInfo<'a> {
|
||||||
/// The <pw> tag under <authInfo>
|
/// The <pw> tag under <authInfo>
|
||||||
#[serde(rename = "domain:pw", alias = "pw")]
|
#[xml(rename = "pw")]
|
||||||
pub password: StringValue<'a>,
|
pub password: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DomainAuthInfo<'a> {
|
impl<'a> DomainAuthInfo<'a> {
|
||||||
|
@ -173,3 +218,12 @@ impl<'a> DomainAuthInfo<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The <status> type on contact transactions
|
||||||
|
#[derive(Debug, FromXml, ToXml)]
|
||||||
|
#[xml(rename = "status", ns(XMLNS))]
|
||||||
|
pub struct Status<'a> {
|
||||||
|
/// The status name, represented by the 's' attr on <status> tags
|
||||||
|
#[xml(attribute, rename = "s")]
|
||||||
|
pub status: Cow<'a, str>,
|
||||||
|
}
|
||||||
|
|
|
@ -1,55 +1,72 @@
|
||||||
//! Types for EPP domain check request
|
//! Types for EPP domain check request
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use instant_xml::{FromXml, Serializer, ToXml};
|
||||||
|
|
||||||
use super::XMLNS;
|
use super::XMLNS;
|
||||||
use crate::common::{CheckResponse, NoExtension, StringValue};
|
use crate::common::{NoExtension, EPP_XMLNS};
|
||||||
use crate::request::{Command, Transaction};
|
use crate::request::{Command, Transaction};
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
impl<'a> Transaction<NoExtension> for DomainCheck<'a> {}
|
impl<'a> Transaction<NoExtension> for DomainCheck<'a> {}
|
||||||
|
|
||||||
impl<'a> Command for DomainCheck<'a> {
|
impl<'a> Command for DomainCheck<'a> {
|
||||||
type Response = CheckResponse;
|
type Response = CheckData;
|
||||||
const COMMAND: &'static str = "check";
|
const COMMAND: &'static str = "check";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request
|
// Request
|
||||||
|
|
||||||
/// Type for <name> elements under the domain <check> tag
|
#[derive(Debug, ToXml)]
|
||||||
#[derive(Serialize, Debug)]
|
#[xml(rename = "check", ns(XMLNS))]
|
||||||
struct DomainList<'a> {
|
struct DomainList<'a> {
|
||||||
#[serde(rename = "xmlns:domain")]
|
#[xml(rename = "name", ns(XMLNS))]
|
||||||
/// XML namespace for domain commands
|
domains: &'a [&'a str],
|
||||||
xmlns: &'a str,
|
|
||||||
#[serde(rename = "domain:name")]
|
|
||||||
/// List of domains to be checked for availability
|
|
||||||
domains: Vec<StringValue<'a>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
fn serialize_domains<W: fmt::Write + ?Sized>(
|
||||||
struct SerializeDomainCheck<'a> {
|
domains: &[&str],
|
||||||
#[serde(rename = "domain:check")]
|
serializer: &mut Serializer<W>,
|
||||||
list: DomainList<'a>,
|
) -> Result<(), instant_xml::Error> {
|
||||||
|
DomainList { domains }.serialize(None, serializer)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<DomainCheck<'a>> for SerializeDomainCheck<'a> {
|
#[derive(ToXml, Debug)]
|
||||||
fn from(check: DomainCheck<'a>) -> Self {
|
#[xml(rename = "check", ns(EPP_XMLNS))]
|
||||||
Self {
|
|
||||||
list: DomainList {
|
|
||||||
xmlns: XMLNS,
|
|
||||||
domains: check.domains.iter().map(|&d| d.into()).collect(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The EPP `check` command for domains
|
|
||||||
#[derive(Clone, Debug, Serialize)]
|
|
||||||
#[serde(into = "SerializeDomainCheck")]
|
|
||||||
pub struct DomainCheck<'a> {
|
pub struct DomainCheck<'a> {
|
||||||
/// The list of domains to be checked
|
/// The list of domains to be checked for availability
|
||||||
|
#[xml(serialize_with = "serialize_domains")]
|
||||||
pub domains: &'a [&'a str],
|
pub domains: &'a [&'a str],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Response
|
||||||
|
|
||||||
|
#[derive(Debug, FromXml)]
|
||||||
|
#[xml(rename = "name", ns(XMLNS))]
|
||||||
|
pub struct Checked {
|
||||||
|
#[xml(attribute, rename = "avail")]
|
||||||
|
pub available: bool,
|
||||||
|
#[xml(attribute)]
|
||||||
|
pub reason: Option<String>,
|
||||||
|
#[xml(direct)]
|
||||||
|
pub id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, FromXml)]
|
||||||
|
#[xml(rename = "cd", ns(XMLNS))]
|
||||||
|
pub struct CheckedDomain {
|
||||||
|
/// Data under the <cd> tag
|
||||||
|
#[xml(rename = "cd")]
|
||||||
|
pub inner: Checked,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type that represents the <chkData> tag for host check response
|
||||||
|
#[derive(Debug, FromXml)]
|
||||||
|
#[xml(rename = "chkData", ns(XMLNS))]
|
||||||
|
pub struct CheckData {
|
||||||
|
pub list: Vec<CheckedDomain>,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::DomainCheck;
|
use super::DomainCheck;
|
||||||
|
@ -67,15 +84,15 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn response() {
|
fn response() {
|
||||||
let object = response_from_file::<DomainCheck>("response/domain/check.xml");
|
let object = response_from_file::<DomainCheck>("response/domain/check.xml");
|
||||||
let result = object.res_data().unwrap();
|
let result = dbg!(&object).res_data().unwrap();
|
||||||
|
|
||||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||||
assert_eq!(result.list[0].id, "eppdev.com");
|
assert_eq!(result.list[0].inner.id, "eppdev.com");
|
||||||
assert!(result.list[0].available);
|
assert!(result.list[0].inner.available);
|
||||||
assert_eq!(result.list[1].id, "eppdev.net");
|
assert_eq!(result.list[1].inner.id, "eppdev.net");
|
||||||
assert!(!result.list[1].available);
|
assert!(!result.list[1].inner.available);
|
||||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
//! Types for EPP domain create request
|
//! Types for EPP domain create request
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use instant_xml::{FromXml, ToXml};
|
||||||
|
|
||||||
use super::{DomainAuthInfo, DomainContact, HostList, Period, XMLNS};
|
use super::{DomainAuthInfo, DomainContact, HostInfo, NameServers, Period, XMLNS};
|
||||||
use crate::common::{NoExtension, StringValue};
|
use crate::common::{NoExtension, EPP_XMLNS};
|
||||||
use crate::request::{Command, Transaction};
|
use crate::request::{Command, Transaction};
|
||||||
|
|
||||||
impl<'a> Transaction<NoExtension> for DomainCreate<'a> {}
|
impl<'a> Transaction<NoExtension> for DomainCreate<'a> {}
|
||||||
|
@ -17,39 +17,31 @@ impl<'a> Command for DomainCreate<'a> {
|
||||||
// Request
|
// Request
|
||||||
|
|
||||||
/// Type for elements under the domain <create> tag
|
/// Type for elements under the domain <create> tag
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "create", ns(XMLNS))]
|
||||||
pub struct DomainCreateRequestData<'a> {
|
pub struct DomainCreateRequestData<'a> {
|
||||||
/// XML namespace for domain commands
|
|
||||||
#[serde(rename = "xmlns:domain")]
|
|
||||||
pub xmlns: &'a str,
|
|
||||||
/// The domain name
|
/// The domain name
|
||||||
#[serde(rename = "domain:name")]
|
pub name: &'a str,
|
||||||
pub name: StringValue<'a>,
|
|
||||||
/// The period of registration
|
/// The period of registration
|
||||||
#[serde(rename = "domain:period")]
|
|
||||||
pub period: Period,
|
pub period: Period,
|
||||||
/// The list of nameserver hosts
|
/// The list of nameserver hosts
|
||||||
/// either of type `HostObjList` or `HostAttrList`
|
/// either of type `HostObjList` or `HostAttrList`
|
||||||
#[serde(rename = "domain:ns")]
|
pub ns: Option<NameServers<'a>>,
|
||||||
pub ns: Option<HostList<'a>>,
|
|
||||||
/// The domain registrant
|
/// The domain registrant
|
||||||
#[serde(rename = "domain:registrant")]
|
pub registrant: Option<&'a str>,
|
||||||
pub registrant: Option<StringValue<'a>>,
|
|
||||||
/// The list of contacts for the domain
|
/// The list of contacts for the domain
|
||||||
#[serde(rename = "domain:contact")]
|
|
||||||
pub contacts: Option<&'a [DomainContact<'a>]>,
|
pub contacts: Option<&'a [DomainContact<'a>]>,
|
||||||
/// The auth info for the domain
|
/// The auth info for the domain
|
||||||
#[serde(rename = "domain:authInfo")]
|
|
||||||
pub auth_info: DomainAuthInfo<'a>,
|
pub auth_info: DomainAuthInfo<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
/// Type for EPP XML <create> command for domains
|
/// Type for EPP XML <create> command for domains
|
||||||
|
#[xml(rename = "create", ns(EPP_XMLNS))]
|
||||||
pub struct DomainCreate<'a> {
|
pub struct DomainCreate<'a> {
|
||||||
/// The data for the domain to be created with
|
/// The data for the domain to be created with
|
||||||
/// T being the type of nameserver list (`HostObjList` or `HostAttrList`)
|
/// T being the type of nameserver list (`HostObjList` or `HostAttrList`)
|
||||||
/// to be supplied
|
/// to be supplied
|
||||||
#[serde(rename = "domain:create")]
|
|
||||||
pub domain: DomainCreateRequestData<'a>,
|
pub domain: DomainCreateRequestData<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,18 +49,17 @@ impl<'a> DomainCreate<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
period: Period,
|
period: Period,
|
||||||
ns: Option<HostList<'a>>,
|
ns: Option<&'a [HostInfo<'a>]>,
|
||||||
registrant_id: Option<&'a str>,
|
registrant: Option<&'a str>,
|
||||||
auth_password: &'a str,
|
auth_password: &'a str,
|
||||||
contacts: Option<&'a [DomainContact<'a>]>,
|
contacts: Option<&'a [DomainContact<'a>]>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
domain: DomainCreateRequestData {
|
domain: DomainCreateRequestData {
|
||||||
xmlns: XMLNS,
|
name,
|
||||||
name: name.into(),
|
|
||||||
period,
|
period,
|
||||||
ns,
|
ns: ns.map(|ns| NameServers { ns: ns.to_vec() }),
|
||||||
registrant: registrant_id.map(|id| id.into()),
|
registrant,
|
||||||
auth_info: DomainAuthInfo::new(auth_password),
|
auth_info: DomainAuthInfo::new(auth_password),
|
||||||
contacts,
|
contacts,
|
||||||
},
|
},
|
||||||
|
@ -79,37 +70,27 @@ impl<'a> DomainCreate<'a> {
|
||||||
// Response
|
// Response
|
||||||
|
|
||||||
/// Type that represents the <chkData> tag for domain create response
|
/// Type that represents the <chkData> tag for domain create response
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Debug, FromXml)]
|
||||||
pub struct DomainCreateResponseData {
|
#[xml(rename = "creData", ns(XMLNS))]
|
||||||
/// XML namespace for domain response data
|
pub struct DomainCreateResponse {
|
||||||
#[serde(rename = "xmlns:domain")]
|
|
||||||
pub xmlns: String,
|
|
||||||
/// The domain name
|
/// The domain name
|
||||||
pub name: StringValue<'static>,
|
pub name: String,
|
||||||
/// The creation date
|
/// The creation date
|
||||||
#[serde(rename = "crDate")]
|
#[xml(rename = "crDate")]
|
||||||
pub created_at: DateTime<Utc>,
|
pub created_at: DateTime<Utc>,
|
||||||
/// The expiry date
|
/// The expiry date
|
||||||
#[serde(rename = "exDate")]
|
#[xml(rename = "exDate")]
|
||||||
pub expiring_at: Option<DateTime<Utc>>,
|
pub expiring_at: Option<DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type that represents the <resData> tag for domain create response
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
pub struct DomainCreateResponse {
|
|
||||||
/// Data under the <chkData> tag
|
|
||||||
#[serde(rename = "creData")]
|
|
||||||
pub create_data: DomainCreateResponseData,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
|
||||||
use chrono::{TimeZone, Utc};
|
use chrono::{TimeZone, Utc};
|
||||||
|
|
||||||
use super::{DomainContact, DomainCreate, HostList, Period};
|
use super::{DomainContact, DomainCreate, Period};
|
||||||
use crate::domain::{HostAttr, HostAttrList, HostObjList};
|
use crate::domain::{HostAttr, HostInfo, HostObj};
|
||||||
use crate::response::ResultCode;
|
use crate::response::ResultCode;
|
||||||
use crate::tests::{assert_serialized, response_from_file, CLTRID, SUCCESS_MSG, SVTRID};
|
use crate::tests::{assert_serialized, response_from_file, CLTRID, SUCCESS_MSG, SVTRID};
|
||||||
|
|
||||||
|
@ -159,11 +140,18 @@ mod tests {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let hosts = &["ns1.test.com".into(), "ns2.test.com".into()];
|
let hosts = &[
|
||||||
|
HostInfo::Obj(HostObj {
|
||||||
|
name: "ns1.test.com".into(),
|
||||||
|
}),
|
||||||
|
HostInfo::Obj(HostObj {
|
||||||
|
name: "ns2.test.com".into(),
|
||||||
|
}),
|
||||||
|
];
|
||||||
let object = DomainCreate::new(
|
let object = DomainCreate::new(
|
||||||
"eppdev-1.com",
|
"eppdev-1.com",
|
||||||
Period::years(1).unwrap(),
|
Period::years(1).unwrap(),
|
||||||
Some(HostList::HostObjList(HostObjList { hosts })),
|
Some(hosts),
|
||||||
Some("eppdev-contact-3"),
|
Some("eppdev-contact-3"),
|
||||||
"epP4uthd#v",
|
"epP4uthd#v",
|
||||||
Some(contacts),
|
Some(contacts),
|
||||||
|
@ -190,23 +178,23 @@ mod tests {
|
||||||
];
|
];
|
||||||
|
|
||||||
let hosts = &[
|
let hosts = &[
|
||||||
HostAttr {
|
HostInfo::Attr(HostAttr {
|
||||||
name: "ns1.eppdev-1.com".into(),
|
name: "ns1.eppdev-1.com".into(),
|
||||||
addresses: None,
|
addresses: None,
|
||||||
},
|
}),
|
||||||
HostAttr {
|
HostInfo::Attr(HostAttr {
|
||||||
name: "ns2.eppdev-1.com".into(),
|
name: "ns2.eppdev-1.com".into(),
|
||||||
addresses: Some(vec![
|
addresses: Some(vec![
|
||||||
IpAddr::from([177, 232, 12, 58]),
|
IpAddr::from([177, 232, 12, 58]),
|
||||||
IpAddr::from([0x2404, 0x6800, 0x4001, 0x801, 0, 0, 0, 0x200e]),
|
IpAddr::from([0x2404, 0x6800, 0x4001, 0x801, 0, 0, 0, 0x200e]),
|
||||||
]),
|
]),
|
||||||
},
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
let object = DomainCreate::new(
|
let object = DomainCreate::new(
|
||||||
"eppdev-2.com",
|
"eppdev-2.com",
|
||||||
Period::years(1).unwrap(),
|
Period::years(1).unwrap(),
|
||||||
Some(HostList::HostAttrList(HostAttrList { hosts })),
|
Some(hosts),
|
||||||
Some("eppdev-contact-3"),
|
Some("eppdev-contact-3"),
|
||||||
"epP4uthd#v",
|
"epP4uthd#v",
|
||||||
Some(contacts),
|
Some(contacts),
|
||||||
|
@ -222,17 +210,17 @@ mod tests {
|
||||||
let result = object.res_data().unwrap();
|
let result = object.res_data().unwrap();
|
||||||
|
|
||||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||||
assert_eq!(result.create_data.name, "eppdev-2.com".into());
|
assert_eq!(result.name, "eppdev-2.com");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result.create_data.created_at,
|
result.created_at,
|
||||||
Utc.with_ymd_and_hms(2021, 7, 25, 18, 11, 35).unwrap()
|
Utc.with_ymd_and_hms(2021, 7, 25, 18, 11, 35).unwrap()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*result.create_data.expiring_at.as_ref().unwrap(),
|
*result.expiring_at.as_ref().unwrap(),
|
||||||
Utc.with_ymd_and_hms(2022, 7, 25, 18, 11, 34).unwrap()
|
Utc.with_ymd_and_hms(2022, 7, 25, 18, 11, 34).unwrap()
|
||||||
);
|
);
|
||||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
//! Types for EPP domain delete request
|
//! Types for EPP domain delete request
|
||||||
|
|
||||||
|
use instant_xml::ToXml;
|
||||||
|
|
||||||
use super::XMLNS;
|
use super::XMLNS;
|
||||||
use crate::common::{NoExtension, StringValue};
|
use crate::common::{NoExtension, EPP_XMLNS};
|
||||||
use crate::request::{Command, Transaction};
|
use crate::request::{Command, Transaction};
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
impl<'a> Transaction<NoExtension> for DomainDelete<'a> {}
|
impl<'a> Transaction<NoExtension> for DomainDelete<'a> {}
|
||||||
|
|
||||||
|
@ -15,30 +16,24 @@ impl<'a> Command for DomainDelete<'a> {
|
||||||
impl<'a> DomainDelete<'a> {
|
impl<'a> DomainDelete<'a> {
|
||||||
pub fn new(name: &'a str) -> Self {
|
pub fn new(name: &'a str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
domain: DomainDeleteRequestData {
|
domain: DomainDeleteRequestData { name },
|
||||||
xmlns: XMLNS,
|
|
||||||
name: name.into(),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type for <name> element under the domain <delete> tag
|
/// Type for <name> element under the domain <delete> tag
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "delete", ns(XMLNS))]
|
||||||
pub struct DomainDeleteRequestData<'a> {
|
pub struct DomainDeleteRequestData<'a> {
|
||||||
/// XML namespace for domain commands
|
|
||||||
#[serde(rename = "xmlns:domain")]
|
|
||||||
xmlns: &'a str,
|
|
||||||
/// The domain to be deleted
|
/// The domain to be deleted
|
||||||
#[serde(rename = "domain:name")]
|
name: &'a str,
|
||||||
name: StringValue<'a>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
/// Type for EPP XML <delete> command for domains
|
/// Type for EPP XML <delete> command for domains
|
||||||
|
#[xml(rename = "delete", ns(EPP_XMLNS))]
|
||||||
pub struct DomainDelete<'a> {
|
pub struct DomainDelete<'a> {
|
||||||
/// The data under the <delete> tag for domain deletion
|
/// The data under the <delete> tag for domain deletion
|
||||||
#[serde(rename = "domain:delete")]
|
|
||||||
domain: DomainDeleteRequestData<'a>,
|
domain: DomainDeleteRequestData<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,8 +54,8 @@ mod tests {
|
||||||
let object = response_from_file::<DomainDelete>("response/domain/delete.xml");
|
let object = response_from_file::<DomainDelete>("response/domain/delete.xml");
|
||||||
|
|
||||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
//! Types for EPP domain info request
|
//! Types for EPP domain info request
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use instant_xml::{FromXml, ToXml};
|
||||||
|
|
||||||
use super::{DomainAuthInfo, DomainContact, HostAttr, XMLNS};
|
use super::{DomainAuthInfo, DomainContact, HostAttr, NameServers, Status, XMLNS};
|
||||||
use crate::common::{NoExtension, ObjectStatus, StringValue};
|
use crate::common::{NoExtension, EPP_XMLNS};
|
||||||
use crate::request::{Command, Transaction};
|
use crate::request::{Command, Transaction};
|
||||||
|
|
||||||
impl<'a> Transaction<NoExtension> for DomainInfo<'a> {}
|
impl<'a> Transaction<NoExtension> for DomainInfo<'a> {}
|
||||||
|
@ -18,8 +18,7 @@ impl<'a> DomainInfo<'a> {
|
||||||
pub fn new(name: &'a str, auth_password: Option<&'a str>) -> Self {
|
pub fn new(name: &'a str, auth_password: Option<&'a str>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
info: DomainInfoRequestData {
|
info: DomainInfoRequestData {
|
||||||
xmlns: XMLNS,
|
name: Domain { hosts: "all", name },
|
||||||
domain: Domain { hosts: "all", name },
|
|
||||||
auth_info: auth_password.map(|password| DomainAuthInfo {
|
auth_info: auth_password.map(|password| DomainAuthInfo {
|
||||||
password: password.into(),
|
password: password.into(),
|
||||||
}),
|
}),
|
||||||
|
@ -31,34 +30,32 @@ impl<'a> DomainInfo<'a> {
|
||||||
// Request
|
// Request
|
||||||
|
|
||||||
/// Type for data under the <name> element tag for the domain <info> tag
|
/// Type for data under the <name> element tag for the domain <info> tag
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "name", ns(XMLNS))]
|
||||||
pub struct Domain<'a> {
|
pub struct Domain<'a> {
|
||||||
/// The hosts attribute. Default value is "all"
|
/// The hosts attribute. Default value is "all"
|
||||||
|
#[xml(attribute)]
|
||||||
hosts: &'a str,
|
hosts: &'a str,
|
||||||
/// The name of the domain
|
/// The name of the domain
|
||||||
#[serde(rename = "$value")]
|
#[xml(direct)]
|
||||||
name: &'a str,
|
name: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type for <name> element under the domain <info> tag
|
/// Type for <name> element under the domain <info> tag
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "info", ns(XMLNS))]
|
||||||
pub struct DomainInfoRequestData<'a> {
|
pub struct DomainInfoRequestData<'a> {
|
||||||
/// XML namespace for domain commands
|
|
||||||
#[serde(rename = "xmlns:domain")]
|
|
||||||
xmlns: &'a str,
|
|
||||||
/// The data for the domain to be queried
|
/// The data for the domain to be queried
|
||||||
#[serde(rename = "domain:name")]
|
name: Domain<'a>,
|
||||||
domain: Domain<'a>,
|
|
||||||
/// The auth info for the domain
|
/// The auth info for the domain
|
||||||
#[serde(rename = "domain:authInfo")]
|
|
||||||
auth_info: Option<DomainAuthInfo<'a>>,
|
auth_info: Option<DomainAuthInfo<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
/// Type for EPP XML <info> command for domains
|
/// Type for EPP XML <info> command for domains
|
||||||
|
#[xml(rename = "info", ns(EPP_XMLNS))]
|
||||||
pub struct DomainInfo<'a> {
|
pub struct DomainInfo<'a> {
|
||||||
/// The data under the <info> tag for domain info
|
/// The data under the <info> tag for domain info
|
||||||
#[serde(rename = "domain:info")]
|
|
||||||
info: DomainInfoRequestData<'a>,
|
info: DomainInfoRequestData<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,73 +63,66 @@ pub struct DomainInfo<'a> {
|
||||||
|
|
||||||
/// The two types of ns lists, hostObj and hostAttr, that may be returned in the
|
/// The two types of ns lists, hostObj and hostAttr, that may be returned in the
|
||||||
/// domain info response
|
/// domain info response
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Debug, FromXml)]
|
||||||
pub struct DomainNsList {
|
pub struct DomainNsList {
|
||||||
/// List of <hostObj> ns elements
|
/// List of <hostObj> ns elements
|
||||||
#[serde(rename = "hostObj")]
|
#[xml(rename = "hostObj")]
|
||||||
pub host_obj: Option<Vec<StringValue<'static>>>,
|
pub host_obj: Option<Vec<String>>,
|
||||||
/// List of <hostAttr> ns elements
|
/// List of <hostAttr> ns elements
|
||||||
pub host_attr: Option<Vec<HostAttr<'static>>>,
|
pub host_attr: Option<Vec<HostAttr<'static>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type that represents the <infData> tag for domain info response
|
/// Type that represents the <infData> tag for domain info response
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Debug, FromXml)]
|
||||||
pub struct DomainInfoResponseData {
|
#[xml(rename = "infData", ns(XMLNS))]
|
||||||
|
pub struct DomainInfoResponse {
|
||||||
/// The domain name
|
/// The domain name
|
||||||
pub name: StringValue<'static>,
|
pub name: String,
|
||||||
/// The domain ROID
|
/// The domain ROID
|
||||||
pub roid: StringValue<'static>,
|
pub roid: String,
|
||||||
/// The list of domain statuses
|
/// The list of domain statuses
|
||||||
#[serde(rename = "status")]
|
#[xml(rename = "status")]
|
||||||
pub statuses: Option<Vec<ObjectStatus<'static>>>,
|
pub statuses: Option<Vec<Status<'static>>>,
|
||||||
/// The domain registrant
|
/// The domain registrant
|
||||||
pub registrant: Option<StringValue<'static>>,
|
pub registrant: Option<String>,
|
||||||
/// The list of domain contacts
|
/// The list of domain contacts
|
||||||
#[serde(rename = "contact")]
|
#[xml(rename = "contact")]
|
||||||
pub contacts: Option<Vec<DomainContact<'static>>>,
|
pub contacts: Option<Vec<DomainContact<'static>>>,
|
||||||
/// The list of domain nameservers
|
/// The list of domain nameservers
|
||||||
#[serde(rename = "ns")]
|
pub ns: Option<NameServers<'static>>,
|
||||||
pub ns: Option<DomainNsList>,
|
|
||||||
/// The list of domain hosts
|
/// The list of domain hosts
|
||||||
#[serde(rename = "host")]
|
#[xml(rename = "host")]
|
||||||
pub hosts: Option<Vec<StringValue<'static>>>,
|
pub hosts: Option<Vec<String>>,
|
||||||
/// The epp user who owns the domain
|
/// The epp user who owns the domain
|
||||||
#[serde(rename = "clID")]
|
#[xml(rename = "clID")]
|
||||||
pub client_id: StringValue<'static>,
|
pub client_id: String,
|
||||||
/// The epp user who created the domain
|
/// The epp user who created the domain
|
||||||
#[serde(rename = "crID")]
|
#[xml(rename = "crID")]
|
||||||
pub creator_id: Option<StringValue<'static>>,
|
pub creator_id: Option<String>,
|
||||||
/// The domain creation date
|
/// The domain creation date
|
||||||
#[serde(rename = "crDate")]
|
#[xml(rename = "crDate")]
|
||||||
pub created_at: Option<DateTime<Utc>>,
|
pub created_at: Option<DateTime<Utc>>,
|
||||||
/// The domain expiry date
|
/// The domain expiry date
|
||||||
#[serde(rename = "exDate")]
|
#[xml(rename = "exDate")]
|
||||||
pub expiring_at: Option<DateTime<Utc>>,
|
pub expiring_at: Option<DateTime<Utc>>,
|
||||||
/// The epp user who last updated the domain
|
/// The epp user who last updated the domain
|
||||||
#[serde(rename = "upID")]
|
#[xml(rename = "upID")]
|
||||||
pub updater_id: Option<StringValue<'static>>,
|
pub updater_id: Option<String>,
|
||||||
/// The domain last updated date
|
/// The domain last updated date
|
||||||
#[serde(rename = "upDate")]
|
#[xml(rename = "upDate")]
|
||||||
pub updated_at: Option<DateTime<Utc>>,
|
pub updated_at: Option<DateTime<Utc>>,
|
||||||
/// The domain transfer date
|
/// The domain transfer date
|
||||||
#[serde(rename = "trDate")]
|
#[xml(rename = "trDate")]
|
||||||
pub transferred_at: Option<DateTime<Utc>>,
|
pub transferred_at: Option<DateTime<Utc>>,
|
||||||
/// The domain auth info
|
/// The domain auth info
|
||||||
#[serde(rename = "authInfo")]
|
#[xml(rename = "authInfo")]
|
||||||
pub auth_info: Option<DomainAuthInfo<'static>>,
|
pub auth_info: Option<DomainAuthInfo<'static>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type that represents the <resData> tag for domain info response
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
pub struct DomainInfoResponse {
|
|
||||||
/// Data under the <resData> tag
|
|
||||||
#[serde(rename = "infData")]
|
|
||||||
pub info_data: DomainInfoResponseData,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::DomainInfo;
|
use super::DomainInfo;
|
||||||
|
use crate::domain::{HostInfo, HostObj};
|
||||||
use crate::response::ResultCode;
|
use crate::response::ResultCode;
|
||||||
use crate::tests::{assert_serialized, response_from_file, CLTRID, SUCCESS_MSG, SVTRID};
|
use crate::tests::{assert_serialized, response_from_file, CLTRID, SUCCESS_MSG, SVTRID};
|
||||||
use chrono::{TimeZone, Utc};
|
use chrono::{TimeZone, Utc};
|
||||||
|
@ -146,57 +136,61 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn response() {
|
fn response() {
|
||||||
let object = response_from_file::<DomainInfo>("response/domain/info.xml");
|
let object = response_from_file::<DomainInfo>("response/domain/info.xml");
|
||||||
|
dbg!(&object);
|
||||||
|
|
||||||
let result = object.res_data().unwrap();
|
let result = object.res_data().unwrap();
|
||||||
let auth_info = result.info_data.auth_info.as_ref().unwrap();
|
let auth_info = result.auth_info.as_ref().unwrap();
|
||||||
let ns_list = result.info_data.ns.as_ref().unwrap();
|
let ns = result.ns.as_ref().unwrap();
|
||||||
let ns = ns_list.host_obj.as_ref().unwrap();
|
let hosts = result.hosts.as_ref().unwrap();
|
||||||
let hosts = result.info_data.hosts.as_ref().unwrap();
|
let statuses = result.statuses.as_ref().unwrap();
|
||||||
let statuses = result.info_data.statuses.as_ref().unwrap();
|
let registrant = result.registrant.as_ref().unwrap();
|
||||||
let registrant = result.info_data.registrant.as_ref().unwrap();
|
let contacts = result.contacts.as_ref().unwrap();
|
||||||
let contacts = result.info_data.contacts.as_ref().unwrap();
|
|
||||||
|
|
||||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||||
assert_eq!(result.info_data.name, "eppdev-1.com".into());
|
assert_eq!(result.name, "eppdev-1.com");
|
||||||
assert_eq!(result.info_data.roid, "125899511_DOMAIN_COM-VRSN".into());
|
assert_eq!(result.roid, "125899511_DOMAIN_COM-VRSN");
|
||||||
assert_eq!(statuses[0].status, "ok".to_string());
|
assert_eq!(statuses[0].status, "ok".to_string());
|
||||||
assert_eq!(statuses[1].status, "clientTransferProhibited".to_string());
|
assert_eq!(statuses[1].status, "clientTransferProhibited".to_string());
|
||||||
assert_eq!(*registrant, "eppdev-contact-2".into());
|
assert_eq!(*registrant, "eppdev-contact-2");
|
||||||
assert_eq!(contacts[0].id, "eppdev-contact-2".to_string());
|
assert_eq!(contacts[0].id, "eppdev-contact-2".to_string());
|
||||||
assert_eq!(contacts[0].contact_type, "admin".to_string());
|
assert_eq!(contacts[0].contact_type, "admin".to_string());
|
||||||
assert_eq!(contacts[1].id, "eppdev-contact-2".to_string());
|
assert_eq!(contacts[1].id, "eppdev-contact-2".to_string());
|
||||||
assert_eq!(contacts[1].contact_type, "tech".to_string());
|
assert_eq!(contacts[1].contact_type, "tech".to_string());
|
||||||
assert_eq!(contacts[2].id, "eppdev-contact-2".to_string());
|
assert_eq!(contacts[2].id, "eppdev-contact-2".to_string());
|
||||||
assert_eq!(contacts[2].contact_type, "billing".to_string());
|
assert_eq!(contacts[2].contact_type, "billing".to_string());
|
||||||
assert_eq!((*ns)[0], "ns1.eppdev-1.com".into());
|
|
||||||
assert_eq!((*ns)[1], "ns2.eppdev-1.com".into());
|
|
||||||
assert_eq!((*hosts)[0], "ns1.eppdev-1.com".into());
|
|
||||||
assert_eq!((*hosts)[1], "ns2.eppdev-1.com".into());
|
|
||||||
assert_eq!(result.info_data.client_id, "eppdev".into());
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*result.info_data.creator_id.as_ref().unwrap(),
|
ns.ns[0],
|
||||||
"SYSTEM".into()
|
HostInfo::Obj(HostObj {
|
||||||
|
name: "ns1.eppdev-1.com".into()
|
||||||
|
})
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*result.info_data.created_at.as_ref().unwrap(),
|
ns.ns[1],
|
||||||
|
HostInfo::Obj(HostObj {
|
||||||
|
name: "ns2.eppdev-1.com".into()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert_eq!((*hosts)[0], "ns1.eppdev-1.com");
|
||||||
|
assert_eq!((*hosts)[1], "ns2.eppdev-1.com");
|
||||||
|
assert_eq!(result.client_id, "eppdev");
|
||||||
|
assert_eq!(*result.creator_id.as_ref().unwrap(), "SYSTEM");
|
||||||
|
assert_eq!(
|
||||||
|
*result.created_at.as_ref().unwrap(),
|
||||||
Utc.with_ymd_and_hms(2021, 7, 23, 15, 31, 20).unwrap()
|
Utc.with_ymd_and_hms(2021, 7, 23, 15, 31, 20).unwrap()
|
||||||
);
|
);
|
||||||
|
assert_eq!(*result.updater_id.as_ref().unwrap(), "SYSTEM");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*result.info_data.updater_id.as_ref().unwrap(),
|
*result.updated_at.as_ref().unwrap(),
|
||||||
"SYSTEM".into()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
*result.info_data.updated_at.as_ref().unwrap(),
|
|
||||||
Utc.with_ymd_and_hms(2021, 7, 23, 15, 31, 21).unwrap()
|
Utc.with_ymd_and_hms(2021, 7, 23, 15, 31, 21).unwrap()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*result.info_data.expiring_at.as_ref().unwrap(),
|
*result.expiring_at.as_ref().unwrap(),
|
||||||
Utc.with_ymd_and_hms(2023, 7, 23, 15, 31, 20).unwrap()
|
Utc.with_ymd_and_hms(2023, 7, 23, 15, 31, 20).unwrap()
|
||||||
);
|
);
|
||||||
assert_eq!(auth_info.password, "epP4uthd#v".into());
|
assert_eq!(auth_info.password, "epP4uthd#v");
|
||||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
//! Types for EPP domain renew request
|
//! Types for EPP domain renew request
|
||||||
|
|
||||||
use chrono::{DateTime, NaiveDate, Utc};
|
use chrono::{DateTime, NaiveDate, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use instant_xml::{FromXml, ToXml};
|
||||||
|
|
||||||
use super::{Period, XMLNS};
|
use super::{Period, XMLNS};
|
||||||
use crate::common::{NoExtension, StringValue};
|
use crate::common::{NoExtension, EPP_XMLNS};
|
||||||
use crate::request::{Command, Transaction};
|
use crate::request::{Command, Transaction};
|
||||||
|
|
||||||
impl<'a> Transaction<NoExtension> for DomainRenew<'a> {}
|
impl<'a> Transaction<NoExtension> for DomainRenew<'a> {}
|
||||||
|
@ -16,12 +16,10 @@ impl<'a> Command for DomainRenew<'a> {
|
||||||
|
|
||||||
impl<'a> DomainRenew<'a> {
|
impl<'a> DomainRenew<'a> {
|
||||||
pub fn new(name: &'a str, current_expiry_date: NaiveDate, period: Period) -> Self {
|
pub fn new(name: &'a str, current_expiry_date: NaiveDate, period: Period) -> Self {
|
||||||
let exp_date_str = current_expiry_date.format("%Y-%m-%d").to_string().into();
|
|
||||||
Self {
|
Self {
|
||||||
domain: DomainRenewRequestData {
|
domain: DomainRenewRequestData {
|
||||||
xmlns: XMLNS,
|
name,
|
||||||
name: name.into(),
|
current_expiry_date,
|
||||||
current_expiry_date: exp_date_str,
|
|
||||||
period,
|
period,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -31,48 +29,38 @@ impl<'a> DomainRenew<'a> {
|
||||||
// Request
|
// Request
|
||||||
|
|
||||||
/// Type for data under the domain <renew> tag
|
/// Type for data under the domain <renew> tag
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "renew", ns(XMLNS))]
|
||||||
pub struct DomainRenewRequestData<'a> {
|
pub struct DomainRenewRequestData<'a> {
|
||||||
/// XML namespace for domain commands
|
|
||||||
#[serde(rename = "xmlns:domain")]
|
|
||||||
xmlns: &'a str,
|
|
||||||
/// The name of the domain to be renewed
|
/// The name of the domain to be renewed
|
||||||
#[serde(rename = "domain:name")]
|
name: &'a str,
|
||||||
name: StringValue<'a>,
|
|
||||||
/// The current expiry date of the domain in 'Y-m-d' format
|
/// The current expiry date of the domain in 'Y-m-d' format
|
||||||
#[serde(rename = "domain:curExpDate")]
|
#[xml(rename = "curExpDate")]
|
||||||
current_expiry_date: StringValue<'a>,
|
current_expiry_date: NaiveDate,
|
||||||
/// The period of renewal
|
/// The period of renewal
|
||||||
#[serde(rename = "domain:period")]
|
|
||||||
period: Period,
|
period: Period,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
/// Type for EPP XML <renew> command for domains
|
/// Type for EPP XML <renew> command for domains
|
||||||
|
#[xml(rename = "renew", ns(EPP_XMLNS))]
|
||||||
pub struct DomainRenew<'a> {
|
pub struct DomainRenew<'a> {
|
||||||
/// The data under the <renew> tag for the domain renewal
|
/// The data under the <renew> tag for the domain renewal
|
||||||
#[serde(rename = "domain:renew")]
|
#[xml(rename = "renew")]
|
||||||
domain: DomainRenewRequestData<'a>,
|
domain: DomainRenewRequestData<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Response
|
// Response
|
||||||
|
|
||||||
/// Type that represents the <renData> tag for domain renew response
|
/// Type that represents the <renData> tag for domain renew response
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Debug, FromXml)]
|
||||||
pub struct DomainRenewResponseData {
|
#[xml(rename = "renData", ns(XMLNS))]
|
||||||
/// The name of the domain
|
|
||||||
pub name: StringValue<'static>,
|
|
||||||
/// The new expiry date after renewal
|
|
||||||
#[serde(rename = "exDate")]
|
|
||||||
pub expiring_at: Option<DateTime<Utc>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Type that represents the <resData> tag for domain renew response
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
pub struct DomainRenewResponse {
|
pub struct DomainRenewResponse {
|
||||||
/// Data under the <renData> tag
|
/// The name of the domain
|
||||||
#[serde(rename = "renData")]
|
pub name: String,
|
||||||
pub renew_data: DomainRenewResponseData,
|
/// The new expiry date after renewal
|
||||||
|
#[xml(rename = "exDate")]
|
||||||
|
pub expiring_at: Option<DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -97,13 +85,13 @@ mod tests {
|
||||||
let result = object.res_data().unwrap();
|
let result = object.res_data().unwrap();
|
||||||
|
|
||||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||||
assert_eq!(result.renew_data.name, "eppdev-1.com".into());
|
assert_eq!(result.name, "eppdev-1.com");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*result.renew_data.expiring_at.as_ref().unwrap(),
|
*result.expiring_at.as_ref().unwrap(),
|
||||||
Utc.with_ymd_and_hms(2024, 7, 23, 15, 31, 20).unwrap()
|
Utc.with_ymd_and_hms(2024, 7, 23, 15, 31, 20).unwrap()
|
||||||
);
|
);
|
||||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
//! Types for EPP domain transfer request
|
//! Types for EPP domain transfer request
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use instant_xml::{FromXml, ToXml};
|
||||||
|
|
||||||
use super::{DomainAuthInfo, Period, XMLNS};
|
use super::{DomainAuthInfo, Period, XMLNS};
|
||||||
use crate::common::{NoExtension, StringValue};
|
use crate::common::{NoExtension, EPP_XMLNS};
|
||||||
use crate::request::{Command, Transaction};
|
use crate::request::{Command, Transaction};
|
||||||
|
|
||||||
impl<'a> Transaction<NoExtension> for DomainTransfer<'a> {}
|
impl<'a> Transaction<NoExtension> for DomainTransfer<'a> {}
|
||||||
|
|
||||||
impl<'a> Command for DomainTransfer<'a> {
|
impl<'a> Command for DomainTransfer<'a> {
|
||||||
type Response = DomainTransferResponse;
|
type Response = DomainTransferResponseData;
|
||||||
const COMMAND: &'static str = "transfer";
|
const COMMAND: &'static str = "transfer";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,8 +54,7 @@ impl<'a> DomainTransfer<'a> {
|
||||||
Self {
|
Self {
|
||||||
operation,
|
operation,
|
||||||
domain: DomainTransferReqData {
|
domain: DomainTransferReqData {
|
||||||
xmlns: XMLNS,
|
name,
|
||||||
name: name.into(),
|
|
||||||
period,
|
period,
|
||||||
auth_info,
|
auth_info,
|
||||||
},
|
},
|
||||||
|
@ -66,71 +65,60 @@ impl<'a> DomainTransfer<'a> {
|
||||||
// Request
|
// Request
|
||||||
|
|
||||||
/// Type for elements under the domain <transfer> tag
|
/// Type for elements under the domain <transfer> tag
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "transfer", ns(XMLNS))]
|
||||||
pub struct DomainTransferReqData<'a> {
|
pub struct DomainTransferReqData<'a> {
|
||||||
/// XML namespace for domain commands
|
|
||||||
#[serde(rename = "xmlns:domain")]
|
|
||||||
xmlns: &'a str,
|
|
||||||
/// The name of the domain under transfer
|
/// The name of the domain under transfer
|
||||||
#[serde(rename = "domain:name")]
|
name: &'a str,
|
||||||
name: StringValue<'a>,
|
|
||||||
/// The period of renewal upon a successful transfer
|
/// The period of renewal upon a successful transfer
|
||||||
/// Only applicable in case of a transfer request
|
/// Only applicable in case of a transfer request
|
||||||
#[serde(rename = "domain:period")]
|
|
||||||
period: Option<Period>,
|
period: Option<Period>,
|
||||||
/// The authInfo for the domain under transfer
|
/// The authInfo for the domain under transfer
|
||||||
/// Only applicable to domain transfer and domain transfer query requests
|
/// Only applicable to domain transfer and domain transfer query requests
|
||||||
#[serde(rename = "domain:authInfo")]
|
#[xml(rename = "authInfo")]
|
||||||
auth_info: Option<DomainAuthInfo<'a>>,
|
auth_info: Option<DomainAuthInfo<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "transfer", ns(EPP_XMLNS))]
|
||||||
/// Type for EPP XML <transfer> command for domains
|
/// Type for EPP XML <transfer> command for domains
|
||||||
pub struct DomainTransfer<'a> {
|
pub struct DomainTransfer<'a> {
|
||||||
/// The transfer operation to perform indicated by the 'op' attr
|
/// The transfer operation to perform indicated by the 'op' attr
|
||||||
/// The values are one of transfer or query
|
/// The values are one of transfer or query
|
||||||
#[serde(rename = "op")]
|
#[xml(rename = "op", attribute)]
|
||||||
operation: &'a str,
|
operation: &'a str,
|
||||||
/// The data under the <transfer> tag in the transfer request
|
/// The data under the <transfer> tag in the transfer request
|
||||||
#[serde(rename = "domain:transfer")]
|
|
||||||
domain: DomainTransferReqData<'a>,
|
domain: DomainTransferReqData<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Response
|
// Response
|
||||||
|
|
||||||
/// Type that represents the <trnData> tag for domain transfer response
|
/// Type that represents the <trnData> tag for domain transfer response
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Debug, FromXml)]
|
||||||
|
#[xml(rename = "trnData", ns(XMLNS))]
|
||||||
pub struct DomainTransferResponseData {
|
pub struct DomainTransferResponseData {
|
||||||
/// The domain name
|
/// The domain name
|
||||||
pub name: StringValue<'static>,
|
pub name: String,
|
||||||
/// The domain transfer status
|
/// The domain transfer status
|
||||||
#[serde(rename = "trStatus")]
|
#[xml(rename = "trStatus")]
|
||||||
pub transfer_status: StringValue<'static>,
|
pub transfer_status: String,
|
||||||
/// The epp user who requested the transfer
|
/// The epp user who requested the transfer
|
||||||
#[serde(rename = "reID")]
|
#[xml(rename = "reID")]
|
||||||
pub requester_id: StringValue<'static>,
|
pub requester_id: String,
|
||||||
/// The transfer rquest date
|
/// The transfer rquest date
|
||||||
#[serde(rename = "reDate")]
|
#[xml(rename = "reDate")]
|
||||||
pub requested_at: DateTime<Utc>,
|
pub requested_at: DateTime<Utc>,
|
||||||
/// The epp user who should acknowledge the transfer request
|
/// The epp user who should acknowledge the transfer request
|
||||||
#[serde(rename = "acID")]
|
#[xml(rename = "acID")]
|
||||||
pub ack_id: StringValue<'static>,
|
pub ack_id: String,
|
||||||
/// THe date by which the acknowledgment should be made
|
/// THe date by which the acknowledgment should be made
|
||||||
#[serde(rename = "acDate")]
|
#[xml(rename = "acDate")]
|
||||||
pub ack_by: DateTime<Utc>,
|
pub ack_by: DateTime<Utc>,
|
||||||
/// The domain expiry date
|
/// The domain expiry date
|
||||||
#[serde(rename = "exDate")]
|
#[xml(rename = "exDate")]
|
||||||
pub expiring_at: Option<DateTime<Utc>>,
|
pub expiring_at: Option<DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type that represents the <resData> tag for domain transfer response
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
pub struct DomainTransferResponse {
|
|
||||||
/// Data under the <trnData> tag
|
|
||||||
#[serde(rename = "trnData")]
|
|
||||||
pub transfer_data: DomainTransferResponseData,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use chrono::{TimeZone, Utc};
|
use chrono::{TimeZone, Utc};
|
||||||
|
@ -182,26 +170,26 @@ mod tests {
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
object.result.message,
|
object.result.message,
|
||||||
"Command completed successfully; action pending".into()
|
"Command completed successfully; action pending"
|
||||||
);
|
);
|
||||||
assert_eq!(result.transfer_data.name, "eppdev-transfer.com".into());
|
assert_eq!(result.name, "eppdev-transfer.com");
|
||||||
assert_eq!(result.transfer_data.transfer_status, "pending".into());
|
assert_eq!(result.transfer_status, "pending");
|
||||||
assert_eq!(result.transfer_data.requester_id, "eppdev".into());
|
assert_eq!(result.requester_id, "eppdev");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result.transfer_data.requested_at,
|
result.requested_at,
|
||||||
Utc.with_ymd_and_hms(2021, 7, 23, 15, 31, 21).unwrap(),
|
Utc.with_ymd_and_hms(2021, 7, 23, 15, 31, 21).unwrap(),
|
||||||
);
|
);
|
||||||
assert_eq!(result.transfer_data.ack_id, "ClientY".into());
|
assert_eq!(result.ack_id, "ClientY");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result.transfer_data.ack_by,
|
result.ack_by,
|
||||||
Utc.with_ymd_and_hms(2021, 7, 28, 15, 31, 21).unwrap()
|
Utc.with_ymd_and_hms(2021, 7, 28, 15, 31, 21).unwrap()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result.transfer_data.expiring_at,
|
result.expiring_at,
|
||||||
Utc.with_ymd_and_hms(2022, 7, 2, 14, 53, 19).single(),
|
Utc.with_ymd_and_hms(2022, 7, 2, 14, 53, 19).single(),
|
||||||
);
|
);
|
||||||
assert_eq!(*object.tr_ids.client_tr_id.as_ref().unwrap(), CLTRID.into());
|
assert_eq!(*object.tr_ids.client_tr_id.as_ref().unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -209,9 +197,9 @@ mod tests {
|
||||||
let object = response_from_file::<DomainTransfer>("response/domain/transfer_approve.xml");
|
let object = response_from_file::<DomainTransfer>("response/domain/transfer_approve.xml");
|
||||||
|
|
||||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -219,9 +207,9 @@ mod tests {
|
||||||
let object = response_from_file::<DomainTransfer>("response/domain/transfer_reject.xml");
|
let object = response_from_file::<DomainTransfer>("response/domain/transfer_reject.xml");
|
||||||
|
|
||||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -229,9 +217,9 @@ mod tests {
|
||||||
let object = response_from_file::<DomainTransfer>("response/domain/transfer_cancel.xml");
|
let object = response_from_file::<DomainTransfer>("response/domain/transfer_cancel.xml");
|
||||||
|
|
||||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -241,24 +229,24 @@ mod tests {
|
||||||
let result = object.res_data().unwrap();
|
let result = object.res_data().unwrap();
|
||||||
|
|
||||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||||
assert_eq!(result.transfer_data.name, "eppdev-transfer.com".into());
|
assert_eq!(result.name, "eppdev-transfer.com");
|
||||||
assert_eq!(result.transfer_data.transfer_status, "pending".into());
|
assert_eq!(result.transfer_status, "pending");
|
||||||
assert_eq!(result.transfer_data.requester_id, "eppdev".into());
|
assert_eq!(result.requester_id, "eppdev");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result.transfer_data.requested_at,
|
result.requested_at,
|
||||||
Utc.with_ymd_and_hms(2021, 7, 23, 15, 31, 21).unwrap()
|
Utc.with_ymd_and_hms(2021, 7, 23, 15, 31, 21).unwrap()
|
||||||
);
|
);
|
||||||
assert_eq!(result.transfer_data.ack_id, "ClientY".into());
|
assert_eq!(result.ack_id, "ClientY");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result.transfer_data.ack_by,
|
result.ack_by,
|
||||||
Utc.with_ymd_and_hms(2021, 7, 28, 15, 31, 21).unwrap()
|
Utc.with_ymd_and_hms(2021, 7, 28, 15, 31, 21).unwrap()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result.transfer_data.expiring_at,
|
result.expiring_at,
|
||||||
Utc.with_ymd_and_hms(2022, 7, 2, 14, 53, 19).single()
|
Utc.with_ymd_and_hms(2022, 7, 2, 14, 53, 19).single()
|
||||||
);
|
);
|
||||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
//! Types for EPP domain check request
|
//! Types for EPP domain check request
|
||||||
//!
|
|
||||||
use super::{DomainAuthInfo, DomainContact, HostList, XMLNS};
|
use instant_xml::ToXml;
|
||||||
|
|
||||||
|
use super::{DomainAuthInfo, DomainContact, NameServers, Status, XMLNS};
|
||||||
use crate::{
|
use crate::{
|
||||||
common::{NoExtension, ObjectStatus, StringValue},
|
common::{NoExtension, EPP_XMLNS},
|
||||||
request::{Command, Transaction},
|
request::{Command, Transaction},
|
||||||
};
|
};
|
||||||
|
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
impl<'a> Transaction<NoExtension> for DomainUpdate<'a> {}
|
impl<'a> Transaction<NoExtension> for DomainUpdate<'a> {}
|
||||||
|
|
||||||
impl<'a> Command for DomainUpdate<'a> {
|
impl<'a> Command for DomainUpdate<'a> {
|
||||||
|
@ -19,8 +19,7 @@ impl<'a> DomainUpdate<'a> {
|
||||||
pub fn new(name: &'a str) -> Self {
|
pub fn new(name: &'a str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
domain: DomainUpdateRequestData {
|
domain: DomainUpdateRequestData {
|
||||||
xmlns: XMLNS,
|
name,
|
||||||
name: name.into(),
|
|
||||||
add: None,
|
add: None,
|
||||||
remove: None,
|
remove: None,
|
||||||
change_info: None,
|
change_info: None,
|
||||||
|
@ -34,75 +33,82 @@ impl<'a> DomainUpdate<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the data for the <add> tag
|
/// Sets the data for the <add> tag
|
||||||
pub fn add(&mut self, add: DomainAddRemove<'a>) {
|
pub fn add(&mut self, add: DomainAdd<'a>) {
|
||||||
self.domain.add = Some(add);
|
self.domain.add = Some(add);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the data for the <rem> tag
|
/// Sets the data for the <rem> tag
|
||||||
pub fn remove(&mut self, remove: DomainAddRemove<'a>) {
|
pub fn remove(&mut self, remove: DomainRemove<'a>) {
|
||||||
self.domain.remove = Some(remove);
|
self.domain.remove = Some(remove);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type for elements under the <chg> tag for domain update
|
/// Type for elements under the <chg> tag for domain update
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "chg", ns(XMLNS))]
|
||||||
pub struct DomainChangeInfo<'a> {
|
pub struct DomainChangeInfo<'a> {
|
||||||
/// The new registrant contact for the domain
|
/// The new registrant contact for the domain
|
||||||
#[serde(rename = "domain:registrant")]
|
pub registrant: Option<&'a str>,
|
||||||
pub registrant: Option<StringValue<'a>>,
|
|
||||||
/// The new auth info for the domain
|
/// The new auth info for the domain
|
||||||
#[serde(rename = "domain:authInfo")]
|
|
||||||
pub auth_info: Option<DomainAuthInfo<'a>>,
|
pub auth_info: Option<DomainAuthInfo<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type for elements under the <add> and <rem> tags for domain update
|
/// Type for elements under the <add> and <rem> tags for domain update
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
pub struct DomainAddRemove<'a> {
|
#[xml(rename = "add", ns(XMLNS))]
|
||||||
|
pub struct DomainAdd<'a> {
|
||||||
/// The list of nameservers to add or remove
|
/// The list of nameservers to add or remove
|
||||||
/// Type T can be either a `HostObjList` or `HostAttrList`
|
/// Type T can be either a `HostObjList` or `HostAttrList`
|
||||||
#[serde(rename = "domain:ns")]
|
pub ns: Option<NameServers<'a>>,
|
||||||
pub ns: Option<HostList<'a>>,
|
|
||||||
/// The list of contacts to add to or remove from the domain
|
/// The list of contacts to add to or remove from the domain
|
||||||
#[serde(rename = "domain:contact")]
|
|
||||||
pub contacts: Option<&'a [DomainContact<'a>]>,
|
pub contacts: Option<&'a [DomainContact<'a>]>,
|
||||||
/// The list of statuses to add to or remove from the domain
|
/// The list of statuses to add to or remove from the domain
|
||||||
#[serde(rename = "domain:status")]
|
pub statuses: Option<&'a [Status<'a>]>,
|
||||||
pub statuses: Option<&'a [ObjectStatus<'a>]>,
|
}
|
||||||
|
|
||||||
|
/// Type for elements under the <add> and <rem> tags for domain update
|
||||||
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "rem", ns(XMLNS))]
|
||||||
|
pub struct DomainRemove<'a> {
|
||||||
|
/// The list of nameservers to add or remove
|
||||||
|
/// Type T can be either a `HostObjList` or `HostAttrList`
|
||||||
|
pub ns: Option<NameServers<'a>>,
|
||||||
|
/// The list of contacts to add to or remove from the domain
|
||||||
|
pub contacts: Option<&'a [DomainContact<'a>]>,
|
||||||
|
/// The list of statuses to add to or remove from the domain
|
||||||
|
pub statuses: Option<&'a [Status<'a>]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type for elements under the <update> tag for domain update
|
/// Type for elements under the <update> tag for domain update
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "update", ns(XMLNS))]
|
||||||
pub struct DomainUpdateRequestData<'a> {
|
pub struct DomainUpdateRequestData<'a> {
|
||||||
/// XML namespace for domain commands
|
|
||||||
#[serde(rename = "xmlns:domain")]
|
|
||||||
pub xmlns: &'a str,
|
|
||||||
/// The name of the domain to update
|
/// The name of the domain to update
|
||||||
#[serde(rename = "domain:name")]
|
pub name: &'a str,
|
||||||
pub name: StringValue<'a>,
|
|
||||||
/// `DomainAddRemove` Object containing the list of elements to be added
|
/// `DomainAddRemove` Object containing the list of elements to be added
|
||||||
/// to the domain
|
/// to the domain
|
||||||
#[serde(rename = "domain:add")]
|
pub add: Option<DomainAdd<'a>>,
|
||||||
pub add: Option<DomainAddRemove<'a>>,
|
|
||||||
/// `DomainAddRemove` Object containing the list of elements to be removed
|
/// `DomainAddRemove` Object containing the list of elements to be removed
|
||||||
/// from the domain
|
/// from the domain
|
||||||
#[serde(rename = "domain:rem")]
|
pub remove: Option<DomainRemove<'a>>,
|
||||||
pub remove: Option<DomainAddRemove<'a>>,
|
|
||||||
/// The data under the <chg> tag for domain update
|
/// The data under the <chg> tag for domain update
|
||||||
#[serde(rename = "domain:chg")]
|
#[xml(rename = "domain:chg")]
|
||||||
pub change_info: Option<DomainChangeInfo<'a>>,
|
pub change_info: Option<DomainChangeInfo<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
|
||||||
/// Type for EPP XML <update> command for domains
|
/// Type for EPP XML <update> command for domains
|
||||||
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "update", ns(EPP_XMLNS))]
|
||||||
pub struct DomainUpdate<'a> {
|
pub struct DomainUpdate<'a> {
|
||||||
#[serde(rename = "domain:update")]
|
|
||||||
pub domain: DomainUpdateRequestData<'a>,
|
pub domain: DomainUpdateRequestData<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{DomainAddRemove, DomainAuthInfo, DomainChangeInfo, DomainContact, DomainUpdate};
|
use super::{
|
||||||
use crate::common::ObjectStatus;
|
DomainAdd, DomainAuthInfo, DomainChangeInfo, DomainContact, DomainRemove, DomainUpdate,
|
||||||
|
};
|
||||||
|
use crate::domain::Status;
|
||||||
use crate::response::ResultCode;
|
use crate::response::ResultCode;
|
||||||
use crate::tests::{assert_serialized, response_from_file, CLTRID, SUCCESS_MSG, SVTRID};
|
use crate::tests::{assert_serialized, response_from_file, CLTRID, SUCCESS_MSG, SVTRID};
|
||||||
|
|
||||||
|
@ -110,11 +116,11 @@ mod tests {
|
||||||
fn command() {
|
fn command() {
|
||||||
let mut object = DomainUpdate::new("eppdev.com");
|
let mut object = DomainUpdate::new("eppdev.com");
|
||||||
|
|
||||||
let statuses = &[ObjectStatus {
|
let statuses = &[Status {
|
||||||
status: "clientDeleteProhibited".into(),
|
status: "clientDeleteProhibited".into(),
|
||||||
}];
|
}];
|
||||||
|
|
||||||
let add = DomainAddRemove {
|
let add = DomainAdd {
|
||||||
ns: None,
|
ns: None,
|
||||||
contacts: None,
|
contacts: None,
|
||||||
statuses: Some(statuses),
|
statuses: Some(statuses),
|
||||||
|
@ -125,7 +131,7 @@ mod tests {
|
||||||
id: "eppdev-contact-2".into(),
|
id: "eppdev-contact-2".into(),
|
||||||
}];
|
}];
|
||||||
|
|
||||||
let remove = DomainAddRemove {
|
let remove = DomainRemove {
|
||||||
ns: None,
|
ns: None,
|
||||||
contacts: Some(contacts),
|
contacts: Some(contacts),
|
||||||
statuses: None,
|
statuses: None,
|
||||||
|
@ -147,8 +153,8 @@ mod tests {
|
||||||
let object = response_from_file::<DomainUpdate>("response/domain/update.xml");
|
let object = response_from_file::<DomainUpdate>("response/domain/update.xml");
|
||||||
|
|
||||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,10 +28,10 @@ impl Display for Error {
|
||||||
Error::Command(e) => {
|
Error::Command(e) => {
|
||||||
write!(f, "command error: {}", e.result.message)
|
write!(f, "command error: {}", e.result.message)
|
||||||
}
|
}
|
||||||
Error::Io(e) => write!(f, "I/O error: {}", e),
|
Error::Io(e) => write!(f, "I/O error: {e}"),
|
||||||
Error::Timeout => write!(f, "timeout"),
|
Error::Timeout => write!(f, "timeout"),
|
||||||
Error::Xml(e) => write!(f, "(de)serialization error: {}", e),
|
Error::Xml(e) => write!(f, "(de)serialization error: {e}"),
|
||||||
Error::Other(e) => write!(f, "error: {}", e),
|
Error::Other(e) => write!(f, "error: {e}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,13 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use chrono::FixedOffset;
|
use chrono::FixedOffset;
|
||||||
use serde::Serialize;
|
use instant_xml::ToXml;
|
||||||
|
|
||||||
use crate::common::{NoExtension, StringValue};
|
use crate::common::NoExtension;
|
||||||
use crate::domain::update::DomainUpdate;
|
use crate::domain::update::DomainUpdate;
|
||||||
use crate::request::{Extension, Transaction};
|
use crate::request::{Extension, Transaction};
|
||||||
|
|
||||||
use super::namestore::{NameStore, NameStoreData};
|
use super::namestore::NameStore;
|
||||||
|
|
||||||
pub const XMLNS: &str = "http://www.verisign.com/epp/sync-1.0";
|
pub const XMLNS: &str = "http://www.verisign.com/epp/sync-1.0";
|
||||||
|
|
||||||
|
@ -70,47 +70,35 @@ impl Update {
|
||||||
/// Create a new sync update request
|
/// Create a new sync update request
|
||||||
pub fn new(expiration: GMonthDay) -> Self {
|
pub fn new(expiration: GMonthDay) -> Self {
|
||||||
Self {
|
Self {
|
||||||
data: UpdateData {
|
exp: expiration.to_string(),
|
||||||
xmlns: XMLNS,
|
|
||||||
exp: expiration.to_string().into(),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UpdateWithNameStore<'_> {
|
impl<'a> UpdateWithNameStore<'a> {
|
||||||
/// Create a new sync update with namestore request
|
/// Create a new sync update with namestore request
|
||||||
pub fn new(expiration: GMonthDay, subproduct: &str) -> Self {
|
pub fn new(expiration: GMonthDay, subproduct: &'a str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
sync: Update::new(expiration).data,
|
sync: Update::new(expiration),
|
||||||
namestore: NameStoreData::new(subproduct),
|
namestore: NameStore::new(subproduct),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, ToXml)]
|
||||||
pub struct Update {
|
#[xml(inline)]
|
||||||
#[serde(rename = "sync:update")]
|
|
||||||
pub data: UpdateData,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
pub struct UpdateWithNameStore<'a> {
|
pub struct UpdateWithNameStore<'a> {
|
||||||
#[serde(rename = "sync:update")]
|
pub sync: Update,
|
||||||
pub sync: UpdateData,
|
pub namestore: NameStore<'a>,
|
||||||
#[serde(rename = "namestoreExt:namestoreExt")]
|
|
||||||
pub namestore: NameStoreData<'a>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
|
||||||
/// Type for EPP XML <consolidate> extension
|
/// Type for EPP XML <consolidate> extension
|
||||||
pub struct UpdateData {
|
#[derive(Debug, ToXml)]
|
||||||
/// XML namespace for the consolidate extension
|
#[xml(rename = "update", ns(XMLNS))]
|
||||||
#[serde(rename = "xmlns:sync")]
|
pub struct Update {
|
||||||
pub xmlns: &'static str,
|
|
||||||
/// The expiry date of the domain
|
/// The expiry date of the domain
|
||||||
#[serde(rename = "sync:expMonthDay")]
|
#[xml(rename = "expMonthDay")]
|
||||||
pub exp: StringValue<'static>,
|
pub exp: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
//!
|
//!
|
||||||
//! https://www.verisign.com/assets/epp-sdk/verisign_epp-extension_low-balance_v01.html
|
//! https://www.verisign.com/assets/epp-sdk/verisign_epp-extension_low-balance_v01.html
|
||||||
|
|
||||||
use serde::Deserialize;
|
use instant_xml::FromXml;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, FromXml, PartialEq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[xml(ns(XMLNS), rename = "pollData", rename_all = "camelCase")]
|
||||||
pub struct LowBalance {
|
pub struct LowBalance {
|
||||||
pub registrar_name: String,
|
pub registrar_name: String,
|
||||||
pub credit_limit: String,
|
pub credit_limit: String,
|
||||||
|
@ -13,24 +13,28 @@ pub struct LowBalance {
|
||||||
pub available_credit: String,
|
pub available_credit: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, FromXml, PartialEq)]
|
||||||
|
#[xml(ns(XMLNS), rename = "creditThreshold")]
|
||||||
pub struct Threshold {
|
pub struct Threshold {
|
||||||
|
#[xml(attribute)]
|
||||||
pub r#type: ThresholdType,
|
pub r#type: ThresholdType,
|
||||||
#[serde(rename = "$value")]
|
#[xml(direct)]
|
||||||
pub value: String,
|
pub value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
|
#[derive(Clone, Copy, Debug, FromXml, PartialEq)]
|
||||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
#[xml(scalar, rename_all = "SCREAMING_SNAKE_CASE")]
|
||||||
pub enum ThresholdType {
|
pub enum ThresholdType {
|
||||||
Fixed,
|
Fixed,
|
||||||
Percent,
|
Percent,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const XMLNS: &str = "http://www.verisign.com/epp/lowbalance-poll-1.0";
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::message::poll::{MessageData, MessagePollResponse};
|
use crate::message::poll::MessagePollResponse;
|
||||||
use crate::message::MessagePoll;
|
use crate::message::MessagePoll;
|
||||||
use crate::response::ResultCode;
|
use crate::response::ResultCode;
|
||||||
use crate::tests::{response_from_file, CLTRID, SVTRID};
|
use crate::tests::{response_from_file, CLTRID, SVTRID};
|
||||||
|
@ -40,10 +44,8 @@ mod tests {
|
||||||
let object = response_from_file::<MessagePoll>("response/message/poll_low_balance.xml");
|
let object = response_from_file::<MessagePoll>("response/message/poll_low_balance.xml");
|
||||||
dbg!(&object);
|
dbg!(&object);
|
||||||
|
|
||||||
let low_balance = match object.res_data {
|
let low_balance = match object.res_data() {
|
||||||
Some(MessagePollResponse {
|
Some(MessagePollResponse::LowBalance(low_balance)) => low_balance,
|
||||||
message_data: MessageData::LowBalance(low_balance),
|
|
||||||
}) => low_balance,
|
|
||||||
_ => panic!("Unexpected message data"),
|
_ => panic!("Unexpected message data"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -64,10 +66,10 @@ mod tests {
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
object.result.message,
|
object.result.message,
|
||||||
"Command completed successfully; ack to dequeue".into()
|
"Command completed successfully; ack to dequeue"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,9 @@
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use instant_xml::{FromXml, ToXml};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
common::StringValue,
|
|
||||||
contact::{
|
contact::{
|
||||||
check::ContactCheck, create::ContactCreate, delete::ContactDelete, info::ContactInfo,
|
check::ContactCheck, create::ContactCreate, delete::ContactDelete, info::ContactInfo,
|
||||||
update::ContactUpdate,
|
update::ContactUpdate,
|
||||||
|
@ -51,22 +50,9 @@ impl Transaction<NameStore<'_>> for HostUpdate<'_> {}
|
||||||
|
|
||||||
impl<'a> NameStore<'a> {
|
impl<'a> NameStore<'a> {
|
||||||
/// Create a new RGP restore report request
|
/// Create a new RGP restore report request
|
||||||
pub fn new(subproduct: &str) -> NameStore {
|
pub fn new(subproduct: &'a str) -> NameStore {
|
||||||
NameStore {
|
NameStore {
|
||||||
data: NameStoreData {
|
subproduct: subproduct.into(),
|
||||||
xmlns: XMLNS.into(),
|
|
||||||
subproduct: subproduct.to_owned().into(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> NameStoreData<'a> {
|
|
||||||
/// Create a new RGP restore report request
|
|
||||||
pub fn new(subproduct: &str) -> Self {
|
|
||||||
Self {
|
|
||||||
xmlns: XMLNS.into(),
|
|
||||||
subproduct: subproduct.to_owned().into(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,22 +61,13 @@ impl<'a> Extension for NameStore<'a> {
|
||||||
type Response = NameStore<'static>;
|
type Response = NameStore<'static>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, FromXml, ToXml)]
|
||||||
#[serde(rename = "namestoreExt:namestoreExt")]
|
|
||||||
pub struct NameStore<'a> {
|
|
||||||
#[serde(rename = "namestoreExt:namestoreExt", alias = "namestoreExt")]
|
|
||||||
pub data: NameStoreData<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
/// Type for EPP XML <namestoreExt> extension
|
/// Type for EPP XML <namestoreExt> extension
|
||||||
pub struct NameStoreData<'a> {
|
#[xml(rename = "namestoreExt", ns(XMLNS))]
|
||||||
/// XML namespace for the RGP restore extension
|
pub struct NameStore<'a> {
|
||||||
#[serde(rename = "xmlns:namestoreExt", alias = "xmlns")]
|
|
||||||
pub xmlns: Cow<'a, str>,
|
|
||||||
/// The object holding the list of domains to be checked
|
/// The object holding the list of domains to be checked
|
||||||
#[serde(rename = "namestoreExt:subProduct", alias = "subProduct")]
|
#[xml(rename = "subProduct")]
|
||||||
pub subproduct: StringValue<'a>,
|
pub subproduct: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -118,7 +95,7 @@ mod tests {
|
||||||
let object = response_from_file_with_ext::<DomainCheck, NameStore>(
|
let object = response_from_file_with_ext::<DomainCheck, NameStore>(
|
||||||
"response/extensions/namestore.xml",
|
"response/extensions/namestore.xml",
|
||||||
);
|
);
|
||||||
let ext = object.extension.unwrap();
|
let ext = object.extension().unwrap();
|
||||||
assert_eq!(ext.data.subproduct, "com".into());
|
assert_eq!(ext.subproduct, "com");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,4 @@
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
pub mod report;
|
pub mod report;
|
||||||
pub mod request;
|
pub mod request;
|
||||||
|
|
||||||
pub const XMLNS: &str = "urn:ietf:params:xml:ns:rgp-1.0";
|
pub const XMLNS: &str = "urn:ietf:params:xml:ns:rgp-1.0";
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
|
||||||
pub struct Update<T> {
|
|
||||||
#[serde(
|
|
||||||
rename = "rgp:update",
|
|
||||||
alias = "update",
|
|
||||||
alias = "upData",
|
|
||||||
alias = "infData"
|
|
||||||
)]
|
|
||||||
pub data: T,
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
//! Types for EPP RGP restore report
|
//! Types for EPP RGP restore report
|
||||||
|
|
||||||
use crate::common::{NoExtension, StringValue};
|
use chrono::{DateTime, SecondsFormat, Utc};
|
||||||
|
use instant_xml::ToXml;
|
||||||
|
|
||||||
|
use crate::common::NoExtension;
|
||||||
use crate::domain::update::DomainUpdate;
|
use crate::domain::update::DomainUpdate;
|
||||||
use crate::request::{Extension, Transaction};
|
use crate::request::{Extension, Transaction};
|
||||||
use chrono::{DateTime, SecondsFormat, Utc};
|
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
use super::{Update, XMLNS};
|
use super::XMLNS;
|
||||||
|
|
||||||
impl<'a> Transaction<Update<RgpRestoreReport<'a>>> for DomainUpdate<'a> {}
|
impl<'a> Transaction<Update<RgpRestoreReport<'a>>> for DomainUpdate<'a> {}
|
||||||
|
|
||||||
|
@ -18,28 +19,19 @@ impl<'a> RgpRestoreReport<'a> {
|
||||||
deleted_at: DateTime<Utc>,
|
deleted_at: DateTime<Utc>,
|
||||||
restored_at: DateTime<Utc>,
|
restored_at: DateTime<Utc>,
|
||||||
restore_reason: &'a str,
|
restore_reason: &'a str,
|
||||||
statements: &[&'a str],
|
statements: &'a [&'a str],
|
||||||
other: &'a str,
|
other: &'a str,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let statements = statements.iter().map(|&s| s.into()).collect();
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
xmlns: XMLNS,
|
op: "report",
|
||||||
restore: RgpRestoreReportSection {
|
report: RgpRestoreReportSectionData {
|
||||||
op: "report",
|
pre_data,
|
||||||
report: RgpRestoreReportSectionData {
|
post_data,
|
||||||
pre_data: pre_data.into(),
|
deleted_at: deleted_at.to_rfc3339_opts(SecondsFormat::AutoSi, true),
|
||||||
post_data: post_data.into(),
|
restored_at: restored_at.to_rfc3339_opts(SecondsFormat::AutoSi, true),
|
||||||
deleted_at: deleted_at
|
restore_reason,
|
||||||
.to_rfc3339_opts(SecondsFormat::AutoSi, true)
|
statements,
|
||||||
.into(),
|
other,
|
||||||
restored_at: restored_at
|
|
||||||
.to_rfc3339_opts(SecondsFormat::AutoSi, true)
|
|
||||||
.into(),
|
|
||||||
restore_reason: restore_reason.into(),
|
|
||||||
statements,
|
|
||||||
other: other.into(),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,53 +41,51 @@ impl<'a> Extension for Update<RgpRestoreReport<'a>> {
|
||||||
type Response = NoExtension;
|
type Response = NoExtension;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "update", ns(XMLNS))]
|
||||||
|
pub struct Update<T> {
|
||||||
|
pub data: T,
|
||||||
|
}
|
||||||
|
|
||||||
/// Type corresponding to the <report> section in the EPP rgp restore extension
|
/// Type corresponding to the <report> section in the EPP rgp restore extension
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "report", ns(XMLNS))]
|
||||||
pub struct RgpRestoreReportSectionData<'a> {
|
pub struct RgpRestoreReportSectionData<'a> {
|
||||||
/// The pre-delete registration date
|
/// The pre-delete registration date
|
||||||
#[serde(rename = "rgp:preData")]
|
#[xml(rename = "preData")]
|
||||||
pre_data: StringValue<'a>,
|
pre_data: &'a str,
|
||||||
/// The post-delete registration date
|
/// The post-delete registration date
|
||||||
#[serde(rename = "rgp:postData")]
|
#[xml(rename = "postData")]
|
||||||
post_data: StringValue<'a>,
|
post_data: &'a str,
|
||||||
/// The domain deletion date
|
/// The domain deletion date
|
||||||
#[serde(rename = "rgp:delTime")]
|
#[xml(rename = "delTime")]
|
||||||
deleted_at: StringValue<'a>,
|
deleted_at: String,
|
||||||
/// The domain restore request date
|
/// The domain restore request date
|
||||||
#[serde(rename = "rgp:resTime")]
|
#[xml(rename = "resTime")]
|
||||||
restored_at: StringValue<'a>,
|
restored_at: String,
|
||||||
/// The reason for domain restoration
|
/// The reason for domain restoration
|
||||||
#[serde(rename = "rgp:resReason")]
|
#[xml(rename = "resReason")]
|
||||||
restore_reason: StringValue<'a>,
|
restore_reason: &'a str,
|
||||||
/// The registrar's statements on the domain restoration
|
/// The registrar's statements on the domain restoration
|
||||||
#[serde(rename = "rgp:statement")]
|
#[xml(rename = "statement")]
|
||||||
statements: Vec<StringValue<'a>>,
|
statements: &'a [&'a str],
|
||||||
/// Other remarks for domain restoration
|
/// Other remarks for domain restoration
|
||||||
#[serde(rename = "rgp:other")]
|
#[xml(rename = "other")]
|
||||||
other: StringValue<'a>,
|
other: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type corresponding to the <restore> section in the rgp restore extension
|
#[derive(Debug, ToXml)]
|
||||||
#[derive(Serialize, Debug)]
|
/// Type for EPP XML <check> command for domains
|
||||||
pub struct RgpRestoreReportSection<'a> {
|
#[xml(rename = "restore", ns(XMLNS))]
|
||||||
|
pub struct RgpRestoreReport<'a> {
|
||||||
/// The value of the op attribute for the <restore> tag
|
/// The value of the op attribute for the <restore> tag
|
||||||
|
#[xml(attribute)]
|
||||||
op: &'a str,
|
op: &'a str,
|
||||||
/// Data for the <report> tag
|
/// Data for the <report> tag
|
||||||
#[serde(rename = "rgp:report")]
|
#[xml(rename = "rgp:report")]
|
||||||
report: RgpRestoreReportSectionData<'a>,
|
report: RgpRestoreReportSectionData<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
|
||||||
/// Type for EPP XML <check> command for domains
|
|
||||||
pub struct RgpRestoreReport<'a> {
|
|
||||||
/// XML namespace for the RGP restore extension
|
|
||||||
#[serde(rename = "xmlns:rgp")]
|
|
||||||
xmlns: &'a str,
|
|
||||||
/// The object holding the list of domains to be checked
|
|
||||||
#[serde(rename = "rgp:restore")]
|
|
||||||
restore: RgpRestoreReportSection<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
|
@ -1,75 +1,86 @@
|
||||||
//! Types for EPP RGP restore request
|
//! Types for EPP RGP restore request
|
||||||
|
|
||||||
|
use instant_xml::{FromXml, ToXml};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
domain::{info::DomainInfo, update::DomainUpdate},
|
domain::{info::DomainInfo, update::DomainUpdate},
|
||||||
request::{Extension, Transaction},
|
request::{Extension, Transaction},
|
||||||
};
|
};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use super::XMLNS;
|
||||||
|
|
||||||
use super::{Update, XMLNS};
|
|
||||||
|
|
||||||
impl<'a> Transaction<Update<RgpRestoreRequest<'a>>> for DomainUpdate<'a> {}
|
impl<'a> Transaction<Update<RgpRestoreRequest<'a>>> for DomainUpdate<'a> {}
|
||||||
|
|
||||||
impl<'a> Transaction<Update<RgpRestoreRequest<'a>>> for DomainInfo<'a> {}
|
impl<'a> Transaction<Update<RgpRestoreRequest<'a>>> for DomainInfo<'a> {}
|
||||||
|
|
||||||
impl<'a> Extension for Update<RgpRestoreRequest<'a>> {
|
impl<'a> Extension for Update<RgpRestoreRequest<'a>> {
|
||||||
type Response = Update<RgpRequestResponse>;
|
type Response = RgpRequestResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request
|
// Request
|
||||||
|
|
||||||
/// Type corresponding to the <restore> tag for an rgp restore request
|
#[derive(Debug, FromXml, ToXml)]
|
||||||
#[derive(Serialize, Debug)]
|
#[xml(rename = "update", ns(XMLNS))]
|
||||||
pub struct RgpRestoreRequestData<'a> {
|
pub struct Update<T> {
|
||||||
/// The value of the op attribute in the <restore> tag
|
pub data: T,
|
||||||
pub op: &'a str,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
/// Type corresponding to the <restore> tag for an rgp restore request
|
||||||
/// Type for EPP XML <check> command for domains
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "restore", ns(XMLNS))]
|
||||||
pub struct RgpRestoreRequest<'a> {
|
pub struct RgpRestoreRequest<'a> {
|
||||||
/// XML namespace for the RGP restore extension
|
/// The value of the op attribute in the <restore> tag
|
||||||
#[serde(rename = "xmlns:rgp")]
|
#[xml(attribute)]
|
||||||
xmlns: &'a str,
|
pub op: &'a str,
|
||||||
/// The object holding the list of domains to be checked
|
|
||||||
#[serde(rename = "rgp:restore")]
|
|
||||||
restore: RgpRestoreRequestData<'a>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for RgpRestoreRequest<'static> {
|
impl Default for RgpRestoreRequest<'static> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self { op: "request" }
|
||||||
xmlns: XMLNS,
|
|
||||||
restore: RgpRestoreRequestData { op: "request" },
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Response
|
// Response
|
||||||
|
|
||||||
/// Type that represents the <rgpStatus> tag for domain rgp restore request response
|
/// Type that represents the <rgpStatus> tag for domain rgp restore request response
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Debug, FromXml)]
|
||||||
|
#[xml(rename = "rgpStatus", ns(XMLNS))]
|
||||||
pub struct RgpStatus {
|
pub struct RgpStatus {
|
||||||
/// The domain RGP status
|
/// The domain RGP status
|
||||||
#[serde(rename = "s")]
|
#[xml(rename = "s", attribute)]
|
||||||
pub status: String,
|
pub status: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Debug, FromXml)]
|
||||||
#[serde(rename = "upData")]
|
#[xml(rename = "upData", ns(XMLNS))]
|
||||||
/// Type that represents the <resData> tag for domain transfer response
|
/// Type that represents the <resData> tag for domain transfer response
|
||||||
pub struct RgpRequestResponse {
|
pub struct RgpRequestUpdateResponse {
|
||||||
/// Data under the <rgpStatus> tag
|
/// Data under the <rgpStatus> tag
|
||||||
#[serde(rename = "rgpStatus")]
|
|
||||||
pub rgp_status: Vec<RgpStatus>,
|
pub rgp_status: Vec<RgpStatus>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, FromXml)]
|
||||||
|
#[xml(rename = "infData", ns(XMLNS))]
|
||||||
|
/// Type that represents the <resData> tag for domain transfer response
|
||||||
|
pub struct RgpRequestInfoResponse {
|
||||||
|
/// Data under the <rgpStatus> tag
|
||||||
|
pub rgp_status: Vec<RgpStatus>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type that represents the <resData> tag for domain transfer response
|
||||||
|
#[derive(Debug, FromXml)]
|
||||||
|
#[xml(forward)]
|
||||||
|
pub enum RgpRequestResponse {
|
||||||
|
Update(RgpRequestUpdateResponse),
|
||||||
|
Info(RgpRequestInfoResponse),
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{RgpRestoreRequest, Update};
|
use super::{RgpRestoreRequest, Update};
|
||||||
use crate::domain::info::DomainInfo;
|
use crate::domain::info::DomainInfo;
|
||||||
use crate::domain::update::{DomainChangeInfo, DomainUpdate};
|
use crate::domain::update::{DomainChangeInfo, DomainUpdate};
|
||||||
|
use crate::extensions::rgp::request::RgpRequestResponse;
|
||||||
use crate::response::ResultCode;
|
use crate::response::ResultCode;
|
||||||
use crate::tests::{assert_serialized, response_from_file_with_ext, SUCCESS_MSG, SVTRID};
|
use crate::tests::{assert_serialized, response_from_file_with_ext, SUCCESS_MSG, SVTRID};
|
||||||
|
|
||||||
|
@ -102,9 +113,15 @@ mod tests {
|
||||||
let ext = object.extension.unwrap();
|
let ext = object.extension.unwrap();
|
||||||
|
|
||||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||||
assert_eq!(ext.data.rgp_status[0].status, "pendingRestore".to_string());
|
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
let data = match ext.data {
|
||||||
|
RgpRequestResponse::Update(data) => data,
|
||||||
|
_ => panic!("Unexpected response type"),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(data.rgp_status[0].status, "pendingRestore".to_string());
|
||||||
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -114,7 +131,12 @@ mod tests {
|
||||||
);
|
);
|
||||||
let ext = object.extension.unwrap();
|
let ext = object.extension.unwrap();
|
||||||
|
|
||||||
assert_eq!(ext.data.rgp_status[0].status, "addPeriod");
|
let data = match ext.data {
|
||||||
assert_eq!(ext.data.rgp_status[1].status, "renewPeriod");
|
RgpRequestResponse::Info(data) => data,
|
||||||
|
_ => panic!("Unexpected response type"),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(data.rgp_status[0].status, "addPeriod");
|
||||||
|
assert_eq!(data.rgp_status[1].status, "renewPeriod");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
302
src/hello.rs
302
src/hello.rs
|
@ -1,30 +1,15 @@
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Deserializer, Serialize};
|
use instant_xml::{Deserializer, FromXml, ToXml};
|
||||||
|
|
||||||
use crate::common::{Options, ServiceExtension, Services, StringValue, EPP_XMLNS};
|
use crate::common::{Options, ServiceExtension, Services, EPP_XMLNS};
|
||||||
|
|
||||||
// Request
|
// Request
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize)]
|
#[derive(Debug, PartialEq, ToXml)]
|
||||||
struct Hello;
|
#[xml(rename = "hello", ns(EPP_XMLNS))]
|
||||||
|
pub(crate) struct Hello;
|
||||||
#[derive(Debug, PartialEq, Serialize)]
|
|
||||||
#[serde(rename = "epp")]
|
|
||||||
pub struct HelloDocument {
|
|
||||||
xmlns: &'static str,
|
|
||||||
hello: Hello,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for HelloDocument {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
xmlns: EPP_XMLNS,
|
|
||||||
hello: Hello,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Response
|
// Response
|
||||||
|
|
||||||
|
@ -36,193 +21,235 @@ pub struct ServiceMenu {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Simplified service menu type for deserialization to `ServiceMenu` type from EPP greeting XML
|
/// Simplified service menu type for deserialization to `ServiceMenu` type from EPP greeting XML
|
||||||
#[derive(Deserialize, Debug, PartialEq)]
|
#[derive(Debug, FromXml, PartialEq)]
|
||||||
|
#[xml(ns(EPP_XMLNS), rename = "svcMenu")]
|
||||||
struct FlattenedServiceMenu {
|
struct FlattenedServiceMenu {
|
||||||
pub version: StringValue<'static>,
|
pub version: String,
|
||||||
pub lang: StringValue<'static>,
|
pub lang: String,
|
||||||
#[serde(rename = "objURI")]
|
#[xml(rename = "objURI")]
|
||||||
pub obj_uris: Vec<StringValue<'static>>,
|
pub obj_uris: Vec<String>,
|
||||||
#[serde(rename = "svcExtension")]
|
#[xml(rename = "svcExtension")]
|
||||||
pub svc_ext: Option<ServiceExtension<'static>>,
|
pub svc_ext: Option<ServiceExtension<'static>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'de: 'a> Deserialize<'de> for ServiceMenu {
|
impl<'xml> FromXml<'xml> for ServiceMenu {
|
||||||
/// Deserializes the <svcMenu> data to the `ServiceMenu` type
|
fn matches(id: instant_xml::Id<'_>, field: Option<instant_xml::Id<'_>>) -> bool {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
FlattenedServiceMenu::matches(id, field)
|
||||||
where
|
}
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let flattened_svc_menu = FlattenedServiceMenu::deserialize(deserializer)?;
|
|
||||||
|
|
||||||
let svc_menu = ServiceMenu {
|
/// Deserializes the <svcMenu> data to the `ServiceMenu` type
|
||||||
options: Options {
|
fn deserialize<'cx>(
|
||||||
version: flattened_svc_menu.version,
|
into: &mut Self::Accumulator,
|
||||||
lang: flattened_svc_menu.lang,
|
field: &'static str,
|
||||||
},
|
deserializer: &mut Deserializer<'cx, 'xml>,
|
||||||
services: Services {
|
) -> Result<(), instant_xml::Error> {
|
||||||
obj_uris: flattened_svc_menu.obj_uris,
|
dbg!(&into);
|
||||||
svc_ext: flattened_svc_menu.svc_ext,
|
|
||||||
},
|
let mut value = None;
|
||||||
|
FlattenedServiceMenu::deserialize(&mut value, field, deserializer)?;
|
||||||
|
let flattened = match value {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return Ok(()),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(svc_menu)
|
*into = Some(ServiceMenu {
|
||||||
|
options: Options {
|
||||||
|
version: flattened.version.into(),
|
||||||
|
lang: flattened.lang.into(),
|
||||||
|
},
|
||||||
|
services: Services {
|
||||||
|
obj_uris: flattened.obj_uris.into_iter().map(|s| s.into()).collect(),
|
||||||
|
svc_ext: flattened.svc_ext,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Accumulator = Option<Self>;
|
||||||
|
const KIND: instant_xml::Kind = FlattenedServiceMenu::KIND;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type corresponding to <all> in the EPP greeting XML
|
/// Type corresponding to <all> in the EPP greeting XML
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "all", ns(EPP_XMLNS))]
|
||||||
pub struct All;
|
pub struct All;
|
||||||
|
|
||||||
/// Type corresponding to <none> in the EPP greeting XML
|
/// Type corresponding to <none> in the EPP greeting XML
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "noAccess", ns(EPP_XMLNS))]
|
||||||
pub struct NoAccess;
|
pub struct NoAccess;
|
||||||
|
|
||||||
/// Type corresponding to <null> in the EPP greeting XML
|
/// Type corresponding to <null> in the EPP greeting XML
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "null", ns(EPP_XMLNS))]
|
||||||
pub struct Null;
|
pub struct Null;
|
||||||
|
|
||||||
/// Type corresponding to <personal> in the EPP greeting XML
|
/// Type corresponding to <personal> in the EPP greeting XML
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "personal", ns(EPP_XMLNS))]
|
||||||
pub struct Personal;
|
pub struct Personal;
|
||||||
|
|
||||||
/// Type corresponding to <personalAndOther> in the EPP greeting XML
|
/// Type corresponding to <personalAndOther> in the EPP greeting XML
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "personalAndOther", ns(EPP_XMLNS))]
|
||||||
pub struct PersonalAndOther;
|
pub struct PersonalAndOther;
|
||||||
|
|
||||||
/// Type corresponding to <other> in the EPP greeting XML
|
/// Type corresponding to <other> in the EPP greeting XML
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "other", ns(EPP_XMLNS))]
|
||||||
pub struct Other;
|
pub struct Other;
|
||||||
|
|
||||||
/// Type corresponding to possible <retention> type values
|
/// Type corresponding to possible <retention> type values
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(forward)]
|
||||||
pub enum AccessType {
|
pub enum AccessType {
|
||||||
/// Data for the <all> tag
|
/// Data for the <all> tag
|
||||||
#[serde(rename = "all")]
|
|
||||||
All(All),
|
All(All),
|
||||||
/// Data for the <none> tag
|
/// Data for the <none> tag
|
||||||
#[serde(rename = "none")]
|
|
||||||
NoAccess(NoAccess),
|
NoAccess(NoAccess),
|
||||||
/// Data for the <null> tag
|
/// Data for the <null> tag
|
||||||
#[serde(rename = "null")]
|
|
||||||
Null(Null),
|
Null(Null),
|
||||||
/// Data for the <personal> tag
|
/// Data for the <personal> tag
|
||||||
#[serde(rename = "personal")]
|
|
||||||
Personal(Personal),
|
Personal(Personal),
|
||||||
/// Data for the <personalAndOther> tag
|
/// Data for the <personalAndOther> tag
|
||||||
#[serde(rename = "personalAndOther")]
|
|
||||||
PersonalAndOther(PersonalAndOther),
|
PersonalAndOther(PersonalAndOther),
|
||||||
/// Data for the <other> tag
|
/// Data for the <other> tag
|
||||||
#[serde(rename = "other")]
|
|
||||||
Other(Other),
|
Other(Other),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type corresponding to <access> in the EPP greeting XML
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[xml(rename = "access", ns(EPP_XMLNS))]
|
||||||
pub struct Access {
|
pub struct Access {
|
||||||
#[serde(flatten)]
|
inner: AccessType,
|
||||||
pub ty: AccessType,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type corresponding to possible <purpose> type values
|
/// Type corresponding to possible <purpose> type values
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(forward)]
|
||||||
pub enum PurposeType {
|
pub enum PurposeType {
|
||||||
/// Data for the <admin> tag
|
/// Data for the <admin> tag
|
||||||
#[serde(rename = "admin")]
|
Admin(Admin),
|
||||||
Admin,
|
|
||||||
/// Data for the <contact> tag
|
/// Data for the <contact> tag
|
||||||
#[serde(rename = "contact")]
|
Contact(Contact),
|
||||||
Contact,
|
|
||||||
/// Data for the <prov> tag
|
/// Data for the <prov> tag
|
||||||
#[serde(rename = "prov")]
|
Prov(Prov),
|
||||||
Prov,
|
|
||||||
/// Data for the <other> tag
|
/// Data for the <other> tag
|
||||||
#[serde(rename = "other")]
|
OtherPurpose(OtherPurpose),
|
||||||
OtherPurpose,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "admin", ns(EPP_XMLNS))]
|
||||||
|
pub struct Admin;
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "contact", ns(EPP_XMLNS))]
|
||||||
|
pub struct Contact;
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "prov", ns(EPP_XMLNS))]
|
||||||
|
pub struct Prov;
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "otherPurpose", ns(EPP_XMLNS))]
|
||||||
|
pub struct OtherPurpose;
|
||||||
|
|
||||||
/// Type corresponding to <purpose> in the EPP greeting XML
|
/// Type corresponding to <purpose> in the EPP greeting XML
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "purpose", ns(EPP_XMLNS))]
|
||||||
pub struct Purpose {
|
pub struct Purpose {
|
||||||
#[serde(rename = "$value")]
|
|
||||||
pub purpose: Vec<PurposeType>,
|
pub purpose: Vec<PurposeType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type corresponding to possible <purpose> type values
|
/// Type corresponding to possible <purpose> type values
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(forward)]
|
||||||
pub enum RecipientType {
|
pub enum RecipientType {
|
||||||
/// Data for the <other> tag
|
/// Data for the <other> tag
|
||||||
#[serde(rename = "other")]
|
Other(Other),
|
||||||
Other,
|
|
||||||
/// Data for the <ours> tag
|
/// Data for the <ours> tag
|
||||||
#[serde(rename = "ours")]
|
Ours(Ours),
|
||||||
Ours,
|
|
||||||
/// Data for the <public> tag
|
/// Data for the <public> tag
|
||||||
#[serde(rename = "public")]
|
Public(Public),
|
||||||
Public,
|
|
||||||
/// Data for the <same> tag
|
/// Data for the <same> tag
|
||||||
#[serde(rename = "same")]
|
Same(Same),
|
||||||
Same,
|
|
||||||
/// Data for the <unrelated> tag
|
/// Data for the <unrelated> tag
|
||||||
#[serde(rename = "unrelated")]
|
Unrelated(Unrelated),
|
||||||
Unrelated,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "ours", ns(EPP_XMLNS))]
|
||||||
|
pub struct Ours;
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "public", ns(EPP_XMLNS))]
|
||||||
|
pub struct Public;
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "unrelated", ns(EPP_XMLNS))]
|
||||||
|
pub struct Unrelated;
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "same", ns(EPP_XMLNS))]
|
||||||
|
pub struct Same;
|
||||||
|
|
||||||
/// Type corresponding to <recipeint> in the EPP greeting XML
|
/// Type corresponding to <recipeint> in the EPP greeting XML
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "recipient", ns(EPP_XMLNS))]
|
||||||
pub struct Recipient {
|
pub struct Recipient {
|
||||||
#[serde(rename = "$value")]
|
|
||||||
pub recipient: Vec<RecipientType>,
|
pub recipient: Vec<RecipientType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type corresponding to <business> in the EPP greeting XML
|
/// Type corresponding to <business> in the EPP greeting XML
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "business", ns(EPP_XMLNS))]
|
||||||
pub struct Business;
|
pub struct Business;
|
||||||
|
|
||||||
/// Type corresponding to <indefinite> in the EPP greeting XML
|
/// Type corresponding to <indefinite> in the EPP greeting XML
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "indefinite", ns(EPP_XMLNS))]
|
||||||
pub struct Indefinite;
|
pub struct Indefinite;
|
||||||
|
|
||||||
/// Type corresponding to <legal> in the EPP greeting XML
|
/// Type corresponding to <legal> in the EPP greeting XML
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "legal", ns(EPP_XMLNS))]
|
||||||
pub struct Legal;
|
pub struct Legal;
|
||||||
|
|
||||||
/// Type corresponding to <none> in the EPP greeting XML
|
/// Type corresponding to <none> in the EPP greeting XML
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "none", ns(EPP_XMLNS))]
|
||||||
pub struct No;
|
pub struct No;
|
||||||
|
|
||||||
/// Type corresponding to <stated> in the EPP greeting XML
|
/// Type corresponding to <stated> in the EPP greeting XML
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "stated", ns(EPP_XMLNS))]
|
||||||
pub struct Stated;
|
pub struct Stated;
|
||||||
|
|
||||||
/// Type corresponding to possible <retention> type values
|
/// Type corresponding to possible <retention> type values
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(forward, rename = "retention", ns(EPP_XMLNS))]
|
||||||
pub enum RetentionType {
|
pub enum RetentionType {
|
||||||
/// Data for the <business> tag
|
/// Data for the <business> tag
|
||||||
#[serde(rename = "business")]
|
|
||||||
Business(Business),
|
Business(Business),
|
||||||
/// Data for the <indefinite> tag
|
/// Data for the <indefinite> tag
|
||||||
#[serde(rename = "indefinite")]
|
|
||||||
Indefinite(Indefinite),
|
Indefinite(Indefinite),
|
||||||
/// Data for the <legal> tag
|
/// Data for the <legal> tag
|
||||||
#[serde(rename = "legal")]
|
|
||||||
Legal(Legal),
|
Legal(Legal),
|
||||||
/// Data for the <none> tag
|
/// Data for the <none> tag
|
||||||
#[serde(rename = "none")]
|
None(No),
|
||||||
No(No),
|
|
||||||
/// Data for the <stated> tag
|
/// Data for the <stated> tag
|
||||||
#[serde(rename = "stated")]
|
|
||||||
Stated(Stated),
|
Stated(Stated),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type corresponding to <retention> in the EPP greeting XML
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[xml(rename = "retention", ns(EPP_XMLNS))]
|
||||||
pub struct Retention {
|
pub struct Retention {
|
||||||
#[serde(flatten)]
|
inner: RetentionType,
|
||||||
pub ty: RetentionType,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type corresponding to <statement> in the EPP greeting XML (pending more compliant implementation)
|
/// Type corresponding to <statement> in the EPP greeting XML (pending more compliant implementation)
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "statement", ns(EPP_XMLNS))]
|
||||||
pub struct Statement {
|
pub struct Statement {
|
||||||
/// Data for the <purpose> tag
|
/// Data for the <purpose> tag
|
||||||
pub purpose: Purpose,
|
pub purpose: Purpose,
|
||||||
|
@ -233,39 +260,35 @@ pub struct Statement {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type corresponding to <absolute> value in the EPP greeting XML
|
/// Type corresponding to <absolute> value in the EPP greeting XML
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
pub struct Absolute {
|
#[xml(rename = "absolute", ns(EPP_XMLNS))]
|
||||||
#[serde(rename = "$value")]
|
pub struct Absolute(String);
|
||||||
pub absolute: StringValue<'static>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Type corresponding to <relative> value in the EPP greeting XML
|
/// Type corresponding to <relative> value in the EPP greeting XML
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
pub struct Relative {
|
#[xml(rename = "relative", ns(EPP_XMLNS))]
|
||||||
#[serde(rename = "$value")]
|
pub struct Relative(String);
|
||||||
pub relative: StringValue<'static>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Type corresponding to possible <expiry> type values
|
/// Type corresponding to possible <expiry> type values
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(forward)]
|
||||||
pub enum ExpiryType {
|
pub enum ExpiryType {
|
||||||
/// Data for the <absolute> tag
|
/// Data for the <absolute> tag
|
||||||
#[serde(rename = "absolute")]
|
|
||||||
Absolute(Absolute),
|
Absolute(Absolute),
|
||||||
/// Data for the <relative> tag
|
/// Data for the <relative> tag
|
||||||
#[serde(rename = "relative")]
|
|
||||||
Relative(Relative),
|
Relative(Relative),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type corresponding to <expiry> in the EPP greeting XML
|
/// Type corresponding to possible <expiry> type values
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "expiry", ns(EPP_XMLNS))]
|
||||||
pub struct Expiry {
|
pub struct Expiry {
|
||||||
#[serde(flatten)]
|
inner: ExpiryType,
|
||||||
pub ty: ExpiryType,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type corresponding to <dcp> in the EPP greeting XML
|
/// Type corresponding to <dcp> in the EPP greeting XML
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "dcp", ns(EPP_XMLNS))]
|
||||||
pub struct Dcp {
|
pub struct Dcp {
|
||||||
/// Data for the <access> tag
|
/// Data for the <access> tag
|
||||||
pub access: Access,
|
pub access: Access,
|
||||||
|
@ -275,42 +298,34 @@ pub struct Dcp {
|
||||||
pub expiry: Option<Expiry>,
|
pub expiry: Option<Expiry>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
|
||||||
#[serde(rename_all = "lowercase")]
|
|
||||||
/// Type corresponding to the <greeting> tag in the EPP greeting XML
|
/// Type corresponding to the <greeting> tag in the EPP greeting XML
|
||||||
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(ns(EPP_XMLNS), rename = "greeting", rename_all = "lowercase")]
|
||||||
pub struct Greeting {
|
pub struct Greeting {
|
||||||
/// The service ID
|
/// The service ID
|
||||||
#[serde(rename = "svID")]
|
#[xml(rename = "svID")]
|
||||||
pub service_id: String,
|
pub service_id: String,
|
||||||
/// The date from the EPP server
|
/// The date from the EPP server
|
||||||
#[serde(rename = "svDate")]
|
#[xml(rename = "svDate")]
|
||||||
pub service_date: DateTime<Utc>,
|
pub service_date: DateTime<Utc>,
|
||||||
/// Data under the <svcMenu> element
|
/// Data under the <svcMenu> element
|
||||||
#[serde(rename = "svcMenu")]
|
|
||||||
pub svc_menu: ServiceMenu,
|
pub svc_menu: ServiceMenu,
|
||||||
/// Data under the <dcp> element
|
/// Data under the <dcp> element
|
||||||
pub dcp: Dcp,
|
pub dcp: Dcp,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
|
||||||
#[serde(rename = "epp")]
|
|
||||||
pub struct GreetingDocument {
|
|
||||||
#[serde(rename = "greeting")]
|
|
||||||
pub data: Greeting,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use chrono::{TimeZone, Utc};
|
use chrono::{TimeZone, Utc};
|
||||||
|
|
||||||
use super::{ExpiryType, GreetingDocument, HelloDocument, Relative};
|
use super::{ExpiryType, Greeting, Hello, Relative};
|
||||||
use crate::tests::get_xml;
|
use crate::tests::get_xml;
|
||||||
use crate::xml;
|
use crate::xml;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hello() {
|
fn hello() {
|
||||||
let xml = get_xml("request/hello.xml").unwrap();
|
let xml = get_xml("request/hello.xml").unwrap();
|
||||||
let serialized = xml::serialize(&HelloDocument::default()).unwrap();
|
let serialized = xml::serialize(Hello).unwrap();
|
||||||
|
|
||||||
assert_eq!(xml, serialized);
|
assert_eq!(xml, serialized);
|
||||||
}
|
}
|
||||||
|
@ -318,19 +333,18 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn greeting() {
|
fn greeting() {
|
||||||
let xml = get_xml("response/greeting.xml").unwrap();
|
let xml = get_xml("response/greeting.xml").unwrap();
|
||||||
let object = xml::deserialize::<GreetingDocument>(xml.as_str()).unwrap();
|
let object = xml::deserialize::<Greeting>(xml.as_str()).unwrap();
|
||||||
|
|
||||||
assert_eq!(object.data.service_id, "ISPAPI EPP Server");
|
assert_eq!(object.service_id, "ISPAPI EPP Server");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
object.data.service_date,
|
object.service_date,
|
||||||
Utc.with_ymd_and_hms(2021, 7, 25, 14, 51, 17).unwrap()
|
Utc.with_ymd_and_hms(2021, 7, 25, 14, 51, 17).unwrap()
|
||||||
);
|
);
|
||||||
assert_eq!(object.data.svc_menu.options.version, "1.0".into());
|
assert_eq!(object.svc_menu.options.version, "1.0");
|
||||||
assert_eq!(object.data.svc_menu.options.lang, "en".into());
|
assert_eq!(object.svc_menu.options.lang, "en");
|
||||||
assert_eq!(object.data.svc_menu.services.obj_uris.len(), 4);
|
assert_eq!(object.svc_menu.services.obj_uris.len(), 4);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
object
|
object
|
||||||
.data
|
|
||||||
.svc_menu
|
.svc_menu
|
||||||
.services
|
.services
|
||||||
.svc_ext
|
.svc_ext
|
||||||
|
@ -340,12 +354,10 @@ mod tests {
|
||||||
.len(),
|
.len(),
|
||||||
5
|
5
|
||||||
);
|
);
|
||||||
assert_eq!(object.data.dcp.statement.len(), 2);
|
assert_eq!(object.dcp.statement.len(), 2);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
object.data.dcp.expiry.unwrap().ty,
|
object.dcp.expiry.unwrap().inner,
|
||||||
ExpiryType::Relative(Relative {
|
ExpiryType::Relative(Relative("P1M".into()))
|
||||||
relative: "P1M".into()
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,59 +1,74 @@
|
||||||
//! Types for EPP host check request
|
//! Types for EPP host check request
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::{self, Debug};
|
||||||
|
|
||||||
|
use instant_xml::{FromXml, Serializer, ToXml};
|
||||||
|
|
||||||
use super::XMLNS;
|
use super::XMLNS;
|
||||||
use crate::common::{CheckResponse, NoExtension, StringValue};
|
use crate::common::{NoExtension, EPP_XMLNS};
|
||||||
use crate::request::{Command, Transaction};
|
use crate::request::{Command, Transaction};
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
impl<'a> Transaction<NoExtension> for HostCheck<'a> {}
|
impl<'a> Transaction<NoExtension> for HostCheck<'a> {}
|
||||||
|
|
||||||
impl<'a> Command for HostCheck<'a> {
|
impl<'a> Command for HostCheck<'a> {
|
||||||
type Response = CheckResponse;
|
type Response = CheckData;
|
||||||
const COMMAND: &'static str = "check";
|
const COMMAND: &'static str = "check";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request
|
// Request
|
||||||
|
|
||||||
/// Type for data under the host <check> tag
|
/// Type for data under the host <check> tag
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
struct HostList<'a> {
|
#[xml(rename = "check", ns(XMLNS))]
|
||||||
/// XML namespace for host commands
|
struct HostCheckData<'a> {
|
||||||
#[serde(rename = "xmlns:host")]
|
|
||||||
xmlns: &'a str,
|
|
||||||
/// List of hosts to be checked for availability
|
/// List of hosts to be checked for availability
|
||||||
#[serde(rename = "host:name")]
|
name: &'a [&'a str],
|
||||||
hosts: Vec<StringValue<'a>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
fn serialize_hosts<W: fmt::Write + ?Sized>(
|
||||||
/// Type for EPP XML <check> command for hosts
|
hosts: &[&str],
|
||||||
struct SerializeHostCheck<'a> {
|
serializer: &mut Serializer<W>,
|
||||||
/// The instance holding the list of hosts to be checked
|
) -> Result<(), instant_xml::Error> {
|
||||||
#[serde(rename = "host:check")]
|
HostCheckData { name: hosts }.serialize(None, serializer)
|
||||||
list: HostList<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<HostCheck<'a>> for SerializeHostCheck<'a> {
|
|
||||||
fn from(check: HostCheck<'a>) -> Self {
|
|
||||||
Self {
|
|
||||||
list: HostList {
|
|
||||||
xmlns: XMLNS,
|
|
||||||
hosts: check.hosts.iter().map(|&id| id.into()).collect(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The EPP `check` command for hosts
|
/// The EPP `check` command for hosts
|
||||||
#[derive(Clone, Debug, Serialize)]
|
#[derive(Clone, Debug, ToXml)]
|
||||||
#[serde(into = "SerializeHostCheck")]
|
#[xml(rename = "check", ns(EPP_XMLNS))]
|
||||||
pub struct HostCheck<'a> {
|
pub struct HostCheck<'a> {
|
||||||
/// The list of hosts to be checked
|
/// The list of hosts to be checked
|
||||||
|
#[xml(serialize_with = "serialize_hosts")]
|
||||||
pub hosts: &'a [&'a str],
|
pub hosts: &'a [&'a str],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Response
|
||||||
|
|
||||||
|
#[derive(Debug, FromXml)]
|
||||||
|
#[xml(rename = "name", ns(XMLNS))]
|
||||||
|
pub struct Checked {
|
||||||
|
#[xml(attribute, rename = "avail")]
|
||||||
|
pub available: bool,
|
||||||
|
#[xml(attribute)]
|
||||||
|
pub reason: Option<String>,
|
||||||
|
#[xml(direct)]
|
||||||
|
pub id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, FromXml)]
|
||||||
|
#[xml(rename = "cd", ns(XMLNS))]
|
||||||
|
pub struct CheckedHost {
|
||||||
|
/// Data under the <cd> tag
|
||||||
|
#[xml(rename = "cd")]
|
||||||
|
pub inner: Checked,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type that represents the <chkData> tag for host check response
|
||||||
|
#[derive(Debug, FromXml)]
|
||||||
|
#[xml(rename = "chkData", ns(XMLNS))]
|
||||||
|
pub struct CheckData {
|
||||||
|
pub list: Vec<CheckedHost>,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::HostCheck;
|
use super::HostCheck;
|
||||||
|
@ -74,12 +89,12 @@ mod tests {
|
||||||
let result = object.res_data().unwrap();
|
let result = object.res_data().unwrap();
|
||||||
|
|
||||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||||
assert_eq!(result.list[0].id, "host1.eppdev-1.com");
|
assert_eq!(result.list[0].inner.id, "host1.eppdev-1.com");
|
||||||
assert!(result.list[0].available);
|
assert!(result.list[0].inner.available);
|
||||||
assert_eq!(result.list[1].id, "ns1.testing.com");
|
assert_eq!(result.list[1].inner.id, "ns1.testing.com");
|
||||||
assert!(!result.list[1].available);
|
assert!(!result.list[1].inner.available);
|
||||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,25 +3,24 @@
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use instant_xml::{FromXml, ToXml};
|
||||||
|
|
||||||
use super::XMLNS;
|
use super::{serialize_host_addrs_option, XMLNS};
|
||||||
use crate::common::{serialize_host_addrs_option, NoExtension, StringValue};
|
use crate::common::{NoExtension, EPP_XMLNS};
|
||||||
use crate::request::{Command, Transaction};
|
use crate::request::{Command, Transaction};
|
||||||
|
|
||||||
impl<'a> Transaction<NoExtension> for HostCreate<'a> {}
|
impl<'a> Transaction<NoExtension> for HostCreate<'a> {}
|
||||||
|
|
||||||
impl<'a> Command for HostCreate<'a> {
|
impl<'a> Command for HostCreate<'a> {
|
||||||
type Response = HostCreateResponse;
|
type Response = HostCreateData;
|
||||||
const COMMAND: &'static str = "create";
|
const COMMAND: &'static str = "create";
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> HostCreate<'a> {
|
impl<'a> HostCreate<'a> {
|
||||||
pub fn new(host: &'a str, addresses: Option<&'a [IpAddr]>) -> Self {
|
pub fn new(name: &'a str, addresses: Option<&'a [IpAddr]>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
host: HostCreateRequestData {
|
host: HostCreateRequest {
|
||||||
xmlns: XMLNS,
|
name,
|
||||||
name: host.into(),
|
|
||||||
addresses,
|
addresses,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -31,47 +30,37 @@ impl<'a> HostCreate<'a> {
|
||||||
// Request
|
// Request
|
||||||
|
|
||||||
/// Type for data under the host <create> tag
|
/// Type for data under the host <create> tag
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
pub struct HostCreateRequestData<'a> {
|
#[xml(rename = "create", ns(XMLNS))]
|
||||||
/// XML namespace for host commands
|
pub struct HostCreateRequest<'a> {
|
||||||
#[serde(rename = "xmlns:host")]
|
|
||||||
xmlns: &'a str,
|
|
||||||
/// The name of the host to be created
|
/// The name of the host to be created
|
||||||
#[serde(rename = "host:name")]
|
pub name: &'a str,
|
||||||
pub name: StringValue<'a>,
|
|
||||||
/// The list of IP addresses for the host
|
/// The list of IP addresses for the host
|
||||||
#[serde(rename = "host:addr", serialize_with = "serialize_host_addrs_option")]
|
#[xml(serialize_with = "serialize_host_addrs_option")]
|
||||||
pub addresses: Option<&'a [IpAddr]>,
|
pub addresses: Option<&'a [IpAddr]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
|
||||||
/// Type for EPP XML <create> command for hosts
|
/// Type for EPP XML <create> command for hosts
|
||||||
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "create", ns(EPP_XMLNS))]
|
||||||
pub struct HostCreate<'a> {
|
pub struct HostCreate<'a> {
|
||||||
/// The instance holding the data for the host to be created
|
/// The instance holding the data for the host to be created
|
||||||
#[serde(rename = "host:create")]
|
host: HostCreateRequest<'a>,
|
||||||
host: HostCreateRequestData<'a>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Response
|
// Response
|
||||||
|
|
||||||
/// Type that represents the <creData> tag for host create response
|
/// Type that represents the <creData> tag for host create response
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Debug, FromXml)]
|
||||||
|
#[xml(rename = "creData", ns(XMLNS))]
|
||||||
pub struct HostCreateData {
|
pub struct HostCreateData {
|
||||||
/// The host name
|
/// The host name
|
||||||
pub name: StringValue<'static>,
|
pub name: String,
|
||||||
/// The host creation date
|
/// The host creation date
|
||||||
#[serde(rename = "crDate")]
|
#[xml(rename = "crDate")]
|
||||||
pub created_at: DateTime<Utc>,
|
pub created_at: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type that represents the <resData> tag for host check response
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
pub struct HostCreateResponse {
|
|
||||||
/// Data under the <creData> tag
|
|
||||||
#[serde(rename = "creData")]
|
|
||||||
pub create_data: HostCreateData,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use chrono::{TimeZone, Utc};
|
use chrono::{TimeZone, Utc};
|
||||||
|
@ -97,13 +86,13 @@ mod tests {
|
||||||
let result = object.res_data().unwrap();
|
let result = object.res_data().unwrap();
|
||||||
|
|
||||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||||
assert_eq!(result.create_data.name, "host2.eppdev-1.com".into());
|
assert_eq!(result.name, "host2.eppdev-1.com");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result.create_data.created_at,
|
result.created_at,
|
||||||
Utc.with_ymd_and_hms(2021, 7, 26, 5, 28, 55).unwrap()
|
Utc.with_ymd_and_hms(2021, 7, 26, 5, 28, 55).unwrap()
|
||||||
);
|
);
|
||||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
//! Types for EPP host delete request
|
//! Types for EPP host delete request
|
||||||
|
|
||||||
|
use instant_xml::ToXml;
|
||||||
|
|
||||||
use super::XMLNS;
|
use super::XMLNS;
|
||||||
use crate::common::{NoExtension, StringValue};
|
use crate::common::{NoExtension, EPP_XMLNS};
|
||||||
use crate::request::{Command, Transaction};
|
use crate::request::{Command, Transaction};
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
impl<'a> Transaction<NoExtension> for HostDelete<'a> {}
|
impl<'a> Transaction<NoExtension> for HostDelete<'a> {}
|
||||||
|
|
||||||
|
@ -15,31 +16,25 @@ impl<'a> Command for HostDelete<'a> {
|
||||||
impl<'a> HostDelete<'a> {
|
impl<'a> HostDelete<'a> {
|
||||||
pub fn new(name: &'a str) -> Self {
|
pub fn new(name: &'a str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
host: HostDeleteRequestData {
|
host: HostDeleteRequest { name },
|
||||||
xmlns: XMLNS,
|
|
||||||
name: name.into(),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type for data under the host <delete> tag
|
/// Type for data under the host <delete> tag
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
pub struct HostDeleteRequestData<'a> {
|
#[xml(rename = "delete", ns(XMLNS))]
|
||||||
/// XML namespace for host commands
|
pub struct HostDeleteRequest<'a> {
|
||||||
#[serde(rename = "xmlns:host")]
|
|
||||||
xmlns: &'a str,
|
|
||||||
/// The host to be deleted
|
/// The host to be deleted
|
||||||
#[serde(rename = "host:name")]
|
name: &'a str,
|
||||||
name: StringValue<'a>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
|
||||||
/// Type for EPP XML <delete> command for hosts
|
/// Type for EPP XML <delete> command for hosts
|
||||||
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "delete", ns(EPP_XMLNS))]
|
||||||
pub struct HostDelete<'a> {
|
pub struct HostDelete<'a> {
|
||||||
/// The instance holding the data for the host to be deleted
|
/// The instance holding the data for the host to be deleted
|
||||||
#[serde(rename = "host:delete")]
|
host: HostDeleteRequest<'a>,
|
||||||
host: HostDeleteRequestData<'a>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -58,8 +53,8 @@ mod tests {
|
||||||
fn response() {
|
fn response() {
|
||||||
let object = response_from_file::<HostDelete>("response/host/delete.xml");
|
let object = response_from_file::<HostDelete>("response/host/delete.xml");
|
||||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
128
src/host/info.rs
128
src/host/info.rs
|
@ -4,26 +4,23 @@ use std::net::IpAddr;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use instant_xml::{FromXml, ToXml};
|
||||||
|
|
||||||
use super::XMLNS;
|
use super::{HostAddr, Status, XMLNS};
|
||||||
use crate::common::{HostAddr, NoExtension, ObjectStatus, StringValue};
|
use crate::common::{NoExtension, EPP_XMLNS};
|
||||||
use crate::request::{Command, Transaction};
|
use crate::request::{Command, Transaction};
|
||||||
|
|
||||||
impl<'a> Transaction<NoExtension> for HostInfo<'a> {}
|
impl<'a> Transaction<NoExtension> for HostInfo<'a> {}
|
||||||
|
|
||||||
impl<'a> Command for HostInfo<'a> {
|
impl<'a> Command for HostInfo<'a> {
|
||||||
type Response = HostInfoResponse;
|
type Response = HostInfoResponseData;
|
||||||
const COMMAND: &'static str = "info";
|
const COMMAND: &'static str = "info";
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> HostInfo<'a> {
|
impl<'a> HostInfo<'a> {
|
||||||
pub fn new(name: &'a str) -> Self {
|
pub fn new(name: &'a str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
info: HostInfoRequestData {
|
info: HostInfoRequestData { name },
|
||||||
xmlns: XMLNS,
|
|
||||||
name: name.into(),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,78 +28,91 @@ impl<'a> HostInfo<'a> {
|
||||||
// Request
|
// Request
|
||||||
|
|
||||||
/// Type for data under the host <info> tag
|
/// Type for data under the host <info> tag
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "info", ns(XMLNS))]
|
||||||
pub struct HostInfoRequestData<'a> {
|
pub struct HostInfoRequestData<'a> {
|
||||||
/// XML namespace for host commands
|
|
||||||
#[serde(rename = "xmlns:host")]
|
|
||||||
xmlns: &'a str,
|
|
||||||
/// The name of the host to be queried
|
/// The name of the host to be queried
|
||||||
#[serde(rename = "host:name")]
|
name: &'a str,
|
||||||
name: StringValue<'a>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
|
||||||
/// Type for EPP XML <info> command for hosts
|
/// Type for EPP XML <info> command for hosts
|
||||||
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "info", ns(EPP_XMLNS))]
|
||||||
pub struct HostInfo<'a> {
|
pub struct HostInfo<'a> {
|
||||||
/// The instance holding the data for the host query
|
/// The instance holding the data for the host query
|
||||||
#[serde(rename = "host:info")]
|
#[xml(rename = "host:info")]
|
||||||
info: HostInfoRequestData<'a>,
|
info: HostInfoRequestData<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Response
|
// Response
|
||||||
|
|
||||||
/// Type that represents the <infData> tag for host info response
|
/// Type that represents the <infData> tag for host info response
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Debug, FromXml)]
|
||||||
|
#[xml(rename = "infData", ns(XMLNS))]
|
||||||
pub struct HostInfoResponseData {
|
pub struct HostInfoResponseData {
|
||||||
/// The host name
|
/// The host name
|
||||||
pub name: StringValue<'static>,
|
pub name: String,
|
||||||
/// The host ROID
|
/// The host ROID
|
||||||
pub roid: StringValue<'static>,
|
pub roid: String,
|
||||||
/// The list of host statuses
|
/// The list of host statuses
|
||||||
#[serde(rename = "status")]
|
#[xml(rename = "status")]
|
||||||
pub statuses: Vec<ObjectStatus<'static>>,
|
pub statuses: Vec<Status<'static>>,
|
||||||
/// The list of host IP addresses
|
/// The list of host IP addresses
|
||||||
#[serde(rename = "addr", deserialize_with = "deserialize_host_addrs")]
|
#[xml(rename = "addr", deserialize_with = "deserialize_host_addrs")]
|
||||||
pub addresses: Vec<IpAddr>,
|
pub addresses: Vec<IpAddr>,
|
||||||
/// The epp user to whom the host belongs
|
/// The epp user to whom the host belongs
|
||||||
#[serde(rename = "clID")]
|
#[xml(rename = "clID")]
|
||||||
pub client_id: StringValue<'static>,
|
pub client_id: String,
|
||||||
/// THe epp user that created the host
|
/// THe epp user that created the host
|
||||||
#[serde(rename = "crID")]
|
#[xml(rename = "crID")]
|
||||||
pub creator_id: StringValue<'static>,
|
pub creator_id: String,
|
||||||
/// The host creation date
|
/// The host creation date
|
||||||
#[serde(rename = "crDate")]
|
#[xml(rename = "crDate")]
|
||||||
pub created_at: DateTime<Utc>,
|
pub created_at: DateTime<Utc>,
|
||||||
/// The epp user that last updated the host
|
/// The epp user that last updated the host
|
||||||
#[serde(rename = "upID")]
|
#[xml(rename = "upID")]
|
||||||
pub updater_id: Option<StringValue<'static>>,
|
pub updater_id: Option<String>,
|
||||||
/// The host last update date
|
/// The host last update date
|
||||||
#[serde(rename = "upDate")]
|
#[xml(rename = "upDate")]
|
||||||
pub updated_at: Option<DateTime<Utc>>,
|
pub updated_at: Option<DateTime<Utc>>,
|
||||||
/// The host transfer date
|
/// The host transfer date
|
||||||
#[serde(rename = "trDate")]
|
#[xml(rename = "trDate")]
|
||||||
pub transferred_at: Option<DateTime<Utc>>,
|
pub transferred_at: Option<DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_host_addrs<'de, D>(de: D) -> Result<Vec<IpAddr>, D::Error>
|
fn deserialize_host_addrs(
|
||||||
where
|
into: &mut Vec<IpAddr>,
|
||||||
D: serde::de::Deserializer<'de>,
|
field: &'static str,
|
||||||
{
|
deserializer: &mut instant_xml::de::Deserializer<'_, '_>,
|
||||||
let addrs = Vec::<HostAddr<'static>>::deserialize(de)?;
|
) -> Result<(), instant_xml::Error> {
|
||||||
addrs
|
let mut addrs = Vec::new();
|
||||||
.into_iter()
|
Vec::<HostAddr<'static>>::deserialize(&mut addrs, field, deserializer)?;
|
||||||
.map(|addr| IpAddr::from_str(&addr.address))
|
|
||||||
.collect::<Result<_, _>>()
|
for addr in addrs {
|
||||||
.map_err(|e| serde::de::Error::custom(format!("{}", e)))
|
match IpAddr::from_str(&addr.address) {
|
||||||
|
Ok(ip) => into.push(ip),
|
||||||
|
Err(_) => {
|
||||||
|
return Err(instant_xml::Error::UnexpectedValue(format!(
|
||||||
|
"invalid IP address '{}'",
|
||||||
|
&addr.address
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
/// Type that represents the <resData> tag for host info response
|
/// Type that represents the <resData> tag for host info response
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Debug, FromXml)]
|
||||||
|
#[xml(rename = "infData", ns(XMLNS))]
|
||||||
pub struct HostInfoResponse {
|
pub struct HostInfoResponse {
|
||||||
/// Data under the <infData> tag
|
/// Data under the <infData> tag
|
||||||
#[serde(rename = "infData")]
|
#[xml(rename = "infData")]
|
||||||
pub info_data: HostInfoResponseData,
|
pub info_data: HostInfoResponseData,
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
@ -124,33 +134,27 @@ mod tests {
|
||||||
let result = object.res_data().unwrap();
|
let result = object.res_data().unwrap();
|
||||||
|
|
||||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||||
assert_eq!(result.info_data.name, "host2.eppdev-1.com".into());
|
assert_eq!(result.name, "host2.eppdev-1.com");
|
||||||
assert_eq!(result.info_data.roid, "UNDEF-ROID".into());
|
assert_eq!(result.roid, "UNDEF-ROID");
|
||||||
assert_eq!(result.info_data.statuses[0].status, "ok".to_string());
|
assert_eq!(result.statuses[0].status, "ok".to_string());
|
||||||
|
assert_eq!(result.addresses[0], IpAddr::from([29, 245, 122, 14]));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result.info_data.addresses[0],
|
result.addresses[1],
|
||||||
IpAddr::from([29, 245, 122, 14])
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
result.info_data.addresses[1],
|
|
||||||
IpAddr::from([0x2404, 0x6800, 0x4001, 0x801, 0, 0, 0, 0x200e])
|
IpAddr::from([0x2404, 0x6800, 0x4001, 0x801, 0, 0, 0, 0x200e])
|
||||||
);
|
);
|
||||||
assert_eq!(result.info_data.client_id, "eppdev".into());
|
assert_eq!(result.client_id, "eppdev");
|
||||||
assert_eq!(result.info_data.creator_id, "creator".into());
|
assert_eq!(result.creator_id, "creator");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result.info_data.created_at,
|
result.created_at,
|
||||||
Utc.with_ymd_and_hms(2021, 7, 26, 5, 28, 55).unwrap()
|
Utc.with_ymd_and_hms(2021, 7, 26, 5, 28, 55).unwrap()
|
||||||
);
|
);
|
||||||
|
assert_eq!(*(result.updater_id.as_ref().unwrap()), "creator");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*(result.info_data.updater_id.as_ref().unwrap()),
|
result.updated_at,
|
||||||
"creator".into()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
result.info_data.updated_at,
|
|
||||||
Utc.with_ymd_and_hms(2021, 7, 26, 5, 28, 55).single()
|
Utc.with_ymd_and_hms(2021, 7, 26, 5, 28, 55).single()
|
||||||
);
|
);
|
||||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,11 @@
|
||||||
|
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
|
||||||
use super::XMLNS;
|
use instant_xml::ToXml;
|
||||||
use crate::common::{serialize_host_addrs_option, NoExtension, ObjectStatus, StringValue};
|
|
||||||
|
use super::{serialize_host_addrs_option, Status, XMLNS};
|
||||||
|
use crate::common::{NoExtension, EPP_XMLNS};
|
||||||
use crate::request::{Command, Transaction};
|
use crate::request::{Command, Transaction};
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
impl<'a> Transaction<NoExtension> for HostUpdate<'a> {}
|
impl<'a> Transaction<NoExtension> for HostUpdate<'a> {}
|
||||||
|
|
||||||
|
@ -17,9 +18,8 @@ impl<'a> Command for HostUpdate<'a> {
|
||||||
impl<'a> HostUpdate<'a> {
|
impl<'a> HostUpdate<'a> {
|
||||||
pub fn new(name: &'a str) -> Self {
|
pub fn new(name: &'a str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
host: HostUpdateRequestData {
|
host: HostUpdateRequest {
|
||||||
xmlns: XMLNS,
|
name,
|
||||||
name: name.into(),
|
|
||||||
add: None,
|
add: None,
|
||||||
remove: None,
|
remove: None,
|
||||||
change_info: None,
|
change_info: None,
|
||||||
|
@ -33,68 +33,77 @@ impl<'a> HostUpdate<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the data for the <add> element of the host update
|
/// Sets the data for the <add> element of the host update
|
||||||
pub fn add(&mut self, add: HostAddRemove<'a>) {
|
pub fn add(&mut self, add: HostAdd<'a>) {
|
||||||
self.host.add = Some(add);
|
self.host.add = Some(add);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the data for the <rem> element of the host update
|
/// Sets the data for the <rem> element of the host update
|
||||||
pub fn remove(&mut self, remove: HostAddRemove<'a>) {
|
pub fn remove(&mut self, remove: HostRemove<'a>) {
|
||||||
self.host.remove = Some(remove);
|
self.host.remove = Some(remove);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type for data under the <chg> tag
|
/// Type for data under the <chg> tag
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "chg", ns(XMLNS))]
|
||||||
pub struct HostChangeInfo<'a> {
|
pub struct HostChangeInfo<'a> {
|
||||||
/// The new name for the host
|
/// The new name for the host
|
||||||
#[serde(rename = "host:name")]
|
pub name: &'a str,
|
||||||
pub name: StringValue<'a>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type for data under the <add> and <rem> tags
|
/// Type for data under the <add> and <rem> tags
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
pub struct HostAddRemove<'a> {
|
#[xml(rename = "add", ns(XMLNS))]
|
||||||
|
pub struct HostAdd<'a> {
|
||||||
/// The IP addresses to be added to or removed from the host
|
/// The IP addresses to be added to or removed from the host
|
||||||
#[serde(rename = "host:addr", serialize_with = "serialize_host_addrs_option")]
|
#[xml(rename = "host:addr", serialize_with = "serialize_host_addrs_option")]
|
||||||
pub addresses: Option<&'a [IpAddr]>,
|
pub addresses: Option<&'a [IpAddr]>,
|
||||||
/// The statuses to be added to or removed from the host
|
/// The statuses to be added to or removed from the host
|
||||||
#[serde(rename = "host:status")]
|
#[xml(rename = "host:status")]
|
||||||
pub statuses: Option<&'a [ObjectStatus<'a>]>,
|
pub statuses: Option<&'a [Status<'a>]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type for data under the <add> and <rem> tags
|
||||||
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "rem", ns(XMLNS))]
|
||||||
|
pub struct HostRemove<'a> {
|
||||||
|
/// The IP addresses to be added to or removed from the host
|
||||||
|
#[xml(rename = "host:addr", serialize_with = "serialize_host_addrs_option")]
|
||||||
|
pub addresses: Option<&'a [IpAddr]>,
|
||||||
|
/// The statuses to be added to or removed from the host
|
||||||
|
#[xml(rename = "host:status")]
|
||||||
|
pub statuses: Option<&'a [Status<'a>]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type for data under the host <update> tag
|
/// Type for data under the host <update> tag
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
pub struct HostUpdateRequestData<'a> {
|
#[xml(rename = "update", ns(XMLNS))]
|
||||||
/// XML namespace for host commands
|
pub struct HostUpdateRequest<'a> {
|
||||||
#[serde(rename = "xmlns:host")]
|
|
||||||
xmlns: &'a str,
|
|
||||||
/// The name of the host
|
/// The name of the host
|
||||||
#[serde(rename = "host:name")]
|
name: &'a str,
|
||||||
name: StringValue<'a>,
|
|
||||||
/// The IP addresses and statuses to be added to the host
|
/// The IP addresses and statuses to be added to the host
|
||||||
#[serde(rename = "host:add")]
|
#[xml(rename = "host:add")]
|
||||||
add: Option<HostAddRemove<'a>>,
|
add: Option<HostAdd<'a>>,
|
||||||
/// The IP addresses and statuses to be removed from the host
|
/// The IP addresses and statuses to be removed from the host
|
||||||
#[serde(rename = "host:rem")]
|
#[xml(rename = "host:rem")]
|
||||||
remove: Option<HostAddRemove<'a>>,
|
remove: Option<HostRemove<'a>>,
|
||||||
/// The host details that need to be updated
|
/// The host details that need to be updated
|
||||||
#[serde(rename = "host:chg")]
|
#[xml(rename = "host:chg")]
|
||||||
change_info: Option<HostChangeInfo<'a>>,
|
change_info: Option<HostChangeInfo<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
|
||||||
/// Type for EPP XML <update> command for hosts
|
/// Type for EPP XML <update> command for hosts
|
||||||
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "update", ns(EPP_XMLNS))]
|
||||||
pub struct HostUpdate<'a> {
|
pub struct HostUpdate<'a> {
|
||||||
/// The instance holding the data for the host to be updated
|
/// The instance holding the data for the host to be updated
|
||||||
#[serde(rename = "host:update")]
|
host: HostUpdateRequest<'a>,
|
||||||
host: HostUpdateRequestData<'a>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::IpAddr;
|
use super::IpAddr;
|
||||||
use super::{HostAddRemove, HostChangeInfo, HostUpdate};
|
use super::{HostAdd, HostChangeInfo, HostRemove, HostUpdate, Status};
|
||||||
use crate::common::ObjectStatus;
|
|
||||||
use crate::response::ResultCode;
|
use crate::response::ResultCode;
|
||||||
use crate::tests::{assert_serialized, response_from_file, CLTRID, SUCCESS_MSG, SVTRID};
|
use crate::tests::{assert_serialized, response_from_file, CLTRID, SUCCESS_MSG, SVTRID};
|
||||||
|
|
||||||
|
@ -104,16 +113,16 @@ mod tests {
|
||||||
0x2404, 0x6800, 0x4001, 0x801, 0, 0, 0, 0x200e,
|
0x2404, 0x6800, 0x4001, 0x801, 0, 0, 0, 0x200e,
|
||||||
])];
|
])];
|
||||||
|
|
||||||
let add = HostAddRemove {
|
let add = HostAdd {
|
||||||
addresses: Some(addr),
|
addresses: Some(addr),
|
||||||
statuses: None,
|
statuses: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let statuses = &[ObjectStatus {
|
let statuses = &[Status {
|
||||||
status: "clientDeleteProhibited".into(),
|
status: "clientDeleteProhibited".into(),
|
||||||
}];
|
}];
|
||||||
|
|
||||||
let remove = HostAddRemove {
|
let remove = HostRemove {
|
||||||
addresses: None,
|
addresses: None,
|
||||||
statuses: Some(statuses),
|
statuses: Some(statuses),
|
||||||
};
|
};
|
||||||
|
@ -123,7 +132,7 @@ mod tests {
|
||||||
object.add(add);
|
object.add(add);
|
||||||
object.remove(remove);
|
object.remove(remove);
|
||||||
object.info(HostChangeInfo {
|
object.info(HostChangeInfo {
|
||||||
name: "host2.eppdev-1.com".into(),
|
name: "host2.eppdev-1.com",
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_serialized("request/host/update.xml", &object);
|
assert_serialized("request/host/update.xml", &object);
|
||||||
|
@ -134,8 +143,8 @@ mod tests {
|
||||||
let object = response_from_file::<HostUpdate>("response/host/update.xml");
|
let object = response_from_file::<HostUpdate>("response/host/update.xml");
|
||||||
|
|
||||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
61
src/lib.rs
61
src/lib.rs
|
@ -60,7 +60,7 @@
|
||||||
//! use std::time::Duration;
|
//! use std::time::Duration;
|
||||||
//!
|
//!
|
||||||
//! use epp_client::EppClient;
|
//! use epp_client::EppClient;
|
||||||
//! use epp_client::domain::DomainCheck;
|
//! use epp_client::domain::check::DomainCheck;
|
||||||
//! use epp_client::login::Login;
|
//! use epp_client::login::Login;
|
||||||
//!
|
//!
|
||||||
//! #[tokio::main]
|
//! #[tokio::main]
|
||||||
|
@ -79,9 +79,11 @@
|
||||||
//! let domain_check = DomainCheck { domains: &["eppdev.com", "eppdev.net"] };
|
//! let domain_check = DomainCheck { domains: &["eppdev.com", "eppdev.net"] };
|
||||||
//! let response = client.transact(&domain_check, "transaction-id").await.unwrap();
|
//! let response = client.transact(&domain_check, "transaction-id").await.unwrap();
|
||||||
//!
|
//!
|
||||||
//! response.res_data.unwrap().list
|
//! response.res_data()
|
||||||
|
//! .unwrap()
|
||||||
|
//! .list
|
||||||
//! .iter()
|
//! .iter()
|
||||||
//! .for_each(|chk| println!("Domain: {}, Available: {}", chk.id, chk.available));
|
//! .for_each(|chk| println!("Domain: {}, Available: {}", chk.inner.id, chk.inner.available));
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
@ -113,6 +115,12 @@ pub mod extensions {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod host {
|
pub mod host {
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::fmt;
|
||||||
|
use std::net::IpAddr;
|
||||||
|
|
||||||
|
use instant_xml::{FromXml, Serializer, ToXml};
|
||||||
|
|
||||||
pub mod check;
|
pub mod check;
|
||||||
pub use check::HostCheck;
|
pub use check::HostCheck;
|
||||||
|
|
||||||
|
@ -129,6 +137,53 @@ pub mod host {
|
||||||
pub use update::HostUpdate;
|
pub use update::HostUpdate;
|
||||||
|
|
||||||
pub const XMLNS: &str = "urn:ietf:params:xml:ns:host-1.0";
|
pub const XMLNS: &str = "urn:ietf:params:xml:ns:host-1.0";
|
||||||
|
|
||||||
|
/// The <status> type on contact transactions
|
||||||
|
#[derive(Debug, FromXml, ToXml)]
|
||||||
|
#[xml(rename = "status", ns(XMLNS))]
|
||||||
|
pub struct Status<'a> {
|
||||||
|
/// The status name, represented by the 's' attr on <status> tags
|
||||||
|
#[xml(attribute, rename = "s")]
|
||||||
|
pub status: Cow<'a, str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The <hostAddr> types domain or host transactions
|
||||||
|
#[derive(Debug, FromXml, ToXml)]
|
||||||
|
#[xml(rename = "addr", ns(XMLNS))]
|
||||||
|
pub(crate) struct HostAddr<'a> {
|
||||||
|
#[xml(attribute, rename = "ip")]
|
||||||
|
pub ip_version: Option<Cow<'a, str>>,
|
||||||
|
#[xml(direct)]
|
||||||
|
pub address: Cow<'a, str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&IpAddr> for HostAddr<'static> {
|
||||||
|
fn from(addr: &IpAddr) -> Self {
|
||||||
|
Self {
|
||||||
|
ip_version: Some(match addr {
|
||||||
|
IpAddr::V4(_) => "v4".into(),
|
||||||
|
IpAddr::V6(_) => "v6".into(),
|
||||||
|
}),
|
||||||
|
address: addr.to_string().into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn serialize_host_addrs_option<T: AsRef<[IpAddr]>, W: fmt::Write + ?Sized>(
|
||||||
|
addrs: &Option<T>,
|
||||||
|
serializer: &mut Serializer<'_, W>,
|
||||||
|
) -> Result<(), instant_xml::Error> {
|
||||||
|
let addrs = match addrs {
|
||||||
|
Some(addrs) => addrs.as_ref(),
|
||||||
|
None => return Ok(()),
|
||||||
|
};
|
||||||
|
|
||||||
|
for addr in addrs {
|
||||||
|
HostAddr::from(addr).serialize(None, serializer)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod message {
|
pub mod message {
|
||||||
|
|
33
src/login.rs
33
src/login.rs
|
@ -1,31 +1,32 @@
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use serde::Serialize;
|
use instant_xml::ToXml;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
common::{NoExtension, Options, ServiceExtension, Services, StringValue},
|
common::{NoExtension, Options, ServiceExtension, Services, EPP_XMLNS},
|
||||||
contact, domain, host,
|
contact, domain, host,
|
||||||
request::{Command, Transaction, EPP_LANG, EPP_VERSION},
|
request::{Command, Transaction, EPP_LANG, EPP_VERSION},
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<'a> Transaction<NoExtension> for Login<'a> {}
|
impl<'a> Transaction<NoExtension> for Login<'a> {}
|
||||||
|
|
||||||
#[derive(Serialize, Debug, Eq, PartialEq)]
|
|
||||||
/// Type corresponding to the <login> tag in an EPP XML login request
|
/// Type corresponding to the <login> tag in an EPP XML login request
|
||||||
|
#[derive(Debug, Eq, PartialEq, ToXml)]
|
||||||
|
#[xml(rename = "login", ns(EPP_XMLNS))]
|
||||||
pub struct Login<'a> {
|
pub struct Login<'a> {
|
||||||
/// The username to use for the login
|
/// The username to use for the login
|
||||||
#[serde(rename(serialize = "clID", deserialize = "clID"))]
|
#[xml(rename = "clID")]
|
||||||
username: StringValue<'a>,
|
username: &'a str,
|
||||||
/// The password to use for the login
|
/// The password to use for the login
|
||||||
#[serde(rename = "pw", default)]
|
#[xml(rename = "pw")]
|
||||||
password: StringValue<'a>,
|
password: &'a str,
|
||||||
/// A new password which should be set
|
/// A new password which should be set
|
||||||
#[serde(rename = "newPW", default, skip_serializing_if = "Option::is_none")]
|
#[xml(rename = "newPW")]
|
||||||
new_password: Option<StringValue<'a>>,
|
new_password: Option<&'a str>,
|
||||||
/// Data under the <options> tag
|
/// Data under the <options> tag
|
||||||
options: Options<'a>,
|
options: Options<'a>,
|
||||||
/// Data under the <svcs> tag
|
/// Data under the <svcs> tag
|
||||||
#[serde(rename = "svcs")]
|
#[xml(rename = "svcs")]
|
||||||
services: Services<'a>,
|
services: Services<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,9 +40,9 @@ impl<'a> Login<'a> {
|
||||||
let ext_uris = ext_uris.map(|uris| uris.iter().map(|&u| u.into()).collect());
|
let ext_uris = ext_uris.map(|uris| uris.iter().map(|&u| u.into()).collect());
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
username: username.into(),
|
username,
|
||||||
password: password.into(),
|
password,
|
||||||
new_password: new_password.map(Into::into),
|
new_password,
|
||||||
options: Options {
|
options: Options {
|
||||||
version: EPP_VERSION.into(),
|
version: EPP_VERSION.into(),
|
||||||
lang: EPP_LANG.into(),
|
lang: EPP_LANG.into(),
|
||||||
|
@ -90,8 +91,8 @@ mod tests {
|
||||||
fn response() {
|
fn response() {
|
||||||
let object = response_from_file::<Login>("response/login.xml");
|
let object = response_from_file::<Login>("response/login.xml");
|
||||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use instant_xml::{FromXml, ToXml};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
common::NoExtension,
|
common::{NoExtension, EPP_XMLNS},
|
||||||
request::{Command, Transaction},
|
request::{Command, Transaction},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -14,8 +14,9 @@ impl Command for Logout {
|
||||||
const COMMAND: &'static str = "logout";
|
const COMMAND: &'static str = "logout";
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq, ToXml)]
|
||||||
/// Type corresponding to the <logout> tag in an EPP XML logout request
|
/// Type corresponding to the <logout> tag in an EPP XML logout request
|
||||||
|
#[xml(rename = "logout", ns(EPP_XMLNS))]
|
||||||
pub struct Logout;
|
pub struct Logout;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -40,9 +41,9 @@ mod tests {
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
object.result.message,
|
object.result.message,
|
||||||
"Command completed successfully; ending session".into()
|
"Command completed successfully; ending session"
|
||||||
);
|
);
|
||||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
//! Types for EPP message ack request
|
//! Types for EPP message ack request
|
||||||
|
|
||||||
use crate::common::NoExtension;
|
use instant_xml::ToXml;
|
||||||
|
|
||||||
|
use crate::common::{NoExtension, EPP_XMLNS};
|
||||||
use crate::request::{Command, Transaction};
|
use crate::request::{Command, Transaction};
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
impl<'a> Transaction<NoExtension> for MessageAck<'a> {}
|
impl<'a> Transaction<NoExtension> for MessageAck<'a> {}
|
||||||
|
|
||||||
|
@ -11,14 +12,16 @@ impl<'a> Command for MessageAck<'a> {
|
||||||
const COMMAND: &'static str = "poll";
|
const COMMAND: &'static str = "poll";
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
/// Type for EPP XML <poll> command for message ack
|
/// Type for EPP XML <poll> command for message ack
|
||||||
|
#[xml(rename = "poll", ns(EPP_XMLNS))]
|
||||||
pub struct MessageAck<'a> {
|
pub struct MessageAck<'a> {
|
||||||
/// The type of operation to perform
|
/// The type of operation to perform
|
||||||
/// The value is "ack" for message acknowledgement
|
/// The value is "ack" for message acknowledgement
|
||||||
|
#[xml(attribute)]
|
||||||
op: &'a str,
|
op: &'a str,
|
||||||
/// The ID of the message to be acknowledged
|
/// The ID of the message to be acknowledged
|
||||||
#[serde(rename = "msgID")]
|
#[xml(rename = "msgID", attribute)]
|
||||||
message_id: &'a str,
|
message_id: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,9 +52,9 @@ mod tests {
|
||||||
let msg = object.message_queue().unwrap();
|
let msg = object.message_queue().unwrap();
|
||||||
|
|
||||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||||
assert_eq!(msg.count, 4);
|
assert_eq!(msg.count, 4);
|
||||||
assert_eq!(msg.id, "12345".to_string());
|
assert_eq!(msg.id, "12345".to_string());
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::common::NoExtension;
|
use instant_xml::{FromXml, ToXml};
|
||||||
|
|
||||||
|
use crate::common::{NoExtension, EPP_XMLNS};
|
||||||
use crate::domain::transfer::DomainTransferResponseData;
|
use crate::domain::transfer::DomainTransferResponseData;
|
||||||
use crate::extensions::low_balance::LowBalance;
|
use crate::extensions::low_balance::LowBalance;
|
||||||
use crate::host::info::HostInfoResponseData;
|
use crate::host::info::HostInfoResponseData;
|
||||||
use crate::request::{Command, Transaction};
|
use crate::request::{Command, Transaction};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
impl<'a> Transaction<NoExtension> for MessagePoll<'a> {}
|
impl<'a> Transaction<NoExtension> for MessagePoll<'a> {}
|
||||||
|
|
||||||
|
@ -14,11 +15,13 @@ impl<'a> Command for MessagePoll<'a> {
|
||||||
|
|
||||||
// Request
|
// Request
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Debug, ToXml)]
|
||||||
/// Type for EPP XML <poll> command for message poll
|
/// Type for EPP XML <poll> command for message poll
|
||||||
|
#[xml(rename = "poll", ns(EPP_XMLNS))]
|
||||||
pub struct MessagePoll<'a> {
|
pub struct MessagePoll<'a> {
|
||||||
/// The type of operation to perform
|
/// The type of operation to perform
|
||||||
/// The value is "req" for message polling
|
/// The value is "req" for message polling
|
||||||
|
#[xml(attribute)]
|
||||||
op: &'a str,
|
op: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,32 +34,20 @@ impl Default for MessagePoll<'static> {
|
||||||
// Response
|
// Response
|
||||||
|
|
||||||
/// Type that represents the <trnData> tag for message poll response
|
/// Type that represents the <trnData> tag for message poll response
|
||||||
#[non_exhaustive]
|
#[derive(Debug, FromXml)]
|
||||||
#[derive(Deserialize, Debug)]
|
#[xml(forward)]
|
||||||
pub enum MessageData {
|
pub enum MessagePollResponse {
|
||||||
/// Data under the <domain:trnData> tag
|
/// Data under the <domain:trnData> tag
|
||||||
#[serde(rename = "trnData")]
|
|
||||||
DomainTransfer(DomainTransferResponseData),
|
DomainTransfer(DomainTransferResponseData),
|
||||||
/// Data under the <host:infData> tag
|
/// Data under the <host:infData> tag
|
||||||
#[serde(rename = "infData")]
|
|
||||||
HostInfo(HostInfoResponseData),
|
HostInfo(HostInfoResponseData),
|
||||||
/// Data under the <lowbalance> tag
|
/// Data under the <lowbalance> tag
|
||||||
#[serde(rename = "pollData")]
|
|
||||||
LowBalance(LowBalance),
|
LowBalance(LowBalance),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type that represents the <resData> tag for message poll response
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
pub struct MessagePollResponse {
|
|
||||||
/// Data under the <trnData> tag
|
|
||||||
#[serde(rename = "trnData", alias = "infData", alias = "pollData")]
|
|
||||||
pub message_data: MessageData,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::MessagePoll;
|
use super::{MessagePoll, MessagePollResponse};
|
||||||
use crate::message::poll::MessageData;
|
|
||||||
use crate::response::ResultCode;
|
use crate::response::ResultCode;
|
||||||
use crate::tests::{assert_serialized, response_from_file, CLTRID, SVTRID};
|
use crate::tests::{assert_serialized, response_from_file, CLTRID, SVTRID};
|
||||||
|
|
||||||
|
@ -81,7 +72,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
object.result.message,
|
object.result.message,
|
||||||
"Command completed successfully; ack to dequeue".into()
|
"Command completed successfully; ack to dequeue"
|
||||||
);
|
);
|
||||||
assert_eq!(msg.count, 5);
|
assert_eq!(msg.count, 5);
|
||||||
assert_eq!(msg.id, "12345".to_string());
|
assert_eq!(msg.id, "12345".to_string());
|
||||||
|
@ -89,20 +80,17 @@ mod tests {
|
||||||
msg.date,
|
msg.date,
|
||||||
Utc.with_ymd_and_hms(2021, 7, 23, 19, 12, 43).single()
|
Utc.with_ymd_and_hms(2021, 7, 23, 19, 12, 43).single()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(msg.message.as_ref().unwrap().text, "Transfer requested.");
|
||||||
*(msg.message.as_ref().unwrap()),
|
|
||||||
"Transfer requested.".into()
|
|
||||||
);
|
|
||||||
|
|
||||||
if let MessageData::DomainTransfer(tr) = &result.message_data {
|
if let MessagePollResponse::DomainTransfer(tr) = &result {
|
||||||
assert_eq!(tr.name, "eppdev-transfer.com".into());
|
assert_eq!(tr.name, "eppdev-transfer.com");
|
||||||
assert_eq!(tr.transfer_status, "pending".into());
|
assert_eq!(tr.transfer_status, "pending");
|
||||||
assert_eq!(tr.requester_id, "eppdev".into());
|
assert_eq!(tr.requester_id, "eppdev");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
tr.requested_at,
|
tr.requested_at,
|
||||||
Utc.with_ymd_and_hms(2021, 7, 23, 15, 31, 21).unwrap()
|
Utc.with_ymd_and_hms(2021, 7, 23, 15, 31, 21).unwrap()
|
||||||
);
|
);
|
||||||
assert_eq!(tr.ack_id, "ClientY".into());
|
assert_eq!(tr.ack_id, "ClientY");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
tr.ack_by,
|
tr.ack_by,
|
||||||
Utc.with_ymd_and_hms(2021, 7, 28, 15, 31, 21).unwrap()
|
Utc.with_ymd_and_hms(2021, 7, 28, 15, 31, 21).unwrap()
|
||||||
|
@ -115,8 +103,8 @@ mod tests {
|
||||||
panic!("Wrong type");
|
panic!("Wrong type");
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -131,7 +119,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
object.result.message,
|
object.result.message,
|
||||||
"Command completed successfully; ack to dequeue".into()
|
"Command completed successfully; ack to dequeue"
|
||||||
);
|
);
|
||||||
assert_eq!(msg.count, 4);
|
assert_eq!(msg.count, 4);
|
||||||
assert_eq!(msg.id, "12345".to_string());
|
assert_eq!(msg.id, "12345".to_string());
|
||||||
|
@ -139,22 +127,19 @@ mod tests {
|
||||||
msg.date,
|
msg.date,
|
||||||
Utc.with_ymd_and_hms(2022, 1, 2, 11, 30, 45).single()
|
Utc.with_ymd_and_hms(2022, 1, 2, 11, 30, 45).single()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(msg.message.as_ref().unwrap().text, "Unused objects policy");
|
||||||
*(msg.message.as_ref().unwrap()),
|
|
||||||
"Unused objects policy".into()
|
|
||||||
);
|
|
||||||
|
|
||||||
if let MessageData::HostInfo(host) = &result.message_data {
|
if let MessagePollResponse::HostInfo(host) = &result {
|
||||||
assert_eq!(host.name, "ns.test.com".into());
|
assert_eq!(host.name, "ns.test.com");
|
||||||
|
|
||||||
assert_eq!(host.roid, "1234".into());
|
assert_eq!(host.roid, "1234");
|
||||||
assert!(host.statuses.iter().any(|s| s.status == "ok"));
|
assert!(host.statuses.iter().any(|s| s.status == "ok"));
|
||||||
assert!(host
|
assert!(host
|
||||||
.addresses
|
.addresses
|
||||||
.iter()
|
.iter()
|
||||||
.any(|a| a == &IpAddr::from([1, 1, 1, 1])));
|
.any(|a| a == &IpAddr::from([1, 1, 1, 1])));
|
||||||
assert_eq!(host.client_id, "1234".into());
|
assert_eq!(host.client_id, "1234");
|
||||||
assert_eq!(host.creator_id, "user".into());
|
assert_eq!(host.creator_id, "user");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
host.created_at,
|
host.created_at,
|
||||||
Utc.with_ymd_and_hms(2021, 12, 1, 22, 40, 48).unwrap()
|
Utc.with_ymd_and_hms(2021, 12, 1, 22, 40, 48).unwrap()
|
||||||
|
@ -168,14 +153,15 @@ mod tests {
|
||||||
panic!("Wrong type");
|
panic!("Wrong type");
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn message_only_response() {
|
fn message_only_response() {
|
||||||
let object = response_from_file::<MessagePoll>("response/message/poll_message_only.xml");
|
let object = response_from_file::<MessagePoll>("response/message/poll_message_only.xml");
|
||||||
let msg = object.message_queue().unwrap();
|
let msg = object.message_queue().unwrap();
|
||||||
|
dbg!(&msg);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
object.result.code,
|
object.result.code,
|
||||||
|
@ -183,7 +169,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
object.result.message,
|
object.result.message,
|
||||||
"Command completed successfully; ack to dequeue".into()
|
"Command completed successfully; ack to dequeue"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(msg.count, 4);
|
assert_eq!(msg.count, 4);
|
||||||
|
@ -192,13 +178,10 @@ mod tests {
|
||||||
msg.date,
|
msg.date,
|
||||||
Utc.with_ymd_and_hms(2000, 6, 8, 22, 10, 0).single()
|
Utc.with_ymd_and_hms(2000, 6, 8, 22, 10, 0).single()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(msg.message.as_ref().unwrap().text, "Credit balance low.");
|
||||||
*(msg.message.as_ref().unwrap()),
|
|
||||||
"Credit balance low.".into()
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -211,10 +194,10 @@ mod tests {
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
object.result.message,
|
object.result.message,
|
||||||
"Command completed successfully; no messages".into()
|
"Command completed successfully; no messages"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
//! Types for EPP requests
|
//! Types for EPP requests
|
||||||
|
|
||||||
use serde::{de::DeserializeOwned, ser::SerializeStruct, ser::Serializer, Serialize};
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use crate::common::{StringValue, EPP_XMLNS};
|
use instant_xml::{FromXmlOwned, ToXml};
|
||||||
|
|
||||||
|
use crate::common::EPP_XMLNS;
|
||||||
|
|
||||||
pub const EPP_VERSION: &str = "1.0";
|
pub const EPP_VERSION: &str = "1.0";
|
||||||
pub const EPP_LANG: &str = "en";
|
pub const EPP_LANG: &str = "en";
|
||||||
|
@ -11,62 +12,63 @@ pub const EPP_LANG: &str = "en";
|
||||||
/// Trait to set correct value for xml tags when tags are being generated from generic types
|
/// Trait to set correct value for xml tags when tags are being generated from generic types
|
||||||
pub trait Transaction<Ext: Extension>: Command + Sized {}
|
pub trait Transaction<Ext: Extension>: Command + Sized {}
|
||||||
|
|
||||||
pub trait Command: Serialize + Debug {
|
pub trait Command: ToXml + Debug {
|
||||||
type Response: DeserializeOwned + Debug;
|
type Response: FromXmlOwned + Debug;
|
||||||
const COMMAND: &'static str;
|
const COMMAND: &'static str;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Extension: Serialize + Debug {
|
pub trait Extension: ToXml + Debug {
|
||||||
type Response: DeserializeOwned + Debug;
|
type Response: FromXmlOwned + Debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
/// Type corresponding to the <command> tag in an EPP XML request
|
/// Type corresponding to the <command> tag in an EPP XML request
|
||||||
/// with an <extension> tag
|
/// with an <extension> tag
|
||||||
struct CommandWrapper<'a, D, E> {
|
pub(crate) struct CommandWrapper<'a, D, E> {
|
||||||
pub command: &'static str,
|
pub command: &'static str,
|
||||||
/// The instance that will be used to populate the <command> tag
|
/// The instance that will be used to populate the <command> tag
|
||||||
pub data: &'a D,
|
pub data: &'a D,
|
||||||
/// The client TRID
|
/// The client TRID
|
||||||
pub extension: Option<&'a E>,
|
pub extension: Option<&'a E>,
|
||||||
pub client_tr_id: StringValue<'a>,
|
pub client_tr_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, D: Serialize, E: Serialize> Serialize for CommandWrapper<'a, D, E> {
|
impl<'a, E: Extension, D: Transaction<E>> CommandWrapper<'a, D, E> {
|
||||||
/// Serializes the generic type T to the proper XML tag (set by the `#[element_name(name = <tagname>)]` attribute) for the request
|
pub(crate) fn new(data: &'a D, extension: Option<&'a E>, client_tr_id: &'a str) -> Self {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: Serializer,
|
|
||||||
{
|
|
||||||
let mut state = serializer.serialize_struct("command", 3)?;
|
|
||||||
state.serialize_field(self.command, self.data)?;
|
|
||||||
state.serialize_field("extension", &self.extension)?;
|
|
||||||
state.serialize_field("clTRID", &self.client_tr_id)?;
|
|
||||||
state.end()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize)]
|
|
||||||
#[serde(rename = "epp")]
|
|
||||||
pub(crate) struct CommandDocument<'a, Cmd, Ext> {
|
|
||||||
xmlns: &'static str,
|
|
||||||
command: CommandWrapper<'a, Cmd, Ext>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Cmd, Ext> CommandDocument<'a, Cmd, Ext> {
|
|
||||||
pub(crate) fn new(data: &'a Cmd, extension: Option<&'a Ext>, client_tr_id: &'a str) -> Self
|
|
||||||
where
|
|
||||||
Cmd: Transaction<Ext>,
|
|
||||||
Ext: Extension,
|
|
||||||
{
|
|
||||||
Self {
|
Self {
|
||||||
xmlns: EPP_XMLNS,
|
command: D::COMMAND,
|
||||||
command: CommandWrapper {
|
data,
|
||||||
command: Cmd::COMMAND,
|
extension,
|
||||||
data,
|
client_tr_id: client_tr_id.into(),
|
||||||
extension,
|
|
||||||
client_tr_id: client_tr_id.into(),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a, D: ToXml, E: ToXml> ToXml for CommandWrapper<'a, D, E> {
|
||||||
|
fn serialize<W: std::fmt::Write + ?Sized>(
|
||||||
|
&self,
|
||||||
|
_: Option<instant_xml::Id<'_>>,
|
||||||
|
serializer: &mut instant_xml::Serializer<W>,
|
||||||
|
) -> Result<(), instant_xml::Error> {
|
||||||
|
let prefix = serializer.write_start("command", EPP_XMLNS)?;
|
||||||
|
serializer.end_start()?;
|
||||||
|
self.data.serialize(None, serializer)?;
|
||||||
|
if let Some(extension) = self.extension {
|
||||||
|
Ext { inner: extension }.serialize(None, serializer)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let id_prefix = serializer.write_start("clTRID", EPP_XMLNS)?;
|
||||||
|
serializer.end_start()?;
|
||||||
|
serializer.write_str(&self.client_tr_id)?;
|
||||||
|
serializer.write_close(id_prefix, "clTRID")?;
|
||||||
|
|
||||||
|
serializer.write_close(prefix, "command")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, ToXml)]
|
||||||
|
#[xml(rename = "extension", ns(EPP_XMLNS))]
|
||||||
|
struct Ext<E> {
|
||||||
|
inner: E,
|
||||||
|
}
|
||||||
|
|
160
src/response.rs
160
src/response.rs
|
@ -3,43 +3,44 @@
|
||||||
use std::fmt::{self, Debug};
|
use std::fmt::{self, Debug};
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::Deserialize;
|
use instant_xml::{FromXml, Kind};
|
||||||
|
|
||||||
use crate::common::StringValue;
|
use crate::common::EPP_XMLNS;
|
||||||
|
|
||||||
/// Type corresponding to the <undef> tag an EPP response XML
|
/// Type corresponding to the <undef> tag an EPP response XML
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "undef", ns(EPP_XMLNS))]
|
||||||
pub struct Undef;
|
pub struct Undef;
|
||||||
|
|
||||||
/// Type corresponding to the <value> tag under <extValue> in an EPP response XML
|
/// Type corresponding to the <value> tag under <extValue> in an EPP response XML
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "value", ns(EPP_XMLNS))]
|
||||||
pub struct ResultValue {
|
pub struct ResultValue {
|
||||||
/// The XML namespace for the <value> tag
|
|
||||||
#[serde(rename = "xmlns:epp")]
|
|
||||||
xmlns: String,
|
|
||||||
/// The <undef> element
|
/// The <undef> element
|
||||||
pub undef: Undef,
|
pub undef: Undef,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type corresponding to the <extValue> tag in an EPP response XML
|
/// Type corresponding to the <extValue> tag in an EPP response XML
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "extValue", ns(EPP_XMLNS))]
|
||||||
pub struct ExtValue {
|
pub struct ExtValue {
|
||||||
/// Data under the <value> tag
|
/// Data under the <value> tag
|
||||||
pub value: ResultValue,
|
pub value: ResultValue,
|
||||||
/// Data under the <reason> tag
|
/// Data under the <reason> tag
|
||||||
pub reason: StringValue<'static>,
|
pub reason: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type corresponding to the <result> tag in an EPP response XML
|
/// Type corresponding to the <result> tag in an EPP response XML
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "result", ns(EPP_XMLNS))]
|
||||||
pub struct EppResult {
|
pub struct EppResult {
|
||||||
/// The result code
|
/// The result code
|
||||||
|
#[xml(attribute)]
|
||||||
pub code: ResultCode,
|
pub code: ResultCode,
|
||||||
/// The result message
|
/// The result message
|
||||||
#[serde(rename = "msg")]
|
#[xml(rename = "msg")]
|
||||||
pub message: StringValue<'static>,
|
pub message: String,
|
||||||
/// Data under the <extValue> tag
|
/// Data under the <extValue> tag
|
||||||
#[serde(rename = "extValue")]
|
|
||||||
pub ext_value: Option<ExtValue>,
|
pub ext_value: Option<ExtValue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,13 +137,37 @@ impl ResultCode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for ResultCode {
|
impl<'xml> FromXml<'xml> for ResultCode {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<ResultCode, D::Error>
|
fn matches(id: instant_xml::Id<'_>, field: Option<instant_xml::Id<'_>>) -> bool {
|
||||||
where
|
match field {
|
||||||
D: serde::de::Deserializer<'de>,
|
Some(field) => id == field,
|
||||||
{
|
None => false,
|
||||||
deserializer.deserialize_u16(ResultCodeVisitor)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn deserialize<'cx>(
|
||||||
|
into: &mut Self::Accumulator,
|
||||||
|
field: &'static str,
|
||||||
|
deserializer: &mut instant_xml::Deserializer<'cx, 'xml>,
|
||||||
|
) -> Result<(), instant_xml::Error> {
|
||||||
|
let mut value = None;
|
||||||
|
u16::deserialize(&mut value, field, deserializer)?;
|
||||||
|
if let Some(value) = value {
|
||||||
|
*into = match ResultCode::from_u16(value) {
|
||||||
|
Some(value) => Some(value),
|
||||||
|
None => {
|
||||||
|
return Err(instant_xml::Error::UnexpectedValue(format!(
|
||||||
|
"unexpected result code '{value}'"
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
type Accumulator = Option<Self>;
|
||||||
|
const KIND: instant_xml::Kind = Kind::Scalar;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ResultCodeVisitor;
|
struct ResultCodeVisitor;
|
||||||
|
@ -166,71 +191,82 @@ impl<'de> serde::de::Visitor<'de> for ResultCodeVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type corresponding to the <trID> tag in an EPP response XML
|
/// Type corresponding to the <trID> tag in an EPP response XML
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "trID", ns(EPP_XMLNS))]
|
||||||
pub struct ResponseTRID {
|
pub struct ResponseTRID {
|
||||||
/// The client TRID
|
/// The client TRID
|
||||||
#[serde(rename = "clTRID")]
|
#[xml(rename = "clTRID")]
|
||||||
pub client_tr_id: Option<StringValue<'static>>,
|
pub client_tr_id: Option<String>,
|
||||||
/// The server TRID
|
/// The server TRID
|
||||||
#[serde(rename = "svTRID")]
|
#[xml(rename = "svTRID")]
|
||||||
pub server_tr_id: StringValue<'static>,
|
pub server_tr_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type corresponding to the <msgQ> tag in an EPP response XML
|
/// Type corresponding to the <msgQ> tag in an EPP response XML
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "msgQ", ns(EPP_XMLNS))]
|
||||||
pub struct MessageQueue {
|
pub struct MessageQueue {
|
||||||
/// The message count
|
/// The message count
|
||||||
|
#[xml(attribute)]
|
||||||
pub count: u32,
|
pub count: u32,
|
||||||
/// The message ID
|
/// The message ID
|
||||||
|
#[xml(attribute)]
|
||||||
pub id: String,
|
pub id: String,
|
||||||
/// The message date
|
/// The message date
|
||||||
#[serde(rename = "qDate")]
|
#[xml(rename = "qDate")]
|
||||||
pub date: Option<DateTime<Utc>>,
|
pub date: Option<DateTime<Utc>>,
|
||||||
/// The message text
|
/// The message text
|
||||||
#[serde(rename = "msg")]
|
#[xml(rename = "msg")]
|
||||||
pub message: Option<StringValue<'static>>,
|
pub message: Option<Message>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "msg", ns(EPP_XMLNS))]
|
||||||
|
pub struct Message {
|
||||||
|
#[xml(attribute)]
|
||||||
|
pub lang: Option<String>,
|
||||||
|
#[xml(direct)]
|
||||||
|
pub text: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, FromXml, PartialEq)]
|
||||||
/// Type corresponding to the <response> tag in an EPP response XML
|
/// Type corresponding to the <response> tag in an EPP response XML
|
||||||
/// containing an <extension> tag
|
/// containing an <extension> tag
|
||||||
|
#[xml(rename = "response", ns(EPP_XMLNS))]
|
||||||
pub struct Response<D, E> {
|
pub struct Response<D, E> {
|
||||||
/// Data under the <result> tag
|
/// Data under the <result> tag
|
||||||
pub result: EppResult,
|
pub result: EppResult,
|
||||||
/// Data under the <msgQ> tag
|
/// Data under the <msgQ> tag
|
||||||
#[serde(rename = "msgQ")]
|
#[xml(rename = "msgQ")]
|
||||||
pub message_queue: Option<MessageQueue>,
|
pub message_queue: Option<MessageQueue>,
|
||||||
#[serde(rename = "resData")]
|
|
||||||
/// Data under the <resData> tag
|
/// Data under the <resData> tag
|
||||||
pub res_data: Option<D>,
|
pub res_data: Option<ResponseData<D>>,
|
||||||
/// Data under the <extension> tag
|
/// Data under the <extension> tag
|
||||||
pub extension: Option<E>,
|
pub extension: Option<Extension<E>>,
|
||||||
/// Data under the <trID> tag
|
/// Data under the <trID> tag
|
||||||
#[serde(rename = "trID")]
|
|
||||||
pub tr_ids: ResponseTRID,
|
pub tr_ids: ResponseTRID,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
#[serde(rename = "epp")]
|
#[xml(rename = "resData", ns(EPP_XMLNS))]
|
||||||
pub struct ResponseDocument<D, E> {
|
pub struct ResponseData<D> {
|
||||||
#[serde(rename = "response")]
|
data: D,
|
||||||
pub data: Response<D, E>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Eq, PartialEq)]
|
impl<D> ResponseData<D> {
|
||||||
#[serde(rename = "epp")]
|
pub fn into_inner(self) -> D {
|
||||||
pub struct ResultDocument {
|
self.data
|
||||||
#[serde(rename = "response")]
|
}
|
||||||
pub data: ResponseStatus,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Debug, FromXml, PartialEq)]
|
||||||
/// Type corresponding to the <response> tag in an EPP response XML
|
/// Type corresponding to the <response> tag in an EPP response XML
|
||||||
/// without <msgQ> or <resData> sections. Generally used for error handling
|
/// without <msgQ> or <resData> sections. Generally used for error handling
|
||||||
|
#[xml(rename = "response", ns(EPP_XMLNS))]
|
||||||
pub struct ResponseStatus {
|
pub struct ResponseStatus {
|
||||||
/// Data under the <result> tag
|
/// Data under the <result> tag
|
||||||
pub result: EppResult,
|
pub result: EppResult,
|
||||||
#[serde(rename = "trID")]
|
#[xml(rename = "trID")]
|
||||||
/// Data under the <trID> tag
|
/// Data under the <trID> tag
|
||||||
pub tr_ids: ResponseTRID,
|
pub tr_ids: ResponseTRID,
|
||||||
}
|
}
|
||||||
|
@ -239,10 +275,18 @@ impl<T, E> Response<T, E> {
|
||||||
/// Returns the data under the corresponding <resData> from the EPP XML
|
/// Returns the data under the corresponding <resData> from the EPP XML
|
||||||
pub fn res_data(&self) -> Option<&T> {
|
pub fn res_data(&self) -> Option<&T> {
|
||||||
match &self.res_data {
|
match &self.res_data {
|
||||||
Some(res_data) => Some(res_data),
|
Some(res_data) => Some(&res_data.data),
|
||||||
None => None,
|
None => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn extension(&self) -> Option<&E> {
|
||||||
|
match &self.extension {
|
||||||
|
Some(extension) => Some(&extension.data),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the data under the corresponding <msgQ> from the EPP XML
|
/// Returns the data under the corresponding <msgQ> from the EPP XML
|
||||||
pub fn message_queue(&self) -> Option<&MessageQueue> {
|
pub fn message_queue(&self) -> Option<&MessageQueue> {
|
||||||
match &self.message_queue {
|
match &self.message_queue {
|
||||||
|
@ -252,24 +296,30 @@ impl<T, E> Response<T, E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||||
|
#[xml(rename = "extension", ns(EPP_XMLNS))]
|
||||||
|
pub struct Extension<E> {
|
||||||
|
pub data: E,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{ResultCode, ResultDocument};
|
use super::{ResponseStatus, ResultCode};
|
||||||
use crate::tests::{get_xml, CLTRID, SVTRID};
|
use crate::tests::{get_xml, CLTRID, SVTRID};
|
||||||
use crate::xml;
|
use crate::xml;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn error() {
|
fn error() {
|
||||||
let xml = get_xml("response/error.xml").unwrap();
|
let xml = get_xml("response/error.xml").unwrap();
|
||||||
let object = xml::deserialize::<ResultDocument>(xml.as_str()).unwrap();
|
let object = xml::deserialize::<ResponseStatus>(xml.as_str()).unwrap();
|
||||||
|
|
||||||
assert_eq!(object.data.result.code, ResultCode::ObjectDoesNotExist);
|
assert_eq!(object.result.code, ResultCode::ObjectDoesNotExist);
|
||||||
assert_eq!(object.data.result.message, "Object does not exist".into());
|
assert_eq!(object.result.message, "Object does not exist");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
object.data.result.ext_value.unwrap().reason,
|
object.result.ext_value.unwrap().reason,
|
||||||
"545 Object not found".into()
|
"545 Object not found"
|
||||||
);
|
);
|
||||||
assert_eq!(object.data.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||||
assert_eq!(object.data.tr_ids.server_tr_id, SVTRID.into());
|
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,13 @@
|
||||||
use std::{error::Error, fs::File, io::Read};
|
use std::{error::Error, fs::File, io::Read};
|
||||||
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
use similar_asserts::assert_eq;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
client::RequestData,
|
client::RequestData,
|
||||||
common::NoExtension,
|
common::NoExtension,
|
||||||
request::{Command, CommandDocument, Extension, Transaction},
|
request::{Command, CommandWrapper, Extension, Transaction},
|
||||||
response::{Response, ResponseDocument},
|
response::Response,
|
||||||
xml,
|
xml,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -46,8 +47,8 @@ pub(crate) fn assert_serialized<'c, 'e, Cmd, Ext>(
|
||||||
{
|
{
|
||||||
let expected = get_xml(path).unwrap();
|
let expected = get_xml(path).unwrap();
|
||||||
let req = req.into();
|
let req = req.into();
|
||||||
let document = CommandDocument::new(req.command, req.extension, CLTRID);
|
let document = CommandWrapper::new(req.command, req.extension, CLTRID);
|
||||||
assert_eq!(expected, xml::serialize(&document).unwrap());
|
assert_eq!(expected, xml::serialize(document).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn response_from_file<'c, Cmd>(
|
pub(crate) fn response_from_file<'c, Cmd>(
|
||||||
|
@ -67,7 +68,8 @@ where
|
||||||
Ext: Extension,
|
Ext: Extension,
|
||||||
{
|
{
|
||||||
let xml = get_xml(path).unwrap();
|
let xml = get_xml(path).unwrap();
|
||||||
let rsp = xml::deserialize::<ResponseDocument<Cmd::Response, Ext::Response>>(&xml).unwrap();
|
dbg!(&xml);
|
||||||
assert!(rsp.data.result.code.is_success());
|
let rsp = xml::deserialize::<Response<Cmd::Response, Ext::Response>>(&xml).unwrap();
|
||||||
rsp.data
|
assert!(rsp.result.code.is_success());
|
||||||
|
rsp
|
||||||
}
|
}
|
||||||
|
|
20
src/xml.rs
20
src/xml.rs
|
@ -1,19 +1,29 @@
|
||||||
//! Types to use in serialization to and deserialization from EPP XML
|
//! Types to use in serialization to and deserialization from EPP XML
|
||||||
|
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use instant_xml::{FromXml, FromXmlOwned, ToXml};
|
||||||
|
|
||||||
|
use crate::common::EPP_XMLNS;
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
|
||||||
pub const EPP_XML_HEADER: &str = r#"<?xml version="1.0" encoding="UTF-8" standalone="no"?>"#;
|
pub const EPP_XML_HEADER: &str = r#"<?xml version="1.0" encoding="UTF-8" standalone="no"?>"#;
|
||||||
|
|
||||||
pub(crate) fn serialize(doc: &impl Serialize) -> Result<String, Error> {
|
pub(crate) fn serialize(data: impl ToXml) -> Result<String, Error> {
|
||||||
Ok(format!(
|
Ok(format!(
|
||||||
"{}\r\n{}",
|
"{}\r\n{}",
|
||||||
EPP_XML_HEADER,
|
EPP_XML_HEADER,
|
||||||
quick_xml::se::to_string(doc).map_err(|e| Error::Xml(e.into()))?
|
instant_xml::to_string(&Epp { data }).map_err(|e| Error::Xml(e.into()))?
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn deserialize<T: DeserializeOwned>(xml: &str) -> Result<T, Error> {
|
pub(crate) fn deserialize<T: FromXmlOwned>(xml: &str) -> Result<T, Error> {
|
||||||
quick_xml::de::from_str(xml).map_err(|e| Error::Xml(e.into()))
|
match instant_xml::from_str::<Epp<T>>(xml) {
|
||||||
|
Ok(Epp { data }) => Ok(data),
|
||||||
|
Err(e) => Err(Error::Xml(e.into())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromXml, ToXml)]
|
||||||
|
#[xml(rename = "epp", ns(EPP_XMLNS))]
|
||||||
|
pub(crate) struct Epp<T> {
|
||||||
|
pub(crate) data: T,
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,7 +131,7 @@ async fn client() {
|
||||||
assert_eq!(rsp.result.code, ResultCode::CommandCompletedSuccessfully);
|
assert_eq!(rsp.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||||
|
|
||||||
let result = rsp.res_data().unwrap();
|
let result = rsp.res_data().unwrap();
|
||||||
assert_eq!(result.list[0].id, "eppdev.com");
|
assert_eq!(result.list[0].inner.id, "eppdev.com");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<check>
|
<check>
|
||||||
<contact:check xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
|
<check xmlns="urn:ietf:params:xml:ns:contact-1.0">
|
||||||
<contact:id>eppdev-contact-1</contact:id>
|
<id>eppdev-contact-1</id>
|
||||||
<contact:id>eppdev-contact-2</contact:id>
|
<id>eppdev-contact-2</id>
|
||||||
</contact:check>
|
</check>
|
||||||
</check>
|
</check>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -2,28 +2,28 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<create>
|
<create>
|
||||||
<contact:create xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
|
<create xmlns="urn:ietf:params:xml:ns:contact-1.0">
|
||||||
<contact:id>eppdev-contact-3</contact:id>
|
<id>eppdev-contact-3</id>
|
||||||
<contact:postalInfo type="int">
|
<postalInfo type="int">
|
||||||
<contact:name>John Doe</contact:name>
|
<name>John Doe</name>
|
||||||
<contact:org>Acme Widgets</contact:org>
|
<org>Acme Widgets</org>
|
||||||
<contact:addr>
|
<addr>
|
||||||
<contact:street>58</contact:street>
|
<street>58</street>
|
||||||
<contact:street>Orchid Road</contact:street>
|
<street>Orchid Road</street>
|
||||||
<contact:city>Paris</contact:city>
|
<city>Paris</city>
|
||||||
<contact:sp>Paris</contact:sp>
|
<sp>Paris</sp>
|
||||||
<contact:pc>392374</contact:pc>
|
<pc>392374</pc>
|
||||||
<contact:cc>FR</contact:cc>
|
<cc>FR</cc>
|
||||||
</contact:addr>
|
</addr>
|
||||||
</contact:postalInfo>
|
</postalInfo>
|
||||||
<contact:voice x="123">+33.47237942</contact:voice>
|
<voice x="123">+33.47237942</voice>
|
||||||
<contact:fax x="677">+33.86698799</contact:fax>
|
<fax x="677">+33.86698799</fax>
|
||||||
<contact:email>contact@eppdev.net</contact:email>
|
<email>contact@eppdev.net</email>
|
||||||
<contact:authInfo>
|
<authInfo>
|
||||||
<contact:pw>eppdev-387323</contact:pw>
|
<pw>eppdev-387323</pw>
|
||||||
</contact:authInfo>
|
</authInfo>
|
||||||
</contact:create>
|
</create>
|
||||||
</create>
|
</create>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<delete>
|
<delete>
|
||||||
<contact:delete xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
|
<delete xmlns="urn:ietf:params:xml:ns:contact-1.0">
|
||||||
<contact:id>eppdev-contact-3</contact:id>
|
<id>eppdev-contact-3</id>
|
||||||
</contact:delete>
|
</delete>
|
||||||
</delete>
|
</delete>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<info>
|
<info>
|
||||||
<contact:info xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
|
<info xmlns="urn:ietf:params:xml:ns:contact-1.0">
|
||||||
<contact:id>eppdev-contact-3</contact:id>
|
<id>eppdev-contact-3</id>
|
||||||
<contact:authInfo>
|
<authInfo>
|
||||||
<contact:pw>eppdev-387323</contact:pw>
|
<pw>eppdev-387323</pw>
|
||||||
</contact:authInfo>
|
</authInfo>
|
||||||
</contact:info>
|
</info>
|
||||||
</info>
|
</info>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -2,35 +2,35 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<update>
|
<update>
|
||||||
<contact:update xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
|
<update xmlns="urn:ietf:params:xml:ns:contact-1.0">
|
||||||
<contact:id>eppdev-contact-3</contact:id>
|
<id>eppdev-contact-3</id>
|
||||||
<contact:add>
|
<add>
|
||||||
<contact:status s="clientTransferProhibited"/>
|
<status s="clientTransferProhibited"></status>
|
||||||
</contact:add>
|
</add>
|
||||||
<contact:rem>
|
<rem>
|
||||||
<contact:status s="clientDeleteProhibited"/>
|
<status s="clientDeleteProhibited"></status>
|
||||||
</contact:rem>
|
</rem>
|
||||||
<contact:chg>
|
<chg>
|
||||||
<contact:postalInfo type="loc">
|
<postalInfo type="loc">
|
||||||
<contact:name>John Doe</contact:name>
|
<name>John Doe</name>
|
||||||
<contact:org>Acme Widgets</contact:org>
|
<org>Acme Widgets</org>
|
||||||
<contact:addr>
|
<addr>
|
||||||
<contact:street>58</contact:street>
|
<street>58</street>
|
||||||
<contact:street>Orchid Road</contact:street>
|
<street>Orchid Road</street>
|
||||||
<contact:city>Paris</contact:city>
|
<city>Paris</city>
|
||||||
<contact:sp>Paris</contact:sp>
|
<sp>Paris</sp>
|
||||||
<contact:pc>392374</contact:pc>
|
<pc>392374</pc>
|
||||||
<contact:cc>FR</contact:cc>
|
<cc>FR</cc>
|
||||||
</contact:addr>
|
</addr>
|
||||||
</contact:postalInfo>
|
</postalInfo>
|
||||||
<contact:voice>+33.47237942</contact:voice>
|
<voice>+33.47237942</voice>
|
||||||
<contact:email>newemail@eppdev.net</contact:email>
|
<email>newemail@eppdev.net</email>
|
||||||
<contact:authInfo>
|
<authInfo>
|
||||||
<contact:pw>eppdev-387323</contact:pw>
|
<pw>eppdev-387323</pw>
|
||||||
</contact:authInfo>
|
</authInfo>
|
||||||
</contact:chg>
|
</chg>
|
||||||
</contact:update>
|
</update>
|
||||||
</update>
|
</update>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<check>
|
<check>
|
||||||
<domain:check xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
<check xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||||
<domain:name>eppdev.com</domain:name>
|
<name>eppdev.com</name>
|
||||||
<domain:name>eppdev.net</domain:name>
|
<name>eppdev.net</name>
|
||||||
</domain:check>
|
</check>
|
||||||
</check>
|
</check>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -2,18 +2,18 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<create>
|
<create>
|
||||||
<domain:create xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
<create xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||||
<domain:name>eppdev-1.com</domain:name>
|
<name>eppdev-1.com</name>
|
||||||
<domain:period unit="y">1</domain:period>
|
<period unit="y">1</period>
|
||||||
<domain:registrant>eppdev-contact-3</domain:registrant>
|
<registrant>eppdev-contact-3</registrant>
|
||||||
<domain:contact type="admin">eppdev-contact-3</domain:contact>
|
<contact type="admin">eppdev-contact-3</contact>
|
||||||
<domain:contact type="tech">eppdev-contact-3</domain:contact>
|
<contact type="tech">eppdev-contact-3</contact>
|
||||||
<domain:contact type="billing">eppdev-contact-3</domain:contact>
|
<contact type="billing">eppdev-contact-3</contact>
|
||||||
<domain:authInfo>
|
<authInfo>
|
||||||
<domain:pw>epP4uthd#v</domain:pw>
|
<pw>epP4uthd#v</pw>
|
||||||
</domain:authInfo>
|
</authInfo>
|
||||||
</domain:create>
|
</create>
|
||||||
</create>
|
</create>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -2,28 +2,28 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<create>
|
<create>
|
||||||
<domain:create xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
<create xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||||
<domain:name>eppdev-2.com</domain:name>
|
<name>eppdev-2.com</name>
|
||||||
<domain:period unit="y">1</domain:period>
|
<period unit="y">1</period>
|
||||||
<domain:ns>
|
<ns>
|
||||||
<domain:hostAttr>
|
<hostAttr>
|
||||||
<domain:hostName>ns1.eppdev-1.com</domain:hostName>
|
<hostName>ns1.eppdev-1.com</hostName>
|
||||||
</domain:hostAttr>
|
</hostAttr>
|
||||||
<domain:hostAttr>
|
<hostAttr>
|
||||||
<domain:hostName>ns2.eppdev-1.com</domain:hostName>
|
<hostName>ns2.eppdev-1.com</hostName>
|
||||||
<domain:hostAddr ip="v4">177.232.12.58</domain:hostAddr>
|
<hostAddr ip="v4">177.232.12.58</hostAddr>
|
||||||
<domain:hostAddr ip="v6">2404:6800:4001:801::200e</domain:hostAddr>
|
<hostAddr ip="v6">2404:6800:4001:801::200e</hostAddr>
|
||||||
</domain:hostAttr>
|
</hostAttr>
|
||||||
</domain:ns>
|
</ns>
|
||||||
<domain:registrant>eppdev-contact-3</domain:registrant>
|
<registrant>eppdev-contact-3</registrant>
|
||||||
<domain:contact type="admin">eppdev-contact-3</domain:contact>
|
<contact type="admin">eppdev-contact-3</contact>
|
||||||
<domain:contact type="tech">eppdev-contact-3</domain:contact>
|
<contact type="tech">eppdev-contact-3</contact>
|
||||||
<domain:contact type="billing">eppdev-contact-3</domain:contact>
|
<contact type="billing">eppdev-contact-3</contact>
|
||||||
<domain:authInfo>
|
<authInfo>
|
||||||
<domain:pw>epP4uthd#v</domain:pw>
|
<pw>epP4uthd#v</pw>
|
||||||
</domain:authInfo>
|
</authInfo>
|
||||||
</domain:create>
|
</create>
|
||||||
</create>
|
</create>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -2,22 +2,22 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<create>
|
<create>
|
||||||
<domain:create xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
<create xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||||
<domain:name>eppdev-1.com</domain:name>
|
<name>eppdev-1.com</name>
|
||||||
<domain:period unit="y">1</domain:period>
|
<period unit="y">1</period>
|
||||||
<domain:ns>
|
<ns>
|
||||||
<domain:hostObj>ns1.test.com</domain:hostObj>
|
<hostObj>ns1.test.com</hostObj>
|
||||||
<domain:hostObj>ns2.test.com</domain:hostObj>
|
<hostObj>ns2.test.com</hostObj>
|
||||||
</domain:ns>
|
</ns>
|
||||||
<domain:registrant>eppdev-contact-3</domain:registrant>
|
<registrant>eppdev-contact-3</registrant>
|
||||||
<domain:contact type="admin">eppdev-contact-3</domain:contact>
|
<contact type="admin">eppdev-contact-3</contact>
|
||||||
<domain:contact type="tech">eppdev-contact-3</domain:contact>
|
<contact type="tech">eppdev-contact-3</contact>
|
||||||
<domain:contact type="billing">eppdev-contact-3</domain:contact>
|
<contact type="billing">eppdev-contact-3</contact>
|
||||||
<domain:authInfo>
|
<authInfo>
|
||||||
<domain:pw>epP4uthd#v</domain:pw>
|
<pw>epP4uthd#v</pw>
|
||||||
</domain:authInfo>
|
</authInfo>
|
||||||
</domain:create>
|
</create>
|
||||||
</create>
|
</create>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<delete>
|
<delete>
|
||||||
<domain:delete xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
<delete xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||||
<domain:name>eppdev.com</domain:name>
|
<name>eppdev.com</name>
|
||||||
</domain:delete>
|
</delete>
|
||||||
</delete>
|
</delete>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<info>
|
<info>
|
||||||
<domain:info xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
<info xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||||
<domain:name hosts="all">eppdev.com</domain:name>
|
<name hosts="all">eppdev.com</name>
|
||||||
<domain:authInfo>
|
<authInfo>
|
||||||
<domain:pw>2fooBAR</domain:pw>
|
<pw>2fooBAR</pw>
|
||||||
</domain:authInfo>
|
</authInfo>
|
||||||
</domain:info>
|
</info>
|
||||||
</info>
|
</info>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<renew>
|
<renew>
|
||||||
<domain:renew xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
<renew xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||||
<domain:name>eppdev.com</domain:name>
|
<name>eppdev.com</name>
|
||||||
<domain:curExpDate>2022-07-23</domain:curExpDate>
|
<curExpDate>2022-07-23</curExpDate>
|
||||||
<domain:period unit="y">1</domain:period>
|
<period unit="y">1</period>
|
||||||
</domain:renew>
|
</renew>
|
||||||
</renew>
|
</renew>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<transfer op="approve">
|
<transfer op="approve">
|
||||||
<domain:transfer xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
<transfer xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||||
<domain:name>testing.com</domain:name>
|
<name>testing.com</name>
|
||||||
</domain:transfer>
|
</transfer>
|
||||||
</transfer>
|
</transfer>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<transfer op="cancel">
|
<transfer op="cancel">
|
||||||
<domain:transfer xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
<transfer xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||||
<domain:name>testing.com</domain:name>
|
<name>testing.com</name>
|
||||||
</domain:transfer>
|
</transfer>
|
||||||
</transfer>
|
</transfer>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<transfer op="query">
|
<transfer op="query">
|
||||||
<domain:transfer xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
<transfer xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||||
<domain:name>testing.com</domain:name>
|
<name>testing.com</name>
|
||||||
<domain:authInfo>
|
<authInfo>
|
||||||
<domain:pw>epP4uthd#v</domain:pw>
|
<pw>epP4uthd#v</pw>
|
||||||
</domain:authInfo>
|
</authInfo>
|
||||||
</domain:transfer>
|
</transfer>
|
||||||
</transfer>
|
</transfer>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<transfer op="reject">
|
<transfer op="reject">
|
||||||
<domain:transfer xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
<transfer xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||||
<domain:name>testing.com</domain:name>
|
<name>testing.com</name>
|
||||||
</domain:transfer>
|
</transfer>
|
||||||
</transfer>
|
</transfer>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<transfer op="request">
|
<transfer op="request">
|
||||||
<domain:transfer xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
<transfer xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||||
<domain:name>testing.com</domain:name>
|
<name>testing.com</name>
|
||||||
<domain:period unit="y">1</domain:period>
|
<period unit="y">1</period>
|
||||||
<domain:authInfo>
|
<authInfo>
|
||||||
<domain:pw>epP4uthd#v</domain:pw>
|
<pw>epP4uthd#v</pw>
|
||||||
</domain:authInfo>
|
</authInfo>
|
||||||
</domain:transfer>
|
</transfer>
|
||||||
</transfer>
|
</transfer>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -2,21 +2,21 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<update>
|
<update>
|
||||||
<domain:update xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
<update xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||||
<domain:name>eppdev.com</domain:name>
|
<name>eppdev.com</name>
|
||||||
<domain:add>
|
<add>
|
||||||
<domain:status s="clientDeleteProhibited"/>
|
<status s="clientDeleteProhibited"></status>
|
||||||
</domain:add>
|
</add>
|
||||||
<domain:rem>
|
<rem>
|
||||||
<domain:contact type="billing">eppdev-contact-2</domain:contact>
|
<contact type="billing">eppdev-contact-2</contact>
|
||||||
</domain:rem>
|
</rem>
|
||||||
<domain:chg>
|
<chg>
|
||||||
<domain:authInfo>
|
<authInfo>
|
||||||
<domain:pw>epP5uthd#v</domain:pw>
|
<pw>epP5uthd#v</pw>
|
||||||
</domain:authInfo>
|
</authInfo>
|
||||||
</domain:chg>
|
</chg>
|
||||||
</domain:update>
|
</update>
|
||||||
</update>
|
</update>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -2,16 +2,16 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<update>
|
<update>
|
||||||
<domain:update xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
<update xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||||
<domain:name>eppdev.com</domain:name>
|
<name>eppdev.com</name>
|
||||||
<domain:chg/>
|
<chg></chg>
|
||||||
</domain:update>
|
</update>
|
||||||
</update>
|
</update>
|
||||||
<extension>
|
<extension>
|
||||||
<sync:update xmlns:sync="http://www.verisign.com/epp/sync-1.0">
|
<update xmlns="http://www.verisign.com/epp/sync-1.0">
|
||||||
<sync:expMonthDay>--05-31</sync:expMonthDay>
|
<expMonthDay>--05-31</expMonthDay>
|
||||||
</sync:update>
|
</update>
|
||||||
</extension>
|
</extension>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<update>
|
<update>
|
||||||
<domain:update xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
<update xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||||
<domain:name>eppdev.com</domain:name>
|
<name>eppdev.com</name>
|
||||||
<domain:chg/>
|
<chg></chg>
|
||||||
</domain:update>
|
</update>
|
||||||
</update>
|
</update>
|
||||||
<extension>
|
<extension>
|
||||||
<sync:update xmlns:sync="http://www.verisign.com/epp/sync-1.0">
|
<update xmlns="http://www.verisign.com/epp/sync-1.0">
|
||||||
<sync:expMonthDay>--05-31</sync:expMonthDay>
|
<expMonthDay>--05-31</expMonthDay>
|
||||||
</sync:update>
|
</update>
|
||||||
<namestoreExt:namestoreExt xmlns:namestoreExt="http://www.verisign-grs.com/epp/namestoreExt-1.1">
|
<namestoreExt xmlns="http://www.verisign-grs.com/epp/namestoreExt-1.1">
|
||||||
<namestoreExt:subProduct>com</namestoreExt:subProduct>
|
<subProduct>com</subProduct>
|
||||||
</namestoreExt:namestoreExt>
|
</namestoreExt>
|
||||||
</extension>
|
</extension>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -2,17 +2,17 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<check>
|
<check>
|
||||||
<domain:check xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
<check xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||||
<domain:name>example1.com</domain:name>
|
<name>example1.com</name>
|
||||||
<domain:name>example2.com</domain:name>
|
<name>example2.com</name>
|
||||||
<domain:name>example3.com</domain:name>
|
<name>example3.com</name>
|
||||||
</domain:check>
|
</check>
|
||||||
</check>
|
</check>
|
||||||
<extension>
|
<extension>
|
||||||
<namestoreExt:namestoreExt xmlns:namestoreExt="http://www.verisign-grs.com/epp/namestoreExt-1.1">
|
<namestoreExt xmlns="http://www.verisign-grs.com/epp/namestoreExt-1.1">
|
||||||
<namestoreExt:subProduct>com</namestoreExt:subProduct>
|
<subProduct>com</subProduct>
|
||||||
</namestoreExt:namestoreExt>
|
</namestoreExt>
|
||||||
</extension>
|
</extension>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -2,27 +2,27 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<update>
|
<update>
|
||||||
<domain:update xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
<update xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||||
<domain:name>eppdev.com</domain:name>
|
<name>eppdev.com</name>
|
||||||
<domain:chg/>
|
<chg></chg>
|
||||||
</domain:update>
|
</update>
|
||||||
</update>
|
</update>
|
||||||
<extension>
|
<extension>
|
||||||
<rgp:update xmlns:rgp="urn:ietf:params:xml:ns:rgp-1.0">
|
<update xmlns="urn:ietf:params:xml:ns:rgp-1.0">
|
||||||
<rgp:restore op="report">
|
<restore op="report">
|
||||||
<rgp:report>
|
<report>
|
||||||
<rgp:preData>Pre-delete registration data goes here. Both XML and free text are allowed.</rgp:preData>
|
<preData>Pre-delete registration data goes here. Both XML and free text are allowed.</preData>
|
||||||
<rgp:postData>Post-restore registration data goes here. Both XML and free text are allowed.</rgp:postData>
|
<postData>Post-restore registration data goes here. Both XML and free text are allowed.</postData>
|
||||||
<rgp:delTime>2021-07-10T22:00:00Z</rgp:delTime>
|
<delTime>2021-07-10T22:00:00Z</delTime>
|
||||||
<rgp:resTime>2021-07-20T22:00:00Z</rgp:resTime>
|
<resTime>2021-07-20T22:00:00Z</resTime>
|
||||||
<rgp:resReason>Registrant error.</rgp:resReason>
|
<resReason>Registrant error.</resReason>
|
||||||
<rgp:statement>This registrar has not restored the Registered Name in order to assume the rights to use or sell the Registered Name for itself or for any third party.</rgp:statement>
|
<statement>This registrar has not restored the Registered Name in order to assume the rights to use or sell the Registered Name for itself or for any third party.</statement>
|
||||||
<rgp:statement>The information in this report is true to best of this registrar's knowledge, and this registrar acknowledges that intentionally supplying false information in this report shall constitute an incurable material breach of the Registry-Registrar Agreement.</rgp:statement>
|
<statement>The information in this report is true to best of this registrar's knowledge, and this registrar acknowledges that intentionally supplying false information in this report shall constitute an incurable material breach of the Registry-Registrar Agreement.</statement>
|
||||||
<rgp:other>Supporting information goes here.</rgp:other>
|
<other>Supporting information goes here.</other>
|
||||||
</rgp:report>
|
</report>
|
||||||
</rgp:restore>
|
</restore>
|
||||||
</rgp:update>
|
</update>
|
||||||
</extension>
|
</extension>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -2,16 +2,16 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<update>
|
<update>
|
||||||
<domain:update xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
<update xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||||
<domain:name>eppdev.com</domain:name>
|
<name>eppdev.com</name>
|
||||||
<domain:chg/>
|
<chg></chg>
|
||||||
</domain:update>
|
</update>
|
||||||
</update>
|
</update>
|
||||||
<extension>
|
<extension>
|
||||||
<rgp:update xmlns:rgp="urn:ietf:params:xml:ns:rgp-1.0">
|
<update xmlns="urn:ietf:params:xml:ns:rgp-1.0">
|
||||||
<rgp:restore op="request"/>
|
<restore op="request"></restore>
|
||||||
</rgp:update>
|
</update>
|
||||||
</extension>
|
</extension>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<hello/>
|
<hello />
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<check>
|
<check>
|
||||||
<host:check xmlns:host="urn:ietf:params:xml:ns:host-1.0">
|
<check xmlns="urn:ietf:params:xml:ns:host-1.0">
|
||||||
<host:name>ns1.eppdev-1.com</host:name>
|
<name>ns1.eppdev-1.com</name>
|
||||||
<host:name>host1.eppdev-1.com</host:name>
|
<name>host1.eppdev-1.com</name>
|
||||||
</host:check>
|
</check>
|
||||||
</check>
|
</check>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<create>
|
<create>
|
||||||
<host:create xmlns:host="urn:ietf:params:xml:ns:host-1.0">
|
<create xmlns="urn:ietf:params:xml:ns:host-1.0">
|
||||||
<host:name>host1.eppdev-1.com</host:name>
|
<name>host1.eppdev-1.com</name>
|
||||||
<host:addr ip="v4">29.245.122.14</host:addr>
|
<addr ip="v4">29.245.122.14</addr>
|
||||||
<host:addr ip="v6">2404:6800:4001:801::200e</host:addr>
|
<addr ip="v6">2404:6800:4001:801::200e</addr>
|
||||||
</host:create>
|
</create>
|
||||||
</create>
|
</create>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<delete>
|
<delete>
|
||||||
<host:delete xmlns:host="urn:ietf:params:xml:ns:host-1.0">
|
<delete xmlns="urn:ietf:params:xml:ns:host-1.0">
|
||||||
<host:name>ns1.eppdev-1.com</host:name>
|
<name>ns1.eppdev-1.com</name>
|
||||||
</host:delete>
|
</delete>
|
||||||
</delete>
|
</delete>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<info>
|
<info>
|
||||||
<host:info xmlns:host="urn:ietf:params:xml:ns:host-1.0">
|
<info xmlns="urn:ietf:params:xml:ns:host-1.0">
|
||||||
<host:name>ns1.eppdev-1.com</host:name>
|
<name>ns1.eppdev-1.com</name>
|
||||||
</host:info>
|
</info>
|
||||||
</info>
|
</info>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<update>
|
<update>
|
||||||
<host:update xmlns:host="urn:ietf:params:xml:ns:host-1.0">
|
<update xmlns="urn:ietf:params:xml:ns:host-1.0">
|
||||||
<host:name>host1.eppdev-1.com</host:name>
|
<name>host1.eppdev-1.com</name>
|
||||||
<host:add>
|
<add>
|
||||||
<host:addr ip="v6">2404:6800:4001:801::200e</host:addr>
|
<addr ip="v6">2404:6800:4001:801::200e</addr>
|
||||||
</host:add>
|
</add>
|
||||||
<host:rem>
|
<rem>
|
||||||
<host:status s="clientDeleteProhibited"/>
|
<status s="clientDeleteProhibited"></status>
|
||||||
</host:rem>
|
</rem>
|
||||||
<host:chg>
|
<chg>
|
||||||
<host:name>host2.eppdev-1.com</host:name>
|
<name>host2.eppdev-1.com</name>
|
||||||
</host:chg>
|
</chg>
|
||||||
</host:update>
|
</update>
|
||||||
</update>
|
</update>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<logout/>
|
<logout />
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<poll op="ack" msgID="12345"/>
|
<poll op="ack" msgID="12345"></poll>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||||
<command>
|
<command>
|
||||||
<poll op="req"/>
|
<poll op="req"></poll>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
</command>
|
</command>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -24,4 +24,4 @@
|
||||||
<svTRID>RO-6879-1627224678242975</svTRID>
|
<svTRID>RO-6879-1627224678242975</svTRID>
|
||||||
</trID>
|
</trID>
|
||||||
</response>
|
</response>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -51,4 +51,4 @@
|
||||||
</expiry>
|
</expiry>
|
||||||
</dcp>
|
</dcp>
|
||||||
</greeting>
|
</greeting>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -19,4 +19,4 @@
|
||||||
<svTRID>RO-6879-1627224678242975</svTRID>
|
<svTRID>RO-6879-1627224678242975</svTRID>
|
||||||
</trID>
|
</trID>
|
||||||
</response>
|
</response>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
|
@ -21,18 +21,9 @@
|
||||||
<host:upDate>2021-12-01T22:40:48Z</host:upDate>
|
<host:upDate>2021-12-01T22:40:48Z</host:upDate>
|
||||||
</host:infData>
|
</host:infData>
|
||||||
</resData>
|
</resData>
|
||||||
<extension>
|
|
||||||
<changePoll:changeData state="before" xmlns:changePoll="urn:ietf:params:xml:ns:changePoll-1.0">
|
|
||||||
<changePoll:operation op="purge">delete</changePoll:operation>
|
|
||||||
<changePoll:date>2022-01-02T11:30:45Z</changePoll:date>
|
|
||||||
<changePoll:svTRID>1234</changePoll:svTRID>
|
|
||||||
<changePoll:who>regy_batch</changePoll:who>
|
|
||||||
<changePoll:reason>Unused objects policy</changePoll:reason>
|
|
||||||
</changePoll:changeData>
|
|
||||||
</extension>
|
|
||||||
<trID>
|
<trID>
|
||||||
<clTRID>cltrid:1626454866</clTRID>
|
<clTRID>cltrid:1626454866</clTRID>
|
||||||
<svTRID>RO-6879-1627224678242975</svTRID>
|
<svTRID>RO-6879-1627224678242975</svTRID>
|
||||||
</trID>
|
</trID>
|
||||||
</response>
|
</response>
|
||||||
</epp>
|
</epp>
|
||||||
|
|
Loading…
Reference in New Issue