async_client: add docs/test for register_user, send_uiaa and RegistrationBuilder

master
Devin R 2020-06-02 14:57:56 -04:00
parent 5abac19b72
commit 6df1f12b45
4 changed files with 154 additions and 27 deletions

View File

@ -17,6 +17,7 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use std::fmt::{self, Debug};
use std::path::Path; use std::path::Path;
use std::result::Result as StdResult; use std::result::Result as StdResult;
use std::sync::Arc; use std::sync::Arc;
@ -66,8 +67,8 @@ pub struct Client {
} }
#[cfg_attr(tarpaulin, skip)] #[cfg_attr(tarpaulin, skip)]
impl std::fmt::Debug for Client { impl Debug for Client {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> StdResult<(), std::fmt::Error> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> StdResult<(), fmt::Error> {
write!(fmt, "Client {{ homeserver: {} }}", self.homeserver) write!(fmt, "Client {{ homeserver: {} }}", self.homeserver)
} }
} }
@ -106,8 +107,8 @@ pub struct ClientConfig {
} }
#[cfg_attr(tarpaulin, skip)] #[cfg_attr(tarpaulin, skip)]
impl std::fmt::Debug for ClientConfig { impl Debug for ClientConfig {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> StdResult<(), std::fmt::Error> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut res = fmt.debug_struct("ClientConfig"); let mut res = fmt.debug_struct("ClientConfig");
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
@ -264,6 +265,7 @@ use api::r0::sync::sync_events;
#[cfg(feature = "encryption")] #[cfg(feature = "encryption")]
use api::r0::to_device::send_event_to_device; use api::r0::to_device::send_event_to_device;
use api::r0::typing::create_typing_event; use api::r0::typing::create_typing_event;
use api::r0::uiaa::UiaaResponse;
impl Client { impl Client {
/// Creates a new client for making HTTP requests to the given homeserver. /// Creates a new client for making HTTP requests to the given homeserver.
@ -414,7 +416,7 @@ impl Client {
/// device_id from a previous login call. Note that this should be done /// device_id from a previous login call. Note that this should be done
/// only if the client also holds the encryption keys for this device. /// only if the client also holds the encryption keys for this device.
#[instrument(skip(password))] #[instrument(skip(password))]
pub async fn login<S: Into<String> + std::fmt::Debug>( pub async fn login<S: Into<String> + Debug>(
&self, &self,
user: S, user: S,
password: S, password: S,
@ -454,9 +456,24 @@ impl Client {
/// ///
/// * `registration` - The easiest way to create this request is using the `RegistrationBuilder`. /// * `registration` - The easiest way to create this request is using the `RegistrationBuilder`.
/// ///
///
/// # Examples /// # Examples
/// ``` /// ```
/// /// # use std::convert::TryFrom;
/// # use matrix_sdk::{Client, RegistrationBuilder};
/// # use matrix_sdk::api::r0::account::register::RegistrationKind;
/// # use matrix_sdk::identifiers::DeviceId;
/// # use url::Url;
/// # let homeserver = Url::parse("http://example.com").unwrap();
/// # let mut rt = tokio::runtime::Runtime::new().unwrap();
/// # rt.block_on(async {
/// let mut builder = RegistrationBuilder::default();
/// builder.password("pass")
/// .username("user")
/// .kind(RegistrationKind::User);
/// let mut client = Client::new(homeserver).unwrap();
/// client.register_user(builder).await;
/// # })
/// ``` /// ```
#[instrument(skip(registration))] #[instrument(skip(registration))]
pub async fn register_user<R: Into<register::Request>>( pub async fn register_user<R: Into<register::Request>>(
@ -466,7 +483,7 @@ impl Client {
info!("Registering to {}", self.homeserver); info!("Registering to {}", self.homeserver);
let request = registration.into(); let request = registration.into();
println!("{:#?}", request);
self.send_uiaa(request).await self.send_uiaa(request).await
} }
@ -896,7 +913,7 @@ impl Client {
Ok(response) Ok(response)
} }
/// Send an arbitrary request to the server, without updating client state /// Send an arbitrary request to the server, without updating client state.
/// ///
/// **Warning:** Because this method *does not* update the client state, it is /// **Warning:** Because this method *does not* update the client state, it is
/// important to make sure than you account for this yourself, and use wrapper methods /// important to make sure than you account for this yourself, and use wrapper methods
@ -934,7 +951,7 @@ impl Client {
/// // returned /// // returned
/// # }) /// # })
/// ``` /// ```
pub async fn send<Request: Endpoint<ResponseError = crate::api::Error> + std::fmt::Debug>( pub async fn send<Request: Endpoint<ResponseError = crate::api::Error> + Debug>(
&self, &self,
request: Request, request: Request,
) -> Result<Request::Response> { ) -> Result<Request::Response> {
@ -999,9 +1016,43 @@ impl Client {
Ok(<Request::Response>::try_from(http_response)?) Ok(<Request::Response>::try_from(http_response)?)
} }
async fn send_uiaa< // TODO I couldn't figure out a way to share code between these two send methods
Request: Endpoint<ResponseError = api::r0::uiaa::UiaaResponse> + std::fmt::Debug, // as they are essentially completely different types?
>( //
/// Send an arbitrary request to the server, without updating client state.
///
/// This version allows the client to make registration requests.
///
/// **Warning:** Because this method *does not* update the client state, it is
/// important to make sure than you account for this yourself, and use wrapper methods
/// where available. This method should *only* be used if a wrapper method for the
/// endpoint you'd like to use is not available.
///
/// # Arguments
///
/// * `request` - This version of send is for dealing with types that return
/// a `UiaaResponse` as the `Endpoint<ResponseError = UiaaResponse>` associated type.
///
/// # Examples
/// ```
/// # use std::convert::TryFrom;
/// # use matrix_sdk::{Client, RegistrationBuilder};
/// # use matrix_sdk::api::r0::account::register::{RegistrationKind, Request};
/// # use matrix_sdk::identifiers::DeviceId;
/// # use url::Url;
/// # let homeserver = Url::parse("http://example.com").unwrap();
/// # let mut rt = tokio::runtime::Runtime::new().unwrap();
/// # rt.block_on(async {
/// let mut builder = RegistrationBuilder::default();
/// builder.password("pass")
/// .username("user")
/// .kind(RegistrationKind::User);
/// let mut client = Client::new(homeserver).unwrap();
/// let req: Request = builder.into();
/// client.send_uiaa(req).await;
/// # })
/// ```
pub async fn send_uiaa<Request: Endpoint<ResponseError = UiaaResponse> + Debug>(
&self, &self,
request: Request, request: Request,
) -> Result<Request::Response> { ) -> Result<Request::Response> {
@ -1363,14 +1414,16 @@ impl Client {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::{ use super::{
ban_user, create_receipt, create_typing_event, forget_room, invite_user, kick_user, api::r0::uiaa::AuthData, ban_user, create_receipt, create_typing_event, forget_room,
leave_room, set_read_marker, Invite3pid, MessageEventContent, invite_user, kick_user, leave_room, register::RegistrationKind, set_read_marker,
Invite3pid, MessageEventContent,
}; };
use super::{Client, ClientConfig, Session, SyncSettings, Url}; use super::{Client, ClientConfig, Session, SyncSettings, Url};
use crate::events::collections::all::RoomEvent; use crate::events::collections::all::RoomEvent;
use crate::events::room::member::MembershipState; use crate::events::room::member::MembershipState;
use crate::events::room::message::TextMessageEventContent; use crate::events::room::message::TextMessageEventContent;
use crate::identifiers::{EventId, RoomId, RoomIdOrAliasId, UserId}; use crate::identifiers::{EventId, RoomId, RoomIdOrAliasId, UserId};
use crate::RegistrationBuilder;
use matrix_sdk_base::JsonStore; use matrix_sdk_base::JsonStore;
use matrix_sdk_test::{EventBuilder, EventsFile}; use matrix_sdk_test::{EventBuilder, EventsFile};
@ -1551,6 +1604,44 @@ mod test {
} }
} }
#[tokio::test]
async fn register_error() {
let homeserver = Url::from_str(&mockito::server_url()).unwrap();
let _m = mock("POST", "/_matrix/client/r0/register")
.with_status(403)
.with_body_from_file("../test_data/registration_response_error.json")
.create();
let mut user = RegistrationBuilder::default();
user.username("user")
.password("password")
.auth(AuthData::FallbackAcknowledgement {
session: "foobar".to_string(),
})
.kind(RegistrationKind::User);
let client = Client::new(homeserver).unwrap();
if let Err(err) = client.register_user(user).await {
if let crate::Error::UiaaError(crate::FromHttpResponseError::Http(
// TODO this should be a UiaaError need to investigate
crate::ServerError::Unknown(e),
)) = err
{
assert!(e.to_string().starts_with("EOF while parsing"))
} else {
panic!(
"found the wrong `Error` type {:#?}, expected `ServerError::Unknown",
err
);
}
} else {
panic!("this request should return an `Err` variant")
}
}
#[tokio::test] #[tokio::test]
async fn join_room_by_id() { async fn join_room_by_id() {
let homeserver = Url::from_str(&mockito::server_url()).unwrap(); let homeserver = Url::from_str(&mockito::server_url()).unwrap();

View File

@ -51,12 +51,16 @@ pub enum Error {
#[error("can't convert between ruma_client_api and hyper types.")] #[error("can't convert between ruma_client_api and hyper types.")]
IntoHttp(RumaIntoHttpError), IntoHttp(RumaIntoHttpError),
/// An error occured in the Matrix client library. /// An error occurred in the Matrix client library.
#[error(transparent)] #[error(transparent)]
MatrixError(#[from] MatrixError), MatrixError(#[from] MatrixError),
/// An error occured in the Matrix client library. /// An error occurred while authenticating.
#[error("can't convert between ruma_client_api and hyper types.")] ///
/// When registering or authenticating the Matrix server can send a `UiaaResponse`
/// as the error type, this is a User-Interactive Authentication API response. This
/// represents an error with information about how to authenticate the user.
#[error("User-Interactive Authentication required.")]
UiaaError(RumaResponseError<UiaaError>), UiaaError(RumaResponseError<UiaaError>),
} }

View File

@ -2,6 +2,7 @@ use crate::api;
use crate::events::room::power_levels::PowerLevelsEventContent; use crate::events::room::power_levels::PowerLevelsEventContent;
use crate::events::EventJson; use crate::events::EventJson;
use crate::identifiers::{DeviceId, RoomId, UserId}; use crate::identifiers::{DeviceId, RoomId, UserId};
use api::r0::account::register;
use api::r0::account::register::RegistrationKind; use api::r0::account::register::RegistrationKind;
use api::r0::filter::RoomEventFilter; use api::r0::filter::RoomEventFilter;
use api::r0::membership::Invite3pid; use api::r0::membership::Invite3pid;
@ -296,18 +297,16 @@ impl Into<get_message_events::Request> for MessagesRequestBuilder {
/// ``` /// ```
/// # use std::convert::TryFrom; /// # use std::convert::TryFrom;
/// # use matrix_sdk::{Client, RegistrationBuilder}; /// # use matrix_sdk::{Client, RegistrationBuilder};
/// # use api::r0::account::register::RegistrationKind; /// # use matrix_sdk::api::r0::account::register::RegistrationKind;
/// # use matrix_sdk::identifiers::DeviceId; /// # use matrix_sdk::identifiers::DeviceId;
/// # use url::Url; /// # use url::Url;
/// # let homeserver = Url::parse("http://example.com").unwrap(); /// # let homeserver = Url::parse("http://example.com").unwrap();
/// # let mut rt = tokio::runtime::Runtime::new().unwrap(); /// # let mut rt = tokio::runtime::Runtime::new().unwrap();
/// # rt.block_on(async { /// # rt.block_on(async {
/// let mut builder = RegistrationBuilder::default(); /// let mut builder = RegistrationBuilder::default();
/// builder.creation_content(false) /// builder.password("pass")
/// .initial_state(vec![]) /// .username("user")
/// .visibility(Visibility::Public) /// .kind(RegistrationKind::User);
/// .name("name")
/// .room_version("v1.0");
/// let mut client = Client::new(homeserver).unwrap(); /// let mut client = Client::new(homeserver).unwrap();
/// client.register_user(builder).await; /// client.register_user(builder).await;
/// # }) /// # })
@ -343,8 +342,8 @@ impl RegistrationBuilder {
/// local part of the desired Matrix ID. /// local part of the desired Matrix ID.
/// ///
/// If omitted, the homeserver MUST generate a Matrix ID local part. /// If omitted, the homeserver MUST generate a Matrix ID local part.
pub fn username(&mut self, username: String) -> &mut Self { pub fn username(&mut self, username: &str) -> &mut Self {
self.username = Some(username); self.username = Some(username.to_string());
self self
} }
@ -352,8 +351,8 @@ impl RegistrationBuilder {
/// ///
/// If this does not correspond to a known client device, a new device will be created. /// If this does not correspond to a known client device, a new device will be created.
/// The server will auto-generate a device_id if this is not specified. /// The server will auto-generate a device_id if this is not specified.
pub fn device_id(&mut self, device_id: String) -> &mut Self { pub fn device_id(&mut self, device_id: &str) -> &mut Self {
self.device_id = Some(device_id); self.device_id = Some(device_id.to_string());
self self
} }
@ -392,6 +391,20 @@ impl RegistrationBuilder {
} }
} }
impl Into<register::Request> for RegistrationBuilder {
fn into(self) -> register::Request {
register::Request {
password: self.password,
username: self.username,
device_id: self.device_id,
initial_device_display_name: self.initial_device_display_name,
auth: self.auth,
kind: self.kind,
inhibit_login: self.inhibit_login,
}
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use std::collections::BTreeMap; use std::collections::BTreeMap;

View File

@ -0,0 +1,19 @@
{
"errcode": "M_FORBIDDEN",
"error": "Invalid password",
"completed": ["example.type.foo"],
"flows": [
{
"stages": ["example.type.foo", "example.type.bar"]
},
{
"stages": ["example.type.foo", "example.type.baz"]
}
],
"params": {
"example.type.baz": {
"example_key": "foobar"
}
},
"session": "xxxxxx"
}