Use different type wrappers instead of single EppObject

This commit is contained in:
Dirkjan Ochtman 2021-12-08 16:38:58 +01:00 committed by masalachai
parent 57d60807d8
commit ce30f1599f
13 changed files with 211 additions and 194 deletions

View File

@ -45,10 +45,9 @@
use std::{error::Error, fmt::Debug}; use std::{error::Error, fmt::Debug};
use crate::common::EppObject;
use crate::config::EppClientConfig; use crate::config::EppClientConfig;
use crate::error; use crate::error;
use crate::hello::{Greeting, Hello}; use crate::hello::{Greeting, GreetingDocument, HelloDocument};
use crate::registry::{epp_connect, EppConnection}; use crate::registry::{epp_connect, EppConnection};
use crate::request::{EppExtension, EppRequest}; use crate::request::{EppExtension, EppRequest};
use crate::response::Response; use crate::response::Response;
@ -81,11 +80,11 @@ impl EppClient {
/// Executes an EPP Hello call and returns the response as an `Greeting` /// Executes an EPP Hello call and returns the response as an `Greeting`
pub async fn hello(&mut self) -> Result<Greeting, Box<dyn Error>> { pub async fn hello(&mut self) -> Result<Greeting, Box<dyn Error>> {
let hello_xml = EppObject::<Hello>::build(Hello).serialize()?; let hello_xml = HelloDocument::default().serialize()?;
let response = self.connection.transact(&hello_xml).await?; let response = self.connection.transact(&hello_xml).await?;
Ok(EppObject::<Greeting>::deserialize(&response)?.data) Ok(GreetingDocument::deserialize(&response)?.data)
} }
pub async fn transact<T, E>( pub async fn transact<T, E>(
@ -117,6 +116,6 @@ impl EppClient {
/// Returns the greeting received on establishment of the connection as an `Greeting` /// Returns the greeting received on establishment of the connection as an `Greeting`
pub fn greeting(&self) -> Result<Greeting, error::Error> { pub fn greeting(&self) -> Result<Greeting, error::Error> {
EppObject::<Greeting>::deserialize(&self.connection.greeting).map(|obj| obj.data) GreetingDocument::deserialize(&self.connection.greeting).map(|obj| obj.data)
} }
} }

View File

@ -3,11 +3,11 @@
use std::{fmt::Display, str::FromStr}; use std::{fmt::Display, str::FromStr};
use epp_client_macros::ElementName; use epp_client_macros::ElementName;
use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer}; use serde::{Deserialize, Serialize};
use crate::request::EppExtension; use crate::request::EppExtension;
const EPP_XMLNS: &str = "urn:ietf:params:xml:ns:epp-1.0"; pub(crate) const EPP_XMLNS: &str = "urn:ietf:params:xml:ns:epp-1.0";
/// Wraps String for easier serialization to and from values that are inner text /// Wraps String for easier serialization to and from values that are inner text
/// for tags rather than attributes /// for tags rather than attributes
@ -46,33 +46,6 @@ impl EppExtension for NoExtension {
type Response = NoExtension; type Response = NoExtension;
} }
/// An EPP XML Document that is used either as an EPP XML request or
/// an EPP XML response
#[derive(Deserialize, Debug, PartialEq)]
#[serde(rename = "epp")]
pub struct EppObject<T: ElementName> {
/// XML namespace for the &lt;epp&gt; tag
pub xmlns: String,
/// the request or response object that is set or received in the EPP XML document
#[serde(alias = "greeting", alias = "response")]
pub data: T,
// TODO: save serialized xml in the instance for debugging or client logging purposes
// #[serde(skip)]
// pub xml: Option<String>,
}
impl<T: ElementName + Serialize> Serialize for EppObject<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("epp", 4)?;
state.serialize_field("xmlns", &self.xmlns)?;
state.serialize_field(T::ELEMENT, &self.data)?;
state.end()
}
}
/// The <option> type in EPP XML login requests /// The <option> type in EPP XML login requests
#[derive(Serialize, Deserialize, Debug, PartialEq)] #[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename = "options")] #[serde(rename = "options")]
@ -93,26 +66,6 @@ impl Options {
} }
} }
/// Type representing the &lt;extension&gt; tag for an EPP document
#[derive(Deserialize, Debug, PartialEq)]
#[serde(rename = "extension")]
pub struct Extension<E: ElementName> {
/// Data under the &lt;extension&gt; tag
#[serde(alias = "upData", alias = "namestoreExt", alias = "infData")]
pub data: E,
}
impl<E: ElementName + Serialize> Serialize for Extension<E> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut state = serializer.serialize_struct("extension", 1)?;
state.serialize_field(E::ELEMENT, &self.data)?;
state.end()
}
}
/// The <svcExtension> type in EPP XML /// The <svcExtension> type in EPP XML
#[derive(Serialize, Deserialize, Debug, PartialEq)] #[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename = "svcExtension")] #[serde(rename = "svcExtension")]
@ -133,17 +86,6 @@ pub struct Services {
pub svc_ext: Option<ServiceExtension>, pub svc_ext: Option<ServiceExtension>,
} }
impl<T: ElementName> EppObject<T> {
/// Create the enclosing EPP XML tag &lt;epp&gt; for data that represents an EPP XML request or response
pub fn build(data: T) -> EppObject<T> {
EppObject {
// xml: None,
data,
xmlns: EPP_XMLNS.to_string(),
}
}
}
/// The &lt;status&gt; attribute on EPP XML for domain transactions /// The &lt;status&gt; attribute on EPP XML for domain transactions
pub type DomainStatus = ContactStatus; pub type DomainStatus = ContactStatus;
/// The &lt;status&gt; attribute on EPP XML for host transactions /// The &lt;status&gt; attribute on EPP XML for host transactions

View File

@ -3,11 +3,10 @@
use std::fmt; use std::fmt;
use chrono::FixedOffset; use chrono::FixedOffset;
use epp_client_macros::ElementName;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
common::{ElementName, NoExtension, StringValue}, common::{NoExtension, StringValue},
request::EppExtension, request::EppExtension,
}; };
@ -64,7 +63,6 @@ impl fmt::Display for GMonthDay {
/// use epp_client::config::{EppClientConfig, RegistryConfig}; /// use epp_client::config::{EppClientConfig, RegistryConfig};
/// use epp_client::EppClient; /// use epp_client::EppClient;
/// use epp_client::common::{DomainStatus, DomainContact}; /// use epp_client::common::{DomainStatus, DomainContact};
/// use epp_client::extensions::consolidate::Sync;
/// use epp_client::domain::update::DomainUpdate; /// use epp_client::domain::update::DomainUpdate;
/// use epp_client::extensions::consolidate; /// use epp_client::extensions::consolidate;
/// use epp_client::extensions::consolidate::GMonthDay; /// use epp_client::extensions::consolidate::GMonthDay;
@ -98,10 +96,10 @@ impl fmt::Display for GMonthDay {
/// client.transact(login, "transaction-id").await.unwrap(); /// client.transact(login, "transaction-id").await.unwrap();
/// ///
/// let exp = GMonthDay::new(5, 31, None).unwrap(); /// let exp = GMonthDay::new(5, 31, None).unwrap();
/// let consolidate_ext = consolidate::Sync::new(exp); /// let consolidate_ext = consolidate::Update::new(exp);
/// ///
/// // Create an DomainUpdate instance /// // Create an DomainUpdate instance
/// let mut domain_update = DomainUpdate::<consolidate::Sync>::new("eppdev-100.com").with_extension(consolidate_ext); /// let mut domain_update = DomainUpdate::<consolidate::Update>::new("eppdev-100.com").with_extension(consolidate_ext);
/// ///
/// // send it to the registry and receive a response of type EppDomainUpdateResponse /// // send it to the registry and receive a response of type EppDomainUpdateResponse
/// let response = client.transact(domain_update, "transaction-id").await.unwrap(); /// let response = client.transact(domain_update, "transaction-id").await.unwrap();
@ -112,24 +110,32 @@ impl fmt::Display for GMonthDay {
/// client.transact(logout, "transaction-id").await.unwrap(); /// client.transact(logout, "transaction-id").await.unwrap();
/// } /// }
/// ``` /// ```
impl Sync { impl Update {
/// Create a new RGP restore report request /// Create a new RGP restore report request
pub fn new(expiration: GMonthDay) -> Sync { pub fn new(expiration: GMonthDay) -> Self {
Sync { Self {
xmlns: XMLNS.to_string(), data: UpdateData {
exp: expiration.to_string().into(), xmlns: XMLNS.to_string(),
exp: expiration.to_string().into(),
},
} }
} }
} }
impl EppExtension for Sync { impl EppExtension for Update {
type Response = NoExtension; type Response = NoExtension;
} }
#[derive(Serialize, Deserialize, Debug, ElementName)] #[derive(Debug, Deserialize, Serialize)]
#[element_name(name = "sync:update")] #[serde(rename = "extension")]
pub struct Update {
#[serde(rename = "sync:update")]
pub data: UpdateData,
}
#[derive(Serialize, Deserialize, Debug)]
/// Type for EPP XML &lt;consolidate&gt; extension /// Type for EPP XML &lt;consolidate&gt; extension
pub struct Sync { pub struct UpdateData {
/// XML namespace for the consolidate extension /// XML namespace for the consolidate extension
#[serde(rename = "xmlns:sync", alias = "xmlns")] #[serde(rename = "xmlns:sync", alias = "xmlns")]
pub xmlns: String, pub xmlns: String,

View File

@ -1,12 +1,8 @@
//! Types for EPP namestore request and responses //! Types for EPP namestore request and responses
use epp_client_macros::ElementName;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{common::StringValue, request::EppExtension};
common::{ElementName, StringValue},
request::EppExtension,
};
pub const XMLNS: &str = "http://www.verisign-grs.com/epp/namestoreExt-1.1"; pub const XMLNS: &str = "http://www.verisign-grs.com/epp/namestoreExt-1.1";
@ -68,8 +64,10 @@ impl NameStore {
/// Create a new RGP restore report request /// Create a new RGP restore report request
pub fn new(subproduct: &str) -> NameStore { pub fn new(subproduct: &str) -> NameStore {
NameStore { NameStore {
xmlns: XMLNS.to_string(), data: NameStoreData {
subproduct: subproduct.into(), xmlns: XMLNS.to_string(),
subproduct: subproduct.into(),
},
} }
} }
} }
@ -78,10 +76,16 @@ impl EppExtension for NameStore {
type Response = NameStore; type Response = NameStore;
} }
#[derive(Serialize, Deserialize, Debug, ElementName)] #[derive(Debug, Deserialize, Serialize)]
#[element_name(name = "namestoreExt:namestoreExt")] #[serde(rename = "namestoreExt:namestoreExt")]
/// Type for EPP XML &lt;namestoreExt&gt; extension
pub struct NameStore { pub struct NameStore {
#[serde(rename = "namestoreExt:namestoreExt", alias = "namestoreExt")]
pub data: NameStoreData,
}
#[derive(Serialize, Deserialize, Debug)]
/// Type for EPP XML &lt;namestoreExt&gt; extension
pub struct NameStoreData {
/// XML namespace for the RGP restore extension /// XML namespace for the RGP restore extension
#[serde(rename = "xmlns:namestoreExt", alias = "xmlns")] #[serde(rename = "xmlns:namestoreExt", alias = "xmlns")]
pub xmlns: String, pub xmlns: String,

View File

@ -7,7 +7,7 @@ use crate::request::EppExtension;
use chrono::{DateTime, SecondsFormat, Utc}; use chrono::{DateTime, SecondsFormat, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use super::XMLNS; use super::{Update, XMLNS};
/// Type that represents the domain rgp restore report extension /// Type that represents the domain rgp restore report extension
/// ///
@ -19,7 +19,7 @@ use super::XMLNS;
/// use epp_client::config::{EppClientConfig, RegistryConfig}; /// use epp_client::config::{EppClientConfig, RegistryConfig};
/// use epp_client::EppClient; /// use epp_client::EppClient;
/// use epp_client::common::{DomainStatus, DomainContact}; /// use epp_client::common::{DomainStatus, DomainContact};
/// use epp_client::extensions::rgp::report::RgpRestoreReport; /// use epp_client::extensions::rgp::{Update, report::RgpRestoreReport};
/// use epp_client::domain::update::DomainUpdate; /// use epp_client::domain::update::DomainUpdate;
/// use epp_client::common::NoExtension; /// use epp_client::common::NoExtension;
/// use epp_client::login::Login; /// use epp_client::login::Login;
@ -63,7 +63,7 @@ use super::XMLNS;
/// ]; /// ];
/// let other = "Supporting information goes here."; /// let other = "Supporting information goes here.";
/// ///
/// let domain_restore_report = RgpRestoreReport::new( /// let domain_restore_report = Update { data: RgpRestoreReport::new(
/// pre_data, /// pre_data,
/// post_data, /// post_data,
/// deleted_at, /// deleted_at,
@ -71,10 +71,10 @@ use super::XMLNS;
/// restore_reason, /// restore_reason,
/// &statements, /// &statements,
/// other /// other
/// ); /// ) };
/// ///
/// // Create an DomainUpdate instance /// // Create an DomainUpdate instance
/// let mut domain_update = DomainUpdate::<RgpRestoreReport>::new("eppdev-100.com").with_extension(domain_restore_report); /// let mut domain_update = DomainUpdate::<Update<RgpRestoreReport>>::new("eppdev-100.com").with_extension(domain_restore_report);
/// ///
/// // send it to the registry and receive a response of type EppDomainUpdateResponse /// // send it to the registry and receive a response of type EppDomainUpdateResponse
/// let response = client.transact(domain_update, "transaction-id").await.unwrap(); /// let response = client.transact(domain_update, "transaction-id").await.unwrap();
@ -120,7 +120,7 @@ impl RgpRestoreReport {
} }
} }
impl EppExtension for RgpRestoreReport { impl EppExtension for Update<RgpRestoreReport> {
type Response = NoExtension; type Response = NoExtension;
} }

View File

@ -8,7 +8,7 @@ use crate::request::EppExtension;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use super::XMLNS; use super::{Update, XMLNS};
/// Type that represents the &lt;epp&gt; request for a domain rgp restore request command /// Type that represents the &lt;epp&gt; request for a domain rgp restore request command
/// ///
@ -19,7 +19,7 @@ use super::XMLNS;
/// ///
/// use epp_client::config::{EppClientConfig, RegistryConfig}; /// use epp_client::config::{EppClientConfig, RegistryConfig};
/// use epp_client::EppClient; /// use epp_client::EppClient;
/// use epp_client::extensions::rgp::request::RgpRestoreRequest; /// use epp_client::extensions::rgp::{Update, request::RgpRestoreRequest};
/// use epp_client::domain::update::DomainUpdate; /// use epp_client::domain::update::DomainUpdate;
/// use epp_client::login::Login; /// use epp_client::login::Login;
/// use epp_client::logout::Logout; /// use epp_client::logout::Logout;
@ -49,10 +49,10 @@ use super::XMLNS;
/// client.transact(login, "transaction-id").await.unwrap(); /// client.transact(login, "transaction-id").await.unwrap();
/// ///
/// // Create an RgpRestoreRequest instance /// // Create an RgpRestoreRequest instance
/// let domain_restore_req = RgpRestoreRequest::new(); /// let domain_restore_req = Update { data: RgpRestoreRequest::default() };
/// ///
/// // Create an DomainUpdate instance /// // Create an DomainUpdate instance
/// let mut domain_update = DomainUpdate::<RgpRestoreRequest>::new("eppdev-100.com").with_extension(domain_restore_req); /// let mut domain_update = DomainUpdate::<Update<RgpRestoreRequest>>::new("eppdev-100.com").with_extension(domain_restore_req);
/// ///
/// // send it to the registry and receive a response of type EppDomainUpdateResponse /// // send it to the registry and receive a response of type EppDomainUpdateResponse
/// let response = client.transact(domain_update, "transaction-id").await.unwrap(); /// let response = client.transact(domain_update, "transaction-id").await.unwrap();
@ -63,26 +63,9 @@ use super::XMLNS;
/// client.transact(logout, "transaction-id").await.unwrap(); /// client.transact(logout, "transaction-id").await.unwrap();
/// } /// }
/// ``` /// ```
impl RgpRestoreRequest {
/// Creates a new instance of EppDomainRgpRestoreRequest
pub fn new() -> RgpRestoreRequest {
RgpRestoreRequest {
xmlns: XMLNS.to_string(),
restore: RgpRestoreRequestData {
op: "request".to_string(),
},
}
}
}
impl Default for RgpRestoreRequest { impl EppExtension for Update<RgpRestoreRequest> {
fn default() -> Self { type Response = Update<RgpRequestResponse>;
Self::new()
}
}
impl EppExtension for RgpRestoreRequest {
type Response = RgpRequestResponse;
} }
// Request // Request
@ -106,6 +89,17 @@ pub struct RgpRestoreRequest {
restore: RgpRestoreRequestData, restore: RgpRestoreRequestData,
} }
impl Default for RgpRestoreRequest {
fn default() -> Self {
Self {
xmlns: XMLNS.to_string(),
restore: RgpRestoreRequestData {
op: "request".to_string(),
},
}
}
}
// Response // Response
/// Type that represents the &lt;rgpStatus&gt; tag for domain rgp restore request response /// Type that represents the &lt;rgpStatus&gt; tag for domain rgp restore request response

View File

@ -3,14 +3,31 @@ use std::fmt::Debug;
use epp_client_macros::ElementName; use epp_client_macros::ElementName;
use serde::{Deserialize, Deserializer, Serialize}; use serde::{Deserialize, Deserializer, Serialize};
use crate::common::{ElementName, Options, ServiceExtension, Services, StringValue}; use crate::common::{ElementName, Options, ServiceExtension, Services, StringValue, EPP_XMLNS};
use crate::xml::EppXml;
// Request // Request
#[derive(Serialize, Deserialize, Debug, PartialEq, ElementName)] #[derive(Debug, PartialEq, Serialize)]
#[element_name(name = "hello")] struct Hello;
/// Type corresponding to the <hello> tag in an EPP XML hello request
pub 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,
}
}
}
impl EppXml for HelloDocument {}
// Response // Response
@ -278,3 +295,12 @@ pub struct Greeting {
/// Data under the <dcp> element /// Data under the <dcp> element
pub dcp: Dcp, pub dcp: Dcp,
} }
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename = "epp")]
pub struct GreetingDocument {
#[serde(rename = "greeting")]
pub data: Greeting,
}
impl EppXml for GreetingDocument {}

View File

@ -136,10 +136,23 @@ pub mod extensions {
pub mod consolidate; pub mod consolidate;
pub mod namestore; pub mod namestore;
pub mod rgp { pub mod rgp {
use serde::{Deserialize, Serialize};
pub mod report; pub mod report;
pub mod request; pub mod request;
pub const XMLNS: &str = "urn:ietf:params:xml:ns:rgp-1.0"; pub const XMLNS: &str = "urn:ietf:params:xml:ns:rgp-1.0";
#[derive(Debug, Deserialize, Serialize)]
pub struct Update<T> {
#[serde(
rename = "rgp:update",
alias = "update",
alias = "upData",
alias = "infData"
)]
pub data: T,
}
} }
} }

View File

@ -4,8 +4,8 @@ use serde::{de::DeserializeOwned, ser::SerializeStruct, ser::Serializer, Deseria
use std::fmt::Debug; use std::fmt::Debug;
use crate::{ use crate::{
common::{ElementName, EppObject, Extension, StringValue}, common::{ElementName, StringValue, EPP_XMLNS},
response::{Response, ResponseStatus}, response::{Response, ResponseDocument, ResponseStatus},
xml::EppXml, xml::EppXml,
}; };
use epp_client_macros::ElementName; use epp_client_macros::ElementName;
@ -15,16 +15,16 @@ pub const EPP_LANG: &str = "en";
/// Trait to set correct value for xml tags when tags are being generated from generic types /// Trait to set correct value for xml tags when tags are being generated from generic types
pub trait EppRequest<E: EppExtension>: Sized + Debug { pub trait EppRequest<E: EppExtension>: Sized + Debug {
type Input: ElementName + DeserializeOwned + Serialize + Sized + Debug; type Input: ElementName + Serialize + Sized + Debug;
type Output: DeserializeOwned + Serialize + Debug; type Output: DeserializeOwned + Debug;
fn into_parts(self) -> (Self::Input, Option<E>); fn into_parts(self) -> (Self::Input, Option<E>);
fn serialize_request(self, client_tr_id: &str) -> Result<String, Box<dyn std::error::Error>> { fn serialize_request(self, client_tr_id: &str) -> Result<String, Box<dyn std::error::Error>> {
let (command, extension) = self.into_parts(); let (command, extension) = self.into_parts();
let extension = extension.map(|data| Extension { data }); <CommandDocument<Self::Input, E> as EppXml>::serialize(&CommandDocument::new(Command {
EppXml::serialize(&EppObject::build(Command { command: <Self::Input as ElementName>::ELEMENT,
command, data: command,
extension, extension,
client_tr_id: client_tr_id.into(), client_tr_id: client_tr_id.into(),
})) }))
@ -33,7 +33,7 @@ pub trait EppRequest<E: EppExtension>: Sized + Debug {
fn deserialize_response( fn deserialize_response(
epp_xml: &str, epp_xml: &str,
) -> Result<Response<Self::Output, E::Response>, crate::error::Error> { ) -> Result<Response<Self::Output, E::Response>, crate::error::Error> {
let rsp = <EppObject<Response<Self::Output, E::Response>> as EppXml>::deserialize(epp_xml)?; let rsp = <ResponseDocument<Self::Output, E::Response> as EppXml>::deserialize(epp_xml)?;
match rsp.data.result.code { match rsp.data.result.code {
0..=2000 => Ok(rsp.data), 0..=2000 => Ok(rsp.data),
_ => Err(crate::error::Error::EppCommandError(ResponseStatus { _ => Err(crate::error::Error::EppCommandError(ResponseStatus {
@ -44,44 +44,52 @@ pub trait EppRequest<E: EppExtension>: Sized + Debug {
} }
} }
pub trait EppExtension: ElementName + DeserializeOwned + Serialize + Sized + Debug { pub trait EppExtension: Serialize + Sized + Debug {
type Response: ElementName + DeserializeOwned + Serialize + Debug; type Response: DeserializeOwned + Debug;
} }
#[derive(Deserialize, Debug, PartialEq, ElementName)] #[derive(Deserialize, Debug, PartialEq, ElementName)]
#[element_name(name = "command")] #[element_name(name = "command")]
/// Type corresponding to the &lt;command&gt; tag in an EPP XML request /// Type corresponding to the &lt;command&gt; tag in an EPP XML request
/// with an &lt;extension&gt; tag /// with an &lt;extension&gt; tag
pub struct Command<T: ElementName, E: ElementName> { pub struct Command<D, E> {
pub command: &'static str,
/// The instance that will be used to populate the &lt;command&gt; tag /// The instance that will be used to populate the &lt;command&gt; tag
pub command: T, pub data: D,
/// The client TRID /// The client TRID
pub extension: Option<Extension<E>>, pub extension: Option<E>,
#[serde(rename = "clTRID")] #[serde(rename = "clTRID")]
pub client_tr_id: StringValue, pub client_tr_id: StringValue,
} }
impl<T: ElementName + Serialize, E: ElementName + Serialize> Serialize for Command<T, E> { impl<D: Serialize, E: Serialize> Serialize for Command<D, E> {
/// Serializes the generic type T to the proper XML tag (set by the `#[element_name(name = <tagname>)]` attribute) for the request /// 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> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where where
S: Serializer, S: Serializer,
{ {
let mut state = serializer.serialize_struct("command", 3)?; let mut state = serializer.serialize_struct("command", 3)?;
state.serialize_field(T::ELEMENT, &self.command)?; state.serialize_field(self.command, &self.data)?;
state.serialize_field("extension", &self.extension)?; state.serialize_field("extension", &self.extension)?;
state.serialize_field("clTRID", &self.client_tr_id)?; state.serialize_field("clTRID", &self.client_tr_id)?;
state.end() state.end()
} }
} }
impl<T: ElementName, E: ElementName> Command<T, E> { #[derive(Debug, Deserialize, PartialEq, Serialize)]
/// Creates a new &lt;command&gt; tag for an EPP document with a containing &lt;extension&gt; tag #[serde(rename = "epp")]
pub fn build(command: T, ext: E, client_tr_id: &str) -> Command<T, E> { pub struct CommandDocument<D, E> {
Command { xmlns: &'static str,
command: Command<D, E>,
}
impl<D, E> CommandDocument<D, E> {
pub fn new(command: Command<D, E>) -> Self {
Self {
xmlns: EPP_XMLNS,
command, command,
extension: Some(Extension { data: ext }),
client_tr_id: client_tr_id.into(),
} }
} }
} }
impl<D: Serialize, E: Serialize> EppXml for CommandDocument<D, E> {}

View File

@ -1,10 +1,11 @@
//! Types for EPP responses //! Types for EPP responses
use epp_client_macros::*; use epp_client_macros::*;
use serde::{Deserialize, Serialize}; use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::fmt::Debug; use std::fmt::Debug;
use crate::common::{ElementName, Extension, StringValue}; use crate::common::{ElementName, StringValue};
use crate::xml::EppXml;
/// Type corresponding to the <undef> tag an EPP response XML /// Type corresponding to the <undef> tag an EPP response XML
#[derive(Serialize, Deserialize, Debug, PartialEq)] #[derive(Serialize, Deserialize, Debug, PartialEq)]
@ -70,10 +71,9 @@ pub struct MessageQueue {
#[derive(Serialize, Deserialize, Debug, PartialEq, ElementName)] #[derive(Serialize, Deserialize, Debug, PartialEq, ElementName)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
#[element_name(name = "response")]
/// Type corresponding to the &lt;response&gt; tag in an EPP response XML /// Type corresponding to the &lt;response&gt; tag in an EPP response XML
/// containing an &lt;extension&gt; tag /// containing an &lt;extension&gt; tag
pub struct Response<T, E: ElementName> { pub struct Response<D, E> {
/// Data under the <result> tag /// Data under the <result> tag
pub result: EppResult, pub result: EppResult,
/// Data under the <msgQ> tag /// Data under the <msgQ> tag
@ -81,14 +81,32 @@ pub struct Response<T, E: ElementName> {
pub message_queue: Option<MessageQueue>, pub message_queue: Option<MessageQueue>,
#[serde(rename = "resData")] #[serde(rename = "resData")]
/// Data under the &lt;resData&gt; tag /// Data under the &lt;resData&gt; tag
pub res_data: Option<T>, pub res_data: Option<D>,
/// Data under the &lt;extension&gt; tag /// Data under the &lt;extension&gt; tag
pub extension: Option<Extension<E>>, pub extension: Option<E>,
/// Data under the <trID> tag /// Data under the <trID> tag
#[serde(rename = "trID")] #[serde(rename = "trID")]
pub tr_ids: ResponseTRID, pub tr_ids: ResponseTRID,
} }
#[derive(Deserialize, Debug, PartialEq)]
#[serde(rename = "epp")]
pub struct ResponseDocument<D, E> {
#[serde(rename = "response")]
pub data: Response<D, E>,
}
impl<D: DeserializeOwned, E: DeserializeOwned> EppXml for ResponseDocument<D, E> {}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename = "epp")]
pub struct ResultDocument {
#[serde(rename = "response")]
pub data: ResponseStatus,
}
impl EppXml for ResultDocument {}
#[derive(Serialize, Deserialize, Debug, PartialEq, ElementName)] #[derive(Serialize, Deserialize, Debug, PartialEq, ElementName)]
#[element_name(name = "response")] #[element_name(name = "response")]
/// Type corresponding to the &lt;response&gt; tag in an EPP response XML /// Type corresponding to the &lt;response&gt; tag in an EPP response XML

View File

@ -3,7 +3,7 @@
mod response { mod response {
use super::super::get_xml; use super::super::get_xml;
use super::super::CLTRID; use super::super::CLTRID;
use crate::common::{EppObject, NoExtension}; use crate::common::NoExtension;
use crate::contact::check::ContactCheck; use crate::contact::check::ContactCheck;
use crate::contact::create::ContactCreate; use crate::contact::create::ContactCreate;
use crate::contact::delete::ContactDelete; use crate::contact::delete::ContactDelete;
@ -21,9 +21,9 @@ mod response {
use crate::domain::transfer::DomainTransferRequest; use crate::domain::transfer::DomainTransferRequest;
use crate::domain::update::DomainUpdate; use crate::domain::update::DomainUpdate;
use crate::extensions::namestore::NameStore; use crate::extensions::namestore::NameStore;
use crate::extensions::rgp::request::RgpRestoreRequest; use crate::extensions::rgp;
use crate::hello::ExpiryType; use crate::hello::ExpiryType;
use crate::hello::Greeting; use crate::hello::GreetingDocument;
use crate::hello::Relative; use crate::hello::Relative;
use crate::host::check::HostCheck; use crate::host::check::HostCheck;
use crate::host::create::HostCreate; use crate::host::create::HostCreate;
@ -35,7 +35,7 @@ mod response {
use crate::message::ack::MessageAck; use crate::message::ack::MessageAck;
use crate::message::poll::MessagePoll; use crate::message::poll::MessagePoll;
use crate::request::EppRequest; use crate::request::EppRequest;
use crate::response::ResponseStatus; use crate::response::ResultDocument;
use crate::xml::EppXml; use crate::xml::EppXml;
const SVTRID: &str = "RO-6879-1627224678242975"; const SVTRID: &str = "RO-6879-1627224678242975";
@ -44,7 +44,7 @@ mod response {
#[test] #[test]
fn greeting() { fn greeting() {
let xml = get_xml("response/greeting.xml").unwrap(); let xml = get_xml("response/greeting.xml").unwrap();
let object = EppObject::<Greeting>::deserialize(xml.as_str()).unwrap(); let object = GreetingDocument::deserialize(xml.as_str()).unwrap();
assert_eq!(object.data.service_id, "ISPAPI EPP Server"); assert_eq!(object.data.service_id, "ISPAPI EPP Server");
assert_eq!(object.data.service_date, "2021-07-25T14:51:17.0Z"); assert_eq!(object.data.service_date, "2021-07-25T14:51:17.0Z");
@ -75,7 +75,7 @@ mod response {
#[test] #[test]
fn error() { fn error() {
let xml = get_xml("response/error.xml").unwrap(); let xml = get_xml("response/error.xml").unwrap();
let object = EppObject::<ResponseStatus>::deserialize(xml.as_str()).unwrap(); let object = ResultDocument::deserialize(xml.as_str()).unwrap();
assert_eq!(object.data.result.code, 2303); assert_eq!(object.data.result.code, 2303);
assert_eq!(object.data.result.message, "Object does not exist".into()); assert_eq!(object.data.result.message, "Object does not exist".into());
@ -637,7 +637,11 @@ mod response {
#[test] #[test]
fn rgp_restore_response() { fn rgp_restore_response() {
let xml = get_xml("response/extensions/rgp_restore.xml").unwrap(); let xml = get_xml("response/extensions/rgp_restore.xml").unwrap();
let object = DomainUpdate::<RgpRestoreRequest>::deserialize_response(xml.as_str()).unwrap(); let object =
DomainUpdate::<rgp::Update<rgp::request::RgpRestoreRequest>>::deserialize_response(
xml.as_str(),
)
.unwrap();
let ext = object.extension.unwrap(); let ext = object.extension.unwrap();
@ -650,7 +654,11 @@ mod response {
#[test] #[test]
fn rgp_restore_domain_info_response() { fn rgp_restore_domain_info_response() {
let xml = get_xml("response/extensions/domain_info_rgp.xml").unwrap(); let xml = get_xml("response/extensions/domain_info_rgp.xml").unwrap();
let object = DomainInfo::<RgpRestoreRequest>::deserialize_response(xml.as_str()).unwrap(); let object =
DomainInfo::<rgp::Update<rgp::request::RgpRestoreRequest>>::deserialize_response(
xml.as_str(),
)
.unwrap();
let ext = object.extension.unwrap(); let ext = object.extension.unwrap();

View File

@ -8,8 +8,8 @@ mod request {
use crate::common::HostObjList; use crate::common::HostObjList;
use crate::common::NoExtension; use crate::common::NoExtension;
use crate::common::{ use crate::common::{
Address, ContactStatus, DomainAuthInfo, DomainContact, DomainStatus, EppObject, HostAddr, Address, ContactStatus, DomainAuthInfo, DomainContact, DomainStatus, HostAddr, HostAttr,
HostAttr, HostStatus, Phone, PostalInfo, HostStatus, Phone, PostalInfo,
}; };
use crate::contact::check::ContactCheck; use crate::contact::check::ContactCheck;
use crate::contact::create::ContactCreate; use crate::contact::create::ContactCreate;
@ -32,9 +32,8 @@ mod request {
use crate::extensions::consolidate; use crate::extensions::consolidate;
use crate::extensions::consolidate::GMonthDay; use crate::extensions::consolidate::GMonthDay;
use crate::extensions::namestore::NameStore; use crate::extensions::namestore::NameStore;
use crate::extensions::rgp::report::RgpRestoreReport; use crate::extensions::rgp::{self, report::RgpRestoreReport, request::RgpRestoreRequest};
use crate::extensions::rgp::request::RgpRestoreRequest; use crate::hello::HelloDocument;
use crate::hello::Hello;
use crate::host::check::HostCheck; use crate::host::check::HostCheck;
use crate::host::create::HostCreate; use crate::host::create::HostCreate;
use crate::host::delete::HostDelete; use crate::host::delete::HostDelete;
@ -54,7 +53,7 @@ mod request {
#[test] #[test]
fn hello() { fn hello() {
let xml = get_xml("request/hello.xml").unwrap(); let xml = get_xml("request/hello.xml").unwrap();
let serialized = EppObject::<Hello>::build(Hello).serialize().unwrap(); let serialized = HelloDocument::default().serialize().unwrap();
assert_eq!(xml, serialized); assert_eq!(xml, serialized);
} }
@ -527,9 +526,11 @@ mod request {
fn rgp_restore_request() { fn rgp_restore_request() {
let xml = get_xml("request/extensions/rgp_restore_request.xml").unwrap(); let xml = get_xml("request/extensions/rgp_restore_request.xml").unwrap();
let domain_restore_request = RgpRestoreRequest::new(); let domain_restore_request = rgp::Update {
data: RgpRestoreRequest::default(),
};
let mut object = DomainUpdate::<RgpRestoreReport>::new("eppdev.com") let mut object = DomainUpdate::<rgp::Update<RgpRestoreRequest>>::new("eppdev.com")
.with_extension(domain_restore_request); .with_extension(domain_restore_request);
let change_info = DomainChangeInfo { let change_info = DomainChangeInfo {
@ -561,17 +562,19 @@ mod request {
]; ];
let other = "Supporting information goes here."; let other = "Supporting information goes here.";
let domain_restore_report = RgpRestoreReport::new( let domain_restore_report = rgp::Update {
pre_data, data: RgpRestoreReport::new(
post_data, pre_data,
deleted_at, post_data,
restored_at, deleted_at,
restore_reason, restored_at,
statements, restore_reason,
other, statements,
); other,
),
};
let mut object = DomainUpdate::<RgpRestoreReport>::new("eppdev.com") let mut object = DomainUpdate::<rgp::Update<RgpRestoreReport>>::new("eppdev.com")
.with_extension(domain_restore_report); .with_extension(domain_restore_report);
let change_info = DomainChangeInfo { let change_info = DomainChangeInfo {
@ -607,10 +610,10 @@ mod request {
let exp = GMonthDay::new(5, 31, None).unwrap(); let exp = GMonthDay::new(5, 31, None).unwrap();
let consolidate_ext = consolidate::Sync::new(exp); let consolidate_ext = consolidate::Update::new(exp);
let mut object = let mut object =
DomainUpdate::<consolidate::Sync>::new("eppdev.com").with_extension(consolidate_ext); DomainUpdate::<consolidate::Update>::new("eppdev.com").with_extension(consolidate_ext);
object.info(DomainChangeInfo { object.info(DomainChangeInfo {
registrant: None, registrant: None,

View File

@ -3,26 +3,30 @@
use quick_xml::de::from_str; use quick_xml::de::from_str;
use quick_xml::se; use quick_xml::se;
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
use std::{error::Error, fmt::Debug}; use std::error::Error;
use crate::common::{ElementName, EppObject};
use crate::error; use crate::error;
pub const EPP_XML_HEADER: &str = r#"<?xml version="1.0" encoding="UTF-8" standalone="no"?>"#; pub const EPP_XML_HEADER: &str = r#"<?xml version="1.0" encoding="UTF-8" standalone="no"?>"#;
impl<T: Serialize + DeserializeOwned + ElementName + Debug> EppXml for EppObject<T> { /// Trait to be implemented by serializers. Currently the only included serializer is `quick-xml`
type Output = EppObject<T>; pub trait EppXml: Sized {
/// Serializes the EppObject instance to an EPP XML document /// Serializes the EppObject instance to an EPP XML document
fn serialize(&self) -> Result<String, Box<dyn Error>> { fn serialize(&self) -> Result<String, Box<dyn Error>>
where
Self: Serialize,
{
let epp_xml = format!("{}\r\n{}", EPP_XML_HEADER, se::to_string(self)?); let epp_xml = format!("{}\r\n{}", EPP_XML_HEADER, se::to_string(self)?);
Ok(epp_xml) Ok(epp_xml)
} }
/// Deserializes an EPP XML document to an EppObject instance /// Deserializes an EPP XML document to an EppObject instance
fn deserialize(epp_xml: &str) -> Result<Self::Output, error::Error> { fn deserialize(epp_xml: &str) -> Result<Self, error::Error>
let object: Self::Output = match from_str(epp_xml) { where
Self: DeserializeOwned + Sized,
{
let object: Self = match from_str(epp_xml) {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
return Err(error::Error::EppDeserializationError(format!( return Err(error::Error::EppDeserializationError(format!(
@ -35,11 +39,3 @@ impl<T: Serialize + DeserializeOwned + ElementName + Debug> EppXml for EppObject
Ok(object) Ok(object)
} }
} }
/// Trait to be implemented by serializers. Currently the only included serializer is `quick-xml`
pub trait EppXml {
type Output: Debug;
fn serialize(&self) -> Result<String, Box<dyn Error>>;
fn deserialize(epp_xml: &str) -> Result<Self::Output, error::Error>;
}