Merge pull request #15 from JadedBlueEyes/stabilize-appservice-edus
appservice-api: Stabilize support for sending ephemeral data to appservices
This commit is contained in:
commit
112ccc24cb
@ -19,8 +19,8 @@ client = []
|
|||||||
server = []
|
server = []
|
||||||
|
|
||||||
unstable-exhaustive-types = []
|
unstable-exhaustive-types = []
|
||||||
unstable-msc2409 = []
|
|
||||||
unstable-msc3202 = []
|
unstable-msc3202 = []
|
||||||
|
unstable-msc4203 = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
js_int = { workspace = true, features = ["serde"] }
|
js_int = { workspace = true, features = ["serde"] }
|
||||||
|
@ -7,39 +7,29 @@ pub mod v1 {
|
|||||||
//!
|
//!
|
||||||
//! [spec]: https://spec.matrix.org/latest/application-service-api/#put_matrixappv1transactionstxnid
|
//! [spec]: https://spec.matrix.org/latest/application-service-api/#put_matrixappv1transactionstxnid
|
||||||
|
|
||||||
#[cfg(any(feature = "unstable-msc2409", feature = "unstable-msc3202"))]
|
use std::borrow::Cow;
|
||||||
|
#[cfg(feature = "unstable-msc3202")]
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
|
||||||
use std::{
|
|
||||||
collections::btree_map,
|
|
||||||
ops::{Deref, DerefMut},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(any(feature = "unstable-msc2409", feature = "unstable-msc3202"))]
|
#[cfg(feature = "unstable-msc3202")]
|
||||||
use js_int::UInt;
|
use js_int::UInt;
|
||||||
#[cfg(any(feature = "unstable-msc2409", feature = "unstable-msc3202"))]
|
#[cfg(feature = "unstable-msc3202")]
|
||||||
use ruma_common::OwnedUserId;
|
use ruma_common::OwnedUserId;
|
||||||
use ruma_common::{
|
use ruma_common::{
|
||||||
api::{request, response, Metadata},
|
api::{request, response, Metadata},
|
||||||
metadata,
|
metadata,
|
||||||
serde::Raw,
|
serde::{from_raw_json_value, JsonObject, Raw},
|
||||||
OwnedTransactionId,
|
OwnedTransactionId,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
|
||||||
use ruma_common::{
|
|
||||||
presence::PresenceState, serde::from_raw_json_value, OwnedEventId, OwnedRoomId,
|
|
||||||
};
|
|
||||||
#[cfg(feature = "unstable-msc3202")]
|
#[cfg(feature = "unstable-msc3202")]
|
||||||
use ruma_common::{OneTimeKeyAlgorithm, OwnedDeviceId};
|
use ruma_common::{OneTimeKeyAlgorithm, OwnedDeviceId};
|
||||||
use ruma_events::AnyTimelineEvent;
|
#[cfg(feature = "unstable-msc4203")]
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
use ruma_events::AnyToDeviceEvent;
|
||||||
use ruma_events::{receipt::Receipt, AnyToDeviceEvent};
|
use ruma_events::{
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
presence::PresenceEvent, receipt::ReceiptEvent, typing::TypingEvent, AnyTimelineEvent,
|
||||||
use serde::Deserializer;
|
};
|
||||||
#[cfg(any(feature = "unstable-msc2409", feature = "unstable-msc3202"))]
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde_json::value::{RawValue as RawJsonValue, Value as JsonValue};
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
|
||||||
use serde_json::{value::RawValue as RawJsonValue, Value as JsonValue};
|
|
||||||
|
|
||||||
const METADATA: Metadata = metadata! {
|
const METADATA: Metadata = metadata! {
|
||||||
method: PUT,
|
method: PUT,
|
||||||
@ -93,17 +83,12 @@ pub mod v1 {
|
|||||||
pub device_unused_fallback_key_types:
|
pub device_unused_fallback_key_types:
|
||||||
BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceId, Vec<OneTimeKeyAlgorithm>>>,
|
BTreeMap<OwnedUserId, BTreeMap<OwnedDeviceId, Vec<OneTimeKeyAlgorithm>>>,
|
||||||
|
|
||||||
/// A list of EDUs.
|
/// A list of ephemeral data.
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
#[serde(default, skip_serializing_if = "<[_]>::is_empty")]
|
||||||
#[serde(
|
pub ephemeral: Vec<EphemeralData>,
|
||||||
default,
|
|
||||||
skip_serializing_if = "<[_]>::is_empty",
|
|
||||||
rename = "de.sorunome.msc2409.ephemeral"
|
|
||||||
)]
|
|
||||||
pub ephemeral: Vec<Edu>,
|
|
||||||
|
|
||||||
/// A list of to-device messages.
|
/// A list of to-device messages.
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
#[cfg(feature = "unstable-msc4203")]
|
||||||
#[serde(
|
#[serde(
|
||||||
default,
|
default,
|
||||||
skip_serializing_if = "<[_]>::is_empty",
|
skip_serializing_if = "<[_]>::is_empty",
|
||||||
@ -129,9 +114,8 @@ pub mod v1 {
|
|||||||
device_one_time_keys_count: BTreeMap::new(),
|
device_one_time_keys_count: BTreeMap::new(),
|
||||||
#[cfg(feature = "unstable-msc3202")]
|
#[cfg(feature = "unstable-msc3202")]
|
||||||
device_unused_fallback_key_types: BTreeMap::new(),
|
device_unused_fallback_key_types: BTreeMap::new(),
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
|
||||||
ephemeral: Vec::new(),
|
ephemeral: Vec::new(),
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
#[cfg(feature = "unstable-msc4203")]
|
||||||
to_device: Vec::new(),
|
to_device: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -173,230 +157,118 @@ pub mod v1 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type for passing ephemeral data to homeservers.
|
/// Type for passing ephemeral data to application services.
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
|
||||||
#[derive(Clone, Debug, Serialize)]
|
#[derive(Clone, Debug, Serialize)]
|
||||||
#[non_exhaustive]
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
pub enum Edu {
|
#[serde(untagged)]
|
||||||
/// An EDU representing presence updates for users of the sending homeserver.
|
pub enum EphemeralData {
|
||||||
Presence(PresenceContent),
|
/// A presence update for a user.
|
||||||
|
Presence(PresenceEvent),
|
||||||
|
|
||||||
/// An EDU representing receipt updates for users of the sending homeserver.
|
/// A receipt update for a room.
|
||||||
Receipt(ReceiptContent),
|
Receipt(ReceiptEvent),
|
||||||
|
|
||||||
/// A typing notification EDU for a user in a room.
|
/// A typing notification update for a room.
|
||||||
Typing(TypingContent),
|
Typing(TypingEvent),
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[serde(skip)]
|
_Custom(_CustomEphemeralData),
|
||||||
_Custom(JsonValue),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
impl EphemeralData {
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
/// A reference to the `type` string of the data.
|
||||||
struct EduDeHelper {
|
pub fn data_type(&self) -> &str {
|
||||||
/// The message type field
|
match self {
|
||||||
r#type: String,
|
Self::Presence(_) => "m.presence",
|
||||||
content: Box<RawJsonValue>,
|
Self::Receipt(_) => "m.receipt",
|
||||||
|
Self::Typing(_) => "m.typing",
|
||||||
|
Self::_Custom(c) => &c.data_type,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
/// The data as a JSON object.
|
||||||
impl<'de> Deserialize<'de> for Edu {
|
///
|
||||||
|
/// Prefer to use the public variants of `EphemeralData` where possible; this method is
|
||||||
|
/// meant to be used for unsupported data types only.
|
||||||
|
pub fn data(&self) -> Cow<'_, JsonObject> {
|
||||||
|
fn serialize<T: Serialize>(obj: &T) -> JsonObject {
|
||||||
|
match serde_json::to_value(obj).expect("ephemeral data serialization to succeed") {
|
||||||
|
JsonValue::Object(obj) => obj,
|
||||||
|
_ => panic!("all ephemeral data types must serialize to objects"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Self::Presence(d) => Cow::Owned(serialize(d)),
|
||||||
|
Self::Receipt(d) => Cow::Owned(serialize(d)),
|
||||||
|
Self::Typing(d) => Cow::Owned(serialize(d)),
|
||||||
|
Self::_Custom(c) => Cow::Borrowed(&c.data),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for EphemeralData {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
let json = Box::<RawJsonValue>::deserialize(deserializer)?;
|
#[derive(Deserialize)]
|
||||||
let EduDeHelper { r#type, content } = from_raw_json_value(&json)?;
|
struct EphemeralDataDeHelper {
|
||||||
|
/// The data type.
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
data_type: String,
|
||||||
|
}
|
||||||
|
|
||||||
Ok(match r#type.as_ref() {
|
let json = Box::<RawJsonValue>::deserialize(deserializer)?;
|
||||||
"m.presence" => Self::Presence(from_raw_json_value(&content)?),
|
let EphemeralDataDeHelper { data_type } = from_raw_json_value(&json)?;
|
||||||
"m.receipt" => Self::Receipt(from_raw_json_value(&content)?),
|
|
||||||
"m.typing" => Self::Typing(from_raw_json_value(&content)?),
|
Ok(match data_type.as_ref() {
|
||||||
_ => Self::_Custom(from_raw_json_value(&content)?),
|
"m.presence" => Self::Presence(from_raw_json_value(&json)?),
|
||||||
|
"m.receipt" => Self::Receipt(from_raw_json_value(&json)?),
|
||||||
|
"m.typing" => Self::Typing(from_raw_json_value(&json)?),
|
||||||
|
_ => Self::_Custom(_CustomEphemeralData {
|
||||||
|
data_type,
|
||||||
|
data: from_raw_json_value(&json)?,
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The content for "m.presence" Edu.
|
/// Ephemeral data with an unknown type.
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
#[doc(hidden)]
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Debug, Clone)]
|
||||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
pub struct _CustomEphemeralData {
|
||||||
pub struct PresenceContent {
|
/// The type of the data.
|
||||||
/// A list of presence updates that the receiving server is likely to be interested in.
|
data_type: String,
|
||||||
pub push: Vec<PresenceUpdate>,
|
/// The data.
|
||||||
|
data: JsonObject,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
impl Serialize for _CustomEphemeralData {
|
||||||
impl PresenceContent {
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
/// Creates a new `PresenceContent`.
|
|
||||||
pub fn new(push: Vec<PresenceUpdate>) -> Self {
|
|
||||||
Self { push }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An update to the presence of a user.
|
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
||||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
|
||||||
pub struct PresenceUpdate {
|
|
||||||
/// The user ID this presence EDU is for.
|
|
||||||
pub user_id: OwnedUserId,
|
|
||||||
|
|
||||||
/// The presence of the user.
|
|
||||||
pub presence: PresenceState,
|
|
||||||
|
|
||||||
/// An optional description to accompany the presence.
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
|
||||||
pub status_msg: Option<String>,
|
|
||||||
|
|
||||||
/// The number of milliseconds that have elapsed since the user last did something.
|
|
||||||
pub last_active_ago: UInt,
|
|
||||||
|
|
||||||
/// Whether or not the user is currently active.
|
|
||||||
///
|
|
||||||
/// Defaults to false.
|
|
||||||
#[serde(default, skip_serializing_if = "ruma_common::serde::is_default")]
|
|
||||||
pub currently_active: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
|
||||||
impl PresenceUpdate {
|
|
||||||
/// Creates a new `PresenceUpdate` with the given `user_id`, `presence` and `last_activity`.
|
|
||||||
pub fn new(user_id: OwnedUserId, presence: PresenceState, last_activity: UInt) -> Self {
|
|
||||||
Self {
|
|
||||||
user_id,
|
|
||||||
presence,
|
|
||||||
last_active_ago: last_activity,
|
|
||||||
status_msg: None,
|
|
||||||
currently_active: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The content for "m.receipt" Edu.
|
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
||||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
|
||||||
#[serde(transparent)]
|
|
||||||
pub struct ReceiptContent(pub BTreeMap<OwnedRoomId, ReceiptMap>);
|
|
||||||
|
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
|
||||||
impl ReceiptContent {
|
|
||||||
/// Creates a new `ReceiptContent`.
|
|
||||||
pub fn new(receipts: BTreeMap<OwnedRoomId, ReceiptMap>) -> Self {
|
|
||||||
Self(receipts)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
|
||||||
impl Deref for ReceiptContent {
|
|
||||||
type Target = BTreeMap<OwnedRoomId, ReceiptMap>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
|
||||||
impl DerefMut for ReceiptContent {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
|
||||||
impl IntoIterator for ReceiptContent {
|
|
||||||
type Item = (OwnedRoomId, ReceiptMap);
|
|
||||||
type IntoIter = btree_map::IntoIter<OwnedRoomId, ReceiptMap>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.0.into_iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
|
||||||
impl FromIterator<(OwnedRoomId, ReceiptMap)> for ReceiptContent {
|
|
||||||
fn from_iter<T>(iter: T) -> Self
|
|
||||||
where
|
where
|
||||||
T: IntoIterator<Item = (OwnedRoomId, ReceiptMap)>,
|
S: serde::Serializer,
|
||||||
{
|
{
|
||||||
Self(BTreeMap::from_iter(iter))
|
self.data.serialize(serializer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mapping between user and `ReceiptData`.
|
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
||||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
|
||||||
pub struct ReceiptMap {
|
|
||||||
/// Read receipts for users in the room.
|
|
||||||
#[serde(rename = "m.read")]
|
|
||||||
pub read: BTreeMap<OwnedUserId, ReceiptData>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
|
||||||
impl ReceiptMap {
|
|
||||||
/// Creates a new `ReceiptMap`.
|
|
||||||
pub fn new(read: BTreeMap<OwnedUserId, ReceiptData>) -> Self {
|
|
||||||
Self { read }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Metadata about the event that was last read and when.
|
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
||||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
|
||||||
pub struct ReceiptData {
|
|
||||||
/// Metadata for the read receipt.
|
|
||||||
pub data: Receipt,
|
|
||||||
|
|
||||||
/// The extremity event ID the user has read up to.
|
|
||||||
pub event_ids: Vec<OwnedEventId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
|
||||||
impl ReceiptData {
|
|
||||||
/// Creates a new `ReceiptData`.
|
|
||||||
pub fn new(data: Receipt, event_ids: Vec<OwnedEventId>) -> Self {
|
|
||||||
Self { data, event_ids }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The content for "m.typing" Edu.
|
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
||||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
|
||||||
pub struct TypingContent {
|
|
||||||
/// The room where the user's typing status has been updated.
|
|
||||||
pub room_id: OwnedRoomId,
|
|
||||||
|
|
||||||
/// The user ID that has had their typing status changed.
|
|
||||||
pub user_id: OwnedUserId,
|
|
||||||
|
|
||||||
/// Whether the user is typing in the room or not.
|
|
||||||
pub typing: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
|
||||||
impl TypingContent {
|
|
||||||
/// Creates a new `TypingContent`.
|
|
||||||
pub fn new(room_id: OwnedRoomId, user_id: OwnedUserId, typing: bool) -> Self {
|
|
||||||
Self { room_id, user_id, typing }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "server")]
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use ruma_common::api::{OutgoingRequest, SendAccessToken};
|
use assert_matches2::assert_matches;
|
||||||
use serde_json::json;
|
use js_int::uint;
|
||||||
|
use ruma_common::{event_id, room_id, user_id, MilliSecondsSinceUnixEpoch};
|
||||||
|
use ruma_events::receipt::ReceiptType;
|
||||||
|
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
|
||||||
|
|
||||||
use super::Request;
|
use super::EphemeralData;
|
||||||
|
|
||||||
|
#[cfg(feature = "client")]
|
||||||
#[test]
|
#[test]
|
||||||
fn decode_request_contains_events_field() {
|
fn request_contains_events_field() {
|
||||||
let dummy_event = serde_json::from_value(json!({
|
use ruma_common::api::{OutgoingRequest, SendAccessToken};
|
||||||
|
|
||||||
|
let dummy_event_json = json!({
|
||||||
"type": "m.room.message",
|
"type": "m.room.message",
|
||||||
"event_id": "$143273582443PhrSn:example.com",
|
"event_id": "$143273582443PhrSn:example.com",
|
||||||
"origin_server_ts": 1,
|
"origin_server_ts": 1,
|
||||||
@ -406,11 +278,11 @@ pub mod v1 {
|
|||||||
"body": "test",
|
"body": "test",
|
||||||
"msgtype": "m.text",
|
"msgtype": "m.text",
|
||||||
},
|
},
|
||||||
}))
|
});
|
||||||
.unwrap();
|
let dummy_event = from_json_value(dummy_event_json.clone()).unwrap();
|
||||||
let events = vec![dummy_event];
|
let events = vec![dummy_event];
|
||||||
|
|
||||||
let req = Request::new("any_txn_id".into(), events)
|
let req = super::Request::new("any_txn_id".into(), events)
|
||||||
.try_into_http_request::<Vec<u8>>(
|
.try_into_http_request::<Vec<u8>>(
|
||||||
"https://homeserver.tld",
|
"https://homeserver.tld",
|
||||||
SendAccessToken::IfRequired("auth_tok"),
|
SendAccessToken::IfRequired("auth_tok"),
|
||||||
@ -420,9 +292,98 @@ pub mod v1 {
|
|||||||
let json_body: serde_json::Value = serde_json::from_slice(req.body()).unwrap();
|
let json_body: serde_json::Value = serde_json::from_slice(req.body()).unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
1,
|
json_body,
|
||||||
json_body.as_object().unwrap().get("events").unwrap().as_array().unwrap().len()
|
json!({
|
||||||
|
"events": [
|
||||||
|
dummy_event_json,
|
||||||
|
]
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serde_ephemeral_data() {
|
||||||
|
let room_id = room_id!("!jEsUZKDJdhlrceRyVU:server.local");
|
||||||
|
let user_id = user_id!("@alice:server.local");
|
||||||
|
let event_id = event_id!("$1435641916114394fHBL");
|
||||||
|
|
||||||
|
// Test m.typing serde.
|
||||||
|
let typing_json = json!({
|
||||||
|
"type": "m.typing",
|
||||||
|
"room_id": room_id,
|
||||||
|
"content": {
|
||||||
|
"user_ids": [user_id],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let data = from_json_value::<EphemeralData>(typing_json.clone()).unwrap();
|
||||||
|
assert_matches!(&data, EphemeralData::Typing(typing));
|
||||||
|
assert_eq!(typing.room_id, room_id);
|
||||||
|
assert_eq!(typing.content.user_ids, &[user_id.to_owned()]);
|
||||||
|
|
||||||
|
let serialized_data = to_json_value(data).unwrap();
|
||||||
|
assert_eq!(serialized_data, typing_json);
|
||||||
|
|
||||||
|
// Test m.receipt serde.
|
||||||
|
let receipt_json = json!({
|
||||||
|
"type": "m.receipt",
|
||||||
|
"room_id": room_id,
|
||||||
|
"content": {
|
||||||
|
event_id: {
|
||||||
|
"m.read": {
|
||||||
|
user_id: {
|
||||||
|
"ts": 453,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let data = from_json_value::<EphemeralData>(receipt_json.clone()).unwrap();
|
||||||
|
assert_matches!(&data, EphemeralData::Receipt(receipt));
|
||||||
|
assert_eq!(receipt.room_id, room_id);
|
||||||
|
let event_receipts = receipt.content.get(event_id).unwrap();
|
||||||
|
let event_read_receipts = event_receipts.get(&ReceiptType::Read).unwrap();
|
||||||
|
let event_user_read_receipt = event_read_receipts.get(user_id).unwrap();
|
||||||
|
assert_eq!(event_user_read_receipt.ts, Some(MilliSecondsSinceUnixEpoch(uint!(453))));
|
||||||
|
|
||||||
|
let serialized_data = to_json_value(data).unwrap();
|
||||||
|
assert_eq!(serialized_data, receipt_json);
|
||||||
|
|
||||||
|
// Test m.presence serde.
|
||||||
|
let presence_json = json!({
|
||||||
|
"type": "m.presence",
|
||||||
|
"sender": user_id,
|
||||||
|
"content": {
|
||||||
|
"avatar_url": "mxc://localhost/wefuiwegh8742w",
|
||||||
|
"currently_active": false,
|
||||||
|
"last_active_ago": 785,
|
||||||
|
"presence": "online",
|
||||||
|
"status_msg": "Making cupcakes",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let data = from_json_value::<EphemeralData>(presence_json.clone()).unwrap();
|
||||||
|
assert_matches!(&data, EphemeralData::Presence(presence));
|
||||||
|
assert_eq!(presence.sender, user_id);
|
||||||
|
assert_eq!(presence.content.currently_active, Some(false));
|
||||||
|
|
||||||
|
let serialized_data = to_json_value(data).unwrap();
|
||||||
|
assert_eq!(serialized_data, presence_json);
|
||||||
|
|
||||||
|
// Test custom serde.
|
||||||
|
let custom_json = json!({
|
||||||
|
"type": "dev.ruma.custom",
|
||||||
|
"key": "value",
|
||||||
|
"content": {
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let data = from_json_value::<EphemeralData>(custom_json.clone()).unwrap();
|
||||||
|
|
||||||
|
let serialized_data = to_json_value(data).unwrap();
|
||||||
|
assert_eq!(serialized_data, custom_json);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,14 +96,15 @@ pub struct Registration {
|
|||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub rate_limited: Option<bool>,
|
pub rate_limited: Option<bool>,
|
||||||
|
|
||||||
/// Whether the homeserver should send EDUs to the appservice, as per MSC2409
|
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none", alias = "de.sorunome.msc2409.push_ephemeral")]
|
|
||||||
pub receive_ephemeral: Option<bool>,
|
|
||||||
|
|
||||||
/// The external protocols which the application service provides (e.g. IRC).
|
/// The external protocols which the application service provides (e.g. IRC).
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub protocols: Option<Vec<String>>,
|
pub protocols: Option<Vec<String>>,
|
||||||
|
|
||||||
|
/// Whether the application service wants to receive ephemeral data.
|
||||||
|
///
|
||||||
|
/// Defaults to `false`.
|
||||||
|
#[serde(default, skip_serializing_if = "ruma_common::serde::is_default", alias = "de.sorunome.msc2409.push_ephemeral")]
|
||||||
|
pub receive_ephemeral: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initial set of fields of `Registration`.
|
/// Initial set of fields of `Registration`.
|
||||||
@ -140,9 +141,6 @@ pub struct RegistrationInit {
|
|||||||
/// The sender is excluded.
|
/// The sender is excluded.
|
||||||
pub rate_limited: Option<bool>,
|
pub rate_limited: Option<bool>,
|
||||||
|
|
||||||
/// Whether the homeserver should send EDUs to the appservice, as per MSC2409
|
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
|
||||||
pub receive_ephemeral: Option<bool>,
|
|
||||||
|
|
||||||
/// The external protocols which the application service provides (e.g. IRC).
|
/// The external protocols which the application service provides (e.g. IRC).
|
||||||
pub protocols: Option<Vec<String>>,
|
pub protocols: Option<Vec<String>>,
|
||||||
@ -158,8 +156,6 @@ impl From<RegistrationInit> for Registration {
|
|||||||
sender_localpart,
|
sender_localpart,
|
||||||
namespaces,
|
namespaces,
|
||||||
rate_limited,
|
rate_limited,
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
|
||||||
receive_ephemeral,
|
|
||||||
protocols,
|
protocols,
|
||||||
} = init;
|
} = init;
|
||||||
Self {
|
Self {
|
||||||
@ -170,8 +166,7 @@ impl From<RegistrationInit> for Registration {
|
|||||||
sender_localpart,
|
sender_localpart,
|
||||||
namespaces,
|
namespaces,
|
||||||
rate_limited,
|
rate_limited,
|
||||||
#[cfg(feature = "unstable-msc2409")]
|
receive_ephemeral: false,
|
||||||
receive_ephemeral,
|
|
||||||
protocols,
|
protocols,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -220,7 +220,7 @@ unstable-extensible-events = [
|
|||||||
"unstable-msc3955",
|
"unstable-msc3955",
|
||||||
]
|
]
|
||||||
unstable-msc1767 = ["ruma-events?/unstable-msc1767"]
|
unstable-msc1767 = ["ruma-events?/unstable-msc1767"]
|
||||||
unstable-msc2409 = ["ruma-appservice-api?/unstable-msc2409"]
|
unstable-msc4203 = ["ruma-appservice-api?/unstable-msc4203"]
|
||||||
unstable-msc2448 = [
|
unstable-msc2448 = [
|
||||||
"ruma-client-api?/unstable-msc2448",
|
"ruma-client-api?/unstable-msc2448",
|
||||||
"ruma-events?/unstable-msc2448",
|
"ruma-events?/unstable-msc2448",
|
||||||
@ -282,7 +282,6 @@ unstable-unspecified = [
|
|||||||
# Private features, only used in test / benchmarking code
|
# Private features, only used in test / benchmarking code
|
||||||
__unstable-mscs = [
|
__unstable-mscs = [
|
||||||
"unstable-msc1767",
|
"unstable-msc1767",
|
||||||
"unstable-msc2409",
|
|
||||||
"unstable-msc2448",
|
"unstable-msc2448",
|
||||||
"unstable-msc2654",
|
"unstable-msc2654",
|
||||||
"unstable-msc2666",
|
"unstable-msc2666",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user