client-api: Add support for the appservice ping mechanism

According to MSC2659
This commit is contained in:
Kévin Commaille 2023-05-24 21:17:35 +02:00 committed by Kévin Commaille
parent 7239be4fcb
commit 3b722c7faa
5 changed files with 116 additions and 0 deletions

View File

@ -12,6 +12,7 @@ Improvements:
- Add support for using an existing session to log in another (MSC3882 / Matrix 1.7)
- Add support for media download redirects (MSC3860 / Matrix 1.7)
- Stabilize support for asynchronous media uploads (MSC2246 / Matrix 1.7)
- Add support for the appservice ping mechanism (MSC 2659 / Matrix 1.7)
# 0.16.2

View File

@ -1,3 +1,4 @@
//! Endpoints part of the application service extension of the client-server API
pub mod request_ping;
pub mod set_room_visibility;

View File

@ -0,0 +1,64 @@
//! `POST /_matrix/client/*/appservice/{appserviceId}/ping}`
//!
//! Ask the homeserver to ping the application service to ensure the connection works.
pub mod v1 {
//! `/v1/` ([spec])
//!
//! [spec]: https://spec.matrix.org/latest/application-service-api/#post_matrixclientv1appserviceappserviceidping
use std::time::Duration;
use ruma_common::{
api::{request, response, Metadata},
metadata, OwnedTransactionId,
};
const METADATA: Metadata = metadata! {
method: POST,
rate_limited: false,
authentication: AccessToken,
history: {
unstable => "/_matrix/client/unstable/fi.mau.msc2659/appservice/:appservice_id/ping",
1.7 => "/_matrix/client/v1/appservice/:appservice_id/ping",
}
};
/// Request type for the `request_ping` endpoint.
#[request(error = crate::Error)]
pub struct Request {
/// The appservice ID of the appservice to ping.
///
/// This must be the same as the appservice whose `as_token` is being used to authenticate
/// the request.
#[ruma_api(path)]
pub appservice_id: String,
/// Transaction ID that is passed through to the `POST /_matrix/app/v1/ping` call.
#[serde(skip_serializing_if = "Option::is_none")]
pub transaction_id: Option<OwnedTransactionId>,
}
/// Response type for the `request_ping` endpoint.
#[response(error = crate::Error)]
pub struct Response {
/// The duration in milliseconds that the `POST /_matrix/app/v1/ping` request took from the
/// homeserver's point of view.
#[serde(with = "ruma_common::serde::duration::ms", rename = "duration_ms")]
duration: Duration,
}
impl Request {
/// Creates a new `Request` with the given appservice ID.
pub fn new(appservice_id: String) -> Self {
Self { appservice_id, transaction_id: None }
}
}
impl Response {
/// Creates an `Response` with the given duration.
pub fn new(duration: Duration) -> Self {
Self { duration }
}
}
}

View File

@ -163,6 +163,24 @@ pub enum ErrorKind {
#[cfg(feature = "unstable-msc3575")]
UnknownPos,
/// M_URL_NOT_SET
UrlNotSet,
/// M_BAD_STATUS
BadStatus {
/// The HTTP status code of the response.
status: Option<http::StatusCode>,
/// The body of the response.
body: Option<String>,
},
/// M_CONNECTION_FAILED
ConnectionFailed,
/// M_CONNECTION_TIMEOUT
ConnectionTimeout,
#[doc(hidden)]
_Custom { errcode: PrivOwnedStr, extra: Extra },
}
@ -215,6 +233,10 @@ impl AsRef<str> for ErrorKind {
Self::CannotOverwriteMedia => "M_CANNOT_OVERWRITE_MEDIA",
#[cfg(feature = "unstable-msc3575")]
Self::UnknownPos => "M_UNKNOWN_POS",
Self::UrlNotSet => "M_URL_NOT_SET",
Self::BadStatus { .. } => "M_BAD_STATUS",
Self::ConnectionFailed => "M_CONNECTION_FAILED",
Self::ConnectionTimeout => "M_CONNECTION_TIMEOUT",
Self::_Custom { errcode, .. } => &errcode.0,
}
}

View File

@ -22,6 +22,8 @@ enum Field<'de> {
RetryAfterMs,
RoomVersion,
AdminContact,
Status,
Body,
Other(Cow<'de, str>),
}
@ -33,6 +35,8 @@ impl<'de> Field<'de> {
"retry_after_ms" => Self::RetryAfterMs,
"room_version" => Self::RoomVersion,
"admin_contact" => Self::AdminContact,
"status" => Self::Status,
"body" => Self::Body,
_ => Self::Other(s),
}
}
@ -96,6 +100,8 @@ impl<'de> Visitor<'de> for ErrorKindVisitor {
let mut retry_after_ms = None;
let mut room_version = None;
let mut admin_contact = None;
let mut status = None;
let mut body = None;
let mut extra = BTreeMap::new();
macro_rules! set_field {
@ -118,6 +124,8 @@ impl<'de> Visitor<'de> for ErrorKindVisitor {
(@variant_containing retry_after_ms) => { ErrCode::LimitExceeded };
(@variant_containing room_version) => { ErrCode::IncompatibleRoomVersion };
(@variant_containing admin_contact) => { ErrCode::ResourceLimitExceeded };
(@variant_containing status) => { ErrCode::BadStatus };
(@variant_containing body) => { ErrCode::BadStatus };
(@inner $field:ident) => {
{
if $field.is_some() {
@ -135,6 +143,8 @@ impl<'de> Visitor<'de> for ErrorKindVisitor {
Field::RetryAfterMs => set_field!(retry_after_ms),
Field::RoomVersion => set_field!(room_version),
Field::AdminContact => set_field!(admin_contact),
Field::Status => set_field!(status),
Field::Body => set_field!(body),
Field::Other(other) => match extra.entry(other.into_owned()) {
Entry::Vacant(v) => {
v.insert(map.next_value()?);
@ -214,6 +224,20 @@ impl<'de> Visitor<'de> for ErrorKindVisitor {
ErrCode::CannotOverwriteMedia => ErrorKind::CannotOverwriteMedia,
#[cfg(feature = "unstable-msc3575")]
ErrCode::UnknownPos => ErrorKind::UnknownPos,
ErrCode::UrlNotSet => ErrorKind::UrlNotSet,
ErrCode::BadStatus => ErrorKind::BadStatus {
status: status
.map(|s| {
from_json_value::<u16>(s)
.map_err(de::Error::custom)?
.try_into()
.map_err(de::Error::custom)
})
.transpose()?,
body: body.map(from_json_value).transpose().map_err(de::Error::custom)?,
},
ErrCode::ConnectionFailed => ErrorKind::ConnectionFailed,
ErrCode::ConnectionTimeout => ErrorKind::ConnectionTimeout,
ErrCode::_Custom(errcode) => ErrorKind::_Custom { errcode, extra },
})
}
@ -265,6 +289,10 @@ enum ErrCode {
CannotOverwriteMedia,
#[cfg(feature = "unstable-msc3575")]
UnknownPos,
UrlNotSet,
BadStatus,
ConnectionFailed,
ConnectionTimeout,
_Custom(PrivOwnedStr),
}