diff --git a/README.md b/README.md index 259f674..47e3cca 100644 --- a/README.md +++ b/README.md @@ -33,12 +33,15 @@ and other EPP extensions in the future, and to eventually be RFC compliant with - Message Poll - Message Ack +- RGP Restore Request +- RGP Restore Report + ## Usage Just add the following to your project's `Cargo.toml` ```toml -epp-client = "0.1.2" +epp-client = "0.2" ``` ## Prerequisites @@ -100,6 +103,13 @@ async fn main() { } ``` +The output would look similar to the following + +``` +Domain: eppdev.com, Available: 1 +Domain: eppdev.net, Available: 1 +``` + ## Request Currently I don't have access to a registry's OT&E account to do extensive testing. I am using [hexonet's EPP Gateway](https://wiki.hexonet.net/wiki/EPP_Gateway) for testing, but access to a registry's OT&E account would be very helpful, so if anyone could help me out with one I would be very grateful! \ No newline at end of file diff --git a/epp-client/Cargo.toml b/epp-client/Cargo.toml index 4f7d489..0e1aff5 100644 --- a/epp-client/Cargo.toml +++ b/epp-client/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "epp-client" -version = "0.1.2" +version = "0.2.0" edition = "2018" license = "MIT" authors = ["Ritesh Chitlangi "] @@ -10,7 +10,7 @@ repository = "https://github.com/masalachai/epp-client" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -epp-client-macros = "0.1" #{ path = "../epp-client-macros" } +epp-client-macros = { path = "../epp-client-macros" } bytes = "1" chrono = "0.4" confy = "0.4" diff --git a/epp-client/README.md b/epp-client/README.md index 259f674..47e3cca 100644 --- a/epp-client/README.md +++ b/epp-client/README.md @@ -33,12 +33,15 @@ and other EPP extensions in the future, and to eventually be RFC compliant with - Message Poll - Message Ack +- RGP Restore Request +- RGP Restore Report + ## Usage Just add the following to your project's `Cargo.toml` ```toml -epp-client = "0.1.2" +epp-client = "0.2" ``` ## Prerequisites @@ -100,6 +103,13 @@ async fn main() { } ``` +The output would look similar to the following + +``` +Domain: eppdev.com, Available: 1 +Domain: eppdev.net, Available: 1 +``` + ## Request Currently I don't have access to a registry's OT&E account to do extensive testing. I am using [hexonet's EPP Gateway](https://wiki.hexonet.net/wiki/EPP_Gateway) for testing, but access to a registry's OT&E account would be very helpful, so if anyone could help me out with one I would be very grateful! \ No newline at end of file diff --git a/epp-client/src/epp.rs b/epp-client/src/epp.rs index 6cca177..63b7046 100644 --- a/epp-client/src/epp.rs +++ b/epp-client/src/epp.rs @@ -15,6 +15,8 @@ pub use request::domain::create::*; pub use request::domain::delete::*; pub use request::domain::info::*; pub use request::domain::renew::*; +pub use request::domain::rgp::report::*; +pub use request::domain::rgp::request::*; pub use request::domain::transfer::*; pub use request::domain::update::*; pub use request::host::check::*; @@ -35,6 +37,8 @@ pub use response::domain::create::*; pub use response::domain::delete::*; pub use response::domain::info::*; pub use response::domain::renew::*; +pub use response::domain::rgp::report::*; +pub use response::domain::rgp::request::*; pub use response::domain::transfer::*; pub use response::domain::update::*; pub use response::host::check::*; diff --git a/epp-client/src/epp/object.rs b/epp-client/src/epp/object.rs index a1ebbe1..3b060de 100644 --- a/epp-client/src/epp/object.rs +++ b/epp-client/src/epp/object.rs @@ -2,6 +2,7 @@ pub mod data; +use epp_client_macros::*; use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer}; use std::fmt::Display; @@ -46,6 +47,11 @@ pub trait ElementName { fn element_name(&self) -> &'static str; } +#[derive(Serialize, Deserialize, Debug, PartialEq, ElementName)] +#[element_name(name = "empty")] +/// An empty placeholder tag. To be refactored to something more compliant later. +pub struct EmptyTag; + /// An EPP XML Document that is used either as an EPP XML request or /// an EPP XML response #[derive(Deserialize, Debug, PartialEq)] @@ -73,6 +79,7 @@ impl Serialize for EppObject { S: Serializer, { let data_name = self.data.element_name(); + let mut state = serializer.serialize_struct("epp", 4)?; state.serialize_field("xmlns", &self.xmlns)?; state.serialize_field("xmlns:xsi", &self.xmlns_xsi)?; @@ -102,6 +109,28 @@ impl Options { } } +/// Type representing the <extension> tag for an EPP document +#[derive(Deserialize, Debug, PartialEq)] +#[serde(rename = "extension")] +pub struct Extension { + /// Data under the <extension> tag + #[serde(alias = "upData")] + pub data: E, +} + +impl Serialize for Extension { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let data_name = self.data.element_name(); + + let mut state = serializer.serialize_struct("extension", 1)?; + state.serialize_field(data_name, &self.data)?; + state.end() + } +} + /// The type in EPP XML #[derive(Serialize, Deserialize, Debug, PartialEq)] #[serde(rename = "svcExtension")] diff --git a/epp-client/src/epp/request.rs b/epp-client/src/epp/request.rs index 87ab4df..b4dae59 100644 --- a/epp-client/src/epp/request.rs +++ b/epp-client/src/epp/request.rs @@ -10,11 +10,16 @@ use std::error::Error; use std::time::SystemTime; use crate::epp::object::{ - ElementName, EppObject, Options, ServiceExtension, Services, StringValue, StringValueTrait, + ElementName, EmptyTag, EppObject, Extension, Options, ServiceExtension, Services, StringValue, + StringValueTrait, }; use crate::epp::xml::{EPP_CONTACT_XMLNS, EPP_DOMAIN_XMLNS, EPP_HOST_XMLNS, EPP_LANG, EPP_VERSION}; use epp_client_macros::*; +/// Type corresponding to the <command> tag in an EPP XML request +/// without an <extension> tag +pub type Command = CommandWithExtension; + /// The EPP Hello request pub type EppHello = EppObject; /// The EPP Login Request @@ -25,28 +30,55 @@ pub type EppLogout = EppObject>; #[derive(Deserialize, Debug, PartialEq, ElementName)] #[element_name(name = "command")] /// Type corresponding to the <command> tag in an EPP XML request -pub struct Command { +/// with an <extension> tag +pub struct CommandWithExtension { /// The instance that will be used to populate the <command> tag pub command: T, /// The client TRID + pub extension: Option>, #[serde(rename = "clTRID")] pub client_tr_id: StringValue, } -impl Serialize for Command { +impl Serialize + for CommandWithExtension +{ /// Serializes the generic type T to the proper XML tag (set by the `#[element_name(name = )]` attribute) for the request fn serialize(&self, serializer: S) -> Result where S: Serializer, { let command_name = self.command.element_name(); - let mut state = serializer.serialize_struct("command", 2)?; + let mut state = serializer.serialize_struct("command", 3)?; state.serialize_field(command_name, &self.command)?; + state.serialize_field("extension", &self.extension)?; state.serialize_field("clTRID", &self.client_tr_id)?; state.end() } } +impl Command { + /// Creates a new <command> tag for an EPP document + pub fn new(command: T, client_tr_id: &str) -> Command { + Command { + command: command, + extension: None, + client_tr_id: client_tr_id.to_string_value(), + } + } +} + +impl CommandWithExtension { + /// Creates a new <command> tag for an EPP document with a containing <extension> tag + pub fn build(command: T, ext: E, client_tr_id: &str) -> CommandWithExtension { + CommandWithExtension { + command: command, + extension: Some(Extension { data: ext }), + client_tr_id: client_tr_id.to_string_value(), + } + } +} + /// Basic client TRID generation function. Mainly used for testing. Users of the library should use their own clTRID generation function. pub fn generate_client_tr_id(username: &str) -> Result> { let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?; @@ -118,6 +150,7 @@ impl EppLogin { EppObject::build(Command:: { command: login, + extension: None, client_tr_id: client_tr_id.to_string_value(), }) } @@ -143,6 +176,7 @@ impl EppLogout { pub fn new(client_tr_id: &str) -> EppLogout { EppObject::build(Command:: { command: Logout, + extension: None, client_tr_id: client_tr_id.to_string_value(), }) } diff --git a/epp-client/src/epp/request/contact/check.rs b/epp-client/src/epp/request/contact/check.rs index 14f4fec..cba964b 100644 --- a/epp-client/src/epp/request/contact/check.rs +++ b/epp-client/src/epp/request/contact/check.rs @@ -73,9 +73,6 @@ impl EppContactCheck { }, }; - EppObject::build(Command:: { - command: contact_check, - client_tr_id: client_tr_id.to_string_value(), - }) + EppObject::build(Command::::new(contact_check, client_tr_id)) } } diff --git a/epp-client/src/epp/request/contact/create.rs b/epp-client/src/epp/request/contact/create.rs index d374715..9c2f0fc 100644 --- a/epp-client/src/epp/request/contact/create.rs +++ b/epp-client/src/epp/request/contact/create.rs @@ -107,10 +107,7 @@ impl EppContactCreate { }, }; - EppObject::build(Command:: { - command: contact_create, - client_tr_id: client_tr_id.to_string_value(), - }) + EppObject::build(Command::::new(contact_create, client_tr_id)) } /// Sets the <fax> data for the request diff --git a/epp-client/src/epp/request/contact/delete.rs b/epp-client/src/epp/request/contact/delete.rs index 3909999..f633d60 100644 --- a/epp-client/src/epp/request/contact/delete.rs +++ b/epp-client/src/epp/request/contact/delete.rs @@ -60,14 +60,13 @@ pub struct ContactDelete { impl EppContactDelete { /// Creates a new EppObject for contact delete corresponding to the <epp> tag in EPP XML pub fn new(id: &str, client_tr_id: &str) -> EppContactDelete { - EppObject::build(Command:: { - command: ContactDelete { - contact: ContactDeleteData { - xmlns: EPP_CONTACT_XMLNS.to_string(), - id: id.to_string_value(), - }, + let contact_delete = ContactDelete { + contact: ContactDeleteData { + xmlns: EPP_CONTACT_XMLNS.to_string(), + id: id.to_string_value(), }, - client_tr_id: client_tr_id.to_string_value(), - }) + }; + + EppObject::build(Command::::new(contact_delete, client_tr_id)) } } diff --git a/epp-client/src/epp/request/contact/info.rs b/epp-client/src/epp/request/contact/info.rs index 5e1eb26..49314b3 100644 --- a/epp-client/src/epp/request/contact/info.rs +++ b/epp-client/src/epp/request/contact/info.rs @@ -65,15 +65,14 @@ pub struct ContactInfo { impl EppContactInfo { /// Creates a new EppObject for contact info corresponding to the <epp> tag in EPP XML pub fn new(id: &str, auth_password: &str, client_tr_id: &str) -> EppContactInfo { - EppObject::build(Command:: { - command: ContactInfo { - info: ContactInfoData { - xmlns: EPP_CONTACT_XMLNS.to_string(), - id: id.to_string_value(), - auth_info: AuthInfo::new(auth_password), - }, + let contact_info = ContactInfo { + info: ContactInfoData { + xmlns: EPP_CONTACT_XMLNS.to_string(), + id: id.to_string_value(), + auth_info: AuthInfo::new(auth_password), }, - client_tr_id: client_tr_id.to_string_value(), - }) + }; + + EppObject::build(Command::::new(contact_info, client_tr_id)) } } diff --git a/epp-client/src/epp/request/contact/update.rs b/epp-client/src/epp/request/contact/update.rs index 1fe6613..5ef0030 100644 --- a/epp-client/src/epp/request/contact/update.rs +++ b/epp-client/src/epp/request/contact/update.rs @@ -93,18 +93,16 @@ pub struct ContactUpdate { impl EppContactUpdate { /// Creates a new EppObject for contact update corresponding to the <epp> tag in EPP XML pub fn new(id: &str, client_tr_id: &str) -> EppContactUpdate { - EppObject::build(Command:: { - command: ContactUpdate { - contact: ContactUpdateData { - xmlns: EPP_CONTACT_XMLNS.to_string(), - id: id.to_string_value(), - add_statuses: None, - remove_statuses: None, - change_info: None, - }, + let contact_update = ContactUpdate { + contact: ContactUpdateData { + xmlns: EPP_CONTACT_XMLNS.to_string(), + id: id.to_string_value(), + add_statuses: None, + remove_statuses: None, + change_info: None, }, - client_tr_id: client_tr_id.to_string_value(), - }) + }; + EppObject::build(Command::::new(contact_update, client_tr_id)) } /// Sets the data for the <chg> tag for the contact update request diff --git a/epp-client/src/epp/request/domain.rs b/epp-client/src/epp/request/domain.rs index 0ef8def..18462b3 100644 --- a/epp-client/src/epp/request/domain.rs +++ b/epp-client/src/epp/request/domain.rs @@ -5,5 +5,6 @@ pub mod create; pub mod delete; pub mod info; pub mod renew; +pub mod rgp; pub mod transfer; pub mod update; diff --git a/epp-client/src/epp/request/domain/check.rs b/epp-client/src/epp/request/domain/check.rs index a3b9cfc..45f7db1 100644 --- a/epp-client/src/epp/request/domain/check.rs +++ b/epp-client/src/epp/request/domain/check.rs @@ -73,9 +73,6 @@ impl EppDomainCheck { }, }; - EppObject::build(Command:: { - command: domain_check, - client_tr_id: client_tr_id.to_string_value(), - }) + EppObject::build(Command::::new(domain_check, client_tr_id)) } } diff --git a/epp-client/src/epp/request/domain/create.rs b/epp-client/src/epp/request/domain/create.rs index 063744b..2fc92b8 100644 --- a/epp-client/src/epp/request/domain/create.rs +++ b/epp-client/src/epp/request/domain/create.rs @@ -112,20 +112,22 @@ impl EppDomainCreate { .map(|n| n.to_string_value()) .collect::>(); - EppObject::build(Command::> { - command: DomainCreate { - domain: DomainCreateData { - xmlns: EPP_DOMAIN_XMLNS.to_string(), - name: name.to_string_value(), - period: Period::new(period), - ns: Some(HostObjList { hosts: ns_list }), - registrant: Some(registrant_id.to_string_value()), - auth_info: AuthInfo::new(auth_password), - contacts: Some(contacts), - }, + let domain_create = DomainCreate { + domain: DomainCreateData { + xmlns: EPP_DOMAIN_XMLNS.to_string(), + name: name.to_string_value(), + period: Period::new(period), + ns: Some(HostObjList { hosts: ns_list }), + registrant: Some(registrant_id.to_string_value()), + auth_info: AuthInfo::new(auth_password), + contacts: Some(contacts), }, - client_tr_id: client_tr_id.to_string_value(), - }) + }; + + EppObject::build(Command::>::new( + domain_create, + client_tr_id, + )) } /// Creates a new EppObject for domain create corresponding to the <epp> tag in EPP XML @@ -138,20 +140,21 @@ impl EppDomainCreate { contacts: Vec, client_tr_id: &str, ) -> EppDomainCreate { - EppObject::build(Command::> { - command: DomainCreate { - domain: DomainCreateData { - xmlns: EPP_DOMAIN_XMLNS.to_string(), - name: name.to_string_value(), - period: Period::new(period), - ns: None, - registrant: Some(registrant_id.to_string_value()), - auth_info: AuthInfo::new(auth_password), - contacts: Some(contacts), - }, + let domain_create = DomainCreate { + domain: DomainCreateData { + xmlns: EPP_DOMAIN_XMLNS.to_string(), + name: name.to_string_value(), + period: Period::new(period), + ns: None, + registrant: Some(registrant_id.to_string_value()), + auth_info: AuthInfo::new(auth_password), + contacts: Some(contacts), }, - client_tr_id: client_tr_id.to_string_value(), - }) + }; + EppObject::build(Command::>::new( + domain_create, + client_tr_id, + )) } /// Creates a new EppObject for domain create corresponding to the <epp> tag in EPP XML @@ -162,20 +165,22 @@ impl EppDomainCreate { auth_password: &str, client_tr_id: &str, ) -> EppDomainCreate { - EppObject::build(Command::> { - command: DomainCreate { - domain: DomainCreateData { - xmlns: EPP_DOMAIN_XMLNS.to_string(), - name: name.to_string_value(), - period: Period::new(period), - ns: None, - registrant: None, - auth_info: AuthInfo::new(auth_password), - contacts: None, - }, + let domain_create = DomainCreate { + domain: DomainCreateData { + xmlns: EPP_DOMAIN_XMLNS.to_string(), + name: name.to_string_value(), + period: Period::new(period), + ns: None, + registrant: None, + auth_info: AuthInfo::new(auth_password), + contacts: None, }, - client_tr_id: client_tr_id.to_string_value(), - }) + }; + + EppObject::build(Command::>::new( + domain_create, + client_tr_id, + )) } /// Creates a new EppObject for domain create corresponding to the <epp> tag in EPP XML @@ -189,19 +194,20 @@ impl EppDomainCreate { contacts: Vec, client_tr_id: &str, ) -> EppDomainCreateWithHostAttr { - EppObject::build(Command::> { - command: DomainCreate { - domain: DomainCreateData { - xmlns: EPP_DOMAIN_XMLNS.to_string(), - name: name.to_string_value(), - period: Period::new(period), - ns: Some(HostAttrList { hosts: ns }), - registrant: Some(registrant_id.to_string_value()), - auth_info: AuthInfo::new(auth_password), - contacts: Some(contacts), - }, + let domain_create = DomainCreate { + domain: DomainCreateData { + xmlns: EPP_DOMAIN_XMLNS.to_string(), + name: name.to_string_value(), + period: Period::new(period), + ns: Some(HostAttrList { hosts: ns }), + registrant: Some(registrant_id.to_string_value()), + auth_info: AuthInfo::new(auth_password), + contacts: Some(contacts), }, - client_tr_id: client_tr_id.to_string_value(), - }) + }; + EppObject::build(Command::>::new( + domain_create, + client_tr_id, + )) } } diff --git a/epp-client/src/epp/request/domain/delete.rs b/epp-client/src/epp/request/domain/delete.rs index 1be0a7a..2163e94 100644 --- a/epp-client/src/epp/request/domain/delete.rs +++ b/epp-client/src/epp/request/domain/delete.rs @@ -57,14 +57,14 @@ pub struct DomainDelete { impl EppDomainDelete { /// Creates a new EppObject for domain delete corresponding to the <epp> tag in EPP XML pub fn new(name: &str, client_tr_id: &str) -> EppDomainDelete { - EppObject::build(Command:: { - command: DomainDelete { + EppObject::build(Command::::new( + DomainDelete { domain: DomainDeleteData { xmlns: EPP_DOMAIN_XMLNS.to_string(), name: name.to_string_value(), }, }, - client_tr_id: client_tr_id.to_string_value(), - }) + client_tr_id, + )) } } diff --git a/epp-client/src/epp/request/domain/info.rs b/epp-client/src/epp/request/domain/info.rs index bc80973..77c81a2 100644 --- a/epp-client/src/epp/request/domain/info.rs +++ b/epp-client/src/epp/request/domain/info.rs @@ -2,7 +2,7 @@ use epp_client_macros::*; -use crate::epp::object::{ElementName, EppObject, StringValueTrait}; +use crate::epp::object::{ElementName, EppObject}; use crate::epp::request::Command; use crate::epp::xml::EPP_DOMAIN_XMLNS; use serde::{Deserialize, Serialize}; @@ -68,8 +68,8 @@ pub struct DomainInfo { impl EppDomainInfo { /// Creates a new EppObject for domain info corresponding to the <epp> tag in EPP XML pub fn new(name: &str, client_tr_id: &str) -> EppDomainInfo { - EppObject::build(Command:: { - command: DomainInfo { + EppObject::build(Command::::new( + DomainInfo { info: DomainInfoData { xmlns: EPP_DOMAIN_XMLNS.to_string(), domain: Domain { @@ -78,7 +78,7 @@ impl EppDomainInfo { }, }, }, - client_tr_id: client_tr_id.to_string_value(), - }) + client_tr_id, + )) } } diff --git a/epp-client/src/epp/request/domain/renew.rs b/epp-client/src/epp/request/domain/renew.rs index 01a39b4..bc637d3 100644 --- a/epp-client/src/epp/request/domain/renew.rs +++ b/epp-client/src/epp/request/domain/renew.rs @@ -78,8 +78,8 @@ impl EppDomainRenew { .to_string() .to_string_value(); - EppObject::build(Command:: { - command: DomainRenew { + EppObject::build(Command::::new( + DomainRenew { domain: DomainRenewData { xmlns: EPP_DOMAIN_XMLNS.to_string(), name: name.to_string_value(), @@ -87,8 +87,8 @@ impl EppDomainRenew { period: Period::new(years), }, }, - client_tr_id: client_tr_id.to_string_value(), - }) + client_tr_id, + )) } pub fn set_period(&mut self, period: Period) { diff --git a/epp-client/src/epp/request/domain/rgp.rs b/epp-client/src/epp/request/domain/rgp.rs new file mode 100644 index 0000000..8775fb4 --- /dev/null +++ b/epp-client/src/epp/request/domain/rgp.rs @@ -0,0 +1,4 @@ +//! Types for EPP RGP restore requests + +pub mod report; +pub mod request; diff --git a/epp-client/src/epp/request/domain/rgp/report.rs b/epp-client/src/epp/request/domain/rgp/report.rs new file mode 100644 index 0000000..7060643 --- /dev/null +++ b/epp-client/src/epp/request/domain/rgp/report.rs @@ -0,0 +1,175 @@ +//! Types for EPP RGP restore report + +use epp_client_macros::*; + +use crate::epp::object::data::HostObjList; +use crate::epp::object::{ElementName, EppObject, StringValue, StringValueTrait}; +use crate::epp::request::domain::update::{DomainChangeInfo, DomainUpdate, DomainUpdateData}; +use crate::epp::request::{CommandWithExtension, Extension}; +use crate::epp::xml::{ + EPP_DOMAIN_RGP_EXT_SCHEMA_LOCATION, EPP_DOMAIN_RGP_EXT_XMLNS, EPP_DOMAIN_XMLNS, +}; +use chrono::{DateTime, SecondsFormat, Utc}; +use serde::{Deserialize, Serialize}; + +/// Type that represents the <epp> request for domain rgp restore report command +/// +/// ## Usage +/// +/// ```ignore +/// use epp_client::EppClient; +/// use epp_client::epp::{EppDomainRgpRestoreReport, EppDomainRgpRestoreReportResponse}; +/// use epp_client::epp::generate_client_tr_id; +/// use chrono::{DateTime, NaiveDate}; +/// use std::str::FromStr; +/// +/// #[tokio::main] +/// async fn main() { +/// // Create an instance of EppClient, specifying the name of the registry as in +/// // the config file +/// let mut client = match EppClient::new("verisign").await { +/// Ok(client) => client, +/// Err(e) => panic!("Failed to create EppClient: {}", e) +/// }; +/// +/// let pre_data = +/// "Pre-delete registration data goes here. Both XML and free text are allowed."; +/// let post_data = +/// "Post-restore registration data goes here. Both XML and free text are allowed."; +/// let deleted_at = DateTime::from_str("2021-07-10T22:00:00.0Z").unwrap(); +/// let restored_at = DateTime::from_str("2021-07-20T22:00:00.0Z").unwrap(); +/// let restore_reason = "Registrant error."; +/// let statements = vec![ +/// "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.", +/// "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.", +/// ]; +/// let other = "Supporting information goes here."; +/// +/// // Create an EppDomainRgpRestoreReport instance +/// let domain_restore_report = EppDomainRgpRestoreReport::new( +/// "eppdev.com", +/// pre_data, +/// post_data, +/// deleted_at, +/// restored_at, +/// restore_reason, +/// statements, +/// other, +/// generate_client_tr_id(&client).as_str() +/// ); +/// +/// // send it to the registry and receive a response of type EppDomainRgpRestoreReportResponse +/// let response = client.transact::<_, EppDomainRgpRestoreReportResponse>(&domain_restore_report).await.unwrap(); +/// +/// println!("{:?}", response); +/// } +/// ``` +pub type EppDomainRgpRestoreReport = + EppObject, RgpRestoreReport>>; + +/// Type corresponding to the <report> section in the EPP rgp restore extension +#[derive(Serialize, Deserialize, Debug)] +pub struct RgpRestoreReportData { + /// The pre-delete registration date + #[serde(rename = "preData")] + pre_data: StringValue, + /// The post-delete registration date + #[serde(rename = "postData")] + post_data: StringValue, + /// The domain deletion date + #[serde(rename = "delTime")] + deleted_at: StringValue, + /// The domain restore request date + #[serde(rename = "resTime")] + restored_at: StringValue, + /// The reason for domain restoration + #[serde(rename = "resReason")] + restore_reason: StringValue, + /// The registrar's statements on the domain restoration + #[serde(rename = "statement")] + statements: Vec, + /// Other remarks for domain restoration + other: StringValue, +} + +/// Type corresponding to the <restore> section in the rgp restore extension +#[derive(Serialize, Deserialize, Debug)] +pub struct RgpRestoreReportSection { + /// The value of the op attribute for the <restore> tag + op: String, + /// Data for the <report> tag + report: RgpRestoreReportData, +} + +#[derive(Serialize, Deserialize, Debug, ElementName)] +#[element_name(name = "update")] +/// Type for EPP XML <check> command for domains +pub struct RgpRestoreReport { + /// XML namespace for the RGP restore extension + xmlns: String, + /// XML schema location for the RGP restore extension + #[serde(rename = "xsi:schemaLocation")] + schema_location: String, + /// The object holding the list of domains to be checked + restore: RgpRestoreReportSection, +} + +impl EppDomainRgpRestoreReport { + /// Creates a new EppObject for domain rgp restore report corresponding to the <epp> tag in EPP XML + pub fn new( + name: &str, + pre_data: &str, + post_data: &str, + deleted_at: DateTime, + restored_at: DateTime, + restore_reason: &str, + statements: Vec<&str>, + other: &str, + client_tr_id: &str, + ) -> EppDomainRgpRestoreReport { + let statements = statements + .iter() + .map(|s| s.to_string_value()) + .collect::>(); + + let command = CommandWithExtension::, RgpRestoreReport> { + command: DomainUpdate { + domain: DomainUpdateData { + xmlns: EPP_DOMAIN_XMLNS.to_string(), + name: name.to_string_value(), + add: None, + remove: None, + change_info: Some(DomainChangeInfo { + registrant: None, + auth_info: None, + }), + }, + }, + extension: Some(Extension { + data: RgpRestoreReport { + xmlns: EPP_DOMAIN_RGP_EXT_XMLNS.to_string(), + schema_location: EPP_DOMAIN_RGP_EXT_SCHEMA_LOCATION.to_string(), + restore: RgpRestoreReportSection { + op: "report".to_string(), + report: RgpRestoreReportData { + pre_data: pre_data.to_string_value(), + post_data: post_data.to_string_value(), + deleted_at: deleted_at + .to_rfc3339_opts(SecondsFormat::AutoSi, true) + .to_string_value(), + restored_at: restored_at + .to_rfc3339_opts(SecondsFormat::AutoSi, true) + .to_string_value(), + restore_reason: restore_reason.to_string_value(), + statements: statements, + other: other.to_string_value(), + }, + }, + }, + }), + client_tr_id: client_tr_id.to_string_value(), + }; + + EppObject::build(command) + } +} diff --git a/epp-client/src/epp/request/domain/rgp/request.rs b/epp-client/src/epp/request/domain/rgp/request.rs new file mode 100644 index 0000000..4d3335e --- /dev/null +++ b/epp-client/src/epp/request/domain/rgp/request.rs @@ -0,0 +1,97 @@ +//! Types for EPP RGP restore request + +use epp_client_macros::*; + +use crate::epp::object::data::HostObjList; +use crate::epp::object::{ElementName, EppObject, StringValueTrait}; +use crate::epp::request::domain::update::{DomainChangeInfo, DomainUpdate, DomainUpdateData}; +use crate::epp::request::{CommandWithExtension, Extension}; +use crate::epp::xml::{ + EPP_DOMAIN_RGP_EXT_SCHEMA_LOCATION, EPP_DOMAIN_RGP_EXT_XMLNS, EPP_DOMAIN_XMLNS, +}; +use serde::{Deserialize, Serialize}; + +/// Type that represents the <epp> request for a domain rgp restore request command +/// +/// ## Usage +/// +/// ```ignore +/// use epp_client::EppClient; +/// use epp_client::epp::{EppDomainRgpRestoreRequest, EppDomainRgpRestoreRequestResponse}; +/// use epp_client::epp::generate_client_tr_id; +/// +/// #[tokio::main] +/// async fn main() { +/// // Create an instance of EppClient, specifying the name of the registry as in +/// // the config file +/// let mut client = match EppClient::new("verisign").await { +/// Ok(client) => client, +/// Err(e) => panic!("Failed to create EppClient: {}", e) +/// }; +/// +/// // Create an EppDomainRgpRestoreRequest instance +/// let domain_restore_req = EppDomainRgpRestoreRequest::new( +/// "eppdev.com", +/// generate_client_tr_id(&client).as_str() +/// ); +/// +/// // send it to the registry and receive a response of type EppDomainRgpRestoreRequestResponse +/// let response = client.transact::<_, EppDomainRgpRestoreRequestResponse>(&domain_restore_req).await.unwrap(); +/// +/// println!("{:?}", response); +/// } +/// ``` +pub type EppDomainRgpRestoreRequest = + EppObject, RgpRestoreRequest>>; + +/// Type corresponding to the <restore> tag for an rgp restore request +#[derive(Serialize, Deserialize, Debug)] +pub struct RgpRestoreRequestData { + /// The value of the op attribute in the <restore> tag + pub op: String, +} + +#[derive(Serialize, Deserialize, Debug, ElementName)] +#[element_name(name = "update")] +/// Type for EPP XML <check> command for domains +pub struct RgpRestoreRequest { + /// XML namespace for the RGP restore extension + xmlns: String, + /// XML schema location for the RGP restore extension + #[serde(rename = "xsi:schemaLocation")] + schema_location: String, + /// The object holding the list of domains to be checked + restore: RgpRestoreRequestData, +} + +impl EppDomainRgpRestoreRequest { + /// Creates a new EppObject for domain rgp restore request corresponding to the <epp> tag in EPP XML + pub fn new(name: &str, client_tr_id: &str) -> EppDomainRgpRestoreRequest { + let command = CommandWithExtension::, RgpRestoreRequest> { + command: DomainUpdate { + domain: DomainUpdateData { + xmlns: EPP_DOMAIN_XMLNS.to_string(), + name: name.to_string_value(), + add: None, + remove: None, + change_info: Some(DomainChangeInfo { + registrant: None, + auth_info: None, + }), + }, + }, + extension: Some(Extension { + data: RgpRestoreRequest { + xmlns: EPP_DOMAIN_RGP_EXT_XMLNS.to_string(), + schema_location: EPP_DOMAIN_RGP_EXT_SCHEMA_LOCATION.to_string(), + restore: RgpRestoreRequestData { + op: "request".to_string(), + }, + }, + }), + client_tr_id: client_tr_id.to_string_value(), + }; + + EppObject::build(command) + } +} diff --git a/epp-client/src/epp/request/domain/transfer.rs b/epp-client/src/epp/request/domain/transfer.rs index c7b7a24..1d8e226 100644 --- a/epp-client/src/epp/request/domain/transfer.rs +++ b/epp-client/src/epp/request/domain/transfer.rs @@ -200,8 +200,8 @@ impl EppDomainTransferRequest { auth_password: &str, client_tr_id: &str, ) -> EppDomainTransferRequest { - EppObject::build(Command:: { - command: DomainTransfer { + EppObject::build(Command::::new( + DomainTransfer { operation: "request".to_string(), domain: DomainTransferData { xmlns: EPP_DOMAIN_XMLNS.to_string(), @@ -210,8 +210,8 @@ impl EppDomainTransferRequest { auth_info: Some(AuthInfo::new(auth_password)), }, }, - client_tr_id: client_tr_id.to_string_value(), - }) + client_tr_id, + )) } /// Sets the period for renewal in case of a successful transfer @@ -223,8 +223,8 @@ impl EppDomainTransferRequest { impl EppDomainTransferApprove { /// Creates a new EppObject for domain transfer approval corresponding to the <epp> tag in EPP XML pub fn approve(name: &str, client_tr_id: &str) -> EppDomainTransferApprove { - EppObject::build(Command:: { - command: DomainTransfer { + EppObject::build(Command::::new( + DomainTransfer { operation: "approve".to_string(), domain: DomainTransferData { xmlns: EPP_DOMAIN_XMLNS.to_string(), @@ -233,16 +233,16 @@ impl EppDomainTransferApprove { auth_info: None, }, }, - client_tr_id: client_tr_id.to_string_value(), - }) + client_tr_id, + )) } } impl EppDomainTransferCancel { /// Creates a new EppObject for domain transfer request cancellation corresponding to the <epp> tag in EPP XML pub fn cancel(name: &str, client_tr_id: &str) -> EppDomainTransferCancel { - EppObject::build(Command:: { - command: DomainTransfer { + EppObject::build(Command::::new( + DomainTransfer { operation: "cancel".to_string(), domain: DomainTransferData { xmlns: EPP_DOMAIN_XMLNS.to_string(), @@ -251,16 +251,16 @@ impl EppDomainTransferCancel { auth_info: None, }, }, - client_tr_id: client_tr_id.to_string_value(), - }) + client_tr_id, + )) } } impl EppDomainTransferReject { /// Creates a new EppObject for domain transfer rejection corresponding to the <epp> tag in EPP XML pub fn reject(name: &str, client_tr_id: &str) -> EppDomainTransferReject { - EppObject::build(Command:: { - command: DomainTransfer { + EppObject::build(Command::::new( + DomainTransfer { operation: "reject".to_string(), domain: DomainTransferData { xmlns: EPP_DOMAIN_XMLNS.to_string(), @@ -269,16 +269,16 @@ impl EppDomainTransferReject { auth_info: None, }, }, - client_tr_id: client_tr_id.to_string_value(), - }) + client_tr_id, + )) } } impl EppDomainTransferQuery { /// Creates a new EppObject for domain transfer request query corresponding to the <epp> tag in EPP XML pub fn query(name: &str, auth_password: &str, client_tr_id: &str) -> EppDomainTransferQuery { - EppObject::build(Command:: { - command: DomainTransfer { + EppObject::build(Command::::new( + DomainTransfer { operation: "query".to_string(), domain: DomainTransferData { xmlns: EPP_DOMAIN_XMLNS.to_string(), @@ -287,7 +287,7 @@ impl EppDomainTransferQuery { auth_info: Some(AuthInfo::new(auth_password)), }, }, - client_tr_id: client_tr_id.to_string_value(), - }) + client_tr_id, + )) } } diff --git a/epp-client/src/epp/request/domain/update.rs b/epp-client/src/epp/request/domain/update.rs index cd50a09..16e7e53 100644 --- a/epp-client/src/epp/request/domain/update.rs +++ b/epp-client/src/epp/request/domain/update.rs @@ -95,19 +95,19 @@ pub struct DomainAddRemove { #[derive(Serialize, Deserialize, Debug)] pub struct DomainUpdateData { /// XML namespace for domain commands - xmlns: String, + pub xmlns: String, /// The name of the domain to update - name: StringValue, + pub name: StringValue, /// `DomainAddRemove` Object containing the list of elements to be added /// to the domain - add: Option>, + pub add: Option>, /// `DomainAddRemove` Object containing the list of elements to be removed /// from the domain #[serde(rename = "rem")] - remove: Option>, + pub remove: Option>, /// The data under the <chg> tag for domain update #[serde(rename = "chg")] - change_info: Option, + pub change_info: Option, } #[derive(Serialize, Deserialize, Debug, ElementName)] @@ -115,15 +115,15 @@ pub struct DomainUpdateData { /// Type for EPP XML <update> command for domains pub struct DomainUpdate { #[serde(rename = "update")] - domain: DomainUpdateData, + pub domain: DomainUpdateData, } impl EppDomainUpdate { /// Creates a new EppObject for domain update corresponding to the <epp> tag in EPP XML /// with the <ns> tag containing <hostObj> tags pub fn new(name: &str, client_tr_id: &str) -> EppDomainUpdate { - EppObject::build(Command::> { - command: DomainUpdate { + EppObject::build(Command::>::new( + DomainUpdate { domain: DomainUpdateData { xmlns: EPP_DOMAIN_XMLNS.to_string(), name: name.to_string_value(), @@ -132,8 +132,8 @@ impl EppDomainUpdate { change_info: None, }, }, - client_tr_id: client_tr_id.to_string_value(), - }) + client_tr_id, + )) } /// Sets the data for the <chg> tag @@ -156,8 +156,8 @@ impl EppDomainUpdateWithHostAttr { /// Creates a new EppObject for domain update corresponding to the <epp> tag in EPP XML /// with the <ns> tag containing <hostAttr> tags pub fn new(name: &str, client_tr_id: &str) -> EppDomainUpdateWithHostAttr { - EppObject::build(Command::> { - command: DomainUpdate { + EppObject::build(Command::>::new( + DomainUpdate { domain: DomainUpdateData { xmlns: EPP_DOMAIN_XMLNS.to_string(), name: name.to_string_value(), @@ -166,8 +166,8 @@ impl EppDomainUpdateWithHostAttr { change_info: None, }, }, - client_tr_id: client_tr_id.to_string_value(), - }) + client_tr_id, + )) } /// Sets the data for the <chg> tag diff --git a/epp-client/src/epp/request/host/check.rs b/epp-client/src/epp/request/host/check.rs index 8fa16f9..2d999b9 100644 --- a/epp-client/src/epp/request/host/check.rs +++ b/epp-client/src/epp/request/host/check.rs @@ -73,9 +73,6 @@ impl EppHostCheck { }, }; - EppObject::build(Command:: { - command: host_check, - client_tr_id: client_tr_id.to_string_value(), - }) + EppObject::build(Command::::new(host_check, client_tr_id)) } } diff --git a/epp-client/src/epp/request/host/create.rs b/epp-client/src/epp/request/host/create.rs index 2bf2e18..a117242 100644 --- a/epp-client/src/epp/request/host/create.rs +++ b/epp-client/src/epp/request/host/create.rs @@ -76,9 +76,6 @@ impl EppHostCreate { }, }; - EppObject::build(Command:: { - command: host_create, - client_tr_id: client_tr_id.to_string_value(), - }) + EppObject::build(Command::::new(host_create, client_tr_id)) } } diff --git a/epp-client/src/epp/request/host/delete.rs b/epp-client/src/epp/request/host/delete.rs index 4d1732b..c196cc5 100644 --- a/epp-client/src/epp/request/host/delete.rs +++ b/epp-client/src/epp/request/host/delete.rs @@ -57,14 +57,14 @@ pub struct HostDelete { impl EppHostDelete { /// Creates a new EppObject for host delete corresponding to the <epp> tag in EPP XML pub fn new(name: &str, client_tr_id: &str) -> EppHostDelete { - EppObject::build(Command:: { - command: HostDelete { + EppObject::build(Command::::new( + HostDelete { host: HostDeleteData { xmlns: EPP_HOST_XMLNS.to_string(), name: name.to_string_value(), }, }, - client_tr_id: client_tr_id.to_string_value(), - }) + client_tr_id, + )) } } diff --git a/epp-client/src/epp/request/host/info.rs b/epp-client/src/epp/request/host/info.rs index b40096e..aad3720 100644 --- a/epp-client/src/epp/request/host/info.rs +++ b/epp-client/src/epp/request/host/info.rs @@ -57,14 +57,14 @@ pub struct HostInfo { impl EppHostInfo { /// Creates a new EppObject for host info corresponding to the <epp> tag in EPP XML pub fn new(name: &str, client_tr_id: &str) -> EppHostInfo { - EppObject::build(Command:: { - command: HostInfo { + EppObject::build(Command::::new( + HostInfo { info: HostInfoData { xmlns: EPP_HOST_XMLNS.to_string(), name: name.to_string_value(), }, }, - client_tr_id: client_tr_id.to_string_value(), - }) + client_tr_id, + )) } } diff --git a/epp-client/src/epp/request/host/update.rs b/epp-client/src/epp/request/host/update.rs index a178a3e..981d221 100644 --- a/epp-client/src/epp/request/host/update.rs +++ b/epp-client/src/epp/request/host/update.rs @@ -107,8 +107,8 @@ pub struct HostUpdate { impl EppHostUpdate { /// Creates a new EppObject for host update corresponding to the <epp> tag in EPP XML pub fn new(name: &str, client_tr_id: &str) -> EppHostUpdate { - EppObject::build(Command:: { - command: HostUpdate { + EppObject::build(Command::::new( + HostUpdate { host: HostUpdateData { xmlns: EPP_HOST_XMLNS.to_string(), name: name.to_string_value(), @@ -117,8 +117,8 @@ impl EppHostUpdate { change_info: None, }, }, - client_tr_id: client_tr_id.to_string_value(), - }) + client_tr_id, + )) } /// Sets the data for the <chg> element of the host update diff --git a/epp-client/src/epp/request/message/ack.rs b/epp-client/src/epp/request/message/ack.rs index cb71185..9094259 100644 --- a/epp-client/src/epp/request/message/ack.rs +++ b/epp-client/src/epp/request/message/ack.rs @@ -2,7 +2,7 @@ use epp_client_macros::*; -use crate::epp::object::{ElementName, EppObject, StringValueTrait}; +use crate::epp::object::{ElementName, EppObject}; use crate::epp::request::Command; use serde::{Deserialize, Serialize}; @@ -49,12 +49,12 @@ pub struct MessageAck { impl EppMessageAck { /// Creates a new EppObject for <poll> ack corresponding to the <epp> tag in EPP XML pub fn new(message_id: u32, client_tr_id: &str) -> EppMessageAck { - EppObject::build(Command:: { - command: MessageAck { + EppObject::build(Command::::new( + MessageAck { op: "ack".to_string(), message_id: message_id.to_string(), }, - client_tr_id: client_tr_id.to_string_value(), - }) + client_tr_id, + )) } } diff --git a/epp-client/src/epp/request/message/poll.rs b/epp-client/src/epp/request/message/poll.rs index 67d2da0..3718b70 100644 --- a/epp-client/src/epp/request/message/poll.rs +++ b/epp-client/src/epp/request/message/poll.rs @@ -2,7 +2,7 @@ use epp_client_macros::*; -use crate::epp::object::{ElementName, EppObject, StringValueTrait}; +use crate::epp::object::{ElementName, EppObject}; use crate::epp::request::Command; use serde::{Deserialize, Serialize}; @@ -47,11 +47,11 @@ pub struct MessagePoll { impl EppMessagePoll { /// Creates a new EppObject for <poll> req corresponding to the <epp> tag in EPP XML pub fn new(client_tr_id: &str) -> EppMessagePoll { - EppObject::build(Command:: { - command: MessagePoll { + EppObject::build(Command::::new( + MessagePoll { op: "req".to_string(), }, - client_tr_id: client_tr_id.to_string_value(), - }) + client_tr_id, + )) } } diff --git a/epp-client/src/epp/response.rs b/epp-client/src/epp/response.rs index bb500e0..f96c6b0 100644 --- a/epp-client/src/epp/response.rs +++ b/epp-client/src/epp/response.rs @@ -7,11 +7,15 @@ pub mod message; use epp_client_macros::*; use serde::{Deserialize, Deserializer, Serialize}; +use std::fmt::Debug; use crate::epp::object::{ - ElementName, EppObject, Options, ServiceExtension, Services, StringValue, + ElementName, EmptyTag, EppObject, Extension, Options, ServiceExtension, Services, StringValue, }; +/// Type corresponding to the <response> tag in an EPP response without an <extension> section +pub type CommandResponse = CommandResponseWithExtension; + /// The EPP Greeting that is received on a successful connection and in response to an EPP hello pub type EppGreeting = EppObject; /// A generic EPP Response to an EPP command with a result section, a status code and a message @@ -224,7 +228,8 @@ pub struct MessageQueue { #[serde(rename_all = "lowercase")] #[element_name(name = "response")] /// Type corresponding to the <response> tag in an EPP response XML -pub struct CommandResponse { +/// containing an <extension> tag +pub struct CommandResponseWithExtension { /// Data under the tag pub result: EppResult, /// Data under the tag @@ -233,6 +238,8 @@ pub struct CommandResponse { #[serde(rename = "resData")] /// Data under the <resData> tag pub res_data: Option, + /// Data under the <extension> tag + pub extension: Option>, /// Data under the tag #[serde(rename = "trID")] pub tr_ids: ResponseTRID, @@ -250,7 +257,7 @@ pub struct CommandResponseStatus { pub tr_ids: ResponseTRID, } -impl CommandResponse { +impl CommandResponseWithExtension { /// Returns the data under the corresponding <resData> from the EPP XML pub fn res_data(&self) -> Option<&T> { match &self.res_data { diff --git a/epp-client/src/epp/response/domain.rs b/epp-client/src/epp/response/domain.rs index c9cf686..b3a8323 100644 --- a/epp-client/src/epp/response/domain.rs +++ b/epp-client/src/epp/response/domain.rs @@ -5,5 +5,6 @@ pub mod create; pub mod delete; pub mod info; pub mod renew; +pub mod rgp; pub mod transfer; pub mod update; diff --git a/epp-client/src/epp/response/domain/info.rs b/epp-client/src/epp/response/domain/info.rs index cb7e18d..536dc53 100644 --- a/epp-client/src/epp/response/domain/info.rs +++ b/epp-client/src/epp/response/domain/info.rs @@ -4,10 +4,12 @@ use serde::{Deserialize, Serialize}; use crate::epp::object::data::{AuthInfo, DomainContact, DomainStatus, HostAttr}; use crate::epp::object::{EppObject, StringValue}; -use crate::epp::response::CommandResponse; +use crate::epp::response::domain::rgp::request::RgpRequestResult; +use crate::epp::response::CommandResponseWithExtension; /// Type that represents the <epp> tag for the EPP XML domain info response -pub type EppDomainInfoResponse = EppObject>; +pub type EppDomainInfoResponse = + EppObject>; /// The two types of ns lists, hostObj and hostAttr, that may be returned in the /// domain info response diff --git a/epp-client/src/epp/response/domain/rgp.rs b/epp-client/src/epp/response/domain/rgp.rs new file mode 100644 index 0000000..20cf477 --- /dev/null +++ b/epp-client/src/epp/response/domain/rgp.rs @@ -0,0 +1,4 @@ +//! Types for RGP restore responses + +pub mod report; +pub mod request; diff --git a/epp-client/src/epp/response/domain/rgp/report.rs b/epp-client/src/epp/response/domain/rgp/report.rs new file mode 100644 index 0000000..9081457 --- /dev/null +++ b/epp-client/src/epp/response/domain/rgp/report.rs @@ -0,0 +1,4 @@ +use crate::epp::response::EppCommandResponse; + +/// Type that represents the <epp> tag for the EPP XML rgp restore report response +pub type EppDomainRgpRestoreReportResponse = EppCommandResponse; diff --git a/epp-client/src/epp/response/domain/rgp/request.rs b/epp-client/src/epp/response/domain/rgp/request.rs new file mode 100644 index 0000000..ea39767 --- /dev/null +++ b/epp-client/src/epp/response/domain/rgp/request.rs @@ -0,0 +1,32 @@ +use epp_client_macros::*; + +use crate::epp::object::{ElementName, EmptyTag, EppObject}; +use crate::epp::response::CommandResponseWithExtension; +use serde::{Deserialize, Serialize}; + +/// Type that represents the <epp> tag for the EPP XML rgp restore request response +pub type EppDomainRgpRestoreRequestResponse = + EppObject>; + +/// Type that represents the <rgpStatus> tag for domain rgp restore request response +#[derive(Serialize, Deserialize, Debug)] +pub struct RgpStatus { + /// The domain RGP status + #[serde(rename = "s")] + pub status: String, +} + +#[derive(Serialize, Deserialize, Debug, ElementName)] +#[serde(rename = "upData")] +#[element_name(name = "upData")] +/// Type that represents the <resData> tag for domain transfer response +pub struct RgpRequestResult { + #[serde(rename = "xmlns:rgp")] + xmlns: String, + /// XML schema location for domain response data + #[serde(rename = "xsi:schemaLocation")] + schema_location: String, + /// Data under the <rgpStatus> tag + #[serde(rename = "rgpStatus")] + pub rgp_status: RgpStatus, +} diff --git a/epp-client/src/epp/xml.rs b/epp-client/src/epp/xml.rs index 6cd665a..00ef46e 100644 --- a/epp-client/src/epp/xml.rs +++ b/epp-client/src/epp/xml.rs @@ -18,6 +18,9 @@ pub const EPP_HOST_XMLNS: &str = "urn:ietf:params:xml:ns:host-1.0"; pub const EPP_CONTACT_SCHEMA_LOCATION: &str = "urn:ietf:params:xml:ns:contact-1.0 contact-1.0.xsd"; pub const EPP_DOMAIN_SCHEMA_LOCATION: &str = "urn:ietf:params:xml:ns:domain-1.0 domain-1.0.xsd"; +pub const EPP_DOMAIN_RGP_EXT_XMLNS: &str = "urn:ietf:params:xml:ns:rgp-1.0"; +pub const EPP_DOMAIN_RGP_EXT_SCHEMA_LOCATION: &str = "urn:ietf:params:xml:ns:rgp-1.0 rgp-1.0.xsd"; + pub const EPP_VERSION: &str = "1.0"; pub const EPP_LANG: &str = "en"; diff --git a/epp-client/src/epp/xml/quick_xml.rs b/epp-client/src/epp/xml/quick_xml.rs index e94e447..b3892a5 100644 --- a/epp-client/src/epp/xml/quick_xml.rs +++ b/epp-client/src/epp/xml/quick_xml.rs @@ -12,12 +12,14 @@ use crate::error; impl EppXml for EppObject { type Output = EppObject; + /// Serializes the EppObject instance to an EPP XML document fn serialize(&self) -> Result> { let epp_xml = format!("{}\r\n{}", EPP_XML_HEADER, se::to_string(self)?); Ok(epp_xml) } + /// Deserializes an EPP XML document to an EppObject instance fn deserialize(epp_xml: &str) -> Result { let object: Self::Output = match from_str(epp_xml) { Ok(v) => v, diff --git a/epp-client/src/lib.rs b/epp-client/src/lib.rs index 123b022..3f4d60f 100644 --- a/epp-client/src/lib.rs +++ b/epp-client/src/lib.rs @@ -30,6 +30,9 @@ //! - Message Poll - [`EppMessagePoll`](epp/request/message/poll/type.EppMessagePoll.html) //! - Message Ack - [`EppMessageAck`](epp/request/message/ack/type.EppMessageAck.html) //! +//! - RGP Restore Request - [`EppDomainRgpRestoreRequest`](epp/request/domain/rgp/request/type.EppDomainRgpRestoreRequest.html) +//! - RGP Restore Report - [`EppDomainRgpRestoreReport`](epp/request/domain/rgp/report/type.EppDomainRgpRestoreReport.html) +//! //! ## Prerequisites //! //! To use the library, you must have an `epp-client/epp-client.toml` config file with the relevant registry @@ -88,6 +91,13 @@ //! .for_each(|chk| println!("Domain: {}, Available: {}", chk.domain.name, chk.domain.available)); //! } //! ``` +//! +//! The output would look similar to the following +//! +//! ``` +//! Domain: eppdev.com, Available: 1 +//! Domain: eppdev.net, Available: 1 +//! ``` #[macro_use] extern crate log; diff --git a/epp-client/src/tests/de.rs b/epp-client/src/tests/de.rs index 835b865..cea2aee 100644 --- a/epp-client/src/tests/de.rs +++ b/epp-client/src/tests/de.rs @@ -746,4 +746,17 @@ mod response { assert_eq!(msg.id, "12345".to_string()); assert_eq!(object.data.tr_ids.server_tr_id, SVTRID.to_string_value()); } + + #[test] + fn rgp_restore_response() { + let xml = get_xml("response/domain/rgp_restore.xml").unwrap(); + let object = EppDomainRgpRestoreRequestResponse::deserialize(xml.as_str()).unwrap(); + + let ext = object.data.extension.unwrap(); + + assert_eq!(object.data.result.code, 1000); + assert_eq!(object.data.result.message, SUCCESS_MSG.to_string_value()); + assert_eq!(ext.data.rgp_status.status, "pendingRestore".to_string()); + assert_eq!(object.data.tr_ids.server_tr_id, SVTRID.to_string_value()); + } } diff --git a/epp-client/src/tests/se.rs b/epp-client/src/tests/se.rs index a732df7..c6e1147 100644 --- a/epp-client/src/tests/se.rs +++ b/epp-client/src/tests/se.rs @@ -11,7 +11,8 @@ mod request { use crate::epp::request::{EppHello, EppLogin, EppLogout}; use crate::epp::xml::EppXml; use crate::epp::*; - use chrono::NaiveDate; + use chrono::{DateTime, NaiveDate}; + use std::str::FromStr; #[test] fn hello() { @@ -481,4 +482,49 @@ mod request { assert_eq!(xml, serialized); } + + #[test] + fn rgp_restore_request() { + let xml = get_xml("request/domain/rgp_restore_request.xml").unwrap(); + + let object = EppDomainRgpRestoreRequest::new("eppdev.com", CLTRID); + + let serialized = object.serialize().unwrap(); + + assert_eq!(xml, serialized); + } + + #[test] + fn rgp_restore_report() { + let xml = get_xml("request/domain/rgp_restore_report.xml").unwrap(); + + let pre_data = + "Pre-delete registration data goes here. Both XML and free text are allowed."; + let post_data = + "Post-restore registration data goes here. Both XML and free text are allowed."; + let deleted_at = DateTime::from_str("2021-07-10T22:00:00.0Z").unwrap(); + let restored_at = DateTime::from_str("2021-07-20T22:00:00.0Z").unwrap(); + let restore_reason = "Registrant error."; + let statements = vec![ + "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.", + "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.", + ]; + let other = "Supporting information goes here."; + + let object = EppDomainRgpRestoreReport::new( + "eppdev.com", + pre_data, + post_data, + deleted_at, + restored_at, + restore_reason, + statements, + other, + CLTRID, + ); + + let serialized = object.serialize().unwrap(); + + assert_eq!(xml, serialized); + } } diff --git a/epp-client/test/resources/request/domain/rgp_restore_report.xml b/epp-client/test/resources/request/domain/rgp_restore_report.xml new file mode 100644 index 0000000..544600d --- /dev/null +++ b/epp-client/test/resources/request/domain/rgp_restore_report.xml @@ -0,0 +1,28 @@ + + + + + + eppdev.com + + + + + + + + Pre-delete registration data goes here. Both XML and free text are allowed. + Post-restore registration data goes here. Both XML and free text are allowed. + 2021-07-10T22:00:00Z + 2021-07-20T22:00:00Z + Registrant error. + 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. + 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. + Supporting information goes here. + + + + + cltrid:1626454866 + + \ No newline at end of file diff --git a/epp-client/test/resources/request/domain/rgp_restore_request.xml b/epp-client/test/resources/request/domain/rgp_restore_request.xml new file mode 100644 index 0000000..6b8df08 --- /dev/null +++ b/epp-client/test/resources/request/domain/rgp_restore_request.xml @@ -0,0 +1,17 @@ + + + + + + eppdev.com + + + + + + + + + cltrid:1626454866 + + \ No newline at end of file diff --git a/epp-client/test/resources/response/domain/rgp_restore.xml b/epp-client/test/resources/response/domain/rgp_restore.xml new file mode 100644 index 0000000..f056dac --- /dev/null +++ b/epp-client/test/resources/response/domain/rgp_restore.xml @@ -0,0 +1,17 @@ + + + + + Command completed successfully + + + + + + + + cltrid:1626454866 + RO-6879-1627224678242975 + + + \ No newline at end of file