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"
|
||||
celes = "2.1"
|
||||
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"] }
|
||||
tokio = { version = "1.0", features = ["io-util", "net", "time"] }
|
||||
tokio-rustls = { version = "0.23", optional = true }
|
||||
|
@ -26,3 +26,4 @@ regex = "1.5"
|
|||
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
|
||||
tokio-test = "0.4"
|
||||
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;
|
||||
use crate::connection::{self, EppConnection};
|
||||
use crate::error::Error;
|
||||
use crate::hello::{Greeting, GreetingDocument, HelloDocument};
|
||||
use crate::request::{Command, CommandDocument, Extension, Transaction};
|
||||
use crate::response::{Response, ResponseDocument, ResponseStatus};
|
||||
use crate::hello::{Greeting, Hello};
|
||||
use crate::request::{Command, CommandWrapper, Extension, Transaction};
|
||||
use crate::response::{Response, ResponseStatus};
|
||||
use crate::xml;
|
||||
|
||||
/// 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::time::Duration;
|
||||
/// #
|
||||
/// use epp_client::EppClient;
|
||||
/// use epp_client::domain::DomainCheck;
|
||||
/// use epp_client::common::NoExtension;
|
||||
/// use instant_epp::EppClient;
|
||||
/// use instant_epp::domain::DomainCheck;
|
||||
/// use instant_epp::common::NoExtension;
|
||||
///
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() {
|
||||
|
@ -55,9 +55,12 @@ use crate::xml;
|
|||
/// // Execute an EPP Command against the registry with distinct request and response objects
|
||||
/// let domain_check = DomainCheck { domains: &["eppdev.com", "eppdev.net"] };
|
||||
/// let response = client.transact(&domain_check, "transaction-id").await.unwrap();
|
||||
/// response.res_data.unwrap().list
|
||||
/// response
|
||||
/// .res_data()
|
||||
/// .unwrap()
|
||||
/// .list
|
||||
/// .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`
|
||||
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);
|
||||
let response = self.connection.transact(&xml)?.await?;
|
||||
debug!("{}: greeting: {}", self.connection.registry, &response);
|
||||
|
||||
Ok(xml::deserialize::<GreetingDocument>(&response)?.data)
|
||||
xml::deserialize::<Greeting>(&response)
|
||||
}
|
||||
|
||||
pub async fn transact<'c, 'e, Cmd, Ext>(
|
||||
|
@ -122,29 +125,28 @@ impl<C: Connector> EppClient<C> {
|
|||
Ext: Extension + 'e,
|
||||
{
|
||||
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)?;
|
||||
|
||||
debug!("{}: request: {}", self.connection.registry, &xml);
|
||||
let response = self.connection.transact(&xml)?.await?;
|
||||
debug!("{}: response: {}", self.connection.registry, &response);
|
||||
|
||||
let rsp =
|
||||
match xml::deserialize::<ResponseDocument<Cmd::Response, Ext::Response>>(&response) {
|
||||
Ok(rsp) => rsp,
|
||||
Err(e) => {
|
||||
error!(%response, "failed to deserialize response for transaction: {e}");
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
let rsp = match xml::deserialize::<Response<Cmd::Response, Ext::Response>>(&response) {
|
||||
Ok(rsp) => rsp,
|
||||
Err(e) => {
|
||||
error!(%response, "failed to deserialize response for transaction: {e}");
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
if rsp.data.result.code.is_success() {
|
||||
return Ok(rsp.data);
|
||||
if rsp.result.code.is_success() {
|
||||
return Ok(rsp);
|
||||
}
|
||||
|
||||
let err = crate::error::Error::Command(Box::new(ResponseStatus {
|
||||
result: rsp.data.result,
|
||||
tr_ids: rsp.data.tr_ids,
|
||||
result: rsp.result,
|
||||
tr_ids: rsp.tr_ids,
|
||||
}));
|
||||
|
||||
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`
|
||||
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> {
|
||||
|
|
205
src/common.rs
205
src/common.rs
|
@ -1,135 +1,45 @@
|
|||
//! Common data types included in EPP Requests and Responses
|
||||
|
||||
use std::ops::Deref;
|
||||
use std::{borrow::Cow, fmt::Display, net::IpAddr};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use serde::ser::SerializeSeq;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use instant_xml::{FromXml, ToXml};
|
||||
|
||||
use crate::request::Extension;
|
||||
|
||||
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
|
||||
/// 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.
|
||||
#[derive(Debug, Eq, PartialEq, ToXml)]
|
||||
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 {
|
||||
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
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
||||
#[serde(rename = "options")]
|
||||
#[derive(Debug, Eq, FromXml, PartialEq, ToXml)]
|
||||
#[xml(rename = "options", ns(EPP_XMLNS))]
|
||||
pub struct Options<'a> {
|
||||
/// The EPP version being used
|
||||
pub version: StringValue<'a>,
|
||||
pub version: Cow<'a, str>,
|
||||
/// The language that will be used during EPP transactions
|
||||
pub lang: StringValue<'a>,
|
||||
pub lang: Cow<'a, str>,
|
||||
}
|
||||
|
||||
impl<'a> Options<'a> {
|
||||
|
@ -143,73 +53,26 @@ impl<'a> Options<'a> {
|
|||
}
|
||||
|
||||
/// The <svcExtension> type in EPP XML
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
||||
#[serde(rename = "svcExtension")]
|
||||
#[derive(Debug, Eq, FromXml, PartialEq, ToXml)]
|
||||
#[xml(rename = "svcExtension", ns(EPP_XMLNS))]
|
||||
pub struct ServiceExtension<'a> {
|
||||
/// The service extension URIs being represented by <extURI> in EPP XML
|
||||
#[serde(rename = "extURI")]
|
||||
pub ext_uris: Option<Vec<StringValue<'a>>>,
|
||||
#[xml(rename = "extURI")]
|
||||
pub ext_uris: Option<Vec<Cow<'a, str>>>,
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
/// The service URIs being used by this EPP session represented by <objURI> in EPP XML
|
||||
#[serde(rename = "objURI")]
|
||||
pub obj_uris: Vec<StringValue<'a>>,
|
||||
/// The <svcExtention> being used in this EPP session
|
||||
#[serde(rename = "svcExtension")]
|
||||
#[xml(rename = "objURI")]
|
||||
pub obj_uris: Vec<Cow<'a, str>>,
|
||||
// The <svcExtension> being used in this EPP session
|
||||
#[xml(rename = "svcExtension")]
|
||||
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.
|
||||
///
|
||||
/// 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::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::common::StringValue;
|
||||
use instant_xml::{display_to_xml, from_xml_str, FromXml, ToXml};
|
||||
|
||||
pub mod check;
|
||||
pub use check::ContactCheck;
|
||||
|
@ -22,9 +21,39 @@ pub use update::ContactUpdate;
|
|||
|
||||
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);
|
||||
|
||||
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 {
|
||||
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
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[derive(Debug, Clone, FromXml, ToXml)]
|
||||
#[xml(rename = "authInfo", ns(XMLNS))]
|
||||
pub struct ContactAuthInfo<'a> {
|
||||
/// The <pw> tag under <authInfo>
|
||||
#[serde(rename = "contact:pw", alias = "pw")]
|
||||
pub password: StringValue<'a>,
|
||||
#[xml(rename = "pw")]
|
||||
pub password: Cow<'a, str>,
|
||||
}
|
||||
|
||||
impl<'a> ContactAuthInfo<'a> {
|
||||
|
@ -58,18 +88,46 @@ impl<'a> ContactAuthInfo<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// The data for <voice> and <fax> types on domain transactions
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct Phone<'a> {
|
||||
/// The inner text on the <voice> and <fax> tags
|
||||
#[serde(rename = "$value")]
|
||||
pub number: Cow<'a, str>,
|
||||
/// The data for <voice> types on domain transactions
|
||||
#[derive(Debug, Clone, FromXml, ToXml)]
|
||||
#[xml(rename = "voice", ns(XMLNS))]
|
||||
pub struct Voice<'a> {
|
||||
/// The value of the 'x' attr on <voice> and <fax> tags
|
||||
#[serde(rename = "x")]
|
||||
#[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> 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
|
||||
pub fn new(number: &'a str) -> Self {
|
||||
Self {
|
||||
|
@ -85,22 +143,21 @@ impl<'a> Phone<'a> {
|
|||
}
|
||||
|
||||
/// 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> {
|
||||
/// The <street> tags under <addr>
|
||||
#[serde(rename = "contact:street", alias = "street")]
|
||||
pub street: Vec<StringValue<'a>>,
|
||||
pub street: Vec<Cow<'a, str>>,
|
||||
/// The <city> tag under <addr>
|
||||
#[serde(rename = "contact:city", alias = "city")]
|
||||
pub city: StringValue<'a>,
|
||||
pub city: Cow<'a, str>,
|
||||
/// The <sp> tag under <addr>
|
||||
#[serde(rename = "contact:sp", alias = "sp")]
|
||||
pub province: StringValue<'a>,
|
||||
#[xml(rename = "sp")]
|
||||
pub province: Cow<'a, str>,
|
||||
/// The <pc> tag under <addr>
|
||||
#[serde(rename = "contact:pc", alias = "pc")]
|
||||
pub postal_code: StringValue<'a>,
|
||||
#[xml(rename = "pc")]
|
||||
pub postal_code: Cow<'a, str>,
|
||||
/// The <cc> tag under <addr>
|
||||
#[serde(rename = "contact:cc", alias = "cc")]
|
||||
#[xml(rename = "cc")]
|
||||
pub country: Country,
|
||||
}
|
||||
|
||||
|
@ -126,35 +183,43 @@ impl<'a> Address<'a> {
|
|||
}
|
||||
|
||||
/// 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> {
|
||||
/// The 'type' attr on <postalInfo>
|
||||
#[serde(rename = "type")]
|
||||
pub info_type: String,
|
||||
#[xml(rename = "type", attribute)]
|
||||
pub info_type: Cow<'a, str>,
|
||||
/// The <name> tag under <postalInfo>
|
||||
#[serde(rename = "contact:name", alias = "name")]
|
||||
pub name: StringValue<'a>,
|
||||
pub name: Cow<'a, str>,
|
||||
/// The <org> tag under <postalInfo>
|
||||
#[serde(rename = "contact:org", alias = "org")]
|
||||
pub organization: StringValue<'a>,
|
||||
#[xml(rename = "org")]
|
||||
pub organization: Cow<'a, str>,
|
||||
/// The <addr> tag under <postalInfo>
|
||||
#[serde(rename = "contact:addr", alias = "addr")]
|
||||
pub address: Address<'a>,
|
||||
}
|
||||
|
||||
impl<'a> PostalInfo<'a> {
|
||||
/// Creates a new PostalInfo instance
|
||||
pub fn new(
|
||||
info_type: &str,
|
||||
info_type: &'a str,
|
||||
name: &'a str,
|
||||
organization: &'a str,
|
||||
address: Address<'a>,
|
||||
) -> Self {
|
||||
Self {
|
||||
info_type: info_type.to_string(),
|
||||
info_type: info_type.into(),
|
||||
name: name.into(),
|
||||
organization: organization.into(),
|
||||
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 crate::common::{CheckResponse, NoExtension, StringValue};
|
||||
use crate::common::{NoExtension, EPP_XMLNS};
|
||||
use crate::request::{Command, Transaction};
|
||||
use serde::Serialize;
|
||||
|
||||
impl<'a> Transaction<NoExtension> for ContactCheck<'a> {}
|
||||
|
||||
impl<'a> Command for ContactCheck<'a> {
|
||||
type Response = CheckResponse;
|
||||
type Response = CheckData;
|
||||
const COMMAND: &'static str = "check";
|
||||
}
|
||||
|
||||
// Request
|
||||
|
||||
/// Type that represents the <check> command for contact transactions
|
||||
#[derive(Serialize, Debug)]
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "check", ns(XMLNS))]
|
||||
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
|
||||
#[serde(rename = "contact:id")]
|
||||
contact_ids: Vec<StringValue<'a>>,
|
||||
id: &'a [&'a str],
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
struct SerializeContactCheck<'a> {
|
||||
/// The <check> tag for the contact check command
|
||||
#[serde(rename = "contact:check")]
|
||||
list: ContactList<'a>,
|
||||
}
|
||||
|
||||
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(),
|
||||
},
|
||||
}
|
||||
}
|
||||
fn serialize_contacts<W: fmt::Write + ?Sized>(
|
||||
ids: &[&str],
|
||||
serializer: &mut Serializer<W>,
|
||||
) -> Result<(), instant_xml::Error> {
|
||||
ContactList { id: ids }.serialize(None, serializer)
|
||||
}
|
||||
|
||||
/// The EPP `check` command for contacts
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
#[serde(into = "SerializeContactCheck")]
|
||||
#[derive(Clone, Debug, ToXml)]
|
||||
#[xml(rename = "check", ns(EPP_XMLNS))]
|
||||
pub struct ContactCheck<'a> {
|
||||
/// The list of contact IDs to be checked
|
||||
#[xml(serialize_with = "serialize_contacts")]
|
||||
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)]
|
||||
mod tests {
|
||||
use super::ContactCheck;
|
||||
|
@ -72,12 +87,12 @@ mod tests {
|
|||
let results = object.res_data().unwrap();
|
||||
|
||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
||||
assert_eq!(results.list[0].id, "eppdev-contact-1");
|
||||
assert!(!results.list[0].available);
|
||||
assert_eq!(results.list[1].id, "eppdev-contact-2");
|
||||
assert!(results.list[1].available);
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
||||
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||
assert_eq!(results.list[0].inner.id, "eppdev-contact-1");
|
||||
assert!(!results.list[0].inner.available);
|
||||
assert_eq!(results.list[1].inner.id, "eppdev-contact-2");
|
||||
assert!(results.list[1].inner.available);
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,53 +1,45 @@
|
|||
//! Types for EPP contact create request
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use instant_xml::{FromXml, ToXml};
|
||||
|
||||
use super::{ContactAuthInfo, Phone, PostalInfo, XMLNS};
|
||||
use crate::common::{NoExtension, StringValue};
|
||||
use super::{ContactAuthInfo, Fax, PostalInfo, Voice, XMLNS};
|
||||
use crate::common::{NoExtension, EPP_XMLNS};
|
||||
use crate::request::{Command, Transaction};
|
||||
|
||||
impl<'a> Transaction<NoExtension> for ContactCreate<'a> {}
|
||||
|
||||
impl<'a> Command for ContactCreate<'a> {
|
||||
type Response = ContactCreateResponse;
|
||||
type Response = ContactCreateData;
|
||||
const COMMAND: &'static str = "create";
|
||||
}
|
||||
|
||||
// Request
|
||||
|
||||
/// Type for elements under the contact <create> tag
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct Contact<'a> {
|
||||
/// XML namespace for contact commands
|
||||
#[serde(rename = "xmlns:contact")]
|
||||
xmlns: &'a str,
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "create", ns(XMLNS))]
|
||||
pub struct ContactCreateRequest<'a> {
|
||||
/// Contact <id> tag
|
||||
#[serde(rename = "contact:id")]
|
||||
id: StringValue<'a>,
|
||||
id: &'a str,
|
||||
/// Contact <postalInfo> tag
|
||||
#[serde(rename = "contact:postalInfo")]
|
||||
postal_info: PostalInfo<'a>,
|
||||
/// Contact <voice> tag
|
||||
#[serde(rename = "contact:voice")]
|
||||
voice: Phone<'a>,
|
||||
/// Contact <fax> tag,
|
||||
#[serde(rename = "contact:fax")]
|
||||
fax: Option<Phone<'a>>,
|
||||
voice: Voice<'a>,
|
||||
/// Contact <fax> tag,]
|
||||
fax: Option<Fax<'a>>,
|
||||
/// Contact <email> tag
|
||||
#[serde(rename = "contact:email")]
|
||||
email: StringValue<'a>,
|
||||
email: &'a str,
|
||||
/// Contact <authInfo> tag
|
||||
#[serde(rename = "contact:authInfo")]
|
||||
auth_info: ContactAuthInfo<'a>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
/// Type for EPP XML <create> command for contacts
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "create", ns(EPP_XMLNS))]
|
||||
pub struct ContactCreate<'a> {
|
||||
/// Data for <create> command for contact
|
||||
#[serde(rename = "contact:create")]
|
||||
pub contact: Contact<'a>,
|
||||
pub contact: ContactCreateRequest<'a>,
|
||||
}
|
||||
|
||||
impl<'a> ContactCreate<'a> {
|
||||
|
@ -55,24 +47,23 @@ impl<'a> ContactCreate<'a> {
|
|||
id: &'a str,
|
||||
email: &'a str,
|
||||
postal_info: PostalInfo<'a>,
|
||||
voice: Phone<'a>,
|
||||
voice: Voice<'a>,
|
||||
auth_password: &'a str,
|
||||
) -> Self {
|
||||
Self {
|
||||
contact: Contact {
|
||||
xmlns: XMLNS,
|
||||
id: id.into(),
|
||||
contact: ContactCreateRequest {
|
||||
id,
|
||||
postal_info,
|
||||
voice,
|
||||
fax: None,
|
||||
email: email.into(),
|
||||
email,
|
||||
auth_info: ContactAuthInfo::new(auth_password),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
}
|
||||
|
@ -80,28 +71,21 @@ impl<'a> ContactCreate<'a> {
|
|||
// 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 {
|
||||
/// The contact id
|
||||
pub id: StringValue<'static>,
|
||||
#[serde(rename = "crDate")]
|
||||
pub id: String,
|
||||
#[xml(rename = "crDate")]
|
||||
/// The contact creation date
|
||||
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)]
|
||||
mod tests {
|
||||
use chrono::{TimeZone, Utc};
|
||||
|
||||
use super::{ContactCreate, Phone, PostalInfo};
|
||||
use super::{ContactCreate, Fax, PostalInfo, Voice};
|
||||
use crate::contact::Address;
|
||||
use crate::response::ResultCode;
|
||||
use crate::tests::{assert_serialized, response_from_file, CLTRID, SUCCESS_MSG, SVTRID};
|
||||
|
@ -111,9 +95,9 @@ mod tests {
|
|||
let street = &["58", "Orchid Road"];
|
||||
let address = Address::new(street, "Paris", "Paris", "392374", "FR".parse().unwrap());
|
||||
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");
|
||||
let mut fax = Phone::new("+33.86698799");
|
||||
let mut fax = Fax::new("+33.86698799");
|
||||
fax.set_extension("677");
|
||||
|
||||
let mut object = ContactCreate::new(
|
||||
|
@ -134,13 +118,13 @@ mod tests {
|
|||
let results = object.res_data().unwrap();
|
||||
|
||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
||||
assert_eq!(results.create_data.id, "eppdev-contact-4".into());
|
||||
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||
assert_eq!(results.id, "eppdev-contact-4");
|
||||
assert_eq!(
|
||||
results.create_data.created_at,
|
||||
results.created_at,
|
||||
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.server_tr_id, SVTRID.into());
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
//! Types for EPP contact delete request
|
||||
|
||||
use instant_xml::ToXml;
|
||||
|
||||
use super::XMLNS;
|
||||
use crate::common::{NoExtension, StringValue};
|
||||
use crate::common::{NoExtension, EPP_XMLNS};
|
||||
use crate::request::{Command, Transaction};
|
||||
use serde::Serialize;
|
||||
|
||||
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
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct ContactDeleteRequestData<'a> {
|
||||
/// XML namespace for the <delete> command for contacts
|
||||
#[serde(rename = "xmlns:contact")]
|
||||
xmlns: &'a str,
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "delete", ns(XMLNS))]
|
||||
pub struct ContactDeleteRequest<'a> {
|
||||
/// The id of the contact to be deleted
|
||||
#[serde(rename = "contact:id")]
|
||||
id: StringValue<'a>,
|
||||
id: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
/// The <delete> type for the contact delete EPP command
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "delete", ns(EPP_XMLNS))]
|
||||
pub struct ContactDelete<'a> {
|
||||
#[serde(rename = "contact:delete")]
|
||||
/// The data for the <delete> tag for a contact delete command
|
||||
contact: ContactDeleteRequestData<'a>,
|
||||
contact: ContactDeleteRequest<'a>,
|
||||
}
|
||||
|
||||
impl<'a> ContactDelete<'a> {
|
||||
pub fn new(id: &'a str) -> Self {
|
||||
Self {
|
||||
contact: ContactDeleteRequestData {
|
||||
xmlns: XMLNS,
|
||||
id: id.into(),
|
||||
},
|
||||
contact: ContactDeleteRequest { id },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -58,8 +53,8 @@ mod tests {
|
|||
fn response() {
|
||||
let object = response_from_file::<ContactDelete>("response/contact/delete.xml");
|
||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
||||
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,49 +1,44 @@
|
|||
//! Types for EPP contact info request
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use instant_xml::{FromXml, ToXml};
|
||||
|
||||
use super::{ContactAuthInfo, Phone, PostalInfo, XMLNS};
|
||||
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};
|
||||
|
||||
impl<'a> Transaction<NoExtension> for ContactInfo<'a> {}
|
||||
|
||||
impl<'a> Command for ContactInfo<'a> {
|
||||
type Response = ContactInfoResponse;
|
||||
type Response = ContactInfoData;
|
||||
const COMMAND: &'static str = "info";
|
||||
}
|
||||
|
||||
// Request
|
||||
|
||||
/// Type for elements under the contact <info> tag
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct ContactInfoRequestData<'a> {
|
||||
/// XML namespace for contact commands
|
||||
#[serde(rename = "xmlns:contact")]
|
||||
xmlns: &'a str,
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "info", ns(XMLNS))]
|
||||
pub struct ContactInfoRequest<'a> {
|
||||
/// The contact id for the info command
|
||||
#[serde(rename = "contact:id")]
|
||||
id: StringValue<'a>,
|
||||
id: &'a str,
|
||||
/// The <authInfo> data
|
||||
#[serde(rename = "contact:authInfo")]
|
||||
auth_info: ContactAuthInfo<'a>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
/// Type for EPP XML <info> command for contacts
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "info", ns(EPP_XMLNS))]
|
||||
pub struct ContactInfo<'a> {
|
||||
/// Data for <info> command for contact
|
||||
#[serde(rename = "contact:info")]
|
||||
info: ContactInfoRequestData<'a>,
|
||||
info: ContactInfoRequest<'a>,
|
||||
}
|
||||
|
||||
impl<'a> ContactInfo<'a> {
|
||||
pub fn new(id: &'a str, auth_password: &'a str) -> ContactInfo<'a> {
|
||||
Self {
|
||||
info: ContactInfoRequestData {
|
||||
xmlns: XMLNS,
|
||||
id: id.into(),
|
||||
info: ContactInfoRequest {
|
||||
id,
|
||||
auth_info: ContactAuthInfo::new(auth_password),
|
||||
},
|
||||
}
|
||||
|
@ -53,53 +48,44 @@ impl<'a> ContactInfo<'a> {
|
|||
// Response
|
||||
|
||||
/// Type that represents the <infData> tag for contact check response
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct ContactInfoData<'a> {
|
||||
#[derive(Debug, FromXml)]
|
||||
#[xml(rename = "infData", ns(XMLNS))]
|
||||
pub struct ContactInfoData {
|
||||
/// The contact id
|
||||
pub id: StringValue<'a>,
|
||||
pub id: String,
|
||||
/// The contact ROID
|
||||
pub roid: StringValue<'a>,
|
||||
pub roid: String,
|
||||
/// The list of contact statuses
|
||||
#[serde(rename = "status")]
|
||||
pub statuses: Vec<ObjectStatus<'a>>,
|
||||
pub statuses: Vec<Status<'static>>,
|
||||
/// The postal info for the contact
|
||||
#[serde(rename = "postalInfo")]
|
||||
pub postal_info: PostalInfo<'a>,
|
||||
pub postal_info: PostalInfo<'static>,
|
||||
/// The voice data for the contact
|
||||
pub voice: Phone<'a>,
|
||||
pub voice: Voice<'static>,
|
||||
/// The fax data for the contact
|
||||
pub fax: Option<Phone<'a>>,
|
||||
pub fax: Option<Fax<'static>>,
|
||||
/// The email for the contact
|
||||
pub email: StringValue<'a>,
|
||||
pub email: String,
|
||||
/// The epp user to whom the contact belongs
|
||||
#[serde(rename = "clID")]
|
||||
pub client_id: StringValue<'a>,
|
||||
#[xml(rename = "clID")]
|
||||
pub client_id: String,
|
||||
/// The epp user who created the contact
|
||||
#[serde(rename = "crID")]
|
||||
pub creator_id: StringValue<'a>,
|
||||
#[xml(rename = "crID")]
|
||||
pub creator_id: String,
|
||||
/// The creation date
|
||||
#[serde(rename = "crDate")]
|
||||
#[xml(rename = "crDate")]
|
||||
pub created_at: DateTime<Utc>,
|
||||
/// The epp user who last updated the contact
|
||||
#[serde(rename = "upID")]
|
||||
pub updater_id: Option<StringValue<'a>>,
|
||||
#[xml(rename = "upID")]
|
||||
pub updater_id: Option<String>,
|
||||
/// The last update date
|
||||
#[serde(rename = "upDate")]
|
||||
#[xml(rename = "upDate")]
|
||||
pub updated_at: Option<DateTime<Utc>>,
|
||||
/// The contact transfer date
|
||||
#[serde(rename = "trDate")]
|
||||
#[xml(rename = "trDate")]
|
||||
pub transferred_at: Option<DateTime<Utc>>,
|
||||
/// The contact auth info
|
||||
#[serde(rename = "authInfo")]
|
||||
pub auth_info: Option<ContactAuthInfo<'a>>,
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
#[xml(rename = "authInfo")]
|
||||
pub auth_info: Option<ContactAuthInfo<'static>>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -121,58 +107,43 @@ mod tests {
|
|||
let object = response_from_file::<ContactInfo>("response/contact/info.xml");
|
||||
|
||||
let result = object.res_data().unwrap();
|
||||
let fax = result.info_data.fax.as_ref().unwrap();
|
||||
let voice_ext = result.info_data.voice.extension.as_ref().unwrap();
|
||||
let fax = result.fax.as_ref().unwrap();
|
||||
let voice_ext = result.voice.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.message, SUCCESS_MSG.into());
|
||||
assert_eq!(result.info_data.id, "eppdev-contact-3".into());
|
||||
assert_eq!(result.info_data.roid, "UNDEF-ROID".into());
|
||||
assert_eq!(result.info_data.statuses[0].status, "ok".to_string());
|
||||
assert_eq!(result.info_data.postal_info.info_type, "loc".to_string());
|
||||
assert_eq!(result.info_data.postal_info.name, "John Doe".into());
|
||||
assert_eq!(
|
||||
result.info_data.postal_info.organization,
|
||||
"Acme Widgets".into()
|
||||
);
|
||||
assert_eq!(result.info_data.postal_info.address.street[0], "58".into());
|
||||
assert_eq!(
|
||||
result.info_data.postal_info.address.street[1],
|
||||
"Orchid Road".into()
|
||||
);
|
||||
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!(object.result.message, SUCCESS_MSG);
|
||||
assert_eq!(result.id, "eppdev-contact-3");
|
||||
assert_eq!(result.roid, "UNDEF-ROID");
|
||||
assert_eq!(result.statuses[0].status, "ok");
|
||||
assert_eq!(result.postal_info.info_type, "loc");
|
||||
assert_eq!(result.postal_info.name, "John Doe");
|
||||
assert_eq!(result.postal_info.organization, "Acme Widgets");
|
||||
assert_eq!(result.postal_info.address.street[0], "58");
|
||||
assert_eq!(result.postal_info.address.street[1], "Orchid Road");
|
||||
assert_eq!(result.postal_info.address.city, "Paris");
|
||||
assert_eq!(result.postal_info.address.province, "Paris");
|
||||
assert_eq!(result.postal_info.address.postal_code, "392374");
|
||||
assert_eq!(result.postal_info.address.country.alpha2, "FR");
|
||||
assert_eq!(result.voice.number, "+33.47237942".to_string());
|
||||
assert_eq!(*voice_ext, "123".to_string());
|
||||
assert_eq!(fax.number, "+33.86698799".to_string());
|
||||
assert_eq!(*fax_ext, "243".to_string());
|
||||
assert_eq!(result.info_data.email, "contact@eppdev.net".into());
|
||||
assert_eq!(result.info_data.client_id, "eppdev".into());
|
||||
assert_eq!(result.info_data.creator_id, "SYSTEM".into());
|
||||
assert_eq!(result.email, "contact@eppdev.net");
|
||||
assert_eq!(result.client_id, "eppdev");
|
||||
assert_eq!(result.creator_id, "SYSTEM");
|
||||
assert_eq!(
|
||||
result.info_data.created_at,
|
||||
result.created_at,
|
||||
Utc.with_ymd_and_hms(2021, 7, 23, 13, 9, 9).unwrap(),
|
||||
);
|
||||
assert_eq!(*(result.updater_id.as_ref().unwrap()), "SYSTEM");
|
||||
assert_eq!(
|
||||
*(result.info_data.updater_id.as_ref().unwrap()),
|
||||
"SYSTEM".into()
|
||||
);
|
||||
assert_eq!(
|
||||
result.info_data.updated_at,
|
||||
result.updated_at,
|
||||
Utc.with_ymd_and_hms(2021, 7, 23, 13, 9, 9).single()
|
||||
);
|
||||
assert_eq!(auth_info.password, "eppdev-387323".into());
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
||||
assert_eq!(auth_info.password, "eppdev-387323");
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
//! Types for EPP contact create request
|
||||
|
||||
use super::{ContactAuthInfo, Phone, PostalInfo, XMLNS};
|
||||
use crate::common::{NoExtension, ObjectStatus, StringValue};
|
||||
use instant_xml::ToXml;
|
||||
|
||||
use super::{ContactAuthInfo, Fax, PostalInfo, Status, Voice, XMLNS};
|
||||
use crate::common::{NoExtension, EPP_XMLNS};
|
||||
use crate::request::{Command, Transaction};
|
||||
use serde::Serialize;
|
||||
|
||||
impl<'a> Transaction<NoExtension> for ContactUpdate<'a> {}
|
||||
|
||||
|
@ -15,9 +16,8 @@ impl<'a> Command for ContactUpdate<'a> {
|
|||
impl<'a> ContactUpdate<'a> {
|
||||
pub fn new(id: &'a str) -> ContactUpdate {
|
||||
Self {
|
||||
contact: ContactUpdateRequestData {
|
||||
xmlns: XMLNS,
|
||||
id: id.into(),
|
||||
contact: ContactUpdateRequest {
|
||||
id,
|
||||
add_statuses: None,
|
||||
remove_statuses: None,
|
||||
change_info: None,
|
||||
|
@ -30,11 +30,11 @@ impl<'a> ContactUpdate<'a> {
|
|||
&mut self,
|
||||
email: &'a str,
|
||||
postal_info: PostalInfo<'a>,
|
||||
voice: Phone<'a>,
|
||||
voice: Voice<'a>,
|
||||
auth_password: &'a str,
|
||||
) {
|
||||
self.contact.change_info = Some(ContactChangeInfo {
|
||||
email: Some(email.into()),
|
||||
email: Some(email),
|
||||
postal_info: Some(postal_info),
|
||||
voice: Some(voice),
|
||||
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
|
||||
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 {
|
||||
info.fax = Some(fax)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the data for the <add> tag for the contact update request
|
||||
pub fn add(&mut self, status: &'a [ObjectStatus]) {
|
||||
self.contact.add_statuses = Some(StatusList { status });
|
||||
pub fn add(&mut self, statuses: &'a [Status]) {
|
||||
self.contact.add_statuses = Some(AddStatuses { statuses });
|
||||
}
|
||||
|
||||
/// Sets the data for the <rem> tag for the contact update request
|
||||
pub fn remove(&mut self, status: &'a [ObjectStatus]) {
|
||||
self.contact.remove_statuses = Some(StatusList { status });
|
||||
pub fn remove(&mut self, statuses: &'a [Status]) {
|
||||
self.contact.remove_statuses = Some(RemoveStatuses { statuses });
|
||||
}
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
#[serde(rename = "contact:postalInfo")]
|
||||
postal_info: Option<PostalInfo<'a>>,
|
||||
#[serde(rename = "contact:voice")]
|
||||
voice: Option<Phone<'a>>,
|
||||
#[serde(rename = "contact:fax")]
|
||||
fax: Option<Phone<'a>>,
|
||||
#[serde(rename = "contact:email")]
|
||||
email: Option<StringValue<'a>>,
|
||||
#[serde(rename = "contact:authInfo")]
|
||||
voice: Option<Voice<'a>>,
|
||||
fax: Option<Fax<'a>>,
|
||||
email: Option<&'a str>,
|
||||
auth_info: Option<ContactAuthInfo<'a>>,
|
||||
}
|
||||
|
||||
/// Type for list of elements of the <status> tag for contact update request
|
||||
#[derive(Serialize, Debug)]
|
||||
#[derive(Debug, ToXml)]
|
||||
pub struct StatusList<'a> {
|
||||
#[serde(rename = "contact:status")]
|
||||
status: &'a [ObjectStatus<'a>],
|
||||
status: &'a [Status<'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
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct ContactUpdateRequestData<'a> {
|
||||
#[serde(rename = "xmlns:contact")]
|
||||
xmlns: &'a str,
|
||||
#[serde(rename = "contact:id")]
|
||||
id: StringValue<'a>,
|
||||
#[serde(rename = "contact:add")]
|
||||
add_statuses: Option<StatusList<'a>>,
|
||||
#[serde(rename = "contact:rem")]
|
||||
remove_statuses: Option<StatusList<'a>>,
|
||||
#[serde(rename = "contact:chg")]
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "update", ns(XMLNS))]
|
||||
pub struct ContactUpdateRequest<'a> {
|
||||
id: &'a str,
|
||||
add_statuses: Option<AddStatuses<'a>>,
|
||||
#[xml(rename = "rem")]
|
||||
remove_statuses: Option<RemoveStatuses<'a>>,
|
||||
change_info: Option<ContactChangeInfo<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
/// Type for EPP XML <update> command for contacts
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "update", ns(EPP_XMLNS))]
|
||||
pub struct ContactUpdate<'a> {
|
||||
/// The data under the <update> tag for the contact update
|
||||
#[serde(rename = "contact:update")]
|
||||
contact: ContactUpdateRequestData<'a>,
|
||||
contact: ContactUpdateRequest<'a>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{ContactUpdate, Phone, PostalInfo};
|
||||
use crate::common::ObjectStatus;
|
||||
use super::{ContactUpdate, PostalInfo, Status, Voice};
|
||||
use crate::contact::Address;
|
||||
use crate::response::ResultCode;
|
||||
use crate::tests::{assert_serialized, response_from_file, CLTRID, SUCCESS_MSG, SVTRID};
|
||||
|
@ -120,14 +122,14 @@ mod tests {
|
|||
let street = &["58", "Orchid Road"];
|
||||
let address = Address::new(street, "Paris", "Paris", "392374", "FR".parse().unwrap());
|
||||
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");
|
||||
let add_statuses = &[ObjectStatus {
|
||||
let add_statuses = &[Status {
|
||||
status: "clientTransferProhibited".into(),
|
||||
}];
|
||||
object.add(add_statuses);
|
||||
let remove_statuses = &[ObjectStatus {
|
||||
let remove_statuses = &[Status {
|
||||
status: "clientDeleteProhibited".into(),
|
||||
}];
|
||||
object.remove(remove_statuses);
|
||||
|
@ -139,8 +141,8 @@ mod tests {
|
|||
fn contact_update() {
|
||||
let object = response_from_file::<ContactUpdate>("response/contact/update.xml");
|
||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
||||
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
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::fmt;
|
||||
use std::net::IpAddr;
|
||||
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;
|
||||
|
||||
pub mod check;
|
||||
|
@ -31,85 +32,128 @@ pub use update::DomainUpdate;
|
|||
pub const XMLNS: &str = "urn:ietf:params:xml:ns:domain-1.0";
|
||||
|
||||
/// 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> {
|
||||
/// The <hostName> tag
|
||||
#[serde(rename = "domain:hostName", alias = "hostName")]
|
||||
pub name: StringValue<'a>,
|
||||
#[xml(rename = "hostName")]
|
||||
pub name: Cow<'a, str>,
|
||||
/// The <hostAddr> tags
|
||||
#[serde(
|
||||
rename = "domain:hostAddr",
|
||||
alias = "hostAddr",
|
||||
#[xml(
|
||||
rename = "hostAddr",
|
||||
serialize_with = "serialize_host_addrs_option",
|
||||
deserialize_with = "deserialize_host_addrs_option"
|
||||
)]
|
||||
pub addresses: Option<Vec<IpAddr>>,
|
||||
}
|
||||
|
||||
fn deserialize_host_addrs_option<'de, D>(de: D) -> Result<Option<Vec<IpAddr>>, D::Error>
|
||||
where
|
||||
D: serde::de::Deserializer<'de>,
|
||||
{
|
||||
let addrs = Option::<Vec<HostAddr<'static>>>::deserialize(de)?;
|
||||
let addrs = match addrs {
|
||||
Some(addrs) => addrs,
|
||||
None => return Ok(None),
|
||||
fn deserialize_host_addrs_option<'xml>(
|
||||
into: &mut OptionAccumulator<Vec<IpAddr>, Vec<IpAddr>>,
|
||||
field: &'static str,
|
||||
deserializer: &mut Deserializer<'_, 'xml>,
|
||||
) -> Result<(), instant_xml::Error> {
|
||||
let mut value = <Option<Vec<HostAddr<'static>>> as FromXml<'xml>>::Accumulator::default();
|
||||
<Option<Vec<HostAddr<'static>>>>::deserialize(&mut value, field, deserializer)?;
|
||||
let new = match value.try_done(field)? {
|
||||
Some(new) => new,
|
||||
None => return Ok(()),
|
||||
};
|
||||
|
||||
let result = addrs
|
||||
.into_iter()
|
||||
.map(|addr| IpAddr::from_str(&addr.address))
|
||||
.collect::<Result<_, _>>();
|
||||
let into = into.get_mut();
|
||||
for addr in new {
|
||||
match IpAddr::from_str(&addr.address) {
|
||||
Ok(ip) => into.push(ip),
|
||||
Err(_) => {
|
||||
return Err(instant_xml::Error::UnexpectedValue(format!(
|
||||
"invalid IP address '{}'",
|
||||
&addr.address
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match result {
|
||||
Ok(addrs) => Ok(Some(addrs)),
|
||||
Err(e) => Err(serde::de::Error::custom(format!("{}", e))),
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 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
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct HostAttrList<'a> {
|
||||
/// The list of <hostAttr> tags
|
||||
#[serde(rename = "domain:hostAttr", alias = "hostAttr")]
|
||||
pub hosts: &'a [HostAttr<'a>],
|
||||
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(())
|
||||
}
|
||||
|
||||
/// The list of <hostObj> types for domain transactions. Typically under an <ns> tag
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct HostObjList<'a> {
|
||||
/// The list of <hostObj> tags
|
||||
#[serde(rename = "domain:hostObj", alias = "hostObj")]
|
||||
pub hosts: &'a [StringValue<'a>],
|
||||
#[derive(Clone, Debug, Eq, FromXml, PartialEq, ToXml)]
|
||||
#[xml(rename = "hostObj", ns(XMLNS))]
|
||||
pub struct HostObj<'a> {
|
||||
#[xml(direct)]
|
||||
pub name: Cow<'a, str>,
|
||||
}
|
||||
|
||||
/// Enum that can accept one type which corresponds to either the <hostObj> or <hostAttr>
|
||||
/// list of tags
|
||||
#[derive(Serialize, Debug)]
|
||||
#[serde(untagged)]
|
||||
pub enum HostList<'a> {
|
||||
HostObjList(HostObjList<'a>),
|
||||
HostAttrList(HostAttrList<'a>),
|
||||
#[derive(Clone, Debug, Eq, FromXml, PartialEq, ToXml)]
|
||||
#[xml(forward)]
|
||||
pub enum HostInfo<'a> {
|
||||
Attr(HostAttr<'a>),
|
||||
Obj(HostObj<'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
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[derive(Debug, FromXml, ToXml)]
|
||||
#[xml(rename = "contact", ns(XMLNS))]
|
||||
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)
|
||||
#[serde(rename = "type")]
|
||||
#[xml(attribute, rename = "type")]
|
||||
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
|
||||
#[derive(Clone, Copy, Debug, Serialize)]
|
||||
#[derive(Clone, Copy, Debug, ToXml)]
|
||||
#[xml(rename = "period", ns(XMLNS))]
|
||||
pub struct Period {
|
||||
/// The interval (usually 'y' indicating years)
|
||||
#[xml(attribute)]
|
||||
unit: char,
|
||||
/// The length of the registration, renewal or transfer period (usually in years)
|
||||
#[serde(rename = "$value")]
|
||||
#[xml(direct)]
|
||||
length: u8,
|
||||
}
|
||||
|
||||
|
@ -158,11 +202,12 @@ pub const SIX_MONTHS: Period = Period {
|
|||
};
|
||||
|
||||
/// 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> {
|
||||
/// The <pw> tag under <authInfo>
|
||||
#[serde(rename = "domain:pw", alias = "pw")]
|
||||
pub password: StringValue<'a>,
|
||||
#[xml(rename = "pw")]
|
||||
pub password: Cow<'a, str>,
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use instant_xml::{FromXml, Serializer, ToXml};
|
||||
|
||||
use super::XMLNS;
|
||||
use crate::common::{CheckResponse, NoExtension, StringValue};
|
||||
use crate::common::{NoExtension, EPP_XMLNS};
|
||||
use crate::request::{Command, Transaction};
|
||||
use serde::Serialize;
|
||||
|
||||
impl<'a> Transaction<NoExtension> for DomainCheck<'a> {}
|
||||
|
||||
impl<'a> Command for DomainCheck<'a> {
|
||||
type Response = CheckResponse;
|
||||
type Response = CheckData;
|
||||
const COMMAND: &'static str = "check";
|
||||
}
|
||||
|
||||
// Request
|
||||
|
||||
/// Type for <name> elements under the domain <check> tag
|
||||
#[derive(Serialize, Debug)]
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "check", ns(XMLNS))]
|
||||
struct DomainList<'a> {
|
||||
#[serde(rename = "xmlns:domain")]
|
||||
/// XML namespace for domain commands
|
||||
xmlns: &'a str,
|
||||
#[serde(rename = "domain:name")]
|
||||
/// List of domains to be checked for availability
|
||||
domains: Vec<StringValue<'a>>,
|
||||
#[xml(rename = "name", ns(XMLNS))]
|
||||
domains: &'a [&'a str],
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
struct SerializeDomainCheck<'a> {
|
||||
#[serde(rename = "domain:check")]
|
||||
list: DomainList<'a>,
|
||||
fn serialize_domains<W: fmt::Write + ?Sized>(
|
||||
domains: &[&str],
|
||||
serializer: &mut Serializer<W>,
|
||||
) -> Result<(), instant_xml::Error> {
|
||||
DomainList { domains }.serialize(None, serializer)
|
||||
}
|
||||
|
||||
impl<'a> From<DomainCheck<'a>> for SerializeDomainCheck<'a> {
|
||||
fn from(check: DomainCheck<'a>) -> Self {
|
||||
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")]
|
||||
#[derive(ToXml, Debug)]
|
||||
#[xml(rename = "check", ns(EPP_XMLNS))]
|
||||
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],
|
||||
}
|
||||
|
||||
// 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)]
|
||||
mod tests {
|
||||
use super::DomainCheck;
|
||||
|
@ -67,15 +84,15 @@ mod tests {
|
|||
#[test]
|
||||
fn response() {
|
||||
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.message, SUCCESS_MSG.into());
|
||||
assert_eq!(result.list[0].id, "eppdev.com");
|
||||
assert!(result.list[0].available);
|
||||
assert_eq!(result.list[1].id, "eppdev.net");
|
||||
assert!(!result.list[1].available);
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
||||
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||
assert_eq!(result.list[0].inner.id, "eppdev.com");
|
||||
assert!(result.list[0].inner.available);
|
||||
assert_eq!(result.list[1].inner.id, "eppdev.net");
|
||||
assert!(!result.list[1].inner.available);
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
//! Types for EPP domain create request
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use instant_xml::{FromXml, ToXml};
|
||||
|
||||
use super::{DomainAuthInfo, DomainContact, HostList, Period, XMLNS};
|
||||
use crate::common::{NoExtension, StringValue};
|
||||
use super::{DomainAuthInfo, DomainContact, HostInfo, NameServers, Period, XMLNS};
|
||||
use crate::common::{NoExtension, EPP_XMLNS};
|
||||
use crate::request::{Command, Transaction};
|
||||
|
||||
impl<'a> Transaction<NoExtension> for DomainCreate<'a> {}
|
||||
|
@ -17,39 +17,31 @@ impl<'a> Command for DomainCreate<'a> {
|
|||
// Request
|
||||
|
||||
/// Type for elements under the domain <create> tag
|
||||
#[derive(Serialize, Debug)]
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "create", ns(XMLNS))]
|
||||
pub struct DomainCreateRequestData<'a> {
|
||||
/// XML namespace for domain commands
|
||||
#[serde(rename = "xmlns:domain")]
|
||||
pub xmlns: &'a str,
|
||||
/// The domain name
|
||||
#[serde(rename = "domain:name")]
|
||||
pub name: StringValue<'a>,
|
||||
pub name: &'a str,
|
||||
/// The period of registration
|
||||
#[serde(rename = "domain:period")]
|
||||
pub period: Period,
|
||||
/// The list of nameserver hosts
|
||||
/// either of type `HostObjList` or `HostAttrList`
|
||||
#[serde(rename = "domain:ns")]
|
||||
pub ns: Option<HostList<'a>>,
|
||||
pub ns: Option<NameServers<'a>>,
|
||||
/// The domain registrant
|
||||
#[serde(rename = "domain:registrant")]
|
||||
pub registrant: Option<StringValue<'a>>,
|
||||
pub registrant: Option<&'a str>,
|
||||
/// The list of contacts for the domain
|
||||
#[serde(rename = "domain:contact")]
|
||||
pub contacts: Option<&'a [DomainContact<'a>]>,
|
||||
/// The auth info for the domain
|
||||
#[serde(rename = "domain:authInfo")]
|
||||
pub auth_info: DomainAuthInfo<'a>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
#[derive(Debug, ToXml)]
|
||||
/// Type for EPP XML <create> command for domains
|
||||
#[xml(rename = "create", ns(EPP_XMLNS))]
|
||||
pub struct DomainCreate<'a> {
|
||||
/// The data for the domain to be created with
|
||||
/// T being the type of nameserver list (`HostObjList` or `HostAttrList`)
|
||||
/// to be supplied
|
||||
#[serde(rename = "domain:create")]
|
||||
pub domain: DomainCreateRequestData<'a>,
|
||||
}
|
||||
|
||||
|
@ -57,18 +49,17 @@ impl<'a> DomainCreate<'a> {
|
|||
pub fn new(
|
||||
name: &'a str,
|
||||
period: Period,
|
||||
ns: Option<HostList<'a>>,
|
||||
registrant_id: Option<&'a str>,
|
||||
ns: Option<&'a [HostInfo<'a>]>,
|
||||
registrant: Option<&'a str>,
|
||||
auth_password: &'a str,
|
||||
contacts: Option<&'a [DomainContact<'a>]>,
|
||||
) -> Self {
|
||||
Self {
|
||||
domain: DomainCreateRequestData {
|
||||
xmlns: XMLNS,
|
||||
name: name.into(),
|
||||
name,
|
||||
period,
|
||||
ns,
|
||||
registrant: registrant_id.map(|id| id.into()),
|
||||
ns: ns.map(|ns| NameServers { ns: ns.to_vec() }),
|
||||
registrant,
|
||||
auth_info: DomainAuthInfo::new(auth_password),
|
||||
contacts,
|
||||
},
|
||||
|
@ -79,37 +70,27 @@ impl<'a> DomainCreate<'a> {
|
|||
// Response
|
||||
|
||||
/// Type that represents the <chkData> tag for domain create response
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct DomainCreateResponseData {
|
||||
/// XML namespace for domain response data
|
||||
#[serde(rename = "xmlns:domain")]
|
||||
pub xmlns: String,
|
||||
#[derive(Debug, FromXml)]
|
||||
#[xml(rename = "creData", ns(XMLNS))]
|
||||
pub struct DomainCreateResponse {
|
||||
/// The domain name
|
||||
pub name: StringValue<'static>,
|
||||
pub name: String,
|
||||
/// The creation date
|
||||
#[serde(rename = "crDate")]
|
||||
#[xml(rename = "crDate")]
|
||||
pub created_at: DateTime<Utc>,
|
||||
/// The expiry date
|
||||
#[serde(rename = "exDate")]
|
||||
#[xml(rename = "exDate")]
|
||||
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)]
|
||||
mod tests {
|
||||
use std::net::IpAddr;
|
||||
|
||||
use chrono::{TimeZone, Utc};
|
||||
|
||||
use super::{DomainContact, DomainCreate, HostList, Period};
|
||||
use crate::domain::{HostAttr, HostAttrList, HostObjList};
|
||||
use super::{DomainContact, DomainCreate, Period};
|
||||
use crate::domain::{HostAttr, HostInfo, HostObj};
|
||||
use crate::response::ResultCode;
|
||||
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(
|
||||
"eppdev-1.com",
|
||||
Period::years(1).unwrap(),
|
||||
Some(HostList::HostObjList(HostObjList { hosts })),
|
||||
Some(hosts),
|
||||
Some("eppdev-contact-3"),
|
||||
"epP4uthd#v",
|
||||
Some(contacts),
|
||||
|
@ -190,23 +178,23 @@ mod tests {
|
|||
];
|
||||
|
||||
let hosts = &[
|
||||
HostAttr {
|
||||
HostInfo::Attr(HostAttr {
|
||||
name: "ns1.eppdev-1.com".into(),
|
||||
addresses: None,
|
||||
},
|
||||
HostAttr {
|
||||
}),
|
||||
HostInfo::Attr(HostAttr {
|
||||
name: "ns2.eppdev-1.com".into(),
|
||||
addresses: Some(vec![
|
||||
IpAddr::from([177, 232, 12, 58]),
|
||||
IpAddr::from([0x2404, 0x6800, 0x4001, 0x801, 0, 0, 0, 0x200e]),
|
||||
]),
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
||||
let object = DomainCreate::new(
|
||||
"eppdev-2.com",
|
||||
Period::years(1).unwrap(),
|
||||
Some(HostList::HostAttrList(HostAttrList { hosts })),
|
||||
Some(hosts),
|
||||
Some("eppdev-contact-3"),
|
||||
"epP4uthd#v",
|
||||
Some(contacts),
|
||||
|
@ -222,17 +210,17 @@ mod tests {
|
|||
let result = object.res_data().unwrap();
|
||||
|
||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
||||
assert_eq!(result.create_data.name, "eppdev-2.com".into());
|
||||
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||
assert_eq!(result.name, "eppdev-2.com");
|
||||
assert_eq!(
|
||||
result.create_data.created_at,
|
||||
result.created_at,
|
||||
Utc.with_ymd_and_hms(2021, 7, 25, 18, 11, 35).unwrap()
|
||||
);
|
||||
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()
|
||||
);
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
//! Types for EPP domain delete request
|
||||
|
||||
use instant_xml::ToXml;
|
||||
|
||||
use super::XMLNS;
|
||||
use crate::common::{NoExtension, StringValue};
|
||||
use crate::common::{NoExtension, EPP_XMLNS};
|
||||
use crate::request::{Command, Transaction};
|
||||
use serde::Serialize;
|
||||
|
||||
impl<'a> Transaction<NoExtension> for DomainDelete<'a> {}
|
||||
|
||||
|
@ -15,30 +16,24 @@ impl<'a> Command for DomainDelete<'a> {
|
|||
impl<'a> DomainDelete<'a> {
|
||||
pub fn new(name: &'a str) -> Self {
|
||||
Self {
|
||||
domain: DomainDeleteRequestData {
|
||||
xmlns: XMLNS,
|
||||
name: name.into(),
|
||||
},
|
||||
domain: DomainDeleteRequestData { name },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Type for <name> element under the domain <delete> tag
|
||||
#[derive(Serialize, Debug)]
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "delete", ns(XMLNS))]
|
||||
pub struct DomainDeleteRequestData<'a> {
|
||||
/// XML namespace for domain commands
|
||||
#[serde(rename = "xmlns:domain")]
|
||||
xmlns: &'a str,
|
||||
/// The domain to be deleted
|
||||
#[serde(rename = "domain:name")]
|
||||
name: StringValue<'a>,
|
||||
name: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
#[derive(Debug, ToXml)]
|
||||
/// Type for EPP XML <delete> command for domains
|
||||
#[xml(rename = "delete", ns(EPP_XMLNS))]
|
||||
pub struct DomainDelete<'a> {
|
||||
/// The data under the <delete> tag for domain deletion
|
||||
#[serde(rename = "domain:delete")]
|
||||
domain: DomainDeleteRequestData<'a>,
|
||||
}
|
||||
|
||||
|
@ -59,8 +54,8 @@ mod tests {
|
|||
let object = response_from_file::<DomainDelete>("response/domain/delete.xml");
|
||||
|
||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
||||
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
//! Types for EPP domain info request
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use instant_xml::{FromXml, ToXml};
|
||||
|
||||
use super::{DomainAuthInfo, DomainContact, HostAttr, XMLNS};
|
||||
use crate::common::{NoExtension, ObjectStatus, StringValue};
|
||||
use super::{DomainAuthInfo, DomainContact, HostAttr, NameServers, Status, XMLNS};
|
||||
use crate::common::{NoExtension, EPP_XMLNS};
|
||||
use crate::request::{Command, Transaction};
|
||||
|
||||
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 {
|
||||
Self {
|
||||
info: DomainInfoRequestData {
|
||||
xmlns: XMLNS,
|
||||
domain: Domain { hosts: "all", name },
|
||||
name: Domain { hosts: "all", name },
|
||||
auth_info: auth_password.map(|password| DomainAuthInfo {
|
||||
password: password.into(),
|
||||
}),
|
||||
|
@ -31,34 +30,32 @@ impl<'a> DomainInfo<'a> {
|
|||
// Request
|
||||
|
||||
/// 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> {
|
||||
/// The hosts attribute. Default value is "all"
|
||||
#[xml(attribute)]
|
||||
hosts: &'a str,
|
||||
/// The name of the domain
|
||||
#[serde(rename = "$value")]
|
||||
#[xml(direct)]
|
||||
name: &'a str,
|
||||
}
|
||||
|
||||
/// Type for <name> element under the domain <info> tag
|
||||
#[derive(Serialize, Debug)]
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "info", ns(XMLNS))]
|
||||
pub struct DomainInfoRequestData<'a> {
|
||||
/// XML namespace for domain commands
|
||||
#[serde(rename = "xmlns:domain")]
|
||||
xmlns: &'a str,
|
||||
/// The data for the domain to be queried
|
||||
#[serde(rename = "domain:name")]
|
||||
domain: Domain<'a>,
|
||||
name: Domain<'a>,
|
||||
/// The auth info for the domain
|
||||
#[serde(rename = "domain:authInfo")]
|
||||
auth_info: Option<DomainAuthInfo<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
#[derive(Debug, ToXml)]
|
||||
/// Type for EPP XML <info> command for domains
|
||||
#[xml(rename = "info", ns(EPP_XMLNS))]
|
||||
pub struct DomainInfo<'a> {
|
||||
/// The data under the <info> tag for domain info
|
||||
#[serde(rename = "domain:info")]
|
||||
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
|
||||
/// domain info response
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[derive(Debug, FromXml)]
|
||||
pub struct DomainNsList {
|
||||
/// List of <hostObj> ns elements
|
||||
#[serde(rename = "hostObj")]
|
||||
pub host_obj: Option<Vec<StringValue<'static>>>,
|
||||
#[xml(rename = "hostObj")]
|
||||
pub host_obj: Option<Vec<String>>,
|
||||
/// List of <hostAttr> ns elements
|
||||
pub host_attr: Option<Vec<HostAttr<'static>>>,
|
||||
}
|
||||
|
||||
/// Type that represents the <infData> tag for domain info response
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct DomainInfoResponseData {
|
||||
#[derive(Debug, FromXml)]
|
||||
#[xml(rename = "infData", ns(XMLNS))]
|
||||
pub struct DomainInfoResponse {
|
||||
/// The domain name
|
||||
pub name: StringValue<'static>,
|
||||
pub name: String,
|
||||
/// The domain ROID
|
||||
pub roid: StringValue<'static>,
|
||||
pub roid: String,
|
||||
/// The list of domain statuses
|
||||
#[serde(rename = "status")]
|
||||
pub statuses: Option<Vec<ObjectStatus<'static>>>,
|
||||
#[xml(rename = "status")]
|
||||
pub statuses: Option<Vec<Status<'static>>>,
|
||||
/// The domain registrant
|
||||
pub registrant: Option<StringValue<'static>>,
|
||||
pub registrant: Option<String>,
|
||||
/// The list of domain contacts
|
||||
#[serde(rename = "contact")]
|
||||
#[xml(rename = "contact")]
|
||||
pub contacts: Option<Vec<DomainContact<'static>>>,
|
||||
/// The list of domain nameservers
|
||||
#[serde(rename = "ns")]
|
||||
pub ns: Option<DomainNsList>,
|
||||
pub ns: Option<NameServers<'static>>,
|
||||
/// The list of domain hosts
|
||||
#[serde(rename = "host")]
|
||||
pub hosts: Option<Vec<StringValue<'static>>>,
|
||||
#[xml(rename = "host")]
|
||||
pub hosts: Option<Vec<String>>,
|
||||
/// The epp user who owns the domain
|
||||
#[serde(rename = "clID")]
|
||||
pub client_id: StringValue<'static>,
|
||||
#[xml(rename = "clID")]
|
||||
pub client_id: String,
|
||||
/// The epp user who created the domain
|
||||
#[serde(rename = "crID")]
|
||||
pub creator_id: Option<StringValue<'static>>,
|
||||
#[xml(rename = "crID")]
|
||||
pub creator_id: Option<String>,
|
||||
/// The domain creation date
|
||||
#[serde(rename = "crDate")]
|
||||
#[xml(rename = "crDate")]
|
||||
pub created_at: Option<DateTime<Utc>>,
|
||||
/// The domain expiry date
|
||||
#[serde(rename = "exDate")]
|
||||
#[xml(rename = "exDate")]
|
||||
pub expiring_at: Option<DateTime<Utc>>,
|
||||
/// The epp user who last updated the domain
|
||||
#[serde(rename = "upID")]
|
||||
pub updater_id: Option<StringValue<'static>>,
|
||||
#[xml(rename = "upID")]
|
||||
pub updater_id: Option<String>,
|
||||
/// The domain last updated date
|
||||
#[serde(rename = "upDate")]
|
||||
#[xml(rename = "upDate")]
|
||||
pub updated_at: Option<DateTime<Utc>>,
|
||||
/// The domain transfer date
|
||||
#[serde(rename = "trDate")]
|
||||
#[xml(rename = "trDate")]
|
||||
pub transferred_at: Option<DateTime<Utc>>,
|
||||
/// The domain auth info
|
||||
#[serde(rename = "authInfo")]
|
||||
#[xml(rename = "authInfo")]
|
||||
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)]
|
||||
mod tests {
|
||||
use super::DomainInfo;
|
||||
use crate::domain::{HostInfo, HostObj};
|
||||
use crate::response::ResultCode;
|
||||
use crate::tests::{assert_serialized, response_from_file, CLTRID, SUCCESS_MSG, SVTRID};
|
||||
use chrono::{TimeZone, Utc};
|
||||
|
@ -146,57 +136,61 @@ mod tests {
|
|||
#[test]
|
||||
fn response() {
|
||||
let object = response_from_file::<DomainInfo>("response/domain/info.xml");
|
||||
dbg!(&object);
|
||||
|
||||
let result = object.res_data().unwrap();
|
||||
let auth_info = result.info_data.auth_info.as_ref().unwrap();
|
||||
let ns_list = result.info_data.ns.as_ref().unwrap();
|
||||
let ns = ns_list.host_obj.as_ref().unwrap();
|
||||
let hosts = result.info_data.hosts.as_ref().unwrap();
|
||||
let statuses = result.info_data.statuses.as_ref().unwrap();
|
||||
let registrant = result.info_data.registrant.as_ref().unwrap();
|
||||
let contacts = result.info_data.contacts.as_ref().unwrap();
|
||||
let auth_info = result.auth_info.as_ref().unwrap();
|
||||
let ns = result.ns.as_ref().unwrap();
|
||||
let hosts = result.hosts.as_ref().unwrap();
|
||||
let statuses = result.statuses.as_ref().unwrap();
|
||||
let registrant = result.registrant.as_ref().unwrap();
|
||||
let contacts = result.contacts.as_ref().unwrap();
|
||||
|
||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
||||
assert_eq!(result.info_data.name, "eppdev-1.com".into());
|
||||
assert_eq!(result.info_data.roid, "125899511_DOMAIN_COM-VRSN".into());
|
||||
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||
assert_eq!(result.name, "eppdev-1.com");
|
||||
assert_eq!(result.roid, "125899511_DOMAIN_COM-VRSN");
|
||||
assert_eq!(statuses[0].status, "ok".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].contact_type, "admin".to_string());
|
||||
assert_eq!(contacts[1].id, "eppdev-contact-2".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].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!(
|
||||
*result.info_data.creator_id.as_ref().unwrap(),
|
||||
"SYSTEM".into()
|
||||
ns.ns[0],
|
||||
HostInfo::Obj(HostObj {
|
||||
name: "ns1.eppdev-1.com".into()
|
||||
})
|
||||
);
|
||||
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()
|
||||
);
|
||||
assert_eq!(*result.updater_id.as_ref().unwrap(), "SYSTEM");
|
||||
assert_eq!(
|
||||
*result.info_data.updater_id.as_ref().unwrap(),
|
||||
"SYSTEM".into()
|
||||
);
|
||||
assert_eq!(
|
||||
*result.info_data.updated_at.as_ref().unwrap(),
|
||||
*result.updated_at.as_ref().unwrap(),
|
||||
Utc.with_ymd_and_hms(2021, 7, 23, 15, 31, 21).unwrap()
|
||||
);
|
||||
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()
|
||||
);
|
||||
assert_eq!(auth_info.password, "epP4uthd#v".into());
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
||||
assert_eq!(auth_info.password, "epP4uthd#v");
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
//! Types for EPP domain renew request
|
||||
|
||||
use chrono::{DateTime, NaiveDate, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use instant_xml::{FromXml, ToXml};
|
||||
|
||||
use super::{Period, XMLNS};
|
||||
use crate::common::{NoExtension, StringValue};
|
||||
use crate::common::{NoExtension, EPP_XMLNS};
|
||||
use crate::request::{Command, Transaction};
|
||||
|
||||
impl<'a> Transaction<NoExtension> for DomainRenew<'a> {}
|
||||
|
@ -16,12 +16,10 @@ impl<'a> Command for DomainRenew<'a> {
|
|||
|
||||
impl<'a> DomainRenew<'a> {
|
||||
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 {
|
||||
domain: DomainRenewRequestData {
|
||||
xmlns: XMLNS,
|
||||
name: name.into(),
|
||||
current_expiry_date: exp_date_str,
|
||||
name,
|
||||
current_expiry_date,
|
||||
period,
|
||||
},
|
||||
}
|
||||
|
@ -31,48 +29,38 @@ impl<'a> DomainRenew<'a> {
|
|||
// Request
|
||||
|
||||
/// Type for data under the domain <renew> tag
|
||||
#[derive(Serialize, Debug)]
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "renew", ns(XMLNS))]
|
||||
pub struct DomainRenewRequestData<'a> {
|
||||
/// XML namespace for domain commands
|
||||
#[serde(rename = "xmlns:domain")]
|
||||
xmlns: &'a str,
|
||||
/// The name of the domain to be renewed
|
||||
#[serde(rename = "domain:name")]
|
||||
name: StringValue<'a>,
|
||||
name: &'a str,
|
||||
/// The current expiry date of the domain in 'Y-m-d' format
|
||||
#[serde(rename = "domain:curExpDate")]
|
||||
current_expiry_date: StringValue<'a>,
|
||||
#[xml(rename = "curExpDate")]
|
||||
current_expiry_date: NaiveDate,
|
||||
/// The period of renewal
|
||||
#[serde(rename = "domain:period")]
|
||||
period: Period,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
#[derive(Debug, ToXml)]
|
||||
/// Type for EPP XML <renew> command for domains
|
||||
#[xml(rename = "renew", ns(EPP_XMLNS))]
|
||||
pub struct DomainRenew<'a> {
|
||||
/// The data under the <renew> tag for the domain renewal
|
||||
#[serde(rename = "domain:renew")]
|
||||
#[xml(rename = "renew")]
|
||||
domain: DomainRenewRequestData<'a>,
|
||||
}
|
||||
|
||||
// Response
|
||||
|
||||
/// Type that represents the <renData> tag for domain renew response
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct DomainRenewResponseData {
|
||||
/// 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)]
|
||||
#[derive(Debug, FromXml)]
|
||||
#[xml(rename = "renData", ns(XMLNS))]
|
||||
pub struct DomainRenewResponse {
|
||||
/// Data under the <renData> tag
|
||||
#[serde(rename = "renData")]
|
||||
pub renew_data: DomainRenewResponseData,
|
||||
/// The name of the domain
|
||||
pub name: String,
|
||||
/// The new expiry date after renewal
|
||||
#[xml(rename = "exDate")]
|
||||
pub expiring_at: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -97,13 +85,13 @@ mod tests {
|
|||
let result = object.res_data().unwrap();
|
||||
|
||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
||||
assert_eq!(result.renew_data.name, "eppdev-1.com".into());
|
||||
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||
assert_eq!(result.name, "eppdev-1.com");
|
||||
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()
|
||||
);
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
//! Types for EPP domain transfer request
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use instant_xml::{FromXml, ToXml};
|
||||
|
||||
use super::{DomainAuthInfo, Period, XMLNS};
|
||||
use crate::common::{NoExtension, StringValue};
|
||||
use crate::common::{NoExtension, EPP_XMLNS};
|
||||
use crate::request::{Command, Transaction};
|
||||
|
||||
impl<'a> Transaction<NoExtension> for DomainTransfer<'a> {}
|
||||
|
||||
impl<'a> Command for DomainTransfer<'a> {
|
||||
type Response = DomainTransferResponse;
|
||||
type Response = DomainTransferResponseData;
|
||||
const COMMAND: &'static str = "transfer";
|
||||
}
|
||||
|
||||
|
@ -54,8 +54,7 @@ impl<'a> DomainTransfer<'a> {
|
|||
Self {
|
||||
operation,
|
||||
domain: DomainTransferReqData {
|
||||
xmlns: XMLNS,
|
||||
name: name.into(),
|
||||
name,
|
||||
period,
|
||||
auth_info,
|
||||
},
|
||||
|
@ -66,71 +65,60 @@ impl<'a> DomainTransfer<'a> {
|
|||
// Request
|
||||
|
||||
/// Type for elements under the domain <transfer> tag
|
||||
#[derive(Serialize, Debug)]
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "transfer", ns(XMLNS))]
|
||||
pub struct DomainTransferReqData<'a> {
|
||||
/// XML namespace for domain commands
|
||||
#[serde(rename = "xmlns:domain")]
|
||||
xmlns: &'a str,
|
||||
/// The name of the domain under transfer
|
||||
#[serde(rename = "domain:name")]
|
||||
name: StringValue<'a>,
|
||||
name: &'a str,
|
||||
/// The period of renewal upon a successful transfer
|
||||
/// Only applicable in case of a transfer request
|
||||
#[serde(rename = "domain:period")]
|
||||
period: Option<Period>,
|
||||
/// The authInfo for the domain under transfer
|
||||
/// Only applicable to domain transfer and domain transfer query requests
|
||||
#[serde(rename = "domain:authInfo")]
|
||||
#[xml(rename = "authInfo")]
|
||||
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
|
||||
pub struct DomainTransfer<'a> {
|
||||
/// The transfer operation to perform indicated by the 'op' attr
|
||||
/// The values are one of transfer or query
|
||||
#[serde(rename = "op")]
|
||||
#[xml(rename = "op", attribute)]
|
||||
operation: &'a str,
|
||||
/// The data under the <transfer> tag in the transfer request
|
||||
#[serde(rename = "domain:transfer")]
|
||||
domain: DomainTransferReqData<'a>,
|
||||
}
|
||||
|
||||
// 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 {
|
||||
/// The domain name
|
||||
pub name: StringValue<'static>,
|
||||
pub name: String,
|
||||
/// The domain transfer status
|
||||
#[serde(rename = "trStatus")]
|
||||
pub transfer_status: StringValue<'static>,
|
||||
#[xml(rename = "trStatus")]
|
||||
pub transfer_status: String,
|
||||
/// The epp user who requested the transfer
|
||||
#[serde(rename = "reID")]
|
||||
pub requester_id: StringValue<'static>,
|
||||
#[xml(rename = "reID")]
|
||||
pub requester_id: String,
|
||||
/// The transfer rquest date
|
||||
#[serde(rename = "reDate")]
|
||||
#[xml(rename = "reDate")]
|
||||
pub requested_at: DateTime<Utc>,
|
||||
/// The epp user who should acknowledge the transfer request
|
||||
#[serde(rename = "acID")]
|
||||
pub ack_id: StringValue<'static>,
|
||||
#[xml(rename = "acID")]
|
||||
pub ack_id: String,
|
||||
/// THe date by which the acknowledgment should be made
|
||||
#[serde(rename = "acDate")]
|
||||
#[xml(rename = "acDate")]
|
||||
pub ack_by: DateTime<Utc>,
|
||||
/// The domain expiry date
|
||||
#[serde(rename = "exDate")]
|
||||
#[xml(rename = "exDate")]
|
||||
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)]
|
||||
mod tests {
|
||||
use chrono::{TimeZone, Utc};
|
||||
|
@ -182,26 +170,26 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
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.transfer_data.transfer_status, "pending".into());
|
||||
assert_eq!(result.transfer_data.requester_id, "eppdev".into());
|
||||
assert_eq!(result.name, "eppdev-transfer.com");
|
||||
assert_eq!(result.transfer_status, "pending");
|
||||
assert_eq!(result.requester_id, "eppdev");
|
||||
assert_eq!(
|
||||
result.transfer_data.requested_at,
|
||||
result.requested_at,
|
||||
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!(
|
||||
result.transfer_data.ack_by,
|
||||
result.ack_by,
|
||||
Utc.with_ymd_and_hms(2021, 7, 28, 15, 31, 21).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
result.transfer_data.expiring_at,
|
||||
result.expiring_at,
|
||||
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.server_tr_id, SVTRID.into());
|
||||
assert_eq!(*object.tr_ids.client_tr_id.as_ref().unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -209,9 +197,9 @@ mod tests {
|
|||
let object = response_from_file::<DomainTransfer>("response/domain/transfer_approve.xml");
|
||||
|
||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
||||
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -219,9 +207,9 @@ mod tests {
|
|||
let object = response_from_file::<DomainTransfer>("response/domain/transfer_reject.xml");
|
||||
|
||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
||||
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -229,9 +217,9 @@ mod tests {
|
|||
let object = response_from_file::<DomainTransfer>("response/domain/transfer_cancel.xml");
|
||||
|
||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
||||
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -241,24 +229,24 @@ mod tests {
|
|||
let result = object.res_data().unwrap();
|
||||
|
||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
||||
assert_eq!(result.transfer_data.name, "eppdev-transfer.com".into());
|
||||
assert_eq!(result.transfer_data.transfer_status, "pending".into());
|
||||
assert_eq!(result.transfer_data.requester_id, "eppdev".into());
|
||||
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||
assert_eq!(result.name, "eppdev-transfer.com");
|
||||
assert_eq!(result.transfer_status, "pending");
|
||||
assert_eq!(result.requester_id, "eppdev");
|
||||
assert_eq!(
|
||||
result.transfer_data.requested_at,
|
||||
result.requested_at,
|
||||
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!(
|
||||
result.transfer_data.ack_by,
|
||||
result.ack_by,
|
||||
Utc.with_ymd_and_hms(2021, 7, 28, 15, 31, 21).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
result.transfer_data.expiring_at,
|
||||
result.expiring_at,
|
||||
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.server_tr_id, SVTRID.into());
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
//! 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::{
|
||||
common::{NoExtension, ObjectStatus, StringValue},
|
||||
common::{NoExtension, EPP_XMLNS},
|
||||
request::{Command, Transaction},
|
||||
};
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
impl<'a> Transaction<NoExtension> for DomainUpdate<'a> {}
|
||||
|
||||
impl<'a> Command for DomainUpdate<'a> {
|
||||
|
@ -19,8 +19,7 @@ impl<'a> DomainUpdate<'a> {
|
|||
pub fn new(name: &'a str) -> Self {
|
||||
Self {
|
||||
domain: DomainUpdateRequestData {
|
||||
xmlns: XMLNS,
|
||||
name: name.into(),
|
||||
name,
|
||||
add: None,
|
||||
remove: None,
|
||||
change_info: None,
|
||||
|
@ -34,75 +33,82 @@ impl<'a> DomainUpdate<'a> {
|
|||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
/// The new registrant contact for the domain
|
||||
#[serde(rename = "domain:registrant")]
|
||||
pub registrant: Option<StringValue<'a>>,
|
||||
pub registrant: Option<&'a str>,
|
||||
/// The new auth info for the domain
|
||||
#[serde(rename = "domain:authInfo")]
|
||||
pub auth_info: Option<DomainAuthInfo<'a>>,
|
||||
}
|
||||
|
||||
/// Type for elements under the <add> and <rem> tags for domain update
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct DomainAddRemove<'a> {
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "add", ns(XMLNS))]
|
||||
pub struct DomainAdd<'a> {
|
||||
/// The list of nameservers to add or remove
|
||||
/// Type T can be either a `HostObjList` or `HostAttrList`
|
||||
#[serde(rename = "domain:ns")]
|
||||
pub ns: Option<HostList<'a>>,
|
||||
pub ns: Option<NameServers<'a>>,
|
||||
/// The list of contacts to add to or remove from the domain
|
||||
#[serde(rename = "domain:contact")]
|
||||
pub contacts: Option<&'a [DomainContact<'a>]>,
|
||||
/// The list of statuses to add to or remove from the domain
|
||||
#[serde(rename = "domain:status")]
|
||||
pub statuses: Option<&'a [ObjectStatus<'a>]>,
|
||||
pub statuses: Option<&'a [Status<'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
|
||||
#[derive(Serialize, Debug)]
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "update", ns(XMLNS))]
|
||||
pub struct DomainUpdateRequestData<'a> {
|
||||
/// XML namespace for domain commands
|
||||
#[serde(rename = "xmlns:domain")]
|
||||
pub xmlns: &'a str,
|
||||
/// The name of the domain to update
|
||||
#[serde(rename = "domain:name")]
|
||||
pub name: StringValue<'a>,
|
||||
pub name: &'a str,
|
||||
/// `DomainAddRemove` Object containing the list of elements to be added
|
||||
/// to the domain
|
||||
#[serde(rename = "domain:add")]
|
||||
pub add: Option<DomainAddRemove<'a>>,
|
||||
pub add: Option<DomainAdd<'a>>,
|
||||
/// `DomainAddRemove` Object containing the list of elements to be removed
|
||||
/// from the domain
|
||||
#[serde(rename = "domain:rem")]
|
||||
pub remove: Option<DomainAddRemove<'a>>,
|
||||
pub remove: Option<DomainRemove<'a>>,
|
||||
/// The data under the <chg> tag for domain update
|
||||
#[serde(rename = "domain:chg")]
|
||||
#[xml(rename = "domain:chg")]
|
||||
pub change_info: Option<DomainChangeInfo<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
/// Type for EPP XML <update> command for domains
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "update", ns(EPP_XMLNS))]
|
||||
pub struct DomainUpdate<'a> {
|
||||
#[serde(rename = "domain:update")]
|
||||
pub domain: DomainUpdateRequestData<'a>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{DomainAddRemove, DomainAuthInfo, DomainChangeInfo, DomainContact, DomainUpdate};
|
||||
use crate::common::ObjectStatus;
|
||||
use super::{
|
||||
DomainAdd, DomainAuthInfo, DomainChangeInfo, DomainContact, DomainRemove, DomainUpdate,
|
||||
};
|
||||
use crate::domain::Status;
|
||||
use crate::response::ResultCode;
|
||||
use crate::tests::{assert_serialized, response_from_file, CLTRID, SUCCESS_MSG, SVTRID};
|
||||
|
||||
|
@ -110,11 +116,11 @@ mod tests {
|
|||
fn command() {
|
||||
let mut object = DomainUpdate::new("eppdev.com");
|
||||
|
||||
let statuses = &[ObjectStatus {
|
||||
let statuses = &[Status {
|
||||
status: "clientDeleteProhibited".into(),
|
||||
}];
|
||||
|
||||
let add = DomainAddRemove {
|
||||
let add = DomainAdd {
|
||||
ns: None,
|
||||
contacts: None,
|
||||
statuses: Some(statuses),
|
||||
|
@ -125,7 +131,7 @@ mod tests {
|
|||
id: "eppdev-contact-2".into(),
|
||||
}];
|
||||
|
||||
let remove = DomainAddRemove {
|
||||
let remove = DomainRemove {
|
||||
ns: None,
|
||||
contacts: Some(contacts),
|
||||
statuses: None,
|
||||
|
@ -147,8 +153,8 @@ mod tests {
|
|||
let object = response_from_file::<DomainUpdate>("response/domain/update.xml");
|
||||
|
||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
||||
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,10 +28,10 @@ impl Display for Error {
|
|||
Error::Command(e) => {
|
||||
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::Xml(e) => write!(f, "(de)serialization error: {}", e),
|
||||
Error::Other(e) => write!(f, "error: {}", e),
|
||||
Error::Xml(e) => write!(f, "(de)serialization error: {e}"),
|
||||
Error::Other(e) => write!(f, "error: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
use std::fmt;
|
||||
|
||||
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::request::{Extension, Transaction};
|
||||
|
||||
use super::namestore::{NameStore, NameStoreData};
|
||||
use super::namestore::NameStore;
|
||||
|
||||
pub const XMLNS: &str = "http://www.verisign.com/epp/sync-1.0";
|
||||
|
||||
|
@ -70,47 +70,35 @@ impl Update {
|
|||
/// Create a new sync update request
|
||||
pub fn new(expiration: GMonthDay) -> Self {
|
||||
Self {
|
||||
data: UpdateData {
|
||||
xmlns: XMLNS,
|
||||
exp: expiration.to_string().into(),
|
||||
},
|
||||
exp: expiration.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UpdateWithNameStore<'_> {
|
||||
impl<'a> UpdateWithNameStore<'a> {
|
||||
/// 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 {
|
||||
sync: Update::new(expiration).data,
|
||||
namestore: NameStoreData::new(subproduct),
|
||||
sync: Update::new(expiration),
|
||||
namestore: NameStore::new(subproduct),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Update {
|
||||
#[serde(rename = "sync:update")]
|
||||
pub data: UpdateData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(inline)]
|
||||
pub struct UpdateWithNameStore<'a> {
|
||||
#[serde(rename = "sync:update")]
|
||||
pub sync: UpdateData,
|
||||
#[serde(rename = "namestoreExt:namestoreExt")]
|
||||
pub namestore: NameStoreData<'a>,
|
||||
pub sync: Update,
|
||||
pub namestore: NameStore<'a>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
/// Type for EPP XML <consolidate> extension
|
||||
pub struct UpdateData {
|
||||
/// XML namespace for the consolidate extension
|
||||
#[serde(rename = "xmlns:sync")]
|
||||
pub xmlns: &'static str,
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "update", ns(XMLNS))]
|
||||
pub struct Update {
|
||||
/// The expiry date of the domain
|
||||
#[serde(rename = "sync:expMonthDay")]
|
||||
pub exp: StringValue<'static>,
|
||||
#[xml(rename = "expMonthDay")]
|
||||
pub exp: String,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
//!
|
||||
//! 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)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[derive(Clone, Debug, FromXml, PartialEq)]
|
||||
#[xml(ns(XMLNS), rename = "pollData", rename_all = "camelCase")]
|
||||
pub struct LowBalance {
|
||||
pub registrar_name: String,
|
||||
pub credit_limit: String,
|
||||
|
@ -13,24 +13,28 @@ pub struct LowBalance {
|
|||
pub available_credit: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq)]
|
||||
#[derive(Clone, Debug, FromXml, PartialEq)]
|
||||
#[xml(ns(XMLNS), rename = "creditThreshold")]
|
||||
pub struct Threshold {
|
||||
#[xml(attribute)]
|
||||
pub r#type: ThresholdType,
|
||||
#[serde(rename = "$value")]
|
||||
#[xml(direct)]
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
#[derive(Clone, Copy, Debug, FromXml, PartialEq)]
|
||||
#[xml(scalar, rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum ThresholdType {
|
||||
Fixed,
|
||||
Percent,
|
||||
}
|
||||
|
||||
const XMLNS: &str = "http://www.verisign.com/epp/lowbalance-poll-1.0";
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::message::poll::{MessageData, MessagePollResponse};
|
||||
use crate::message::poll::MessagePollResponse;
|
||||
use crate::message::MessagePoll;
|
||||
use crate::response::ResultCode;
|
||||
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");
|
||||
dbg!(&object);
|
||||
|
||||
let low_balance = match object.res_data {
|
||||
Some(MessagePollResponse {
|
||||
message_data: MessageData::LowBalance(low_balance),
|
||||
}) => low_balance,
|
||||
let low_balance = match object.res_data() {
|
||||
Some(MessagePollResponse::LowBalance(low_balance)) => low_balance,
|
||||
_ => panic!("Unexpected message data"),
|
||||
};
|
||||
|
||||
|
@ -64,10 +66,10 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
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.server_tr_id, SVTRID.into());
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,9 @@
|
|||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use instant_xml::{FromXml, ToXml};
|
||||
|
||||
use crate::{
|
||||
common::StringValue,
|
||||
contact::{
|
||||
check::ContactCheck, create::ContactCreate, delete::ContactDelete, info::ContactInfo,
|
||||
update::ContactUpdate,
|
||||
|
@ -51,22 +50,9 @@ impl Transaction<NameStore<'_>> for HostUpdate<'_> {}
|
|||
|
||||
impl<'a> NameStore<'a> {
|
||||
/// Create a new RGP restore report request
|
||||
pub fn new(subproduct: &str) -> NameStore {
|
||||
pub fn new(subproduct: &'a str) -> NameStore {
|
||||
NameStore {
|
||||
data: NameStoreData {
|
||||
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(),
|
||||
subproduct: subproduct.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,22 +61,13 @@ impl<'a> Extension for NameStore<'a> {
|
|||
type Response = NameStore<'static>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(rename = "namestoreExt:namestoreExt")]
|
||||
pub struct NameStore<'a> {
|
||||
#[serde(rename = "namestoreExt:namestoreExt", alias = "namestoreExt")]
|
||||
pub data: NameStoreData<'a>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[derive(Debug, FromXml, ToXml)]
|
||||
/// Type for EPP XML <namestoreExt> extension
|
||||
pub struct NameStoreData<'a> {
|
||||
/// XML namespace for the RGP restore extension
|
||||
#[serde(rename = "xmlns:namestoreExt", alias = "xmlns")]
|
||||
pub xmlns: Cow<'a, str>,
|
||||
#[xml(rename = "namestoreExt", ns(XMLNS))]
|
||||
pub struct NameStore<'a> {
|
||||
/// The object holding the list of domains to be checked
|
||||
#[serde(rename = "namestoreExt:subProduct", alias = "subProduct")]
|
||||
pub subproduct: StringValue<'a>,
|
||||
#[xml(rename = "subProduct")]
|
||||
pub subproduct: Cow<'a, str>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -118,7 +95,7 @@ mod tests {
|
|||
let object = response_from_file_with_ext::<DomainCheck, NameStore>(
|
||||
"response/extensions/namestore.xml",
|
||||
);
|
||||
let ext = object.extension.unwrap();
|
||||
assert_eq!(ext.data.subproduct, "com".into());
|
||||
let ext = object.extension().unwrap();
|
||||
assert_eq!(ext.subproduct, "com");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,4 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub mod report;
|
||||
pub mod request;
|
||||
|
||||
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
|
||||
|
||||
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::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> {}
|
||||
|
||||
|
@ -18,28 +19,19 @@ impl<'a> RgpRestoreReport<'a> {
|
|||
deleted_at: DateTime<Utc>,
|
||||
restored_at: DateTime<Utc>,
|
||||
restore_reason: &'a str,
|
||||
statements: &[&'a str],
|
||||
statements: &'a [&'a str],
|
||||
other: &'a str,
|
||||
) -> Self {
|
||||
let statements = statements.iter().map(|&s| s.into()).collect();
|
||||
|
||||
Self {
|
||||
xmlns: XMLNS,
|
||||
restore: RgpRestoreReportSection {
|
||||
op: "report",
|
||||
report: RgpRestoreReportSectionData {
|
||||
pre_data: pre_data.into(),
|
||||
post_data: post_data.into(),
|
||||
deleted_at: deleted_at
|
||||
.to_rfc3339_opts(SecondsFormat::AutoSi, true)
|
||||
.into(),
|
||||
restored_at: restored_at
|
||||
.to_rfc3339_opts(SecondsFormat::AutoSi, true)
|
||||
.into(),
|
||||
restore_reason: restore_reason.into(),
|
||||
statements,
|
||||
other: other.into(),
|
||||
},
|
||||
op: "report",
|
||||
report: RgpRestoreReportSectionData {
|
||||
pre_data,
|
||||
post_data,
|
||||
deleted_at: deleted_at.to_rfc3339_opts(SecondsFormat::AutoSi, true),
|
||||
restored_at: restored_at.to_rfc3339_opts(SecondsFormat::AutoSi, true),
|
||||
restore_reason,
|
||||
statements,
|
||||
other,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -49,53 +41,51 @@ impl<'a> Extension for Update<RgpRestoreReport<'a>> {
|
|||
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
|
||||
#[derive(Serialize, Debug)]
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "report", ns(XMLNS))]
|
||||
pub struct RgpRestoreReportSectionData<'a> {
|
||||
/// The pre-delete registration date
|
||||
#[serde(rename = "rgp:preData")]
|
||||
pre_data: StringValue<'a>,
|
||||
#[xml(rename = "preData")]
|
||||
pre_data: &'a str,
|
||||
/// The post-delete registration date
|
||||
#[serde(rename = "rgp:postData")]
|
||||
post_data: StringValue<'a>,
|
||||
#[xml(rename = "postData")]
|
||||
post_data: &'a str,
|
||||
/// The domain deletion date
|
||||
#[serde(rename = "rgp:delTime")]
|
||||
deleted_at: StringValue<'a>,
|
||||
#[xml(rename = "delTime")]
|
||||
deleted_at: String,
|
||||
/// The domain restore request date
|
||||
#[serde(rename = "rgp:resTime")]
|
||||
restored_at: StringValue<'a>,
|
||||
#[xml(rename = "resTime")]
|
||||
restored_at: String,
|
||||
/// The reason for domain restoration
|
||||
#[serde(rename = "rgp:resReason")]
|
||||
restore_reason: StringValue<'a>,
|
||||
#[xml(rename = "resReason")]
|
||||
restore_reason: &'a str,
|
||||
/// The registrar's statements on the domain restoration
|
||||
#[serde(rename = "rgp:statement")]
|
||||
statements: Vec<StringValue<'a>>,
|
||||
#[xml(rename = "statement")]
|
||||
statements: &'a [&'a str],
|
||||
/// Other remarks for domain restoration
|
||||
#[serde(rename = "rgp:other")]
|
||||
other: StringValue<'a>,
|
||||
#[xml(rename = "other")]
|
||||
other: &'a str,
|
||||
}
|
||||
|
||||
/// Type corresponding to the <restore> section in the rgp restore extension
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct RgpRestoreReportSection<'a> {
|
||||
#[derive(Debug, ToXml)]
|
||||
/// Type for EPP XML <check> command for domains
|
||||
#[xml(rename = "restore", ns(XMLNS))]
|
||||
pub struct RgpRestoreReport<'a> {
|
||||
/// The value of the op attribute for the <restore> tag
|
||||
#[xml(attribute)]
|
||||
op: &'a str,
|
||||
/// Data for the <report> tag
|
||||
#[serde(rename = "rgp:report")]
|
||||
#[xml(rename = "rgp:report")]
|
||||
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)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
|
|
|
@ -1,75 +1,86 @@
|
|||
//! Types for EPP RGP restore request
|
||||
|
||||
use instant_xml::{FromXml, ToXml};
|
||||
|
||||
use crate::{
|
||||
domain::{info::DomainInfo, update::DomainUpdate},
|
||||
request::{Extension, Transaction},
|
||||
};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{Update, XMLNS};
|
||||
use super::XMLNS;
|
||||
|
||||
impl<'a> Transaction<Update<RgpRestoreRequest<'a>>> for DomainUpdate<'a> {}
|
||||
|
||||
impl<'a> Transaction<Update<RgpRestoreRequest<'a>>> for DomainInfo<'a> {}
|
||||
|
||||
impl<'a> Extension for Update<RgpRestoreRequest<'a>> {
|
||||
type Response = Update<RgpRequestResponse>;
|
||||
type Response = RgpRequestResponse;
|
||||
}
|
||||
|
||||
// Request
|
||||
|
||||
/// Type corresponding to the <restore> tag for an rgp restore request
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct RgpRestoreRequestData<'a> {
|
||||
/// The value of the op attribute in the <restore> tag
|
||||
pub op: &'a str,
|
||||
#[derive(Debug, FromXml, ToXml)]
|
||||
#[xml(rename = "update", ns(XMLNS))]
|
||||
pub struct Update<T> {
|
||||
pub data: T,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
/// Type for EPP XML <check> command for domains
|
||||
/// Type corresponding to the <restore> tag for an rgp restore request
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "restore", ns(XMLNS))]
|
||||
pub struct RgpRestoreRequest<'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: RgpRestoreRequestData<'a>,
|
||||
/// The value of the op attribute in the <restore> tag
|
||||
#[xml(attribute)]
|
||||
pub op: &'a str,
|
||||
}
|
||||
|
||||
impl Default for RgpRestoreRequest<'static> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
xmlns: XMLNS,
|
||||
restore: RgpRestoreRequestData { op: "request" },
|
||||
}
|
||||
Self { op: "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 {
|
||||
/// The domain RGP status
|
||||
#[serde(rename = "s")]
|
||||
#[xml(rename = "s", attribute)]
|
||||
pub status: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename = "upData")]
|
||||
#[derive(Debug, FromXml)]
|
||||
#[xml(rename = "upData", ns(XMLNS))]
|
||||
/// Type that represents the <resData> tag for domain transfer response
|
||||
pub struct RgpRequestResponse {
|
||||
pub struct RgpRequestUpdateResponse {
|
||||
/// Data under the <rgpStatus> tag
|
||||
#[serde(rename = "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)]
|
||||
mod tests {
|
||||
use super::{RgpRestoreRequest, Update};
|
||||
use crate::domain::info::DomainInfo;
|
||||
use crate::domain::update::{DomainChangeInfo, DomainUpdate};
|
||||
use crate::extensions::rgp::request::RgpRequestResponse;
|
||||
use crate::response::ResultCode;
|
||||
use crate::tests::{assert_serialized, response_from_file_with_ext, SUCCESS_MSG, SVTRID};
|
||||
|
||||
|
@ -102,9 +113,15 @@ mod tests {
|
|||
let ext = object.extension.unwrap();
|
||||
|
||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
||||
assert_eq!(ext.data.rgp_status[0].status, "pendingRestore".to_string());
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
||||
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||
|
||||
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]
|
||||
|
@ -114,7 +131,12 @@ mod tests {
|
|||
);
|
||||
let ext = object.extension.unwrap();
|
||||
|
||||
assert_eq!(ext.data.rgp_status[0].status, "addPeriod");
|
||||
assert_eq!(ext.data.rgp_status[1].status, "renewPeriod");
|
||||
let data = match ext.data {
|
||||
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 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
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize)]
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Debug, PartialEq, ToXml)]
|
||||
#[xml(rename = "hello", ns(EPP_XMLNS))]
|
||||
pub(crate) struct Hello;
|
||||
|
||||
// Response
|
||||
|
||||
|
@ -36,193 +21,235 @@ pub struct ServiceMenu {
|
|||
}
|
||||
|
||||
/// 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 {
|
||||
pub version: StringValue<'static>,
|
||||
pub lang: StringValue<'static>,
|
||||
#[serde(rename = "objURI")]
|
||||
pub obj_uris: Vec<StringValue<'static>>,
|
||||
#[serde(rename = "svcExtension")]
|
||||
pub version: String,
|
||||
pub lang: String,
|
||||
#[xml(rename = "objURI")]
|
||||
pub obj_uris: Vec<String>,
|
||||
#[xml(rename = "svcExtension")]
|
||||
pub svc_ext: Option<ServiceExtension<'static>>,
|
||||
}
|
||||
|
||||
impl<'a, 'de: 'a> Deserialize<'de> for ServiceMenu {
|
||||
/// Deserializes the <svcMenu> data to the `ServiceMenu` type
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let flattened_svc_menu = FlattenedServiceMenu::deserialize(deserializer)?;
|
||||
impl<'xml> FromXml<'xml> for ServiceMenu {
|
||||
fn matches(id: instant_xml::Id<'_>, field: Option<instant_xml::Id<'_>>) -> bool {
|
||||
FlattenedServiceMenu::matches(id, field)
|
||||
}
|
||||
|
||||
let svc_menu = ServiceMenu {
|
||||
options: Options {
|
||||
version: flattened_svc_menu.version,
|
||||
lang: flattened_svc_menu.lang,
|
||||
},
|
||||
services: Services {
|
||||
obj_uris: flattened_svc_menu.obj_uris,
|
||||
svc_ext: flattened_svc_menu.svc_ext,
|
||||
},
|
||||
/// Deserializes the <svcMenu> data to the `ServiceMenu` type
|
||||
fn deserialize<'cx>(
|
||||
into: &mut Self::Accumulator,
|
||||
field: &'static str,
|
||||
deserializer: &mut Deserializer<'cx, 'xml>,
|
||||
) -> Result<(), instant_xml::Error> {
|
||||
dbg!(&into);
|
||||
|
||||
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
|
||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
||||
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||
#[xml(rename = "all", ns(EPP_XMLNS))]
|
||||
pub struct All;
|
||||
|
||||
/// 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;
|
||||
|
||||
/// 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;
|
||||
|
||||
/// 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;
|
||||
|
||||
/// 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;
|
||||
|
||||
/// 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;
|
||||
|
||||
/// Type corresponding to possible <retention> type values
|
||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
||||
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||
#[xml(forward)]
|
||||
pub enum AccessType {
|
||||
/// Data for the <all> tag
|
||||
#[serde(rename = "all")]
|
||||
All(All),
|
||||
/// Data for the <none> tag
|
||||
#[serde(rename = "none")]
|
||||
NoAccess(NoAccess),
|
||||
/// Data for the <null> tag
|
||||
#[serde(rename = "null")]
|
||||
Null(Null),
|
||||
/// Data for the <personal> tag
|
||||
#[serde(rename = "personal")]
|
||||
Personal(Personal),
|
||||
/// Data for the <personalAndOther> tag
|
||||
#[serde(rename = "personalAndOther")]
|
||||
PersonalAndOther(PersonalAndOther),
|
||||
/// Data for the <other> tag
|
||||
#[serde(rename = "other")]
|
||||
Other(Other),
|
||||
}
|
||||
|
||||
/// Type corresponding to <access> in the EPP greeting XML
|
||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
||||
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||
#[xml(rename = "access", ns(EPP_XMLNS))]
|
||||
pub struct Access {
|
||||
#[serde(flatten)]
|
||||
pub ty: AccessType,
|
||||
inner: AccessType,
|
||||
}
|
||||
|
||||
/// Type corresponding to possible <purpose> type values
|
||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
||||
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||
#[xml(forward)]
|
||||
pub enum PurposeType {
|
||||
/// Data for the <admin> tag
|
||||
#[serde(rename = "admin")]
|
||||
Admin,
|
||||
Admin(Admin),
|
||||
/// Data for the <contact> tag
|
||||
#[serde(rename = "contact")]
|
||||
Contact,
|
||||
Contact(Contact),
|
||||
/// Data for the <prov> tag
|
||||
#[serde(rename = "prov")]
|
||||
Prov,
|
||||
Prov(Prov),
|
||||
/// 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
|
||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
||||
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||
#[xml(rename = "purpose", ns(EPP_XMLNS))]
|
||||
pub struct Purpose {
|
||||
#[serde(rename = "$value")]
|
||||
pub purpose: Vec<PurposeType>,
|
||||
}
|
||||
|
||||
/// Type corresponding to possible <purpose> type values
|
||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
||||
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||
#[xml(forward)]
|
||||
pub enum RecipientType {
|
||||
/// Data for the <other> tag
|
||||
#[serde(rename = "other")]
|
||||
Other,
|
||||
Other(Other),
|
||||
/// Data for the <ours> tag
|
||||
#[serde(rename = "ours")]
|
||||
Ours,
|
||||
Ours(Ours),
|
||||
/// Data for the <public> tag
|
||||
#[serde(rename = "public")]
|
||||
Public,
|
||||
Public(Public),
|
||||
/// Data for the <same> tag
|
||||
#[serde(rename = "same")]
|
||||
Same,
|
||||
Same(Same),
|
||||
/// 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
|
||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
||||
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||
#[xml(rename = "recipient", ns(EPP_XMLNS))]
|
||||
pub struct Recipient {
|
||||
#[serde(rename = "$value")]
|
||||
pub recipient: Vec<RecipientType>,
|
||||
}
|
||||
|
||||
/// 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;
|
||||
|
||||
/// 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;
|
||||
|
||||
/// 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;
|
||||
|
||||
/// 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;
|
||||
|
||||
/// 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;
|
||||
|
||||
/// 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 {
|
||||
/// Data for the <business> tag
|
||||
#[serde(rename = "business")]
|
||||
Business(Business),
|
||||
/// Data for the <indefinite> tag
|
||||
#[serde(rename = "indefinite")]
|
||||
Indefinite(Indefinite),
|
||||
/// Data for the <legal> tag
|
||||
#[serde(rename = "legal")]
|
||||
Legal(Legal),
|
||||
/// Data for the <none> tag
|
||||
#[serde(rename = "none")]
|
||||
No(No),
|
||||
None(No),
|
||||
/// Data for the <stated> tag
|
||||
#[serde(rename = "stated")]
|
||||
Stated(Stated),
|
||||
}
|
||||
|
||||
/// Type corresponding to <retention> in the EPP greeting XML
|
||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
||||
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||
#[xml(rename = "retention", ns(EPP_XMLNS))]
|
||||
pub struct Retention {
|
||||
#[serde(flatten)]
|
||||
pub ty: RetentionType,
|
||||
inner: RetentionType,
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
/// Data for the <purpose> tag
|
||||
pub purpose: Purpose,
|
||||
|
@ -233,39 +260,35 @@ pub struct Statement {
|
|||
}
|
||||
|
||||
/// Type corresponding to <absolute> value in the EPP greeting XML
|
||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
||||
pub struct Absolute {
|
||||
#[serde(rename = "$value")]
|
||||
pub absolute: StringValue<'static>,
|
||||
}
|
||||
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||
#[xml(rename = "absolute", ns(EPP_XMLNS))]
|
||||
pub struct Absolute(String);
|
||||
|
||||
/// Type corresponding to <relative> value in the EPP greeting XML
|
||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
||||
pub struct Relative {
|
||||
#[serde(rename = "$value")]
|
||||
pub relative: StringValue<'static>,
|
||||
}
|
||||
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||
#[xml(rename = "relative", ns(EPP_XMLNS))]
|
||||
pub struct Relative(String);
|
||||
|
||||
/// Type corresponding to possible <expiry> type values
|
||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
||||
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||
#[xml(forward)]
|
||||
pub enum ExpiryType {
|
||||
/// Data for the <absolute> tag
|
||||
#[serde(rename = "absolute")]
|
||||
Absolute(Absolute),
|
||||
/// Data for the <relative> tag
|
||||
#[serde(rename = "relative")]
|
||||
Relative(Relative),
|
||||
}
|
||||
|
||||
/// Type corresponding to <expiry> in the EPP greeting XML
|
||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
||||
/// Type corresponding to possible <expiry> type values
|
||||
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||
#[xml(rename = "expiry", ns(EPP_XMLNS))]
|
||||
pub struct Expiry {
|
||||
#[serde(flatten)]
|
||||
pub ty: ExpiryType,
|
||||
inner: ExpiryType,
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
/// Data for the <access> tag
|
||||
pub access: Access,
|
||||
|
@ -275,42 +298,34 @@ pub struct Dcp {
|
|||
pub expiry: Option<Expiry>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
/// 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 {
|
||||
/// The service ID
|
||||
#[serde(rename = "svID")]
|
||||
#[xml(rename = "svID")]
|
||||
pub service_id: String,
|
||||
/// The date from the EPP server
|
||||
#[serde(rename = "svDate")]
|
||||
#[xml(rename = "svDate")]
|
||||
pub service_date: DateTime<Utc>,
|
||||
/// Data under the <svcMenu> element
|
||||
#[serde(rename = "svcMenu")]
|
||||
pub svc_menu: ServiceMenu,
|
||||
/// Data under the <dcp> element
|
||||
pub dcp: Dcp,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
||||
#[serde(rename = "epp")]
|
||||
pub struct GreetingDocument {
|
||||
#[serde(rename = "greeting")]
|
||||
pub data: Greeting,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use chrono::{TimeZone, Utc};
|
||||
|
||||
use super::{ExpiryType, GreetingDocument, HelloDocument, Relative};
|
||||
use super::{ExpiryType, Greeting, Hello, Relative};
|
||||
use crate::tests::get_xml;
|
||||
use crate::xml;
|
||||
|
||||
#[test]
|
||||
fn hello() {
|
||||
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);
|
||||
}
|
||||
|
@ -318,19 +333,18 @@ mod tests {
|
|||
#[test]
|
||||
fn greeting() {
|
||||
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!(
|
||||
object.data.service_date,
|
||||
object.service_date,
|
||||
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.data.svc_menu.options.lang, "en".into());
|
||||
assert_eq!(object.data.svc_menu.services.obj_uris.len(), 4);
|
||||
assert_eq!(object.svc_menu.options.version, "1.0");
|
||||
assert_eq!(object.svc_menu.options.lang, "en");
|
||||
assert_eq!(object.svc_menu.services.obj_uris.len(), 4);
|
||||
assert_eq!(
|
||||
object
|
||||
.data
|
||||
.svc_menu
|
||||
.services
|
||||
.svc_ext
|
||||
|
@ -340,12 +354,10 @@ mod tests {
|
|||
.len(),
|
||||
5
|
||||
);
|
||||
assert_eq!(object.data.dcp.statement.len(), 2);
|
||||
assert_eq!(object.dcp.statement.len(), 2);
|
||||
assert_eq!(
|
||||
object.data.dcp.expiry.unwrap().ty,
|
||||
ExpiryType::Relative(Relative {
|
||||
relative: "P1M".into()
|
||||
})
|
||||
object.dcp.expiry.unwrap().inner,
|
||||
ExpiryType::Relative(Relative("P1M".into()))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,59 +1,74 @@
|
|||
//! 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 crate::common::{CheckResponse, NoExtension, StringValue};
|
||||
use crate::common::{NoExtension, EPP_XMLNS};
|
||||
use crate::request::{Command, Transaction};
|
||||
use serde::Serialize;
|
||||
|
||||
impl<'a> Transaction<NoExtension> for HostCheck<'a> {}
|
||||
|
||||
impl<'a> Command for HostCheck<'a> {
|
||||
type Response = CheckResponse;
|
||||
type Response = CheckData;
|
||||
const COMMAND: &'static str = "check";
|
||||
}
|
||||
|
||||
// Request
|
||||
|
||||
/// Type for data under the host <check> tag
|
||||
#[derive(Serialize, Debug)]
|
||||
struct HostList<'a> {
|
||||
/// XML namespace for host commands
|
||||
#[serde(rename = "xmlns:host")]
|
||||
xmlns: &'a str,
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "check", ns(XMLNS))]
|
||||
struct HostCheckData<'a> {
|
||||
/// List of hosts to be checked for availability
|
||||
#[serde(rename = "host:name")]
|
||||
hosts: Vec<StringValue<'a>>,
|
||||
name: &'a [&'a str],
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
/// Type for EPP XML <check> command for hosts
|
||||
struct SerializeHostCheck<'a> {
|
||||
/// The instance holding the list of hosts to be checked
|
||||
#[serde(rename = "host:check")]
|
||||
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(),
|
||||
},
|
||||
}
|
||||
}
|
||||
fn serialize_hosts<W: fmt::Write + ?Sized>(
|
||||
hosts: &[&str],
|
||||
serializer: &mut Serializer<W>,
|
||||
) -> Result<(), instant_xml::Error> {
|
||||
HostCheckData { name: hosts }.serialize(None, serializer)
|
||||
}
|
||||
|
||||
/// The EPP `check` command for hosts
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
#[serde(into = "SerializeHostCheck")]
|
||||
#[derive(Clone, Debug, ToXml)]
|
||||
#[xml(rename = "check", ns(EPP_XMLNS))]
|
||||
pub struct HostCheck<'a> {
|
||||
/// The list of hosts to be checked
|
||||
#[xml(serialize_with = "serialize_hosts")]
|
||||
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)]
|
||||
mod tests {
|
||||
use super::HostCheck;
|
||||
|
@ -74,12 +89,12 @@ mod tests {
|
|||
let result = object.res_data().unwrap();
|
||||
|
||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
||||
assert_eq!(result.list[0].id, "host1.eppdev-1.com");
|
||||
assert!(result.list[0].available);
|
||||
assert_eq!(result.list[1].id, "ns1.testing.com");
|
||||
assert!(!result.list[1].available);
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
||||
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||
assert_eq!(result.list[0].inner.id, "host1.eppdev-1.com");
|
||||
assert!(result.list[0].inner.available);
|
||||
assert_eq!(result.list[1].inner.id, "ns1.testing.com");
|
||||
assert!(!result.list[1].inner.available);
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,25 +3,24 @@
|
|||
use std::net::IpAddr;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use instant_xml::{FromXml, ToXml};
|
||||
|
||||
use super::XMLNS;
|
||||
use crate::common::{serialize_host_addrs_option, NoExtension, StringValue};
|
||||
use super::{serialize_host_addrs_option, XMLNS};
|
||||
use crate::common::{NoExtension, EPP_XMLNS};
|
||||
use crate::request::{Command, Transaction};
|
||||
|
||||
impl<'a> Transaction<NoExtension> for HostCreate<'a> {}
|
||||
|
||||
impl<'a> Command for HostCreate<'a> {
|
||||
type Response = HostCreateResponse;
|
||||
type Response = HostCreateData;
|
||||
const COMMAND: &'static str = "create";
|
||||
}
|
||||
|
||||
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 {
|
||||
host: HostCreateRequestData {
|
||||
xmlns: XMLNS,
|
||||
name: host.into(),
|
||||
host: HostCreateRequest {
|
||||
name,
|
||||
addresses,
|
||||
},
|
||||
}
|
||||
|
@ -31,47 +30,37 @@ impl<'a> HostCreate<'a> {
|
|||
// Request
|
||||
|
||||
/// Type for data under the host <create> tag
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct HostCreateRequestData<'a> {
|
||||
/// XML namespace for host commands
|
||||
#[serde(rename = "xmlns:host")]
|
||||
xmlns: &'a str,
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "create", ns(XMLNS))]
|
||||
pub struct HostCreateRequest<'a> {
|
||||
/// The name of the host to be created
|
||||
#[serde(rename = "host:name")]
|
||||
pub name: StringValue<'a>,
|
||||
pub name: &'a str,
|
||||
/// 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]>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
/// Type for EPP XML <create> command for hosts
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "create", ns(EPP_XMLNS))]
|
||||
pub struct HostCreate<'a> {
|
||||
/// The instance holding the data for the host to be created
|
||||
#[serde(rename = "host:create")]
|
||||
host: HostCreateRequestData<'a>,
|
||||
host: HostCreateRequest<'a>,
|
||||
}
|
||||
|
||||
// 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 {
|
||||
/// The host name
|
||||
pub name: StringValue<'static>,
|
||||
pub name: String,
|
||||
/// The host creation date
|
||||
#[serde(rename = "crDate")]
|
||||
#[xml(rename = "crDate")]
|
||||
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)]
|
||||
mod tests {
|
||||
use chrono::{TimeZone, Utc};
|
||||
|
@ -97,13 +86,13 @@ mod tests {
|
|||
let result = object.res_data().unwrap();
|
||||
|
||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
||||
assert_eq!(result.create_data.name, "host2.eppdev-1.com".into());
|
||||
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||
assert_eq!(result.name, "host2.eppdev-1.com");
|
||||
assert_eq!(
|
||||
result.create_data.created_at,
|
||||
result.created_at,
|
||||
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.server_tr_id, SVTRID.into());
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
//! Types for EPP host delete request
|
||||
|
||||
use instant_xml::ToXml;
|
||||
|
||||
use super::XMLNS;
|
||||
use crate::common::{NoExtension, StringValue};
|
||||
use crate::common::{NoExtension, EPP_XMLNS};
|
||||
use crate::request::{Command, Transaction};
|
||||
use serde::Serialize;
|
||||
|
||||
impl<'a> Transaction<NoExtension> for HostDelete<'a> {}
|
||||
|
||||
|
@ -15,31 +16,25 @@ impl<'a> Command for HostDelete<'a> {
|
|||
impl<'a> HostDelete<'a> {
|
||||
pub fn new(name: &'a str) -> Self {
|
||||
Self {
|
||||
host: HostDeleteRequestData {
|
||||
xmlns: XMLNS,
|
||||
name: name.into(),
|
||||
},
|
||||
host: HostDeleteRequest { name },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Type for data under the host <delete> tag
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct HostDeleteRequestData<'a> {
|
||||
/// XML namespace for host commands
|
||||
#[serde(rename = "xmlns:host")]
|
||||
xmlns: &'a str,
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "delete", ns(XMLNS))]
|
||||
pub struct HostDeleteRequest<'a> {
|
||||
/// The host to be deleted
|
||||
#[serde(rename = "host:name")]
|
||||
name: StringValue<'a>,
|
||||
name: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
/// Type for EPP XML <delete> command for hosts
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "delete", ns(EPP_XMLNS))]
|
||||
pub struct HostDelete<'a> {
|
||||
/// The instance holding the data for the host to be deleted
|
||||
#[serde(rename = "host:delete")]
|
||||
host: HostDeleteRequestData<'a>,
|
||||
host: HostDeleteRequest<'a>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -58,8 +53,8 @@ mod tests {
|
|||
fn response() {
|
||||
let object = response_from_file::<HostDelete>("response/host/delete.xml");
|
||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
||||
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
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 chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use instant_xml::{FromXml, ToXml};
|
||||
|
||||
use super::XMLNS;
|
||||
use crate::common::{HostAddr, NoExtension, ObjectStatus, StringValue};
|
||||
use super::{HostAddr, Status, XMLNS};
|
||||
use crate::common::{NoExtension, EPP_XMLNS};
|
||||
use crate::request::{Command, Transaction};
|
||||
|
||||
impl<'a> Transaction<NoExtension> for HostInfo<'a> {}
|
||||
|
||||
impl<'a> Command for HostInfo<'a> {
|
||||
type Response = HostInfoResponse;
|
||||
type Response = HostInfoResponseData;
|
||||
const COMMAND: &'static str = "info";
|
||||
}
|
||||
|
||||
impl<'a> HostInfo<'a> {
|
||||
pub fn new(name: &'a str) -> Self {
|
||||
Self {
|
||||
info: HostInfoRequestData {
|
||||
xmlns: XMLNS,
|
||||
name: name.into(),
|
||||
},
|
||||
info: HostInfoRequestData { name },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,78 +28,91 @@ impl<'a> HostInfo<'a> {
|
|||
// Request
|
||||
|
||||
/// Type for data under the host <info> tag
|
||||
#[derive(Serialize, Debug)]
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "info", ns(XMLNS))]
|
||||
pub struct HostInfoRequestData<'a> {
|
||||
/// XML namespace for host commands
|
||||
#[serde(rename = "xmlns:host")]
|
||||
xmlns: &'a str,
|
||||
/// The name of the host to be queried
|
||||
#[serde(rename = "host:name")]
|
||||
name: StringValue<'a>,
|
||||
name: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
/// Type for EPP XML <info> command for hosts
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "info", ns(EPP_XMLNS))]
|
||||
pub struct HostInfo<'a> {
|
||||
/// The instance holding the data for the host query
|
||||
#[serde(rename = "host:info")]
|
||||
#[xml(rename = "host:info")]
|
||||
info: HostInfoRequestData<'a>,
|
||||
}
|
||||
|
||||
// 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 {
|
||||
/// The host name
|
||||
pub name: StringValue<'static>,
|
||||
pub name: String,
|
||||
/// The host ROID
|
||||
pub roid: StringValue<'static>,
|
||||
pub roid: String,
|
||||
/// The list of host statuses
|
||||
#[serde(rename = "status")]
|
||||
pub statuses: Vec<ObjectStatus<'static>>,
|
||||
#[xml(rename = "status")]
|
||||
pub statuses: Vec<Status<'static>>,
|
||||
/// 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>,
|
||||
/// The epp user to whom the host belongs
|
||||
#[serde(rename = "clID")]
|
||||
pub client_id: StringValue<'static>,
|
||||
#[xml(rename = "clID")]
|
||||
pub client_id: String,
|
||||
/// THe epp user that created the host
|
||||
#[serde(rename = "crID")]
|
||||
pub creator_id: StringValue<'static>,
|
||||
#[xml(rename = "crID")]
|
||||
pub creator_id: String,
|
||||
/// The host creation date
|
||||
#[serde(rename = "crDate")]
|
||||
#[xml(rename = "crDate")]
|
||||
pub created_at: DateTime<Utc>,
|
||||
/// The epp user that last updated the host
|
||||
#[serde(rename = "upID")]
|
||||
pub updater_id: Option<StringValue<'static>>,
|
||||
#[xml(rename = "upID")]
|
||||
pub updater_id: Option<String>,
|
||||
/// The host last update date
|
||||
#[serde(rename = "upDate")]
|
||||
#[xml(rename = "upDate")]
|
||||
pub updated_at: Option<DateTime<Utc>>,
|
||||
/// The host transfer date
|
||||
#[serde(rename = "trDate")]
|
||||
#[xml(rename = "trDate")]
|
||||
pub transferred_at: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
fn deserialize_host_addrs<'de, D>(de: D) -> Result<Vec<IpAddr>, D::Error>
|
||||
where
|
||||
D: serde::de::Deserializer<'de>,
|
||||
{
|
||||
let addrs = Vec::<HostAddr<'static>>::deserialize(de)?;
|
||||
addrs
|
||||
.into_iter()
|
||||
.map(|addr| IpAddr::from_str(&addr.address))
|
||||
.collect::<Result<_, _>>()
|
||||
.map_err(|e| serde::de::Error::custom(format!("{}", e)))
|
||||
fn deserialize_host_addrs(
|
||||
into: &mut Vec<IpAddr>,
|
||||
field: &'static str,
|
||||
deserializer: &mut instant_xml::de::Deserializer<'_, '_>,
|
||||
) -> Result<(), instant_xml::Error> {
|
||||
let mut addrs = Vec::new();
|
||||
Vec::<HostAddr<'static>>::deserialize(&mut addrs, field, deserializer)?;
|
||||
|
||||
for addr in addrs {
|
||||
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
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[derive(Debug, FromXml)]
|
||||
#[xml(rename = "infData", ns(XMLNS))]
|
||||
pub struct HostInfoResponse {
|
||||
/// Data under the <infData> tag
|
||||
#[serde(rename = "infData")]
|
||||
#[xml(rename = "infData")]
|
||||
pub info_data: HostInfoResponseData,
|
||||
}
|
||||
*/
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
@ -124,33 +134,27 @@ mod tests {
|
|||
let result = object.res_data().unwrap();
|
||||
|
||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
||||
assert_eq!(result.info_data.name, "host2.eppdev-1.com".into());
|
||||
assert_eq!(result.info_data.roid, "UNDEF-ROID".into());
|
||||
assert_eq!(result.info_data.statuses[0].status, "ok".to_string());
|
||||
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||
assert_eq!(result.name, "host2.eppdev-1.com");
|
||||
assert_eq!(result.roid, "UNDEF-ROID");
|
||||
assert_eq!(result.statuses[0].status, "ok".to_string());
|
||||
assert_eq!(result.addresses[0], IpAddr::from([29, 245, 122, 14]));
|
||||
assert_eq!(
|
||||
result.info_data.addresses[0],
|
||||
IpAddr::from([29, 245, 122, 14])
|
||||
);
|
||||
assert_eq!(
|
||||
result.info_data.addresses[1],
|
||||
result.addresses[1],
|
||||
IpAddr::from([0x2404, 0x6800, 0x4001, 0x801, 0, 0, 0, 0x200e])
|
||||
);
|
||||
assert_eq!(result.info_data.client_id, "eppdev".into());
|
||||
assert_eq!(result.info_data.creator_id, "creator".into());
|
||||
assert_eq!(result.client_id, "eppdev");
|
||||
assert_eq!(result.creator_id, "creator");
|
||||
assert_eq!(
|
||||
result.info_data.created_at,
|
||||
result.created_at,
|
||||
Utc.with_ymd_and_hms(2021, 7, 26, 5, 28, 55).unwrap()
|
||||
);
|
||||
assert_eq!(*(result.updater_id.as_ref().unwrap()), "creator");
|
||||
assert_eq!(
|
||||
*(result.info_data.updater_id.as_ref().unwrap()),
|
||||
"creator".into()
|
||||
);
|
||||
assert_eq!(
|
||||
result.info_data.updated_at,
|
||||
result.updated_at,
|
||||
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.server_tr_id, SVTRID.into());
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,11 @@
|
|||
|
||||
use std::net::IpAddr;
|
||||
|
||||
use super::XMLNS;
|
||||
use crate::common::{serialize_host_addrs_option, NoExtension, ObjectStatus, StringValue};
|
||||
use instant_xml::ToXml;
|
||||
|
||||
use super::{serialize_host_addrs_option, Status, XMLNS};
|
||||
use crate::common::{NoExtension, EPP_XMLNS};
|
||||
use crate::request::{Command, Transaction};
|
||||
use serde::Serialize;
|
||||
|
||||
impl<'a> Transaction<NoExtension> for HostUpdate<'a> {}
|
||||
|
||||
|
@ -17,9 +18,8 @@ impl<'a> Command for HostUpdate<'a> {
|
|||
impl<'a> HostUpdate<'a> {
|
||||
pub fn new(name: &'a str) -> Self {
|
||||
Self {
|
||||
host: HostUpdateRequestData {
|
||||
xmlns: XMLNS,
|
||||
name: name.into(),
|
||||
host: HostUpdateRequest {
|
||||
name,
|
||||
add: None,
|
||||
remove: None,
|
||||
change_info: None,
|
||||
|
@ -33,68 +33,77 @@ impl<'a> HostUpdate<'a> {
|
|||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
}
|
||||
|
||||
/// Type for data under the <chg> tag
|
||||
#[derive(Serialize, Debug)]
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "chg", ns(XMLNS))]
|
||||
pub struct HostChangeInfo<'a> {
|
||||
/// The new name for the host
|
||||
#[serde(rename = "host:name")]
|
||||
pub name: StringValue<'a>,
|
||||
pub name: &'a str,
|
||||
}
|
||||
|
||||
/// Type for data under the <add> and <rem> tags
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct HostAddRemove<'a> {
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "add", ns(XMLNS))]
|
||||
pub struct HostAdd<'a> {
|
||||
/// 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]>,
|
||||
/// The statuses to be added to or removed from the host
|
||||
#[serde(rename = "host:status")]
|
||||
pub statuses: Option<&'a [ObjectStatus<'a>]>,
|
||||
#[xml(rename = "host:status")]
|
||||
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
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct HostUpdateRequestData<'a> {
|
||||
/// XML namespace for host commands
|
||||
#[serde(rename = "xmlns:host")]
|
||||
xmlns: &'a str,
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "update", ns(XMLNS))]
|
||||
pub struct HostUpdateRequest<'a> {
|
||||
/// The name of the host
|
||||
#[serde(rename = "host:name")]
|
||||
name: StringValue<'a>,
|
||||
name: &'a str,
|
||||
/// The IP addresses and statuses to be added to the host
|
||||
#[serde(rename = "host:add")]
|
||||
add: Option<HostAddRemove<'a>>,
|
||||
#[xml(rename = "host:add")]
|
||||
add: Option<HostAdd<'a>>,
|
||||
/// The IP addresses and statuses to be removed from the host
|
||||
#[serde(rename = "host:rem")]
|
||||
remove: Option<HostAddRemove<'a>>,
|
||||
#[xml(rename = "host:rem")]
|
||||
remove: Option<HostRemove<'a>>,
|
||||
/// The host details that need to be updated
|
||||
#[serde(rename = "host:chg")]
|
||||
#[xml(rename = "host:chg")]
|
||||
change_info: Option<HostChangeInfo<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
/// Type for EPP XML <update> command for hosts
|
||||
#[derive(Debug, ToXml)]
|
||||
#[xml(rename = "update", ns(EPP_XMLNS))]
|
||||
pub struct HostUpdate<'a> {
|
||||
/// The instance holding the data for the host to be updated
|
||||
#[serde(rename = "host:update")]
|
||||
host: HostUpdateRequestData<'a>,
|
||||
host: HostUpdateRequest<'a>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::IpAddr;
|
||||
use super::{HostAddRemove, HostChangeInfo, HostUpdate};
|
||||
use crate::common::ObjectStatus;
|
||||
use super::{HostAdd, HostChangeInfo, HostRemove, HostUpdate, Status};
|
||||
use crate::response::ResultCode;
|
||||
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,
|
||||
])];
|
||||
|
||||
let add = HostAddRemove {
|
||||
let add = HostAdd {
|
||||
addresses: Some(addr),
|
||||
statuses: None,
|
||||
};
|
||||
|
||||
let statuses = &[ObjectStatus {
|
||||
let statuses = &[Status {
|
||||
status: "clientDeleteProhibited".into(),
|
||||
}];
|
||||
|
||||
let remove = HostAddRemove {
|
||||
let remove = HostRemove {
|
||||
addresses: None,
|
||||
statuses: Some(statuses),
|
||||
};
|
||||
|
@ -123,7 +132,7 @@ mod tests {
|
|||
object.add(add);
|
||||
object.remove(remove);
|
||||
object.info(HostChangeInfo {
|
||||
name: "host2.eppdev-1.com".into(),
|
||||
name: "host2.eppdev-1.com",
|
||||
});
|
||||
|
||||
assert_serialized("request/host/update.xml", &object);
|
||||
|
@ -134,8 +143,8 @@ mod tests {
|
|||
let object = response_from_file::<HostUpdate>("response/host/update.xml");
|
||||
|
||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
||||
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
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 epp_client::EppClient;
|
||||
//! use epp_client::domain::DomainCheck;
|
||||
//! use epp_client::domain::check::DomainCheck;
|
||||
//! use epp_client::login::Login;
|
||||
//!
|
||||
//! #[tokio::main]
|
||||
|
@ -79,9 +79,11 @@
|
|||
//! let domain_check = DomainCheck { domains: &["eppdev.com", "eppdev.net"] };
|
||||
//! let response = client.transact(&domain_check, "transaction-id").await.unwrap();
|
||||
//!
|
||||
//! response.res_data.unwrap().list
|
||||
//! response.res_data()
|
||||
//! .unwrap()
|
||||
//! .list
|
||||
//! .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 {
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::net::IpAddr;
|
||||
|
||||
use instant_xml::{FromXml, Serializer, ToXml};
|
||||
|
||||
pub mod check;
|
||||
pub use check::HostCheck;
|
||||
|
||||
|
@ -129,6 +137,53 @@ pub mod host {
|
|||
pub use update::HostUpdate;
|
||||
|
||||
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 {
|
||||
|
|
33
src/login.rs
33
src/login.rs
|
@ -1,31 +1,32 @@
|
|||
use std::fmt::Debug;
|
||||
|
||||
use serde::Serialize;
|
||||
use instant_xml::ToXml;
|
||||
|
||||
use crate::{
|
||||
common::{NoExtension, Options, ServiceExtension, Services, StringValue},
|
||||
common::{NoExtension, Options, ServiceExtension, Services, EPP_XMLNS},
|
||||
contact, domain, host,
|
||||
request::{Command, Transaction, EPP_LANG, EPP_VERSION},
|
||||
};
|
||||
|
||||
impl<'a> Transaction<NoExtension> for Login<'a> {}
|
||||
|
||||
#[derive(Serialize, Debug, Eq, PartialEq)]
|
||||
/// 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> {
|
||||
/// The username to use for the login
|
||||
#[serde(rename(serialize = "clID", deserialize = "clID"))]
|
||||
username: StringValue<'a>,
|
||||
#[xml(rename = "clID")]
|
||||
username: &'a str,
|
||||
/// The password to use for the login
|
||||
#[serde(rename = "pw", default)]
|
||||
password: StringValue<'a>,
|
||||
#[xml(rename = "pw")]
|
||||
password: &'a str,
|
||||
/// A new password which should be set
|
||||
#[serde(rename = "newPW", default, skip_serializing_if = "Option::is_none")]
|
||||
new_password: Option<StringValue<'a>>,
|
||||
#[xml(rename = "newPW")]
|
||||
new_password: Option<&'a str>,
|
||||
/// Data under the <options> tag
|
||||
options: Options<'a>,
|
||||
/// Data under the <svcs> tag
|
||||
#[serde(rename = "svcs")]
|
||||
#[xml(rename = "svcs")]
|
||||
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());
|
||||
|
||||
Self {
|
||||
username: username.into(),
|
||||
password: password.into(),
|
||||
new_password: new_password.map(Into::into),
|
||||
username,
|
||||
password,
|
||||
new_password,
|
||||
options: Options {
|
||||
version: EPP_VERSION.into(),
|
||||
lang: EPP_LANG.into(),
|
||||
|
@ -90,8 +91,8 @@ mod tests {
|
|||
fn response() {
|
||||
let object = response_from_file::<Login>("response/login.xml");
|
||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||
assert_eq!(object.result.message, SUCCESS_MSG.into());
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
||||
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use std::fmt::Debug;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use instant_xml::{FromXml, ToXml};
|
||||
|
||||
use crate::{
|
||||
common::NoExtension,
|
||||
common::{NoExtension, EPP_XMLNS},
|
||||
request::{Command, Transaction},
|
||||
};
|
||||
|
||||
|
@ -14,8 +14,9 @@ impl Command for 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
|
||||
#[xml(rename = "logout", ns(EPP_XMLNS))]
|
||||
pub struct Logout;
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -40,9 +41,9 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
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.server_tr_id, SVTRID.into());
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
//! 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 serde::Serialize;
|
||||
|
||||
impl<'a> Transaction<NoExtension> for MessageAck<'a> {}
|
||||
|
||||
|
@ -11,14 +12,16 @@ impl<'a> Command for MessageAck<'a> {
|
|||
const COMMAND: &'static str = "poll";
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
#[derive(Debug, ToXml)]
|
||||
/// Type for EPP XML <poll> command for message ack
|
||||
#[xml(rename = "poll", ns(EPP_XMLNS))]
|
||||
pub struct MessageAck<'a> {
|
||||
/// The type of operation to perform
|
||||
/// The value is "ack" for message acknowledgement
|
||||
#[xml(attribute)]
|
||||
op: &'a str,
|
||||
/// The ID of the message to be acknowledged
|
||||
#[serde(rename = "msgID")]
|
||||
#[xml(rename = "msgID", attribute)]
|
||||
message_id: &'a str,
|
||||
}
|
||||
|
||||
|
@ -49,9 +52,9 @@ mod tests {
|
|||
let msg = object.message_queue().unwrap();
|
||||
|
||||
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.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::extensions::low_balance::LowBalance;
|
||||
use crate::host::info::HostInfoResponseData;
|
||||
use crate::request::{Command, Transaction};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
impl<'a> Transaction<NoExtension> for MessagePoll<'a> {}
|
||||
|
||||
|
@ -14,11 +15,13 @@ impl<'a> Command for MessagePoll<'a> {
|
|||
|
||||
// Request
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
#[derive(Debug, ToXml)]
|
||||
/// Type for EPP XML <poll> command for message poll
|
||||
#[xml(rename = "poll", ns(EPP_XMLNS))]
|
||||
pub struct MessagePoll<'a> {
|
||||
/// The type of operation to perform
|
||||
/// The value is "req" for message polling
|
||||
#[xml(attribute)]
|
||||
op: &'a str,
|
||||
}
|
||||
|
||||
|
@ -31,32 +34,20 @@ impl Default for MessagePoll<'static> {
|
|||
// Response
|
||||
|
||||
/// Type that represents the <trnData> tag for message poll response
|
||||
#[non_exhaustive]
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub enum MessageData {
|
||||
#[derive(Debug, FromXml)]
|
||||
#[xml(forward)]
|
||||
pub enum MessagePollResponse {
|
||||
/// Data under the <domain:trnData> tag
|
||||
#[serde(rename = "trnData")]
|
||||
DomainTransfer(DomainTransferResponseData),
|
||||
/// Data under the <host:infData> tag
|
||||
#[serde(rename = "infData")]
|
||||
HostInfo(HostInfoResponseData),
|
||||
/// Data under the <lowbalance> tag
|
||||
#[serde(rename = "pollData")]
|
||||
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)]
|
||||
mod tests {
|
||||
use super::MessagePoll;
|
||||
use crate::message::poll::MessageData;
|
||||
use super::{MessagePoll, MessagePollResponse};
|
||||
use crate::response::ResultCode;
|
||||
use crate::tests::{assert_serialized, response_from_file, CLTRID, SVTRID};
|
||||
|
||||
|
@ -81,7 +72,7 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
object.result.message,
|
||||
"Command completed successfully; ack to dequeue".into()
|
||||
"Command completed successfully; ack to dequeue"
|
||||
);
|
||||
assert_eq!(msg.count, 5);
|
||||
assert_eq!(msg.id, "12345".to_string());
|
||||
|
@ -89,20 +80,17 @@ mod tests {
|
|||
msg.date,
|
||||
Utc.with_ymd_and_hms(2021, 7, 23, 19, 12, 43).single()
|
||||
);
|
||||
assert_eq!(
|
||||
*(msg.message.as_ref().unwrap()),
|
||||
"Transfer requested.".into()
|
||||
);
|
||||
assert_eq!(msg.message.as_ref().unwrap().text, "Transfer requested.");
|
||||
|
||||
if let MessageData::DomainTransfer(tr) = &result.message_data {
|
||||
assert_eq!(tr.name, "eppdev-transfer.com".into());
|
||||
assert_eq!(tr.transfer_status, "pending".into());
|
||||
assert_eq!(tr.requester_id, "eppdev".into());
|
||||
if let MessagePollResponse::DomainTransfer(tr) = &result {
|
||||
assert_eq!(tr.name, "eppdev-transfer.com");
|
||||
assert_eq!(tr.transfer_status, "pending");
|
||||
assert_eq!(tr.requester_id, "eppdev");
|
||||
assert_eq!(
|
||||
tr.requested_at,
|
||||
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!(
|
||||
tr.ack_by,
|
||||
Utc.with_ymd_and_hms(2021, 7, 28, 15, 31, 21).unwrap()
|
||||
|
@ -115,8 +103,8 @@ mod tests {
|
|||
panic!("Wrong type");
|
||||
}
|
||||
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -131,7 +119,7 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
object.result.message,
|
||||
"Command completed successfully; ack to dequeue".into()
|
||||
"Command completed successfully; ack to dequeue"
|
||||
);
|
||||
assert_eq!(msg.count, 4);
|
||||
assert_eq!(msg.id, "12345".to_string());
|
||||
|
@ -139,22 +127,19 @@ mod tests {
|
|||
msg.date,
|
||||
Utc.with_ymd_and_hms(2022, 1, 2, 11, 30, 45).single()
|
||||
);
|
||||
assert_eq!(
|
||||
*(msg.message.as_ref().unwrap()),
|
||||
"Unused objects policy".into()
|
||||
);
|
||||
assert_eq!(msg.message.as_ref().unwrap().text, "Unused objects policy");
|
||||
|
||||
if let MessageData::HostInfo(host) = &result.message_data {
|
||||
assert_eq!(host.name, "ns.test.com".into());
|
||||
if let MessagePollResponse::HostInfo(host) = &result {
|
||||
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
|
||||
.addresses
|
||||
.iter()
|
||||
.any(|a| a == &IpAddr::from([1, 1, 1, 1])));
|
||||
assert_eq!(host.client_id, "1234".into());
|
||||
assert_eq!(host.creator_id, "user".into());
|
||||
assert_eq!(host.client_id, "1234");
|
||||
assert_eq!(host.creator_id, "user");
|
||||
assert_eq!(
|
||||
host.created_at,
|
||||
Utc.with_ymd_and_hms(2021, 12, 1, 22, 40, 48).unwrap()
|
||||
|
@ -168,14 +153,15 @@ mod tests {
|
|||
panic!("Wrong type");
|
||||
}
|
||||
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn message_only_response() {
|
||||
let object = response_from_file::<MessagePoll>("response/message/poll_message_only.xml");
|
||||
let msg = object.message_queue().unwrap();
|
||||
dbg!(&msg);
|
||||
|
||||
assert_eq!(
|
||||
object.result.code,
|
||||
|
@ -183,7 +169,7 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
object.result.message,
|
||||
"Command completed successfully; ack to dequeue".into()
|
||||
"Command completed successfully; ack to dequeue"
|
||||
);
|
||||
|
||||
assert_eq!(msg.count, 4);
|
||||
|
@ -192,13 +178,10 @@ mod tests {
|
|||
msg.date,
|
||||
Utc.with_ymd_and_hms(2000, 6, 8, 22, 10, 0).single()
|
||||
);
|
||||
assert_eq!(
|
||||
*(msg.message.as_ref().unwrap()),
|
||||
"Credit balance low.".into()
|
||||
);
|
||||
assert_eq!(msg.message.as_ref().unwrap().text, "Credit balance low.");
|
||||
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID.into());
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -211,10 +194,10 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
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.server_tr_id, SVTRID.into());
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
//! Types for EPP requests
|
||||
|
||||
use serde::{de::DeserializeOwned, ser::SerializeStruct, ser::Serializer, Serialize};
|
||||
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_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
|
||||
pub trait Transaction<Ext: Extension>: Command + Sized {}
|
||||
|
||||
pub trait Command: Serialize + Debug {
|
||||
type Response: DeserializeOwned + Debug;
|
||||
pub trait Command: ToXml + Debug {
|
||||
type Response: FromXmlOwned + Debug;
|
||||
const COMMAND: &'static str;
|
||||
}
|
||||
|
||||
pub trait Extension: Serialize + Debug {
|
||||
type Response: DeserializeOwned + Debug;
|
||||
pub trait Extension: ToXml + Debug {
|
||||
type Response: FromXmlOwned + Debug;
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
/// Type corresponding to the <command> tag in an EPP XML request
|
||||
/// with an <extension> tag
|
||||
struct CommandWrapper<'a, D, E> {
|
||||
pub(crate) struct CommandWrapper<'a, D, E> {
|
||||
pub command: &'static str,
|
||||
/// The instance that will be used to populate the <command> tag
|
||||
pub data: &'a D,
|
||||
/// The client TRID
|
||||
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> {
|
||||
/// Serializes the generic type T to the proper XML tag (set by the `#[element_name(name = <tagname>)]` attribute) for the request
|
||||
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,
|
||||
{
|
||||
impl<'a, E: Extension, D: Transaction<E>> CommandWrapper<'a, D, E> {
|
||||
pub(crate) fn new(data: &'a D, extension: Option<&'a E>, client_tr_id: &'a str) -> Self {
|
||||
Self {
|
||||
xmlns: EPP_XMLNS,
|
||||
command: CommandWrapper {
|
||||
command: Cmd::COMMAND,
|
||||
data,
|
||||
extension,
|
||||
client_tr_id: client_tr_id.into(),
|
||||
},
|
||||
command: D::COMMAND,
|
||||
data,
|
||||
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 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
|
||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
||||
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||
#[xml(rename = "undef", ns(EPP_XMLNS))]
|
||||
pub struct Undef;
|
||||
|
||||
/// 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 {
|
||||
/// The XML namespace for the <value> tag
|
||||
#[serde(rename = "xmlns:epp")]
|
||||
xmlns: String,
|
||||
/// The <undef> element
|
||||
pub undef: Undef,
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
/// Data under the <value> tag
|
||||
pub value: ResultValue,
|
||||
/// Data under the <reason> tag
|
||||
pub reason: StringValue<'static>,
|
||||
pub reason: String,
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
/// The result code
|
||||
#[xml(attribute)]
|
||||
pub code: ResultCode,
|
||||
/// The result message
|
||||
#[serde(rename = "msg")]
|
||||
pub message: StringValue<'static>,
|
||||
#[xml(rename = "msg")]
|
||||
pub message: String,
|
||||
/// Data under the <extValue> tag
|
||||
#[serde(rename = "extValue")]
|
||||
pub ext_value: Option<ExtValue>,
|
||||
}
|
||||
|
||||
|
@ -136,13 +137,37 @@ impl ResultCode {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for ResultCode {
|
||||
fn deserialize<D>(deserializer: D) -> Result<ResultCode, D::Error>
|
||||
where
|
||||
D: serde::de::Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_u16(ResultCodeVisitor)
|
||||
impl<'xml> FromXml<'xml> for ResultCode {
|
||||
fn matches(id: instant_xml::Id<'_>, field: Option<instant_xml::Id<'_>>) -> bool {
|
||||
match field {
|
||||
Some(field) => id == field,
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -166,71 +191,82 @@ impl<'de> serde::de::Visitor<'de> for ResultCodeVisitor {
|
|||
}
|
||||
|
||||
/// 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 {
|
||||
/// The client TRID
|
||||
#[serde(rename = "clTRID")]
|
||||
pub client_tr_id: Option<StringValue<'static>>,
|
||||
#[xml(rename = "clTRID")]
|
||||
pub client_tr_id: Option<String>,
|
||||
/// The server TRID
|
||||
#[serde(rename = "svTRID")]
|
||||
pub server_tr_id: StringValue<'static>,
|
||||
#[xml(rename = "svTRID")]
|
||||
pub server_tr_id: String,
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
/// The message count
|
||||
#[xml(attribute)]
|
||||
pub count: u32,
|
||||
/// The message ID
|
||||
#[xml(attribute)]
|
||||
pub id: String,
|
||||
/// The message date
|
||||
#[serde(rename = "qDate")]
|
||||
#[xml(rename = "qDate")]
|
||||
pub date: Option<DateTime<Utc>>,
|
||||
/// The message text
|
||||
#[serde(rename = "msg")]
|
||||
pub message: Option<StringValue<'static>>,
|
||||
#[xml(rename = "msg")]
|
||||
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
|
||||
/// containing an <extension> tag
|
||||
#[xml(rename = "response", ns(EPP_XMLNS))]
|
||||
pub struct Response<D, E> {
|
||||
/// Data under the <result> tag
|
||||
pub result: EppResult,
|
||||
/// Data under the <msgQ> tag
|
||||
#[serde(rename = "msgQ")]
|
||||
#[xml(rename = "msgQ")]
|
||||
pub message_queue: Option<MessageQueue>,
|
||||
#[serde(rename = "resData")]
|
||||
/// Data under the <resData> tag
|
||||
pub res_data: Option<D>,
|
||||
pub res_data: Option<ResponseData<D>>,
|
||||
/// Data under the <extension> tag
|
||||
pub extension: Option<E>,
|
||||
pub extension: Option<Extension<E>>,
|
||||
/// Data under the <trID> tag
|
||||
#[serde(rename = "trID")]
|
||||
pub tr_ids: ResponseTRID,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
||||
#[serde(rename = "epp")]
|
||||
pub struct ResponseDocument<D, E> {
|
||||
#[serde(rename = "response")]
|
||||
pub data: Response<D, E>,
|
||||
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||
#[xml(rename = "resData", ns(EPP_XMLNS))]
|
||||
pub struct ResponseData<D> {
|
||||
data: D,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Eq, PartialEq)]
|
||||
#[serde(rename = "epp")]
|
||||
pub struct ResultDocument {
|
||||
#[serde(rename = "response")]
|
||||
pub data: ResponseStatus,
|
||||
impl<D> ResponseData<D> {
|
||||
pub fn into_inner(self) -> D {
|
||||
self.data
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Eq, PartialEq)]
|
||||
#[derive(Debug, FromXml, PartialEq)]
|
||||
/// Type corresponding to the <response> tag in an EPP response XML
|
||||
/// without <msgQ> or <resData> sections. Generally used for error handling
|
||||
#[xml(rename = "response", ns(EPP_XMLNS))]
|
||||
pub struct ResponseStatus {
|
||||
/// Data under the <result> tag
|
||||
pub result: EppResult,
|
||||
#[serde(rename = "trID")]
|
||||
#[xml(rename = "trID")]
|
||||
/// Data under the <trID> tag
|
||||
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
|
||||
pub fn res_data(&self) -> Option<&T> {
|
||||
match &self.res_data {
|
||||
Some(res_data) => Some(res_data),
|
||||
Some(res_data) => Some(&res_data.data),
|
||||
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
|
||||
pub fn message_queue(&self) -> Option<&MessageQueue> {
|
||||
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)]
|
||||
mod tests {
|
||||
use super::{ResultCode, ResultDocument};
|
||||
use super::{ResponseStatus, ResultCode};
|
||||
use crate::tests::{get_xml, CLTRID, SVTRID};
|
||||
use crate::xml;
|
||||
|
||||
#[test]
|
||||
fn error() {
|
||||
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.data.result.message, "Object does not exist".into());
|
||||
assert_eq!(object.result.code, ResultCode::ObjectDoesNotExist);
|
||||
assert_eq!(object.result.message, "Object does not exist");
|
||||
assert_eq!(
|
||||
object.data.result.ext_value.unwrap().reason,
|
||||
"545 Object not found".into()
|
||||
object.result.ext_value.unwrap().reason,
|
||||
"545 Object not found"
|
||||
);
|
||||
assert_eq!(object.data.tr_ids.client_tr_id.unwrap(), CLTRID.into());
|
||||
assert_eq!(object.data.tr_ids.server_tr_id, SVTRID.into());
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,12 +3,13 @@
|
|||
use std::{error::Error, fs::File, io::Read};
|
||||
|
||||
use regex::Regex;
|
||||
use similar_asserts::assert_eq;
|
||||
|
||||
use crate::{
|
||||
client::RequestData,
|
||||
common::NoExtension,
|
||||
request::{Command, CommandDocument, Extension, Transaction},
|
||||
response::{Response, ResponseDocument},
|
||||
request::{Command, CommandWrapper, Extension, Transaction},
|
||||
response::Response,
|
||||
xml,
|
||||
};
|
||||
|
||||
|
@ -46,8 +47,8 @@ pub(crate) fn assert_serialized<'c, 'e, Cmd, Ext>(
|
|||
{
|
||||
let expected = get_xml(path).unwrap();
|
||||
let req = req.into();
|
||||
let document = CommandDocument::new(req.command, req.extension, CLTRID);
|
||||
assert_eq!(expected, xml::serialize(&document).unwrap());
|
||||
let document = CommandWrapper::new(req.command, req.extension, CLTRID);
|
||||
assert_eq!(expected, xml::serialize(document).unwrap());
|
||||
}
|
||||
|
||||
pub(crate) fn response_from_file<'c, Cmd>(
|
||||
|
@ -67,7 +68,8 @@ where
|
|||
Ext: Extension,
|
||||
{
|
||||
let xml = get_xml(path).unwrap();
|
||||
let rsp = xml::deserialize::<ResponseDocument<Cmd::Response, Ext::Response>>(&xml).unwrap();
|
||||
assert!(rsp.data.result.code.is_success());
|
||||
rsp.data
|
||||
dbg!(&xml);
|
||||
let rsp = xml::deserialize::<Response<Cmd::Response, Ext::Response>>(&xml).unwrap();
|
||||
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
|
||||
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use instant_xml::{FromXml, FromXmlOwned, ToXml};
|
||||
|
||||
use crate::common::EPP_XMLNS;
|
||||
use crate::error::Error;
|
||||
|
||||
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!(
|
||||
"{}\r\n{}",
|
||||
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> {
|
||||
quick_xml::de::from_str(xml).map_err(|e| Error::Xml(e.into()))
|
||||
pub(crate) fn deserialize<T: FromXmlOwned>(xml: &str) -> Result<T, Error> {
|
||||
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);
|
||||
|
||||
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]
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<check>
|
||||
<contact:check xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
|
||||
<contact:id>eppdev-contact-1</contact:id>
|
||||
<contact:id>eppdev-contact-2</contact:id>
|
||||
</contact:check>
|
||||
<check xmlns="urn:ietf:params:xml:ns:contact-1.0">
|
||||
<id>eppdev-contact-1</id>
|
||||
<id>eppdev-contact-2</id>
|
||||
</check>
|
||||
</check>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -2,27 +2,27 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<contact:create xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
|
||||
<contact:id>eppdev-contact-3</contact:id>
|
||||
<contact:postalInfo type="int">
|
||||
<contact:name>John Doe</contact:name>
|
||||
<contact:org>Acme Widgets</contact:org>
|
||||
<contact:addr>
|
||||
<contact:street>58</contact:street>
|
||||
<contact:street>Orchid Road</contact:street>
|
||||
<contact:city>Paris</contact:city>
|
||||
<contact:sp>Paris</contact:sp>
|
||||
<contact:pc>392374</contact:pc>
|
||||
<contact:cc>FR</contact:cc>
|
||||
</contact:addr>
|
||||
</contact:postalInfo>
|
||||
<contact:voice x="123">+33.47237942</contact:voice>
|
||||
<contact:fax x="677">+33.86698799</contact:fax>
|
||||
<contact:email>contact@eppdev.net</contact:email>
|
||||
<contact:authInfo>
|
||||
<contact:pw>eppdev-387323</contact:pw>
|
||||
</contact:authInfo>
|
||||
</contact:create>
|
||||
<create xmlns="urn:ietf:params:xml:ns:contact-1.0">
|
||||
<id>eppdev-contact-3</id>
|
||||
<postalInfo type="int">
|
||||
<name>John Doe</name>
|
||||
<org>Acme Widgets</org>
|
||||
<addr>
|
||||
<street>58</street>
|
||||
<street>Orchid Road</street>
|
||||
<city>Paris</city>
|
||||
<sp>Paris</sp>
|
||||
<pc>392374</pc>
|
||||
<cc>FR</cc>
|
||||
</addr>
|
||||
</postalInfo>
|
||||
<voice x="123">+33.47237942</voice>
|
||||
<fax x="677">+33.86698799</fax>
|
||||
<email>contact@eppdev.net</email>
|
||||
<authInfo>
|
||||
<pw>eppdev-387323</pw>
|
||||
</authInfo>
|
||||
</create>
|
||||
</create>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<delete>
|
||||
<contact:delete xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
|
||||
<contact:id>eppdev-contact-3</contact:id>
|
||||
</contact:delete>
|
||||
<delete xmlns="urn:ietf:params:xml:ns:contact-1.0">
|
||||
<id>eppdev-contact-3</id>
|
||||
</delete>
|
||||
</delete>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<info>
|
||||
<contact:info xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
|
||||
<contact:id>eppdev-contact-3</contact:id>
|
||||
<contact:authInfo>
|
||||
<contact:pw>eppdev-387323</contact:pw>
|
||||
</contact:authInfo>
|
||||
</contact:info>
|
||||
<info xmlns="urn:ietf:params:xml:ns:contact-1.0">
|
||||
<id>eppdev-contact-3</id>
|
||||
<authInfo>
|
||||
<pw>eppdev-387323</pw>
|
||||
</authInfo>
|
||||
</info>
|
||||
</info>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -2,34 +2,34 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<update>
|
||||
<contact:update xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
|
||||
<contact:id>eppdev-contact-3</contact:id>
|
||||
<contact:add>
|
||||
<contact:status s="clientTransferProhibited"/>
|
||||
</contact:add>
|
||||
<contact:rem>
|
||||
<contact:status s="clientDeleteProhibited"/>
|
||||
</contact:rem>
|
||||
<contact:chg>
|
||||
<contact:postalInfo type="loc">
|
||||
<contact:name>John Doe</contact:name>
|
||||
<contact:org>Acme Widgets</contact:org>
|
||||
<contact:addr>
|
||||
<contact:street>58</contact:street>
|
||||
<contact:street>Orchid Road</contact:street>
|
||||
<contact:city>Paris</contact:city>
|
||||
<contact:sp>Paris</contact:sp>
|
||||
<contact:pc>392374</contact:pc>
|
||||
<contact:cc>FR</contact:cc>
|
||||
</contact:addr>
|
||||
</contact:postalInfo>
|
||||
<contact:voice>+33.47237942</contact:voice>
|
||||
<contact:email>newemail@eppdev.net</contact:email>
|
||||
<contact:authInfo>
|
||||
<contact:pw>eppdev-387323</contact:pw>
|
||||
</contact:authInfo>
|
||||
</contact:chg>
|
||||
</contact:update>
|
||||
<update xmlns="urn:ietf:params:xml:ns:contact-1.0">
|
||||
<id>eppdev-contact-3</id>
|
||||
<add>
|
||||
<status s="clientTransferProhibited"></status>
|
||||
</add>
|
||||
<rem>
|
||||
<status s="clientDeleteProhibited"></status>
|
||||
</rem>
|
||||
<chg>
|
||||
<postalInfo type="loc">
|
||||
<name>John Doe</name>
|
||||
<org>Acme Widgets</org>
|
||||
<addr>
|
||||
<street>58</street>
|
||||
<street>Orchid Road</street>
|
||||
<city>Paris</city>
|
||||
<sp>Paris</sp>
|
||||
<pc>392374</pc>
|
||||
<cc>FR</cc>
|
||||
</addr>
|
||||
</postalInfo>
|
||||
<voice>+33.47237942</voice>
|
||||
<email>newemail@eppdev.net</email>
|
||||
<authInfo>
|
||||
<pw>eppdev-387323</pw>
|
||||
</authInfo>
|
||||
</chg>
|
||||
</update>
|
||||
</update>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<check>
|
||||
<domain:check xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>eppdev.com</domain:name>
|
||||
<domain:name>eppdev.net</domain:name>
|
||||
</domain:check>
|
||||
<check xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<name>eppdev.com</name>
|
||||
<name>eppdev.net</name>
|
||||
</check>
|
||||
</check>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -2,17 +2,17 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<domain:create xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>eppdev-1.com</domain:name>
|
||||
<domain:period unit="y">1</domain:period>
|
||||
<domain:registrant>eppdev-contact-3</domain:registrant>
|
||||
<domain:contact type="admin">eppdev-contact-3</domain:contact>
|
||||
<domain:contact type="tech">eppdev-contact-3</domain:contact>
|
||||
<domain:contact type="billing">eppdev-contact-3</domain:contact>
|
||||
<domain:authInfo>
|
||||
<domain:pw>epP4uthd#v</domain:pw>
|
||||
</domain:authInfo>
|
||||
</domain:create>
|
||||
<create xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<name>eppdev-1.com</name>
|
||||
<period unit="y">1</period>
|
||||
<registrant>eppdev-contact-3</registrant>
|
||||
<contact type="admin">eppdev-contact-3</contact>
|
||||
<contact type="tech">eppdev-contact-3</contact>
|
||||
<contact type="billing">eppdev-contact-3</contact>
|
||||
<authInfo>
|
||||
<pw>epP4uthd#v</pw>
|
||||
</authInfo>
|
||||
</create>
|
||||
</create>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -2,27 +2,27 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<domain:create xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>eppdev-2.com</domain:name>
|
||||
<domain:period unit="y">1</domain:period>
|
||||
<domain:ns>
|
||||
<domain:hostAttr>
|
||||
<domain:hostName>ns1.eppdev-1.com</domain:hostName>
|
||||
</domain:hostAttr>
|
||||
<domain:hostAttr>
|
||||
<domain:hostName>ns2.eppdev-1.com</domain:hostName>
|
||||
<domain:hostAddr ip="v4">177.232.12.58</domain:hostAddr>
|
||||
<domain:hostAddr ip="v6">2404:6800:4001:801::200e</domain:hostAddr>
|
||||
</domain:hostAttr>
|
||||
</domain:ns>
|
||||
<domain:registrant>eppdev-contact-3</domain:registrant>
|
||||
<domain:contact type="admin">eppdev-contact-3</domain:contact>
|
||||
<domain:contact type="tech">eppdev-contact-3</domain:contact>
|
||||
<domain:contact type="billing">eppdev-contact-3</domain:contact>
|
||||
<domain:authInfo>
|
||||
<domain:pw>epP4uthd#v</domain:pw>
|
||||
</domain:authInfo>
|
||||
</domain:create>
|
||||
<create xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<name>eppdev-2.com</name>
|
||||
<period unit="y">1</period>
|
||||
<ns>
|
||||
<hostAttr>
|
||||
<hostName>ns1.eppdev-1.com</hostName>
|
||||
</hostAttr>
|
||||
<hostAttr>
|
||||
<hostName>ns2.eppdev-1.com</hostName>
|
||||
<hostAddr ip="v4">177.232.12.58</hostAddr>
|
||||
<hostAddr ip="v6">2404:6800:4001:801::200e</hostAddr>
|
||||
</hostAttr>
|
||||
</ns>
|
||||
<registrant>eppdev-contact-3</registrant>
|
||||
<contact type="admin">eppdev-contact-3</contact>
|
||||
<contact type="tech">eppdev-contact-3</contact>
|
||||
<contact type="billing">eppdev-contact-3</contact>
|
||||
<authInfo>
|
||||
<pw>epP4uthd#v</pw>
|
||||
</authInfo>
|
||||
</create>
|
||||
</create>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -2,21 +2,21 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<domain:create xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>eppdev-1.com</domain:name>
|
||||
<domain:period unit="y">1</domain:period>
|
||||
<domain:ns>
|
||||
<domain:hostObj>ns1.test.com</domain:hostObj>
|
||||
<domain:hostObj>ns2.test.com</domain:hostObj>
|
||||
</domain:ns>
|
||||
<domain:registrant>eppdev-contact-3</domain:registrant>
|
||||
<domain:contact type="admin">eppdev-contact-3</domain:contact>
|
||||
<domain:contact type="tech">eppdev-contact-3</domain:contact>
|
||||
<domain:contact type="billing">eppdev-contact-3</domain:contact>
|
||||
<domain:authInfo>
|
||||
<domain:pw>epP4uthd#v</domain:pw>
|
||||
</domain:authInfo>
|
||||
</domain:create>
|
||||
<create xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<name>eppdev-1.com</name>
|
||||
<period unit="y">1</period>
|
||||
<ns>
|
||||
<hostObj>ns1.test.com</hostObj>
|
||||
<hostObj>ns2.test.com</hostObj>
|
||||
</ns>
|
||||
<registrant>eppdev-contact-3</registrant>
|
||||
<contact type="admin">eppdev-contact-3</contact>
|
||||
<contact type="tech">eppdev-contact-3</contact>
|
||||
<contact type="billing">eppdev-contact-3</contact>
|
||||
<authInfo>
|
||||
<pw>epP4uthd#v</pw>
|
||||
</authInfo>
|
||||
</create>
|
||||
</create>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<delete>
|
||||
<domain:delete xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>eppdev.com</domain:name>
|
||||
</domain:delete>
|
||||
<delete xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<name>eppdev.com</name>
|
||||
</delete>
|
||||
</delete>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<info>
|
||||
<domain:info xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name hosts="all">eppdev.com</domain:name>
|
||||
<domain:authInfo>
|
||||
<domain:pw>2fooBAR</domain:pw>
|
||||
</domain:authInfo>
|
||||
</domain:info>
|
||||
<info xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<name hosts="all">eppdev.com</name>
|
||||
<authInfo>
|
||||
<pw>2fooBAR</pw>
|
||||
</authInfo>
|
||||
</info>
|
||||
</info>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<renew>
|
||||
<domain:renew xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>eppdev.com</domain:name>
|
||||
<domain:curExpDate>2022-07-23</domain:curExpDate>
|
||||
<domain:period unit="y">1</domain:period>
|
||||
</domain:renew>
|
||||
<renew xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<name>eppdev.com</name>
|
||||
<curExpDate>2022-07-23</curExpDate>
|
||||
<period unit="y">1</period>
|
||||
</renew>
|
||||
</renew>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<transfer op="approve">
|
||||
<domain:transfer xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>testing.com</domain:name>
|
||||
</domain:transfer>
|
||||
<transfer xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<name>testing.com</name>
|
||||
</transfer>
|
||||
</transfer>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<transfer op="cancel">
|
||||
<domain:transfer xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>testing.com</domain:name>
|
||||
</domain:transfer>
|
||||
<transfer xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<name>testing.com</name>
|
||||
</transfer>
|
||||
</transfer>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<transfer op="query">
|
||||
<domain:transfer xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>testing.com</domain:name>
|
||||
<domain:authInfo>
|
||||
<domain:pw>epP4uthd#v</domain:pw>
|
||||
</domain:authInfo>
|
||||
</domain:transfer>
|
||||
<transfer xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<name>testing.com</name>
|
||||
<authInfo>
|
||||
<pw>epP4uthd#v</pw>
|
||||
</authInfo>
|
||||
</transfer>
|
||||
</transfer>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<transfer op="reject">
|
||||
<domain:transfer xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>testing.com</domain:name>
|
||||
</domain:transfer>
|
||||
<transfer xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<name>testing.com</name>
|
||||
</transfer>
|
||||
</transfer>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<transfer op="request">
|
||||
<domain:transfer xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>testing.com</domain:name>
|
||||
<domain:period unit="y">1</domain:period>
|
||||
<domain:authInfo>
|
||||
<domain:pw>epP4uthd#v</domain:pw>
|
||||
</domain:authInfo>
|
||||
</domain:transfer>
|
||||
<transfer xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<name>testing.com</name>
|
||||
<period unit="y">1</period>
|
||||
<authInfo>
|
||||
<pw>epP4uthd#v</pw>
|
||||
</authInfo>
|
||||
</transfer>
|
||||
</transfer>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -2,20 +2,20 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<update>
|
||||
<domain:update xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>eppdev.com</domain:name>
|
||||
<domain:add>
|
||||
<domain:status s="clientDeleteProhibited"/>
|
||||
</domain:add>
|
||||
<domain:rem>
|
||||
<domain:contact type="billing">eppdev-contact-2</domain:contact>
|
||||
</domain:rem>
|
||||
<domain:chg>
|
||||
<domain:authInfo>
|
||||
<domain:pw>epP5uthd#v</domain:pw>
|
||||
</domain:authInfo>
|
||||
</domain:chg>
|
||||
</domain:update>
|
||||
<update xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<name>eppdev.com</name>
|
||||
<add>
|
||||
<status s="clientDeleteProhibited"></status>
|
||||
</add>
|
||||
<rem>
|
||||
<contact type="billing">eppdev-contact-2</contact>
|
||||
</rem>
|
||||
<chg>
|
||||
<authInfo>
|
||||
<pw>epP5uthd#v</pw>
|
||||
</authInfo>
|
||||
</chg>
|
||||
</update>
|
||||
</update>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -2,15 +2,15 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<update>
|
||||
<domain:update xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>eppdev.com</domain:name>
|
||||
<domain:chg/>
|
||||
</domain:update>
|
||||
<update xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<name>eppdev.com</name>
|
||||
<chg></chg>
|
||||
</update>
|
||||
</update>
|
||||
<extension>
|
||||
<sync:update xmlns:sync="http://www.verisign.com/epp/sync-1.0">
|
||||
<sync:expMonthDay>--05-31</sync:expMonthDay>
|
||||
</sync:update>
|
||||
<update xmlns="http://www.verisign.com/epp/sync-1.0">
|
||||
<expMonthDay>--05-31</expMonthDay>
|
||||
</update>
|
||||
</extension>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -2,18 +2,18 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<update>
|
||||
<domain:update xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>eppdev.com</domain:name>
|
||||
<domain:chg/>
|
||||
</domain:update>
|
||||
<update xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<name>eppdev.com</name>
|
||||
<chg></chg>
|
||||
</update>
|
||||
</update>
|
||||
<extension>
|
||||
<sync:update xmlns:sync="http://www.verisign.com/epp/sync-1.0">
|
||||
<sync:expMonthDay>--05-31</sync:expMonthDay>
|
||||
</sync:update>
|
||||
<namestoreExt:namestoreExt xmlns:namestoreExt="http://www.verisign-grs.com/epp/namestoreExt-1.1">
|
||||
<namestoreExt:subProduct>com</namestoreExt:subProduct>
|
||||
</namestoreExt:namestoreExt>
|
||||
<update xmlns="http://www.verisign.com/epp/sync-1.0">
|
||||
<expMonthDay>--05-31</expMonthDay>
|
||||
</update>
|
||||
<namestoreExt xmlns="http://www.verisign-grs.com/epp/namestoreExt-1.1">
|
||||
<subProduct>com</subProduct>
|
||||
</namestoreExt>
|
||||
</extension>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -2,16 +2,16 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<check>
|
||||
<domain:check xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>example1.com</domain:name>
|
||||
<domain:name>example2.com</domain:name>
|
||||
<domain:name>example3.com</domain:name>
|
||||
</domain:check>
|
||||
<check xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<name>example1.com</name>
|
||||
<name>example2.com</name>
|
||||
<name>example3.com</name>
|
||||
</check>
|
||||
</check>
|
||||
<extension>
|
||||
<namestoreExt:namestoreExt xmlns:namestoreExt="http://www.verisign-grs.com/epp/namestoreExt-1.1">
|
||||
<namestoreExt:subProduct>com</namestoreExt:subProduct>
|
||||
</namestoreExt:namestoreExt>
|
||||
<namestoreExt xmlns="http://www.verisign-grs.com/epp/namestoreExt-1.1">
|
||||
<subProduct>com</subProduct>
|
||||
</namestoreExt>
|
||||
</extension>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -2,26 +2,26 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<update>
|
||||
<domain:update xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>eppdev.com</domain:name>
|
||||
<domain:chg/>
|
||||
</domain:update>
|
||||
<update xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<name>eppdev.com</name>
|
||||
<chg></chg>
|
||||
</update>
|
||||
</update>
|
||||
<extension>
|
||||
<rgp:update xmlns:rgp="urn:ietf:params:xml:ns:rgp-1.0">
|
||||
<rgp:restore op="report">
|
||||
<rgp:report>
|
||||
<rgp:preData>Pre-delete registration data goes here. Both XML and free text are allowed.</rgp:preData>
|
||||
<rgp:postData>Post-restore registration data goes here. Both XML and free text are allowed.</rgp:postData>
|
||||
<rgp:delTime>2021-07-10T22:00:00Z</rgp:delTime>
|
||||
<rgp:resTime>2021-07-20T22:00:00Z</rgp:resTime>
|
||||
<rgp:resReason>Registrant error.</rgp: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>
|
||||
<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>
|
||||
<rgp:other>Supporting information goes here.</rgp:other>
|
||||
</rgp:report>
|
||||
</rgp:restore>
|
||||
</rgp:update>
|
||||
<update xmlns="urn:ietf:params:xml:ns:rgp-1.0">
|
||||
<restore op="report">
|
||||
<report>
|
||||
<preData>Pre-delete registration data goes here. Both XML and free text are allowed.</preData>
|
||||
<postData>Post-restore registration data goes here. Both XML and free text are allowed.</postData>
|
||||
<delTime>2021-07-10T22:00:00Z</delTime>
|
||||
<resTime>2021-07-20T22:00:00Z</resTime>
|
||||
<resReason>Registrant error.</resReason>
|
||||
<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>
|
||||
<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>
|
||||
<other>Supporting information goes here.</other>
|
||||
</report>
|
||||
</restore>
|
||||
</update>
|
||||
</extension>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -2,15 +2,15 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<update>
|
||||
<domain:update xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>eppdev.com</domain:name>
|
||||
<domain:chg/>
|
||||
</domain:update>
|
||||
<update xmlns="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<name>eppdev.com</name>
|
||||
<chg></chg>
|
||||
</update>
|
||||
</update>
|
||||
<extension>
|
||||
<rgp:update xmlns:rgp="urn:ietf:params:xml:ns:rgp-1.0">
|
||||
<rgp:restore op="request"/>
|
||||
</rgp:update>
|
||||
<update xmlns="urn:ietf:params:xml:ns:rgp-1.0">
|
||||
<restore op="request"></restore>
|
||||
</update>
|
||||
</extension>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<hello/>
|
||||
<hello />
|
||||
</epp>
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<check>
|
||||
<host:check xmlns:host="urn:ietf:params:xml:ns:host-1.0">
|
||||
<host:name>ns1.eppdev-1.com</host:name>
|
||||
<host:name>host1.eppdev-1.com</host:name>
|
||||
</host:check>
|
||||
<check xmlns="urn:ietf:params:xml:ns:host-1.0">
|
||||
<name>ns1.eppdev-1.com</name>
|
||||
<name>host1.eppdev-1.com</name>
|
||||
</check>
|
||||
</check>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<host:create xmlns:host="urn:ietf:params:xml:ns:host-1.0">
|
||||
<host:name>host1.eppdev-1.com</host:name>
|
||||
<host:addr ip="v4">29.245.122.14</host:addr>
|
||||
<host:addr ip="v6">2404:6800:4001:801::200e</host:addr>
|
||||
</host:create>
|
||||
<create xmlns="urn:ietf:params:xml:ns:host-1.0">
|
||||
<name>host1.eppdev-1.com</name>
|
||||
<addr ip="v4">29.245.122.14</addr>
|
||||
<addr ip="v6">2404:6800:4001:801::200e</addr>
|
||||
</create>
|
||||
</create>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<delete>
|
||||
<host:delete xmlns:host="urn:ietf:params:xml:ns:host-1.0">
|
||||
<host:name>ns1.eppdev-1.com</host:name>
|
||||
</host:delete>
|
||||
<delete xmlns="urn:ietf:params:xml:ns:host-1.0">
|
||||
<name>ns1.eppdev-1.com</name>
|
||||
</delete>
|
||||
</delete>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<info>
|
||||
<host:info xmlns:host="urn:ietf:params:xml:ns:host-1.0">
|
||||
<host:name>ns1.eppdev-1.com</host:name>
|
||||
</host:info>
|
||||
<info xmlns="urn:ietf:params:xml:ns:host-1.0">
|
||||
<name>ns1.eppdev-1.com</name>
|
||||
</info>
|
||||
</info>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -2,18 +2,18 @@
|
|||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<update>
|
||||
<host:update xmlns:host="urn:ietf:params:xml:ns:host-1.0">
|
||||
<host:name>host1.eppdev-1.com</host:name>
|
||||
<host:add>
|
||||
<host:addr ip="v6">2404:6800:4001:801::200e</host:addr>
|
||||
</host:add>
|
||||
<host:rem>
|
||||
<host:status s="clientDeleteProhibited"/>
|
||||
</host:rem>
|
||||
<host:chg>
|
||||
<host:name>host2.eppdev-1.com</host:name>
|
||||
</host:chg>
|
||||
</host:update>
|
||||
<update xmlns="urn:ietf:params:xml:ns:host-1.0">
|
||||
<name>host1.eppdev-1.com</name>
|
||||
<add>
|
||||
<addr ip="v6">2404:6800:4001:801::200e</addr>
|
||||
</add>
|
||||
<rem>
|
||||
<status s="clientDeleteProhibited"></status>
|
||||
</rem>
|
||||
<chg>
|
||||
<name>host2.eppdev-1.com</name>
|
||||
</chg>
|
||||
</update>
|
||||
</update>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<logout/>
|
||||
<logout />
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
</epp>
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<poll op="ack" msgID="12345"/>
|
||||
<poll op="ack" msgID="12345"></poll>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
</epp>
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<poll op="req"/>
|
||||
<poll op="req"></poll>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
</epp>
|
|
@ -21,15 +21,6 @@
|
|||
<host:upDate>2021-12-01T22:40:48Z</host:upDate>
|
||||
</host:infData>
|
||||
</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>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
<svTRID>RO-6879-1627224678242975</svTRID>
|
||||
|
|
Loading…
Reference in New Issue