Add send transaction endpoint
Also refactor PDU types and move them to ruma-events
This commit is contained in:
		
							parent
							
								
									7c934e1b8f
								
							
						
					
					
						commit
						bfad8cf1f1
					
				| @ -10,6 +10,7 @@ Breaking changes: | ||||
|   struct variants | ||||
|   * This change removes the types `EventMatchCondition`, `RoomMemberCountCondition` and | ||||
|     `SenderNotificationPermissionCondition` | ||||
| * Add PDU types: `pdu::{Pdu, PduStub}` | ||||
| 
 | ||||
| Improvements: | ||||
| 
 | ||||
|  | ||||
| @ -147,6 +147,7 @@ pub mod forwarded_room_key; | ||||
| pub mod fully_read; | ||||
| pub mod ignored_user_list; | ||||
| pub mod key; | ||||
| pub mod pdu; | ||||
| pub mod presence; | ||||
| pub mod push_rules; | ||||
| pub mod receipt; | ||||
|  | ||||
							
								
								
									
										325
									
								
								ruma-events/src/pdu.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										325
									
								
								ruma-events/src/pdu.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,325 @@ | ||||
| //! Types for persistent data unit schemas
 | ||||
| //!
 | ||||
| //! The differences between the `RoomV1Pdu` schema and the `RoomV3Pdu` schema are
 | ||||
| //! that the `RoomV1Pdu` takes an `event_id` field (`RoomV3Pdu` does not), and
 | ||||
| //! `auth_events` and `prev_events` take `Vec<(EventId, EventHash)> rather than
 | ||||
| //! `Vec<EventId>` in `RoomV3Pdu`.
 | ||||
| //!
 | ||||
| //! The stubbed versions of each PDU type remove the `event_id` field (if any)
 | ||||
| //! and the `room_id` field for use in PDU templates.
 | ||||
| 
 | ||||
| use std::{collections::BTreeMap, time::SystemTime}; | ||||
| 
 | ||||
| use js_int::UInt; | ||||
| use ruma_events::EventType; | ||||
| use ruma_identifiers::{EventId, RoomId, UserId}; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use serde_json::Value as JsonValue; | ||||
| 
 | ||||
| /// Enum for PDU schemas
 | ||||
| #[derive(Clone, Debug, Deserialize, Serialize)] | ||||
| #[serde(untagged)] | ||||
| pub enum Pdu { | ||||
|     /// PDU for room versions 1 and 2.
 | ||||
|     RoomV1Pdu(RoomV1Pdu), | ||||
|     /// PDU for room versions 3 and above.
 | ||||
|     RoomV3Pdu(RoomV3Pdu), | ||||
| } | ||||
| 
 | ||||
| /// A 'persistent data unit' (event) for room versions 1 and 2.
 | ||||
| #[derive(Clone, Debug, Deserialize, Serialize)] | ||||
| pub struct RoomV1Pdu { | ||||
|     /// Event ID for the PDU.
 | ||||
|     pub event_id: EventId, | ||||
| 
 | ||||
|     /// The room this event belongs to.
 | ||||
|     pub room_id: RoomId, | ||||
| 
 | ||||
|     /// The user id of the user who sent this event.
 | ||||
|     pub sender: UserId, | ||||
| 
 | ||||
|     /// The `server_name` of the homeserver that created this event.
 | ||||
|     pub origin: String, | ||||
| 
 | ||||
|     /// Timestamp (milliseconds since the UNIX epoch) on originating homeserver
 | ||||
|     /// of when this event was created.
 | ||||
|     #[serde(with = "ruma_serde::time::ms_since_unix_epoch")] | ||||
|     pub origin_server_ts: SystemTime, | ||||
| 
 | ||||
|     // TODO: Encode event type as content enum variant, like event enums do
 | ||||
|     /// The event's type.
 | ||||
|     #[serde(rename = "type")] | ||||
|     pub kind: EventType, | ||||
| 
 | ||||
|     /// The event's content.
 | ||||
|     pub content: JsonValue, | ||||
| 
 | ||||
|     /// A key that determines which piece of room state the event represents.
 | ||||
|     #[serde(skip_serializing_if = "Option::is_none")] | ||||
|     pub state_key: Option<String>, | ||||
| 
 | ||||
|     /// Event IDs for the most recent events in the room that the homeserver was
 | ||||
|     /// aware of when it created this event.
 | ||||
|     #[serde(skip_serializing_if = "Vec::is_empty")] | ||||
|     pub prev_events: Vec<(EventId, EventHash)>, | ||||
| 
 | ||||
|     /// The maximum depth of the `prev_events`, plus one.
 | ||||
|     pub depth: UInt, | ||||
| 
 | ||||
|     /// Event IDs for the authorization events that would allow this event to be
 | ||||
|     /// in the room.
 | ||||
|     #[serde(skip_serializing_if = "Vec::is_empty")] | ||||
|     pub auth_events: Vec<(EventId, EventHash)>, | ||||
| 
 | ||||
|     /// For redaction events, the ID of the event being redacted.
 | ||||
|     #[serde(skip_serializing_if = "Option::is_none")] | ||||
|     pub redacts: Option<EventId>, | ||||
| 
 | ||||
|     /// Additional data added by the origin server but not covered by the
 | ||||
|     /// signatures.
 | ||||
|     #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] | ||||
|     pub unsigned: BTreeMap<String, JsonValue>, | ||||
| 
 | ||||
|     /// Content hashes of the PDU.
 | ||||
|     pub hashes: EventHash, | ||||
| 
 | ||||
|     /// Signatures for the PDU.
 | ||||
|     pub signatures: BTreeMap<String, BTreeMap<String, String>>, | ||||
| } | ||||
| 
 | ||||
| /// A 'persistent data unit' (event) for room versions 3 and beyond.
 | ||||
| #[derive(Clone, Debug, Deserialize, Serialize)] | ||||
| pub struct RoomV3Pdu { | ||||
|     /// The room this event belongs to.
 | ||||
|     pub room_id: RoomId, | ||||
| 
 | ||||
|     /// The user id of the user who sent this event.
 | ||||
|     pub sender: UserId, | ||||
| 
 | ||||
|     /// The `server_name` of the homeserver that created this event.
 | ||||
|     pub origin: String, | ||||
| 
 | ||||
|     /// Timestamp (milliseconds since the UNIX epoch) on originating homeserver
 | ||||
|     /// of when this event was created.
 | ||||
|     #[serde(with = "ruma_serde::time::ms_since_unix_epoch")] | ||||
|     pub origin_server_ts: SystemTime, | ||||
| 
 | ||||
|     // TODO: Encode event type as content enum variant, like event enums do
 | ||||
|     /// The event's type.
 | ||||
|     #[serde(rename = "type")] | ||||
|     pub kind: EventType, | ||||
| 
 | ||||
|     /// The event's content.
 | ||||
|     pub content: JsonValue, | ||||
| 
 | ||||
|     /// A key that determines which piece of room state the event represents.
 | ||||
|     #[serde(skip_serializing_if = "Option::is_none")] | ||||
|     pub state_key: Option<String>, | ||||
| 
 | ||||
|     /// Event IDs for the most recent events in the room that the homeserver was
 | ||||
|     /// aware of when it created this event.
 | ||||
|     pub prev_events: Vec<EventId>, | ||||
| 
 | ||||
|     /// The maximum depth of the `prev_events`, plus one.
 | ||||
|     pub depth: UInt, | ||||
| 
 | ||||
|     /// Event IDs for the authorization events that would allow this event to be
 | ||||
|     /// in the room.
 | ||||
|     pub auth_events: Vec<EventId>, | ||||
| 
 | ||||
|     /// For redaction events, the ID of the event being redacted.
 | ||||
|     #[serde(skip_serializing_if = "Option::is_none")] | ||||
|     pub redacts: Option<EventId>, | ||||
| 
 | ||||
|     /// Additional data added by the origin server but not covered by the
 | ||||
|     /// signatures.
 | ||||
|     #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] | ||||
|     pub unsigned: BTreeMap<String, JsonValue>, | ||||
| 
 | ||||
|     /// Content hashes of the PDU.
 | ||||
|     pub hashes: EventHash, | ||||
| 
 | ||||
|     /// Signatures for the PDU.
 | ||||
|     pub signatures: BTreeMap<String, BTreeMap<String, String>>, | ||||
| } | ||||
| 
 | ||||
| /// PDU type without event and room IDs.
 | ||||
| #[derive(Clone, Debug, Deserialize, Serialize)] | ||||
| #[serde(untagged)] | ||||
| pub enum PduStub { | ||||
|     /// Stub for PDUs of room version 1 and 2.
 | ||||
|     RoomV1PduStub(RoomV1PduStub), | ||||
| 
 | ||||
|     /// Stub for PDUs of room versions 3 and above.
 | ||||
|     RoomV3PduStub(RoomV3PduStub), | ||||
| } | ||||
| 
 | ||||
| impl PduStub { | ||||
|     /// Helper method to get PDU from a PDU stub.
 | ||||
|     pub fn into_pdu(self, room_id: RoomId, event_id: EventId) -> Pdu { | ||||
|         match self { | ||||
|             PduStub::RoomV1PduStub(v1_stub) => { | ||||
|                 Pdu::RoomV1Pdu(v1_stub.into_v1_pdu(room_id, event_id)) | ||||
|             } | ||||
|             PduStub::RoomV3PduStub(v3_stub) => Pdu::RoomV3Pdu(v3_stub.into_v3_pdu(room_id)), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Stub for PDUs of room version 1 and 2.
 | ||||
| #[derive(Clone, Debug, Deserialize, Serialize)] | ||||
| pub struct RoomV1PduStub { | ||||
|     /// The user id of the user who sent this event.
 | ||||
|     pub sender: UserId, | ||||
| 
 | ||||
|     /// The `server_name` of the homeserver that created this event.
 | ||||
|     pub origin: String, | ||||
| 
 | ||||
|     /// Timestamp (milliseconds since the UNIX epoch) on originating homeserver
 | ||||
|     /// of when this event was created.
 | ||||
|     #[serde(with = "ruma_serde::time::ms_since_unix_epoch")] | ||||
|     pub origin_server_ts: SystemTime, | ||||
| 
 | ||||
|     // TODO: Encode event type as content enum variant, like event enums do
 | ||||
|     /// The event's type.
 | ||||
|     #[serde(rename = "type")] | ||||
|     pub kind: EventType, | ||||
| 
 | ||||
|     /// The event's content.
 | ||||
|     pub content: JsonValue, | ||||
| 
 | ||||
|     /// A key that determines which piece of room state the event represents.
 | ||||
|     #[serde(skip_serializing_if = "Option::is_none")] | ||||
|     pub state_key: Option<String>, | ||||
| 
 | ||||
|     /// Event IDs for the most recent events in the room that the homeserver was
 | ||||
|     /// aware of when it created this event.
 | ||||
|     pub prev_events: Vec<(EventId, EventHash)>, | ||||
| 
 | ||||
|     /// The maximum depth of the `prev_events`, plus one.
 | ||||
|     pub depth: UInt, | ||||
| 
 | ||||
|     /// Event IDs for the authorization events that would allow this event to be
 | ||||
|     /// in the room.
 | ||||
|     pub auth_events: Vec<(EventId, EventHash)>, | ||||
| 
 | ||||
|     /// For redaction events, the ID of the event being redacted.
 | ||||
|     #[serde(skip_serializing_if = "Option::is_none")] | ||||
|     pub redacts: Option<EventId>, | ||||
| 
 | ||||
|     /// Additional data added by the origin server but not covered by the
 | ||||
|     /// signatures.
 | ||||
|     #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] | ||||
|     pub unsigned: BTreeMap<String, JsonValue>, | ||||
| 
 | ||||
|     /// Content hashes of the PDU.
 | ||||
|     pub hashes: EventHash, | ||||
| 
 | ||||
|     /// Signatures for the PDU.
 | ||||
|     pub signatures: BTreeMap<String, BTreeMap<String, String>>, | ||||
| } | ||||
| 
 | ||||
| impl RoomV1PduStub { | ||||
|     /// Converts a V1 PDU stub into a full V1 PDU.
 | ||||
|     pub fn into_v1_pdu(self, room_id: RoomId, event_id: EventId) -> RoomV1Pdu { | ||||
|         RoomV1Pdu { | ||||
|             event_id, | ||||
|             room_id, | ||||
|             sender: self.sender, | ||||
|             origin: self.origin, | ||||
|             origin_server_ts: self.origin_server_ts, | ||||
|             kind: self.kind, | ||||
|             content: self.content, | ||||
|             state_key: self.state_key, | ||||
|             prev_events: self.prev_events, | ||||
|             depth: self.depth, | ||||
|             auth_events: self.auth_events, | ||||
|             redacts: self.redacts, | ||||
|             unsigned: self.unsigned, | ||||
|             hashes: self.hashes, | ||||
|             signatures: self.signatures, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Stub for PDUs of room versions 3 and above.
 | ||||
| #[derive(Clone, Debug, Deserialize, Serialize)] | ||||
| pub struct RoomV3PduStub { | ||||
|     /// The user id of the user who sent this event.
 | ||||
|     pub sender: UserId, | ||||
| 
 | ||||
|     /// The `server_name` of the homeserver that created this event.
 | ||||
|     pub origin: String, | ||||
| 
 | ||||
|     /// Timestamp (milliseconds since the UNIX epoch) on originating homeserver
 | ||||
|     /// of when this event was created.
 | ||||
|     #[serde(with = "ruma_serde::time::ms_since_unix_epoch")] | ||||
|     pub origin_server_ts: SystemTime, | ||||
| 
 | ||||
|     // TODO: Encode event type as content enum variant, like event enums do
 | ||||
|     /// The event's type.
 | ||||
|     #[serde(rename = "type")] | ||||
|     pub kind: EventType, | ||||
| 
 | ||||
|     /// The event's content.
 | ||||
|     pub content: JsonValue, | ||||
| 
 | ||||
|     /// A key that determines which piece of room state the event represents.
 | ||||
|     #[serde(skip_serializing_if = "Option::is_none")] | ||||
|     pub state_key: Option<String>, | ||||
| 
 | ||||
|     /// Event IDs for the most recent events in the room that the homeserver was
 | ||||
|     /// aware of when it created this event.
 | ||||
|     pub prev_events: Vec<EventId>, | ||||
| 
 | ||||
|     /// The maximum depth of the `prev_events`, plus one.
 | ||||
|     pub depth: UInt, | ||||
| 
 | ||||
|     /// Event IDs for the authorization events that would allow this event to be
 | ||||
|     /// in the room.
 | ||||
|     pub auth_events: Vec<EventId>, | ||||
| 
 | ||||
|     /// For redaction events, the ID of the event being redacted.
 | ||||
|     #[serde(skip_serializing_if = "Option::is_none")] | ||||
|     pub redacts: Option<EventId>, | ||||
| 
 | ||||
|     /// Additional data added by the origin server but not covered by the
 | ||||
|     /// signatures.
 | ||||
|     #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] | ||||
|     pub unsigned: BTreeMap<String, JsonValue>, | ||||
| 
 | ||||
|     /// Content hashes of the PDU.
 | ||||
|     pub hashes: EventHash, | ||||
| 
 | ||||
|     /// Signatures for the PDU.
 | ||||
|     pub signatures: BTreeMap<String, BTreeMap<String, String>>, | ||||
| } | ||||
| 
 | ||||
| impl RoomV3PduStub { | ||||
|     /// Converts a V3 PDU stub into a full V3 PDU.
 | ||||
|     pub fn into_v3_pdu(self, room_id: RoomId) -> RoomV3Pdu { | ||||
|         RoomV3Pdu { | ||||
|             room_id, | ||||
|             sender: self.sender, | ||||
|             origin: self.origin, | ||||
|             origin_server_ts: self.origin_server_ts, | ||||
|             kind: self.kind, | ||||
|             content: self.content, | ||||
|             state_key: self.state_key, | ||||
|             prev_events: self.prev_events, | ||||
|             depth: self.depth, | ||||
|             auth_events: self.auth_events, | ||||
|             redacts: self.redacts, | ||||
|             unsigned: self.unsigned, | ||||
|             hashes: self.hashes, | ||||
|             signatures: self.signatures, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Content hashes of a PDU.
 | ||||
| #[derive(Clone, Debug, Deserialize, Serialize)] | ||||
| pub struct EventHash { | ||||
|     /// The SHA-256 hash.
 | ||||
|     pub sha256: String, | ||||
| } | ||||
							
								
								
									
										605
									
								
								ruma-events/tests/pdu.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										605
									
								
								ruma-events/tests/pdu.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,605 @@ | ||||
| use std::{ | ||||
|     collections::BTreeMap, | ||||
|     convert::TryFrom, | ||||
|     time::{Duration, SystemTime}, | ||||
| }; | ||||
| 
 | ||||
| use matches::assert_matches; | ||||
| use ruma_events::{ | ||||
|     pdu::{EventHash, Pdu, PduStub, RoomV1Pdu, RoomV1PduStub, RoomV3Pdu, RoomV3PduStub}, | ||||
|     EventType, | ||||
| }; | ||||
| use ruma_identifiers::{EventId, RoomId, UserId}; | ||||
| use serde_json::{from_value as from_json_value, json, to_value as to_json_value}; | ||||
| 
 | ||||
| #[test] | ||||
| fn serialize_stub_as_v1() { | ||||
|     let mut signatures = BTreeMap::new(); | ||||
|     let mut inner_signature = BTreeMap::new(); | ||||
|     inner_signature.insert( | ||||
|         "ed25519:key_version".to_string(), | ||||
|         "86BytesOfSignatureOfTheRedactedEvent".to_string(), | ||||
|     ); | ||||
|     signatures.insert("example.com".to_string(), inner_signature); | ||||
| 
 | ||||
|     let mut unsigned = BTreeMap::new(); | ||||
|     unsigned.insert("somekey".to_string(), json!({"a": 456})); | ||||
| 
 | ||||
|     let v1_stub = RoomV1PduStub { | ||||
|         sender: UserId::try_from("@sender:example.com").unwrap(), | ||||
|         origin: "matrix.org".to_string(), | ||||
|         origin_server_ts: SystemTime::UNIX_EPOCH + Duration::from_millis(1_592_050_773_658), | ||||
|         kind: EventType::RoomPowerLevels, | ||||
|         content: json!({"testing": 123}), | ||||
|         state_key: Some("state".to_string()), | ||||
|         prev_events: vec![( | ||||
|             EventId::try_from("$previousevent:matrix.org").unwrap(), | ||||
|             EventHash { sha256: "123567".to_string() }, | ||||
|         )], | ||||
|         depth: 2_u32.into(), | ||||
|         auth_events: vec![( | ||||
|             EventId::try_from("$someauthevent:matrix.org").unwrap(), | ||||
|             EventHash { sha256: "21389CFEDABC".to_string() }, | ||||
|         )], | ||||
|         redacts: Some(EventId::try_from("$9654:matrix.org").unwrap()), | ||||
|         unsigned, | ||||
|         hashes: EventHash { sha256: "1233543bABACDEF".to_string() }, | ||||
|         signatures, | ||||
|     }; | ||||
|     let pdu_stub = PduStub::RoomV1PduStub(v1_stub); | ||||
|     let json = json!({ | ||||
|         "sender": "@sender:example.com", | ||||
|         "origin": "matrix.org", | ||||
|         "origin_server_ts": 1_592_050_773_658 as usize, | ||||
|         "type": "m.room.power_levels", | ||||
|         "content": { | ||||
|             "testing": 123 | ||||
|         }, | ||||
|         "state_key": "state", | ||||
|         "prev_events": [ | ||||
|             [ "$previousevent:matrix.org", {"sha256": "123567"} ] | ||||
|         ], | ||||
|         "depth": 2, | ||||
|         "auth_events": [ | ||||
|             ["$someauthevent:matrix.org", {"sha256": "21389CFEDABC"}] | ||||
|         ], | ||||
|         "redacts": "$9654:matrix.org", | ||||
|         "unsigned": { | ||||
|             "somekey": { "a": 456 } }, | ||||
|         "hashes": { "sha256": "1233543bABACDEF" }, | ||||
|         "signatures": { | ||||
|             "example.com": { "ed25519:key_version":"86BytesOfSignatureOfTheRedactedEvent" } | ||||
|         } | ||||
|     }); | ||||
| 
 | ||||
|     assert_eq!(to_json_value(&pdu_stub).unwrap(), json); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn serialize_stub_as_v3() { | ||||
|     let mut signatures = BTreeMap::new(); | ||||
|     let mut inner_signature = BTreeMap::new(); | ||||
|     inner_signature.insert( | ||||
|         "ed25519:key_version".to_string(), | ||||
|         "86BytesOfSignatureOfTheRedactedEvent".to_string(), | ||||
|     ); | ||||
|     signatures.insert("example.com".to_string(), inner_signature); | ||||
| 
 | ||||
|     let mut unsigned = BTreeMap::new(); | ||||
|     unsigned.insert("somekey".to_string(), json!({"a": 456})); | ||||
| 
 | ||||
|     let v3_stub = RoomV3PduStub { | ||||
|         sender: UserId::try_from("@sender:example.com").unwrap(), | ||||
|         origin: "matrix.org".to_string(), | ||||
|         origin_server_ts: SystemTime::UNIX_EPOCH + Duration::from_millis(1_592_050_773_658), | ||||
|         kind: EventType::RoomPowerLevels, | ||||
|         content: json!({"testing": 123}), | ||||
|         state_key: Some("state".to_string()), | ||||
|         prev_events: vec![EventId::try_from("$previousevent:matrix.org").unwrap()], | ||||
|         depth: 2_u32.into(), | ||||
|         auth_events: vec![EventId::try_from("$someauthevent:matrix.org").unwrap()], | ||||
|         redacts: Some(EventId::try_from("$9654:matrix.org").unwrap()), | ||||
|         unsigned, | ||||
|         hashes: EventHash { sha256: "1233543bABACDEF".to_string() }, | ||||
|         signatures, | ||||
|     }; | ||||
|     let pdu_stub = PduStub::RoomV3PduStub(v3_stub); | ||||
|     let json = json!({ | ||||
|         "sender": "@sender:example.com", | ||||
|         "origin": "matrix.org", | ||||
|         "origin_server_ts": 1_592_050_773_658 as usize, | ||||
|         "type": "m.room.power_levels", | ||||
|         "content": { | ||||
|             "testing": 123 | ||||
|         }, | ||||
|         "state_key": "state", | ||||
|         "prev_events": [ "$previousevent:matrix.org" ], | ||||
|         "depth": 2, | ||||
|         "auth_events": ["$someauthevent:matrix.org" ], | ||||
|         "redacts": "$9654:matrix.org", | ||||
|         "unsigned": { | ||||
|             "somekey": { "a": 456 } }, | ||||
|         "hashes": { "sha256": "1233543bABACDEF" }, | ||||
|         "signatures": { | ||||
|             "example.com": { "ed25519:key_version":"86BytesOfSignatureOfTheRedactedEvent" } | ||||
|         } | ||||
|     }); | ||||
| 
 | ||||
|     assert_eq!(to_json_value(&pdu_stub).unwrap(), json); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn test_deserialize_stub_as_v1() { | ||||
|     let json = json!({ | ||||
|         "auth_events": [ | ||||
|             [ | ||||
|                 "$abc123:matrix.org", | ||||
|                 { | ||||
|                     "sha256": "Base64EncodedSha256HashesShouldBe43BytesLong" | ||||
|                 } | ||||
|             ] | ||||
|         ], | ||||
|         "content": { | ||||
|             "key": "value" | ||||
|         }, | ||||
|         "depth": 12, | ||||
|         "event_id": "$a4ecee13e2accdadf56c1025:example.com", | ||||
|         "hashes": { | ||||
|             "sha256": "ThisHashCoversAllFieldsInCaseThisIsRedacted" | ||||
|         }, | ||||
|         "origin": "matrix.org", | ||||
|         "origin_server_ts": 1_234_567_890, | ||||
|         "prev_events": [ | ||||
|             [ | ||||
|                 "$abc123:matrix.org", | ||||
|                 { | ||||
|                     "sha256": "Base64EncodedSha256HashesShouldBe43BytesLong" | ||||
|                 } | ||||
|             ] | ||||
|         ], | ||||
|         "redacts": "$def456:matrix.org", | ||||
|         "room_id": "!abc123:matrix.org", | ||||
|         "sender": "@someone:matrix.org", | ||||
|         "signatures": { | ||||
|             "example.com": { | ||||
|                 "ed25519:key_version:": "86BytesOfSignatureOfTheRedactedEvent" | ||||
|             } | ||||
|         }, | ||||
|         "state_key": "my_key", | ||||
|         "type": "m.room.message", | ||||
|         "unsigned": { | ||||
|             "key": "value" | ||||
|         } | ||||
|     }); | ||||
|     let parsed = from_json_value::<PduStub>(json).unwrap(); | ||||
| 
 | ||||
|     match parsed { | ||||
|         PduStub::RoomV1PduStub(v1_stub) => { | ||||
|             assert_eq!( | ||||
|                 v1_stub.auth_events.first().unwrap().0, | ||||
|                 EventId::try_from("$abc123:matrix.org").unwrap() | ||||
|             ); | ||||
|             assert_eq!( | ||||
|                 v1_stub.auth_events.first().unwrap().1.sha256, | ||||
|                 "Base64EncodedSha256HashesShouldBe43BytesLong" | ||||
|             ); | ||||
|         } | ||||
|         PduStub::RoomV3PduStub(_) => panic!("Matched V3 stub"), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn deserialize_stub_as_v3() { | ||||
|     let json = json!({ | ||||
|         "auth_events": [ | ||||
|             "$abc123:matrix.org" | ||||
|         ], | ||||
|         "content": { | ||||
|             "key": "value" | ||||
|         }, | ||||
|         "depth": 12, | ||||
|         "event_id": "$a4ecee13e2accdadf56c1025:example.com", | ||||
|         "hashes": { | ||||
|             "sha256": "ThisHashCoversAllFieldsInCaseThisIsRedacted" | ||||
|         }, | ||||
|         "origin": "matrix.org", | ||||
|         "origin_server_ts": 1_234_567_890, | ||||
|         "prev_events": [ | ||||
|                 "$abc123:matrix.org" | ||||
|         ], | ||||
|         "redacts": "$def456:matrix.org", | ||||
|         "room_id": "!abc123:matrix.org", | ||||
|         "sender": "@someone:matrix.org", | ||||
|         "signatures": { | ||||
|             "example.com": { | ||||
|                 "ed25519:key_version:": "86BytesOfSignatureOfTheRedactedEvent" | ||||
|             } | ||||
|         }, | ||||
|         "state_key": "my_key", | ||||
|         "type": "m.room.message", | ||||
|         "unsigned": { | ||||
|             "key": "value" | ||||
|         } | ||||
|     }); | ||||
|     let parsed = from_json_value::<PduStub>(json).unwrap(); | ||||
| 
 | ||||
|     match parsed { | ||||
|         PduStub::RoomV1PduStub(_) => panic!("Matched V1 stub"), | ||||
|         PduStub::RoomV3PduStub(v3_stub) => { | ||||
|             assert_eq!( | ||||
|                 v3_stub.auth_events.first().unwrap(), | ||||
|                 &EventId::try_from("$abc123:matrix.org").unwrap() | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn serialize_pdu_as_v1() { | ||||
|     let mut signatures = BTreeMap::new(); | ||||
|     let mut inner_signature = BTreeMap::new(); | ||||
|     inner_signature.insert( | ||||
|         "ed25519:key_version".to_string(), | ||||
|         "86BytesOfSignatureOfTheRedactedEvent".to_string(), | ||||
|     ); | ||||
|     signatures.insert("example.com".to_string(), inner_signature); | ||||
| 
 | ||||
|     let mut unsigned = BTreeMap::new(); | ||||
|     unsigned.insert("somekey".to_string(), json!({"a": 456})); | ||||
| 
 | ||||
|     let v1_pdu = RoomV1Pdu { | ||||
|         room_id: RoomId::try_from("!n8f893n9:example.com").unwrap(), | ||||
|         event_id: EventId::try_from("$somejoinevent:matrix.org").unwrap(), | ||||
|         sender: UserId::try_from("@sender:example.com").unwrap(), | ||||
|         origin: "matrix.org".to_string(), | ||||
|         origin_server_ts: SystemTime::UNIX_EPOCH + Duration::from_millis(1_592_050_773_658), | ||||
|         kind: EventType::RoomPowerLevels, | ||||
|         content: json!({"testing": 123}), | ||||
|         state_key: Some("state".to_string()), | ||||
|         prev_events: vec![( | ||||
|             EventId::try_from("$previousevent:matrix.org").unwrap(), | ||||
|             EventHash { sha256: "123567".to_string() }, | ||||
|         )], | ||||
|         depth: 2_u32.into(), | ||||
|         auth_events: vec![( | ||||
|             EventId::try_from("$someauthevent:matrix.org").unwrap(), | ||||
|             EventHash { sha256: "21389CFEDABC".to_string() }, | ||||
|         )], | ||||
|         redacts: Some(EventId::try_from("$9654:matrix.org").unwrap()), | ||||
|         unsigned, | ||||
|         hashes: EventHash { sha256: "1233543bABACDEF".to_string() }, | ||||
|         signatures, | ||||
|     }; | ||||
|     let pdu = Pdu::RoomV1Pdu(v1_pdu); | ||||
|     let json = json!({ | ||||
|         "room_id": "!n8f893n9:example.com", | ||||
|         "event_id": "$somejoinevent:matrix.org", | ||||
|         "sender": "@sender:example.com", | ||||
|         "origin": "matrix.org", | ||||
|         "origin_server_ts": 1_592_050_773_658 as usize, | ||||
|         "type": "m.room.power_levels", | ||||
|         "content": { | ||||
|             "testing": 123 | ||||
|         }, | ||||
|         "state_key": "state", | ||||
|         "prev_events": [ | ||||
|             [ "$previousevent:matrix.org", {"sha256": "123567"} ] | ||||
|         ], | ||||
|         "depth": 2, | ||||
|         "auth_events": [ | ||||
|             ["$someauthevent:matrix.org", {"sha256": "21389CFEDABC"}] | ||||
|         ], | ||||
|         "redacts": "$9654:matrix.org", | ||||
|         "unsigned": { | ||||
|             "somekey": { "a": 456 } }, | ||||
|         "hashes": { "sha256": "1233543bABACDEF" }, | ||||
|         "signatures": { | ||||
|             "example.com": { "ed25519:key_version":"86BytesOfSignatureOfTheRedactedEvent" } | ||||
|         } | ||||
|     }); | ||||
| 
 | ||||
|     assert_eq!(to_json_value(&pdu).unwrap(), json); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn serialize_pdu_as_v3() { | ||||
|     let mut signatures = BTreeMap::new(); | ||||
|     let mut inner_signature = BTreeMap::new(); | ||||
|     inner_signature.insert( | ||||
|         "ed25519:key_version".to_string(), | ||||
|         "86BytesOfSignatureOfTheRedactedEvent".to_string(), | ||||
|     ); | ||||
|     signatures.insert("example.com".to_string(), inner_signature); | ||||
| 
 | ||||
|     let mut unsigned = BTreeMap::new(); | ||||
|     unsigned.insert("somekey".to_string(), json!({"a": 456})); | ||||
| 
 | ||||
|     let v3_pdu = RoomV3Pdu { | ||||
|         room_id: RoomId::try_from("!n8f893n9:example.com").unwrap(), | ||||
|         sender: UserId::try_from("@sender:example.com").unwrap(), | ||||
|         origin: "matrix.org".to_string(), | ||||
|         origin_server_ts: SystemTime::UNIX_EPOCH + Duration::from_millis(1_592_050_773_658), | ||||
|         kind: EventType::RoomPowerLevels, | ||||
|         content: json!({"testing": 123}), | ||||
|         state_key: Some("state".to_string()), | ||||
|         prev_events: vec![EventId::try_from("$previousevent:matrix.org").unwrap()], | ||||
|         depth: 2_u32.into(), | ||||
|         auth_events: vec![EventId::try_from("$someauthevent:matrix.org").unwrap()], | ||||
|         redacts: Some(EventId::try_from("$9654:matrix.org").unwrap()), | ||||
|         unsigned, | ||||
|         hashes: EventHash { sha256: "1233543bABACDEF".to_string() }, | ||||
|         signatures, | ||||
|     }; | ||||
|     let pdu_stub = Pdu::RoomV3Pdu(v3_pdu); | ||||
|     let json = json!({ | ||||
|         "room_id": "!n8f893n9:example.com", | ||||
|         "sender": "@sender:example.com", | ||||
|         "origin": "matrix.org", | ||||
|         "origin_server_ts": 1_592_050_773_658 as usize, | ||||
|         "type": "m.room.power_levels", | ||||
|         "content": { | ||||
|             "testing": 123 | ||||
|         }, | ||||
|         "state_key": "state", | ||||
|         "prev_events": [ "$previousevent:matrix.org" ], | ||||
|         "depth": 2, | ||||
|         "auth_events": ["$someauthevent:matrix.org" ], | ||||
|         "redacts": "$9654:matrix.org", | ||||
|         "unsigned": { | ||||
|             "somekey": { "a": 456 } }, | ||||
|         "hashes": { "sha256": "1233543bABACDEF" }, | ||||
|         "signatures": { | ||||
|             "example.com": { "ed25519:key_version":"86BytesOfSignatureOfTheRedactedEvent" } | ||||
|         } | ||||
|     }); | ||||
| 
 | ||||
|     assert_eq!(to_json_value(&pdu_stub).unwrap(), json); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn test_deserialize_pdu_as_v1() { | ||||
|     let json = json!({ | ||||
|         "room_id": "!n8f893n9:example.com", | ||||
|         "event_id": "$somejoinevent:matrix.org", | ||||
|         "auth_events": [ | ||||
|             [ | ||||
|                 "$abc123:matrix.org", | ||||
|                 { | ||||
|                     "sha256": "Base64EncodedSha256HashesShouldBe43BytesLong" | ||||
|                 } | ||||
|             ] | ||||
|         ], | ||||
|         "content": { | ||||
|             "key": "value" | ||||
|         }, | ||||
|         "depth": 12, | ||||
|         "event_id": "$a4ecee13e2accdadf56c1025:example.com", | ||||
|         "hashes": { | ||||
|             "sha256": "ThisHashCoversAllFieldsInCaseThisIsRedacted" | ||||
|         }, | ||||
|         "origin": "matrix.org", | ||||
|         "origin_server_ts": 1_234_567_890, | ||||
|         "prev_events": [ | ||||
|             [ | ||||
|                 "$abc123:matrix.org", | ||||
|                 { | ||||
|                     "sha256": "Base64EncodedSha256HashesShouldBe43BytesLong" | ||||
|                 } | ||||
|             ] | ||||
|         ], | ||||
|         "redacts": "$def456:matrix.org", | ||||
|         "room_id": "!abc123:matrix.org", | ||||
|         "sender": "@someone:matrix.org", | ||||
|         "signatures": { | ||||
|             "example.com": { | ||||
|                 "ed25519:key_version:": "86BytesOfSignatureOfTheRedactedEvent" | ||||
|             } | ||||
|         }, | ||||
|         "state_key": "my_key", | ||||
|         "type": "m.room.message", | ||||
|         "unsigned": { | ||||
|             "key": "value" | ||||
|         } | ||||
|     }); | ||||
|     let parsed = from_json_value::<Pdu>(json).unwrap(); | ||||
| 
 | ||||
|     match parsed { | ||||
|         Pdu::RoomV1Pdu(v1_pdu) => { | ||||
|             assert_eq!( | ||||
|                 v1_pdu.auth_events.first().unwrap().0, | ||||
|                 EventId::try_from("$abc123:matrix.org").unwrap() | ||||
|             ); | ||||
|             assert_eq!( | ||||
|                 v1_pdu.auth_events.first().unwrap().1.sha256, | ||||
|                 "Base64EncodedSha256HashesShouldBe43BytesLong" | ||||
|             ); | ||||
|         } | ||||
|         Pdu::RoomV3Pdu(_) => panic!("Matched V3 PDU"), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn deserialize_pdu_as_v3() { | ||||
|     let json = json!({ | ||||
|         "room_id": "!n8f893n9:example.com", | ||||
|         "auth_events": [ | ||||
|             "$abc123:matrix.org" | ||||
|         ], | ||||
|         "content": { | ||||
|             "key": "value" | ||||
|         }, | ||||
|         "depth": 12, | ||||
|         "event_id": "$a4ecee13e2accdadf56c1025:example.com", | ||||
|         "hashes": { | ||||
|             "sha256": "ThisHashCoversAllFieldsInCaseThisIsRedacted" | ||||
|         }, | ||||
|         "origin": "matrix.org", | ||||
|         "origin_server_ts": 1_234_567_890, | ||||
|         "prev_events": [ | ||||
|                 "$abc123:matrix.org" | ||||
|         ], | ||||
|         "redacts": "$def456:matrix.org", | ||||
|         "room_id": "!abc123:matrix.org", | ||||
|         "sender": "@someone:matrix.org", | ||||
|         "signatures": { | ||||
|             "example.com": { | ||||
|                 "ed25519:key_version:": "86BytesOfSignatureOfTheRedactedEvent" | ||||
|             } | ||||
|         }, | ||||
|         "state_key": "my_key", | ||||
|         "type": "m.room.message", | ||||
|         "unsigned": { | ||||
|             "key": "value" | ||||
|         } | ||||
|     }); | ||||
|     let parsed = from_json_value::<Pdu>(json).unwrap(); | ||||
| 
 | ||||
|     match parsed { | ||||
|         Pdu::RoomV1Pdu(_) => panic!("Matched V1 PDU"), | ||||
|         Pdu::RoomV3Pdu(v3_pdu) => { | ||||
|             assert_eq!( | ||||
|                 v3_pdu.auth_events.first().unwrap(), | ||||
|                 &EventId::try_from("$abc123:matrix.org").unwrap() | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn convert_v1_stub_to_pdu() { | ||||
|     let mut signatures = BTreeMap::new(); | ||||
|     let mut inner_signature = BTreeMap::new(); | ||||
|     inner_signature.insert( | ||||
|         "ed25519:key_version".to_string(), | ||||
|         "86BytesOfSignatureOfTheRedactedEvent".to_string(), | ||||
|     ); | ||||
|     signatures.insert("example.com".to_string(), inner_signature); | ||||
| 
 | ||||
|     let mut unsigned = BTreeMap::new(); | ||||
|     unsigned.insert("somekey".to_string(), json!({"a": 456})); | ||||
| 
 | ||||
|     let v1_stub = RoomV1PduStub { | ||||
|         sender: UserId::try_from("@sender:example.com").unwrap(), | ||||
|         origin: "matrix.org".to_string(), | ||||
|         origin_server_ts: SystemTime::UNIX_EPOCH + Duration::from_millis(1_592_050_773_658), | ||||
|         kind: EventType::RoomPowerLevels, | ||||
|         content: json!({"testing": 123}), | ||||
|         state_key: Some("state".to_string()), | ||||
|         prev_events: vec![( | ||||
|             EventId::try_from("$previousevent:matrix.org").unwrap(), | ||||
|             EventHash { sha256: "123567".to_string() }, | ||||
|         )], | ||||
|         depth: 2_u32.into(), | ||||
|         auth_events: vec![( | ||||
|             EventId::try_from("$someauthevent:matrix.org").unwrap(), | ||||
|             EventHash { sha256: "21389CFEDABC".to_string() }, | ||||
|         )], | ||||
|         redacts: Some(EventId::try_from("$9654:matrix.org").unwrap()), | ||||
|         unsigned: (&unsigned).clone(), | ||||
|         hashes: EventHash { sha256: "1233543bABACDEF".to_string() }, | ||||
|         signatures: (&signatures).clone(), | ||||
|     }; | ||||
| 
 | ||||
|     assert_matches!( | ||||
|         v1_stub.into_v1_pdu( | ||||
|             RoomId::try_from("!n8f893n9:example.com").unwrap(), | ||||
|             EventId::try_from("$somejoinevent:matrix.org").unwrap() | ||||
|         ), | ||||
|         RoomV1Pdu { | ||||
|             room_id, | ||||
|             event_id, | ||||
|             sender, | ||||
|             origin, | ||||
|             origin_server_ts, | ||||
|             kind, | ||||
|             content, | ||||
|             state_key, | ||||
|             prev_events, | ||||
|             depth, | ||||
|             auth_events, | ||||
|             redacts, | ||||
|             unsigned, | ||||
|             hashes: EventHash { sha256 }, | ||||
|             signatures, | ||||
|         } if room_id == RoomId::try_from("!n8f893n9:example.com").unwrap() | ||||
|             && event_id == EventId::try_from("$somejoinevent:matrix.org").unwrap() | ||||
|             && sender == UserId::try_from("@sender:example.com").unwrap() | ||||
|             && origin == "matrix.org" | ||||
|             && origin_server_ts == SystemTime::UNIX_EPOCH + Duration::from_millis(1_592_050_773_658) | ||||
|             && kind == EventType::RoomPowerLevels | ||||
|             && content == json!({"testing": 123}) | ||||
|             && state_key == Some("state".to_string()) | ||||
|             && prev_events[0].0 == EventId::try_from("$previousevent:matrix.org").unwrap() | ||||
|             && prev_events[0].1.sha256 == "123567" | ||||
|             && depth == 2_u32.into() | ||||
|             && auth_events.first().unwrap().0 == EventId::try_from("$someauthevent:matrix.org").unwrap() | ||||
|             && auth_events.first().unwrap().1.sha256 == "21389CFEDABC" | ||||
|             && redacts == Some(EventId::try_from("$9654:matrix.org").unwrap()) | ||||
|             && unsigned == (&unsigned).clone() | ||||
|             && sha256 == "1233543bABACDEF" | ||||
|             && signatures == (&signatures).clone() | ||||
|     ); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| fn convert_v3_stub_to_pdu() { | ||||
|     let mut signatures = BTreeMap::new(); | ||||
|     let mut inner_signature = BTreeMap::new(); | ||||
|     inner_signature.insert( | ||||
|         "ed25519:key_version".to_string(), | ||||
|         "86BytesOfSignatureOfTheRedactedEvent".to_string(), | ||||
|     ); | ||||
|     signatures.insert("example.com".to_string(), inner_signature); | ||||
| 
 | ||||
|     let mut unsigned = BTreeMap::new(); | ||||
|     unsigned.insert("somekey".to_string(), json!({"a": 456})); | ||||
| 
 | ||||
|     let v3_stub = RoomV3PduStub { | ||||
|         sender: UserId::try_from("@sender:example.com").unwrap(), | ||||
|         origin: "matrix.org".to_string(), | ||||
|         origin_server_ts: SystemTime::UNIX_EPOCH + Duration::from_millis(1_592_050_773_658), | ||||
|         kind: EventType::RoomPowerLevels, | ||||
|         content: json!({"testing": 123}), | ||||
|         state_key: Some("state".to_string()), | ||||
|         prev_events: vec![EventId::try_from("$previousevent:matrix.org").unwrap()], | ||||
|         depth: 2_u32.into(), | ||||
|         auth_events: vec![EventId::try_from("$someauthevent:matrix.org").unwrap()], | ||||
|         redacts: Some(EventId::try_from("$9654:matrix.org").unwrap()), | ||||
|         unsigned: (&unsigned).clone(), | ||||
|         hashes: EventHash { sha256: "1233543bABACDEF".to_string() }, | ||||
|         signatures: (&signatures).clone(), | ||||
|     }; | ||||
| 
 | ||||
|     assert_matches!( | ||||
|         v3_stub.into_v3_pdu(RoomId::try_from("!n8f893n9:example.com").unwrap()), | ||||
|         RoomV3Pdu { | ||||
|             room_id, | ||||
|             sender, | ||||
|             origin, | ||||
|             origin_server_ts, | ||||
|             kind, | ||||
|             content, | ||||
|             state_key, | ||||
|             prev_events, | ||||
|             depth, | ||||
|             auth_events, | ||||
|             redacts, | ||||
|             unsigned, | ||||
|             hashes: EventHash { sha256 }, | ||||
|             signatures, | ||||
|         } if room_id == RoomId::try_from("!n8f893n9:example.com").unwrap() | ||||
|             && sender == UserId::try_from("@sender:example.com").unwrap() | ||||
|             && origin == "matrix.org" | ||||
|             && origin_server_ts == SystemTime::UNIX_EPOCH + Duration::from_millis(1_592_050_773_658) | ||||
|             && kind == EventType::RoomPowerLevels | ||||
|             && content == json!({"testing": 123}) | ||||
|             && state_key == Some("state".to_string()) | ||||
|             && prev_events == vec![EventId::try_from("$previousevent:matrix.org").unwrap()] | ||||
|             && depth == 2_u32.into() | ||||
|             && auth_events == vec![EventId::try_from("$someauthevent:matrix.org").unwrap()] | ||||
|             && redacts == Some(EventId::try_from("$9654:matrix.org").unwrap()) | ||||
|             && unsigned == (&unsigned).clone() | ||||
|             && sha256 == "1233543bABACDEF" | ||||
|             && signatures == (&signatures).clone() | ||||
|     ); | ||||
| } | ||||
| @ -1,5 +1,9 @@ | ||||
| # [unreleased] | ||||
| 
 | ||||
| Breaking Changes: | ||||
| 
 | ||||
| * Replace `RoomV3Pdu` with `ruma_events::pdu::{Pdu, PduStub}`. | ||||
| 
 | ||||
| Improvements: | ||||
| 
 | ||||
| * Add endpoints: | ||||
| @ -15,6 +19,7 @@ Improvements: | ||||
|         create_join_event_template::v1 | ||||
|     }, | ||||
|     query::get_room_information::v1, | ||||
|     transactions::send_transaction_message::v1, | ||||
|     version::get_server_version::v1 | ||||
|   ``` | ||||
| 
 | ||||
|  | ||||
| @ -2,69 +2,10 @@ | ||||
| 
 | ||||
| #![warn(missing_docs)] | ||||
| 
 | ||||
| use std::{collections::BTreeMap, time::SystemTime}; | ||||
| 
 | ||||
| use ::serde::{Deserialize, Serialize}; | ||||
| use js_int::UInt; | ||||
| use ruma_events::EventType; | ||||
| use ruma_identifiers::{EventId, RoomId, UserId}; | ||||
| use serde_json::Value as JsonValue; | ||||
| 
 | ||||
| mod serde; | ||||
| 
 | ||||
| pub mod directory; | ||||
| pub mod discovery; | ||||
| pub mod membership; | ||||
| pub mod query; | ||||
| 
 | ||||
| /// A 'persistent data unit' (event) for room versions 3 and beyond.
 | ||||
| #[derive(Deserialize, Serialize)] | ||||
| pub struct RoomV3Pdu { | ||||
|     /// The room this event belongs to.
 | ||||
|     pub room_id: RoomId, | ||||
|     /// The user id of the user who sent this event.
 | ||||
|     pub sender: UserId, | ||||
|     /// The `server_name` of the homeserver that created this event.
 | ||||
|     pub origin: String, | ||||
|     /// Timestamp (milliseconds since the UNIX epoch) on originating homeserver
 | ||||
|     /// of when this event was created.
 | ||||
|     #[serde(with = "ruma_serde::time::ms_since_unix_epoch")] | ||||
|     pub origin_server_ts: SystemTime, | ||||
| 
 | ||||
|     // TODO: Replace with event content collection from ruma-events once that exists
 | ||||
|     /// The event's type.
 | ||||
|     #[serde(rename = "type")] | ||||
|     pub kind: EventType, | ||||
|     /// The event's content.
 | ||||
|     pub content: JsonValue, | ||||
| 
 | ||||
|     /// A key that determines which piece of room state the event represents.
 | ||||
|     #[serde(skip_serializing_if = "Option::is_none")] | ||||
|     pub state_key: Option<String>, | ||||
|     /// Event IDs for the most recent events in the room that the homeserver was
 | ||||
|     /// aware of when it created this event.
 | ||||
|     pub prev_events: Vec<EventId>, | ||||
|     /// The maximum depth of the `prev_events`, plus one.
 | ||||
|     pub depth: UInt, | ||||
|     /// Event IDs for the authorization events that would allow this event to be
 | ||||
|     /// in the room.
 | ||||
|     pub auth_events: Vec<EventId>, | ||||
|     /// For redaction events, the ID of the event being redacted.
 | ||||
|     #[serde(skip_serializing_if = "Option::is_none")] | ||||
|     pub redacts: Option<EventId>, | ||||
|     /// Additional data added by the origin server but not covered by the
 | ||||
|     /// signatures.
 | ||||
|     #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] | ||||
|     pub unsigned: BTreeMap<String, JsonValue>, | ||||
|     /// Content hashes of the PDU.
 | ||||
|     pub hashes: EventHash, | ||||
|     /// Signatures for the PDU.
 | ||||
|     pub signatures: BTreeMap<String, BTreeMap<String, String>>, | ||||
| } | ||||
| 
 | ||||
| /// Content hashes of a PDU.
 | ||||
| #[derive(Clone, Debug, Deserialize, Serialize)] | ||||
| pub struct EventHash { | ||||
|     /// The SHA-256 hash.
 | ||||
|     pub sha256: String, | ||||
| } | ||||
| pub mod transactions; | ||||
|  | ||||
| @ -2,11 +2,9 @@ | ||||
| 
 | ||||
| pub mod v1; | ||||
| 
 | ||||
| use ruma_events::EventJson; | ||||
| use ruma_events::{pdu::Pdu, EventJson}; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| 
 | ||||
| use crate::RoomV3Pdu; | ||||
| 
 | ||||
| /// Full state of the room.
 | ||||
| #[derive(Clone, Debug, Deserialize, Serialize)] | ||||
| pub struct RoomState { | ||||
| @ -14,7 +12,7 @@ pub struct RoomState { | ||||
|     pub origin: String, | ||||
|     /// The full set of authorization events that make up the state of the room,
 | ||||
|     /// and their authorization events, recursively.
 | ||||
|     pub auth_chain: Vec<EventJson<RoomV3Pdu>>, | ||||
|     pub auth_chain: Vec<EventJson<Pdu>>, | ||||
|     /// The room state.
 | ||||
|     pub state: Vec<EventJson<RoomV3Pdu>>, | ||||
|     pub state: Vec<EventJson<Pdu>>, | ||||
| } | ||||
|  | ||||
| @ -1,15 +1,10 @@ | ||||
| //! [PUT /_matrix/federation/v1/send_join/{roomId}/{eventId}](https://matrix.org/docs/spec/server_server/r0.1.3#put-matrix-federation-v1-send-join-roomid-eventid)
 | ||||
| 
 | ||||
| use std::{collections::BTreeMap, time::SystemTime}; | ||||
| 
 | ||||
| use js_int::UInt; | ||||
| use ruma_api::ruma_api; | ||||
| use ruma_events::EventType; | ||||
| use ruma_identifiers::{EventId, RoomId, UserId}; | ||||
| use serde_json::Value as JsonValue; | ||||
| use ruma_events::pdu::PduStub; | ||||
| use ruma_identifiers::{EventId, RoomId}; | ||||
| 
 | ||||
| use super::RoomState; | ||||
| use crate::{EventHash, RoomV3Pdu}; | ||||
| 
 | ||||
| ruma_api! { | ||||
|     metadata { | ||||
| @ -29,44 +24,9 @@ ruma_api! { | ||||
|         #[ruma_api(path)] | ||||
|         pub event_id: EventId, | ||||
| 
 | ||||
|         /// The user id of the user who sent this event.
 | ||||
|         pub sender: UserId, | ||||
|         /// The `server_name` of the homeserver that created this event.
 | ||||
|         pub origin: String, | ||||
|         /// Timestamp (milliseconds since the UNIX epoch) on originating homeserver
 | ||||
|         /// of when this event was created.
 | ||||
|         #[serde(with = "ruma_serde::time::ms_since_unix_epoch")] | ||||
|         pub origin_server_ts: SystemTime, | ||||
| 
 | ||||
|         // TODO: Replace with event content collection from ruma-events once that exists
 | ||||
|         /// The event's type.
 | ||||
|         #[serde(rename = "type")] | ||||
|         pub kind: EventType, | ||||
|         /// The event's content.
 | ||||
|         pub content: JsonValue, | ||||
| 
 | ||||
|         /// A key that determines which piece of room state the event represents.
 | ||||
|         #[serde(skip_serializing_if = "Option::is_none")] | ||||
|         pub state_key: Option<String>, | ||||
|         /// Event IDs for the most recent events in the room that the homeserver was
 | ||||
|         /// aware of when it created this event.
 | ||||
|         pub prev_events: Vec<EventId>, | ||||
|         /// The maximum depth of the `prev_events`, plus one.
 | ||||
|         pub depth: UInt, | ||||
|         /// Event IDs for the authorization events that would allow this event to be
 | ||||
|         /// in the room.
 | ||||
|         pub auth_events: Vec<EventId>, | ||||
|         /// For redaction events, the ID of the event being redacted.
 | ||||
|         #[serde(skip_serializing_if = "Option::is_none")] | ||||
|         pub redacts: Option<EventId>, | ||||
|         /// Additional data added by the origin server but not covered by the
 | ||||
|         /// signatures.
 | ||||
|         #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] | ||||
|         pub unsigned: BTreeMap<String, JsonValue>, | ||||
|         /// Content hashes of the PDU.
 | ||||
|         pub hashes: EventHash, | ||||
|         /// Signatures for the PDU.
 | ||||
|         pub signatures: BTreeMap<String, BTreeMap<String, String>>, | ||||
|         /// PDU type without event and room IDs.
 | ||||
|         #[ruma_api(body)] | ||||
|         pub pdu_stub: PduStub, | ||||
|     } | ||||
| 
 | ||||
|     response { | ||||
| @ -76,29 +36,3 @@ ruma_api! { | ||||
|         pub room_state: RoomState, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Request { | ||||
|     /// Helper method to get event ID and PDU (with room ID) from the request
 | ||||
|     /// parameters.
 | ||||
|     pub fn into_id_and_v3_pdu(self) -> (EventId, RoomV3Pdu) { | ||||
|         ( | ||||
|             self.event_id, | ||||
|             RoomV3Pdu { | ||||
|                 room_id: self.room_id, | ||||
|                 sender: self.sender, | ||||
|                 origin: self.origin, | ||||
|                 origin_server_ts: self.origin_server_ts, | ||||
|                 kind: self.kind, | ||||
|                 content: self.content, | ||||
|                 state_key: self.state_key, | ||||
|                 prev_events: self.prev_events, | ||||
|                 depth: self.depth, | ||||
|                 auth_events: self.auth_events, | ||||
|                 redacts: self.redacts, | ||||
|                 unsigned: self.unsigned, | ||||
|                 hashes: self.hashes, | ||||
|                 signatures: self.signatures, | ||||
|             }, | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -2,11 +2,9 @@ | ||||
| 
 | ||||
| use js_int::UInt; | ||||
| use ruma_api::ruma_api; | ||||
| use ruma_events::EventJson; | ||||
| use ruma_events::{pdu::Pdu, EventJson}; | ||||
| use ruma_identifiers::{RoomId, UserId}; | ||||
| 
 | ||||
| use crate::RoomV3Pdu; | ||||
| 
 | ||||
| ruma_api! { | ||||
|     metadata { | ||||
|         description: "Send a request for a join event template to a resident server.", | ||||
| @ -34,6 +32,6 @@ ruma_api! { | ||||
|         /// The version of the room where the server is trying to join.
 | ||||
|         pub room_version: Option<UInt>, | ||||
|         /// An unsigned template event.
 | ||||
|         pub event: EventJson<RoomV3Pdu>, | ||||
|         pub event: EventJson<Pdu>, | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,3 +1,4 @@ | ||||
| //! Modules for custom serde de/-serialization implementations.
 | ||||
| 
 | ||||
| pub mod pdu_process_response; | ||||
| pub mod room_state; | ||||
|  | ||||
							
								
								
									
										156
									
								
								ruma-federation-api/src/serde/pdu_process_response.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								ruma-federation-api/src/serde/pdu_process_response.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,156 @@ | ||||
| use std::{collections::BTreeMap, fmt}; | ||||
| 
 | ||||
| use ruma_identifiers::EventId; | ||||
| use serde::{ | ||||
|     de::{Deserializer, MapAccess, Visitor}, | ||||
|     ser::{SerializeMap, Serializer}, | ||||
|     Deserialize, Serialize, | ||||
| }; | ||||
| 
 | ||||
| pub fn serialize<S>( | ||||
|     response: &BTreeMap<EventId, Result<(), String>>, | ||||
|     serializer: S, | ||||
| ) -> Result<S::Ok, S::Error> | ||||
| where | ||||
|     S: Serializer, | ||||
| { | ||||
|     let mut map = serializer.serialize_map(Some(response.len()))?; | ||||
|     for (key, value) in response { | ||||
|         let wrapped_error = WrappedError { | ||||
|             error: match value { | ||||
|                 Ok(_) => None, | ||||
|                 Err(error) => Some(error.clone()), | ||||
|             }, | ||||
|         }; | ||||
|         map.serialize_entry(&key, &wrapped_error)?; | ||||
|     } | ||||
|     map.end() | ||||
| } | ||||
| 
 | ||||
| pub fn deserialize<'de, D>( | ||||
|     deserializer: D, | ||||
| ) -> Result<BTreeMap<EventId, Result<(), String>>, D::Error> | ||||
| where | ||||
|     D: Deserializer<'de>, | ||||
| { | ||||
|     deserializer.deserialize_map(PduProcessResponseVisitor) | ||||
| } | ||||
| 
 | ||||
| #[derive(Deserialize, Serialize)] | ||||
| struct WrappedError { | ||||
|     #[serde(skip_serializing_if = "Option::is_none")] | ||||
|     error: Option<String>, | ||||
| } | ||||
| 
 | ||||
| struct PduProcessResponseVisitor; | ||||
| 
 | ||||
| impl<'de> Visitor<'de> for PduProcessResponseVisitor { | ||||
|     type Value = BTreeMap<EventId, Result<(), String>>; | ||||
| 
 | ||||
|     fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { | ||||
|         formatter.write_str("A map of EventIds to a map of optional errors") | ||||
|     } | ||||
| 
 | ||||
|     fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error> | ||||
|     where | ||||
|         M: MapAccess<'de>, | ||||
|     { | ||||
|         let mut map = BTreeMap::new(); | ||||
| 
 | ||||
|         while let Some((key, value)) = access.next_entry::<EventId, WrappedError>()? { | ||||
|             let v = match value.error { | ||||
|                 None => Ok(()), | ||||
|                 Some(error) => Err(error), | ||||
|             }; | ||||
|             map.insert(key, v); | ||||
|         } | ||||
|         Ok(map) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use std::{collections::BTreeMap, convert::TryFrom}; | ||||
| 
 | ||||
|     use ruma_identifiers::EventId; | ||||
|     use serde_json::{json, value::Serializer as JsonSerializer}; | ||||
| 
 | ||||
|     use super::{deserialize, serialize}; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn serialize_error() { | ||||
|         let mut response: BTreeMap<EventId, Result<(), String>> = BTreeMap::new(); | ||||
|         response.insert( | ||||
|             EventId::try_from("$someevent:matrix.org").unwrap(), | ||||
|             Err("Some processing error.".into()), | ||||
|         ); | ||||
| 
 | ||||
|         let serialized = serialize(&response, JsonSerializer).unwrap(); | ||||
|         let json = json!({ | ||||
|             "$someevent:matrix.org": { "error": "Some processing error." } | ||||
|         }); | ||||
|         assert_eq!(serialized, json); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn serialize_ok() { | ||||
|         let mut response: BTreeMap<EventId, Result<(), String>> = BTreeMap::new(); | ||||
|         response.insert(EventId::try_from("$someevent:matrix.org").unwrap(), Ok(())); | ||||
| 
 | ||||
|         let serialized = serialize(&response, serde_json::value::Serializer).unwrap(); | ||||
|         let json = json!({ | ||||
|             "$someevent:matrix.org": {} | ||||
|         }); | ||||
|         assert_eq!(serialized, json); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn deserialize_error() { | ||||
|         let json = json!({ | ||||
|             "$someevent:matrix.org": { "error": "Some processing error." } | ||||
|         }); | ||||
| 
 | ||||
|         let response = deserialize(json).unwrap(); | ||||
|         let event_id = EventId::try_from("$someevent:matrix.org").unwrap(); | ||||
| 
 | ||||
|         let event_response = response.get(&event_id).unwrap().clone().unwrap_err(); | ||||
|         assert_eq!(event_response, "Some processing error."); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn deserialize_null_error_is_ok() { | ||||
|         let json = json!({ | ||||
|             "$someevent:matrix.org": { "error": null } | ||||
|         }); | ||||
| 
 | ||||
|         let response = deserialize(json).unwrap(); | ||||
|         let event_id = EventId::try_from("$someevent:matrix.org").unwrap(); | ||||
| 
 | ||||
|         assert!(response.get(&event_id).unwrap().is_ok()); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn desieralize_empty_error_is_err() { | ||||
|         let json = json!({ | ||||
|             "$someevent:matrix.org": { "error": "" } | ||||
|         }); | ||||
| 
 | ||||
|         let response = deserialize(json).unwrap(); | ||||
|         let event_id = EventId::try_from("$someevent:matrix.org").unwrap(); | ||||
| 
 | ||||
|         let event_response = response.get(&event_id).unwrap().clone().unwrap_err(); | ||||
|         assert_eq!(event_response, ""); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn deserialize_ok() { | ||||
|         let json = json!({ | ||||
|             "$someevent:matrix.org": {} | ||||
|         }); | ||||
|         let response = deserialize(json).unwrap(); | ||||
|         assert!(response | ||||
|             .get(&EventId::try_from("$someevent:matrix.org").unwrap()) | ||||
|             .unwrap() | ||||
|             .is_ok()); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										3
									
								
								ruma-federation-api/src/transactions.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								ruma-federation-api/src/transactions.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | ||||
| //! Endpoints for exchanging transaction messages between homeservers.
 | ||||
| 
 | ||||
| pub mod send_transaction_message; | ||||
| @ -0,0 +1,3 @@ | ||||
| //! Endpoint to send live activity messages to another server.
 | ||||
| 
 | ||||
| pub mod v1; | ||||
| @ -0,0 +1,59 @@ | ||||
| //! [PUT /_matrix/federation/v1/send/{txnId}](https://matrix.org/docs/spec/server_server/r0.1.3#put-matrix-federation-v1-send-txnid)
 | ||||
| 
 | ||||
| use std::{collections::BTreeMap, time::SystemTime}; | ||||
| 
 | ||||
| use ruma_api::ruma_api; | ||||
| use ruma_events::pdu::Pdu; | ||||
| use ruma_identifiers::EventId; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use serde_json::Value as JsonValue; | ||||
| 
 | ||||
| ruma_api! { | ||||
|     metadata { | ||||
|         description: "Send transaction messages to another server", | ||||
|         name: "send_transaction_message", | ||||
|         method: PUT, | ||||
|         path: "/_matrix/federation/v1/send/:transaction_id", | ||||
|         rate_limited: false, | ||||
|         requires_authentication: true, | ||||
|     } | ||||
| 
 | ||||
|     request { | ||||
|         /// A transaction ID unique between sending and receiving homeservers.
 | ||||
|         #[ruma_api(path)] | ||||
|         pub transaction_id: String, | ||||
| 
 | ||||
|         /// The server_name of the homeserver sending this transaction.
 | ||||
|         pub origin: String, | ||||
| 
 | ||||
|         /// POSIX timestamp in milliseconds on the originating homeserver when this transaction started.
 | ||||
|         #[serde(with = "ruma_serde::time::ms_since_unix_epoch")] | ||||
|         pub origin_server_ts: SystemTime, | ||||
| 
 | ||||
|         /// List of persistent updates to rooms.
 | ||||
|         ///
 | ||||
|         /// Must not be more than 50 items.
 | ||||
|         pub pdus: Vec<Pdu>, | ||||
| 
 | ||||
|         /// List of ephemeral messages.
 | ||||
|         ///
 | ||||
|         /// Must not be more than 100 items.
 | ||||
|         #[serde(skip_serializing_if = "Vec::is_empty")] | ||||
|         pub edus: Vec<Edu>, | ||||
|     } | ||||
| 
 | ||||
|     response { | ||||
|         /// Map of event IDs and response for each PDU given in the request.
 | ||||
|         #[serde(with = "crate::serde::pdu_process_response")] | ||||
|         pub pdus: BTreeMap<EventId, Result<(), String>>, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Type for passing ephemeral data to homeservers.
 | ||||
| #[derive(Clone, Debug, Deserialize, Serialize)] | ||||
| pub struct Edu { | ||||
|     /// Type of the ephemeral message.
 | ||||
|     pub edu_type: String, | ||||
|     /// Content of ephemeral message
 | ||||
|     pub content: JsonValue, | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user