|
|
|
@ -1,13 +1,17 @@
|
|
|
|
|
use crate::api::methods::sync::SyncResponse; |
|
|
|
|
use reqwest::Client as reqwest_client; |
|
|
|
|
use reqwest::{ |
|
|
|
|
header::{HeaderMap, HeaderValue, CONTENT_TYPE, USER_AGENT}, |
|
|
|
|
StatusCode, |
|
|
|
|
Client as reqwest_client, Response, StatusCode, |
|
|
|
|
}; |
|
|
|
|
use serde::Deserialize; |
|
|
|
|
use std::{collections::HashMap, error::Error, fmt, time}; |
|
|
|
|
use url::{ParseError, Url}; |
|
|
|
|
|
|
|
|
|
#[cfg(test)] |
|
|
|
|
use mockito; |
|
|
|
|
|
|
|
|
|
const V2_API_PATH: &str = "/_matrix/client/r0"; |
|
|
|
|
const SUPPORTED_VERSION: &str = "r0.5.0"; |
|
|
|
|
|
|
|
|
|
#[derive(Debug)] |
|
|
|
|
pub enum MatrixParseError { |
|
|
|
@ -57,25 +61,59 @@ pub struct Client {
|
|
|
|
|
reqwest_client: reqwest_client, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[derive(Deserialize)] |
|
|
|
|
/// Response struct for [Section 2.1 **GET** /_matrix/client/versions](https://matrix.org/docs/spec/client_server/r0.5.0#get-matrix-client-versions).
|
|
|
|
|
pub struct SupportedSpecs { |
|
|
|
|
pub versions: Vec<String>, |
|
|
|
|
pub unstable_features: Option<HashMap<String, bool>>, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Client { |
|
|
|
|
pub fn new( |
|
|
|
|
homeserver_url: &str, |
|
|
|
|
access_token: Option<String>, |
|
|
|
|
mxid: Option<String>, |
|
|
|
|
default_492_wait_ms: Option<u64>, |
|
|
|
|
) -> Result<Self, MatrixParseError> { |
|
|
|
|
) -> Result<Self, Box<dyn Error>> { |
|
|
|
|
let url = Url::parse(homeserver_url)?; |
|
|
|
|
if url.scheme().is_empty() { |
|
|
|
|
return Err(MatrixParseError::EmptyScheme); |
|
|
|
|
panic!("todo: implement handling"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ok(Client { |
|
|
|
|
let client = Client { |
|
|
|
|
homeserver_url: homeserver_url.to_string(), |
|
|
|
|
access_token, |
|
|
|
|
mxid, |
|
|
|
|
default_492_wait_ms: default_492_wait_ms.unwrap_or_else(|| 5000), |
|
|
|
|
reqwest_client: reqwest_client::new(), |
|
|
|
|
}) |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
if !client |
|
|
|
|
.supported_versions()? |
|
|
|
|
.versions |
|
|
|
|
.contains(&SUPPORTED_VERSION.to_string()) |
|
|
|
|
{ |
|
|
|
|
// TODO: Implement proper response
|
|
|
|
|
panic!("server version doesn't support client"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ok(client) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Implementation of [Section 2.1 **GET** /_matrix/client/versions](https://matrix.org/docs/spec/client_server/r0.5.0#get-matrix-client-versions).
|
|
|
|
|
///
|
|
|
|
|
/// Returns a list of matrix specifications a server supports, as well as
|
|
|
|
|
/// a map of unstable features the server has advertised.
|
|
|
|
|
pub fn supported_versions(&self) -> Result<SupportedSpecs, Box<dyn Error>> { |
|
|
|
|
Ok(self |
|
|
|
|
.send( |
|
|
|
|
MatrixHTTPMethod::Get, |
|
|
|
|
Some("/_matrix/client/versions"), |
|
|
|
|
None, |
|
|
|
|
None, |
|
|
|
|
None, |
|
|
|
|
)? |
|
|
|
|
.json()?) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Sends an API request to the homeserver using the specified method and
|
|
|
|
@ -88,6 +126,7 @@ impl Client {
|
|
|
|
|
/// This is a blocking, synchronous send. If the response from the
|
|
|
|
|
/// homeserver indicates that too many requests were sent, it will attempt
|
|
|
|
|
/// to wait the specified duration (or a provided default) before retrying.
|
|
|
|
|
/// TODO: Make async
|
|
|
|
|
fn send( |
|
|
|
|
&self, |
|
|
|
|
method: MatrixHTTPMethod, |
|
|
|
@ -95,14 +134,16 @@ impl Client {
|
|
|
|
|
content: Option<String>, |
|
|
|
|
query_params: Option<HashMap<String, String>>, |
|
|
|
|
headers: Option<HeaderMap>, |
|
|
|
|
) -> Result<String, Box<dyn std::error::Error>> { |
|
|
|
|
) -> Result<Response, Box<dyn std::error::Error>> { |
|
|
|
|
let mut query_params = query_params.unwrap_or_default(); |
|
|
|
|
let mut headers = headers.unwrap_or_default(); |
|
|
|
|
let endpoint = &format!( |
|
|
|
|
"{}{}", |
|
|
|
|
self.homeserver_url, |
|
|
|
|
path.unwrap_or_else(|| V2_API_PATH), |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
#[cfg(test)] |
|
|
|
|
let url = &mockito::server_url(); |
|
|
|
|
#[cfg(not(test))] |
|
|
|
|
let url = &self.homeserver_url; |
|
|
|
|
|
|
|
|
|
let endpoint = &format!("{}{}", url, path.unwrap_or_else(|| V2_API_PATH)); |
|
|
|
|
let mut request = match method { |
|
|
|
|
MatrixHTTPMethod::Get => self.reqwest_client.get(endpoint), |
|
|
|
|
MatrixHTTPMethod::Put => self.reqwest_client.put(endpoint), |
|
|
|
@ -140,7 +181,7 @@ impl Client {
|
|
|
|
|
.send()?; |
|
|
|
|
|
|
|
|
|
if res.status().is_success() { |
|
|
|
|
return Ok(res.text()?); |
|
|
|
|
return Ok(res); |
|
|
|
|
} else if res.status() == StatusCode::TOO_MANY_REQUESTS { |
|
|
|
|
let mut body: HashMap<String, String> = res.json()?; |
|
|
|
|
if let Some(value) = body.get("error") { |
|
|
|
@ -167,7 +208,7 @@ impl Client {
|
|
|
|
|
method: MatrixHTTPMethod, |
|
|
|
|
path: &str, |
|
|
|
|
query_params: HashMap<String, String>, |
|
|
|
|
) -> Result<String, Box<dyn Error>> { |
|
|
|
|
) -> Result<Response, Box<dyn Error>> { |
|
|
|
|
self.send(method, Some(path), None, Some(query_params), None) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -207,14 +248,64 @@ impl Client {
|
|
|
|
|
.to_string(), |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
Ok(serde_json::from_str(&self.send_query( |
|
|
|
|
MatrixHTTPMethod::Get, |
|
|
|
|
"/sync", |
|
|
|
|
params, |
|
|
|
|
)?)?) |
|
|
|
|
Ok(self |
|
|
|
|
.send_query(MatrixHTTPMethod::Get, "/sync", params)? |
|
|
|
|
.json()?) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[cfg(test)] |
|
|
|
|
mod tests { |
|
|
|
|
use super::*; |
|
|
|
|
use mockito::mock; |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn client_init_properly() {} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn supported_versions_complete_resp() { |
|
|
|
|
let _m = mock("GET", "/_matrix/client/versions") |
|
|
|
|
.with_body( |
|
|
|
|
r#"{ |
|
|
|
|
"versions": ["r0.4.0", "r0.5.0"], |
|
|
|
|
"unstable_features": { "m.lazy_load_members": true } |
|
|
|
|
}"#, |
|
|
|
|
) |
|
|
|
|
.create(); |
|
|
|
|
|
|
|
|
|
// "valid" location must be supplied as reqwest attempts to parse it.
|
|
|
|
|
let resp = Client::new("http://dummy.website", None, None, None) |
|
|
|
|
.unwrap() |
|
|
|
|
.supported_versions() |
|
|
|
|
.unwrap(); |
|
|
|
|
assert_eq!(resp.versions, vec!["r0.4.0", "r0.5.0"]); |
|
|
|
|
assert!(resp |
|
|
|
|
.unstable_features |
|
|
|
|
.unwrap_or_default() |
|
|
|
|
.get("m.lazy_load_members") |
|
|
|
|
.unwrap_or_else(|| &false)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn supported_versions_just_versions() { |
|
|
|
|
let _m = mock("GET", "/_matrix/client/versions") |
|
|
|
|
.with_body( |
|
|
|
|
r#"{ |
|
|
|
|
"versions": ["r0.4.0", "r0.5.0"] |
|
|
|
|
}"#, |
|
|
|
|
) |
|
|
|
|
.create(); |
|
|
|
|
|
|
|
|
|
// "valid" location must be supplied as reqwest attempts to parse it.
|
|
|
|
|
let resp = Client::new("http://dummy.website", None, None, None) |
|
|
|
|
.unwrap() |
|
|
|
|
.supported_versions() |
|
|
|
|
.unwrap(); |
|
|
|
|
assert_eq!(resp.versions, vec!["r0.4.0", "r0.5.0"]); |
|
|
|
|
assert!(resp.unstable_features.is_none()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
#[derive(Default)] |
|
|
|
|
pub struct ApiError {} |
|
|
|
|
|
|
|
|
|