api: Add multiple path support to OutgoingRequest
This commit is contained in:
parent
dad84ac1a0
commit
397727726e
@ -172,6 +172,7 @@ impl Parse for Metadata {
|
||||
// TODO replace with error
|
||||
// return Err(syn::Error::new_spanned(metadata_kw, "no path is defined"));
|
||||
r0_path = path.clone();
|
||||
unstable_path = path.clone();
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
|
@ -81,7 +81,9 @@ impl Request {
|
||||
let struct_attributes = &self.attributes;
|
||||
|
||||
let method = &metadata.method;
|
||||
let path = &metadata.path;
|
||||
let unstable_attr = metadata.unstable_path.as_ref().map(|p| quote! { unstable = #p, });
|
||||
let r0_attr = metadata.r0_path.as_ref().map(|p| quote! { r0 = #p, });
|
||||
let stable_attr = metadata.stable_path.as_ref().map(|p| quote! { stable = #p, });
|
||||
let auth_attributes = metadata.authentication.iter().map(|field| {
|
||||
let cfg_expr = all_cfgs_expr(&field.attrs);
|
||||
let value = &field.value;
|
||||
@ -110,7 +112,9 @@ impl Request {
|
||||
#[incoming_derive(!Deserialize, #ruma_api_macros::_FakeDeriveRumaApi)]
|
||||
#[ruma_api(
|
||||
method = #method,
|
||||
path = #path,
|
||||
#unstable_attr
|
||||
#r0_attr
|
||||
#stable_attr
|
||||
error_ty = #error_ty,
|
||||
)]
|
||||
#( #auth_attributes )*
|
||||
|
@ -52,7 +52,9 @@ pub fn expand_derive_request(input: DeriveInput) -> syn::Result<TokenStream> {
|
||||
let mut authentication = None;
|
||||
let mut error_ty = None;
|
||||
let mut method = None;
|
||||
let mut path = None;
|
||||
let mut unstable_path = None;
|
||||
let mut r0_path = None;
|
||||
let mut stable_path = None;
|
||||
|
||||
for attr in input.attrs {
|
||||
if !attr.path.is_ident("ruma_api") {
|
||||
@ -71,8 +73,14 @@ pub fn expand_derive_request(input: DeriveInput) -> syn::Result<TokenStream> {
|
||||
MetaValue::Type(t) if name == "error_ty" => {
|
||||
error_ty = Some(t);
|
||||
}
|
||||
MetaValue::Lit(Lit::Str(s)) if name == "path" => {
|
||||
path = Some(s);
|
||||
MetaValue::Lit(Lit::Str(s)) if name == "unstable" => {
|
||||
unstable_path = Some(s);
|
||||
}
|
||||
MetaValue::Lit(Lit::Str(s)) if name == "r0" => {
|
||||
r0_path = Some(s);
|
||||
}
|
||||
MetaValue::Lit(Lit::Str(s)) if name == "stable" => {
|
||||
stable_path = Some(s);
|
||||
}
|
||||
_ => unreachable!("invalid ruma_api({}) attribute", name),
|
||||
}
|
||||
@ -86,7 +94,9 @@ pub fn expand_derive_request(input: DeriveInput) -> syn::Result<TokenStream> {
|
||||
lifetimes,
|
||||
authentication: authentication.expect("missing authentication attribute"),
|
||||
method: method.expect("missing method attribute"),
|
||||
path: path.expect("missing path attribute"),
|
||||
unstable_path,
|
||||
r0_path,
|
||||
stable_path,
|
||||
error_ty: error_ty.expect("missing error_ty attribute"),
|
||||
};
|
||||
|
||||
@ -110,7 +120,9 @@ struct Request {
|
||||
|
||||
authentication: AuthScheme,
|
||||
method: Ident,
|
||||
path: LitStr,
|
||||
unstable_path: Option<LitStr>,
|
||||
r0_path: Option<LitStr>,
|
||||
stable_path: Option<LitStr>,
|
||||
error_ty: Type,
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::Field;
|
||||
use syn::{Field, LitStr};
|
||||
|
||||
use crate::auth_scheme::AuthScheme;
|
||||
use crate::{auth_scheme::AuthScheme, util};
|
||||
|
||||
use super::{Request, RequestField};
|
||||
|
||||
@ -15,39 +15,29 @@ impl Request {
|
||||
|
||||
let method = &self.method;
|
||||
let error_ty = &self.error_ty;
|
||||
let request_path_string = if self.has_path_fields() {
|
||||
let mut format_string = self.path.value();
|
||||
let mut format_args = Vec::new();
|
||||
|
||||
while let Some(start_of_segment) = format_string.find(':') {
|
||||
// ':' should only ever appear at the start of a segment
|
||||
assert_eq!(&format_string[start_of_segment - 1..start_of_segment], "/");
|
||||
|
||||
let end_of_segment = match format_string[start_of_segment..].find('/') {
|
||||
Some(rel_pos) => start_of_segment + rel_pos,
|
||||
None => format_string.len(),
|
||||
let (unstable_path, r0_path, stable_path) = if self.has_path_fields() {
|
||||
let path_format_args_call_with_percent_encoding = |s: &LitStr| -> TokenStream {
|
||||
util::path_format_args_call(s.value(), &percent_encoding)
|
||||
};
|
||||
|
||||
let path_var = Ident::new(
|
||||
&format_string[start_of_segment + 1..end_of_segment],
|
||||
Span::call_site(),
|
||||
);
|
||||
format_args.push(quote! {
|
||||
#percent_encoding::utf8_percent_encode(
|
||||
&::std::string::ToString::to_string(&self.#path_var),
|
||||
#percent_encoding::NON_ALPHANUMERIC,
|
||||
(
|
||||
self.unstable_path.as_ref().map(path_format_args_call_with_percent_encoding),
|
||||
self.r0_path.as_ref().map(path_format_args_call_with_percent_encoding),
|
||||
self.stable_path.as_ref().map(path_format_args_call_with_percent_encoding),
|
||||
)
|
||||
});
|
||||
format_string.replace_range(start_of_segment..end_of_segment, "{}");
|
||||
}
|
||||
|
||||
quote! {
|
||||
format_args!(#format_string, #(#format_args),*)
|
||||
}
|
||||
} else {
|
||||
quote! { metadata.path.to_owned() }
|
||||
(
|
||||
self.unstable_path.as_ref().map(|path| quote! { format_args!(#path) }),
|
||||
self.r0_path.as_ref().map(|path| quote! { format_args!(#path) }),
|
||||
self.stable_path.as_ref().map(|path| quote! { format_args!(#path) }),
|
||||
)
|
||||
};
|
||||
|
||||
let unstable_path = util::map_option_literal(&unstable_path);
|
||||
let r0_path = util::map_option_literal(&r0_path);
|
||||
let stable_path = util::map_option_literal(&stable_path);
|
||||
|
||||
let request_query_string = if let Some(field) = self.query_map_field() {
|
||||
let field_name = field.ident.as_ref().expect("expected field to have identifier");
|
||||
|
||||
@ -206,6 +196,7 @@ impl Request {
|
||||
self,
|
||||
base_url: &::std::primitive::str,
|
||||
access_token: #ruma_api::SendAccessToken<'_>,
|
||||
considering_versions: &'_ [#ruma_api::MatrixVersion],
|
||||
) -> ::std::result::Result<#http::Request<T>, #ruma_api::error::IntoHttpError> {
|
||||
let metadata = self::METADATA;
|
||||
|
||||
@ -214,7 +205,7 @@ impl Request {
|
||||
.uri(::std::format!(
|
||||
"{}{}{}",
|
||||
base_url.strip_suffix('/').unwrap_or(base_url),
|
||||
#request_path_string,
|
||||
#ruma_api::select_path(considering_versions, &metadata, #unstable_path, #r0_path, #stable_path)?,
|
||||
#request_query_string,
|
||||
));
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use proc_macro_crate::{crate_name, FoundCrate};
|
||||
use quote::{format_ident, quote, ToTokens};
|
||||
use syn::{parse_quote, visit::Visit, AttrStyle, Attribute, Lifetime, NestedMeta, Type};
|
||||
@ -77,3 +77,34 @@ pub fn extract_cfg(attr: &Attribute) -> Option<NestedMeta> {
|
||||
|
||||
Some(list.nested.pop().unwrap().into_value())
|
||||
}
|
||||
|
||||
pub fn path_format_args_call(
|
||||
mut format_string: String,
|
||||
percent_encoding: &TokenStream,
|
||||
) -> TokenStream {
|
||||
let mut format_args = Vec::new();
|
||||
|
||||
while let Some(start_of_segment) = format_string.find(':') {
|
||||
// ':' should only ever appear at the start of a segment
|
||||
assert_eq!(&format_string[start_of_segment - 1..start_of_segment], "/");
|
||||
|
||||
let end_of_segment = match format_string[start_of_segment..].find('/') {
|
||||
Some(rel_pos) => start_of_segment + rel_pos,
|
||||
None => format_string.len(),
|
||||
};
|
||||
|
||||
let path_var =
|
||||
Ident::new(&format_string[start_of_segment + 1..end_of_segment], Span::call_site());
|
||||
format_args.push(quote! {
|
||||
#percent_encoding::utf8_percent_encode(
|
||||
&::std::string::ToString::to_string(&self.#path_var),
|
||||
#percent_encoding::NON_ALPHANUMERIC,
|
||||
)
|
||||
});
|
||||
format_string.replace_range(start_of_segment..end_of_segment, "{}");
|
||||
}
|
||||
|
||||
quote! {
|
||||
format_args!(#format_string, #(#format_args),*)
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,9 @@ ruma-serde = { version = "0.5.0", path = "../ruma-serde" }
|
||||
serde = { version = "1.0.118", features = ["derive"] }
|
||||
serde_json = "1.0.61"
|
||||
thiserror = "1.0.26"
|
||||
tracing = "0.1.25"
|
||||
|
||||
[dev-dependencies]
|
||||
matches = "0.1.8"
|
||||
ruma-events = { version = "0.24.6", path = "../ruma-events" }
|
||||
trybuild = "1.0.38"
|
||||
|
@ -8,7 +8,7 @@ use bytes::BufMut;
|
||||
use serde_json::{from_slice as from_json_slice, Value as JsonValue};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{EndpointError, OutgoingResponse};
|
||||
use crate::{EndpointError, MatrixVersion, OutgoingResponse};
|
||||
|
||||
/// A general-purpose Matrix error type consisting of an HTTP status code and a JSON body.
|
||||
///
|
||||
@ -67,6 +67,18 @@ pub enum IntoHttpError {
|
||||
)]
|
||||
NeedsAuthentication,
|
||||
|
||||
/// Tried to create a request with an old enough version, for which no unstable endpoint
|
||||
/// exists.
|
||||
///
|
||||
/// This is also a fallback error for if the version is too new for this endpoint.
|
||||
#[error("Endpoint was not supported by server-reported versions, but no unstable path to fall back to was defined.")]
|
||||
NoUnstablePath,
|
||||
|
||||
/// Tried to create a request with [`MatrixVersion`]s for all of which this endpoint was
|
||||
/// removed.
|
||||
#[error("Could not create any path variant for endpoint, as it was removed in version {0}")]
|
||||
EndpointRemoved(MatrixVersion),
|
||||
|
||||
/// JSON serialization failed.
|
||||
#[error("JSON serialization failed: {0}")]
|
||||
Json(#[from] serde_json::Error),
|
||||
|
@ -18,7 +18,7 @@
|
||||
#[cfg(not(all(feature = "client", feature = "server")))]
|
||||
compile_error!("ruma_api's Cargo features only exist as a workaround are not meant to be disabled");
|
||||
|
||||
use std::{convert::TryInto as _, error::Error as StdError};
|
||||
use std::{convert::TryInto as _, error::Error as StdError, fmt};
|
||||
|
||||
use bytes::BufMut;
|
||||
use ruma_identifiers::UserId;
|
||||
@ -267,8 +267,18 @@ pub trait OutgoingRequest: Sized {
|
||||
|
||||
/// Tries to convert this request into an `http::Request`.
|
||||
///
|
||||
/// This method should only fail when called on endpoints that require authentication. It may
|
||||
/// also fail with a serialization error in case of bugs in Ruma though.
|
||||
/// On endpoints with authentication, when adequate information isn't provided through
|
||||
/// access_token, this could result in an error. It may also fail with a serialization error
|
||||
/// in case of bugs in Ruma though.
|
||||
///
|
||||
/// It may also fail if, for every version in `considering_versions`;
|
||||
/// - The endpoint is too old, and has been removed in all versions.
|
||||
/// ([`EndpointRemoved`](error::IntoHttpError::EndpointRemoved))
|
||||
/// - The endpoint is too new, and no unstable path is known for this endpoint.
|
||||
/// ([`NoUnstablePath`](error::IntoHttpError::NoUnstablePath))
|
||||
///
|
||||
/// Finally, this will emit a warning through `tracing` if it detects if any version in
|
||||
/// `considering_versions` has deprecated this endpoint.
|
||||
///
|
||||
/// The endpoints path will be appended to the given `base_url`, for example
|
||||
/// `https://matrix.org`. Since all paths begin with a slash, it is not necessary for the
|
||||
@ -277,6 +287,7 @@ pub trait OutgoingRequest: Sized {
|
||||
self,
|
||||
base_url: &str,
|
||||
access_token: SendAccessToken<'_>,
|
||||
considering_versions: &'_ [MatrixVersion],
|
||||
) -> Result<http::Request<T>, IntoHttpError>;
|
||||
}
|
||||
|
||||
@ -302,8 +313,10 @@ pub trait OutgoingRequestAppserviceExt: OutgoingRequest {
|
||||
base_url: &str,
|
||||
access_token: SendAccessToken<'_>,
|
||||
user_id: &UserId,
|
||||
considering_versions: &'_ [MatrixVersion],
|
||||
) -> Result<http::Request<T>, IntoHttpError> {
|
||||
let mut http_request = self.try_into_http_request(base_url, access_token)?;
|
||||
let mut http_request =
|
||||
self.try_into_http_request(base_url, access_token, considering_versions)?;
|
||||
let user_id_query = ruma_serde::urlencoded::to_string(&[("user_id", user_id)])?;
|
||||
|
||||
let uri = http_request.uri().to_owned();
|
||||
@ -400,3 +413,66 @@ pub enum AuthScheme {
|
||||
/// Authentication is performed by setting the `access_token` query parameter.
|
||||
QueryOnlyAccessToken,
|
||||
}
|
||||
|
||||
// This function helps picks the right path (or an error) from a set of matrix versions.
|
||||
//
|
||||
// This function needs to be public, yet hidden, as all `try_into_http_request`s would be using it.
|
||||
//
|
||||
// Note this assumes that `versions`;
|
||||
// - at least has 1 element
|
||||
// - have all elements sorted by its `.into_parts` representation.
|
||||
#[doc(hidden)]
|
||||
pub fn select_path<'a>(
|
||||
versions: &'_ [MatrixVersion],
|
||||
metadata: &'_ Metadata,
|
||||
unstable: Option<fmt::Arguments<'a>>,
|
||||
r0: Option<fmt::Arguments<'a>>,
|
||||
stable: Option<fmt::Arguments<'a>>,
|
||||
) -> Result<fmt::Arguments<'a>, IntoHttpError> {
|
||||
let greater_or_equal_any =
|
||||
|version: MatrixVersion| versions.iter().any(|v| v.is_superset_of(version));
|
||||
let greater_or_equal_all =
|
||||
|version: MatrixVersion| versions.iter().all(|v| v.is_superset_of(version));
|
||||
|
||||
let is_stable_any = metadata.added.map(greater_or_equal_any).unwrap_or(false);
|
||||
|
||||
let is_removed_all = metadata.removed.map(greater_or_equal_all).unwrap_or(false);
|
||||
|
||||
// Only when all the versions (e.g. 1.6-9) are compatible with the version that added it (e.g.
|
||||
// 1.1), yet are also "after" the version that removed it (e.g. 1.3), then we return that error.
|
||||
// Otherwise, this all versions may fall into a different major range, such as 2.X, where
|
||||
// "after" and "compatible" do not exist with the 1.X range, so we at least need to make sure
|
||||
// that the versions are part of the same "range" through the `added` check.
|
||||
if is_stable_any && is_removed_all {
|
||||
return Err(IntoHttpError::EndpointRemoved(metadata.removed.unwrap()));
|
||||
}
|
||||
|
||||
if is_stable_any {
|
||||
let is_deprecated_any = metadata.deprecated.map(greater_or_equal_any).unwrap_or(false);
|
||||
|
||||
if is_deprecated_any {
|
||||
let is_removed_any = metadata.removed.map(greater_or_equal_any).unwrap_or(false);
|
||||
|
||||
if is_removed_any {
|
||||
tracing::warn!("endpoint {} is deprecated, but also removed in one of more server-passed versions: {:?}", metadata.name, versions)
|
||||
} else {
|
||||
tracing::warn!(
|
||||
"endpoint {} is deprecated in one of more server-passed versions: {:?}",
|
||||
metadata.name,
|
||||
versions
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(r0) = r0 {
|
||||
if versions.iter().all(|&v| v == MatrixVersion::V1_0) {
|
||||
// Endpoint was added in 1.0, we return the r0 variant.
|
||||
return Ok(r0);
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(stable.expect("metadata.added enforces the stable path to exist"));
|
||||
}
|
||||
|
||||
unstable.ok_or(IntoHttpError::NoUnstablePath)
|
||||
}
|
||||
|
@ -50,15 +50,15 @@ pub struct Metadata {
|
||||
/// The matrix version that deprecated this endpoint.
|
||||
///
|
||||
/// Deprecation often precedes one matrix version before removal.
|
||||
// TODO add once try_into_http_request has been altered;
|
||||
// This will make `try_into_http_request` emit a warning,
|
||||
// see the corresponding documentation for more information.
|
||||
///
|
||||
/// This will make [`try_into_http_request`](crate::OutgoingRequest::try_into_http_request)
|
||||
/// emit a warning, see the corresponding documentation for more information.
|
||||
pub deprecated: Option<MatrixVersion>,
|
||||
|
||||
/// The matrix version that removed this endpoint.
|
||||
// TODO add once try_into_http_request has been altered;
|
||||
// This will make `try_into_http_request` emit an error,
|
||||
// see the corresponding documentation for more information.
|
||||
///
|
||||
/// This will make [`try_into_http_request`](crate::OutgoingRequest::try_into_http_request)
|
||||
/// emit an error, see the corresponding documentation for more information.
|
||||
pub removed: Option<MatrixVersion>,
|
||||
}
|
||||
|
||||
@ -71,10 +71,11 @@ pub struct Metadata {
|
||||
/// backwards-compatible manner.
|
||||
///
|
||||
/// Matrix has a deprecation policy, read more about it here: <https://spec.matrix.org/v1.2/#deprecation-policy>.
|
||||
// TODO add the following once `EndpointPath` and added/deprecated/removed macros are in place;
|
||||
// Ruma keeps track of when endpoints are added, deprecated, and removed. It'll automatically
|
||||
// select the right endpoint stability variation to use depending on which Matrix version you
|
||||
// pass it with [`EndpointPath`], see its respective documentation for more.
|
||||
///
|
||||
/// Ruma keeps track of when endpoints are added, deprecated, and removed. It'll automatically
|
||||
/// select the right endpoint stability variation to use depending on which Matrix versions you
|
||||
/// pass to [`try_into_http_request`](crate::OutgoingRequest::try_into_http_request), see its
|
||||
/// respective documentation for more information.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
pub enum MatrixVersion {
|
||||
|
@ -1,8 +1,8 @@
|
||||
#![allow(clippy::exhaustive_structs)]
|
||||
|
||||
use ruma_api::{
|
||||
ruma_api, IncomingRequest as _, OutgoingRequest as _, OutgoingRequestAppserviceExt,
|
||||
SendAccessToken,
|
||||
ruma_api, IncomingRequest as _, MatrixVersion, OutgoingRequest as _,
|
||||
OutgoingRequestAppserviceExt, SendAccessToken,
|
||||
};
|
||||
use ruma_identifiers::{user_id, UserId};
|
||||
|
||||
@ -52,7 +52,11 @@ fn request_serde() {
|
||||
|
||||
let http_req = req
|
||||
.clone()
|
||||
.try_into_http_request::<Vec<u8>>("https://homeserver.tld", SendAccessToken::None)
|
||||
.try_into_http_request::<Vec<u8>>(
|
||||
"https://homeserver.tld",
|
||||
SendAccessToken::None,
|
||||
&[MatrixVersion::V1_0],
|
||||
)
|
||||
.unwrap();
|
||||
let req2 = Request::try_from_http_request(http_req, &["barVal", "@bazme:ruma.io"]).unwrap();
|
||||
|
||||
@ -75,7 +79,11 @@ fn invalid_uri_should_not_panic() {
|
||||
user: user_id!("@bazme:ruma.io").to_owned(),
|
||||
};
|
||||
|
||||
let result = req.try_into_http_request::<Vec<u8>>("invalid uri", SendAccessToken::None);
|
||||
let result = req.try_into_http_request::<Vec<u8>>(
|
||||
"invalid uri",
|
||||
SendAccessToken::None,
|
||||
&[MatrixVersion::V1_0],
|
||||
);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
@ -96,6 +104,7 @@ fn request_with_user_id_serde() {
|
||||
"https://homeserver.tld",
|
||||
SendAccessToken::None,
|
||||
user_id,
|
||||
&[MatrixVersion::V1_0],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@ -108,6 +117,8 @@ fn request_with_user_id_serde() {
|
||||
}
|
||||
|
||||
mod without_query {
|
||||
use ruma_api::MatrixVersion;
|
||||
|
||||
use super::{ruma_api, user_id, OutgoingRequestAppserviceExt, SendAccessToken, UserId};
|
||||
|
||||
ruma_api! {
|
||||
@ -154,6 +165,7 @@ mod without_query {
|
||||
"https://homeserver.tld",
|
||||
SendAccessToken::None,
|
||||
user_id,
|
||||
&[MatrixVersion::V1_0],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
#![allow(clippy::exhaustive_structs)]
|
||||
|
||||
use http::header::{Entry, CONTENT_TYPE};
|
||||
use ruma_api::{ruma_api, OutgoingRequest as _, OutgoingResponse as _, SendAccessToken};
|
||||
use ruma_api::{
|
||||
ruma_api, MatrixVersion, OutgoingRequest as _, OutgoingResponse as _, SendAccessToken,
|
||||
};
|
||||
|
||||
ruma_api! {
|
||||
metadata: {
|
||||
@ -48,7 +50,11 @@ fn response_content_type_override() {
|
||||
fn request_content_type_override() {
|
||||
let req = Request { location: None, stuff: "magic".into() };
|
||||
let mut http_req = req
|
||||
.try_into_http_request::<Vec<u8>>("https://homeserver.tld", SendAccessToken::None)
|
||||
.try_into_http_request::<Vec<u8>>(
|
||||
"https://homeserver.tld",
|
||||
SendAccessToken::None,
|
||||
&[MatrixVersion::V1_0],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
|
@ -6,8 +6,8 @@ use bytes::BufMut;
|
||||
use http::{header::CONTENT_TYPE, method::Method};
|
||||
use ruma_api::{
|
||||
error::{FromHttpRequestError, FromHttpResponseError, IntoHttpError, MatrixError, ServerError},
|
||||
AuthScheme, EndpointError, IncomingRequest, IncomingResponse, Metadata, OutgoingRequest,
|
||||
OutgoingResponse, SendAccessToken,
|
||||
AuthScheme, EndpointError, IncomingRequest, IncomingResponse, MatrixVersion, Metadata,
|
||||
OutgoingRequest, OutgoingResponse, SendAccessToken,
|
||||
};
|
||||
use ruma_identifiers::{RoomAliasId, RoomId};
|
||||
use ruma_serde::Outgoing;
|
||||
@ -49,6 +49,8 @@ impl OutgoingRequest for Request {
|
||||
self,
|
||||
base_url: &str,
|
||||
_access_token: SendAccessToken<'_>,
|
||||
// FIXME: properly integrate
|
||||
_considering_versions: &'_ [MatrixVersion],
|
||||
) -> Result<http::Request<T>, IntoHttpError> {
|
||||
let url = (base_url.to_owned() + METADATA.path)
|
||||
.replace(":room_alias", &self.room_alias.to_string());
|
||||
|
@ -1,4 +1,4 @@
|
||||
use ruma_api::{OutgoingRequest as _, OutgoingResponse as _, SendAccessToken};
|
||||
use ruma_api::{MatrixVersion, OutgoingRequest as _, OutgoingResponse as _, SendAccessToken};
|
||||
|
||||
mod get {
|
||||
ruma_api::ruma_api! {
|
||||
@ -36,7 +36,11 @@ mod post {
|
||||
fn empty_post_request_http_repr() {
|
||||
let req = post::Request {};
|
||||
let http_req = req
|
||||
.try_into_http_request::<Vec<u8>>("https://homeserver.tld", SendAccessToken::None)
|
||||
.try_into_http_request::<Vec<u8>>(
|
||||
"https://homeserver.tld",
|
||||
SendAccessToken::None,
|
||||
&[MatrixVersion::V1_0],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Empty POST requests should contain an empty dictionary as a body...
|
||||
@ -46,7 +50,11 @@ fn empty_post_request_http_repr() {
|
||||
fn empty_get_request_http_repr() {
|
||||
let req = get::Request {};
|
||||
let http_req = req
|
||||
.try_into_http_request::<Vec<u8>>("https://homeserver.tld", SendAccessToken::None)
|
||||
.try_into_http_request::<Vec<u8>>(
|
||||
"https://homeserver.tld",
|
||||
SendAccessToken::None,
|
||||
&[MatrixVersion::V1_0],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// ... but GET requests' bodies should be empty.
|
||||
|
101
crates/ruma-api/tests/select_path.rs
Normal file
101
crates/ruma-api/tests/select_path.rs
Normal file
@ -0,0 +1,101 @@
|
||||
use http::Method;
|
||||
use matches::assert_matches;
|
||||
use ruma_api::{
|
||||
error::IntoHttpError,
|
||||
select_path,
|
||||
MatrixVersion::{V1_0, V1_1, V1_2},
|
||||
Metadata,
|
||||
};
|
||||
|
||||
const BASE: Metadata = Metadata {
|
||||
description: "",
|
||||
method: Method::GET,
|
||||
name: "test_endpoint",
|
||||
path: "/depr/path",
|
||||
unstable_path: Some("/unstable/path"),
|
||||
r0_path: Some("/r0/path"),
|
||||
stable_path: Some("/stable/path"),
|
||||
rate_limited: false,
|
||||
authentication: ruma_api::AuthScheme::None,
|
||||
added: None,
|
||||
deprecated: None,
|
||||
removed: None,
|
||||
};
|
||||
|
||||
const U: &str = "u";
|
||||
const S: &str = "s";
|
||||
const R: &str = "r";
|
||||
|
||||
// TODO add test that can hook into tracing and verify the deprecation warning is emitted
|
||||
|
||||
#[test]
|
||||
fn select_stable() {
|
||||
let meta = Metadata { added: Some(V1_1), ..BASE };
|
||||
|
||||
let res = select_path(&[V1_0, V1_1], &meta, None, None, Some(format_args!("{}", S)))
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
assert_eq!(res, S);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select_unstable() {
|
||||
let meta = BASE;
|
||||
|
||||
let res =
|
||||
select_path(&[V1_0], &meta, Some(format_args!("{}", U)), None, None).unwrap().to_string();
|
||||
|
||||
assert_eq!(res, U);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select_r0() {
|
||||
let meta = Metadata { added: Some(V1_0), ..BASE };
|
||||
|
||||
let res =
|
||||
select_path(&[V1_0], &meta, None, Some(format_args!("{}", R)), Some(format_args!("{}", S)))
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
assert_eq!(res, R);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select_removed_err() {
|
||||
let meta = Metadata { added: Some(V1_0), deprecated: Some(V1_1), removed: Some(V1_2), ..BASE };
|
||||
|
||||
let res = select_path(
|
||||
&[V1_2],
|
||||
&meta,
|
||||
Some(format_args!("{}", U)),
|
||||
Some(format_args!("{}", R)),
|
||||
Some(format_args!("{}", S)),
|
||||
)
|
||||
.unwrap_err();
|
||||
|
||||
assert_matches!(res, IntoHttpError::EndpointRemoved(V1_2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partially_removed_but_stable() {
|
||||
let meta = Metadata { added: Some(V1_0), deprecated: Some(V1_1), removed: Some(V1_2), ..BASE };
|
||||
|
||||
let res =
|
||||
select_path(&[V1_1], &meta, None, Some(format_args!("{}", R)), Some(format_args!("{}", S)))
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
assert_eq!(res, S);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_unstable() {
|
||||
let meta = Metadata { added: Some(V1_1), ..BASE };
|
||||
|
||||
let res =
|
||||
select_path(&[V1_0], &meta, None, Some(format_args!("{}", R)), Some(format_args!("{}", S)))
|
||||
.unwrap_err();
|
||||
|
||||
assert_matches!(res, IntoHttpError::NoUnstablePath);
|
||||
}
|
@ -173,6 +173,7 @@ mod tests {
|
||||
.try_into_http_request::<Vec<u8>>(
|
||||
"https://homeserver.tld",
|
||||
SendAccessToken::IfRequired("auth_tok"),
|
||||
&[ruma_api::MatrixVersion::V1_0],
|
||||
)
|
||||
.unwrap();
|
||||
let json_body: serde_json::Value = serde_json::from_slice(req.body()).unwrap();
|
||||
|
@ -76,7 +76,7 @@ mod tests {
|
||||
#[cfg(feature = "client")]
|
||||
#[test]
|
||||
fn construct_request_from_refs() {
|
||||
use ruma_api::{OutgoingRequest as _, SendAccessToken};
|
||||
use ruma_api::{MatrixVersion, OutgoingRequest as _, SendAccessToken};
|
||||
use ruma_identifiers::server_name;
|
||||
|
||||
let req = super::Request {
|
||||
@ -87,6 +87,7 @@ mod tests {
|
||||
.try_into_http_request::<Vec<u8>>(
|
||||
"https://homeserver.tld",
|
||||
SendAccessToken::IfRequired("auth_tok"),
|
||||
&[MatrixVersion::V1_0],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
@ -77,7 +77,7 @@ mod tests {
|
||||
#[cfg(feature = "client")]
|
||||
#[test]
|
||||
fn serialize_request() {
|
||||
use ruma_api::{OutgoingRequest, SendAccessToken};
|
||||
use ruma_api::{MatrixVersion, OutgoingRequest, SendAccessToken};
|
||||
use ruma_identifiers::user_id;
|
||||
|
||||
use crate::r0::filter::FilterDefinition;
|
||||
@ -87,6 +87,7 @@ mod tests {
|
||||
.try_into_http_request::<Vec<u8>>(
|
||||
"https://matrix.org",
|
||||
SendAccessToken::IfRequired("tok"),
|
||||
&[MatrixVersion::V1_0]
|
||||
),
|
||||
Ok(res) if res.body() == b"{}"
|
||||
);
|
||||
|
@ -141,7 +141,7 @@ pub enum Direction {
|
||||
#[cfg(all(test, feature = "client"))]
|
||||
mod tests {
|
||||
use js_int::uint;
|
||||
use ruma_api::{OutgoingRequest, SendAccessToken};
|
||||
use ruma_api::{MatrixVersion, OutgoingRequest, SendAccessToken};
|
||||
use ruma_identifiers::room_id;
|
||||
|
||||
use super::{Direction, Request};
|
||||
@ -175,6 +175,7 @@ mod tests {
|
||||
.try_into_http_request(
|
||||
"https://homeserver.tld",
|
||||
SendAccessToken::IfRequired("auth_tok"),
|
||||
&[MatrixVersion::V1_0],
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
@ -203,6 +204,7 @@ mod tests {
|
||||
.try_into_http_request::<Vec<u8>>(
|
||||
"https://homeserver.tld",
|
||||
SendAccessToken::IfRequired("auth_tok"),
|
||||
&[MatrixVersion::V1_0],
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!("from=token&to=token2&dir=b&limit=0", request.uri().query().unwrap(),);
|
||||
|
@ -317,7 +317,7 @@ mod tests {
|
||||
#[test]
|
||||
#[cfg(feature = "client")]
|
||||
fn serialize_login_request_body() {
|
||||
use ruma_api::{OutgoingRequest, SendAccessToken};
|
||||
use ruma_api::{MatrixVersion, OutgoingRequest, SendAccessToken};
|
||||
use ruma_common::thirdparty::Medium;
|
||||
use serde_json::Value as JsonValue;
|
||||
|
||||
@ -329,7 +329,11 @@ mod tests {
|
||||
device_id: None,
|
||||
initial_device_display_name: Some("test"),
|
||||
}
|
||||
.try_into_http_request("https://homeserver.tld", SendAccessToken::None)
|
||||
.try_into_http_request(
|
||||
"https://homeserver.tld",
|
||||
SendAccessToken::None,
|
||||
&[MatrixVersion::V1_0],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let req_body_value: JsonValue = serde_json::from_slice(req.body()).unwrap();
|
||||
@ -353,7 +357,11 @@ mod tests {
|
||||
device_id: None,
|
||||
initial_device_display_name: Some("test"),
|
||||
}
|
||||
.try_into_http_request("https://homeserver.tld", SendAccessToken::None)
|
||||
.try_into_http_request(
|
||||
"https://homeserver.tld",
|
||||
SendAccessToken::None,
|
||||
&[MatrixVersion::V1_0],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let req_body_value: JsonValue = serde_json::from_slice(req.body()).unwrap();
|
||||
|
@ -45,14 +45,18 @@ impl Response {
|
||||
|
||||
#[cfg(all(test, feature = "client"))]
|
||||
mod tests {
|
||||
use ruma_api::{OutgoingRequest, SendAccessToken};
|
||||
use ruma_api::{MatrixVersion, OutgoingRequest, SendAccessToken};
|
||||
|
||||
use super::Request;
|
||||
|
||||
#[test]
|
||||
fn serialize_sso_login_request_uri() {
|
||||
let req: http::Request<Vec<u8>> = Request { redirect_url: "https://example.com/sso" }
|
||||
.try_into_http_request("https://homeserver.tld", SendAccessToken::None)
|
||||
.try_into_http_request(
|
||||
"https://homeserver.tld",
|
||||
SendAccessToken::None,
|
||||
&[MatrixVersion::V1_0],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
|
@ -69,17 +69,29 @@ impl<'a> ruma_api::OutgoingRequest for Request<'a> {
|
||||
self,
|
||||
base_url: &str,
|
||||
access_token: ruma_api::SendAccessToken<'_>,
|
||||
considering_versions: &'_ [ruma_api::MatrixVersion],
|
||||
) -> Result<http::Request<T>, ruma_api::error::IntoHttpError> {
|
||||
use std::borrow::Cow;
|
||||
|
||||
use http::header;
|
||||
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
|
||||
|
||||
let room_id_percent = utf8_percent_encode(self.room_id.as_str(), NON_ALPHANUMERIC);
|
||||
let event_type_percent = utf8_percent_encode(self.event_type.as_str(), NON_ALPHANUMERIC);
|
||||
|
||||
let mut url = format!(
|
||||
"{}/_matrix/client/r0/rooms/{}/state/{}",
|
||||
"{}{}",
|
||||
base_url.strip_suffix('/').unwrap_or(base_url),
|
||||
utf8_percent_encode(self.room_id.as_str(), NON_ALPHANUMERIC),
|
||||
utf8_percent_encode(self.event_type.as_str(), NON_ALPHANUMERIC)
|
||||
ruma_api::select_path(
|
||||
considering_versions,
|
||||
&METADATA,
|
||||
None,
|
||||
Some(format_args!(
|
||||
"/_matrix/client/r0/rooms/{}/state/{}",
|
||||
room_id_percent, event_type_percent
|
||||
)),
|
||||
None,
|
||||
)?
|
||||
);
|
||||
|
||||
if !self.state_key.is_empty() {
|
||||
|
@ -93,17 +93,29 @@ impl<'a> ruma_api::OutgoingRequest for Request<'a> {
|
||||
self,
|
||||
base_url: &str,
|
||||
access_token: ruma_api::SendAccessToken<'_>,
|
||||
considering_versions: &'_ [ruma_api::MatrixVersion],
|
||||
) -> Result<http::Request<T>, ruma_api::error::IntoHttpError> {
|
||||
use std::borrow::Cow;
|
||||
|
||||
use http::header::{self, HeaderValue};
|
||||
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
|
||||
|
||||
let room_id_percent = utf8_percent_encode(self.room_id.as_str(), NON_ALPHANUMERIC);
|
||||
let event_type_percent = utf8_percent_encode(self.event_type, NON_ALPHANUMERIC);
|
||||
|
||||
let mut url = format!(
|
||||
"{}/_matrix/client/r0/rooms/{}/state/{}",
|
||||
"{}{}",
|
||||
base_url.strip_suffix('/').unwrap_or(base_url),
|
||||
utf8_percent_encode(self.room_id.as_str(), NON_ALPHANUMERIC),
|
||||
utf8_percent_encode(self.event_type, NON_ALPHANUMERIC),
|
||||
ruma_api::select_path(
|
||||
considering_versions,
|
||||
&METADATA,
|
||||
None,
|
||||
Some(format_args!(
|
||||
"/_matrix/client/r0/rooms/{}/state/{}",
|
||||
room_id_percent, event_type_percent
|
||||
)),
|
||||
None,
|
||||
)?
|
||||
);
|
||||
|
||||
// Last URL segment is optional, that is why this trait impl is not generated.
|
||||
|
@ -610,7 +610,7 @@ mod tests {
|
||||
mod client_tests {
|
||||
use std::time::Duration;
|
||||
|
||||
use ruma_api::{OutgoingRequest as _, SendAccessToken};
|
||||
use ruma_api::{MatrixVersion, OutgoingRequest as _, SendAccessToken};
|
||||
|
||||
use super::{Filter, PresenceState, Request};
|
||||
|
||||
@ -623,7 +623,11 @@ mod client_tests {
|
||||
set_presence: &PresenceState::Offline,
|
||||
timeout: Some(Duration::from_millis(30000)),
|
||||
}
|
||||
.try_into_http_request("https://homeserver.tld", SendAccessToken::IfRequired("auth_tok"))
|
||||
.try_into_http_request(
|
||||
"https://homeserver.tld",
|
||||
SendAccessToken::IfRequired("auth_tok"),
|
||||
&[MatrixVersion::V1_0],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let uri = req.uri();
|
||||
|
@ -49,14 +49,18 @@ impl Response {
|
||||
|
||||
#[cfg(all(test, feature = "client"))]
|
||||
mod tests {
|
||||
use ruma_api::{OutgoingRequest as _, SendAccessToken};
|
||||
use ruma_api::{MatrixVersion, OutgoingRequest as _, SendAccessToken};
|
||||
|
||||
use super::Request;
|
||||
|
||||
#[test]
|
||||
fn serialize_sso_login_with_provider_request_uri() {
|
||||
let req = Request { idp_id: "provider", redirect_url: "https://example.com/sso" }
|
||||
.try_into_http_request::<Vec<u8>>("https://homeserver.tld", SendAccessToken::None)
|
||||
.try_into_http_request::<Vec<u8>>(
|
||||
"https://homeserver.tld",
|
||||
SendAccessToken::None,
|
||||
&[MatrixVersion::V1_0],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
|
@ -1,13 +1,17 @@
|
||||
#![cfg(feature = "client")]
|
||||
|
||||
use http::HeaderMap;
|
||||
use ruma_api::{OutgoingRequest as _, SendAccessToken};
|
||||
use ruma_api::{MatrixVersion, OutgoingRequest as _, SendAccessToken};
|
||||
use ruma_client_api::unversioned::discover_homeserver;
|
||||
|
||||
#[test]
|
||||
fn get_request_headers() {
|
||||
let req: http::Request<Vec<u8>> = discover_homeserver::Request::new()
|
||||
.try_into_http_request("https://homeserver.tld", SendAccessToken::None)
|
||||
.try_into_http_request(
|
||||
"https://homeserver.tld",
|
||||
SendAccessToken::None,
|
||||
&[MatrixVersion::V1_0],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(*req.headers(), HeaderMap::default());
|
||||
|
@ -3,6 +3,7 @@ use std::time::Duration;
|
||||
use assign::assign;
|
||||
use async_stream::try_stream;
|
||||
use futures_core::stream::Stream;
|
||||
use ruma_api::MatrixVersion;
|
||||
use ruma_client_api::r0::{
|
||||
account::register::{self, RegistrationKind},
|
||||
session::login::{self, LoginInfo},
|
||||
@ -33,7 +34,7 @@ impl<C: HttpClient> Client<C> {
|
||||
device_id,
|
||||
initial_device_display_name,
|
||||
}
|
||||
))
|
||||
), &[MatrixVersion::V1_0])
|
||||
.await?;
|
||||
|
||||
*self.0.access_token.lock().unwrap() = Some(response.access_token.clone());
|
||||
@ -49,7 +50,10 @@ impl<C: HttpClient> Client<C> {
|
||||
&self,
|
||||
) -> Result<register::Response, Error<C::Error, ruma_client_api::r0::uiaa::UiaaResponse>> {
|
||||
let response = self
|
||||
.send_request(assign!(register::Request::new(), { kind: RegistrationKind::Guest }))
|
||||
.send_request(
|
||||
assign!(register::Request::new(), { kind: RegistrationKind::Guest }),
|
||||
&[MatrixVersion::V1_0],
|
||||
)
|
||||
.await?;
|
||||
|
||||
*self.0.access_token.lock().unwrap() = response.access_token.clone();
|
||||
@ -70,7 +74,10 @@ impl<C: HttpClient> Client<C> {
|
||||
password: &str,
|
||||
) -> Result<register::Response, Error<C::Error, ruma_client_api::r0::uiaa::UiaaResponse>> {
|
||||
let response = self
|
||||
.send_request(assign!(register::Request::new(), { username, password: Some(password) }))
|
||||
.send_request(
|
||||
assign!(register::Request::new(), { username, password: Some(password)}),
|
||||
&[MatrixVersion::V1_0],
|
||||
)
|
||||
.await?;
|
||||
|
||||
*self.0.access_token.lock().unwrap() = response.access_token.clone();
|
||||
@ -120,7 +127,7 @@ impl<C: HttpClient> Client<C> {
|
||||
since: Some(&since),
|
||||
set_presence,
|
||||
timeout,
|
||||
}))
|
||||
}), &[MatrixVersion::V1_0])
|
||||
.await?;
|
||||
|
||||
since = response.next_batch.clone();
|
||||
|
@ -5,7 +5,7 @@ use std::{future::Future, pin::Pin};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use bytes::BufMut;
|
||||
use ruma_api::{OutgoingRequest, SendAccessToken};
|
||||
use ruma_api::{MatrixVersion, OutgoingRequest, SendAccessToken};
|
||||
use ruma_identifiers::UserId;
|
||||
|
||||
use crate::{add_user_id_to_query, ResponseError, ResponseResult};
|
||||
@ -64,9 +64,16 @@ pub trait HttpClientExt: HttpClient {
|
||||
&'a self,
|
||||
homeserver_url: &str,
|
||||
access_token: SendAccessToken<'_>,
|
||||
for_versions: &[MatrixVersion],
|
||||
request: R,
|
||||
) -> Pin<Box<dyn Future<Output = ResponseResult<Self, R>> + 'a>> {
|
||||
self.send_customized_matrix_request(homeserver_url, access_token, request, |_| Ok(()))
|
||||
self.send_customized_matrix_request(
|
||||
homeserver_url,
|
||||
access_token,
|
||||
for_versions,
|
||||
request,
|
||||
|_| Ok(()),
|
||||
)
|
||||
}
|
||||
|
||||
/// Turn a strongly-typed matrix request into an `http::Request`, customize it and send it to
|
||||
@ -76,6 +83,7 @@ pub trait HttpClientExt: HttpClient {
|
||||
&'a self,
|
||||
homeserver_url: &str,
|
||||
access_token: SendAccessToken<'_>,
|
||||
for_versions: &[MatrixVersion],
|
||||
request: R,
|
||||
customize: F,
|
||||
) -> Pin<Box<dyn Future<Output = ResponseResult<Self, R>> + 'a>>
|
||||
@ -87,6 +95,7 @@ pub trait HttpClientExt: HttpClient {
|
||||
self,
|
||||
homeserver_url,
|
||||
access_token,
|
||||
for_versions,
|
||||
request,
|
||||
customize,
|
||||
))
|
||||
@ -101,12 +110,14 @@ pub trait HttpClientExt: HttpClient {
|
||||
&'a self,
|
||||
homeserver_url: &str,
|
||||
access_token: SendAccessToken<'_>,
|
||||
for_versions: &[MatrixVersion],
|
||||
user_id: &'a UserId,
|
||||
request: R,
|
||||
) -> Pin<Box<dyn Future<Output = ResponseResult<Self, R>> + 'a>> {
|
||||
self.send_customized_matrix_request(
|
||||
homeserver_url,
|
||||
access_token,
|
||||
for_versions,
|
||||
request,
|
||||
add_user_id_to_query::<Self, R>(user_id),
|
||||
)
|
||||
|
@ -58,10 +58,11 @@
|
||||
//!
|
||||
//! use ruma_client_api::r0::alias::get_alias;
|
||||
//! use ruma_identifiers::{room_alias_id, room_id};
|
||||
//! use ruma_api::MatrixVersion;
|
||||
//!
|
||||
//! async {
|
||||
//! let response = client
|
||||
//! .send_request(get_alias::Request::new(room_alias_id!("#example_room:example.com")))
|
||||
//! .send_request(get_alias::Request::new(room_alias_id!("#example_room:example.com")), &[MatrixVersion::V1_0])
|
||||
//! .await?;
|
||||
//!
|
||||
//! assert_eq!(response.room_id, room_id!("!n8f893n9:example.com"));
|
||||
@ -97,7 +98,7 @@ use std::{
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use ruma_api::{OutgoingRequest, SendAccessToken};
|
||||
use ruma_api::{MatrixVersion, OutgoingRequest, SendAccessToken};
|
||||
use ruma_identifiers::UserId;
|
||||
|
||||
// "Undo" rename from `Cargo.toml` that only serves to make crate names available as a Cargo
|
||||
@ -179,14 +180,19 @@ impl<C: DefaultConstructibleHttpClient> Client<C> {
|
||||
|
||||
impl<C: HttpClient> Client<C> {
|
||||
/// Makes a request to a Matrix API endpoint.
|
||||
pub async fn send_request<R: OutgoingRequest>(&self, request: R) -> ResponseResult<C, R> {
|
||||
self.send_customized_request(request, |_| Ok(())).await
|
||||
pub async fn send_request<R: OutgoingRequest>(
|
||||
&self,
|
||||
request: R,
|
||||
for_versions: &[MatrixVersion],
|
||||
) -> ResponseResult<C, R> {
|
||||
self.send_customized_request(request, for_versions, |_| Ok(())).await
|
||||
}
|
||||
|
||||
/// Makes a request to a Matrix API endpoint including additional URL parameters.
|
||||
pub async fn send_customized_request<R, F>(
|
||||
&self,
|
||||
request: R,
|
||||
for_versions: &[MatrixVersion],
|
||||
customize: F,
|
||||
) -> ResponseResult<C, R>
|
||||
where
|
||||
@ -203,6 +209,7 @@ impl<C: HttpClient> Client<C> {
|
||||
&self.0.http_client,
|
||||
&self.0.homeserver_url,
|
||||
send_access_token,
|
||||
for_versions,
|
||||
request,
|
||||
customize,
|
||||
)
|
||||
@ -217,8 +224,10 @@ impl<C: HttpClient> Client<C> {
|
||||
&self,
|
||||
user_id: &UserId,
|
||||
request: R,
|
||||
for_versions: &[MatrixVersion],
|
||||
) -> ResponseResult<C, R> {
|
||||
self.send_customized_request(request, add_user_id_to_query::<C, R>(user_id)).await
|
||||
self.send_customized_request(request, for_versions, add_user_id_to_query::<C, R>(user_id))
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@ -226,6 +235,7 @@ fn send_customized_request<'a, C, R, F>(
|
||||
http_client: &'a C,
|
||||
homeserver_url: &str,
|
||||
send_access_token: SendAccessToken<'_>,
|
||||
for_versions: &[MatrixVersion],
|
||||
request: R,
|
||||
customize: F,
|
||||
) -> impl Future<Output = ResponseResult<C, R>> + Send + 'a
|
||||
@ -235,7 +245,7 @@ where
|
||||
F: FnOnce(&mut http::Request<C::RequestBody>) -> Result<(), ResponseError<C, R>>,
|
||||
{
|
||||
let http_req = request
|
||||
.try_into_http_request(homeserver_url, send_access_token)
|
||||
.try_into_http_request(homeserver_url, send_access_token, for_versions)
|
||||
.map_err(ResponseError::<C, R>::from)
|
||||
.and_then(|mut req| {
|
||||
customize(&mut req)?;
|
||||
|
@ -5,6 +5,7 @@ use ruma::{
|
||||
events::room::message::RoomMessageEventContent,
|
||||
RoomAliasId,
|
||||
};
|
||||
use ruma_api::MatrixVersion;
|
||||
use ruma_identifiers::TransactionId;
|
||||
|
||||
type MatrixClient = ruma_client::Client<ruma_client::http_client::HyperNativeTls>;
|
||||
@ -18,14 +19,20 @@ async fn hello_world(
|
||||
let client = MatrixClient::new(homeserver_url, None);
|
||||
client.log_in(username, password, None, Some("ruma-example-client")).await?;
|
||||
|
||||
let room_id = client.send_request(get_alias::Request::new(room_alias)).await?.room_id;
|
||||
client.send_request(join_room_by_id::Request::new(&room_id)).await?;
|
||||
let room_id = client
|
||||
.send_request(get_alias::Request::new(room_alias), &[MatrixVersion::V1_0])
|
||||
.await?
|
||||
.room_id;
|
||||
client.send_request(join_room_by_id::Request::new(&room_id), &[MatrixVersion::V1_0]).await?;
|
||||
client
|
||||
.send_request(send_message_event::Request::new(
|
||||
.send_request(
|
||||
send_message_event::Request::new(
|
||||
&room_id,
|
||||
&TransactionId::new(),
|
||||
&RoomMessageEventContent::text_plain("Hello World!"),
|
||||
)?)
|
||||
)?,
|
||||
&[MatrixVersion::V1_0],
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
|
@ -24,9 +24,12 @@ async fn log_messages(
|
||||
|
||||
let filter = FilterDefinition::ignore_all().into();
|
||||
let initial_sync_response = client
|
||||
.send_request(assign!(sync_events::Request::new(), {
|
||||
.send_request(
|
||||
assign!(sync_events::Request::new(), {
|
||||
filter: Some(&filter),
|
||||
}))
|
||||
}),
|
||||
&[ruma_api::MatrixVersion::V1_0],
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut sync_stream = Box::pin(client.sync(
|
||||
|
@ -1,10 +1,13 @@
|
||||
use std::{convert::TryInto, error::Error, io, process::exit, time::Duration};
|
||||
|
||||
use ruma::{
|
||||
api::client::r0::{
|
||||
api::{
|
||||
client::r0::{
|
||||
filter::FilterDefinition, membership::join_room_by_id, message::send_message_event,
|
||||
sync::sync_events,
|
||||
},
|
||||
MatrixVersion,
|
||||
},
|
||||
assign, client,
|
||||
events::{
|
||||
room::message::{MessageType, RoomMessageEventContent},
|
||||
@ -59,9 +62,12 @@ async fn run() -> Result<(), Box<dyn Error>> {
|
||||
|
||||
let filter = FilterDefinition::ignore_all().into();
|
||||
let initial_sync_response = matrix_client
|
||||
.send_request(assign!(sync_events::Request::new(), {
|
||||
.send_request(
|
||||
assign!(sync_events::Request::new(), {
|
||||
filter: Some(&filter),
|
||||
}))
|
||||
}),
|
||||
&[MatrixVersion::V1_0],
|
||||
)
|
||||
.await?;
|
||||
let user_id = &config.username;
|
||||
let not_senders = &[user_id.clone()];
|
||||
@ -152,7 +158,7 @@ async fn handle_messages(
|
||||
let txn_id = TransactionId::new();
|
||||
let req = send_message_event::Request::new(room_id, &txn_id, &joke_content)?;
|
||||
// Do nothing if we can't send the message.
|
||||
let _ = matrix_client.send_request(req).await;
|
||||
let _ = matrix_client.send_request(req, &[MatrixVersion::V1_0]).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -165,14 +171,16 @@ async fn handle_invitations(
|
||||
room_id: &RoomId,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
println!("invited to {}", &room_id);
|
||||
matrix_client.send_request(join_room_by_id::Request::new(room_id)).await?;
|
||||
matrix_client
|
||||
.send_request(join_room_by_id::Request::new(room_id), &[MatrixVersion::V1_0])
|
||||
.await?;
|
||||
|
||||
let greeting = "Hello! My name is Mr. Bot! I like to tell jokes. Like this one: ";
|
||||
let joke = get_joke(http_client).await.unwrap_or_else(|_| "err... never mind.".to_owned());
|
||||
let content = RoomMessageEventContent::text_plain(format!("{}\n{}", greeting, joke));
|
||||
let txn_id = TransactionId::new();
|
||||
let message = send_message_event::Request::new(room_id, &txn_id, &content)?;
|
||||
matrix_client.send_request(message).await?;
|
||||
matrix_client.send_request(message, &[MatrixVersion::V1_0]).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user