mirror of
https://github.com/element-hq/synapse.git
synced 2026-08-29 01:18:30 +00:00
Iterate on structure
This commit is contained in:
Generated
+805
-55
File diff suppressed because it is too large
Load Diff
@@ -64,6 +64,12 @@ tokio = { version = "1.44.2", features = ["rt", "rt-multi-thread"] }
|
||||
once_cell = "1.18.0"
|
||||
itertools = "0.14.0"
|
||||
|
||||
# TODO: Remove: These are just used to make sure a tokio-postgres backed database pool makes sense
|
||||
# with our interfaces
|
||||
bb8 = "0.8.3"
|
||||
bb8-postgres = "0.8.1"
|
||||
postgres-native-tls = "0.5.0"
|
||||
|
||||
[build-dependencies]
|
||||
blake2 = "0.10.4"
|
||||
hex = "0.4.3"
|
||||
|
||||
@@ -43,4 +43,5 @@ pub enum RoomCreationPreset {
|
||||
pub struct ExperimentalConfig {
|
||||
pub msc3881_enabled: bool,
|
||||
pub msc3575_enabled: bool,
|
||||
pub msc4222_enabled: bool,
|
||||
}
|
||||
|
||||
@@ -14,17 +14,35 @@
|
||||
*/
|
||||
|
||||
use pyo3::{
|
||||
prelude::*,
|
||||
types::{PyAnyMethods, PyModule, PyModuleMethods},
|
||||
Bound, PyResult, Python,
|
||||
};
|
||||
|
||||
use crate::storage::store::Store;
|
||||
|
||||
pub mod versions;
|
||||
|
||||
#[pyclass]
|
||||
struct RustHandlers {
|
||||
versions: versions::VersionsHandler,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl RustHandlers {
|
||||
#[new]
|
||||
#[pyo3(signature = (homeserver))]
|
||||
pub fn py_new(py: Python<'_>, homeserver: &Bound<'_, PyAny>) -> PyResult<RustHandlers> {
|
||||
RustHandlers {
|
||||
versions: versions::VersionsHandler { store: Store {} },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Called when registering modules with python.
|
||||
pub fn register_module(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
let child_module = PyModule::new(py, "handlers")?;
|
||||
// TODO: Expose `get_versions`
|
||||
// child_module.add_class::<PushRule>()?;
|
||||
child_module.add_class::<RustHandlers>()?;
|
||||
|
||||
m.add_submodule(&child_module)?;
|
||||
|
||||
|
||||
+148
-142
@@ -26,147 +26,153 @@ struct VersionsResponse {
|
||||
unstable_features: std::collections::BTreeMap<String, bool>,
|
||||
}
|
||||
|
||||
/// Assemble a `/versions` response
|
||||
async fn get_versions(
|
||||
pub struct VersionsHandler {
|
||||
store: &Store,
|
||||
user_id: Option<&str>,
|
||||
config: SynapseConfig,
|
||||
) -> Result<VersionsResponse, anyhow::Error> {
|
||||
let msc3881_enabled = match user_id {
|
||||
Some(user_id) => {
|
||||
store
|
||||
.is_feature_enabled(user_id, PerUserExperimentalFeature::MSC3881)
|
||||
.await?
|
||||
}
|
||||
None => config.experimental.msc3881_enabled,
|
||||
};
|
||||
|
||||
let msc3575_enabled = match user_id {
|
||||
Some(user_id) => {
|
||||
store
|
||||
.is_feature_enabled(user_id, PerUserExperimentalFeature::MSC3575)
|
||||
.await?
|
||||
}
|
||||
None => config.experimental.msc3575_enabled,
|
||||
};
|
||||
|
||||
// TODO: Calculate these once since they shouldn't change after start-up.
|
||||
// e2ee_forced_public = (
|
||||
// RoomCreationPreset.PUBLIC_CHAT
|
||||
// in config.room.encryption_enabled_by_default_for_room_presets
|
||||
// );
|
||||
// e2ee_forced_private = (
|
||||
// RoomCreationPreset.PRIVATE_CHAT
|
||||
// in config.room.encryption_enabled_by_default_for_room_presets
|
||||
// );
|
||||
// e2ee_forced_trusted_private = (
|
||||
// RoomCreationPreset.TRUSTED_PRIVATE_CHAT
|
||||
// in config.room.encryption_enabled_by_default_for_room_presets
|
||||
// );
|
||||
|
||||
return Ok(VersionsResponse {
|
||||
versions: Vec::from([
|
||||
// XXX: at some point we need to decide whether we need to include
|
||||
// the previous version numbers, given we've defined r0.3.0 to be
|
||||
// backwards compatible with r0.2.0. But need to check how
|
||||
// conscientious we've been in compatibility, and decide whether the
|
||||
// middle number is the major revision when at 0.X.Y (as opposed to
|
||||
// X.Y.Z). And we need to decide whether it's fair to make clients
|
||||
// parse the version string to figure out what's going on.
|
||||
"r0.0.1".to_string(),
|
||||
"r0.1.0".to_string(),
|
||||
"r0.2.0".to_string(),
|
||||
"r0.3.0".to_string(),
|
||||
"r0.4.0".to_string(),
|
||||
"r0.5.0".to_string(),
|
||||
"r0.6.0".to_string(),
|
||||
"r0.6.1".to_string(),
|
||||
"v1.1".to_string(),
|
||||
"v1.2".to_string(),
|
||||
"v1.3".to_string(),
|
||||
"v1.4".to_string(),
|
||||
"v1.5".to_string(),
|
||||
"v1.6".to_string(),
|
||||
"v1.7".to_string(),
|
||||
"v1.8".to_string(),
|
||||
"v1.9".to_string(),
|
||||
"v1.10".to_string(),
|
||||
"v1.11".to_string(),
|
||||
"v1.12".to_string(),
|
||||
]),
|
||||
unstable_features: std::collections::BTreeMap::from([
|
||||
// // Implements support for label-based filtering as described in
|
||||
// // MSC2326.
|
||||
// ("org.matrix.label_based_filtering".to_string(), true),
|
||||
// // Implements support for cross signing as described in MSC1756
|
||||
// ("org.matrix.e2e_cross_signing".to_string(), true),
|
||||
// // Implements additional endpoints as described in MSC2432
|
||||
// ("org.matrix.msc2432".to_string(), true),
|
||||
// // Implements additional endpoints as described in MSC2666
|
||||
// ("uk.half-shot.msc2666.query_mutual_rooms.stable".to_string(), true),
|
||||
// // Whether new rooms will be set to encrypted or not (based on presets).
|
||||
// ("io.element.e2ee_forced.public".to_string(), e2ee_forced_public),
|
||||
// ("io.element.e2ee_forced.private".to_string(), e2ee_forced_private),
|
||||
// ("io.element.e2ee_forced.trusted_private".to_string(), e2ee_forced_trusted_private),
|
||||
// // Supports the busy presence state described in MSC3026.
|
||||
// ("org.matrix.msc3026.busy_presence".to_string(), config.experimental.msc3026_enabled),
|
||||
// // Supports receiving private read receipts as per MSC2285
|
||||
// ("org.matrix.msc2285.stable".to_string(), true), // TODO: Remove when MSC2285 becomes a part of the spec
|
||||
// // Supports filtering of /publicRooms by room type as per MSC3827
|
||||
// ("org.matrix.msc3827.stable".to_string(), true),
|
||||
// // Adds support for thread relations, per MSC3440.
|
||||
// ("org.matrix.msc3440.stable".to_string(), true), // TODO: remove when "v1.3" is added above
|
||||
// // Support for thread read receipts & notification counts.
|
||||
// ("org.matrix.msc3771".to_string(), true),
|
||||
// ("org.matrix.msc3773".to_string(), config.experimental.msc3773_enabled),
|
||||
// // Allows moderators to fetch redacted event content as described in MSC2815
|
||||
// ("fi.mau.msc2815".to_string(), config.experimental.msc2815_enabled),
|
||||
// // Adds a ping endpoint for appservices to check HS->AS connection
|
||||
// ("fi.mau.msc2659.stable".to_string(), true), // TODO: remove when "v1.7" is added above
|
||||
// // TODO: this is no longer needed once unstable MSC3882 does not need to be supported:
|
||||
// ("org.matrix.msc3882".to_string(), config.auth.login_via_existing_enabled),
|
||||
// Adds support for remotely enabling/disabling pushers, as per MSC3881
|
||||
("org.matrix.msc3881".to_string(), msc3881_enabled),
|
||||
// // Adds support for filtering /messages by event relation.
|
||||
// ("org.matrix.msc3874".to_string(), config.experimental.msc3874_enabled),
|
||||
// // Adds support for relation-based redactions as per MSC3912.
|
||||
// ("org.matrix.msc3912".to_string(), config.experimental.msc3912_enabled),
|
||||
// // Whether recursively provide relations is supported.
|
||||
// // TODO This is no longer needed once unstable MSC3981 does not need to be supported.
|
||||
// ("org.matrix.msc3981".to_string(), true),
|
||||
// // Adds support for deleting account data.
|
||||
// ("org.matrix.msc3391".to_string(), config.experimental.msc3391_enabled),
|
||||
// // Allows clients to inhibit profile update propagation.
|
||||
// ("org.matrix.msc4069".to_string(), config.experimental.msc4069_profile_inhibit_propagation),
|
||||
// // Allows clients to handle push for encrypted events.
|
||||
// ("org.matrix.msc4028".to_string(), config.experimental.msc4028_push_encrypted_events),
|
||||
// // MSC4108: Mechanism to allow OIDC sign in and E2EE set up via QR code - 2024 version
|
||||
// ("org.matrix.msc4108".to_string(), (
|
||||
// config.experimental.msc4108_enabled
|
||||
// or (
|
||||
// config.experimental.msc4108_delegation_endpoint
|
||||
// is not None
|
||||
// )
|
||||
// )),
|
||||
// // MSC4140: Delayed events
|
||||
// ("org.matrix.msc4140".to_string(), bool(config.server.max_event_delay_ms)),
|
||||
// Simplified sliding sync
|
||||
("org.matrix.simplified_msc3575".to_string(), msc3575_enabled),
|
||||
// // Arbitrary key-value profile fields.
|
||||
// ("uk.tcpip.msc4133".to_string(), config.experimental.msc4133_enabled),
|
||||
// ("uk.tcpip.msc4133.stable".to_string(), true),
|
||||
// // MSC4155: Invite filtering
|
||||
// ("org.matrix.msc4155".to_string(), config.experimental.msc4155_enabled),
|
||||
// // MSC4306: Support for thread subscriptions
|
||||
// ("org.matrix.msc4306".to_string(), config.experimental.msc4306_enabled),
|
||||
// // MSC4169: Backwards-compatible redaction sending using `/send`
|
||||
// ("com.beeper.msc4169".to_string(), config.experimental.msc4169_enabled),
|
||||
// // MSC4354: Sticky events
|
||||
// ("org.matrix.msc4354".to_string(), config.experimental.msc4354_enabled),
|
||||
// // MSC4380: Invite blocking
|
||||
// ("org.matrix.msc4380.stable".to_string(), true),
|
||||
// // MSC4445: Sync timeline order
|
||||
// ("org.matrix.msc4445.initial_sync_timeline_topological_ordering".to_string(), true),
|
||||
]),
|
||||
});
|
||||
}
|
||||
|
||||
impl VersionsHandler {
|
||||
/// Assemble a `/versions` response
|
||||
async fn get_versions(
|
||||
&self,
|
||||
user_id: Option<&str>,
|
||||
config: SynapseConfig,
|
||||
) -> Result<VersionsResponse, anyhow::Error> {
|
||||
let msc3881_enabled = match user_id {
|
||||
Some(user_id) => {
|
||||
self.store
|
||||
.is_feature_enabled(user_id, PerUserExperimentalFeature::MSC3881)
|
||||
.await?
|
||||
}
|
||||
None => PerUserExperimentalFeature::MSC3881.is_globally_enabled(config),
|
||||
};
|
||||
|
||||
let msc3575_enabled = match user_id {
|
||||
Some(user_id) => {
|
||||
self.store
|
||||
.is_feature_enabled(user_id, PerUserExperimentalFeature::MSC3575)
|
||||
.await?
|
||||
}
|
||||
None => PerUserExperimentalFeature::MSC3575.is_globally_enabled(config),
|
||||
};
|
||||
|
||||
// TODO: Calculate these once since they shouldn't change after start-up.
|
||||
// e2ee_forced_public = (
|
||||
// RoomCreationPreset.PUBLIC_CHAT
|
||||
// in config.room.encryption_enabled_by_default_for_room_presets
|
||||
// );
|
||||
// e2ee_forced_private = (
|
||||
// RoomCreationPreset.PRIVATE_CHAT
|
||||
// in config.room.encryption_enabled_by_default_for_room_presets
|
||||
// );
|
||||
// e2ee_forced_trusted_private = (
|
||||
// RoomCreationPreset.TRUSTED_PRIVATE_CHAT
|
||||
// in config.room.encryption_enabled_by_default_for_room_presets
|
||||
// );
|
||||
|
||||
return Ok(VersionsResponse {
|
||||
versions: Vec::from([
|
||||
// XXX: at some point we need to decide whether we need to include
|
||||
// the previous version numbers, given we've defined r0.3.0 to be
|
||||
// backwards compatible with r0.2.0. But need to check how
|
||||
// conscientious we've been in compatibility, and decide whether the
|
||||
// middle number is the major revision when at 0.X.Y (as opposed to
|
||||
// X.Y.Z). And we need to decide whether it's fair to make clients
|
||||
// parse the version string to figure out what's going on.
|
||||
"r0.0.1".to_string(),
|
||||
"r0.1.0".to_string(),
|
||||
"r0.2.0".to_string(),
|
||||
"r0.3.0".to_string(),
|
||||
"r0.4.0".to_string(),
|
||||
"r0.5.0".to_string(),
|
||||
"r0.6.0".to_string(),
|
||||
"r0.6.1".to_string(),
|
||||
"v1.1".to_string(),
|
||||
"v1.2".to_string(),
|
||||
"v1.3".to_string(),
|
||||
"v1.4".to_string(),
|
||||
"v1.5".to_string(),
|
||||
"v1.6".to_string(),
|
||||
"v1.7".to_string(),
|
||||
"v1.8".to_string(),
|
||||
"v1.9".to_string(),
|
||||
"v1.10".to_string(),
|
||||
"v1.11".to_string(),
|
||||
"v1.12".to_string(),
|
||||
]),
|
||||
unstable_features: std::collections::BTreeMap::from([
|
||||
// // Implements support for label-based filtering as described in
|
||||
// // MSC2326.
|
||||
// ("org.matrix.label_based_filtering".to_string(), true),
|
||||
// // Implements support for cross signing as described in MSC1756
|
||||
// ("org.matrix.e2e_cross_signing".to_string(), true),
|
||||
// // Implements additional endpoints as described in MSC2432
|
||||
// ("org.matrix.msc2432".to_string(), true),
|
||||
// // Implements additional endpoints as described in MSC2666
|
||||
// ("uk.half-shot.msc2666.query_mutual_rooms.stable".to_string(), true),
|
||||
// // Whether new rooms will be set to encrypted or not (based on presets).
|
||||
// ("io.element.e2ee_forced.public".to_string(), e2ee_forced_public),
|
||||
// ("io.element.e2ee_forced.private".to_string(), e2ee_forced_private),
|
||||
// ("io.element.e2ee_forced.trusted_private".to_string(), e2ee_forced_trusted_private),
|
||||
// // Supports the busy presence state described in MSC3026.
|
||||
// ("org.matrix.msc3026.busy_presence".to_string(), config.experimental.msc3026_enabled),
|
||||
// // Supports receiving private read receipts as per MSC2285
|
||||
// ("org.matrix.msc2285.stable".to_string(), true), // TODO: Remove when MSC2285 becomes a part of the spec
|
||||
// // Supports filtering of /publicRooms by room type as per MSC3827
|
||||
// ("org.matrix.msc3827.stable".to_string(), true),
|
||||
// // Adds support for thread relations, per MSC3440.
|
||||
// ("org.matrix.msc3440.stable".to_string(), true), // TODO: remove when "v1.3" is added above
|
||||
// // Support for thread read receipts & notification counts.
|
||||
// ("org.matrix.msc3771".to_string(), true),
|
||||
// ("org.matrix.msc3773".to_string(), config.experimental.msc3773_enabled),
|
||||
// // Allows moderators to fetch redacted event content as described in MSC2815
|
||||
// ("fi.mau.msc2815".to_string(), config.experimental.msc2815_enabled),
|
||||
// // Adds a ping endpoint for appservices to check HS->AS connection
|
||||
// ("fi.mau.msc2659.stable".to_string(), true), // TODO: remove when "v1.7" is added above
|
||||
// // TODO: this is no longer needed once unstable MSC3882 does not need to be supported:
|
||||
// ("org.matrix.msc3882".to_string(), config.auth.login_via_existing_enabled),
|
||||
// Adds support for remotely enabling/disabling pushers, as per MSC3881
|
||||
("org.matrix.msc3881".to_string(), msc3881_enabled),
|
||||
// // Adds support for filtering /messages by event relation.
|
||||
// ("org.matrix.msc3874".to_string(), config.experimental.msc3874_enabled),
|
||||
// // Adds support for relation-based redactions as per MSC3912.
|
||||
// ("org.matrix.msc3912".to_string(), config.experimental.msc3912_enabled),
|
||||
// // Whether recursively provide relations is supported.
|
||||
// // TODO This is no longer needed once unstable MSC3981 does not need to be supported.
|
||||
// ("org.matrix.msc3981".to_string(), true),
|
||||
// // Adds support for deleting account data.
|
||||
// ("org.matrix.msc3391".to_string(), config.experimental.msc3391_enabled),
|
||||
// // Allows clients to inhibit profile update propagation.
|
||||
// ("org.matrix.msc4069".to_string(), config.experimental.msc4069_profile_inhibit_propagation),
|
||||
// // Allows clients to handle push for encrypted events.
|
||||
// ("org.matrix.msc4028".to_string(), config.experimental.msc4028_push_encrypted_events),
|
||||
// // MSC4108: Mechanism to allow OIDC sign in and E2EE set up via QR code - 2024 version
|
||||
// ("org.matrix.msc4108".to_string(), (
|
||||
// config.experimental.msc4108_enabled
|
||||
// or (
|
||||
// config.experimental.msc4108_delegation_endpoint
|
||||
// is not None
|
||||
// )
|
||||
// )),
|
||||
// // MSC4140: Delayed events
|
||||
// ("org.matrix.msc4140".to_string(), bool(config.server.max_event_delay_ms)),
|
||||
// Simplified sliding sync
|
||||
("org.matrix.simplified_msc3575".to_string(), msc3575_enabled),
|
||||
// // Arbitrary key-value profile fields.
|
||||
// ("uk.tcpip.msc4133".to_string(), config.experimental.msc4133_enabled),
|
||||
// ("uk.tcpip.msc4133.stable".to_string(), true),
|
||||
// // MSC4155: Invite filtering
|
||||
// ("org.matrix.msc4155".to_string(), config.experimental.msc4155_enabled),
|
||||
// // MSC4306: Support for thread subscriptions
|
||||
// ("org.matrix.msc4306".to_string(), config.experimental.msc4306_enabled),
|
||||
// // MSC4169: Backwards-compatible redaction sending using `/send`
|
||||
// ("com.beeper.msc4169".to_string(), config.experimental.msc4169_enabled),
|
||||
// // MSC4354: Sticky events
|
||||
// ("org.matrix.msc4354".to_string(), config.experimental.msc4354_enabled),
|
||||
// // MSC4380: Invite blocking
|
||||
// ("org.matrix.msc4380.stable".to_string(), true),
|
||||
// // MSC4445: Sync timeline order
|
||||
// ("org.matrix.msc4445.initial_sync_timeline_topological_ordering".to_string(), true),
|
||||
]),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* This file is licensed under the Affero General Public License (AGPL) version 3.
|
||||
*
|
||||
* Copyright (C) 2026 Element Creations Ltd
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* See the GNU Affero General Public License for more details:
|
||||
* <https://www.gnu.org/licenses/agpl-3.0.html>.
|
||||
*
|
||||
*/
|
||||
|
||||
use crate::db::python_db_pool::DatabasePool as PythonDatabasePool;
|
||||
|
||||
/// A [`tokio_postgres::Transaction`] looking thing that we can use on the Rust side to
|
||||
/// interact with the database
|
||||
pub trait Transaction {
|
||||
pub fn query(&self, sql: &str, args: &[&str]) -> None {
|
||||
todo!("TODO");
|
||||
}
|
||||
}
|
||||
|
||||
/// Database access backed by the database pool we already have in the Python side of Synapse
|
||||
struct SynapsePythonTransaction {
|
||||
db_pool: PythonDatabasePool,
|
||||
}
|
||||
|
||||
impl Transaction for SynapsePythonTransaction {
|
||||
fn query(&self, sql: &str, args: &[&str]) -> None {
|
||||
todo!("TODO");
|
||||
}
|
||||
}
|
||||
|
||||
/// Native Rust database access backed by `tokio-postgres` (for use in synapse-rust-apps)
|
||||
// struct TokioPostgresTransaction {
|
||||
// db_pool: bb8::Pool<PostgresConnectionManager<MakeTlsConnector>>,
|
||||
// }
|
||||
|
||||
// impl Transaction for TokioPostgresTransaction {
|
||||
// fn query(&self, sql: &str, args: &[&str]) -> None {
|
||||
// todo!("TODO");
|
||||
|
||||
// // TODO: Set isolation level
|
||||
|
||||
// let mut conn = self
|
||||
// .db_pool
|
||||
// .get()
|
||||
// .instrument(tracing::info_span!("acquire database connection"))
|
||||
// .await
|
||||
// .map_err(|e| {
|
||||
// sentry::capture_error(&e);
|
||||
// tracing::error!(
|
||||
// error = e.to_string(),
|
||||
// "Failed to acquire database connection"
|
||||
// );
|
||||
// anyhow::anyhow!("Failed to acquire database connection: {e}")
|
||||
// })?;
|
||||
|
||||
// let txn = conn
|
||||
// .transaction()
|
||||
// .instrument(tracing::info_span!("start transaction"))
|
||||
// .await
|
||||
// .context("Failed to start transaction")?;
|
||||
|
||||
// let rows = txn.query(sql, args).await?;
|
||||
|
||||
// rows
|
||||
// }
|
||||
// }
|
||||
@@ -14,5 +14,15 @@
|
||||
*/
|
||||
|
||||
pub mod python_db_pool;
|
||||
pub mod rust_db_pool;
|
||||
|
||||
pub mod db;
|
||||
pub trait DatabasePool {
|
||||
async fn get_transaction(&self, description: &str) -> dyn Transaction;
|
||||
}
|
||||
|
||||
/// A [`tokio_postgres::Transaction`] looking thing that we can use on the Rust side to
|
||||
/// interact with the database
|
||||
pub trait Transaction {
|
||||
async fn query(&self, sql: &str, args: &[&str]) -> ();
|
||||
async fn commit(&self) -> ();
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
|
||||
use pyo3::{intern, prelude::*};
|
||||
|
||||
use crate::storage::db::{DatabasePool, Transaction};
|
||||
|
||||
/// The database engines we support in the Python side of Synapse
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum DatabaseEngine {
|
||||
@@ -32,59 +34,79 @@ impl DatabaseEngine {
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper for a `DatabasePool` from the Python side of Synapse.
|
||||
pub struct DatabasePool<'py> {
|
||||
/// The underlying `DatabasePool`
|
||||
raw: Bound<'py, PyAny>,
|
||||
}
|
||||
pub struct PythonDatabasePool {}
|
||||
|
||||
impl<'py> DatabasePool<'py> {
|
||||
pub fn get_transaction(&mut self, description: &str) -> LoggingTransactionWrapper {
|
||||
todo!("TODO");
|
||||
impl DatabasePool for PythonDatabasePool {
|
||||
pub fn get_transaction(&self, description: &str) -> dyn Transaction {
|
||||
todo!("...");
|
||||
// let execute_fn = self.raw.getattr(intern!(self.raw.py(), "runInteraction"))?;
|
||||
// execute_fn.call1((sql, args))?;
|
||||
// Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper for a `LoggingTransaction` from the Python side of Synapse.
|
||||
pub struct LoggingTransactionWrapper<'py> {
|
||||
/// The underlying `LoggingTransaction`
|
||||
raw: Bound<'py, PyAny>,
|
||||
fn detect_engine(txn_py: &Bound<'_, PyAny>) -> PyResult<DatabaseEngine> {
|
||||
let name = txn_py
|
||||
.getattr("database_engine")
|
||||
.expect("`LoggingTransaction` must have `database_engine` attr")
|
||||
.get_type()
|
||||
.name()
|
||||
.expect("`database_engine` type must have a name")
|
||||
.to_str()
|
||||
.expect("`database_engine` type name must be valid UTF-8")
|
||||
.to_owned();
|
||||
|
||||
/// Dissambiguate which underyling database engine we're working with
|
||||
database_engine: DatabaseEngine,
|
||||
Ok(match name.as_str() {
|
||||
"PostgresEngine" => DatabaseEngine::Postgres,
|
||||
"Sqlite3Engine" => DatabaseEngine::Sqlite,
|
||||
other => unimplemented!(
|
||||
"Unknown database engine {other:?}. This is a Synapse programming error."
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
impl<'py> FromPyObject<'_, 'py> for LoggingTransactionWrapper<'py> {
|
||||
/// Wrapper for a `LoggingTransaction` from the Python side of Synapse.
|
||||
///
|
||||
/// Holds no `'py` lifetime so it can be stored and moved freely across threads.
|
||||
/// Use [`execute`](Self::execute) (or other methods) while holding the GIL.
|
||||
pub struct LoggingTransactionWrapper {
|
||||
/// The underlying `LoggingTransaction`
|
||||
raw: Py<PyAny>,
|
||||
|
||||
/// Disambiguate which underlying database engine we're working with
|
||||
pub database_engine: DatabaseEngine,
|
||||
}
|
||||
|
||||
impl<'a, 'py> FromPyObject<'a, 'py> for LoggingTransactionWrapper {
|
||||
type Error = PyErr;
|
||||
|
||||
/// From Python `LoggingTransaction`
|
||||
fn extract(logging_transaction_python_object: Borrowed<'_, 'py, PyAny>) -> PyResult<Self> {
|
||||
let database_engine = match logging_transaction_python_object
|
||||
.getattr("database_engine")
|
||||
.expect("Expected the Python object you passed to be `LoggingTransaction` which should have `database_engine` attr")
|
||||
.get_type()
|
||||
.name()
|
||||
.expect("Expected `LoggingTransaction.database_engine` to have a type name")
|
||||
.to_str()
|
||||
.expect("Expected to be able to convert the `LoggingTransaction.database_engine` type name to a string")
|
||||
{
|
||||
"PostgresEngine" => DatabaseEngine::Postgres,
|
||||
"Sqlite3Engine" => DatabaseEngine::Sqlite,
|
||||
other => unimplemented!("Unknown database engine {other:?}. This is a Synapse programming error."),
|
||||
};
|
||||
/// Extract from a Python `LoggingTransaction` passed as an argument.
|
||||
///
|
||||
/// The resulting wrapper has `done_tx = None`; Python owns the transaction lifetime.
|
||||
fn extract(obj: Borrowed<'a, 'py, PyAny>) -> PyResult<Self> {
|
||||
let database_engine = detect_engine(&obj.to_owned())?;
|
||||
Ok(Self {
|
||||
raw: logging_transaction_python_object.cast()?.to_owned(),
|
||||
raw: obj.to_owned().unbind(),
|
||||
database_engine,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'py> LoggingTransactionWrapper<'py> {
|
||||
pub fn execute(&mut self, sql: &str, args: &'py Bound<'py, PyAny>) -> PyResult<()> {
|
||||
let execute_fn = self.raw.getattr(intern!(self.raw.py(), "execute"))?;
|
||||
impl LoggingTransactionWrapper {
|
||||
pub fn execute<'py>(
|
||||
&mut self,
|
||||
py: Python<'py>,
|
||||
sql: &str,
|
||||
args: &Bound<'py, PyAny>,
|
||||
) -> PyResult<()> {
|
||||
let execute_fn = self.raw.bind(py).getattr(intern!(py, "execute"))?;
|
||||
execute_fn.call1((sql, args))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Transaction for LoggingTransactionWrapper {
|
||||
fn query(&self, sql: &str, args: &[&str]) -> () {
|
||||
self.execute(sql, args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* This file is licensed under the Affero General Public License (AGPL) version 3.
|
||||
*
|
||||
* Copyright (C) 2026 Element Creations Ltd
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* See the GNU Affero General Public License for more details:
|
||||
* <https://www.gnu.org/licenses/agpl-3.0.html>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
// TODO: remove. This is just here to make sure our `DatabasePool`/`Transaction`
|
||||
// interfaces are compatible with `tokio-postgres`.
|
||||
|
||||
use anyhow::Context;
|
||||
use bb8_postgres::{tokio_postgres::Row, PostgresConnectionManager};
|
||||
use postgres_native_tls::MakeTlsConnector;
|
||||
|
||||
use crate::storage::db::{DatabasePool, Transaction};
|
||||
|
||||
/// Native Rust database access backed by `tokio-postgres` (for use in synapse-rust-apps)
|
||||
pub struct RustDatabasePool {
|
||||
db_pool: bb8::Pool<PostgresConnectionManager<MakeTlsConnector>>,
|
||||
}
|
||||
|
||||
impl DatabasePool for RustDatabasePool {
|
||||
async fn get_transaction(&self, description: &str) -> dyn Transaction {
|
||||
let mut conn = self
|
||||
.db_pool
|
||||
.get()
|
||||
// .instrument(tracing::info_span!("acquire database connection"))
|
||||
.await
|
||||
.context("Failed to acquire database connection")?;
|
||||
|
||||
let txn = conn
|
||||
.transaction()
|
||||
// .instrument(tracing::info_span!("start transaction"))
|
||||
.await
|
||||
.context("Failed to start transaction")?;
|
||||
|
||||
// TODO: Set isolation level
|
||||
txn
|
||||
}
|
||||
}
|
||||
|
||||
struct TokioPostgresTransaction<'a> {
|
||||
txn: bb8_postgres::tokio_postgres::Transaction<'a>,
|
||||
}
|
||||
|
||||
impl Transaction for TokioPostgresTransaction<'_> {
|
||||
async fn query(&self, sql: &str, args: &[&str]) -> () {
|
||||
todo!("TODO");
|
||||
|
||||
let rows = self.txn.query(sql, args).await?;
|
||||
|
||||
rows
|
||||
}
|
||||
|
||||
async fn commit(&self) -> () {
|
||||
self.txn
|
||||
.commit()
|
||||
// .instrument(tracing::info_span!("commit transaction"))
|
||||
.await
|
||||
.context("Failed to commit transaction")?;
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
use sha2::digest::typenum::Len;
|
||||
|
||||
use crate::{config::SynapseConfig, storage::db::DatabasePool};
|
||||
|
||||
/// Currently supported per-user features
|
||||
pub enum PerUserExperimentalFeature {
|
||||
MSC3881,
|
||||
@@ -20,7 +24,20 @@ pub enum PerUserExperimentalFeature {
|
||||
MSC4222,
|
||||
}
|
||||
|
||||
pub struct Store {}
|
||||
impl PerUserExperimentalFeature {
|
||||
pub fn is_globally_enabled(&self, config: SynapseConfig) -> bool {
|
||||
match self {
|
||||
PerUserExperimentalFeature::MSC3881 => config.experimental.msc3881_enabled,
|
||||
PerUserExperimentalFeature::MSC3575 => config.experimental.msc3575_enabled,
|
||||
PerUserExperimentalFeature::MSC4222 => config.experimental.msc4222_enabled,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Store {
|
||||
config: SynapseConfig,
|
||||
db_pool: dyn DatabasePool,
|
||||
}
|
||||
|
||||
impl Store {
|
||||
pub async fn is_feature_enabled(
|
||||
@@ -28,6 +45,28 @@ impl Store {
|
||||
user_id: &str,
|
||||
feature: PerUserExperimentalFeature,
|
||||
) -> Result<bool, anyhow::Error> {
|
||||
todo!("...");
|
||||
if feature.is_globally_enabled(self.config) {
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
let txn = self.db_pool.get_transaction("is_feature_enabled").await;
|
||||
let rows = txn
|
||||
.query(
|
||||
r#"
|
||||
SELECT enabled
|
||||
FROM per_user_experimental_features
|
||||
WHERE user_id = ? AND feature = ?
|
||||
"#,
|
||||
&[user_id, feature],
|
||||
)
|
||||
.await;
|
||||
|
||||
match (rows.len(), rows.first()) {
|
||||
(1, Some(enabled)) => Ok(enabled),
|
||||
(0, None) => Ok(false),
|
||||
_ => {
|
||||
panic!("Synapse programming error");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,13 +25,11 @@ import logging
|
||||
import re
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from synapse.api.constants import RoomCreationPreset
|
||||
from synapse.http.server import HttpServer
|
||||
from synapse.http.servlet import RestServlet
|
||||
from synapse.http.site import SynapseRequest
|
||||
from synapse.rest.admin.experimental_features import ExperimentalFeature
|
||||
from synapse.types import JsonDict
|
||||
from synapse.synapse_rust.handlers.versions import get_versions
|
||||
from synapse.types import JsonDict
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from synapse.server import HomeServer
|
||||
@@ -48,6 +46,7 @@ class VersionsRestServlet(RestServlet):
|
||||
self.config = hs.config
|
||||
self.auth = hs.get_auth()
|
||||
self.store = hs.get_datastores().main
|
||||
self.rust_handlers = hs.get_rust_handlers()
|
||||
|
||||
async def on_GET(self, request: SynapseRequest) -> tuple[int, JsonDict]:
|
||||
user_id = None
|
||||
@@ -80,7 +79,9 @@ class VersionsRestServlet(RestServlet):
|
||||
# authenticated responses are not served from cache.
|
||||
request.setHeader(b"Vary", b"Authorization")
|
||||
|
||||
versions_response_body = await get_versions(user_id, self.config)
|
||||
versions_response_body = await self.rust_handlers.versions.get_versions(
|
||||
user_id, self.config
|
||||
)
|
||||
|
||||
return (
|
||||
200,
|
||||
|
||||
@@ -963,6 +963,10 @@ class HomeServer(metaclass=abc.ABCMeta):
|
||||
def get_set_password_handler(self) -> SetPasswordHandler:
|
||||
return SetPasswordHandler(self)
|
||||
|
||||
@cache_in_self
|
||||
def get_rust_handlers(self) -> RustHandlers:
|
||||
return RustHandlers(self)
|
||||
|
||||
@cache_in_self
|
||||
def get_event_sources(self) -> EventSources:
|
||||
return EventSources(self)
|
||||
|
||||
Reference in New Issue
Block a user