arminius_lib_db_stripped_state/
lib.rs1use arminius_lib_db::{Database, DatabaseError, ResultExt as _};
8use arminius_lib_model::types::StreamId;
9use ruma::{
10 OwnedRoomId, OwnedUserId, UserId,
11 api::federation::membership::RawStrippedState,
12 events::{AnyStrippedStateEvent, StateEventType},
13 serde::Raw,
14};
15
16#[derive(Debug, serde::Deserialize)]
18pub struct StrippedStateEvent {
19 pub content: serde_json::Value,
21 pub sender: OwnedUserId,
23 pub state_key: String,
25 #[serde(rename = "type")]
27 pub event_type: StateEventType,
28}
29
30impl TryFrom<RawStrippedState> for StrippedStateEvent {
31 type Error = serde_json::Error;
32
33 fn try_from(event: RawStrippedState) -> Result<Self, Self::Error> {
34 match event {
35 #[expect(
36 deprecated,
37 reason = "The deprecation specifically states that we should not send, but still accept stripped state events received over federation"
38 )]
39 RawStrippedState::Stripped(raw) => {
40 let event = raw.deserialize()?;
41 Ok(StrippedStateEvent {
42 content: serde_json::to_value(event.content())?,
43 sender: event.sender().to_owned(),
44 state_key: event.state_key().to_owned(),
45 event_type: event.event_type(),
46 })
47 }
48 RawStrippedState::Pdu(raw_value) => Ok(serde_json::from_str(raw_value.get())?),
49 _ => {
50 unreachable!("There are only two variants of this enum")
51 }
52 }
53 }
54}
55
56impl From<StrippedStateEvent> for Raw<AnyStrippedStateEvent> {
57 #[expect(
58 clippy::expect_used,
59 reason = "Because we define this type ourself, and it consists of values and strings, we don't expect serialization failures"
60 )]
61 fn from(event: StrippedStateEvent) -> Self {
62 Raw::new(&serde_json::json!({
63 "content": event.content,
64 "sender": event.sender,
65 "state_key": event.state_key,
66 "type": event.event_type,
67 }))
68 .expect("We know that these types cannot fail to serialize")
69 .cast_unchecked()
70 }
71}
72
73#[derive(Debug)]
76pub struct FederatedInvite {
77 pub invited_user: OwnedUserId,
79 pub room_id: OwnedRoomId,
81 pub state_events: Vec<StrippedStateEvent>,
83}
84
85#[rust_analyzer::prefer_underscore_import]
88pub trait StrippedStateQueries: Database {
89 #[tracing::instrument(skip(self, invite))]
91 async fn insert_federated_invite(
92 &mut self,
93 invite: &FederatedInvite,
94 ) -> Result<(), DatabaseError> {
95 let mut nids = Vec::new();
96 for event in &invite.state_events {
97 let nid = sqlx::query_scalar!(
98 "INSERT INTO stripped_state_events (content, sender, state_key, type) VALUES ($1, $2, $3, $4) RETURNING nid;",
99 event.content,
100 event.sender.as_str(),
101 event.state_key.as_str(),
102 &event.event_type.to_string(),
103 ).fetch_one(self.connection().await?).await?;
104 nids.push(nid);
105 }
106 sqlx::query!(
107 "INSERT INTO federated_invites (user_nid, room_id, state_events) VALUES ((SELECT nid FROM users WHERE user_id = $1), $2, $3)",
108 invite.invited_user.as_str(),
109 invite.room_id.as_str(),
110 &nids
111 )
112 .execute(self.connection().await?)
113 .await?;
114 Ok(())
115 }
116
117 #[tracing::instrument(skip(self))]
119 async fn fetch_federated_invites(
120 &mut self,
121 user: &UserId,
122 from_stream_id: StreamId,
123 ) -> Result<Vec<(StreamId, FederatedInvite)>, DatabaseError> {
124 let rooms = sqlx::query!(
125 r#"
126 SELECT
127 stream_id,
128 room_id,
129 state_events
130 FROM
131 federated_invites
132 JOIN users ON federated_invites.user_nid = users.nid
133 WHERE
134 user_id = $1
135 AND stream_id >= $2
136 "#,
137 user.as_str(),
138 from_stream_id,
139 )
140 .fetch_all(self.connection().await?)
141 .await?;
142 let mut invites = Vec::new();
143 for room in rooms {
144 let state_events = sqlx::query!(
145 "SELECT * FROM stripped_state_events WHERE nid = ANY($1)",
146 room.state_events.as_deref()
147 )
148 .fetch_all(self.connection().await?)
149 .await?
150 .into_iter()
151 .map(|row| -> Result<StrippedStateEvent, DatabaseError> {
152 Ok(StrippedStateEvent {
153 content: row.content,
154 sender: row.sender.parse().invalid_data("Invalid user id in database")?,
155 state_key: row.state_key,
156 event_type: StateEventType::from(row.r#type),
157 })
158 })
159 .collect::<Result<Vec<_>, _>>()?;
160 invites.push((
161 room.stream_id,
162 FederatedInvite {
163 invited_user: user.to_owned(),
164 room_id: room.room_id.parse().invalid_data("Invalid UserID in database")?,
165 state_events,
166 },
167 ));
168 }
169 Ok(invites)
170 }
171
172 #[tracing::instrument(skip(self, invite))]
174 async fn insert_federated_knock(
175 &mut self,
176 invite: &FederatedInvite,
177 ) -> Result<(), DatabaseError> {
178 let mut nids = Vec::new();
179 for event in &invite.state_events {
180 let nid = sqlx::query_scalar!(
181 "INSERT INTO stripped_state_events (content, sender, state_key, type) VALUES ($1, $2, $3, $4) RETURNING nid;",
182 event.content,
183 event.sender.as_str(),
184 event.state_key.as_str(),
185 &event.event_type.to_string(),
186 ).fetch_one(self.connection().await?).await?;
187 nids.push(nid);
188 }
189 sqlx::query!(
190 "INSERT INTO federated_knocks (user_nid, room_id, state_events) VALUES ((SELECT nid FROM users WHERE user_id = $1), $2, $3)",
191 invite.invited_user.as_str(),
192 invite.room_id.as_str(),
193 &nids
194 )
195 .execute(self.connection().await?)
196 .await?;
197 Ok(())
198 }
199
200 #[tracing::instrument(skip(self))]
202 async fn fetch_federated_knocks(
203 &mut self,
204 user: &UserId,
205 from_stream_id: StreamId,
206 ) -> Result<Vec<(StreamId, FederatedInvite)>, DatabaseError> {
207 let rooms = sqlx::query!(
208 r#"
209 SELECT
210 stream_id,
211 room_id,
212 state_events
213 FROM
214 federated_knocks
215 JOIN users ON federated_knocks.user_nid = users.nid
216 WHERE
217 user_id = $1
218 AND stream_id >= $2
219 "#,
220 user.as_str(),
221 from_stream_id,
222 )
223 .fetch_all(self.connection().await?)
224 .await?;
225 let mut knocks = Vec::new();
226 for room in rooms {
227 let state_events = sqlx::query!(
228 "SELECT * FROM stripped_state_events WHERE nid = ANY($1)",
229 room.state_events.as_deref()
230 )
231 .fetch_all(self.connection().await?)
232 .await?
233 .into_iter()
234 .map(|row| -> Result<StrippedStateEvent, DatabaseError> {
235 Ok(StrippedStateEvent {
236 content: row.content,
237 sender: row.sender.parse().invalid_data("Invalid user id in database")?,
238 state_key: row.state_key,
239 event_type: StateEventType::from(row.r#type),
240 })
241 })
242 .collect::<Result<Vec<_>, _>>()?;
243 knocks.push((
244 room.stream_id,
245 FederatedInvite {
246 invited_user: user.to_owned(),
247 room_id: room.room_id.parse().invalid_data("Invalid UserID in database")?,
248 state_events,
249 },
250 ));
251 }
252 Ok(knocks)
253 }
254}
255
256impl<T> StrippedStateQueries for T where T: Database {}