separate account_data service methods for room vs global events

Previously we were mashing everything together as RoomAccountDataEvent,
even the global events. This technically worked, because of the hidden
custom fields on the ruma event types, but it's confusing and easy to
mess up. Separate methods with appropriate types are preferable.
This commit is contained in:
Olivia Lee 2025-03-23 02:08:38 -07:00
parent 6897f0ba34
commit 66210bc32d
No known key found for this signature in database
GPG key ID: 54D568A15B9CD1F9
16 changed files with 349 additions and 361 deletions

View file

@ -12,9 +12,12 @@ use ruma::{
uiaa::{AuthFlow, AuthType, UiaaInfo},
},
events::{
room::message::RoomMessageEventContent, GlobalAccountDataEventType,
room::message::RoomMessageEventContent, AnyGlobalAccountDataEvent,
GlobalAccountDataEventType,
},
push, UserId,
push,
serde::Raw,
UserId,
};
use tracing::{info, warn};
@ -234,16 +237,16 @@ pub(crate) async fn register_route(
services().users.set_displayname(&user_id, Some(displayname.clone()))?;
// Initial account data
services().account_data.update(
None,
services().account_data.update_global(
&user_id,
GlobalAccountDataEventType::PushRules.to_string().into(),
&serde_json::to_value(ruma::events::push_rules::PushRulesEvent {
&GlobalAccountDataEventType::PushRules,
&Raw::new(&ruma::events::push_rules::PushRulesEvent {
content: ruma::events::push_rules::PushRulesEventContent {
global: push::Ruleset::server_default(&user_id),
},
})
.expect("to json always works"),
.expect("constructed event should be valid")
.cast::<AnyGlobalAccountDataEvent>(),
)?;
// Inhibit login does not work for guests

View file

@ -7,12 +7,13 @@ use ruma::{
error::ErrorKind,
},
events::{
AnyGlobalAccountDataEventContent, AnyRoomAccountDataEventContent,
AnyGlobalAccountDataEvent, AnyGlobalAccountDataEventContent,
AnyRoomAccountDataEvent, AnyRoomAccountDataEventContent,
},
serde::Raw,
};
use serde::Deserialize;
use serde_json::{json, value::RawValue as RawJsonValue};
use serde_json::json;
use crate::{services, Ar, Error, Ra, Result};
@ -24,21 +25,17 @@ pub(crate) async fn set_global_account_data_route(
) -> Result<Ra<set_global_account_data::v3::Response>> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let data: serde_json::Value = serde_json::from_str(body.data.json().get())
.map_err(|_| {
Error::BadRequest(ErrorKind::BadJson, "Data is invalid.")
})?;
let event = Raw::new(&json!({
"type": &body.event_type,
"content": &body.data,
}))
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Data is invalid."))?
.cast::<AnyGlobalAccountDataEvent>();
let event_type = body.event_type.to_string();
services().account_data.update(
None,
services().account_data.update_global(
sender_user,
event_type.clone().into(),
&json!({
"type": event_type,
"content": data,
}),
&body.event_type,
&event,
)?;
Ok(Ra(set_global_account_data::v3::Response {}))
@ -52,21 +49,18 @@ pub(crate) async fn set_room_account_data_route(
) -> Result<Ra<set_room_account_data::v3::Response>> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let data: serde_json::Value = serde_json::from_str(body.data.json().get())
.map_err(|_| {
Error::BadRequest(ErrorKind::BadJson, "Data is invalid.")
})?;
let event = Raw::new(&json!({
"type": &body.event_type,
"content": &body.data,
}))
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Data is invalid."))?
.cast::<AnyRoomAccountDataEvent>();
let event_type = body.event_type.to_string();
services().account_data.update(
Some(&body.room_id),
services().account_data.update_room(
&body.room_id,
sender_user,
event_type.clone().into(),
&json!({
"type": event_type,
"content": data,
}),
&body.event_type,
&event,
)?;
Ok(Ra(set_room_account_data::v3::Response {}))
@ -80,16 +74,14 @@ pub(crate) async fn get_global_account_data_route(
) -> Result<Ra<get_global_account_data::v3::Response>> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let event: Box<RawJsonValue> = services()
let event = services()
.account_data
.get(None, sender_user, body.event_type.to_string().into())?
.get_global(sender_user, &body.event_type)?
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Data not found."))?;
let account_data =
serde_json::from_str::<ExtractGlobalEventContent>(event.get())
.map_err(|_| {
Error::bad_database("Invalid account data event in db.")
})?
let account_data = event
.deserialize_as::<ExtractGlobalEventContent>()
.map_err(|_| Error::bad_database("Invalid account data event in db."))?
.content;
Ok(Ra(get_global_account_data::v3::Response {
@ -105,16 +97,14 @@ pub(crate) async fn get_room_account_data_route(
) -> Result<Ra<get_room_account_data::v3::Response>> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let event: Box<RawJsonValue> = services()
let event = services()
.account_data
.get(Some(&body.room_id), sender_user, body.event_type.clone())?
.get_room(&body.room_id, sender_user, &body.event_type)?
.ok_or(Error::BadRequest(ErrorKind::NotFound, "Data not found."))?;
let account_data =
serde_json::from_str::<ExtractRoomEventContent>(event.get())
.map_err(|_| {
Error::bad_database("Invalid account data event in db.")
})?
let account_data = event
.deserialize_as::<ExtractRoomEventContent>()
.map_err(|_| Error::bad_database("Invalid account data event in db."))?
.content;
Ok(Ra(get_room_account_data::v3::Response {

View file

@ -7,8 +7,12 @@ use ruma::{
set_pushrule_actions, set_pushrule_enabled,
},
},
events::{push_rules::PushRulesEvent, GlobalAccountDataEventType},
events::{
push_rules::PushRulesEvent, AnyGlobalAccountDataEvent,
GlobalAccountDataEventType,
},
push::{AnyPushRuleRef, InsertPushRuleError, RemovePushRuleError},
serde::Raw,
};
use crate::{services, Ar, Error, Ra, Result};
@ -23,17 +27,14 @@ pub(crate) async fn get_pushrules_all_route(
let event = services()
.account_data
.get(
None,
sender_user,
GlobalAccountDataEventType::PushRules.to_string().into(),
)?
.get_global(sender_user, &GlobalAccountDataEventType::PushRules)?
.ok_or(Error::BadRequest(
ErrorKind::NotFound,
"PushRules event not found.",
))?;
let account_data = serde_json::from_str::<PushRulesEvent>(event.get())
let account_data = event
.deserialize_as::<PushRulesEvent>()
.map_err(|_| Error::bad_database("Invalid account data event in db."))?
.content;
@ -52,17 +53,14 @@ pub(crate) async fn get_pushrule_route(
let event = services()
.account_data
.get(
None,
sender_user,
GlobalAccountDataEventType::PushRules.to_string().into(),
)?
.get_global(sender_user, &GlobalAccountDataEventType::PushRules)?
.ok_or(Error::BadRequest(
ErrorKind::NotFound,
"PushRules event not found.",
))?;
let account_data = serde_json::from_str::<PushRulesEvent>(event.get())
let account_data = event
.deserialize_as::<PushRulesEvent>()
.map_err(|_| Error::bad_database("Invalid account data event in db."))?
.content;
@ -91,18 +89,14 @@ pub(crate) async fn set_pushrule_route(
let event = services()
.account_data
.get(
None,
sender_user,
GlobalAccountDataEventType::PushRules.to_string().into(),
)?
.get_global(sender_user, &GlobalAccountDataEventType::PushRules)?
.ok_or(Error::BadRequest(
ErrorKind::NotFound,
"PushRules event not found.",
))?;
let mut account_data = serde_json::from_str::<PushRulesEvent>(event.get())
.map_err(|_| {
let mut account_data =
event.deserialize_as::<PushRulesEvent>().map_err(|_| {
Error::bad_database("Invalid account data event in db.")
})?;
@ -142,12 +136,12 @@ pub(crate) async fn set_pushrule_route(
return Err(err);
}
services().account_data.update(
None,
services().account_data.update_global(
sender_user,
GlobalAccountDataEventType::PushRules.to_string().into(),
&serde_json::to_value(account_data)
.expect("to json value always works"),
&GlobalAccountDataEventType::PushRules,
&Raw::new(&account_data)
.expect("json event serialization should always succeed")
.cast::<AnyGlobalAccountDataEvent>(),
)?;
Ok(Ra(set_pushrule::v3::Response {}))
@ -163,17 +157,14 @@ pub(crate) async fn get_pushrule_actions_route(
let event = services()
.account_data
.get(
None,
sender_user,
GlobalAccountDataEventType::PushRules.to_string().into(),
)?
.get_global(sender_user, &GlobalAccountDataEventType::PushRules)?
.ok_or(Error::BadRequest(
ErrorKind::NotFound,
"PushRules event not found.",
))?;
let account_data = serde_json::from_str::<PushRulesEvent>(event.get())
let account_data = event
.deserialize_as::<PushRulesEvent>()
.map_err(|_| Error::bad_database("Invalid account data event in db."))?
.content;
@ -201,18 +192,14 @@ pub(crate) async fn set_pushrule_actions_route(
let event = services()
.account_data
.get(
None,
sender_user,
GlobalAccountDataEventType::PushRules.to_string().into(),
)?
.get_global(sender_user, &GlobalAccountDataEventType::PushRules)?
.ok_or(Error::BadRequest(
ErrorKind::NotFound,
"PushRules event not found.",
))?;
let mut account_data = serde_json::from_str::<PushRulesEvent>(event.get())
.map_err(|_| {
let mut account_data =
event.deserialize_as::<PushRulesEvent>().map_err(|_| {
Error::bad_database("Invalid account data event in db.")
})?;
@ -228,12 +215,12 @@ pub(crate) async fn set_pushrule_actions_route(
));
}
services().account_data.update(
None,
services().account_data.update_global(
sender_user,
GlobalAccountDataEventType::PushRules.to_string().into(),
&serde_json::to_value(account_data)
.expect("to json value always works"),
&GlobalAccountDataEventType::PushRules,
&Raw::new(&account_data)
.expect("json event serialization should always suceed")
.cast::<AnyGlobalAccountDataEvent>(),
)?;
Ok(Ra(set_pushrule_actions::v3::Response {}))
@ -249,18 +236,14 @@ pub(crate) async fn get_pushrule_enabled_route(
let event = services()
.account_data
.get(
None,
sender_user,
GlobalAccountDataEventType::PushRules.to_string().into(),
)?
.get_global(sender_user, &GlobalAccountDataEventType::PushRules)?
.ok_or(Error::BadRequest(
ErrorKind::NotFound,
"PushRules event not found.",
))?;
let account_data = serde_json::from_str::<PushRulesEvent>(event.get())
.map_err(|_| {
let account_data =
event.deserialize_as::<PushRulesEvent>().map_err(|_| {
Error::bad_database("Invalid account data event in db.")
})?;
@ -288,18 +271,14 @@ pub(crate) async fn set_pushrule_enabled_route(
let event = services()
.account_data
.get(
None,
sender_user,
GlobalAccountDataEventType::PushRules.to_string().into(),
)?
.get_global(sender_user, &GlobalAccountDataEventType::PushRules)?
.ok_or(Error::BadRequest(
ErrorKind::NotFound,
"PushRules event not found.",
))?;
let mut account_data = serde_json::from_str::<PushRulesEvent>(event.get())
.map_err(|_| {
let mut account_data =
event.deserialize_as::<PushRulesEvent>().map_err(|_| {
Error::bad_database("Invalid account data event in db.")
})?;
@ -315,12 +294,12 @@ pub(crate) async fn set_pushrule_enabled_route(
));
}
services().account_data.update(
None,
services().account_data.update_global(
sender_user,
GlobalAccountDataEventType::PushRules.to_string().into(),
&serde_json::to_value(account_data)
.expect("to json value always works"),
&GlobalAccountDataEventType::PushRules,
&Raw::new(&account_data)
.expect("json event serialization should always succeed")
.cast::<AnyGlobalAccountDataEvent>(),
)?;
Ok(Ra(set_pushrule_enabled::v3::Response {}))
@ -336,18 +315,14 @@ pub(crate) async fn delete_pushrule_route(
let event = services()
.account_data
.get(
None,
sender_user,
GlobalAccountDataEventType::PushRules.to_string().into(),
)?
.get_global(sender_user, &GlobalAccountDataEventType::PushRules)?
.ok_or(Error::BadRequest(
ErrorKind::NotFound,
"PushRules event not found.",
))?;
let mut account_data = serde_json::from_str::<PushRulesEvent>(event.get())
.map_err(|_| {
let mut account_data =
event.deserialize_as::<PushRulesEvent>().map_err(|_| {
Error::bad_database("Invalid account data event in db.")
})?;
@ -368,12 +343,12 @@ pub(crate) async fn delete_pushrule_route(
return Err(err);
}
services().account_data.update(
None,
services().account_data.update_global(
sender_user,
GlobalAccountDataEventType::PushRules.to_string().into(),
&serde_json::to_value(account_data)
.expect("to json value always works"),
&GlobalAccountDataEventType::PushRules,
&Raw::new(&account_data)
.expect("json event serialization should always suceed")
.cast::<AnyGlobalAccountDataEvent>(),
)?;
Ok(Ra(delete_pushrule::v3::Response {}))

View file

@ -6,8 +6,9 @@ use ruma::{
},
events::{
receipt::{ReceiptThread, ReceiptType},
RoomAccountDataEventType,
AnyRoomAccountDataEvent, RoomAccountDataEventType,
},
serde::Raw,
MilliSecondsSinceUnixEpoch,
};
@ -33,12 +34,13 @@ pub(crate) async fn set_read_marker_route(
event_id: fully_read.clone(),
},
};
services().account_data.update(
Some(&body.room_id),
services().account_data.update_room(
&body.room_id,
sender_user,
RoomAccountDataEventType::FullyRead,
&serde_json::to_value(fully_read_event)
.expect("to json value always works"),
&RoomAccountDataEventType::FullyRead,
&Raw::new(&fully_read_event)
.expect("json event serialization should always suceed")
.cast::<AnyRoomAccountDataEvent>(),
)?;
}
@ -129,12 +131,13 @@ pub(crate) async fn create_receipt_route(
event_id: body.event_id.clone(),
},
};
services().account_data.update(
Some(&body.room_id),
services().account_data.update_room(
&body.room_id,
sender_user,
RoomAccountDataEventType::FullyRead,
&serde_json::to_value(fully_read_event)
.expect("to json value always works"),
&RoomAccountDataEventType::FullyRead,
&Raw::new(&fully_read_event)
.expect("json event serialization should always succeed")
.cast::<AnyRoomAccountDataEvent>(),
)?;
}
create_receipt::v3::ReceiptType::Read => {

View file

@ -644,17 +644,8 @@ pub(crate) async fn sync_events_v4_route(
{
services()
.account_data
.changes_since(None, &sender_user, globalsince)?
.into_iter()
.filter_map(|(_, v)| {
serde_json::from_str(v.json().get())
.map_err(|_| {
Error::bad_database(
"Invalid account event in database.",
)
})
.ok()
})
.global_changes_since(&sender_user, globalsince)?
.into_values()
.collect()
} else {
Vec::new()

View file

@ -234,17 +234,8 @@ pub(crate) async fn sync_events_route(
account_data: GlobalAccountData {
events: services()
.account_data
.changes_since(None, ctx.sender_user, ctx.since)?
.into_iter()
.filter_map(|(_, v)| {
serde_json::from_str(v.json().get())
.map_err(|_| {
Error::bad_database(
"Invalid account event in database.",
)
})
.ok()
})
.global_changes_since(ctx.sender_user, ctx.since)?
.into_values()
.collect(),
},
device_lists: DeviceLists {
@ -880,17 +871,8 @@ async fn load_joined_room(
account_data: RoomAccountData {
events: services()
.account_data
.changes_since(Some(room_id), ctx.sender_user, ctx.since)?
.into_iter()
.filter_map(|(_, v)| {
serde_json::from_str(v.json().get())
.map_err(|_| {
Error::bad_database(
"Invalid account event in database.",
)
})
.ok()
})
.room_changes_since(ctx.sender_user, room_id, ctx.since)?
.into_values()
.collect(),
},
summary: RoomSummary {

View file

@ -4,8 +4,9 @@ use ruma::{
api::client::tag::{create_tag, delete_tag, get_tags},
events::{
tag::{TagEvent, TagEventContent},
RoomAccountDataEventType,
AnyRoomAccountDataEvent, RoomAccountDataEventType,
},
serde::Raw,
};
use crate::{services, Ar, Error, Ra, Result};
@ -20,10 +21,10 @@ pub(crate) async fn update_tag_route(
) -> Result<Ra<create_tag::v3::Response>> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let event = services().account_data.get(
Some(&body.room_id),
let event = services().account_data.get_room(
&body.room_id,
sender_user,
RoomAccountDataEventType::Tag,
&RoomAccountDataEventType::Tag,
)?;
let mut tags_event = event.map_or_else(
@ -35,7 +36,7 @@ pub(crate) async fn update_tag_route(
})
},
|e| {
serde_json::from_str(e.get()).map_err(|_| {
e.deserialize_as::<TagEvent>().map_err(|_| {
Error::bad_database("Invalid account data event in db.")
})
},
@ -46,11 +47,13 @@ pub(crate) async fn update_tag_route(
.tags
.insert(body.tag.clone().into(), body.tag_info.clone());
services().account_data.update(
Some(&body.room_id),
services().account_data.update_room(
&body.room_id,
sender_user,
RoomAccountDataEventType::Tag,
&serde_json::to_value(tags_event).expect("to json value always works"),
&RoomAccountDataEventType::Tag,
&Raw::new(&tags_event)
.expect("json event serialization should always suceed")
.cast::<AnyRoomAccountDataEvent>(),
)?;
Ok(Ra(create_tag::v3::Response {}))
@ -66,10 +69,10 @@ pub(crate) async fn delete_tag_route(
) -> Result<Ra<delete_tag::v3::Response>> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let event = services().account_data.get(
Some(&body.room_id),
let event = services().account_data.get_room(
&body.room_id,
sender_user,
RoomAccountDataEventType::Tag,
&RoomAccountDataEventType::Tag,
)?;
let mut tags_event = event.map_or_else(
@ -81,7 +84,7 @@ pub(crate) async fn delete_tag_route(
})
},
|e| {
serde_json::from_str(e.get()).map_err(|_| {
e.deserialize_as::<TagEvent>().map_err(|_| {
Error::bad_database("Invalid account data event in db.")
})
},
@ -89,11 +92,13 @@ pub(crate) async fn delete_tag_route(
tags_event.content.tags.remove(&body.tag.clone().into());
services().account_data.update(
Some(&body.room_id),
services().account_data.update_room(
&body.room_id,
sender_user,
RoomAccountDataEventType::Tag,
&serde_json::to_value(tags_event).expect("to json value always works"),
&RoomAccountDataEventType::Tag,
&Raw::new(&tags_event)
.expect("json value serialization should always succeed")
.cast::<AnyRoomAccountDataEvent>(),
)?;
Ok(Ra(delete_tag::v3::Response {}))
@ -109,10 +114,10 @@ pub(crate) async fn get_tags_route(
) -> Result<Ra<get_tags::v3::Response>> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let event = services().account_data.get(
Some(&body.room_id),
let event = services().account_data.get_room(
&body.room_id,
sender_user,
RoomAccountDataEventType::Tag,
&RoomAccountDataEventType::Tag,
)?;
let tags_event = event.map_or_else(
@ -124,7 +129,7 @@ pub(crate) async fn get_tags_route(
})
},
|e| {
serde_json::from_str(e.get()).map_err(|_| {
e.deserialize_as::<TagEvent>().map_err(|_| {
Error::bad_database("Invalid account data event in db.")
})
},

View file

@ -7,8 +7,12 @@ use std::{
};
use ruma::{
events::{push_rules::PushRulesEvent, GlobalAccountDataEventType},
events::{
push_rules::PushRulesEvent, AnyGlobalAccountDataEvent,
GlobalAccountDataEventType,
},
push::Ruleset,
serde::Raw,
EventId, OwnedRoomId, RoomId, UserId,
};
use tracing::{debug, error, info, info_span, warn, Instrument};
@ -859,20 +863,15 @@ impl KeyValueDatabase {
let raw_rules_list = services()
.account_data
.get(
None,
.get_global(
&user,
GlobalAccountDataEventType::PushRules
.to_string()
.into(),
&GlobalAccountDataEventType::PushRules,
)
.unwrap()
.expect("Username is invalid");
let mut account_data =
serde_json::from_str::<PushRulesEvent>(
raw_rules_list.get(),
)
let mut account_data = raw_rules_list
.deserialize_as::<PushRulesEvent>()
.unwrap();
let rules_list = &mut account_data.content.global;
@ -927,14 +926,12 @@ impl KeyValueDatabase {
}
}
services().account_data.update(
None,
services().account_data.update_global(
&user,
GlobalAccountDataEventType::PushRules
.to_string()
.into(),
&serde_json::to_value(account_data)
.expect("to json value always works"),
&GlobalAccountDataEventType::PushRules,
&Raw::new(&account_data)
.expect("json serialization should always succeed")
.cast::<AnyGlobalAccountDataEvent>(),
)?;
}
Ok(())
@ -961,20 +958,15 @@ impl KeyValueDatabase {
let raw_rules_list = services()
.account_data
.get(
None,
.get_global(
&user,
GlobalAccountDataEventType::PushRules
.to_string()
.into(),
&GlobalAccountDataEventType::PushRules,
)
.unwrap()
.expect("Username is invalid");
let mut account_data =
serde_json::from_str::<PushRulesEvent>(
raw_rules_list.get(),
)
let mut account_data = raw_rules_list
.deserialize_as::<PushRulesEvent>()
.unwrap();
let user_default_rules = Ruleset::server_default(&user);
@ -983,14 +975,12 @@ impl KeyValueDatabase {
.global
.update_with_server_default(user_default_rules);
services().account_data.update(
None,
services().account_data.update_global(
&user,
GlobalAccountDataEventType::PushRules
.to_string()
.into(),
&serde_json::to_value(account_data)
.expect("to json value always works"),
&GlobalAccountDataEventType::PushRules,
&Raw::new(&account_data)
.expect("json serialization should always succeed")
.cast::<AnyGlobalAccountDataEvent>(),
)?;
}
Ok(())

View file

@ -1,11 +1,8 @@
use std::collections::HashMap;
use ruma::{
api::client::error::ErrorKind,
events::{AnyEphemeralRoomEvent, RoomAccountDataEventType},
serde::Raw,
RoomId, UserId,
};
use ruma::{api::client::error::ErrorKind, RoomId, UserId};
use serde::Deserialize;
use serde_json::value::RawValue;
use crate::{
database::KeyValueDatabase, service, services, utils, Error, Result,
@ -16,9 +13,19 @@ impl service::account_data::Data for KeyValueDatabase {
&self,
room_id: Option<&RoomId>,
user_id: &UserId,
event_type: RoomAccountDataEventType,
data: &serde_json::Value,
event_type: &str,
data: &RawValue,
) -> Result<()> {
// Allowed because we just use this type to validate the schema, and
// don't read the fields.
#[allow(dead_code)]
#[derive(Deserialize)]
struct ExtractEventFields<'a> {
#[serde(rename = "type")]
event_type: &'a str,
content: &'a RawValue,
}
let mut prefix = room_id
.map(ToString::to_string)
.unwrap_or_default()
@ -32,23 +39,20 @@ impl service::account_data::Data for KeyValueDatabase {
roomuserdataid
.extend_from_slice(&services().globals.next_count()?.to_be_bytes());
roomuserdataid.push(0xFF);
roomuserdataid.extend_from_slice(event_type.to_string().as_bytes());
roomuserdataid.extend_from_slice(event_type.as_bytes());
let mut key = prefix;
key.extend_from_slice(event_type.to_string().as_bytes());
key.extend_from_slice(event_type.as_bytes());
if data.get("type").is_none() || data.get("content").is_none() {
if serde_json::from_str::<ExtractEventFields<'_>>(data.get()).is_err() {
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Account data doesn't have all required fields.",
));
}
self.roomuserdataid_accountdata.insert(
&roomuserdataid,
&serde_json::to_vec(&data)
.expect("to_vec always works on json values"),
)?;
self.roomuserdataid_accountdata
.insert(&roomuserdataid, data.get().as_bytes())?;
let prev = self.roomusertype_roomuserdataid.get(&key)?;
@ -66,8 +70,8 @@ impl service::account_data::Data for KeyValueDatabase {
&self,
room_id: Option<&RoomId>,
user_id: &UserId,
kind: RoomAccountDataEventType,
) -> Result<Option<Box<serde_json::value::RawValue>>> {
kind: &str,
) -> Result<Option<Box<RawValue>>> {
let mut key = room_id
.map(ToString::to_string)
.unwrap_or_default()
@ -76,7 +80,7 @@ impl service::account_data::Data for KeyValueDatabase {
key.push(0xFF);
key.extend_from_slice(user_id.as_bytes());
key.push(0xFF);
key.extend_from_slice(kind.to_string().as_bytes());
key.extend_from_slice(kind.as_bytes());
self.roomusertype_roomuserdataid
.get(&key)?
@ -96,8 +100,7 @@ impl service::account_data::Data for KeyValueDatabase {
room_id: Option<&RoomId>,
user_id: &UserId,
since: u64,
) -> Result<HashMap<RoomAccountDataEventType, Raw<AnyEphemeralRoomEvent>>>
{
) -> Result<HashMap<String, Box<RawValue>>> {
let mut userdata = HashMap::new();
let mut prefix = room_id
@ -119,28 +122,23 @@ impl service::account_data::Data for KeyValueDatabase {
.take_while(move |(k, _)| k.starts_with(&prefix))
.map(|(k, v)| {
Ok::<_, Error>((
RoomAccountDataEventType::from(
utils::string_from_bytes(
k.rsplit(|&b| b == 0xFF).next().ok_or_else(
|| {
Error::bad_database(
"RoomUserData ID in db is invalid.",
)
},
)?,
)
.map_err(|_| {
k.rsplit(|&b| b == 0xFF).next().ok_or_else(|| {
Error::bad_database(
"RoomUserData ID in db is invalid.",
)
})?,
),
serde_json::from_slice::<Raw<AnyEphemeralRoomEvent>>(&v)
)
.map_err(|_| {
Error::bad_database("RoomUserData ID in db is invalid.")
})?,
serde_json::from_slice::<Box<RawValue>>(&v).map_err(
|_| {
Error::bad_database(
"Database contains invalid account data.",
)
})?,
},
)?,
))
})
{

View file

@ -1,7 +1,10 @@
use std::collections::HashMap;
use ruma::{
events::{AnyEphemeralRoomEvent, RoomAccountDataEventType},
events::{
AnyGlobalAccountDataEvent, AnyRoomAccountDataEvent,
GlobalAccountDataEventType, RoomAccountDataEventType,
},
serde::Raw,
RoomId, UserId,
};
@ -23,42 +26,104 @@ impl Service {
}
}
/// Places one event in the account data of the user and removes the
/// Places one event in the global account data of the user and removes the
/// previous entry.
#[tracing::instrument(skip(self, room_id, user_id, event_type, data))]
pub(crate) fn update(
#[tracing::instrument(skip(self, user_id, event))]
pub(crate) fn update_global(
&self,
room_id: Option<&RoomId>,
user_id: &UserId,
event_type: RoomAccountDataEventType,
data: &serde_json::Value,
event_type: &GlobalAccountDataEventType,
event: &Raw<AnyGlobalAccountDataEvent>,
) -> Result<()> {
self.db.update(room_id, user_id, event_type, data)
self.db.update(None, user_id, &event_type.to_string(), event.json())
}
/// Searches the account data for a specific kind.
#[tracing::instrument(skip(self, room_id, user_id, event_type))]
pub(crate) fn get(
/// Places one event in the room account data of the user and removes the
/// previous entry for that room.
#[tracing::instrument(skip(self, room_id, user_id, event))]
pub(crate) fn update_room(
&self,
room_id: Option<&RoomId>,
room_id: &RoomId,
user_id: &UserId,
event_type: RoomAccountDataEventType,
) -> Result<Option<Box<serde_json::value::RawValue>>> {
self.db.get(room_id, user_id, event_type)
event_type: &RoomAccountDataEventType,
event: &Raw<AnyRoomAccountDataEvent>,
) -> Result<()> {
self.db.update(
Some(room_id),
user_id,
&event_type.to_string(),
event.json(),
)
}
/// Returns all changes to the account data that happened after `since`.
/// Searches the global account data for a specific kind.
#[tracing::instrument(skip(self, user_id, event_type))]
pub(crate) fn get_global(
&self,
user_id: &UserId,
event_type: &GlobalAccountDataEventType,
) -> Result<Option<Raw<AnyGlobalAccountDataEvent>>> {
Ok(self
.db
.get(None, user_id, &event_type.to_string())?
.map(Raw::from_json))
}
/// Searches the room account data for a specific kind.
#[tracing::instrument(skip(self, room_id, user_id, event_type))]
pub(crate) fn get_room(
&self,
room_id: &RoomId,
user_id: &UserId,
event_type: &RoomAccountDataEventType,
) -> Result<Option<Raw<AnyRoomAccountDataEvent>>> {
Ok(self
.db
.get(Some(room_id), user_id, &event_type.to_string())?
.map(Raw::from_json))
}
/// Returns all changes to global account data that happened after `since`.
///
/// When there have been multiple changes to the same event type, returned
/// map contains the most recent value.
#[tracing::instrument(skip(self, user_id, since))]
pub(crate) fn global_changes_since(
&self,
user_id: &UserId,
since: u64,
) -> Result<
HashMap<GlobalAccountDataEventType, Raw<AnyGlobalAccountDataEvent>>,
> {
Ok(self
.db
.changes_since(None, user_id, since)?
.into_iter()
.map(|(event_type, event)| {
(event_type.into(), Raw::from_json(event))
})
.collect())
}
/// Returns all changes to room account data that happened after `since`.
///
/// When there have been multiple changes to the same event type, returned
/// map contains the most recent value.
#[tracing::instrument(skip(self, room_id, user_id, since))]
pub(crate) fn changes_since(
pub(crate) fn room_changes_since(
&self,
room_id: Option<&RoomId>,
user_id: &UserId,
room_id: &RoomId,
since: u64,
) -> Result<HashMap<RoomAccountDataEventType, Raw<AnyEphemeralRoomEvent>>>
) -> Result<HashMap<RoomAccountDataEventType, Raw<AnyRoomAccountDataEvent>>>
{
self.db.changes_since(room_id, user_id, since)
Ok(self
.db
.changes_since(Some(room_id), user_id, since)?
.into_iter()
.map(|(event_type, event)| {
(event_type.into(), Raw::from_json(event))
})
.collect())
}
}

View file

@ -1,40 +1,51 @@
use std::collections::HashMap;
use ruma::{
events::{AnyEphemeralRoomEvent, RoomAccountDataEventType},
serde::Raw,
RoomId, UserId,
};
use ruma::{RoomId, UserId};
use serde_json::value::RawValue;
use crate::Result;
/// Unlike the service-level API, the database API for account data does not
/// distinguish between global and room events. Because there are no ruma types
/// that cover both, we use strings for the event types and raw json values for
/// the contents.
pub(crate) trait Data: Send + Sync {
/// Places one event in the account data of the user and removes the
/// previous entry.
///
/// If `room_id` is `None`, set a global event, otherwise set a room event
/// in the specified room.
fn update(
&self,
room_id: Option<&RoomId>,
user_id: &UserId,
event_type: RoomAccountDataEventType,
data: &serde_json::Value,
event_type: &str,
data: &RawValue,
) -> Result<()>;
/// Searches the account data for a specific kind.
///
/// If `room_id` is `None`, search global events, otherwise search room
/// events in the specified room.
fn get(
&self,
room_id: Option<&RoomId>,
user_id: &UserId,
kind: RoomAccountDataEventType,
) -> Result<Option<Box<serde_json::value::RawValue>>>;
kind: &str,
) -> Result<Option<Box<RawValue>>>;
/// Returns all changes to the account data that happened after `since`.
///
/// When there have been multiple changes to the same event type, returned
/// map contains the most recent value.
/// If `room_id` is `None`, read global events, otherwise read room events
/// in the specified room.
///
/// Returned as a map from event type to event objects (containing both a
/// `type` and a `content` key). When there have been multiple changes to
/// the same event type, returned map contains the most recent value.
fn changes_since(
&self,
room_id: Option<&RoomId>,
user_id: &UserId,
since: u64,
) -> Result<HashMap<RoomAccountDataEventType, Raw<AnyEphemeralRoomEvent>>>;
) -> Result<HashMap<String, Box<RawValue>>>;
}

View file

@ -20,8 +20,9 @@ use ruma::{
power_levels::RoomPowerLevelsEventContent,
topic::RoomTopicEventContent,
},
TimelineEventType,
AnyGlobalAccountDataEvent, TimelineEventType,
},
serde::Raw,
signatures::verify_json,
EventId, MilliSecondsSinceUnixEpoch, OwnedMxcUri, OwnedRoomId,
OwnedServerName, RoomId, RoomVersionId, ServerName, UserId,
@ -770,20 +771,18 @@ impl Service {
.set_displayname(&user_id, Some(displayname))?;
// Initial account data
services().account_data.update(
None,
services().account_data.update_global(
&user_id,
ruma::events::GlobalAccountDataEventType::PushRules
.to_string()
.into(),
&serde_json::to_value(PushRulesEvent {
&ruma::events::GlobalAccountDataEventType::PushRules,
&Raw::new(&PushRulesEvent {
content: PushRulesEventContent {
global: ruma::push::Ruleset::server_default(
&user_id,
),
},
})
.expect("to json value always works"),
.expect("json serialization should always succeed")
.cast::<AnyGlobalAccountDataEvent>(),
)?;
// we dont add a device since we're not the user, just the

View file

@ -18,11 +18,11 @@ use ruma::{
api::federation::discovery::ServerSigningKeys,
events::{
push_rules::PushRulesEventContent,
room::message::RoomMessageEventContent, GlobalAccountDataEvent,
GlobalAccountDataEventType,
room::message::RoomMessageEventContent, AnyGlobalAccountDataEvent,
GlobalAccountDataEvent, GlobalAccountDataEventType,
},
push::Ruleset,
serde::Base64,
serde::{Base64, Raw},
DeviceId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomAliasId,
OwnedRoomId, OwnedServerName, OwnedUserId, RoomAliasId, RoomVersionId,
ServerName, UInt, UserId,
@ -492,16 +492,16 @@ impl Service {
None => (Ruleset::new(), Ok(false)),
};
services().account_data.update(
None,
services().account_data.update_global(
admin_bot,
GlobalAccountDataEventType::PushRules.to_string().into(),
&serde_json::to_value(&GlobalAccountDataEvent {
&GlobalAccountDataEventType::PushRules,
&Raw::new(&GlobalAccountDataEvent {
content: PushRulesEventContent {
global: ruleset,
},
})
.expect("to json value always works"),
.expect("json serialization should always succeed")
.cast::<AnyGlobalAccountDataEvent>(),
)?;
res

View file

@ -6,8 +6,8 @@ use std::{
use ruma::{
events::{
ignored_user_list::IgnoredUserListEvent, room::member::MembershipState,
AnyStrippedStateEvent, AnySyncStateEvent, GlobalAccountDataEventType,
RoomAccountDataEventType,
AnyGlobalAccountDataEvent, AnyStrippedStateEvent, AnySyncStateEvent,
GlobalAccountDataEventType, RoomAccountDataEventType,
},
serde::Raw,
OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, ServerName, UserId,
@ -92,29 +92,21 @@ impl Service {
self.db.mark_as_joined(user_id, room_id)?;
}
MembershipState::Invite => {
let event_kind = RoomAccountDataEventType::from(
GlobalAccountDataEventType::IgnoredUserList.to_string(),
);
// We want to know if the sender is ignored by the receiver
let is_ignored = services()
.account_data
.get(
// Ignored users are in global account data
None,
.get_global(
// Receiver
user_id,
event_kind.clone(),
&GlobalAccountDataEventType::IgnoredUserList,
)?
.map(|event| {
serde_json::from_str::<IgnoredUserListEvent>(
event.get(),
)
event.deserialize_as::<IgnoredUserListEvent>()
.map_err(|error| {
warn!(
%error,
%event_kind,
"Invalid account data event",
"Invalid m.ignored_user_list account data event",
);
Error::BadDatabase("Invalid account data event.")
})
@ -200,20 +192,18 @@ impl Service {
from_room_id: &RoomId,
to_room_id: &RoomId,
) -> Result<()> {
let Some(event) = services().account_data.get(
Some(from_room_id),
let Some(event) = services().account_data.get_room(
from_room_id,
user_id,
RoomAccountDataEventType::Tag,
&RoomAccountDataEventType::Tag,
)?
else {
return Ok(());
};
let event = serde_json::from_str::<serde_json::Value>(event.get())
.expect("RawValue -> Value should always succeed");
if let Err(error) = services().account_data.update(
Some(to_room_id),
if let Err(error) = services().account_data.update_room(
to_room_id,
user_id,
RoomAccountDataEventType::Tag,
&RoomAccountDataEventType::Tag,
&event,
) {
warn!(%error, "error writing m.tag account data to upgraded room");
@ -231,16 +221,15 @@ impl Service {
from_room_id: &RoomId,
to_room_id: &RoomId,
) -> Result<()> {
let event_kind = RoomAccountDataEventType::from(
GlobalAccountDataEventType::Direct.to_string(),
);
let Some(event) =
services().account_data.get(None, user_id, event_kind.clone())?
let Some(event) = services()
.account_data
.get_global(user_id, &GlobalAccountDataEventType::Direct)?
else {
return Ok(());
};
let mut event = serde_json::from_str::<serde_json::Value>(event.get())
let mut event = event
.deserialize_as::<serde_json::Value>()
.expect("RawValue -> Value should always succeed");
// As a server, we should try not to assume anything about the schema
@ -285,13 +274,14 @@ impl Service {
}
if event_updated {
if let Err(error) = services().account_data.update(
None,
if let Err(error) = services().account_data.update_global(
user_id,
event_kind.clone(),
&event,
&GlobalAccountDataEventType::Direct,
&Raw::new(&event)
.expect("json serialization should always succeed")
.cast::<AnyGlobalAccountDataEvent>(),
) {
warn!(%event_kind, %error, "error writing account data event after upgrading room");
warn!(%error, "error writing m.direct account data event after upgrading room");
}
}
Ok(())

View file

@ -417,19 +417,11 @@ impl Service {
let rules_for_user = services()
.account_data
.get(
None,
user,
GlobalAccountDataEventType::PushRules.to_string().into(),
)?
.get_global(user, &GlobalAccountDataEventType::PushRules)?
.map(|event| {
serde_json::from_str::<PushRulesEvent>(event.get()).map_err(
|_| {
Error::bad_database(
"Invalid push rules event in db.",
)
},
)
event.deserialize_as::<PushRulesEvent>().map_err(|_| {
Error::bad_database("Invalid push rules event in db.")
})
})
.transpose()?
.map_or_else(

View file

@ -857,15 +857,9 @@ async fn handle_push_event(
let rules_for_user = services()
.account_data
.get(
None,
userid,
GlobalAccountDataEventType::PushRules.to_string().into(),
)
.get_global(userid, &GlobalAccountDataEventType::PushRules)
.unwrap_or_default()
.and_then(|event| {
serde_json::from_str::<PushRulesEvent>(event.get()).ok()
})
.and_then(|event| event.deserialize_as::<PushRulesEvent>().ok())
.map_or_else(
|| push::Ruleset::server_default(userid),
|ev: PushRulesEvent| ev.content.global,