diff --git a/ruma-client/src/http_client.rs b/ruma-client/src/http_client.rs index 030445f3..c2ca2a52 100644 --- a/ruma-client/src/http_client.rs +++ b/ruma-client/src/http_client.rs @@ -6,8 +6,9 @@ use std::{future::Future, pin::Pin}; use async_trait::async_trait; use bytes::BufMut; use ruma_api::{OutgoingRequest, SendAccessToken}; +use ruma_identifiers::UserId; -use crate::{ResponseError, ResponseResult}; +use crate::{add_user_id_to_query, ResponseError, ResponseResult}; #[cfg(feature = "hyper")] mod hyper; @@ -90,6 +91,29 @@ pub trait HttpClientExt: HttpClient { customize, )) } + + /// Turn a strongly-typed matrix request into an `http::Request`, add a `user_id` query + /// parameter to it and send it to get back a strongly-typed response. + /// + /// This method is meant to be used by application services when interacting with the + /// client-server API. + fn send_request_as<'a, R: OutgoingRequest + 'a>( + &'a self, + homeserver_url: &str, + access_token: SendAccessToken<'_>, + user_id: &'a UserId, + request: R, + ) -> Pin> + 'a>> + where + ::EndpointError: Send, + { + self.send_customized_request( + homeserver_url, + access_token, + request, + add_user_id_to_query::(user_id), + ) + } } #[async_trait] diff --git a/ruma-client/src/lib.rs b/ruma-client/src/lib.rs index eef15488..3f69540d 100644 --- a/ruma-client/src/lib.rs +++ b/ruma-client/src/lib.rs @@ -79,6 +79,7 @@ use std::{ }; use ruma_api::{OutgoingRequest, SendAccessToken}; +use ruma_identifiers::UserId; // "Undo" rename from `Cargo.toml` that only serves to make `hyper-rustls` available as a Cargo // feature name. @@ -190,6 +191,21 @@ impl Client { ) .await } + + /// Makes a request to a Matrix API endpoint as a virtual user. + /// + /// This method is meant to be used by application services when interacting with the + /// client-server API. + pub async fn send_request_as( + &self, + user_id: &UserId, + request: R, + ) -> ResponseResult + where + ::EndpointError: Send, + { + self.send_customized_request(request, add_user_id_to_query::(user_id)).await + } } fn send_customized_request<'a, C, R, F>( @@ -218,3 +234,25 @@ where Ok(ruma_api::IncomingResponse::try_from_http_response(http_res)?) } } + +fn add_user_id_to_query( + user_id: &UserId, +) -> impl FnOnce(&mut http::Request) -> Result<(), ResponseError> + '_ { + use assign::assign; + use http::uri::Uri; + use ruma_serde::urlencoded; + + move |http_request| { + let extra_params = urlencoded::to_string(&[("user_id", user_id)]).unwrap(); + let uri = http_request.uri_mut(); + let new_path_and_query = match uri.query() { + Some(params) => format!("{}?{}&{}", uri.path(), params, extra_params), + None => format!("{}?{}", uri.path(), extra_params), + }; + *uri = Uri::from_parts(assign!(uri.clone().into_parts(), { + path_and_query: Some(new_path_and_query.parse()?), + }))?; + + Ok(()) + } +}