mirror of
https://github.com/instant-labs/instant-epp.git
synced 2025-02-08 09:52:03 +00:00
introduce connection level extensions
Some EPP extensions such as change poll didn't map to the present command oriented extensions. The <changePoll> element will be present in the poll response without any elements in the <extension> element of the request. There is no allowed value for the <extension> element for the poll command (if you only support RGP, NameStore, and ChangePoll). This now stores enabled general extensions in the EppClient. This allows you to retrieve command level extensions responses using Response::command_extension and connection level extensions using Response::connection_extension
This commit is contained in:
parent
d50f10a2b0
commit
785d3f6771
@ -1,3 +1,4 @@
|
||||
use std::marker::PhantomData;
|
||||
use std::time::Duration;
|
||||
|
||||
#[cfg(feature = "__rustls")]
|
||||
@ -10,7 +11,7 @@ use crate::connection::EppConnection;
|
||||
use crate::error::Error;
|
||||
use crate::hello::{Greeting, Hello};
|
||||
use crate::request::{Command, CommandWrapper, Extension, Transaction};
|
||||
use crate::response::{Response, ResponseStatus};
|
||||
use crate::response::{ConnectionExtensionResponse, Response, ResponseStatus};
|
||||
use crate::xml;
|
||||
|
||||
/// An `EppClient` provides an interface to sending EPP requests to a registry
|
||||
@ -64,12 +65,13 @@ use crate::xml;
|
||||
/// Domain: eppdev.com, Available: 1
|
||||
/// Domain: eppdev.net, Available: 1
|
||||
/// ```
|
||||
pub struct EppClient<C: Connector> {
|
||||
pub struct EppClient<C: Connector, ConnExt> {
|
||||
connection: EppConnection<C>,
|
||||
phantom_type: PhantomData<ConnExt>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "__rustls")]
|
||||
impl EppClient<RustlsConnector> {
|
||||
impl<ConnExt: ConnectionExtensionResponse> EppClient<RustlsConnector, ConnExt> {
|
||||
/// Connect to the specified `addr` and `hostname` over TLS
|
||||
///
|
||||
/// The `registry` is used as a name in internal logging; `host` provides the host name
|
||||
@ -97,11 +99,12 @@ impl EppClient<RustlsConnector> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: Connector> EppClient<C> {
|
||||
impl<C: Connector, ConnExt: ConnectionExtensionResponse> EppClient<C, ConnExt> {
|
||||
/// Create an `EppClient` from an already established connection
|
||||
pub async fn new(connector: C, registry: String, timeout: Duration) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
connection: EppConnection::new(connector, registry, timeout).await?,
|
||||
phantom_type: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
@ -120,7 +123,7 @@ impl<C: Connector> EppClient<C> {
|
||||
&mut self,
|
||||
data: impl Into<RequestData<'c, 'e, Cmd, CmdExt>>,
|
||||
id: &str,
|
||||
) -> Result<Response<Cmd::Response, CmdExt::Response>, Error>
|
||||
) -> Result<Response<Cmd::Response, CmdExt::Response, ConnExt>, Error>
|
||||
where
|
||||
Cmd: Transaction<CmdExt> + Command + 'c,
|
||||
CmdExt: Extension + 'e,
|
||||
@ -133,13 +136,15 @@ impl<C: Connector> EppClient<C> {
|
||||
let response = self.connection.transact(&xml)?.await?;
|
||||
debug!("{}: response: {}", self.connection.registry, &response);
|
||||
|
||||
let rsp = match xml::deserialize::<Response<Cmd::Response, CmdExt::Response>>(&response) {
|
||||
Ok(rsp) => rsp,
|
||||
Err(e) => {
|
||||
error!(%response, "failed to deserialize response for transaction: {e}");
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
let rsp =
|
||||
match xml::deserialize::<Response<Cmd::Response, CmdExt::Response, ConnExt>>(&response)
|
||||
{
|
||||
Ok(rsp) => rsp,
|
||||
Err(e) => {
|
||||
error!(%response, "failed to deserialize response for transaction: {e}");
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
if rsp.result.code.is_success() {
|
||||
return Ok(rsp);
|
||||
|
@ -7,18 +7,11 @@ use std::borrow::Cow;
|
||||
|
||||
use instant_xml::{Error, FromXml, ToXml};
|
||||
|
||||
use crate::{
|
||||
poll::Poll,
|
||||
request::{Extension, Transaction},
|
||||
};
|
||||
use crate::response::ConnectionExtensionResponse;
|
||||
|
||||
pub const XMLNS: &str = "urn:ietf:params:xml:ns:changePoll-1.0";
|
||||
|
||||
impl Transaction<ChangePoll<'_>> for Poll {}
|
||||
|
||||
impl Extension for ChangePoll<'_> {
|
||||
type Response = ChangePoll<'static>;
|
||||
}
|
||||
impl ConnectionExtensionResponse for ChangePoll<'_> {}
|
||||
|
||||
/// Type for EPP XML `<changePoll>` extension
|
||||
///
|
||||
@ -204,13 +197,14 @@ pub enum State {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::common::NoExtension;
|
||||
use crate::poll::Poll;
|
||||
use crate::response::ResultCode;
|
||||
use crate::tests::{response_from_file_with_ext, CLTRID, SVTRID};
|
||||
|
||||
#[test]
|
||||
fn urs_lock_before() {
|
||||
let object = response_from_file_with_ext::<Poll, ChangePoll>(
|
||||
let object = response_from_file_with_ext::<Poll, NoExtension, ChangePoll>(
|
||||
"response/extensions/change_poll/urs_lock_before.xml",
|
||||
);
|
||||
|
||||
@ -223,29 +217,18 @@ mod tests {
|
||||
"Command completed successfully; ack to dequeue"
|
||||
);
|
||||
|
||||
assert_eq!(object.extension().unwrap().state.unwrap(), State::Before);
|
||||
let ext = &object.connection_extension().unwrap();
|
||||
|
||||
assert_eq!(ext.state.unwrap(), State::Before);
|
||||
assert_eq!(ext.operation.kind().unwrap(), OperationKind::Update);
|
||||
assert_eq!(ext.date, "2013-10-22T14:25:57.0Z");
|
||||
assert_eq!(ext.server_tr_id, "12345-XYZ");
|
||||
assert_eq!(ext.who, "URS Admin");
|
||||
assert_eq!(
|
||||
object.extension().unwrap().operation.kind().unwrap(),
|
||||
OperationKind::Update
|
||||
);
|
||||
assert_eq!(object.extension().unwrap().date, "2013-10-22T14:25:57.0Z");
|
||||
assert_eq!(object.extension().unwrap().server_tr_id, "12345-XYZ");
|
||||
assert_eq!(object.extension().unwrap().who, "URS Admin");
|
||||
assert_eq!(
|
||||
object
|
||||
.extension()
|
||||
.unwrap()
|
||||
.case_id
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.kind()
|
||||
.unwrap(),
|
||||
ext.case_id.as_ref().unwrap().kind().unwrap(),
|
||||
CaseIdentifierKind::Urs
|
||||
);
|
||||
assert_eq!(
|
||||
object.extension().unwrap().reason.as_ref().unwrap().inner,
|
||||
"URS Lock"
|
||||
);
|
||||
assert_eq!(ext.reason.as_ref().unwrap().inner, "URS Lock");
|
||||
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
@ -253,7 +236,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn urs_lock_after() {
|
||||
let object = response_from_file_with_ext::<Poll, ChangePoll>(
|
||||
let object = response_from_file_with_ext::<Poll, NoExtension, ChangePoll>(
|
||||
"response/extensions/change_poll/urs_lock_after.xml",
|
||||
);
|
||||
|
||||
@ -265,7 +248,10 @@ mod tests {
|
||||
object.result.message,
|
||||
"Command completed successfully; ack to dequeue"
|
||||
);
|
||||
assert_eq!(object.extension().unwrap().state.unwrap(), State::After);
|
||||
|
||||
let ext = &object.connection_extension().unwrap();
|
||||
|
||||
assert_eq!(ext.state.unwrap(), State::After);
|
||||
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
@ -273,7 +259,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn custom_sync_after() {
|
||||
let object = response_from_file_with_ext::<Poll, ChangePoll>(
|
||||
let object = response_from_file_with_ext::<Poll, NoExtension, ChangePoll>(
|
||||
"response/extensions/change_poll/custom_sync_after.xml",
|
||||
);
|
||||
|
||||
@ -286,15 +272,11 @@ mod tests {
|
||||
"Command completed successfully; ack to dequeue"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
object.extension().unwrap().operation.kind().unwrap(),
|
||||
OperationKind::Custom("sync")
|
||||
);
|
||||
assert_eq!(object.extension().unwrap().who, "CSR");
|
||||
assert_eq!(
|
||||
object.extension().unwrap().reason.as_ref().unwrap().inner,
|
||||
"Customer sync request"
|
||||
);
|
||||
let ext = &object.connection_extension().unwrap();
|
||||
|
||||
assert_eq!(ext.operation.kind().unwrap(), OperationKind::Custom("sync"));
|
||||
assert_eq!(ext.who, "CSR");
|
||||
assert_eq!(ext.reason.as_ref().unwrap().inner, "Customer sync request");
|
||||
|
||||
assert_eq!(object.tr_ids.client_tr_id.unwrap(), CLTRID);
|
||||
assert_eq!(object.tr_ids.server_tr_id, SVTRID);
|
||||
@ -302,7 +284,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn delete_before() {
|
||||
let object = response_from_file_with_ext::<Poll, ChangePoll>(
|
||||
let object = response_from_file_with_ext::<Poll, NoExtension, ChangePoll>(
|
||||
"response/extensions/change_poll/delete_before.xml",
|
||||
);
|
||||
|
||||
@ -321,7 +303,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn autopurge_before() {
|
||||
let object = response_from_file_with_ext::<Poll, ChangePoll>(
|
||||
let object = response_from_file_with_ext::<Poll, NoExtension, ChangePoll>(
|
||||
"response/extensions/change_poll/autopurge_before.xml",
|
||||
);
|
||||
|
||||
@ -340,7 +322,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn update_after() {
|
||||
let object = response_from_file_with_ext::<Poll, ChangePoll>(
|
||||
let object = response_from_file_with_ext::<Poll, NoExtension, ChangePoll>(
|
||||
"response/extensions/change_poll/update_after.xml",
|
||||
);
|
||||
|
||||
|
@ -75,6 +75,7 @@ pub struct NameStore<'a> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::NameStore;
|
||||
use crate::common::NoExtension;
|
||||
use crate::domain::check::DomainCheck;
|
||||
use crate::tests::{assert_serialized, response_from_file_with_ext};
|
||||
|
||||
@ -94,10 +95,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn response() {
|
||||
let object = response_from_file_with_ext::<DomainCheck, NameStore>(
|
||||
let object = response_from_file_with_ext::<DomainCheck, NameStore, NoExtension>(
|
||||
"response/extensions/namestore.xml",
|
||||
);
|
||||
let ext = object.extension().unwrap();
|
||||
let ext = &object.command_extension().unwrap();
|
||||
assert_eq!(ext.subproduct, "com");
|
||||
}
|
||||
}
|
||||
|
@ -69,6 +69,7 @@ pub enum RgpRequestResponse {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{RgpRestoreRequest, Update};
|
||||
use crate::common::NoExtension;
|
||||
use crate::domain::info::DomainInfo;
|
||||
use crate::domain::update::{DomainChangeInfo, DomainUpdate};
|
||||
use crate::extensions::rgp::request::RgpRequestResponse;
|
||||
@ -99,15 +100,16 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn request_response() {
|
||||
let object = response_from_file_with_ext::<DomainUpdate, Update<RgpRestoreRequest>>(
|
||||
"response/extensions/rgp_restore.xml",
|
||||
);
|
||||
let ext = object.extension.unwrap();
|
||||
let object =
|
||||
response_from_file_with_ext::<DomainUpdate, Update<RgpRestoreRequest>, NoExtension>(
|
||||
"response/extensions/rgp_restore.xml",
|
||||
);
|
||||
let ext = object.command_extension().unwrap();
|
||||
|
||||
assert_eq!(object.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||
assert_eq!(object.result.message, SUCCESS_MSG);
|
||||
|
||||
let data = match ext.data {
|
||||
let data = match ext {
|
||||
RgpRequestResponse::Update(data) => data,
|
||||
_ => panic!("Unexpected response type"),
|
||||
};
|
||||
@ -118,12 +120,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn domain_info_request_response() {
|
||||
let object = response_from_file_with_ext::<DomainInfo, Update<RgpRestoreRequest>>(
|
||||
"response/extensions/domain_info_rgp.xml",
|
||||
);
|
||||
let ext = object.extension.unwrap();
|
||||
let object =
|
||||
response_from_file_with_ext::<DomainInfo, Update<RgpRestoreRequest>, NoExtension>(
|
||||
"response/extensions/domain_info_rgp.xml",
|
||||
);
|
||||
let ext = object.command_extension().unwrap();
|
||||
|
||||
let data = match ext.data {
|
||||
let data = match ext {
|
||||
RgpRequestResponse::Info(data) => data,
|
||||
_ => panic!("Unexpected response type"),
|
||||
};
|
||||
|
133
src/response.rs
133
src/response.rs
@ -3,9 +3,9 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use instant_xml::{FromXml, Kind};
|
||||
use instant_xml::{Deserializer, FromXml, FromXmlOwned, Id, Kind};
|
||||
|
||||
use crate::common::EPP_XMLNS;
|
||||
use crate::common::{NoExtension, EPP_XMLNS};
|
||||
|
||||
/// Type corresponding to the `<undef>` tag an EPP response XML
|
||||
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||
@ -238,7 +238,7 @@ pub struct Message {
|
||||
/// Type corresponding to the `<response>` tag in an EPP response XML
|
||||
/// containing an `<extension>` tag
|
||||
#[xml(rename = "response", ns(EPP_XMLNS))]
|
||||
pub struct Response<D, E> {
|
||||
pub struct Response<D, CmdExt, ConnExt> {
|
||||
/// Data under the `<result>` tag
|
||||
pub result: EppResult,
|
||||
/// Data under the `<msgQ>` tag
|
||||
@ -247,7 +247,7 @@ pub struct Response<D, E> {
|
||||
/// Data under the `<resData>` tag
|
||||
pub res_data: Option<ResponseData<D>>,
|
||||
/// Data under the `<extension>` tag
|
||||
pub extension: Option<Extension<E>>,
|
||||
pub extension: Option<Extension<CmdExt, ConnExt>>,
|
||||
/// Data under the `<trID>` tag
|
||||
pub tr_ids: ResponseTRID,
|
||||
}
|
||||
@ -276,7 +276,7 @@ pub struct ResponseStatus {
|
||||
pub tr_ids: ResponseTRID,
|
||||
}
|
||||
|
||||
impl<T, E> Response<T, E> {
|
||||
impl<T, CmdExt, ConnExt> Response<T, CmdExt, ConnExt> {
|
||||
/// Returns the data under the corresponding `<resData>` from the EPP XML
|
||||
pub fn res_data(&self) -> Option<&T> {
|
||||
match &self.res_data {
|
||||
@ -285,9 +285,16 @@ impl<T, E> Response<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extension(&self) -> Option<&E> {
|
||||
pub fn command_extension(&self) -> Option<&CmdExt> {
|
||||
match &self.extension {
|
||||
Some(extension) => Some(&extension.data),
|
||||
Some(extension) => extension.data.command.as_ref(),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connection_extension(&self) -> Option<&ConnExt> {
|
||||
match &self.extension {
|
||||
Some(extension) => extension.data.connection.as_ref(),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
@ -303,8 +310,116 @@ impl<T, E> Response<T, E> {
|
||||
|
||||
#[derive(Debug, Eq, FromXml, PartialEq)]
|
||||
#[xml(rename = "extension", ns(EPP_XMLNS))]
|
||||
pub struct Extension<E> {
|
||||
pub data: E,
|
||||
pub struct Extension<CmdExt, ConnExt> {
|
||||
pub data: CombinedExtensions<CmdExt, ConnExt>,
|
||||
}
|
||||
|
||||
/// Types implementing this can be used as ConnectionExtensions.
|
||||
///
|
||||
/// Their type will be assumed to be in the response's extension element.
|
||||
pub trait ConnectionExtensionResponse: FromXmlOwned + Debug {}
|
||||
|
||||
impl ConnectionExtensionResponse for NoExtension {}
|
||||
|
||||
/// Combines connection extensions and command extensions
|
||||
///
|
||||
/// Some extensions are sent by the server no matter if an extension was defined for the command.
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct CombinedExtensions<CmdExt, ConnExt> {
|
||||
pub command: Option<CmdExt>,
|
||||
pub connection: Option<ConnExt>,
|
||||
}
|
||||
|
||||
// // This manual impl is needed because instant-xml is not able to create the correct derived impl.
|
||||
// // for this transparent type. It fails to set the correct bounds for struct __CombinedExtensionsAccumulator.
|
||||
impl<'xml, CmdExt: FromXml<'xml>, ConnExt: FromXml<'xml>> FromXml<'xml>
|
||||
for CombinedExtensions<CmdExt, ConnExt>
|
||||
{
|
||||
#[inline]
|
||||
fn matches(id: Id<'_>, _: Option<Id<'_>>) -> bool {
|
||||
<CmdExt as FromXml<'xml>>::matches(id, None)
|
||||
|| <ConnExt as FromXml<'xml>>::matches(id, None)
|
||||
}
|
||||
fn deserialize<'cx>(
|
||||
into: &mut Self::Accumulator,
|
||||
_: &'static str,
|
||||
deserializer: &mut Deserializer<'cx, 'xml>,
|
||||
) -> Result<(), instant_xml::Error> {
|
||||
use instant_xml::Kind;
|
||||
let current = deserializer.parent();
|
||||
if <CmdExt as FromXml<'xml>>::matches(current, None) {
|
||||
match <CmdExt as FromXml>::KIND {
|
||||
Kind::Element => {
|
||||
<Option<CmdExt>>::deserialize(
|
||||
&mut into.command,
|
||||
"CombinedExtensions::command",
|
||||
deserializer,
|
||||
)?;
|
||||
}
|
||||
Kind::Scalar => {
|
||||
<Option<CmdExt>>::deserialize(
|
||||
&mut into.command,
|
||||
"CombinedExtensions::command",
|
||||
deserializer,
|
||||
)?;
|
||||
deserializer.ignore()?;
|
||||
}
|
||||
}
|
||||
} else if <ConnExt as FromXml<'xml>>::matches(current, None) {
|
||||
match <ConnExt as FromXml>::KIND {
|
||||
Kind::Element => {
|
||||
<Option<ConnExt>>::deserialize(
|
||||
&mut into.connection,
|
||||
"CombinedExtensions::connection",
|
||||
deserializer,
|
||||
)?;
|
||||
}
|
||||
Kind::Scalar => {
|
||||
<Option<ConnExt>>::deserialize(
|
||||
&mut into.connection,
|
||||
"CombinedExtensions::connection",
|
||||
deserializer,
|
||||
)?;
|
||||
deserializer.ignore()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
type Accumulator = __CombinedExtensionsAccumulator<'xml, CmdExt, ConnExt>;
|
||||
const KIND: instant_xml::Kind = instant_xml::Kind::Element;
|
||||
}
|
||||
|
||||
pub struct __CombinedExtensionsAccumulator<'xml, CommandExt: FromXml<'xml>, ConnExt: FromXml<'xml>>
|
||||
{
|
||||
command: <Option<CommandExt> as FromXml<'xml>>::Accumulator,
|
||||
connection: <Option<ConnExt> as FromXml<'xml>>::Accumulator,
|
||||
}
|
||||
|
||||
impl<'xml, CommandExt: FromXml<'xml>, ConnExt: FromXml<'xml>>
|
||||
instant_xml::Accumulate<CombinedExtensions<CommandExt, ConnExt>>
|
||||
for __CombinedExtensionsAccumulator<'xml, CommandExt, ConnExt>
|
||||
{
|
||||
fn try_done(
|
||||
self,
|
||||
_: &'static str,
|
||||
) -> Result<CombinedExtensions<CommandExt, ConnExt>, instant_xml::Error> {
|
||||
Ok(CombinedExtensions {
|
||||
command: self.command.try_done("CombinedExtensions::command")?,
|
||||
connection: self.connection.try_done("CombinedExtensions::connection")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'xml, CommandExt: FromXml<'xml>, ConnExt: instant_xml::FromXml<'xml>> Default
|
||||
for __CombinedExtensionsAccumulator<'xml, CommandExt, ConnExt>
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
command: Default::default(),
|
||||
connection: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -9,7 +9,7 @@ use crate::{
|
||||
client::RequestData,
|
||||
common::NoExtension,
|
||||
request::{Command, CommandWrapper, Extension, Transaction},
|
||||
response::Response,
|
||||
response::{ConnectionExtensionResponse, Response},
|
||||
xml,
|
||||
};
|
||||
|
||||
@ -53,23 +53,24 @@ pub(crate) fn assert_serialized<'c, 'e, Cmd, Ext>(
|
||||
|
||||
pub(crate) fn response_from_file<'c, Cmd>(
|
||||
path: &str,
|
||||
) -> Response<Cmd::Response, <NoExtension as Extension>::Response>
|
||||
) -> Response<Cmd::Response, <NoExtension as Extension>::Response, NoExtension>
|
||||
where
|
||||
Cmd: Transaction<NoExtension> + Command + 'c,
|
||||
{
|
||||
response_from_file_with_ext::<Cmd, NoExtension>(path)
|
||||
response_from_file_with_ext::<Cmd, NoExtension, NoExtension>(path)
|
||||
}
|
||||
|
||||
pub(crate) fn response_from_file_with_ext<Cmd, Ext>(
|
||||
pub(crate) fn response_from_file_with_ext<Cmd, CmdExt, ConnExt>(
|
||||
path: &str,
|
||||
) -> Response<Cmd::Response, Ext::Response>
|
||||
) -> Response<Cmd::Response, CmdExt::Response, ConnExt>
|
||||
where
|
||||
Cmd: Transaction<NoExtension> + Command,
|
||||
Ext: Extension,
|
||||
CmdExt: Extension,
|
||||
ConnExt: ConnectionExtensionResponse,
|
||||
{
|
||||
let xml = get_xml(path).unwrap();
|
||||
dbg!(&xml);
|
||||
let rsp = xml::deserialize::<Response<Cmd::Response, Ext::Response>>(&xml).unwrap();
|
||||
let rsp = xml::deserialize::<Response<Cmd::Response, CmdExt::Response, ConnExt>>(&xml).unwrap();
|
||||
assert!(rsp.result.code.is_success());
|
||||
rsp
|
||||
}
|
||||
|
@ -4,11 +4,14 @@ use std::str;
|
||||
use std::time::Duration;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use instant_epp::extensions::change_poll::ChangePoll;
|
||||
use instant_epp::poll::{Poll, PollData};
|
||||
use regex::Regex;
|
||||
use tokio::time::timeout;
|
||||
use tokio_test::io::Builder;
|
||||
|
||||
use instant_epp::client::{Connector, EppClient};
|
||||
use instant_epp::common::NoExtension;
|
||||
use instant_epp::domain::{DomainCheck, DomainContact, DomainCreate, Period, PeriodLength};
|
||||
use instant_epp::login::Login;
|
||||
use instant_epp::response::ResultCode;
|
||||
@ -100,9 +103,10 @@ async fn client() {
|
||||
}
|
||||
}
|
||||
|
||||
let mut client = EppClient::new(FakeConnector, "test".into(), Duration::from_secs(5))
|
||||
.await
|
||||
.unwrap();
|
||||
let mut client =
|
||||
EppClient::<_, NoExtension>::new(FakeConnector, "test".into(), Duration::from_secs(5))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(client.xml_greeting(), xml("response/greeting.xml"));
|
||||
let rsp = client
|
||||
@ -176,9 +180,10 @@ async fn dropped() {
|
||||
}
|
||||
}
|
||||
|
||||
let mut client = EppClient::new(FakeConnector, "test".into(), Duration::from_secs(5))
|
||||
.await
|
||||
.unwrap();
|
||||
let mut client =
|
||||
EppClient::<_, NoExtension>::new(FakeConnector, "test".into(), Duration::from_secs(5))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(client.xml_greeting(), xml("response/greeting.xml"));
|
||||
let rsp = client
|
||||
@ -242,3 +247,62 @@ async fn dropped() {
|
||||
let rsp = client.transact(&create, CLTRID).await.unwrap();
|
||||
assert_eq!(rsp.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn poll_with_extensions() {
|
||||
let _guard = log_to_stdout();
|
||||
|
||||
struct FakeConnector;
|
||||
|
||||
#[async_trait]
|
||||
impl Connector for FakeConnector {
|
||||
type Connection = tokio_test::io::Mock;
|
||||
|
||||
async fn connect(&self, _: Duration) -> Result<Self::Connection, Error> {
|
||||
Ok(build_stream(&[
|
||||
"response/greeting.xml",
|
||||
"request/login.xml",
|
||||
"response/login.xml",
|
||||
"request/poll/poll.xml",
|
||||
"response/extensions/change_poll/urs_lock_after.xml",
|
||||
])
|
||||
.build())
|
||||
}
|
||||
}
|
||||
|
||||
let mut client =
|
||||
EppClient::<_, ChangePoll>::new(FakeConnector, "test".into(), Duration::from_secs(5))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(client.xml_greeting(), xml("response/greeting.xml"));
|
||||
let rsp = client
|
||||
.transact(
|
||||
&Login::new(
|
||||
"username",
|
||||
"password",
|
||||
Some("new-password"),
|
||||
Some(&["http://schema.ispapi.net/epp/xml/keyvalue-1.0"]),
|
||||
),
|
||||
CLTRID,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(rsp.result.code, ResultCode::CommandCompletedSuccessfully);
|
||||
|
||||
let rsp = client.transact(&Poll, CLTRID).await.unwrap();
|
||||
assert_eq!(
|
||||
rsp.result.code,
|
||||
ResultCode::CommandCompletedSuccessfullyAckToDequeue
|
||||
);
|
||||
|
||||
let data = rsp.res_data().unwrap();
|
||||
let data = match data {
|
||||
PollData::DomainInfo(info_data) => info_data,
|
||||
_ => panic!("expected domain:infData"),
|
||||
};
|
||||
assert_eq!(data.name, "domain.example");
|
||||
let ext = rsp.connection_extension().unwrap();
|
||||
assert_eq!(ext.case_id.as_ref().unwrap().id, "urs123");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user