Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 30 additions & 23 deletions src/client.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::marker::PhantomData;
use std::time::Duration;

#[cfg(feature = "__rustls")]
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
})
}

Expand All @@ -116,14 +119,14 @@ impl<C: Connector> EppClient<C> {
xml::deserialize::<Greeting>(&response)
}

pub async fn transact<'c, 'e, Cmd, Ext>(
pub async fn transact<'c, 'e, Cmd, CmdExt>(
&mut self,
data: impl Into<RequestData<'c, 'e, Cmd, Ext>>,
data: impl Into<RequestData<'c, 'e, Cmd, CmdExt>>,
id: &str,
) -> Result<Response<Cmd::Response, Ext::Response>, Error>
) -> Result<Response<Cmd::Response, CmdExt::Response, ConnExt>, Error>
where
Cmd: Transaction<Ext> + Command + 'c,
Ext: Extension + 'e,
Cmd: Transaction<CmdExt> + Command + 'c,
CmdExt: Extension + 'e,
{
let data = data.into();
let document = CommandWrapper::new(data.command, data.extension, id);
Expand All @@ -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, Ext::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);
Expand Down Expand Up @@ -179,9 +184,9 @@ impl<C: Connector> EppClient<C> {
}

#[derive(Debug)]
pub struct RequestData<'c, 'e, C, E> {
pub(crate) command: &'c C,
pub(crate) extension: Option<&'e E>,
pub struct RequestData<'c, 'e, Command, CommandExt> {
pub(crate) command: &'c Command,
pub(crate) extension: Option<&'e CommandExt>,
}

impl<'c, C: Command> From<&'c C> for RequestData<'c, 'static, C, NoExtension> {
Expand All @@ -193,8 +198,10 @@ impl<'c, C: Command> From<&'c C> for RequestData<'c, 'static, C, NoExtension> {
}
}

impl<'c, 'e, C: Command, E: Extension> From<(&'c C, &'e E)> for RequestData<'c, 'e, C, E> {
fn from((command, extension): (&'c C, &'e E)) -> Self {
impl<'c, 'e, C: Command, CommandExt: Extension> From<(&'c C, &'e CommandExt)>
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For these impls, it doesn't seem to be an improvement? The Clone and Copy impls are trivial, and the From impl has the bounds right there so it's kind of obvious what's going on?

for RequestData<'c, 'e, C, CommandExt>
{
fn from((command, extension): (&'c C, &'e CommandExt)) -> Self {
Self {
command,
extension: Some(extension),
Expand All @@ -203,14 +210,14 @@ impl<'c, 'e, C: Command, E: Extension> From<(&'c C, &'e E)> for RequestData<'c,
}

// Manual impl because this does not depend on whether `C` and `E` are `Clone`
impl<C, E> Clone for RequestData<'_, '_, C, E> {
impl<Command, CommandExt> Clone for RequestData<'_, '_, Command, CommandExt> {
fn clone(&self) -> Self {
*self
}
}

// Manual impl because this does not depend on whether `C` and `E` are `Copy`
impl<C, E> Copy for RequestData<'_, '_, C, E> {}
impl<Command, CommandExt> Copy for RequestData<'_, '_, Command, CommandExt> {}

#[cfg(feature = "__rustls")]
pub use rustls_connector::RustlsConnector;
Expand Down
102 changes: 41 additions & 61 deletions src/extensions/change_poll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,31 @@
//! As described in RFC8590: [Change Poll Extension for the Extensible Provisioning Protocol (EPP)](https://www.rfc-editor.org/rfc/rfc8590.html).
//! Tests cases in `tests/resources/response/extensions/changepoll`` are taken from the RFC.

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
///
/// Attributes associated with the change
#[derive(Debug, FromXml, ToXml)]
#[xml(rename = "changeData", ns(XMLNS))]
pub struct ChangePoll<'a> {
pub struct ChangePoll {
/// Transform operation executed on the object
pub operation: Operation<'a>,
pub operation: Operation,
/// Date and time when the operation was executed
pub date: Cow<'a, str>,
pub date: String,
/// Server transaction identifier of the operation
#[xml(rename = "svTRID")]
pub server_tr_id: Cow<'a, str>,
pub server_tr_id: String,
/// Who executed the operation
pub who: Cow<'a, str>,
pub who: String,
/// Case identifier associated with the operation
pub case_id: Option<CaseIdentifier<'a>>,
pub case_id: Option<CaseIdentifier>,
/// Reason for executing the operation
pub reason: Option<Reason>,
/// Enumerated state of the object in the poll message
Expand All @@ -46,7 +37,7 @@ pub struct ChangePoll<'a> {
state: Option<State>,
}

impl ChangePoll<'_> {
impl ChangePoll {
/// State reflects if the `infData` describes the object before or after the operation
pub fn state(&self) -> State {
self.state.unwrap_or_default()
Expand All @@ -58,16 +49,16 @@ impl ChangePoll<'_> {
// to make this struct more ergonomic.
#[derive(Debug, FromXml, ToXml)]
#[xml(rename = "operation", ns(XMLNS))]
pub struct Operation<'a> {
pub struct Operation {
/// Custom value for`OperationKind::Custom`
#[xml(attribute, rename = "op")]
op: Option<Cow<'a, str>>,
op: Option<String>,
/// The operation
#[xml(direct)]
kind: OperationType,
}

impl Operation<'_> {
impl Operation {
pub fn kind(&self) -> Result<OperationKind, Error> {
Ok(match self.kind {
OperationType::Create => OperationKind::Create,
Expand Down Expand Up @@ -128,16 +119,16 @@ enum OperationType {
// to make this struct more ergonomic.
#[derive(Debug, FromXml, ToXml)]
#[xml(rename = "caseId", ns(XMLNS))]
pub struct CaseIdentifier<'a> {
pub struct CaseIdentifier {
#[xml(attribute, rename = "type")]
id_type: CaseIdentifierType,
#[xml(attribute)]
name: Option<Cow<'a, str>>,
name: Option<String>,
#[xml(direct)]
pub id: Cow<'a, str>,
pub id: String,
}

impl CaseIdentifier<'_> {
impl CaseIdentifier {
pub fn kind(&self) -> Result<CaseIdentifierKind, Error> {
Ok(match self.id_type {
CaseIdentifierType::Udrp => CaseIdentifierKind::Udrp,
Expand Down Expand Up @@ -204,13 +195,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",
);

Expand All @@ -223,37 +215,26 @@ mod tests {
"Command completed successfully; ack to dequeue"
);

assert_eq!(object.extension().unwrap().state.unwrap(), State::Before);
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");
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()
.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);
}

#[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",
);

Expand All @@ -265,15 +246,18 @@ 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);
}

#[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",
);

Expand All @@ -286,23 +270,19 @@ 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);
}

#[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",
);

Expand All @@ -321,7 +301,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",
);

Expand All @@ -340,7 +320,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",
);

Expand Down
5 changes: 3 additions & 2 deletions src/extensions/namestore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand All @@ -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");
}
}
Loading