Add endpoints for joining rooms
This commit is contained in:
parent
ccbf216f39
commit
26307b6894
@ -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"
|
||||
|
@ -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
1
src/serde.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod room_state;
|
150
src/serde/room_state.rs
Normal file
150
src/serde/room_state.rs
Normal 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()
|
||||
);
|
||||
}
|
||||
}
|
@ -2,3 +2,4 @@
|
||||
|
||||
pub mod get_public_rooms;
|
||||
pub mod get_server_version;
|
||||
pub mod join;
|
||||
|
4
src/v1/join.rs
Normal file
4
src/v1/join.rs
Normal file
@ -0,0 +1,4 @@
|
||||
//! Endpoints for joining rooms.
|
||||
|
||||
pub mod create_join_event;
|
||||
pub mod create_join_event_template;
|
115
src/v1/join/create_join_event.rs
Normal file
115
src/v1/join/create_join_event.rs
Normal 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>>,
|
||||
}
|
39
src/v1/join/create_join_event_template.rs
Normal file
39
src/v1/join/create_join_event_template.rs
Normal 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>,
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user