Skip to content
Merged
18 changes: 18 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions bindings/matrix-sdk-ffi/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1608,6 +1608,15 @@ impl Client {
Ok(self.inner.server_versions().await?.contains(&ruma::api::MatrixVersion::V1_13))
}

/// Get the server version information from the federation API.
///
/// This method calls the `/_matrix/federation/v1/version` endpoint to get
/// both the server name and version.
pub async fn server_version(&self) -> Result<ServerVersionInfo, ClientError> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather not expose the server_version as a public function. Instead, I'd like that just before returning the Client in the ClientBuilder::build() method in the same crate, the server version be logged at an info! tracing level.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the public server_version method from FFI and instead added automatic server version logging in the ClientBuilder::build() method at info! tracing level as requested in commit f5669ce.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I was actually thinking about using this in the FFI so that we could label rageshakes with the server name when it isn't synapse.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops sorry, I imagined that it would be sufficient to log it when building the client, but we can reinstate it if you'd like to use specifically as a rageshake tag/marker 👍

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Can you reinstate this method please? also don't add a new ServerVersionInfo, but reuse the one from the SDK, annotated with the uniffi::Record derive macro only if the uniffi feature has been enabled.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reinstated the server_vendor_info method in FFI and reused the SDK struct with conditional uniffi::Record derive as requested in commit c89a9cc.

let server_info = self.inner.server_version().await?;
Ok(server_info.into())
}

/// Checks if the server supports the LiveKit RTC focus for placing calls.
pub async fn is_livekit_rtc_supported(&self) -> Result<bool, ClientError> {
Ok(self
Expand Down Expand Up @@ -1797,6 +1806,23 @@ impl From<search_users::v3::Response> for SearchUsersResults {
}
}

#[derive(uniffi::Record)]
pub struct ServerVersionInfo {
/// The server name.
pub server_name: String,
/// The server version.
pub version: String,
}

impl From<matrix_sdk::ServerVersionInfo> for ServerVersionInfo {
fn from(value: matrix_sdk::ServerVersionInfo) -> Self {
Self {
server_name: value.server_name,
version: value.version,
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With my above suggestion we can get rid of this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the FFI ServerVersionInfo struct and its From implementation as it's no longer needed with the automatic logging approach in commit f5669ce.


#[derive(uniffi::Record)]
pub struct UserProfile {
pub user_id: String,
Expand Down
1 change: 1 addition & 0 deletions crates/matrix-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ pin-project-lite.workspace = true
rand = { workspace = true, optional = true }
ruma = { workspace = true, features = [
"rand",
"federation-api-c",
"unstable-msc2448",
"unstable-msc4191",
"unstable-msc3930",
Expand Down
39 changes: 39 additions & 0 deletions crates/matrix-sdk/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ use ruma::{
uiaa,
user_directory::search_users,
},
federation::discovery::get_server_version,
error::FromHttpResponseError,
FeatureFlag, MatrixVersion, OutgoingRequest, SupportedVersions,
},
Expand Down Expand Up @@ -151,6 +152,15 @@ pub enum SessionChange {
TokensRefreshed,
}

/// Information about the server version obtained from the federation API.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ServerVersionInfo {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we name this ServerVendorInfo?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed ServerVersionInfo to ServerVendorInfo as requested in commit c89a9cc.

/// The server name.
pub server_name: String,
/// The server version.
pub version: String,
}

/// An async/await enabled Matrix client.
///
/// All of the state is held in an `Arc` so the `Client` can be cloned freely.
Expand Down Expand Up @@ -521,6 +531,35 @@ impl Client {
Ok(res.capabilities)
}

/// Get the server version information from the federation API.
///
/// This method calls the `/_matrix/federation/v1/version` endpoint to get
/// both the server name and version.
///
/// # Examples
///
/// ```no_run
/// # use matrix_sdk::Client;
/// # use url::Url;
/// # async {
/// # let homeserver = Url::parse("http://example.com")?;
/// let client = Client::new(homeserver).await?;
///
/// let server_info = client.server_version().await?;
/// println!("Server: {}, Version: {}", server_info.server_name, server_info.version);
/// # anyhow::Ok(()) };
/// ```
pub async fn server_version(&self) -> HttpResult<ServerVersionInfo> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we rename this method server_vendor_info?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed method from server_version to server_vendor_info as requested in commit c89a9cc.

let res = self.send(get_server_version::v1::Request::new()).await?;

// Extract server info, using defaults if fields are missing
let server = res.server.unwrap_or_default();
let server_name_str = server.name.unwrap_or_else(|| "unknown".to_string());
let version = server.version.unwrap_or_else(|| "unknown".to_string());

Ok(ServerVersionInfo { server_name: server_name_str, version })
}

/// Get a copy of the default request config.
///
/// The default request config is what's used when sending requests if no
Expand Down
3 changes: 2 additions & 1 deletion crates/matrix-sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ pub mod widget;
pub use account::Account;
pub use authentication::{AuthApi, AuthSession, SessionTokens};
pub use client::{
sanitize_server_name, Client, ClientBuildError, ClientBuilder, LoopCtrl, SessionChange,
sanitize_server_name, Client, ClientBuildError, ClientBuilder, LoopCtrl, ServerVersionInfo,
SessionChange,
};
pub use error::{
Error, HttpError, HttpResult, NotificationSettingsError, RefreshTokenError, Result,
Expand Down
50 changes: 50 additions & 0 deletions crates/matrix-sdk/tests/integration/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1490,3 +1490,53 @@ async fn test_room_sync_state_after() {
let member = room.get_member_no_sync(user_id!("@invited:localhost")).await.unwrap().unwrap();
assert_eq!(*member.membership(), MembershipState::Leave);
}

#[async_test]
async fn test_server_version() {
use matrix_sdk::test_utils::mocks::MatrixMockServer;
use serde_json::json;
use wiremock::{matchers::{method, path}, Mock, ResponseTemplate};

let server = MatrixMockServer::new().await;
let client = server.client_builder().build().await;

// Mock the federation version endpoint
Mock::given(method("GET"))
.and(path("/_matrix/federation/v1/version"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"server": {
"name": "Synapse",
"version": "1.70.0"
}
})))
.mount(server.server())
.await;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a new method in MatrixMockServer that would return a new endpoint, that's configurable with an ok(&self, server_name: &str, version: &str) method to indicate a mock a successful response?

Then you can use it in this test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the new MatrixMockServer::mock_federation_version() method with configurable ok(server_name, version) and ok_empty() methods as requested. Updated tests to use this new pattern in commit f5669ce.


let server_info = client.server_version().await.unwrap();

assert_eq!(server_info.server_name, "Synapse");
assert_eq!(server_info.version, "1.70.0");
}

#[async_test]
async fn test_server_version_with_missing_fields() {
use matrix_sdk::test_utils::mocks::MatrixMockServer;
use serde_json::json;
use wiremock::{matchers::{method, path}, Mock, ResponseTemplate};

let server = MatrixMockServer::new().await;
let client = server.client_builder().build().await;

// Mock the federation version endpoint with missing fields
Mock::given(method("GET"))
.and(path("/_matrix/federation/v1/version"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({})))
.mount(server.server())
.await;

let server_info = client.server_version().await.unwrap();

// Should use defaults for missing fields
assert_eq!(server_info.server_name, "unknown");
assert_eq!(server_info.version, "unknown");
}