added RGP restore request and report calls

This commit is contained in:
Ritesh Chitlangi 2021-07-29 23:42:36 +08:00
parent 609de4d89b
commit 0922b82ce0
43 changed files with 720 additions and 183 deletions

View File

@ -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!

View File

@ -1,6 +1,6 @@
[package]
name = "epp-client"
version = "0.1.2"
version = "0.2.0"
edition = "2018"
license = "MIT"
authors = ["Ritesh Chitlangi <ritesh@ayravat.com>"]
@ -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"

View File

@ -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!

View File

@ -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::*;

View File

@ -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<T: ElementName + Serialize> Serialize for EppObject<T> {
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 &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")]
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 data_name = self.data.element_name();
let mut state = serializer.serialize_struct("extension", 1)?;
state.serialize_field(data_name, &self.data)?;
state.end()
}
}
/// The <svcExtension> type in EPP XML
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename = "svcExtension")]

View File

@ -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 &lt;command&gt; tag in an EPP XML request
/// without an &lt;extension&gt; tag
pub type Command<T> = CommandWithExtension<T, EmptyTag>;
/// The EPP Hello request
pub type EppHello = EppObject<Hello>;
/// The EPP Login Request
@ -25,28 +30,55 @@ pub type EppLogout = EppObject<Command<Logout>>;
#[derive(Deserialize, Debug, PartialEq, ElementName)]
#[element_name(name = "command")]
/// Type corresponding to the &lt;command&gt; tag in an EPP XML request
pub struct Command<T: ElementName> {
/// with an &lt;extension&gt; tag
pub struct CommandWithExtension<T: ElementName, E: ElementName> {
/// The instance that will be used to populate the &lt;command&gt; tag
pub command: T,
/// The client TRID
pub extension: Option<Extension<E>>,
#[serde(rename = "clTRID")]
pub client_tr_id: StringValue,
}
impl<T: ElementName + Serialize> Serialize for Command<T> {
impl<T: ElementName + Serialize, E: ElementName + Serialize> Serialize
for CommandWithExtension<T, 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 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<T: ElementName> Command<T> {
/// Creates a new &lt;command&gt; tag for an EPP document
pub fn new(command: T, client_tr_id: &str) -> Command<T> {
Command {
command: command,
extension: None,
client_tr_id: client_tr_id.to_string_value(),
}
}
}
impl<T: ElementName, E: ElementName> CommandWithExtension<T, E> {
/// Creates a new &lt;command&gt; tag for an EPP document with a containing &lt;extension&gt; tag
pub fn build(command: T, ext: E, client_tr_id: &str) -> CommandWithExtension<T, E> {
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<String, Box<dyn Error>> {
let timestamp = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
@ -118,6 +150,7 @@ impl EppLogin {
EppObject::build(Command::<Login> {
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::<Logout> {
command: Logout,
extension: None,
client_tr_id: client_tr_id.to_string_value(),
})
}

View File

@ -73,9 +73,6 @@ impl EppContactCheck {
},
};
EppObject::build(Command::<ContactCheck> {
command: contact_check,
client_tr_id: client_tr_id.to_string_value(),
})
EppObject::build(Command::<ContactCheck>::new(contact_check, client_tr_id))
}
}

View File

@ -107,10 +107,7 @@ impl EppContactCreate {
},
};
EppObject::build(Command::<ContactCreate> {
command: contact_create,
client_tr_id: client_tr_id.to_string_value(),
})
EppObject::build(Command::<ContactCreate>::new(contact_create, client_tr_id))
}
/// Sets the &lt;fax&gt; data for the request

View File

@ -60,14 +60,13 @@ pub struct ContactDelete {
impl EppContactDelete {
/// Creates a new EppObject for contact delete corresponding to the &lt;epp&gt; tag in EPP XML
pub fn new(id: &str, client_tr_id: &str) -> EppContactDelete {
EppObject::build(Command::<ContactDelete> {
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::<ContactDelete>::new(contact_delete, client_tr_id))
}
}

View File

@ -65,15 +65,14 @@ pub struct ContactInfo {
impl EppContactInfo {
/// Creates a new EppObject for contact info corresponding to the &lt;epp&gt; tag in EPP XML
pub fn new(id: &str, auth_password: &str, client_tr_id: &str) -> EppContactInfo {
EppObject::build(Command::<ContactInfo> {
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::<ContactInfo>::new(contact_info, client_tr_id))
}
}

View File

@ -93,18 +93,16 @@ pub struct ContactUpdate {
impl EppContactUpdate {
/// Creates a new EppObject for contact update corresponding to the &lt;epp&gt; tag in EPP XML
pub fn new(id: &str, client_tr_id: &str) -> EppContactUpdate {
EppObject::build(Command::<ContactUpdate> {
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::<ContactUpdate>::new(contact_update, client_tr_id))
}
/// Sets the data for the &lt;chg&gt; tag for the contact update request

View File

@ -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;

View File

@ -73,9 +73,6 @@ impl EppDomainCheck {
},
};
EppObject::build(Command::<DomainCheck> {
command: domain_check,
client_tr_id: client_tr_id.to_string_value(),
})
EppObject::build(Command::<DomainCheck>::new(domain_check, client_tr_id))
}
}

View File

@ -112,20 +112,22 @@ impl EppDomainCreate {
.map(|n| n.to_string_value())
.collect::<Vec<StringValue>>();
EppObject::build(Command::<DomainCreate<HostObjList>> {
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::<DomainCreate<HostObjList>>::new(
domain_create,
client_tr_id,
))
}
/// Creates a new EppObject for domain create corresponding to the &lt;epp&gt; tag in EPP XML
@ -138,20 +140,21 @@ impl EppDomainCreate {
contacts: Vec<DomainContact>,
client_tr_id: &str,
) -> EppDomainCreate {
EppObject::build(Command::<DomainCreate<HostObjList>> {
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::<DomainCreate<HostObjList>>::new(
domain_create,
client_tr_id,
))
}
/// Creates a new EppObject for domain create corresponding to the &lt;epp&gt; tag in EPP XML
@ -162,20 +165,22 @@ impl EppDomainCreate {
auth_password: &str,
client_tr_id: &str,
) -> EppDomainCreate {
EppObject::build(Command::<DomainCreate<HostObjList>> {
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::<DomainCreate<HostObjList>>::new(
domain_create,
client_tr_id,
))
}
/// Creates a new EppObject for domain create corresponding to the &lt;epp&gt; tag in EPP XML
@ -189,19 +194,20 @@ impl EppDomainCreate {
contacts: Vec<DomainContact>,
client_tr_id: &str,
) -> EppDomainCreateWithHostAttr {
EppObject::build(Command::<DomainCreate<HostAttrList>> {
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::<DomainCreate<HostAttrList>>::new(
domain_create,
client_tr_id,
))
}
}

View File

@ -57,14 +57,14 @@ pub struct DomainDelete {
impl EppDomainDelete {
/// Creates a new EppObject for domain delete corresponding to the &lt;epp&gt; tag in EPP XML
pub fn new(name: &str, client_tr_id: &str) -> EppDomainDelete {
EppObject::build(Command::<DomainDelete> {
command: DomainDelete {
EppObject::build(Command::<DomainDelete>::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,
))
}
}

View File

@ -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 &lt;epp&gt; tag in EPP XML
pub fn new(name: &str, client_tr_id: &str) -> EppDomainInfo {
EppObject::build(Command::<DomainInfo> {
command: DomainInfo {
EppObject::build(Command::<DomainInfo>::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,
))
}
}

View File

@ -78,8 +78,8 @@ impl EppDomainRenew {
.to_string()
.to_string_value();
EppObject::build(Command::<DomainRenew> {
command: DomainRenew {
EppObject::build(Command::<DomainRenew>::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) {

View File

@ -0,0 +1,4 @@
//! Types for EPP RGP restore requests
pub mod report;
pub mod request;

View File

@ -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 &lt;epp&gt; 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<CommandWithExtension<DomainUpdate<HostObjList>, RgpRestoreReport>>;
/// Type corresponding to the &lt;report&gt; 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<StringValue>,
/// Other remarks for domain restoration
other: StringValue,
}
/// Type corresponding to the &lt;restore&gt; section in the rgp restore extension
#[derive(Serialize, Deserialize, Debug)]
pub struct RgpRestoreReportSection {
/// The value of the op attribute for the &lt;restore&gt; tag
op: String,
/// Data for the &lt;report&gt; tag
report: RgpRestoreReportData,
}
#[derive(Serialize, Deserialize, Debug, ElementName)]
#[element_name(name = "update")]
/// Type for EPP XML &lt;check&gt; 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 &lt;epp&gt; tag in EPP XML
pub fn new(
name: &str,
pre_data: &str,
post_data: &str,
deleted_at: DateTime<Utc>,
restored_at: DateTime<Utc>,
restore_reason: &str,
statements: Vec<&str>,
other: &str,
client_tr_id: &str,
) -> EppDomainRgpRestoreReport {
let statements = statements
.iter()
.map(|s| s.to_string_value())
.collect::<Vec<StringValue>>();
let command = CommandWithExtension::<DomainUpdate<HostObjList>, 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)
}
}

View File

@ -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 &lt;epp&gt; 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<CommandWithExtension<DomainUpdate<HostObjList>, RgpRestoreRequest>>;
/// Type corresponding to the &lt;restore&gt; tag for an rgp restore request
#[derive(Serialize, Deserialize, Debug)]
pub struct RgpRestoreRequestData {
/// The value of the op attribute in the &lt;restore&gt; tag
pub op: String,
}
#[derive(Serialize, Deserialize, Debug, ElementName)]
#[element_name(name = "update")]
/// Type for EPP XML &lt;check&gt; 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 &lt;epp&gt; tag in EPP XML
pub fn new(name: &str, client_tr_id: &str) -> EppDomainRgpRestoreRequest {
let command = CommandWithExtension::<DomainUpdate<HostObjList>, 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)
}
}

View File

@ -200,8 +200,8 @@ impl EppDomainTransferRequest {
auth_password: &str,
client_tr_id: &str,
) -> EppDomainTransferRequest {
EppObject::build(Command::<DomainTransfer> {
command: DomainTransfer {
EppObject::build(Command::<DomainTransfer>::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 &lt;epp&gt; tag in EPP XML
pub fn approve(name: &str, client_tr_id: &str) -> EppDomainTransferApprove {
EppObject::build(Command::<DomainTransfer> {
command: DomainTransfer {
EppObject::build(Command::<DomainTransfer>::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 &lt;epp&gt; tag in EPP XML
pub fn cancel(name: &str, client_tr_id: &str) -> EppDomainTransferCancel {
EppObject::build(Command::<DomainTransfer> {
command: DomainTransfer {
EppObject::build(Command::<DomainTransfer>::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 &lt;epp&gt; tag in EPP XML
pub fn reject(name: &str, client_tr_id: &str) -> EppDomainTransferReject {
EppObject::build(Command::<DomainTransfer> {
command: DomainTransfer {
EppObject::build(Command::<DomainTransfer>::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 &lt;epp&gt; tag in EPP XML
pub fn query(name: &str, auth_password: &str, client_tr_id: &str) -> EppDomainTransferQuery {
EppObject::build(Command::<DomainTransfer> {
command: DomainTransfer {
EppObject::build(Command::<DomainTransfer>::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,
))
}
}

View File

@ -95,19 +95,19 @@ pub struct DomainAddRemove<T> {
#[derive(Serialize, Deserialize, Debug)]
pub struct DomainUpdateData<T> {
/// 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<DomainAddRemove<T>>,
pub add: Option<DomainAddRemove<T>>,
/// `DomainAddRemove` Object containing the list of elements to be removed
/// from the domain
#[serde(rename = "rem")]
remove: Option<DomainAddRemove<T>>,
pub remove: Option<DomainAddRemove<T>>,
/// The data under the &lt;chg&gt; tag for domain update
#[serde(rename = "chg")]
change_info: Option<DomainChangeInfo>,
pub change_info: Option<DomainChangeInfo>,
}
#[derive(Serialize, Deserialize, Debug, ElementName)]
@ -115,15 +115,15 @@ pub struct DomainUpdateData<T> {
/// Type for EPP XML &lt;update&gt; command for domains
pub struct DomainUpdate<T> {
#[serde(rename = "update")]
domain: DomainUpdateData<T>,
pub domain: DomainUpdateData<T>,
}
impl EppDomainUpdate {
/// Creates a new EppObject for domain update corresponding to the &lt;epp&gt; tag in EPP XML
/// with the &lt;ns&gt; tag containing &lt;hostObj&gt; tags
pub fn new(name: &str, client_tr_id: &str) -> EppDomainUpdate {
EppObject::build(Command::<DomainUpdate<HostObjList>> {
command: DomainUpdate {
EppObject::build(Command::<DomainUpdate<HostObjList>>::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 &lt;chg&gt; tag
@ -156,8 +156,8 @@ impl EppDomainUpdateWithHostAttr {
/// Creates a new EppObject for domain update corresponding to the &lt;epp&gt; tag in EPP XML
/// with the &lt;ns&gt; tag containing &lt;hostAttr&gt; tags
pub fn new(name: &str, client_tr_id: &str) -> EppDomainUpdateWithHostAttr {
EppObject::build(Command::<DomainUpdate<HostAttrList>> {
command: DomainUpdate {
EppObject::build(Command::<DomainUpdate<HostAttrList>>::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 &lt;chg&gt; tag

View File

@ -73,9 +73,6 @@ impl EppHostCheck {
},
};
EppObject::build(Command::<HostCheck> {
command: host_check,
client_tr_id: client_tr_id.to_string_value(),
})
EppObject::build(Command::<HostCheck>::new(host_check, client_tr_id))
}
}

View File

@ -76,9 +76,6 @@ impl EppHostCreate {
},
};
EppObject::build(Command::<HostCreate> {
command: host_create,
client_tr_id: client_tr_id.to_string_value(),
})
EppObject::build(Command::<HostCreate>::new(host_create, client_tr_id))
}
}

View File

@ -57,14 +57,14 @@ pub struct HostDelete {
impl EppHostDelete {
/// Creates a new EppObject for host delete corresponding to the &lt;epp&gt; tag in EPP XML
pub fn new(name: &str, client_tr_id: &str) -> EppHostDelete {
EppObject::build(Command::<HostDelete> {
command: HostDelete {
EppObject::build(Command::<HostDelete>::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,
))
}
}

View File

@ -57,14 +57,14 @@ pub struct HostInfo {
impl EppHostInfo {
/// Creates a new EppObject for host info corresponding to the &lt;epp&gt; tag in EPP XML
pub fn new(name: &str, client_tr_id: &str) -> EppHostInfo {
EppObject::build(Command::<HostInfo> {
command: HostInfo {
EppObject::build(Command::<HostInfo>::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,
))
}
}

View File

@ -107,8 +107,8 @@ pub struct HostUpdate {
impl EppHostUpdate {
/// Creates a new EppObject for host update corresponding to the &lt;epp&gt; tag in EPP XML
pub fn new(name: &str, client_tr_id: &str) -> EppHostUpdate {
EppObject::build(Command::<HostUpdate> {
command: HostUpdate {
EppObject::build(Command::<HostUpdate>::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 &lt;chg&gt; element of the host update

View File

@ -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 &lt;poll&gt; ack corresponding to the &lt;epp&gt; tag in EPP XML
pub fn new(message_id: u32, client_tr_id: &str) -> EppMessageAck {
EppObject::build(Command::<MessageAck> {
command: MessageAck {
EppObject::build(Command::<MessageAck>::new(
MessageAck {
op: "ack".to_string(),
message_id: message_id.to_string(),
},
client_tr_id: client_tr_id.to_string_value(),
})
client_tr_id,
))
}
}

View File

@ -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 &lt;poll&gt; req corresponding to the &lt;epp&gt; tag in EPP XML
pub fn new(client_tr_id: &str) -> EppMessagePoll {
EppObject::build(Command::<MessagePoll> {
command: MessagePoll {
EppObject::build(Command::<MessagePoll>::new(
MessagePoll {
op: "req".to_string(),
},
client_tr_id: client_tr_id.to_string_value(),
})
client_tr_id,
))
}
}

View File

@ -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 &lt;response&gt; tag in an EPP response without an &lt;extension&gt; section
pub type CommandResponse<T> = CommandResponseWithExtension<T, EmptyTag>;
/// The EPP Greeting that is received on a successful connection and in response to an EPP hello
pub type EppGreeting = EppObject<Greeting>;
/// 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 &lt;response&gt; tag in an EPP response XML
pub struct CommandResponse<T> {
/// containing an &lt;extension&gt; tag
pub struct CommandResponseWithExtension<T, E: ElementName> {
/// Data under the <result> tag
pub result: EppResult,
/// Data under the <msgQ> tag
@ -233,6 +238,8 @@ pub struct CommandResponse<T> {
#[serde(rename = "resData")]
/// Data under the &lt;resData&gt; tag
pub res_data: Option<T>,
/// Data under the &lt;extension&gt; tag
pub extension: Option<Extension<E>>,
/// Data under the <trID> tag
#[serde(rename = "trID")]
pub tr_ids: ResponseTRID,
@ -250,7 +257,7 @@ pub struct CommandResponseStatus {
pub tr_ids: ResponseTRID,
}
impl<T> CommandResponse<T> {
impl<T, E: ElementName> CommandResponseWithExtension<T, E> {
/// Returns the data under the corresponding &lt;resData&gt; from the EPP XML
pub fn res_data(&self) -> Option<&T> {
match &self.res_data {

View File

@ -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;

View File

@ -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 &lt;epp&gt; tag for the EPP XML domain info response
pub type EppDomainInfoResponse = EppObject<CommandResponse<DomainInfoResult>>;
pub type EppDomainInfoResponse =
EppObject<CommandResponseWithExtension<DomainInfoResult, RgpRequestResult>>;
/// The two types of ns lists, hostObj and hostAttr, that may be returned in the
/// domain info response

View File

@ -0,0 +1,4 @@
//! Types for RGP restore responses
pub mod report;
pub mod request;

View File

@ -0,0 +1,4 @@
use crate::epp::response::EppCommandResponse;
/// Type that represents the &lt;epp&gt; tag for the EPP XML rgp restore report response
pub type EppDomainRgpRestoreReportResponse = EppCommandResponse;

View File

@ -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 &lt;epp&gt; tag for the EPP XML rgp restore request response
pub type EppDomainRgpRestoreRequestResponse =
EppObject<CommandResponseWithExtension<EmptyTag, RgpRequestResult>>;
/// Type that represents the &lt;rgpStatus&gt; 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 &lt;resData&gt; 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 &lt;rgpStatus&gt; tag
#[serde(rename = "rgpStatus")]
pub rgp_status: RgpStatus,
}

View File

@ -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";

View File

@ -12,12 +12,14 @@ use crate::error;
impl<T: Serialize + DeserializeOwned + ElementName + Debug> EppXml for EppObject<T> {
type Output = EppObject<T>;
/// Serializes the EppObject instance to an EPP XML document
fn serialize(&self) -> Result<String, Box<dyn Error>> {
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<Self::Output, error::Error> {
let object: Self::Output = match from_str(epp_xml) {
Ok(v) => v,

View File

@ -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;

View File

@ -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());
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:ietf:params:xml:ns:epp-1.0 epp-1.0.xsd">
<command>
<update>
<update xmlns="urn:ietf:params:xml:ns:domain-1.0">
<name>eppdev.com</name>
<chg/>
</update>
</update>
<extension>
<update xmlns="urn:ietf:params:xml:ns:rgp-1.0" xsi:schemaLocation="urn:ietf:params:xml:ns:rgp-1.0 rgp-1.0.xsd">
<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&apos;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>
</epp>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:ietf:params:xml:ns:epp-1.0 epp-1.0.xsd">
<command>
<update>
<update xmlns="urn:ietf:params:xml:ns:domain-1.0">
<name>eppdev.com</name>
<chg/>
</update>
</update>
<extension>
<update xmlns="urn:ietf:params:xml:ns:rgp-1.0" xsi:schemaLocation="urn:ietf:params:xml:ns:rgp-1.0 rgp-1.0.xsd">
<restore op="request"/>
</update>
</extension>
<clTRID>cltrid:1626454866</clTRID>
</command>
</epp>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:ietf:params:xml:ns:epp-1.0 epp-1.0.xsd">
<response>
<result code="1000">
<msg lang="en">Command completed successfully</msg>
</result>
<extension>
<rgp:upData xmlns:rgp="urn:ietf:params:xml:ns:rgp-1.0" xsi:schemaLocation="urn:ietf:params:xml:ns:rgp-1.0 rgp-1.0.xsd">
<rgp:rgpStatus s="pendingRestore"/>
</rgp:upData>
</extension>
<trID>
<clTRID>cltrid:1626454866</clTRID>
<svTRID>RO-6879-1627224678242975</svTRID>
</trID>
</response>
</epp>