From 397727726e369ea0073711c1c549f3a2cc32eeeb Mon Sep 17 00:00:00 2001 From: Jonathan de Jong Date: Sat, 12 Feb 2022 13:44:11 +0100 Subject: [PATCH] api: Add multiple path support to OutgoingRequest --- crates/ruma-api-macros/src/api/metadata.rs | 1 + crates/ruma-api-macros/src/api/request.rs | 8 +- crates/ruma-api-macros/src/request.rs | 22 +++- .../ruma-api-macros/src/request/outgoing.rs | 55 ++++------ crates/ruma-api-macros/src/util.rs | 33 +++++- crates/ruma-api/Cargo.toml | 2 + crates/ruma-api/src/error.rs | 14 ++- crates/ruma-api/src/lib.rs | 84 ++++++++++++++- crates/ruma-api/src/metadata.rs | 21 ++-- crates/ruma-api/tests/conversions.rs | 20 +++- crates/ruma-api/tests/header_override.rs | 10 +- crates/ruma-api/tests/manual_endpoint_impl.rs | 6 +- crates/ruma-api/tests/no_fields.rs | 14 ++- crates/ruma-api/tests/select_path.rs | 101 ++++++++++++++++++ .../src/event/push_events/v1.rs | 1 + .../src/r0/directory/get_public_rooms.rs | 3 +- .../src/r0/filter/create_filter.rs | 3 +- .../src/r0/message/get_message_events.rs | 4 +- .../ruma-client-api/src/r0/session/login.rs | 14 ++- .../src/r0/session/sso_login.rs | 8 +- .../src/r0/state/get_state_events_for_key.rs | 18 +++- .../src/r0/state/send_state_event.rs | 18 +++- .../src/r0/sync/sync_events.rs | 8 +- .../src/session/sso_login_with_provider/v3.rs | 8 +- crates/ruma-client-api/tests/headers.rs | 8 +- crates/ruma-client/src/client_api.rs | 15 ++- crates/ruma-client/src/http_client.rs | 15 ++- crates/ruma-client/src/lib.rs | 22 ++-- crates/ruma/examples/hello_world.rs | 21 ++-- crates/ruma/examples/message_log.rs | 9 +- examples/joke_bot/src/main.rs | 26 +++-- 31 files changed, 475 insertions(+), 117 deletions(-) create mode 100644 crates/ruma-api/tests/select_path.rs diff --git a/crates/ruma-api-macros/src/api/metadata.rs b/crates/ruma-api-macros/src/api/metadata.rs index b80179e8..0b86cfa8 100644 --- a/crates/ruma-api-macros/src/api/metadata.rs +++ b/crates/ruma-api-macros/src/api/metadata.rs @@ -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 { diff --git a/crates/ruma-api-macros/src/api/request.rs b/crates/ruma-api-macros/src/api/request.rs index 66fb3b76..ccf7ced7 100644 --- a/crates/ruma-api-macros/src/api/request.rs +++ b/crates/ruma-api-macros/src/api/request.rs @@ -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 )* diff --git a/crates/ruma-api-macros/src/request.rs b/crates/ruma-api-macros/src/request.rs index 2c85b7f3..823f37ea 100644 --- a/crates/ruma-api-macros/src/request.rs +++ b/crates/ruma-api-macros/src/request.rs @@ -52,7 +52,9 @@ pub fn expand_derive_request(input: DeriveInput) -> syn::Result { 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 { 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 { 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, + r0_path: Option, + stable_path: Option, error_ty: Type, } diff --git a/crates/ruma-api-macros/src/request/outgoing.rs b/crates/ruma-api-macros/src/request/outgoing.rs index d5cb884b..c56bb6bd 100644 --- a/crates/ruma-api-macros/src/request/outgoing.rs +++ b/crates/ruma-api-macros/src/request/outgoing.rs @@ -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 (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 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),*) - } + ( + 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), + ) } 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, #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, )); diff --git a/crates/ruma-api-macros/src/util.rs b/crates/ruma-api-macros/src/util.rs index eabda89f..2699a029 100644 --- a/crates/ruma-api-macros/src/util.rs +++ b/crates/ruma-api-macros/src/util.rs @@ -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 { 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),*) + } +} diff --git a/crates/ruma-api/Cargo.toml b/crates/ruma-api/Cargo.toml index 708d56e9..f3a62b81 100644 --- a/crates/ruma-api/Cargo.toml +++ b/crates/ruma-api/Cargo.toml @@ -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" diff --git a/crates/ruma-api/src/error.rs b/crates/ruma-api/src/error.rs index bc215f0d..02c4fbb3 100644 --- a/crates/ruma-api/src/error.rs +++ b/crates/ruma-api/src/error.rs @@ -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), diff --git a/crates/ruma-api/src/lib.rs b/crates/ruma-api/src/lib.rs index 2b77400f..8396f5ba 100644 --- a/crates/ruma-api/src/lib.rs +++ b/crates/ruma-api/src/lib.rs @@ -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, IntoHttpError>; } @@ -302,8 +313,10 @@ pub trait OutgoingRequestAppserviceExt: OutgoingRequest { base_url: &str, access_token: SendAccessToken<'_>, user_id: &UserId, + considering_versions: &'_ [MatrixVersion], ) -> Result, 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>, + r0: Option>, + stable: Option>, +) -> Result, 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) +} diff --git a/crates/ruma-api/src/metadata.rs b/crates/ruma-api/src/metadata.rs index 459b5ddf..1a35e540 100644 --- a/crates/ruma-api/src/metadata.rs +++ b/crates/ruma-api/src/metadata.rs @@ -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, /// 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, } @@ -71,10 +71,11 @@ pub struct Metadata { /// backwards-compatible manner. /// /// Matrix has a deprecation policy, read more about it here: . -// 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 { diff --git a/crates/ruma-api/tests/conversions.rs b/crates/ruma-api/tests/conversions.rs index 61a89113..fde06630 100644 --- a/crates/ruma-api/tests/conversions.rs +++ b/crates/ruma-api/tests/conversions.rs @@ -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::>("https://homeserver.tld", SendAccessToken::None) + .try_into_http_request::>( + "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::>("invalid uri", SendAccessToken::None); + let result = req.try_into_http_request::>( + "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(); diff --git a/crates/ruma-api/tests/header_override.rs b/crates/ruma-api/tests/header_override.rs index 42533da5..b089b5b4 100644 --- a/crates/ruma-api/tests/header_override.rs +++ b/crates/ruma-api/tests/header_override.rs @@ -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::>("https://homeserver.tld", SendAccessToken::None) + .try_into_http_request::>( + "https://homeserver.tld", + SendAccessToken::None, + &[MatrixVersion::V1_0], + ) .unwrap(); assert_eq!( diff --git a/crates/ruma-api/tests/manual_endpoint_impl.rs b/crates/ruma-api/tests/manual_endpoint_impl.rs index a00137a9..3a29fd4b 100644 --- a/crates/ruma-api/tests/manual_endpoint_impl.rs +++ b/crates/ruma-api/tests/manual_endpoint_impl.rs @@ -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, IntoHttpError> { let url = (base_url.to_owned() + METADATA.path) .replace(":room_alias", &self.room_alias.to_string()); diff --git a/crates/ruma-api/tests/no_fields.rs b/crates/ruma-api/tests/no_fields.rs index 6ab2b79c..ba931724 100644 --- a/crates/ruma-api/tests/no_fields.rs +++ b/crates/ruma-api/tests/no_fields.rs @@ -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::>("https://homeserver.tld", SendAccessToken::None) + .try_into_http_request::>( + "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::>("https://homeserver.tld", SendAccessToken::None) + .try_into_http_request::>( + "https://homeserver.tld", + SendAccessToken::None, + &[MatrixVersion::V1_0], + ) .unwrap(); // ... but GET requests' bodies should be empty. diff --git a/crates/ruma-api/tests/select_path.rs b/crates/ruma-api/tests/select_path.rs new file mode 100644 index 00000000..de72beb8 --- /dev/null +++ b/crates/ruma-api/tests/select_path.rs @@ -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); +} diff --git a/crates/ruma-appservice-api/src/event/push_events/v1.rs b/crates/ruma-appservice-api/src/event/push_events/v1.rs index 52505910..a2df2b46 100644 --- a/crates/ruma-appservice-api/src/event/push_events/v1.rs +++ b/crates/ruma-appservice-api/src/event/push_events/v1.rs @@ -173,6 +173,7 @@ mod tests { .try_into_http_request::>( "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(); diff --git a/crates/ruma-client-api/src/r0/directory/get_public_rooms.rs b/crates/ruma-client-api/src/r0/directory/get_public_rooms.rs index 82c0821c..280a1659 100644 --- a/crates/ruma-client-api/src/r0/directory/get_public_rooms.rs +++ b/crates/ruma-client-api/src/r0/directory/get_public_rooms.rs @@ -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::>( "https://homeserver.tld", SendAccessToken::IfRequired("auth_tok"), + &[MatrixVersion::V1_0], ) .unwrap(); diff --git a/crates/ruma-client-api/src/r0/filter/create_filter.rs b/crates/ruma-client-api/src/r0/filter/create_filter.rs index a4e740df..4809353b 100644 --- a/crates/ruma-client-api/src/r0/filter/create_filter.rs +++ b/crates/ruma-client-api/src/r0/filter/create_filter.rs @@ -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::>( "https://matrix.org", SendAccessToken::IfRequired("tok"), + &[MatrixVersion::V1_0] ), Ok(res) if res.body() == b"{}" ); diff --git a/crates/ruma-client-api/src/r0/message/get_message_events.rs b/crates/ruma-client-api/src/r0/message/get_message_events.rs index b33d4a4f..d486e0ea 100644 --- a/crates/ruma-client-api/src/r0/message/get_message_events.rs +++ b/crates/ruma-client-api/src/r0/message/get_message_events.rs @@ -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::>( "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(),); diff --git a/crates/ruma-client-api/src/r0/session/login.rs b/crates/ruma-client-api/src/r0/session/login.rs index d2c2fe3d..1d1ef1e8 100644 --- a/crates/ruma-client-api/src/r0/session/login.rs +++ b/crates/ruma-client-api/src/r0/session/login.rs @@ -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(); diff --git a/crates/ruma-client-api/src/r0/session/sso_login.rs b/crates/ruma-client-api/src/r0/session/sso_login.rs index 8b6984ad..7f9b85fa 100644 --- a/crates/ruma-client-api/src/r0/session/sso_login.rs +++ b/crates/ruma-client-api/src/r0/session/sso_login.rs @@ -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> = 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!( diff --git a/crates/ruma-client-api/src/r0/state/get_state_events_for_key.rs b/crates/ruma-client-api/src/r0/state/get_state_events_for_key.rs index bdfc582d..23db4db5 100644 --- a/crates/ruma-client-api/src/r0/state/get_state_events_for_key.rs +++ b/crates/ruma-client-api/src/r0/state/get_state_events_for_key.rs @@ -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, 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() { diff --git a/crates/ruma-client-api/src/r0/state/send_state_event.rs b/crates/ruma-client-api/src/r0/state/send_state_event.rs index 61f7bc5e..46f7635b 100644 --- a/crates/ruma-client-api/src/r0/state/send_state_event.rs +++ b/crates/ruma-client-api/src/r0/state/send_state_event.rs @@ -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, 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. diff --git a/crates/ruma-client-api/src/r0/sync/sync_events.rs b/crates/ruma-client-api/src/r0/sync/sync_events.rs index 7a03150d..6e795b18 100644 --- a/crates/ruma-client-api/src/r0/sync/sync_events.rs +++ b/crates/ruma-client-api/src/r0/sync/sync_events.rs @@ -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(); diff --git a/crates/ruma-client-api/src/session/sso_login_with_provider/v3.rs b/crates/ruma-client-api/src/session/sso_login_with_provider/v3.rs index 875a5d73..cacfecfe 100644 --- a/crates/ruma-client-api/src/session/sso_login_with_provider/v3.rs +++ b/crates/ruma-client-api/src/session/sso_login_with_provider/v3.rs @@ -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::>("https://homeserver.tld", SendAccessToken::None) + .try_into_http_request::>( + "https://homeserver.tld", + SendAccessToken::None, + &[MatrixVersion::V1_0], + ) .unwrap(); assert_eq!( diff --git a/crates/ruma-client-api/tests/headers.rs b/crates/ruma-client-api/tests/headers.rs index f9cd338c..37ffb17b 100644 --- a/crates/ruma-client-api/tests/headers.rs +++ b/crates/ruma-client-api/tests/headers.rs @@ -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> = 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()); diff --git a/crates/ruma-client/src/client_api.rs b/crates/ruma-client/src/client_api.rs index f5145aa9..1aa982e4 100644 --- a/crates/ruma-client/src/client_api.rs +++ b/crates/ruma-client/src/client_api.rs @@ -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 Client { 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 Client { &self, ) -> Result> { 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 Client { password: &str, ) -> Result> { 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 Client { since: Some(&since), set_presence, timeout, - })) + }), &[MatrixVersion::V1_0]) .await?; since = response.next_batch.clone(); diff --git a/crates/ruma-client/src/http_client.rs b/crates/ruma-client/src/http_client.rs index caaaa43c..87f3bff5 100644 --- a/crates/ruma-client/src/http_client.rs +++ b/crates/ruma-client/src/http_client.rs @@ -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> + '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> + '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> + 'a>> { self.send_customized_matrix_request( homeserver_url, access_token, + for_versions, request, add_user_id_to_query::(user_id), ) diff --git a/crates/ruma-client/src/lib.rs b/crates/ruma-client/src/lib.rs index d03ab034..fd7e5fa6 100644 --- a/crates/ruma-client/src/lib.rs +++ b/crates/ruma-client/src/lib.rs @@ -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 Client { impl Client { /// Makes a request to a Matrix API endpoint. - pub async fn send_request(&self, request: R) -> ResponseResult { - self.send_customized_request(request, |_| Ok(())).await + pub async fn send_request( + &self, + request: R, + for_versions: &[MatrixVersion], + ) -> ResponseResult { + 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( &self, request: R, + for_versions: &[MatrixVersion], customize: F, ) -> ResponseResult where @@ -203,6 +209,7 @@ impl Client { &self.0.http_client, &self.0.homeserver_url, send_access_token, + for_versions, request, customize, ) @@ -217,8 +224,10 @@ impl Client { &self, user_id: &UserId, request: R, + for_versions: &[MatrixVersion], ) -> ResponseResult { - self.send_customized_request(request, add_user_id_to_query::(user_id)).await + self.send_customized_request(request, for_versions, add_user_id_to_query::(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> + Send + 'a @@ -235,7 +245,7 @@ where F: FnOnce(&mut http::Request) -> Result<(), ResponseError>, { 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::::from) .and_then(|mut req| { customize(&mut req)?; diff --git a/crates/ruma/examples/hello_world.rs b/crates/ruma/examples/hello_world.rs index 5ad63d63..2affd3d3 100644 --- a/crates/ruma/examples/hello_world.rs +++ b/crates/ruma/examples/hello_world.rs @@ -5,6 +5,7 @@ use ruma::{ events::room::message::RoomMessageEventContent, RoomAliasId, }; +use ruma_api::MatrixVersion; use ruma_identifiers::TransactionId; type MatrixClient = ruma_client::Client; @@ -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( - &room_id, - &TransactionId::new(), - &RoomMessageEventContent::text_plain("Hello World!"), - )?) + .send_request( + send_message_event::Request::new( + &room_id, + &TransactionId::new(), + &RoomMessageEventContent::text_plain("Hello World!"), + )?, + &[MatrixVersion::V1_0], + ) .await?; Ok(()) diff --git a/crates/ruma/examples/message_log.rs b/crates/ruma/examples/message_log.rs index 4bc98510..1d122be3 100644 --- a/crates/ruma/examples/message_log.rs +++ b/crates/ruma/examples/message_log.rs @@ -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(), { - filter: Some(&filter), - })) + .send_request( + assign!(sync_events::Request::new(), { + filter: Some(&filter), + }), + &[ruma_api::MatrixVersion::V1_0], + ) .await?; let mut sync_stream = Box::pin(client.sync( diff --git a/examples/joke_bot/src/main.rs b/examples/joke_bot/src/main.rs index 18a2b19a..82fea723 100644 --- a/examples/joke_bot/src/main.rs +++ b/examples/joke_bot/src/main.rs @@ -1,9 +1,12 @@ use std::{convert::TryInto, error::Error, io, process::exit, time::Duration}; use ruma::{ - api::client::r0::{ - filter::FilterDefinition, membership::join_room_by_id, message::send_message_event, - sync::sync_events, + api::{ + client::r0::{ + filter::FilterDefinition, membership::join_room_by_id, message::send_message_event, + sync::sync_events, + }, + MatrixVersion, }, assign, client, events::{ @@ -59,9 +62,12 @@ async fn run() -> Result<(), Box> { let filter = FilterDefinition::ignore_all().into(); let initial_sync_response = matrix_client - .send_request(assign!(sync_events::Request::new(), { - filter: Some(&filter), - })) + .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> { 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(()) }