Add endpoints for joining rooms

This commit is contained in:
iinuwa 2020-05-22 17:23:01 -05:00 committed by GitHub
parent ccbf216f39
commit 26307b6894
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 314 additions and 2 deletions

View File

@ -14,6 +14,7 @@ version = "0.0.1"
[dependencies]
js_int = "0.1.4"
matches = "0.1.8"
ruma-api = "0.16.0-rc.3"
ruma-events = "0.21.0-beta.1"
ruma-identifiers = "0.16.0"

View File

@ -4,12 +4,13 @@
use std::collections::BTreeMap;
use ::serde::{Deserialize, Serialize};
use js_int::UInt;
use ruma_events::EventType;
use ruma_identifiers::{EventId, RoomId, UserId};
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
mod serde;
pub mod unversioned;
pub mod v1;
pub mod v2;
@ -59,7 +60,7 @@ pub struct RoomV3Pdu {
}
/// Content hashes of a PDU.
#[derive(Deserialize, Serialize)]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct EventHash {
/// The SHA-256 hash.
pub sha256: String,

1
src/serde.rs Normal file
View File

@ -0,0 +1 @@
pub mod room_state;

150
src/serde/room_state.rs Normal file
View File

@ -0,0 +1,150 @@
//! A module to deserialize a RoomState struct from incorrectly specified v1
//! send_join endpoint.
//!
//! For more information, see this [GitHub issue](https://github.com/matrix-org/matrix-doc/issues/2541).
use std::fmt;
use serde::{
de::{Deserializer, Error, IgnoredAny, SeqAccess, Visitor},
ser::{SerializeSeq, Serializer},
};
use crate::v1::join::create_join_event::RoomState;
pub fn serialize<S>(room_state: &RoomState, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_seq(Some(2))?;
seq.serialize_element(&200)?;
seq.serialize_element(room_state)?;
seq.end()
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<RoomState, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_seq(RoomStateVisitor)
}
struct RoomStateVisitor;
impl<'de> Visitor<'de> for RoomStateVisitor {
type Value = RoomState;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("Room State response wrapped in an array.")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let expected = "a two-element list in the response";
if seq.next_element::<IgnoredAny>()?.is_none() {
return Err(A::Error::invalid_length(0, &expected));
}
let room_state = seq
.next_element()?
.ok_or_else(|| A::Error::invalid_length(1, &expected))?;
while let Some(IgnoredAny) = seq.next_element()? {
// ignore extra elements
}
Ok(room_state)
}
}
#[cfg(test)]
mod tests {
use matches::assert_matches;
use serde_json::{json, to_value as to_json_value};
use super::{deserialize, serialize, RoomState};
#[test]
fn test_deserialize_response() {
let response = json!([
200,
{
"origin": "example.com",
"auth_chain": [],
"state": []
}
]);
let parsed = deserialize(response).unwrap();
assert_matches!(
parsed,
RoomState { origin, auth_chain, state }
if origin == "example.com"
&& auth_chain.is_empty()
&& state.is_empty()
);
}
#[test]
fn test_serialize_response() {
let room_state = RoomState {
origin: "matrix.org".to_string(),
auth_chain: Vec::new(),
state: Vec::new(),
};
let serialized = serialize(&room_state, serde_json::value::Serializer).unwrap();
let expected = to_json_value(&json!(
[
200,
{
"origin": "matrix.org",
"auth_chain": [],
"state": []
}
]
))
.unwrap();
assert_eq!(serialized, expected);
}
#[test]
fn test_too_short_array() {
let json = json!([200]);
let failed_room_state = deserialize(json);
assert_eq!(
failed_room_state.unwrap_err().to_string(),
"invalid length 1, expected a two-element list in the response"
);
}
#[test]
fn test_not_an_array() {
let json = json!({
"origin": "matrix.org",
"auth_chain": [],
"state": []
});
let failed_room_state = deserialize(json);
assert_eq!(
failed_room_state.unwrap_err().to_string(),
"invalid type: map, expected Room State response wrapped in an array.",
)
}
#[test]
fn test_too_long_array() {
let json = json!([200, {"origin": "", "auth_chain": [], "state": []}, 200]);
assert_matches!(
deserialize(json).unwrap(),
RoomState { origin, auth_chain, state }
if origin == ""
&& auth_chain.is_empty()
&& state.is_empty()
);
}
}

View File

@ -2,3 +2,4 @@
pub mod get_public_rooms;
pub mod get_server_version;
pub mod join;

4
src/v1/join.rs Normal file
View File

@ -0,0 +1,4 @@
//! Endpoints for joining rooms.
pub mod create_join_event;
pub mod create_join_event_template;

View File

@ -0,0 +1,115 @@
//! [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;
use js_int::UInt;
use ruma_api::ruma_api;
use ruma_events::{EventJson, EventType};
use ruma_identifiers::{EventId, RoomId, UserId};
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
use crate::{EventHash, RoomV3Pdu};
ruma_api! {
metadata {
description: "Send a join event to a resident server.",
name: "create_join_event",
method: PUT,
path: "/_matrix/federation/v1/send_join/:room_id/:event_id",
rate_limited: false,
requires_authentication: true,
}
request {
/// The room ID that is about to be joined.
#[ruma_api(path)]
pub room_id: RoomId,
/// The user ID the join event will be for.
#[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.
pub origin_server_ts: UInt,
// 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 = "serde_json::Map::is_empty")]
pub unsigned: serde_json::Map<String, JsonValue>,
/// Content hashes of the PDU.
pub hashes: EventHash,
/// Signatures for the PDU.
pub signatures: BTreeMap<String, BTreeMap<String, String>>,
}
response {
/// Full state of the room.
#[ruma_api(body)]
#[serde(with = "crate::serde::room_state")]
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,
},
)
}
}
/// Full state of the room.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct RoomState {
/// The resident server's DNS name.
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>>,
/// The room state.
pub state: Vec<EventJson<RoomV3Pdu>>,
}

View File

@ -0,0 +1,39 @@
//! [GET /_matrix/federation/v1/make_join/{roomId}/{userId}](https://matrix.org/docs/spec/server_server/r0.1.3#get-matrix-federation-v1-make-join-roomid-userid)
use js_int::UInt;
use ruma_api::ruma_api;
use ruma_events::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.",
name: "create_join_event_template",
method: GET,
path: "/_matrix/federation/v1/make_join/:room_id/:user_id",
rate_limited: false,
requires_authentication: true,
}
request {
/// The room ID that is about to be joined.
#[ruma_api(path)]
pub room_id: RoomId,
/// The user ID the join event will be for.
#[ruma_api(path)]
pub user_id: UserId,
#[ruma_api(query)]
/// The room versions the sending server has support for. Defaults to [1].
#[serde(skip_serializing_if = "Vec::is_empty")]
pub ver: Vec<UInt>,
}
response {
/// 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>,
}
}