new request structure with extensions
This commit is contained in:
parent
898f8a2a2f
commit
35f14973e3
|
@ -51,59 +51,3 @@ pub fn element_name_derive(input: TokenStream) -> TokenStream {
|
|||
|
||||
element_name_macro(&ast)
|
||||
}
|
||||
|
||||
fn epp_request_macro(ast: &syn::DeriveInput) -> proc_macro::TokenStream {
|
||||
let name = &ast.ident;
|
||||
let attr = &ast.attrs[0];
|
||||
let mut response_type: Option<syn::Ident> = None;
|
||||
let (impl_generics, type_generics, _) = &ast.generics.split_for_impl();
|
||||
|
||||
if attr.path.is_ident("response") {
|
||||
match attr.parse_meta() {
|
||||
Ok(syn::Meta::List(meta)) => {
|
||||
let item = &meta.nested[0];
|
||||
match item {
|
||||
syn::NestedMeta::Meta(syn::Meta::Path(p)) => {
|
||||
response_type = Some(p.get_ident().unwrap().clone());
|
||||
}
|
||||
_ => panic!("Failed to parse args for epp_types"),
|
||||
}
|
||||
}
|
||||
_ => panic!("Failed to parse args for epp_types"),
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(resp) = response_type {
|
||||
let implement = quote::quote! {
|
||||
impl #impl_generics EppRequest for #name #type_generics {
|
||||
type Output = #resp;
|
||||
|
||||
fn deserialize_response(&self, epp_xml: &str) -> Result<Self::Output, Box<dyn std::error::Error>> {
|
||||
match Self::Output::deserialize(epp_xml) {
|
||||
Ok(v) => Ok(v),
|
||||
Err(e) => Err(format!("epp-client: Deserialization error: {}", e).into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_request(&self) -> Result<String, Box<dyn std::error::Error>> {
|
||||
match &self.0.serialize() {
|
||||
Ok(serialized) => Ok(serialized.to_string()),
|
||||
Err(e) => Err(format!("epp-client: Serialization error: {}", e).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
implement.into()
|
||||
} else {
|
||||
panic!(
|
||||
"response() needs 1 argument, a response type that implements epp_client::epp::xml::EppXml"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[proc_macro_derive(EppRequest, attributes(response))]
|
||||
pub fn epp_request_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let ast = syn::parse(input).expect("Error while parsing EppTransaction macro input");
|
||||
|
||||
epp_request_macro(&ast)
|
||||
}
|
||||
|
|
|
@ -160,21 +160,14 @@ impl EppClient {
|
|||
|
||||
pub async fn transact_new<T: EppRequest + Debug>(
|
||||
&mut self,
|
||||
request: &T,
|
||||
) -> Result<<T as EppRequest>::Output, error::Error> {
|
||||
let epp_xml = request.serialize_request()?;
|
||||
request: T,
|
||||
) -> Result<<<T as EppRequest>::Response as EppXml>::Output, error::Error> {
|
||||
let epp_xml = request.serialize()?;
|
||||
println!("{}", epp_xml);
|
||||
|
||||
let response = self.connection.transact(&epp_xml).await?;
|
||||
|
||||
let status = EppCommandResponse::deserialize(&response)?;
|
||||
|
||||
if status.data.result.code < 2000 {
|
||||
let response = request.deserialize_response(&response)?;
|
||||
Ok(response)
|
||||
} else {
|
||||
let epp_error = EppCommandResponseError::deserialize(&response)?;
|
||||
Err(error::Error::EppCommandError(epp_error))
|
||||
}
|
||||
T::deserialize(&response)
|
||||
}
|
||||
|
||||
/// Fetches the username used in the registry connection
|
||||
|
|
|
@ -1,17 +1,116 @@
|
|||
use epp_client_macros::*;
|
||||
|
||||
use crate::epp::request::domain::check::EppDomainCheck;
|
||||
use crate::epp::request::EppRequest;
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::epp::object::{ElementName, EppObject, Extension, StringValue};
|
||||
use crate::epp::request::{CommandWithExtension, EppRequest};
|
||||
use crate::epp::response::domain::check::EppDomainCheckResponse;
|
||||
use crate::epp::xml::EppXml;
|
||||
use crate::epp::xml::EPP_DOMAIN_XMLNS;
|
||||
|
||||
#[derive(EppRequest, Debug)]
|
||||
#[response(EppDomainCheckResponse)]
|
||||
pub struct Request(EppDomainCheck);
|
||||
type EppDomainCheck<E> = EppObject<CommandWithExtension<DomainCheck, E>>;
|
||||
|
||||
impl Request {
|
||||
/// Creates a new Request for a domain check
|
||||
pub fn new(domains: Vec<&str>, client_tr_id: &str) -> Request {
|
||||
Request(EppDomainCheck::new(domains, client_tr_id))
|
||||
/// Type that represents the <epp> request for domain <check> command
|
||||
///
|
||||
/// ## Usage
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::collections::HashMap;
|
||||
///
|
||||
/// use epp_client::config::{EppClientConfig, EppClientConnection};
|
||||
/// use epp_client::EppClient;
|
||||
/// use epp_client::epp::object::NoExtension;
|
||||
/// use epp_client::domain;
|
||||
/// use epp_client::epp::generate_client_tr_id;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// // Create a config
|
||||
/// let mut registry: HashMap<String, EppClientConnection> = HashMap::new();
|
||||
/// registry.insert(
|
||||
/// "registry_name".to_owned(),
|
||||
/// EppClientConnection {
|
||||
/// host: "example.com".to_owned(),
|
||||
/// port: 700,
|
||||
/// username: "username".to_owned(),
|
||||
/// password: "password".to_owned(),
|
||||
/// ext_uris: None,
|
||||
/// tls_files: None,
|
||||
/// },
|
||||
/// );
|
||||
/// let config = EppClientConfig { registry };
|
||||
///
|
||||
/// // Create an instance of EppClient, passing the config and the registry you want to connect to
|
||||
/// let mut client = match EppClient::new(&config, "registry_name").await {
|
||||
/// Ok(client) => client,
|
||||
/// Err(e) => panic!("Failed to create EppClient: {}", e)
|
||||
/// };
|
||||
///
|
||||
/// // Create an epp_client::domain::check::Request instance
|
||||
/// let domain_check = domain::check::Request::<NoExtension>::new(
|
||||
/// vec!["eppdev.com", "eppdev.net"],
|
||||
/// None,
|
||||
/// generate_client_tr_id(&client).as_str(),
|
||||
/// );
|
||||
///
|
||||
/// // send it to the registry and receive a response of type EppDomainCheckResponse
|
||||
/// let response = client.transact_new(domain_check).await.unwrap();
|
||||
///
|
||||
/// println!("{:?}", response);
|
||||
///
|
||||
/// client.logout().await.unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct Request<E: ElementName + Serialize + DeserializeOwned + Debug>(EppDomainCheck<E>);
|
||||
|
||||
impl<E: ElementName + Serialize + DeserializeOwned + Debug> EppRequest for Request<E> {
|
||||
type Request = EppDomainCheck<E>;
|
||||
type Response = EppDomainCheckResponse;
|
||||
|
||||
fn as_epp_object(&self) -> &Self::Request {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, ElementName)]
|
||||
#[element_name(name = "check")]
|
||||
/// Type for EPP XML <check> command for domains
|
||||
pub struct DomainCheck {
|
||||
/// The object holding the list of domains to be checked
|
||||
#[serde(rename = "domain:check", alias = "check")]
|
||||
list: DomainList,
|
||||
}
|
||||
|
||||
/// Type for <name> elements under the domain <check> tag
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct DomainList {
|
||||
#[serde(rename = "xmlns:domain", alias = "xmlns")]
|
||||
/// XML namespace for domain commands
|
||||
xmlns: String,
|
||||
#[serde(rename = "domain:name", alias = "name")]
|
||||
/// List of domains to be checked for availability
|
||||
domains: Vec<StringValue>,
|
||||
}
|
||||
|
||||
impl<E: ElementName + Serialize + DeserializeOwned + Debug> Request<E> {
|
||||
pub fn new(
|
||||
domains: impl IntoIterator<Item = impl AsRef<str>>,
|
||||
extension: Option<E>,
|
||||
client_tr_id: &str,
|
||||
) -> Self {
|
||||
Self(EppObject::build(CommandWithExtension::<_, _> {
|
||||
command: DomainCheck {
|
||||
list: DomainList {
|
||||
xmlns: EPP_DOMAIN_XMLNS.to_string(),
|
||||
domains: domains
|
||||
.into_iter()
|
||||
.map(|d| d.as_ref().into())
|
||||
.collect::<Vec<StringValue>>(),
|
||||
},
|
||||
},
|
||||
extension: extension.map(|ext| Extension { data: ext }),
|
||||
client_tr_id: client_tr_id.into(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
//! Types for EPP requests and responses
|
||||
|
||||
pub mod ext;
|
||||
pub mod object;
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
pub mod namestore;
|
|
@ -0,0 +1,2 @@
|
|||
pub mod check;
|
||||
pub mod xml;
|
|
@ -0,0 +1,23 @@
|
|||
use epp_client_macros::*;
|
||||
|
||||
use crate::epp::ext::namestore::xml::EPP_DOMAIN_NAMESTORE_EXT_XMLNS;
|
||||
use crate::epp::object::{ElementName, StringValue};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, ElementName)]
|
||||
#[element_name(name = "namestoreExt:namestoreExt")]
|
||||
pub struct NamestoreCheck {
|
||||
#[serde(rename = "xmlns:namestoreExt", alias = "xmlns")]
|
||||
xmlns: String,
|
||||
#[serde(rename = "namestoreExt:subProduct", alias = "subProduct")]
|
||||
sub_product: StringValue,
|
||||
}
|
||||
|
||||
impl NamestoreCheck {
|
||||
pub fn new(sub_product: &str) -> Self {
|
||||
Self {
|
||||
xmlns: EPP_DOMAIN_NAMESTORE_EXT_XMLNS.into(),
|
||||
sub_product: sub_product.into(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
pub const EPP_DOMAIN_NAMESTORE_EXT_XMLNS: &str = "http://www.verisign-grs.com/epp/namestoreExt-1.1";
|
|
@ -39,7 +39,7 @@ pub trait ElementName {
|
|||
#[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;
|
||||
pub struct NoExtension;
|
||||
|
||||
/// An EPP XML Document that is used either as an EPP XML request or
|
||||
/// an EPP XML response
|
||||
|
|
|
@ -13,27 +13,45 @@ use std::fmt::Debug;
|
|||
use std::time::SystemTime;
|
||||
|
||||
use crate::epp::object::{
|
||||
ElementName, EmptyTag, EppObject, Extension, Options, ServiceExtension, Services, StringValue,
|
||||
ElementName, EppObject, Extension, NoExtension, Options, ServiceExtension, Services,
|
||||
StringValue,
|
||||
};
|
||||
use crate::epp::response::{CommandResponseStatus, EppCommandResponse};
|
||||
use crate::epp::xml::{
|
||||
EppXml, EPP_CONTACT_XMLNS, EPP_DOMAIN_XMLNS, EPP_HOST_XMLNS, EPP_LANG, EPP_VERSION,
|
||||
};
|
||||
|
||||
/// Trait to set correct value for xml tags when tags are being generated from generic types
|
||||
/// Trait to handle serialization and de serialization to required request and response types
|
||||
pub trait EppRequest {
|
||||
type Output: EppXml + Debug;
|
||||
type Request: EppXml + Debug;
|
||||
type Response: EppXml + Serialize + Debug;
|
||||
|
||||
fn deserialize_response(
|
||||
&self,
|
||||
fn as_epp_object(&self) -> &Self::Request;
|
||||
|
||||
fn serialize(&self) -> Result<String, Box<dyn Error>> {
|
||||
self.as_epp_object().serialize()
|
||||
}
|
||||
|
||||
fn deserialize(
|
||||
epp_xml: &str,
|
||||
) -> Result<Self::Output, Box<dyn std::error::Error>>;
|
||||
) -> Result<<Self::Response as EppXml>::Output, crate::error::Error> {
|
||||
let status = <EppCommandResponse as EppXml>::deserialize(epp_xml)?;
|
||||
|
||||
fn serialize_request(&self) -> Result<String, Box<dyn std::error::Error>>;
|
||||
match status.data.result.code {
|
||||
0..=2000 => Self::Response::deserialize(epp_xml),
|
||||
_ => Err(crate::error::Error::EppCommandError(EppObject::build(
|
||||
CommandResponseStatus {
|
||||
result: status.data.result,
|
||||
tr_ids: status.data.tr_ids,
|
||||
},
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Type corresponding to the <command> tag in an EPP XML request
|
||||
/// without an <extension> tag
|
||||
pub type Command<T> = CommandWithExtension<T, EmptyTag>;
|
||||
pub type Command<T> = CommandWithExtension<T, NoExtension>;
|
||||
|
||||
/// The EPP Hello request
|
||||
pub type EppHello = EppObject<Hello>;
|
||||
|
|
|
@ -10,11 +10,12 @@ use serde::{Deserialize, Deserializer, Serialize};
|
|||
use std::fmt::Debug;
|
||||
|
||||
use crate::epp::object::{
|
||||
ElementName, EmptyTag, EppObject, Extension, Options, ServiceExtension, Services, StringValue,
|
||||
ElementName, EppObject, Extension, NoExtension, Options, ServiceExtension, Services,
|
||||
StringValue,
|
||||
};
|
||||
|
||||
/// Type corresponding to the <response> tag in an EPP response without an <extension> section
|
||||
pub type CommandResponse<T> = CommandResponseWithExtension<T, EmptyTag>;
|
||||
pub type CommandResponse<T> = CommandResponseWithExtension<T, NoExtension>;
|
||||
|
||||
/// The EPP Greeting that is received on a successful connection and in response to an EPP hello
|
||||
pub type EppGreeting = EppObject<Greeting>;
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use epp_client_macros::*;
|
||||
|
||||
use crate::epp::object::{ElementName, EmptyTag, EppObject};
|
||||
use crate::epp::object::{ElementName, EppObject, NoExtension};
|
||||
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<CommandResponseWithExtension<EmptyTag, RgpRequestResult>>;
|
||||
EppObject<CommandResponseWithExtension<NoExtension, RgpRequestResult>>;
|
||||
|
||||
/// Type that represents the <rgpStatus> tag for domain rgp restore request response
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
//! Module for automated tests
|
||||
|
||||
pub mod de;
|
||||
pub mod int;
|
||||
pub mod se;
|
||||
|
||||
use regex::Regex;
|
||||
|
|
|
@ -4,11 +4,12 @@ mod request {
|
|||
use super::super::get_xml;
|
||||
use super::super::CLTRID;
|
||||
use crate::domain;
|
||||
use crate::epp::ext::namestore::check::NamestoreCheck;
|
||||
use crate::epp::object::data::{
|
||||
Address, ContactStatus, DomainAuthInfo, DomainContact, DomainStatus, HostAddr, HostAttr,
|
||||
HostStatus, Phone, PostalInfo,
|
||||
};
|
||||
use crate::epp::object::StringValueTrait;
|
||||
use crate::epp::object::NoExtension;
|
||||
use crate::epp::request::{EppHello, EppLogin, EppLogout, EppRequest};
|
||||
use crate::epp::xml::EppXml;
|
||||
use crate::epp::*;
|
||||
|
@ -145,9 +146,30 @@ mod request {
|
|||
fn wrapped_domain_check() {
|
||||
let xml = get_xml("request/domain/check.xml").unwrap();
|
||||
|
||||
let object = domain::check::Request::new(vec!["eppdev.com", "eppdev.net"], CLTRID);
|
||||
let object = domain::check::Request::<NoExtension>::new(
|
||||
vec!["eppdev.com", "eppdev.net"],
|
||||
None,
|
||||
CLTRID,
|
||||
);
|
||||
|
||||
let serialized = object.serialize_request().unwrap();
|
||||
let serialized = object.serialize().unwrap();
|
||||
|
||||
assert_eq!(xml, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn domain_check_namestore() {
|
||||
let xml = get_xml("request/domain/ext/namestore/check.xml").unwrap();
|
||||
|
||||
let ext = NamestoreCheck::new("dotCC");
|
||||
|
||||
let object = domain::check::Request::<NamestoreCheck>::new(
|
||||
vec!["eppdev.com", "eppdev.net"],
|
||||
Some(ext),
|
||||
CLTRID,
|
||||
);
|
||||
|
||||
let serialized = object.serialize().unwrap();
|
||||
|
||||
assert_eq!(xml, serialized);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<check>
|
||||
<domain:check xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>eppdev.com</domain:name>
|
||||
<domain:name>eppdev.net</domain:name>
|
||||
</domain:check>
|
||||
</check>
|
||||
<extension>
|
||||
<namestoreExt:namestoreExt xmlns:namestoreExt="http://www.verisign-grs.com/epp/namestoreExt-1.1">
|
||||
<namestoreExt:subProduct>dotCC</namestoreExt:subProduct>
|
||||
</namestoreExt:namestoreExt>
|
||||
</extension>
|
||||
<clTRID>cltrid:1626454866</clTRID>
|
||||
</command>
|
||||
</epp>
|
|
@ -1,11 +1,12 @@
|
|||
use super::CLTRID;
|
||||
use super::RESOURCES_DIR;
|
||||
use crate::config::EppClientConfig;
|
||||
use crate::domain;
|
||||
use crate::EppClient;
|
||||
use epp_client::config::EppClientConfig;
|
||||
use epp_client::domain;
|
||||
use epp_client::epp::object::NoExtension;
|
||||
use epp_client::EppClient;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use toml;
|
||||
|
||||
const RESOURCES_DIR: &str = "./test/resources";
|
||||
const CLTRID: &str = "cltrid:1626454866";
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore]
|
||||
|
@ -22,9 +23,11 @@ async fn domain_check() {
|
|||
};
|
||||
|
||||
let domains = vec!["eppdev.com", "eppdev.net"];
|
||||
let request = domain::check::Request::new(domains, CLTRID);
|
||||
let request = domain::check::Request::<NoExtension>::new(domains, None, CLTRID);
|
||||
|
||||
client.transact_new(&request).await.unwrap();
|
||||
let response = client.transact_new(request).await.unwrap();
|
||||
|
||||
println!("{:?}", response);
|
||||
|
||||
client.logout().await.unwrap();
|
||||
}
|
Loading…
Reference in New Issue