arminius_lib_sync/syncv4/execute/fetch/
rooms.rs1use std::collections::{BTreeMap, HashMap};
8
9use arminius_lib_component::error::{InternalServerError, ResultExt as _};
10use arminius_lib_db_room::EventQueries as _;
11use arminius_lib_db_sync::syncv4::{SyncedRoomTimeline, TransactedSyncV4Queries};
12use arminius_lib_events::{ContextualizedFields as _, RoomEvent, Verified};
13use itertools::{Either, Itertools as _};
14use ruma::{
15 OwnedRoomId, UInt,
16 api::client::sync::sync_events,
17 events::{AnySyncStateEvent, AnySyncTimelineEvent},
18 serde::Raw,
19};
20
21use crate::syncv4::{connect, error::SyncV4Error, plan};
22
23pub(super) async fn fetch_rooms<T: TransactedSyncV4Queries>(
25 transaction: &mut T,
26 connection: &connect::ResolvedConnection,
27 rooms_bump_stamps: &HashMap<OwnedRoomId, usize>,
28 rooms_to_sync: &HashMap<OwnedRoomId, plan::RoomToSync>,
29) -> Result<BTreeMap<OwnedRoomId, sync_events::v5::response::Room>, SyncV4Error> {
30 let last_live_stream_id = connection.prev_events_stream_id().unwrap_or(0);
31 let to_events_stream_id = connection.next_token.event_stream_position;
32
33 let (joined_rooms_params, never_joined_rooms_params): (
34 HashMap<OwnedRoomId, (i64, i64)>,
35 HashMap<OwnedRoomId, bool>,
36 ) = rooms_to_sync.iter().partition_map(|(room_id, room_to_sync)| match room_to_sync {
37 plan::RoomToSync::Joined { from_stream_id: sync_room_from, room_config, .. } => {
38 Either::Left((room_id.clone(), (*sync_room_from, room_config.timeline_limit)))
39 }
40 plan::RoomToSync::NeverJoined { initial } => Either::Right((room_id.to_owned(), *initial)),
41 });
42
43 let joined_rooms_timelines = transaction
44 .fetch_syncv4_joined_rooms_timelines(
45 &connection.key.user_id,
46 &joined_rooms_params,
47 last_live_stream_id,
48 to_events_stream_id,
49 )
50 .await?;
51
52 let mut joined_rooms_required_states = HashMap::new();
55
56 for (room_id, from_stream_id, required_state) in
58 rooms_to_sync.iter().filter_map(|(room_id, room_to_sync)| match room_to_sync {
59 plan::RoomToSync::Joined { from_stream_id, room_config, .. } => {
60 Some((room_id, from_stream_id, &room_config.required_state))
61 }
62 _ => None,
63 }) {
64 let required_states = transaction
65 .fetch_rooms_required_states(
66 room_id,
67 required_state,
68 *from_stream_id,
70 to_events_stream_id,
71 )
72 .await?;
73 joined_rooms_required_states.insert(room_id.clone(), required_states);
74 }
75
76 let mut rooms = compute_joined_rooms(
77 transaction,
78 connection,
79 joined_rooms_timelines,
80 joined_rooms_required_states,
81 rooms_bump_stamps,
82 rooms_to_sync,
83 )
84 .await?;
85
86 let mut never_joined_rooms =
87 compute_never_joined_rooms(transaction, never_joined_rooms_params, rooms_bump_stamps)
88 .await?;
89
90 rooms.append(&mut never_joined_rooms);
91
92 Ok(rooms)
93}
94
95async fn compute_joined_rooms<T: TransactedSyncV4Queries>(
99 transaction: &mut T,
100 connection: &connect::ResolvedConnection,
101 rooms_timelines: HashMap<OwnedRoomId, SyncedRoomTimeline>,
102 rooms_required_states: HashMap<OwnedRoomId, Vec<RoomEvent<Verified>>>,
103 rooms_bump_stamps: &HashMap<OwnedRoomId, usize>,
104 rooms_to_sync: &HashMap<OwnedRoomId, plan::RoomToSync>,
105) -> Result<BTreeMap<OwnedRoomId, sync_events::v5::response::Room>, SyncV4Error> {
106 let mut result = BTreeMap::new();
107
108 for (room_id, room_timeline) in rooms_timelines {
109 let mut room = sync_events::v5::response::Room::new();
110
111 room.initial = Some(
112 rooms_to_sync
113 .get(&room_id)
114 .and_then(|room_to_sync| match room_to_sync {
115 plan::RoomToSync::Joined { initial, .. } => Some(*initial),
116 plan::RoomToSync::NeverJoined { .. } => None,
117 })
118 .unrecoverable_error(
119 "Fetched a timeline for a room that is not a joined room of the plan",
120 )?,
121 );
122
123 room.bump_stamp = Some(
124 UInt::try_from(
125 *rooms_bump_stamps.get(&room_id).unrecoverable_error("Missing room bump stamp")?,
126 )
127 .unrecoverable_error("Failed to create UInt")?,
128 );
129
130 let (joined_count, invited_count) =
133 transaction.fetch_room_membership_counts(&room_id).await?;
134
135 room.joined_count =
136 Some(UInt::try_from(joined_count).unrecoverable_error("Failed to create UInt")?);
137 room.invited_count =
138 Some(UInt::try_from(invited_count).unrecoverable_error("Failed to create UInt")?);
139
140 room.limited = room_timeline.limited;
142
143 room.num_live = Some(
145 UInt::try_from(room_timeline.num_live).unrecoverable_error("Failed to create UInt")?,
146 );
147
148 for event in room_timeline.timeline {
151 let client_context = transaction
152 .fetch_client_context(
153 event.room_id(),
154 event.event_id(),
155 &connection.key.user_id,
156 &connection.key.device_id,
157 )
158 .await?;
159
160 room.timeline.push(Raw::<AnySyncTimelineEvent>::from_json(
161 event.to_client_event(client_context),
162 ));
163 }
164
165 result.insert(room_id, room);
166 }
167
168 for (room_id, room_required_state) in rooms_required_states {
170 for event in room_required_state {
171 let client_context = transaction
172 .fetch_client_context(
173 event.room_id(),
174 event.event_id(),
175 &connection.key.user_id,
176 &connection.key.device_id,
177 )
178 .await?;
179
180 let room = result.get_mut(&room_id).unrecoverable_error(
181 "Fetching required states for a room without fetching its timeline ?",
182 )?;
183
184 room.required_state
185 .push(Raw::<AnySyncStateEvent>::from_json(event.to_client_event(client_context)));
186 }
187 }
188
189 Ok(result)
190}
191
192async fn compute_never_joined_rooms<T: TransactedSyncV4Queries>(
194 transaction: &mut T,
195 never_joined_rooms: HashMap<OwnedRoomId, bool>,
196 rooms_bump_stamps: &HashMap<OwnedRoomId, usize>,
197) -> Result<BTreeMap<OwnedRoomId, sync_events::v5::response::Room>, InternalServerError> {
198 let never_joined_room_ids: Vec<OwnedRoomId> = never_joined_rooms.keys().cloned().collect();
199
200 transaction
201 .fetch_rooms_stripped_state(&never_joined_room_ids)
202 .await?
203 .into_iter()
204 .map(|(room_id, invite_state)| {
205 let mut room = sync_events::v5::response::Room::new();
206
207 room.bump_stamp = Some(
208 UInt::try_from(
209 *rooms_bump_stamps
210 .get(&room_id)
211 .unrecoverable_error("Missing room bump stamp")?,
212 )
213 .unrecoverable_error("Failed to create UInt")?,
214 );
215 room.invite_state = Some(invite_state);
216 room.initial = Some(*never_joined_rooms.get(&room_id).unrecoverable_error(
217 "Fetched a stripped state for a room that was not in the plan",
218 )?);
219
220 Ok::<_, InternalServerError>((room_id, room))
221 })
222 .collect::<Result<BTreeMap<_, _>, _>>()
223}