diff --git a/crates/core/src/account/service.rs b/crates/core/src/account/service.rs index bc42d8a..fad30a4 100644 --- a/crates/core/src/account/service.rs +++ b/crates/core/src/account/service.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use matrix::client::resources::session::Session; +use matrix::{admin::resources::user::UserService, client::resources::session::Session}; use tracing::instrument; use url::Url; use uuid::Uuid; @@ -8,7 +8,7 @@ use validator::{Validate, ValidationError}; use matrix::{ admin::resources::{ - user::{ListUsersParams, LoginAsUserDto, ThreePid, User as MatrixUser, UserCreateDto}, + user::{CreateUserBody, ListUsersQuery, LoginAsUserBody, ThreePid}, user_id::UserId, }, Client as MatrixAdminClient, @@ -118,9 +118,9 @@ impl AccountService { /// Matrix Server pub async fn is_email_available(&self, email: &str) -> Result { let user_id = UserId::new(email, self.admin.server_name()); - let exists = MatrixUser::list( + let exists = UserService::list( &self.admin, - ListUsersParams { + ListUsersQuery { user_id: Some(user_id.to_string()), ..Default::default() }, @@ -212,10 +212,10 @@ impl AccountService { Error::Unknown })?; - MatrixUser::create( + UserService::create( &self.admin, user_id.clone(), - UserCreateDto { + CreateUserBody { displayname: Some(dto.username), password: dto.password.to_string(), logout_devices: false, @@ -239,7 +239,7 @@ impl AccountService { Error::Unknown })?; - let matrix_account = MatrixUser::query_user_account(&self.admin, user_id.clone()) + let matrix_account = UserService::query_user_account(&self.admin, user_id.clone()) .await .map_err(|err| { tracing::error!(?err, "Failed to query user account"); @@ -266,7 +266,7 @@ impl AccountService { /// Creates an access token for the given user pub async fn issue_user_token(&self, user_id: UserId) -> Result { let credentials = - MatrixUser::login_as_user(&self.admin, user_id.clone(), LoginAsUserDto::default()) + UserService::login_as_user(&self.admin, user_id.clone(), LoginAsUserBody::default()) .await .map_err(|err| { tracing::error!(?err, ?user_id, "Failed to login as user"); @@ -283,7 +283,7 @@ impl AccountService { tracing::error!(?err, "Failed to get session from matrix as client"); Error::Unknown })?; - let matrix_account = MatrixUser::query_user_account(&self.admin, session.user_id.clone()) + let matrix_account = UserService::query_user_account(&self.admin, session.user_id.clone()) .await .map_err(|err| { tracing::error!(?err, "Failed to query user account"); diff --git a/crates/core/src/room/service.rs b/crates/core/src/room/service.rs index 1219732..6ba2a82 100644 --- a/crates/core/src/room/service.rs +++ b/crates/core/src/room/service.rs @@ -51,7 +51,7 @@ impl RoomService { .await { Ok(room) => Ok(Room { - room_id: room.room_id, + room_id: room.room_id.to_string(), }), Err(err) => { tracing::error!("Failed to create public room: {}", err); diff --git a/crates/matrix/src/admin/resources/room.rs b/crates/matrix/src/admin/resources/room.rs index 1b9ce9d..a603f23 100644 --- a/crates/matrix/src/admin/resources/room.rs +++ b/crates/matrix/src/admin/resources/room.rs @@ -5,48 +5,17 @@ use anyhow::Result; use ruma_common::{serde::Raw, EventId, OwnedRoomAliasId, OwnedRoomId, OwnedUserId, RoomId}; -use ruma_events::{AnyMessageLikeEvent, AnyStateEvent}; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use ruma_events::{AnyMessageLikeEvent, AnyStateEvent, AnyTimelineEvent}; +use serde::{Deserialize, Serialize}; use tracing::instrument; -use crate::{error::MatrixError, http::Client}; +use crate::{error::MatrixError, event_filter::RoomEventFilter, http::Client}; #[derive(Default)] pub struct RoomService; #[derive(Default, Debug, Serialize)] -pub struct RoomEventFilter { - #[serde(skip_serializing_if = "<[_]>::is_empty")] - pub not_types: Vec, - - #[serde(skip_serializing_if = "<[_]>::is_empty")] - pub not_rooms: Vec, - - #[serde(skip_serializing_if = "Option::is_none")] - pub limit: Option, - - #[serde(skip_serializing_if = "<[_]>::is_empty")] - pub rooms: Vec, - - #[serde(skip_serializing_if = "<[_]>::is_empty")] - pub not_senders: Vec, - - #[serde(skip_serializing_if = "<[_]>::is_empty")] - pub senders: Vec, - - #[serde(skip_serializing_if = "<[_]>::is_empty")] - pub types: Vec, - - #[serde(skip_serializing_if = "Option::is_none")] - pub include_urls: Option, - - pub lazy_load_members: bool, - - pub unread_thread_notifications: bool, -} - -#[derive(Default, Debug, Serialize)] -pub struct ListBody { +pub struct ListRoomQuery { #[serde(skip_serializing_if = "Option::is_none")] pub from: Option, @@ -62,7 +31,7 @@ pub struct ListBody { } #[derive(Debug, Serialize)] -pub struct MessagesBody { +pub struct MessagesQuery { pub from: String, #[serde(skip_serializing_if = "String::is_empty")] @@ -78,7 +47,7 @@ pub struct MessagesBody { } #[derive(Default, Debug, Serialize)] -pub struct TimestampToEventBody { +pub struct TimestampToEventQuery { #[serde(skip_serializing_if = "Option::is_none")] pub ts: Option, @@ -86,7 +55,7 @@ pub struct TimestampToEventBody { } #[derive(Default, Debug, Serialize)] -pub struct EventContextBody { +pub struct EventContextQuery { #[serde(skip_serializing_if = "Option::is_none")] pub limit: Option, @@ -95,7 +64,7 @@ pub struct EventContextBody { } #[derive(Debug, Serialize)] -pub struct ReplaceRoomBody { +pub struct ReplaceRoomQuery { #[serde(rename = "new_room_user_id")] pub admin: OwnedUserId, @@ -107,9 +76,9 @@ pub struct ReplaceRoomBody { } #[derive(Default, Debug, Serialize)] -pub struct DeleteBody { +pub struct DeleteQuery { #[serde(flatten, skip_serializing_if = "Option::is_none")] - pub new_room: Option, + pub new_room: Option, pub block: bool, @@ -117,7 +86,7 @@ pub struct DeleteBody { } #[derive(Debug, Deserialize)] -pub struct ListResponse { +pub struct ListRoomResponse { pub rooms: Vec, pub offset: Option, pub total_rooms: Option, @@ -176,8 +145,8 @@ pub struct RoomDetails { } #[derive(Debug, Deserialize)] -pub struct GetEventsResponse { - pub chunk: T, +pub struct GetEventsResponse { + pub chunk: Raw>, pub start: String, pub end: String, pub state: Option>, @@ -219,7 +188,7 @@ pub struct EventContextResponse { } #[derive(Debug, Deserialize)] -pub struct DeleteResponse { +pub struct DeleteRoomResponse { pub kicked_users: Vec, pub failed_to_kick_users: Vec, pub local_aliases: Vec, @@ -253,10 +222,8 @@ impl RoomService { /// /// Refer: https://matrix-org.github.io/synapse/latest/admin_api/rooms.html#list-room-api #[instrument(skip(client))] - pub async fn get_all(client: &Client, params: ListBody) -> Result { - let resp = client - .get_query("/_synapse/admin/v1/rooms", ¶ms) - .await?; + pub async fn get_all(client: &Client, query: ListRoomQuery) -> Result { + let resp = client.get_query("/_synapse/admin/v1/rooms", &query).await?; if resp.status().is_success() { return Ok(resp.json().await?); @@ -318,7 +285,7 @@ impl RoomService { pub async fn get_timestamp_to_event( client: &Client, room_id: &RoomId, - params: TimestampToEventBody, + query: TimestampToEventQuery, ) -> Result { let resp = client .get_query( @@ -326,7 +293,7 @@ impl RoomService { "/_synapse/admin/v1/rooms/{room_id}/timestamp_to_event", room_id = room_id ), - ¶ms, + &query, ) .await?; @@ -397,12 +364,12 @@ impl RoomService { pub async fn delete_room( client: &Client, room_id: &RoomId, - params: DeleteBody, - ) -> Result { + query: DeleteQuery, + ) -> Result { let resp = client .delete_json( format!("/_synapse/admin/v1/rooms/{room_id}", room_id = room_id), - ¶ms, + &query, ) .await?; @@ -421,18 +388,18 @@ impl RoomService { /// /// Refer: https://matrix-org.github.io/synapse/latest/admin_api/rooms.html#room-state-api #[instrument(skip(client))] - pub async fn get_room_events( + pub async fn get_room_events( client: &Client, room_id: &RoomId, - params: MessagesBody, - ) -> Result>>> { + query: MessagesQuery, + ) -> Result { let resp = client .get_query( format!( "/_synapse/admin/v1/rooms/{room_id}/messages", room_id = room_id ), - ¶ms, + &query, ) .await?; @@ -454,7 +421,7 @@ impl RoomService { client: &Client, room_id: &RoomId, event_id: &EventId, - params: EventContextBody, + query: EventContextQuery, ) -> Result { let resp = client .get_query( @@ -463,7 +430,7 @@ impl RoomService { room_id = room_id, event_id = event_id, ), - ¶ms, + &query, ) .await?; diff --git a/crates/matrix/src/admin/resources/user.rs b/crates/matrix/src/admin/resources/user.rs index f3c9d98..03bd5a7 100644 --- a/crates/matrix/src/admin/resources/user.rs +++ b/crates/matrix/src/admin/resources/user.rs @@ -12,6 +12,9 @@ use crate::{error::MatrixError, http::Client}; use super::user_id::UserId; +#[derive(Default)] +pub struct UserService; + #[derive(Debug, Serialize, Deserialize)] pub struct ExternalId { pub auth_provider: String, @@ -50,7 +53,7 @@ pub struct User { } #[derive(Debug, Serialize, Deserialize)] -pub struct UserCreateDto { +pub struct CreateUserBody { pub password: String, pub logout_devices: bool, pub displayname: Option, @@ -64,7 +67,7 @@ pub struct UserCreateDto { } #[derive(Debug, Default, Serialize, Deserialize)] -pub struct ListUsersParams { +pub struct ListUsersQuery { pub user_id: Option, pub name: Option, pub guests: Option, @@ -99,7 +102,7 @@ pub struct ListUsersResponse { } #[derive(Debug, Serialize, Deserialize)] -pub struct UserUpdateDto { +pub struct UpdateUserBody { pub password: String, pub logout_devices: bool, pub displayname: Option, @@ -113,7 +116,7 @@ pub struct UserUpdateDto { } #[derive(Debug, Default, Serialize, Deserialize)] -pub struct LoginAsUserDto { +pub struct LoginAsUserBody { pub valid_until_ms: Option, } @@ -141,12 +144,12 @@ pub struct QueryUserDataResponse { pub user_type: Option, } -impl User { +impl UserService { /// This API returns information about a specific user account. /// /// Refer: https://matrix-org.github.io/synapse/v1.88/admin_api/user_admin_api.html#query-user-account #[instrument(skip(client))] - pub async fn query_user_account(client: &Client, user_id: UserId) -> Result { + pub async fn query_user_account(client: &Client, user_id: UserId) -> Result { let resp = client .get(format!( "/_synapse/admin/v2/users/{user_id}", @@ -170,12 +173,12 @@ impl User { /// if [`UserId`] matches. /// /// Refer: https://matrix-org.github.io/synapse/latest/admin_api/user_admin_api.html#create-or-modify-account - #[instrument(skip(client, dto))] - pub async fn create(client: &Client, user_id: UserId, dto: UserCreateDto) -> Result { + #[instrument(skip(client, body))] + pub async fn create(client: &Client, user_id: UserId, body: CreateUserBody) -> Result { let resp = client .put_json( format!("/_synapse/admin/v2/users/{user_id}", user_id = user_id), - &dto, + &body, ) .await?; @@ -193,10 +196,8 @@ impl User { /// /// Refer: https://matrix-org.github.io/synapse/latest/admin_api/user_admin_api.html#list-accounts #[instrument(skip(client))] - pub async fn list(client: &Client, params: ListUsersParams) -> Result { - let resp = client - .get_query("/_synapse/admin/v2/users", ¶ms) - .await?; + pub async fn list(client: &Client, query: ListUsersQuery) -> Result { + let resp = client.get_query("/_synapse/admin/v2/users", &query).await?; if resp.status().is_success() { return Ok(resp.json().await?); @@ -211,11 +212,11 @@ impl User { /// /// Refer: https://matrix-org.github.io/synapse/latest/admin_api/user_admin_api.html#create-or-modify-account #[instrument(skip(client))] - pub async fn update(client: &Client, user_id: UserId, dto: UserUpdateDto) -> Result { + pub async fn update(client: &Client, user_id: UserId, body: UpdateUserBody) -> Result { let resp = client .put_json( format!("/_synapse/admin/v2/users/{user_id}", user_id = user_id), - &dto, + &body, ) .await?; @@ -246,7 +247,7 @@ impl User { pub async fn login_as_user( client: &Client, user_id: UserId, - dto: LoginAsUserDto, + body: LoginAsUserBody, ) -> Result { let resp = client .post_json( @@ -254,7 +255,7 @@ impl User { "/_synapse/admin/v1/users/{user_id}/login", user_id = user_id ), - &dto, + &body, ) .await?; diff --git a/crates/matrix/src/client/resources/events.rs b/crates/matrix/src/client/resources/events.rs new file mode 100644 index 0000000..c377141 --- /dev/null +++ b/crates/matrix/src/client/resources/events.rs @@ -0,0 +1,275 @@ +use anyhow::Result; +use ruma_common::{serde::Raw, EventId, OwnedEventId, RoomId, TransactionId}; + +use ruma_events::{ + relation::RelationType, AnyMessageLikeEvent, AnyStateEvent, AnyStateEventContent, + AnyTimelineEvent, MessageLikeEventContent, MessageLikeEventType, StateEventContent, + StateEventType, +}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use tracing::instrument; + +use crate::{admin::resources::room::Direction, event_filter::RoomEventFilter, Client}; + +pub struct Events; + +#[derive(Debug, Default, Serialize)] +pub struct GetMessagesQuery { + pub from: Option, + pub to: Option, + pub limit: Option, + pub dir: Option, + pub filter: Option, +} + +#[derive(Debug, Default, Serialize)] +pub struct GetRelationsQuery { + pub from: Option, + pub to: Option, + pub limit: Option, + pub dir: Option, +} + +#[derive(Debug, Deserialize)] +pub struct GetMessagesResponse { + pub chunk: Vec>, + pub start: String, + pub end: String, + pub state: Option>>, +} + +#[derive(Debug, Deserialize)] +#[serde(transparent)] +pub struct GetStateResponse(Vec>); + +#[derive(Debug, Deserialize)] +pub struct GetRelationsResponse { + pub chunk: Vec>, + pub prev_batch: Option, + pub next_batch: Option, +} + +#[derive(Debug, Default, Serialize)] +pub struct SendRedactionBody { + #[serde(skip_serializing_if = "String::is_empty")] + pub reason: String, +} + +#[derive(Debug, Deserialize)] +pub struct SendMessageResponse { + pub event_id: OwnedEventId, +} + +#[derive(Debug, Deserialize)] +pub struct SendStateResponse { + pub event_id: OwnedEventId, +} + +#[derive(Debug, Deserialize)] +pub struct SendRedactionResponse { + pub event_id: OwnedEventId, +} + +impl Events { + #[instrument(skip(client, access_token))] + pub async fn get_event( + client: &Client, + access_token: impl Into, + room_id: &RoomId, + event_id: &EventId, + ) -> Result> { + let mut tmp = (*client).clone(); + tmp.set_token(access_token)?; + + let resp = tmp + .get(format!( + "/_matrix/client/v3/rooms/{room_id}/event/{event_id}", + room_id = room_id, + event_id = event_id, + )) + .await?; + + Ok(resp.json().await?) + } + + #[instrument(skip(client, access_token))] + pub async fn get_messages( + client: &Client, + access_token: impl Into, + room_id: &RoomId, + query: GetMessagesQuery, + ) -> Result { + let mut tmp = (*client).clone(); + tmp.set_token(access_token)?; + + let resp = tmp + .get(format!( + "/_matrix/client/v3/rooms/{room_id}/messages", + room_id = room_id, + )) + .await?; + + Ok(resp.json().await?) + } + + #[instrument(skip(client, access_token))] + pub async fn get_state( + client: &Client, + access_token: impl Into, + room_id: &RoomId, + ) -> Result { + let mut tmp = (*client).clone(); + tmp.set_token(access_token)?; + + let resp = tmp + .get(format!( + "/_matrix/client/v3/rooms/{room_id}/state", + room_id = room_id, + )) + .await?; + + Ok(resp.json().await?) + } + + #[instrument(skip(client, access_token))] + pub async fn get_state_content( + client: &Client, + access_token: impl Into, + room_id: &RoomId, + event_type: StateEventType, + state_key: Option, + ) -> Result> { + let mut tmp = (*client).clone(); + tmp.set_token(access_token)?; + + let mut path = format!( + "/_matrix/client/v3/rooms/{room_id}/state/{event_type}", + room_id = room_id, + event_type = event_type, + ); + + if let Some(state_key) = state_key { + path.push_str(&format!("/{state_key}", state_key = state_key)) + } + + let resp = tmp.get(path).await?; + + Ok(resp.json().await?) + } + + #[instrument(skip(client, access_token))] + pub async fn get_relations( + client: &Client, + access_token: impl Into, + room_id: &RoomId, + event_id: &EventId, + rel_type: Option>, + event_type: Option, + query: GetRelationsQuery, + ) -> Result { + let mut tmp = (*client).clone(); + tmp.set_token(access_token)?; + + let mut path = format!( + "/_matrix/client/v3/rooms/{room_id}/relations/{event_id}", + room_id = room_id, + event_id = event_id, + ); + + if let Some(rel_type) = rel_type { + path.push_str(&format!( + "/{rel_type}", + rel_type = rel_type + .as_ref() + .map_or("m.in_reply_to".into(), ToString::to_string) + )); + + if let Some(event_type) = event_type { + path.push_str(&format!("/{event_type}", event_type = event_type)) + } + } + + let resp = tmp.get_query(path, &query).await?; + + Ok(resp.json().await?) + } + + #[instrument(skip(client, access_token, body))] + pub async fn send_message( + client: &Client, + access_token: impl Into, + room_id: &RoomId, + txn_id: &TransactionId, + body: T, + ) -> Result { + let mut tmp = (*client).clone(); + tmp.set_token(access_token)?; + + let resp = tmp + .put_json( + format!( + "/_matrix/client/v3/rooms/{room_id}/send/{event_type}/{txn_id}", + room_id = room_id, + event_type = body.event_type(), + txn_id = txn_id, + ), + &body, + ) + .await?; + + Ok(resp.json().await?) + } + + #[instrument(skip(client, access_token, body))] + pub async fn send_state( + client: &Client, + access_token: impl Into, + room_id: &RoomId, + state_key: Option, + body: T, + ) -> Result { + let mut tmp = (*client).clone(); + tmp.set_token(access_token)?; + + let mut path = format!( + "/_matrix/client/v3/rooms/{room_id}/state/{event_type}", + room_id = room_id, + event_type = body.event_type(), + ); + + if let Some(state_key) = state_key { + path.push_str(&format!("/{state_key}", state_key = state_key)) + } + + let resp = tmp.put_json(path, &body).await?; + + Ok(resp.json().await?) + } + + #[instrument(skip(client, access_token, body))] + pub async fn send_redaction( + client: &Client, + access_token: impl Into, + room_id: &RoomId, + event_id: &EventId, + txn_id: &TransactionId, + body: SendRedactionBody, + ) -> Result { + let mut tmp = (*client).clone(); + tmp.set_token(access_token)?; + + let resp = tmp + .put_json( + format!( + "/_matrix/client/v3/rooms/{room_id}/redact/{event_id}/{txn_id}", + room_id = room_id, + event_id = event_id, + txn_id = txn_id, + ), + &body, + ) + .await?; + + Ok(resp.json().await?) + } +} diff --git a/crates/matrix/src/client/resources/mod.rs b/crates/matrix/src/client/resources/mod.rs index 89bb850..7bfe4b8 100644 --- a/crates/matrix/src/client/resources/mod.rs +++ b/crates/matrix/src/client/resources/mod.rs @@ -1,4 +1,5 @@ pub mod error; +pub mod events; pub mod login; pub mod room; pub mod session; diff --git a/crates/matrix/src/client/resources/room.rs b/crates/matrix/src/client/resources/room.rs index c12baf7..a14a4df 100644 --- a/crates/matrix/src/client/resources/room.rs +++ b/crates/matrix/src/client/resources/room.rs @@ -1,5 +1,5 @@ use anyhow::Result; -use ruma_common::{serde::Raw, OwnedUserId, RoomId, RoomOrAliasId}; +use ruma_common::{serde::Raw, OwnedRoomId, OwnedUserId, RoomId, RoomOrAliasId}; use ruma_events::{room::power_levels::RoomPowerLevelsEventContent, AnyInitialStateEvent}; use serde::{Deserialize, Serialize}; use tracing::instrument; @@ -86,12 +86,12 @@ pub struct RoomKickOrBanBody { #[derive(Debug, Deserialize)] pub struct CreateRoomResponse { - pub room_id: String, + pub room_id: OwnedRoomId, } #[derive(Debug, Deserialize)] pub struct JoinRoomResponse { - pub room_id: String, + pub room_id: OwnedRoomId, } #[derive(Debug, Deserialize)] diff --git a/crates/matrix/src/event_filter.rs b/crates/matrix/src/event_filter.rs new file mode 100644 index 0000000..35e30b8 --- /dev/null +++ b/crates/matrix/src/event_filter.rs @@ -0,0 +1,33 @@ +use ruma_common::{OwnedRoomId, OwnedUserId}; +use serde::Serialize; + +#[derive(Default, Debug, Serialize)] +pub struct RoomEventFilter { + #[serde(skip_serializing_if = "<[_]>::is_empty")] + pub not_types: Vec, + + #[serde(skip_serializing_if = "<[_]>::is_empty")] + pub not_rooms: Vec, + + #[serde(skip_serializing_if = "Option::is_none")] + pub limit: Option, + + #[serde(skip_serializing_if = "<[_]>::is_empty")] + pub rooms: Vec, + + #[serde(skip_serializing_if = "<[_]>::is_empty")] + pub not_senders: Vec, + + #[serde(skip_serializing_if = "<[_]>::is_empty")] + pub senders: Vec, + + #[serde(skip_serializing_if = "<[_]>::is_empty")] + pub types: Vec, + + #[serde(skip_serializing_if = "Option::is_none")] + pub include_urls: Option, + + pub lazy_load_members: bool, + + pub unread_thread_notifications: bool, +} diff --git a/crates/matrix/src/lib.rs b/crates/matrix/src/lib.rs index c4a73f9..e5458a1 100644 --- a/crates/matrix/src/lib.rs +++ b/crates/matrix/src/lib.rs @@ -6,6 +6,8 @@ mod http; mod error; +mod event_filter; + pub use http::Client; /// Implementation on the Administrator API of Matrix diff --git a/crates/test/src/matrix/mod.rs b/crates/test/src/matrix/mod.rs index 92168cd..c4a8b79 100644 --- a/crates/test/src/matrix/mod.rs +++ b/crates/test/src/matrix/mod.rs @@ -1,3 +1,4 @@ mod room_admin; mod room_client; mod shared_token_registration; +mod util; diff --git a/crates/test/src/matrix/room_admin.rs b/crates/test/src/matrix/room_admin.rs index dd103a1..cd7bd73 100644 --- a/crates/test/src/matrix/room_admin.rs +++ b/crates/test/src/matrix/room_admin.rs @@ -1,186 +1,45 @@ -use std::str::FromStr; - -use commune::{account::service::CreateUnverifiedAccountDto, room::model::Room}; -use matrix::{ - admin::resources::room::{DeleteBody, ListBody, ListResponse, RoomService as AdminRoomService}, - ruma_common::{OwnedRoomId, OwnedUserId}, - Client, +use matrix::admin::resources::room::{ + ListRoomQuery, ListRoomResponse, RoomService as AdminRoomService, }; -use commune::{room::service::CreateRoomDto, util::secret::Secret}; - -use crate::tools::environment::Environment; - -struct Sample { - id: String, - user_id: OwnedUserId, - room_id: OwnedRoomId, -} - -async fn create_accounts( - env: &Environment, - amount: usize, - seed: u64, -) -> Vec<(String, (OwnedUserId, String))> { - let mut result = Vec::with_capacity(amount); - - for i in 0..amount { - let id = format!("{seed}-{i}"); - - let account_dto = CreateUnverifiedAccountDto { - username: format!("{id}-username"), - password: Secret::new("verysecure".to_owned()), - email: format!("{id}-email@matrix.org"), - }; - - let account = env - .commune - .account - .register_unverified(account_dto) - .await - .unwrap(); - let access_token = env - .commune - .account - .issue_user_token(account.user_id.clone()) - .await - .unwrap(); - - let user_id = OwnedUserId::from_str(&account.user_id.to_string()).unwrap(); - result.push((id, (user_id, access_token))); - } - - result -} - -async fn create_rooms(env: &Environment, accounts: &[(String, String)]) -> Vec { - let mut result = Vec::with_capacity(accounts.len()); - - for (id, access_token) in accounts { - let room_dto = CreateRoomDto { - name: format!("{id}-room-name"), - topic: format!("{id}-room-topic"), - alias: format!("{id}-room-alias"), - }; - let Room { room_id } = env - .commune - .room - .create_public_room(&Secret::new(access_token.clone()), room_dto) - .await - .unwrap(); - - result.push(OwnedRoomId::from_str(&room_id).unwrap()); - } - - result -} - -async fn remove_rooms(client: &Client) { - let ListResponse { rooms, .. } = AdminRoomService::get_all(client, ListBody::default()) - .await - .unwrap(); - let room_names: Vec<_> = rooms - .iter() - .map(|r| r.name.clone().unwrap_or(r.room_id.to_string())) - .collect(); - - tracing::info!(?room_names, "purging all rooms!"); - - for room in rooms { - AdminRoomService::delete_room( - client, - room.room_id.as_ref(), - DeleteBody { - new_room: None, - block: true, - purge: true, - }, - ) - .await - .unwrap(); - } -} - #[cfg(test)] mod tests { - use std::sync::Once; - use matrix::{ - admin::resources::room::{MessagesBody, OrderBy}, + admin::resources::room::{MessagesQuery, OrderBy}, ruma_common::{RoomId, ServerName}, - ruma_events::AnyStateEvent, }; - use rand::Rng; use tokio::sync::OnceCell; - use super::*; + use crate::matrix::util::{self, Test}; - pub struct Test { - env: Environment, - samples: Vec, - // seed: u64, - } + use super::*; - static TRACING: Once = Once::new(); static TEST: OnceCell = OnceCell::const_new(); - async fn init() -> Test { - TRACING.call_once(|| { - let _ = tracing_subscriber::fmt::try_init(); - }); - - let mut env = Environment::new().await; - let seed = rand::thread_rng().gen(); - - env.client - .set_token(env.config.synapse_admin_token.clone()) - .unwrap(); - remove_rooms(&env.client).await; - - let accounts = create_accounts(&env, 4, seed).await; - let rooms = create_rooms( - &env, - accounts - .iter() - .map(|(id, (_, access_token))| (id.clone(), access_token.clone())) - .collect::>() - .as_slice(), - ) - .await; - let samples = accounts - .into_iter() - .zip(rooms.into_iter()) - .map(|((id, (user_id, _)), room_id)| Sample { - id, - user_id, - room_id, - }) - .collect(); - - Test { env, samples } - } - #[tokio::test] async fn get_all_rooms() { - let Test { env, samples, .. } = TEST.get_or_init(init).await; - let host = env.config.synapse_server_name.as_str(); + let Test { + samples, + server_name, + client, + } = TEST.get_or_init(util::init).await; - let ListResponse { rooms: resp, .. } = - AdminRoomService::get_all(&env.client, ListBody::default()) + let ListRoomResponse { rooms: resp, .. } = + AdminRoomService::get_all(client, ListRoomQuery::default()) .await .unwrap(); assert_eq!( samples .iter() - .map(|s| Some(format!("{id}-room-name", id = &s.id))) + .map(|s| Some(format!("{id}-room-name", id = s.user_id.localpart()))) .collect::>(), resp.iter().map(|r| r.name.clone()).collect::>(), ); assert_eq!( samples .iter() - .map(|s| format!("#{id}-room-alias:{host}", id = &s.id)) + .map(|s| format!("#{id}-room-alias:{server_name}", id = s.user_id.localpart())) .collect::>(), resp.iter() .map(|r| r.canonical_alias.clone().unwrap()) @@ -191,9 +50,9 @@ mod tests { resp.iter().map(|r| &r.room_id).collect::>(), ); - let ListResponse { rooms: resp, .. } = AdminRoomService::get_all( - &env.client, - ListBody { + let ListRoomResponse { rooms: resp, .. } = AdminRoomService::get_all( + client, + ListRoomQuery { order_by: OrderBy::Creator, ..Default::default() }, @@ -215,11 +74,11 @@ mod tests { #[tokio::test] #[should_panic] async fn get_all_rooms_err() { - let Test { env, .. } = TEST.get_or_init(init).await; + let Test { client, .. } = TEST.get_or_init(util::init).await; let _ = AdminRoomService::get_all( - &env.client, - ListBody { + client, + ListRoomQuery { from: Some(u64::MAX), ..Default::default() }, @@ -230,26 +89,37 @@ mod tests { #[tokio::test] async fn get_room_details() { - let Test { env, samples, .. } = TEST.get_or_init(init).await; - let host = env.config.synapse_server_name.as_str(); + let Test { + samples, + server_name, + client, + } = TEST.get_or_init(util::init).await; let magic_number = Box::into_raw(Box::new(12345)) as usize % samples.len(); let rand = samples.get(magic_number).unwrap(); - let resp = AdminRoomService::get_one(&env.client, &rand.room_id) + let resp = AdminRoomService::get_one(client, &rand.room_id) .await .unwrap(); - assert_eq!(Some(format!("{}-room-name", rand.id)), resp.name); assert_eq!( - Some(format!("#{}-room-alias:{host}", rand.id)), + Some(format!("{}-room-name", rand.user_id.localpart())), + resp.name + ); + assert_eq!( + Some(format!( + "#{}-room-alias:{server_name}", + rand.user_id.localpart() + )), resp.canonical_alias, ); + assert_eq!(Some(rand.user_id.to_string()), resp.creator); assert_eq!( - Some(format!("{}-room-topic", rand.id)), + Some(format!("{}-room-topic", rand.user_id.localpart())), resp.details.and_then(|d| d.topic), ); + assert_eq!(resp.join_rules, Some("public".into())); assert!(!resp.public); @@ -259,27 +129,34 @@ mod tests { #[tokio::test] #[should_panic] async fn get_room_details_err() { - let Test { env, .. } = TEST.get_or_init(init).await; - let host = env.config.synapse_server_name.as_str(); + let Test { + server_name, + client, + .. + } = TEST.get_or_init(util::init).await; - let _ = - AdminRoomService::get_one(&env.client, &RoomId::new(&ServerName::parse(host).unwrap())) - .await - .unwrap(); + let _ = AdminRoomService::get_one( + client, + &RoomId::new(&ServerName::parse(server_name).unwrap()), + ) + .await + .unwrap(); } #[tokio::test] async fn get_room_events() { - let Test { env, samples, .. } = TEST.get_or_init(init).await; + let Test { + samples, client, .. + } = TEST.get_or_init(util::init).await; let magic_number = Box::into_raw(Box::new(12345)) as usize % samples.len(); let rand = samples.get(magic_number).unwrap(); - let resp = AdminRoomService::get_room_events::( - &env.client, + let resp = AdminRoomService::get_room_events( + client, &rand.room_id, // no idea what the type is - MessagesBody { + MessagesQuery { from: "".into(), to: Default::default(), limit: Default::default(), @@ -297,13 +174,16 @@ mod tests { #[tokio::test] #[should_panic] async fn get_room_events_err() { - let Test { env, .. } = TEST.get_or_init(init).await; - let host = env.config.synapse_server_name.as_str(); + let Test { + server_name, + client, + .. + } = TEST.get_or_init(util::init).await; - let _ = AdminRoomService::get_room_events::( - &env.client, - <&RoomId>::try_from(host).unwrap(), - MessagesBody { + let _ = AdminRoomService::get_room_events( + client, + <&RoomId>::try_from(server_name.as_str()).unwrap(), + MessagesQuery { from: "".into(), to: Default::default(), limit: Default::default(), @@ -317,12 +197,14 @@ mod tests { #[tokio::test] async fn get_state_events() { - let Test { env, samples, .. } = TEST.get_or_init(init).await; + let Test { + samples, client, .. + } = TEST.get_or_init(util::init).await; let magic_number = Box::into_raw(Box::new(12345)) as usize % samples.len(); let rand = samples.get(magic_number).unwrap(); - let resp = AdminRoomService::get_state(&env.client, &rand.room_id) + let resp = AdminRoomService::get_state(client, &rand.room_id) .await .unwrap(); @@ -335,22 +217,28 @@ mod tests { #[tokio::test] #[should_panic] async fn get_state_events_err() { - let Test { env, .. } = TEST.get_or_init(init).await; - let host = env.config.synapse_server_name.as_str(); + let Test { + server_name, + client, + .. + } = TEST.get_or_init(util::init).await; - let _ = AdminRoomService::get_state(&env.client, <&RoomId>::try_from(host).unwrap()) - .await - .unwrap(); + let _ = + AdminRoomService::get_state(client, <&RoomId>::try_from(server_name.as_str()).unwrap()) + .await + .unwrap(); } #[tokio::test] async fn get_members() { - let Test { env, samples, .. } = TEST.get_or_init(init).await; + let Test { + samples, client, .. + } = TEST.get_or_init(util::init).await; let magic_number = Box::into_raw(Box::new(12345)) as usize % samples.len(); let rand = samples.get(magic_number).unwrap(); - let resp = AdminRoomService::get_members(&env.client, &rand.room_id) + let resp = AdminRoomService::get_members(client, &rand.room_id) .await .unwrap(); @@ -360,11 +248,17 @@ mod tests { #[tokio::test] #[should_panic] async fn get_members_err() { - let Test { env, .. } = TEST.get_or_init(init).await; - let host = env.config.synapse_server_name.as_str(); + let Test { + server_name, + client, + .. + } = TEST.get_or_init(util::init).await; - let _ = AdminRoomService::get_members(&env.client, <&RoomId>::try_from(host).unwrap()) - .await - .unwrap(); + let _ = AdminRoomService::get_members( + client, + <&RoomId>::try_from(server_name.as_str()).unwrap(), + ) + .await + .unwrap(); } } diff --git a/crates/test/src/matrix/room_client.rs b/crates/test/src/matrix/room_client.rs index 0cd6bec..7a636fd 100644 --- a/crates/test/src/matrix/room_client.rs +++ b/crates/test/src/matrix/room_client.rs @@ -1,111 +1,7 @@ -use std::str::FromStr; - -use commune::{account::service::CreateUnverifiedAccountDto, room::model::Room}; -use matrix::{ - admin::resources::room::{DeleteBody, ListBody, ListResponse, RoomService as AdminRoomService}, - ruma_common::{OwnedRoomId, OwnedUserId}, - Client, -}; - -use commune::{room::service::CreateRoomDto, util::secret::Secret}; - -use crate::tools::environment::Environment; - -struct Sample { - id: String, - user_id: OwnedUserId, - room_id: OwnedRoomId, - access_token: String, -} - -async fn create_accounts( - env: &Environment, - amount: usize, - seed: u64, -) -> Vec<(String, (OwnedUserId, String))> { - let mut result = Vec::with_capacity(amount); - - for i in 0..amount { - let id = format!("{seed}-{i}"); - - let account_dto = CreateUnverifiedAccountDto { - username: format!("{id}-username"), - password: Secret::new("verysecure".to_owned()), - email: format!("{id}-email@matrix.org"), - }; - - let account = env - .commune - .account - .register_unverified(account_dto) - .await - .unwrap(); - let access_token = env - .commune - .account - .issue_user_token(account.user_id.clone()) - .await - .unwrap(); - - let user_id = OwnedUserId::from_str(&account.user_id.to_string()).unwrap(); - result.push((id, (user_id, access_token))); - } - - result -} - -async fn create_rooms(env: &Environment, accounts: &[(String, String)]) -> Vec { - let mut result = Vec::with_capacity(accounts.len()); - - for (id, access_token) in accounts { - let room_dto = CreateRoomDto { - name: format!("{id}-room-name"), - topic: format!("{id}-room-topic"), - alias: format!("{id}-room-alias"), - }; - - let Room { room_id } = env - .commune - .room - .create_public_room(&Secret::new(access_token.clone()), room_dto) - .await - .unwrap(); - - result.push(OwnedRoomId::from_str(&room_id).unwrap()); - } - - result -} - -async fn remove_rooms(client: &Client) { - let ListResponse { rooms, .. } = AdminRoomService::get_all(client, ListBody::default()) - .await - .unwrap(); - let room_names: Vec<_> = rooms - .iter() - .map(|r| r.name.clone().unwrap_or(r.room_id.to_string())) - .collect(); - - tracing::info!(?room_names, "purging all rooms!"); - - for room in rooms { - AdminRoomService::delete_room( - client, - room.room_id.as_ref(), - DeleteBody { - new_room: None, - block: true, - purge: true, - }, - ) - .await - .unwrap(); - } -} +use matrix::ruma_common::{OwnedRoomId, OwnedUserId}; #[cfg(test)] mod tests { - use std::sync::Once; use matrix::{ admin::resources::room::RoomService as AdminRoomService, @@ -115,69 +11,31 @@ mod tests { }, ruma_common::OwnedRoomOrAliasId, }; - use rand::Rng; use tokio::sync::OnceCell; - use super::*; + use crate::matrix::util::{self, Test}; - pub struct Test { - env: Environment, - samples: Vec, - } - - static TRACING: Once = Once::new(); + use super::*; static TEST: OnceCell = OnceCell::const_new(); - async fn init() -> Test { - TRACING.call_once(|| { - let _ = tracing_subscriber::fmt::try_init(); - }); - - let mut env = Environment::new().await; - let seed = rand::thread_rng().gen(); - - env.client - .set_token(env.config.synapse_admin_token.clone()) - .unwrap(); - remove_rooms(&env.client).await; - - let accounts = create_accounts(&env, 4, seed).await; - let rooms = create_rooms( - &env, - accounts - .iter() - .map(|(id, (_, access_token))| (id.clone(), access_token.clone())) - .collect::>() - .as_slice(), - ) - .await; - let samples = accounts - .into_iter() - .zip(rooms.into_iter()) - .map(|((id, (user_id, access_token)), room_id)| Sample { - id, - user_id, - room_id, - access_token, - }) - .collect(); - - Test { env, samples } - } - async fn join_helper() -> Vec<( OwnedRoomId, Vec, Vec>, )> { - let Test { env, samples, .. } = TEST.get_or_init(init).await; + let Test { + samples, client, .. + } = TEST.get_or_init(util::init).await; let mut result = Vec::with_capacity(samples.len()); for sample in samples { - let client = env.client.clone(); + let client = client.clone(); - let guests: Vec<_> = samples.iter().filter(|g| g.id != sample.id).collect(); + let guests: Vec<_> = samples + .iter() + .filter(|g| g.user_id != sample.user_id) + .collect(); let mut resps = Vec::with_capacity(guests.len()); for guest in guests.iter() { @@ -206,10 +64,7 @@ mod tests { #[tokio::test] async fn join_all_rooms() { - let Test { env, .. } = TEST.get_or_init(init).await; - - let mut admin = env.client.clone(); - admin.set_token(&env.config.synapse_admin_token).unwrap(); + let Test { client: admin, .. } = TEST.get_or_init(util::init).await; // first join let result = join_helper().await; @@ -236,17 +91,21 @@ mod tests { #[tokio::test] async fn leave_all_rooms() { - let Test { env, samples, .. } = TEST.get_or_init(init).await; + let Test { + samples, client, .. + } = TEST.get_or_init(util::init).await; - let mut admin = env.client.clone(); - admin.set_token(&env.config.synapse_admin_token).unwrap(); + let admin = client.clone(); let mut result = Vec::with_capacity(samples.len()); for sample in samples { - let client = env.client.clone(); + let client = client.clone(); - let guests: Vec<_> = samples.iter().filter(|g| g.id != sample.id).collect(); + let guests: Vec<_> = samples + .iter() + .filter(|g| g.user_id != sample.user_id) + .collect(); for guest in guests { let client = client.clone(); @@ -284,15 +143,17 @@ mod tests { #[tokio::test] async fn forget_all_rooms() { - let Test { env, samples, .. } = TEST.get_or_init(init).await; - - let mut admin = env.client.clone(); - admin.set_token(&env.config.synapse_admin_token).unwrap(); + let Test { + samples, client, .. + } = TEST.get_or_init(util::init).await; for sample in samples { - let client = env.client.clone(); + let client = client.clone(); - let guests: Vec<_> = samples.iter().filter(|g| g.id != sample.id).collect(); + let guests: Vec<_> = samples + .iter() + .filter(|g| g.user_id != sample.user_id) + .collect(); for guest in guests { let client = client.clone(); @@ -309,6 +170,7 @@ mod tests { } // check whether all guests are still not present anymore the room + let admin = client.clone(); for sample in samples { let room_id = &sample.room_id; @@ -329,7 +191,7 @@ mod tests { // confirm a room can't be forgotten if we didn't leave first for sample in samples { - let client = env.client.clone(); + let client = client.clone(); let room_id = &sample.room_id; let resp = RoomService::forget( @@ -346,10 +208,9 @@ mod tests { #[tokio::test] async fn kick_all_guests() { - let Test { env, samples, .. } = TEST.get_or_init(init).await; - - let mut admin = env.client.clone(); - admin.set_token(&env.config.synapse_admin_token).unwrap(); + let Test { + samples, client, .. + } = TEST.get_or_init(util::init).await; // second join let result = join_helper().await; @@ -357,6 +218,7 @@ mod tests { tracing::info!(?rooms, "joining all guests"); // check whether all guests are in the room and joined the expected room + let admin = client.clone(); for (room_id, guests, resps) in result.iter() { let mut resp = AdminRoomService::get_members(&admin, room_id) .await @@ -374,10 +236,13 @@ mod tests { } for sample in samples { - let client = env.client.clone(); + let client = client.clone(); let room_id = &sample.room_id; - let guests: Vec<_> = samples.iter().filter(|g| g.id != sample.id).collect(); + let guests: Vec<_> = samples + .iter() + .filter(|g| g.user_id != sample.user_id) + .collect(); for guest in guests { let client = client.clone(); @@ -416,10 +281,9 @@ mod tests { #[tokio::test] async fn ban_all_guests() { - let Test { env, samples, .. } = TEST.get_or_init(init).await; - - let mut admin = env.client.clone(); - admin.set_token(&env.config.synapse_admin_token).unwrap(); + let Test { + samples, client, .. + } = TEST.get_or_init(util::init).await; // third join let result = join_helper().await; @@ -427,6 +291,7 @@ mod tests { tracing::info!(?rooms, "joining all guests"); // check whether all guests are in the room and joined the expected room + let admin = client.clone(); for (room_id, guests, resps) in result.iter() { let mut resp = AdminRoomService::get_members(&admin, room_id) .await @@ -444,9 +309,12 @@ mod tests { } for sample in samples { - let client = env.client.clone(); + let client = client.clone(); - let guests: Vec<_> = samples.iter().filter(|g| g.id != sample.id).collect(); + let guests: Vec<_> = samples + .iter() + .filter(|g| g.user_id != sample.user_id) + .collect(); for guest in guests { let client = client.clone(); @@ -492,9 +360,12 @@ mod tests { } for sample in samples { - let client = env.client.clone(); + let client = client.clone(); - let guests: Vec<_> = samples.iter().filter(|g| g.id != sample.id).collect(); + let guests: Vec<_> = samples + .iter() + .filter(|g| g.user_id != sample.user_id) + .collect(); for guest in guests { let client = client.clone(); diff --git a/crates/test/src/matrix/util.rs b/crates/test/src/matrix/util.rs new file mode 100644 index 0000000..b7609dd --- /dev/null +++ b/crates/test/src/matrix/util.rs @@ -0,0 +1,166 @@ +use std::str::FromStr; + +use matrix::{ + admin::resources::{ + room::{DeleteQuery, ListRoomQuery, ListRoomResponse, RoomService as AdminRoomService}, + user::{ + CreateUserBody, LoginAsUserBody, LoginAsUserResponse, UserService as AdminUserService, + }, + user_id::UserId, + }, + client::resources::room::{CreateRoomBody, Room, RoomPreset}, + ruma_common::{OwnedRoomId, OwnedUserId}, + Client, +}; + +use rand::Rng; + +use crate::tools::environment::Environment; + +pub struct Test { + pub samples: Vec, + pub server_name: String, + pub client: Client, +} + +pub struct Sample { + pub user_id: OwnedUserId, + pub room_id: OwnedRoomId, + pub access_token: String, +} + +async fn create_accounts( + client: &Client, + server_name: String, + amount: usize, + seed: u64, +) -> Vec<(OwnedUserId, String)> { + let mut result = Vec::with_capacity(amount); + + for i in 0..amount { + let user_id = UserId::new(format!("{seed}-{i}"), server_name.clone()); + let password = "verysecure".to_owned(); + + let body = CreateUserBody { + password, + logout_devices: false, + displayname: None, + avatar_url: None, + threepids: vec![], + external_ids: vec![], + admin: false, + deactivated: false, + user_type: None, + locked: false, + }; + AdminUserService::create(&client, user_id.clone(), body) + .await + .unwrap(); + + let body = LoginAsUserBody::default(); + let LoginAsUserResponse { access_token } = + AdminUserService::login_as_user(&client, user_id.clone(), body) + .await + .unwrap(); + + let user_id = OwnedUserId::from_str(&user_id.to_string()).unwrap(); + result.push((user_id, access_token)); + } + + result +} + +async fn create_rooms(client: &Client, accounts: &[(OwnedUserId, String)]) -> Vec { + let mut result = Vec::with_capacity(accounts.len()); + + for (user_id, access_token) in accounts { + let id = user_id.localpart(); + let preset = Some(RoomPreset::PublicChat); + + let name = format!("{id}-room-name"); + let topic = format!("{id}-room-topic"); + let room_alias_name = format!("{id}-room-alias"); + + let body = CreateRoomBody { + name, + topic, + room_alias_name, + preset, + ..Default::default() + }; + let resp = Room::create(client, access_token, body).await.unwrap(); + + result.push(resp.room_id); + } + + result +} + +async fn remove_rooms(client: &Client) { + let ListRoomResponse { rooms, .. } = + AdminRoomService::get_all(client, ListRoomQuery::default()) + .await + .unwrap(); + let room_names: Vec<_> = rooms + .iter() + .map(|r| r.name.clone().unwrap_or(r.room_id.to_string())) + .collect(); + + tracing::info!(?room_names, "purging all rooms!"); + + for room in rooms { + AdminRoomService::delete_room( + client, + room.room_id.as_ref(), + DeleteQuery { + new_room: None, + block: true, + purge: true, + }, + ) + .await + .unwrap(); + } +} + +pub async fn init() -> Test { + let _ = tracing_subscriber::fmt::try_init(); + + let seed = rand::thread_rng().gen(); + + let env = Environment::new().await; + + let server_name = env.config.synapse_server_name.clone(); + let admin_token = env.config.synapse_admin_token.clone(); + let mut client = env.client.clone(); + + client.set_token(admin_token).unwrap(); + remove_rooms(&client).await; + + let accounts = create_accounts(&client, server_name.clone(), 4, seed).await; + let rooms = create_rooms( + &client, + accounts + .iter() + .map(|(user_id, access_token)| (user_id.clone(), access_token.clone())) + .collect::>() + .as_slice(), + ) + .await; + + let samples = accounts + .into_iter() + .zip(rooms.into_iter()) + .map(|((user_id, access_token), room_id)| Sample { + user_id, + room_id, + access_token, + }) + .collect(); + + Test { + samples, + server_name, + client, + } +}