tuwunel_service/rooms/spaces/
federation.rs1use futures::{StreamExt, pin_mut, stream::FuturesUnordered};
2use ruma::{
3 OwnedServerName, RoomId,
4 api::federation::space::{
5 SpaceHierarchyParentSummary as ParentSummary,
6 get_hierarchy::v1::{Request, Response},
7 },
8 room::RoomType,
9};
10use tuwunel_core::{Err, Result, debug, implement, trace};
11
12use super::{
13 Accessibility,
14 Accessibility::{Accessible, Inaccessible},
15 Identifier,
16};
17
18#[implement(super::Service)]
20#[tracing::instrument(
21 name = "federation",
22 level = "debug",
23 err(level = "debug"),
24 ret(level = "trace"),
25 skip(self)
26)]
27pub(super) async fn get_summary_and_children_federation(
28 &self,
29 current_room: &RoomId,
30 sender: &Identifier<'_>,
31 via: &[OwnedServerName],
32) -> Result<Accessibility> {
33 let request = Request {
34 room_id: current_room.to_owned(),
35 suggested_only: false,
36 };
37
38 let requests: FuturesUnordered<_> = via
39 .iter()
40 .map(|server| {
41 self.services
42 .federation
43 .execute(server, request.clone())
44 })
45 .collect();
46
47 pin_mut!(requests);
48 debug!(
49 ?current_room,
50 ?sender,
51 ?via,
52 requests = requests.len(),
53 "waiting for federation response"
54 );
55
56 let Some(Ok(Response { room, children, inaccessible_children })) = requests.next().await
57 else {
58 self.cache_put(current_room, None);
59 return Err!(Request(NotFound("Space room not found over federation.")));
60 };
61
62 trace!(
63 ?current_room,
64 ?sender,
65 ?room,
66 ?children,
67 ?inaccessible_children,
68 "federation response"
69 );
70
71 for room_id in &inaccessible_children {
72 self.cache_put(room_id, None);
73 }
74
75 for summary in children
76 .into_iter()
77 .filter(|child| child.room_type.ne(&Some(RoomType::Space)))
78 {
79 let room_id = summary.room_id.clone();
80 let summary = ParentSummary {
81 summary,
82 children_state: Default::default(),
83 };
84
85 self.cache_put(&room_id, Some(&summary));
86 }
87
88 self.cache_put(current_room, Some(&room));
89
90 self.is_accessible_child(current_room, &room.summary.join_rule.clone(), sender)
91 .await
92 .then(|| Ok(Accessible(room)))
93 .unwrap_or(Ok(Inaccessible))
94}