tuwunel_api/client/admin/misc/
fetch_event.rs1use axum::extract::State;
2use ruma::{CanonicalJsonObject, CanonicalJsonValue, serde::Raw};
3use synapse_admin_api::events::fetch_event::v1 as fetch_event;
4use tuwunel_core::{Result, err};
5
6use crate::{Ruma, client::admin::require_admin};
7
8const SOFT_FAILED: &str = "io.element.synapse.soft_failed";
9
10pub(crate) async fn admin_fetch_event_route(
11 State(services): State<crate::State>,
12 body: Ruma<fetch_event::Request>,
13) -> Result<fetch_event::Response> {
14 require_admin(&services, body.sender_user()).await?;
15
16 let mut event = services
17 .timeline
18 .get_pdu_json(&body.event_id)
19 .await
20 .map_err(|_| err!(Request(NotFound("Event not found"))))?;
21
22 if services
23 .pdu_metadata
24 .is_event_soft_failed(&body.event_id)
25 .await
26 {
27 mark_soft_failed(&mut event);
28 }
29
30 let event = serde_json::value::to_raw_value(&event)
31 .map_err(|e| err!("Failed to serialize event: {e}"))?;
32
33 Ok(fetch_event::Response::new(Raw::from_json(event)))
34}
35
36fn mark_soft_failed(event: &mut CanonicalJsonObject) {
39 let Some(CanonicalJsonValue::Object(unsigned)) = event.get_mut("unsigned") else {
40 let unsigned = [(SOFT_FAILED.into(), CanonicalJsonValue::Bool(true))].into();
41 event.insert("unsigned".into(), CanonicalJsonValue::Object(unsigned));
42 return;
43 };
44
45 unsigned.insert(SOFT_FAILED.into(), CanonicalJsonValue::Bool(true));
46}
47
48#[cfg(test)]
49mod tests {
50 use ruma::{CanonicalJsonObject, CanonicalJsonValue};
51
52 use super::{SOFT_FAILED, mark_soft_failed};
53
54 fn soft_failed(event: &CanonicalJsonObject) -> Option<bool> {
55 event
56 .get("unsigned")?
57 .as_object()?
58 .get(SOFT_FAILED)?
59 .as_bool()
60 }
61
62 #[test]
63 fn marks_event_without_unsigned() {
64 let mut event = CanonicalJsonObject::new();
65
66 mark_soft_failed(&mut event);
67
68 assert_eq!(soft_failed(&event), Some(true));
69 }
70
71 #[test]
72 fn marks_event_with_existing_unsigned() {
73 let membership = CanonicalJsonValue::String("leave".into());
74 let unsigned = [("prev_sender".into(), membership)].into();
75 let mut event = [("unsigned".into(), CanonicalJsonValue::Object(unsigned))].into();
76
77 mark_soft_failed(&mut event);
78
79 assert_eq!(soft_failed(&event), Some(true));
80 assert!(
81 event
82 .get("unsigned")
83 .and_then(CanonicalJsonValue::as_object)
84 .is_some_and(|u| u.contains_key("prev_sender"))
85 );
86 }
87}