async_client: add docs/test for register_user, send_uiaa and RegistrationBuilder
parent
5abac19b72
commit
6df1f12b45
|
@ -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();
|
||||||
|
|
|
@ -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>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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"
|
||||||
|
}
|
Loading…
Reference in New Issue