Fix redacted aliases event deserialization

This commit is contained in:
Devin R 2020-07-13 17:11:31 -04:00 committed by Jonas Platte
parent 3f951e931b
commit 6f69a6fe76
No known key found for this signature in database
GPG Key ID: CC154DE0E30B7C67
6 changed files with 92 additions and 32 deletions

View File

@ -151,11 +151,18 @@ fn expand_deserialize_event(
if name == "content" { if name == "content" {
if is_generic && ident.to_string().contains("Redacted") { if is_generic && ident.to_string().contains("Redacted") {
quote! { quote! {
let content = if !C::has_deserialize_fields() { let content = match C::has_deserialize_fields() {
::ruma_events::HasDeserializeFields::False => {
C::empty(&event_type).map_err(A::Error::custom)? C::empty(&event_type).map_err(A::Error::custom)?
} else { },
::ruma_events::HasDeserializeFields::True => {
let json = content.ok_or_else(|| ::serde::de::Error::missing_field("content"))?; let json = content.ok_or_else(|| ::serde::de::Error::missing_field("content"))?;
C::from_parts(&event_type, json).map_err(A::Error::custom)? C::from_parts(&event_type, json).map_err(A::Error::custom)?
},
::ruma_events::HasDeserializeFields::Optional => {
let json = content.unwrap_or(::serde_json::value::RawValue::from_string("{}".to_string()).unwrap());
C::from_parts(&event_type, json).map_err(A::Error::custom)?
},
}; };
} }
} else if is_generic { } else if is_generic {

View File

@ -135,7 +135,13 @@ pub fn expand_event_content(input: &DeriveInput, emit_redacted: bool) -> syn::Re
) )
}; };
let has_fields = if kept_redacted_fields.is_empty() { let has_deserialize_fields = if kept_redacted_fields.is_empty() {
quote! { ::ruma_events::HasDeserializeFields::False }
} else {
quote! { ::ruma_events::HasDeserializeFields::True }
};
let has_serialize_fields = if kept_redacted_fields.is_empty() {
quote! { false } quote! { false }
} else { } else {
quote! { true } quote! { true }
@ -170,11 +176,11 @@ pub fn expand_event_content(input: &DeriveInput, emit_redacted: bool) -> syn::Re
} }
fn has_serialize_fields(&self) -> bool { fn has_serialize_fields(&self) -> bool {
#has_fields #has_serialize_fields
} }
fn has_deserialize_fields() -> bool { fn has_deserialize_fields() -> ::ruma_events::HasDeserializeFields {
#has_fields #has_deserialize_fields
} }
} }
} }

View File

@ -4,9 +4,9 @@ use serde::Serialize;
use serde_json::{value::RawValue as RawJsonValue, Value as JsonValue}; use serde_json::{value::RawValue as RawJsonValue, Value as JsonValue};
use crate::{ use crate::{
BasicEventContent, EphemeralRoomEventContent, EventContent, MessageEventContent, BasicEventContent, EphemeralRoomEventContent, EventContent, HasDeserializeFields,
RedactedEventContent, RedactedMessageEventContent, RedactedStateEventContent, RoomEventContent, MessageEventContent, RedactedEventContent, RedactedMessageEventContent,
StateEventContent, RedactedStateEventContent, RoomEventContent, StateEventContent,
}; };
/// A custom event's type and `content` JSON object. /// A custom event's type and `content` JSON object.
@ -83,8 +83,8 @@ impl RedactedEventContent for RedactedCustomEventContent {
false false
} }
fn has_deserialize_fields() -> bool { fn has_deserialize_fields() -> HasDeserializeFields {
false HasDeserializeFields::False
} }
} }

View File

@ -243,20 +243,27 @@ pub trait StateEventContent: RoomEventContent {}
/// The base trait that all redacted event content types implement. /// The base trait that all redacted event content types implement.
/// ///
/// Implementing this trait allows content types to be serialized as well as deserialized. /// This trait's associated functions and methods should not be used to build
/// redacted events, prefer the `redact` method on `AnyStateEvent` and
/// `AnyMessageEvent` and their "sync" and "stripped" counterparts. The
/// `RedactedEventContent` trait is an implementation detail, ruma makes no
/// API guarantees.
pub trait RedactedEventContent: EventContent { pub trait RedactedEventContent: EventContent {
/// Constructs the redacted event content. /// Constructs the redacted event content.
/// ///
/// If called for anything but "empty" redacted content this will error. /// If called for anything but "empty" redacted content this will error.
#[doc(hidden)]
fn empty(_event_type: &str) -> Result<Self, serde_json::Error> { fn empty(_event_type: &str) -> Result<Self, serde_json::Error> {
Err(serde::de::Error::custom("this event is not redacted")) Err(serde::de::Error::custom("this event is not redacted"))
} }
/// Determines if the redacted event content needs to serialize fields. /// Determines if the redacted event content needs to serialize fields.
#[doc(hidden)]
fn has_serialize_fields(&self) -> bool; fn has_serialize_fields(&self) -> bool;
/// Determines if the redacted event content needs to deserialize fields. /// Determines if the redacted event content needs to deserialize fields.
fn has_deserialize_fields() -> bool; #[doc(hidden)]
fn has_deserialize_fields() -> HasDeserializeFields;
} }
/// Marker trait for the content of a redacted message event. /// Marker trait for the content of a redacted message event.
@ -265,6 +272,21 @@ pub trait RedactedMessageEventContent: RedactedEventContent {}
/// Marker trait for the content of a redacted state event. /// Marker trait for the content of a redacted state event.
pub trait RedactedStateEventContent: RedactedEventContent {} pub trait RedactedStateEventContent: RedactedEventContent {}
/// `HasDeserializeFields` is used in the code generated by the `Event` derive
/// to aid in deserializing redacted events.
#[doc(hidden)]
#[derive(Debug)]
pub enum HasDeserializeFields {
/// Deserialize the event's content, failing if invalid.
True,
/// Return the redacted version of this event's content.
False,
/// `Optional` is used for `RedactedAliasesEventContent` since it has
/// an empty version and one with content left after redaction that
/// must be supported together.
Optional,
}
/// Helper struct to determine if the event has been redacted. /// Helper struct to determine if the event has been redacted.
#[doc(hidden)] #[doc(hidden)]
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]

View File

@ -5,7 +5,9 @@ use ruma_identifiers::RoomAliasId;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::value::RawValue as RawJsonValue; use serde_json::value::RawValue as RawJsonValue;
use crate::{EventContent, RedactedEventContent, RedactedStateEventContent, StateEvent}; use crate::{
EventContent, HasDeserializeFields, RedactedEventContent, RedactedStateEventContent, StateEvent,
};
/// Informs the room about what room aliases it has been given. /// Informs the room about what room aliases it has been given.
pub type AliasesEvent = StateEvent<AliasesEventContent>; pub type AliasesEvent = StateEvent<AliasesEventContent>;
@ -53,8 +55,8 @@ impl RedactedEventContent for RedactedAliasesEventContent {
self.aliases.is_some() self.aliases.is_some()
} }
fn has_deserialize_fields() -> bool { fn has_deserialize_fields() -> HasDeserializeFields {
true HasDeserializeFields::Optional
} }
} }

View File

@ -19,10 +19,6 @@ use ruma_events::{
use ruma_identifiers::{EventId, RoomId, UserId}; use ruma_identifiers::{EventId, RoomId, UserId};
use serde_json::{from_value as from_json_value, json, to_value as to_json_value}; use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
fn is_zst<T>(_: &T) -> bool {
std::mem::size_of::<T>() == 0
}
fn full_unsigned() -> UnsignedData { fn full_unsigned() -> UnsignedData {
let mut unsigned = UnsignedData::default(); let mut unsigned = UnsignedData::default();
// The presence of `redacted_because` triggers the event enum to return early // The presence of `redacted_because` triggers the event enum to return early
@ -63,7 +59,7 @@ fn redacted_message_event_serialize() {
} }
#[test] #[test]
fn redacted_aliases_event_serialize() { fn redacted_aliases_event_serialize_no_content() {
let redacted = RedactedSyncStateEvent { let redacted = RedactedSyncStateEvent {
content: RedactedAliasesEventContent { aliases: None }, content: RedactedAliasesEventContent { aliases: None },
event_id: EventId::try_from("$h29iv0s8:example.com").unwrap(), event_id: EventId::try_from("$h29iv0s8:example.com").unwrap(),
@ -85,6 +81,32 @@ fn redacted_aliases_event_serialize() {
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
#[test]
fn redacted_aliases_event_serialize_with_content() {
let redacted = RedactedSyncStateEvent {
content: RedactedAliasesEventContent { aliases: Some(vec![]) },
event_id: EventId::try_from("$h29iv0s8:example.com").unwrap(),
state_key: "".to_string(),
origin_server_ts: UNIX_EPOCH + Duration::from_millis(1),
sender: UserId::try_from("@carl:example.com").unwrap(),
unsigned: UnsignedData::default(),
};
let expected = json!({
"content": {
"aliases": []
},
"event_id": "$h29iv0s8:example.com",
"state_key": "",
"origin_server_ts": 1,
"sender": "@carl:example.com",
"type": "m.room.aliases"
});
let actual = to_json_value(&redacted).unwrap();
assert_eq!(actual, expected);
}
#[test] #[test]
fn redacted_aliases_deserialize() { fn redacted_aliases_deserialize() {
let unsigned = full_unsigned(); let unsigned = full_unsigned();
@ -101,14 +123,15 @@ fn redacted_aliases_deserialize() {
let actual = to_json_value(&redacted).unwrap(); let actual = to_json_value(&redacted).unwrap();
assert_matches!( assert_matches!(
from_json_value::<EventJson<AnyRoomEventStub>>(actual) from_json_value::<EventJson<AnySyncRoomEvent>>(actual)
.unwrap() .unwrap()
.deserialize() .deserialize()
.unwrap(), .unwrap(),
AnyRoomEventStub::RedactedState(AnyRedactedStateEventStub::RoomAliases(RedactedStateEventStub { AnySyncRoomEvent::RedactedState(AnyRedactedSyncStateEvent::RoomAliases(RedactedSyncStateEvent {
event_id, content, .. content: RedactedAliasesEventContent { aliases },
event_id, ..
})) if event_id == EventId::try_from("$h29iv0s8:example.com").unwrap() })) if event_id == EventId::try_from("$h29iv0s8:example.com").unwrap()
&& is_zst(&content) && aliases.is_none()
) )
} }
@ -133,10 +156,10 @@ fn redacted_deserialize_any_room() {
.deserialize() .deserialize()
.unwrap(), .unwrap(),
AnyRoomEvent::RedactedMessage(AnyRedactedMessageEvent::RoomMessage(RedactedMessageEvent { AnyRoomEvent::RedactedMessage(AnyRedactedMessageEvent::RoomMessage(RedactedMessageEvent {
event_id, room_id, content, .. content: RedactedMessageEventContent,
event_id, room_id, ..
})) if event_id == EventId::try_from("$h29iv0s8:example.com").unwrap() })) if event_id == EventId::try_from("$h29iv0s8:example.com").unwrap()
&& room_id == RoomId::try_from("!roomid:room.com").unwrap() && room_id == RoomId::try_from("!roomid:room.com").unwrap()
&& is_zst(&content)
) )
} }
@ -172,9 +195,9 @@ fn redacted_deserialize_any_room_sync() {
.deserialize() .deserialize()
.unwrap(), .unwrap(),
AnySyncRoomEvent::RedactedMessage(AnyRedactedSyncMessageEvent::RoomMessage(RedactedSyncMessageEvent { AnySyncRoomEvent::RedactedMessage(AnyRedactedSyncMessageEvent::RoomMessage(RedactedSyncMessageEvent {
event_id, content, .. content: RedactedMessageEventContent,
event_id, ..
})) if event_id == EventId::try_from("$h29iv0s8:example.com").unwrap() })) if event_id == EventId::try_from("$h29iv0s8:example.com").unwrap()
&& is_zst(&content)
) )
} }