Skip to main content

tuwunel_api/client/admin/misc/
fetch_event.rs

1use 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
36/// Sets Synapse's `io.element.synapse.soft_failed` marker inside `unsigned`,
37/// creating the `unsigned` object when the event lacks one.
38fn 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}