new transact() function

This commit is contained in:
Ritesh Chitlangi 2021-11-12 23:13:52 +08:00
parent 458f39a6ed
commit 898f8a2a2f
11 changed files with 160 additions and 4 deletions

1
.gitignore vendored
View File

@ -4,3 +4,4 @@
/config /config
/epp-client/examples /epp-client/examples
Cargo.lock Cargo.lock
**/secrets

View File

@ -51,3 +51,59 @@ pub fn element_name_derive(input: TokenStream) -> TokenStream {
element_name_macro(&ast) 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)
}

View File

@ -26,3 +26,4 @@ webpki-roots = "0.22.1"
[dev-dependencies] [dev-dependencies]
tokio-test = "0.4" tokio-test = "0.4"
regex = "1.5" regex = "1.5"
toml = "0.5"

View File

@ -51,7 +51,7 @@ use std::{error::Error, fmt::Debug};
use crate::config::EppClientConfig; use crate::config::EppClientConfig;
use crate::connection::registry::{epp_connect, EppConnection}; use crate::connection::registry::{epp_connect, EppConnection};
use crate::epp::request::{generate_client_tr_id, EppHello, EppLogin, EppLogout}; use crate::epp::request::{generate_client_tr_id, EppHello, EppLogin, EppLogout, EppRequest};
use crate::epp::response::{ use crate::epp::response::{
EppCommandResponse, EppCommandResponseError, EppGreeting, EppLoginResponse, EppLogoutResponse, EppCommandResponse, EppCommandResponseError, EppGreeting, EppLoginResponse, EppLogoutResponse,
}; };
@ -158,6 +158,25 @@ 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()?;
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))
}
}
/// Fetches the username used in the registry connection /// Fetches the username used in the registry connection
pub fn username(&self) -> String { pub fn username(&self) -> String {
self.credentials.0.to_string() self.credentials.0.to_string()

1
epp-client/src/domain.rs Normal file
View File

@ -0,0 +1 @@
pub mod check;

View File

@ -0,0 +1,17 @@
use epp_client_macros::*;
use crate::epp::request::domain::check::EppDomainCheck;
use crate::epp::request::EppRequest;
use crate::epp::response::domain::check::EppDomainCheckResponse;
use crate::epp::xml::EppXml;
#[derive(EppRequest, Debug)]
#[response(EppDomainCheckResponse)]
pub struct Request(EppDomainCheck);
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))
}
}

View File

@ -5,15 +5,31 @@ pub mod domain;
pub mod host; pub mod host;
pub mod message; pub mod message;
use epp_client_macros::*;
use serde::{ser::SerializeStruct, ser::Serializer, Deserialize, Serialize}; use serde::{ser::SerializeStruct, ser::Serializer, Deserialize, Serialize};
use std::error::Error; use std::error::Error;
use std::fmt::Debug;
use std::time::SystemTime; use std::time::SystemTime;
use crate::epp::object::{ use crate::epp::object::{
ElementName, EmptyTag, EppObject, Extension, Options, ServiceExtension, Services, StringValue, ElementName, EmptyTag, EppObject, Extension, Options, ServiceExtension, Services, StringValue,
}; };
use crate::epp::xml::{EPP_CONTACT_XMLNS, EPP_DOMAIN_XMLNS, EPP_HOST_XMLNS, EPP_LANG, EPP_VERSION}; use crate::epp::xml::{
use epp_client_macros::*; 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
pub trait EppRequest {
type Output: EppXml + Debug;
fn deserialize_response(
&self,
epp_xml: &str,
) -> Result<Self::Output, Box<dyn std::error::Error>>;
fn serialize_request(&self) -> Result<String, Box<dyn std::error::Error>>;
}
/// Type corresponding to the &lt;command&gt; tag in an EPP XML request /// Type corresponding to the &lt;command&gt; tag in an EPP XML request
/// without an &lt;extension&gt; tag /// without an &lt;extension&gt; tag

View File

@ -102,6 +102,7 @@ extern crate log;
pub mod config; pub mod config;
pub mod connection; pub mod connection;
pub mod domain;
pub mod epp; pub mod epp;
pub mod error; pub mod error;
pub use connection::client::EppClient; pub use connection::client::EppClient;

View File

@ -0,0 +1,30 @@
use super::CLTRID;
use super::RESOURCES_DIR;
use crate::config::EppClientConfig;
use crate::domain;
use crate::EppClient;
use std::fs;
use std::path::Path;
use toml;
#[tokio::test]
#[ignore]
async fn domain_check() {
let config_file = format!("{}/{}", RESOURCES_DIR, "secrets/epp-client.toml");
let config_path = Path::new(config_file.as_str());
let contents = &fs::read_to_string(config_path).unwrap();
let config: EppClientConfig = toml::from_str(contents).unwrap();
let mut client = match EppClient::new(&config, "testing").await {
Ok(client) => client,
Err(e) => panic!("Failed to create EppClient: {}", e),
};
let domains = vec!["eppdev.com", "eppdev.net"];
let request = domain::check::Request::new(domains, CLTRID);
client.transact_new(&request).await.unwrap();
client.logout().await.unwrap();
}

View File

@ -1,6 +1,7 @@
//! Module for automated tests //! Module for automated tests
pub mod de; pub mod de;
pub mod int;
pub mod se; pub mod se;
use regex::Regex; use regex::Regex;

View File

@ -3,11 +3,13 @@
mod request { mod request {
use super::super::get_xml; use super::super::get_xml;
use super::super::CLTRID; use super::super::CLTRID;
use crate::domain;
use crate::epp::object::data::{ use crate::epp::object::data::{
Address, ContactStatus, DomainAuthInfo, DomainContact, DomainStatus, HostAddr, HostAttr, Address, ContactStatus, DomainAuthInfo, DomainContact, DomainStatus, HostAddr, HostAttr,
HostStatus, Phone, PostalInfo, HostStatus, Phone, PostalInfo,
}; };
use crate::epp::request::{EppHello, EppLogin, EppLogout}; use crate::epp::object::StringValueTrait;
use crate::epp::request::{EppHello, EppLogin, EppLogout, EppRequest};
use crate::epp::xml::EppXml; use crate::epp::xml::EppXml;
use crate::epp::*; use crate::epp::*;
use chrono::{DateTime, NaiveDate}; use chrono::{DateTime, NaiveDate};
@ -139,6 +141,17 @@ mod request {
assert_eq!(xml, serialized); assert_eq!(xml, serialized);
} }
#[test]
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 serialized = object.serialize_request().unwrap();
assert_eq!(xml, serialized);
}
#[test] #[test]
fn domain_create() { fn domain_create() {
let xml = get_xml("request/domain/create.xml").unwrap(); let xml = get_xml("request/domain/create.xml").unwrap();