Add 'ruma-federation-api/' from commit '44a0f493d0ae119fb1175a5f61c2db52ef001fb7'
git-subtree-dir: ruma-federation-api git-subtree-mainline: acff664671e3f53bd012d33228363780eb20cf35 git-subtree-split: 44a0f493d0ae119fb1175a5f61c2db52ef001fb7
This commit is contained in:
commit
10bd7d5f95
27
ruma-federation-api/.builds/beta.yml
Normal file
27
ruma-federation-api/.builds/beta.yml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
image: archlinux
|
||||||
|
packages:
|
||||||
|
- rustup
|
||||||
|
sources:
|
||||||
|
- https://github.com/ruma/ruma-federation-api
|
||||||
|
tasks:
|
||||||
|
- rustup: |
|
||||||
|
# We specify --profile minimal because we'd otherwise download docs
|
||||||
|
rustup toolchain install beta --profile minimal -c rustfmt -c clippy
|
||||||
|
rustup default beta
|
||||||
|
- test: |
|
||||||
|
cd ruma-federation-api
|
||||||
|
|
||||||
|
# We don't want the build to stop on individual failure of independent
|
||||||
|
# tools, so capture tool exit codes and set the task exit code manually
|
||||||
|
set +e
|
||||||
|
|
||||||
|
cargo fmt -- --check
|
||||||
|
fmt_exit=$?
|
||||||
|
|
||||||
|
cargo clippy --all-targets --all-features -- -D warnings
|
||||||
|
clippy_exit=$?
|
||||||
|
|
||||||
|
cargo test --verbose
|
||||||
|
test_exit=$?
|
||||||
|
|
||||||
|
exit $(( $fmt_exit || $clippy_exit || $test_exit ))
|
16
ruma-federation-api/.builds/msrv.yml
Normal file
16
ruma-federation-api/.builds/msrv.yml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
image: archlinux
|
||||||
|
packages:
|
||||||
|
- rustup
|
||||||
|
sources:
|
||||||
|
- https://github.com/ruma/ruma-federation-api
|
||||||
|
tasks:
|
||||||
|
- rustup: |
|
||||||
|
# We specify --profile minimal because we'd otherwise download docs
|
||||||
|
rustup toolchain install 1.40.0 --profile minimal
|
||||||
|
rustup default 1.40.0
|
||||||
|
- test: |
|
||||||
|
cd ruma-federation-api
|
||||||
|
|
||||||
|
# Only make sure the code builds with the MSRV. Tests can require later
|
||||||
|
# Rust versions, don't compile or run them.
|
||||||
|
cargo build --verbose
|
32
ruma-federation-api/.builds/nightly.yml
Normal file
32
ruma-federation-api/.builds/nightly.yml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
image: archlinux
|
||||||
|
packages:
|
||||||
|
- rustup
|
||||||
|
sources:
|
||||||
|
- https://github.com/ruma/ruma-federation-api
|
||||||
|
tasks:
|
||||||
|
- rustup: |
|
||||||
|
rustup toolchain install nightly --profile minimal
|
||||||
|
rustup default nightly
|
||||||
|
|
||||||
|
# Try installing rustfmt & clippy for nightly, but don't fail the build
|
||||||
|
# if they are not available
|
||||||
|
rustup component add rustfmt || true
|
||||||
|
rustup component add clippy || true
|
||||||
|
- test: |
|
||||||
|
cd ruma-federation-api
|
||||||
|
|
||||||
|
# We don't want the build to stop on individual failure of independent
|
||||||
|
# tools, so capture tool exit codes and set the task exit code manually
|
||||||
|
set +e
|
||||||
|
|
||||||
|
if ( rustup component list | grep -q rustfmt ); then
|
||||||
|
cargo fmt -- --check
|
||||||
|
fi
|
||||||
|
fmt_exit=$?
|
||||||
|
|
||||||
|
if ( rustup component list | grep -q clippy ); then
|
||||||
|
cargo clippy --all-targets --all-features -- -D warnings
|
||||||
|
fi
|
||||||
|
clippy_exit=$?
|
||||||
|
|
||||||
|
exit $(( $fmt_exit || $clippy_exit ))
|
29
ruma-federation-api/.builds/stable.yml
Normal file
29
ruma-federation-api/.builds/stable.yml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
image: archlinux
|
||||||
|
packages:
|
||||||
|
- rustup
|
||||||
|
sources:
|
||||||
|
- https://github.com/ruma/ruma-federation-api
|
||||||
|
tasks:
|
||||||
|
- rustup: |
|
||||||
|
# We specify --profile minimal because we'd otherwise download docs
|
||||||
|
rustup toolchain install stable --profile minimal -c rustfmt -c clippy
|
||||||
|
rustup default stable
|
||||||
|
- test: |
|
||||||
|
cd ruma-federation-api
|
||||||
|
|
||||||
|
# We don't want the build to stop on individual failure of independent
|
||||||
|
# tools, so capture tool exit codes and set the task exit code manually
|
||||||
|
set +e
|
||||||
|
|
||||||
|
cargo fmt -- --check
|
||||||
|
fmt_exit=$?
|
||||||
|
|
||||||
|
cargo clippy --all-targets --all-features -- -D warnings
|
||||||
|
clippy_exit=$?
|
||||||
|
|
||||||
|
cargo test --verbose
|
||||||
|
test_exit=$?
|
||||||
|
|
||||||
|
exit $(( $fmt_exit || $clippy_exit || $test_exit ))
|
||||||
|
# TODO: Add audit task once cargo-audit binary releases are available.
|
||||||
|
# See https://github.com/RustSec/cargo-audit/issues/66
|
2
ruma-federation-api/.gitignore
vendored
Normal file
2
ruma-federation-api/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Cargo.lock
|
||||||
|
target
|
25
ruma-federation-api/CHANGELOG.md
Normal file
25
ruma-federation-api/CHANGELOG.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# [unreleased]
|
||||||
|
|
||||||
|
Improvements:
|
||||||
|
|
||||||
|
* Add endpoints:
|
||||||
|
```
|
||||||
|
directory::get_public_rooms::v1,
|
||||||
|
discovery::{
|
||||||
|
discover_homeserver,
|
||||||
|
get_server_keys::v2,
|
||||||
|
get_server_version::v1
|
||||||
|
},
|
||||||
|
membership::{
|
||||||
|
create_join_event::v1,
|
||||||
|
create_join_event_template::v1
|
||||||
|
},
|
||||||
|
query::get_room_information::v1,
|
||||||
|
version::get_server_version::v1
|
||||||
|
```
|
||||||
|
|
||||||
|
# 0.0.1
|
||||||
|
|
||||||
|
Improvements:
|
||||||
|
|
||||||
|
* Provide `RoomV3Pdu` type for room versions 3 and above
|
181
ruma-federation-api/CONTRIBUTING.md
Normal file
181
ruma-federation-api/CONTRIBUTING.md
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
Welcome! Thanks for looking into contributing to our project!
|
||||||
|
|
||||||
|
# Table of Contents
|
||||||
|
|
||||||
|
- [Looking for Help?](#looking-for-help)
|
||||||
|
- [Documentation](#documentation)
|
||||||
|
- [Chat Rooms](#chat-rooms)
|
||||||
|
- [Reporting Issues](#reporting-issues)
|
||||||
|
- [Submitting Code](#submitting-code)
|
||||||
|
- [Coding Style](#coding-style)
|
||||||
|
- [Modifying Endpoints](#modifying-endpoints)
|
||||||
|
- [Submitting PRs](#submitting-prs)
|
||||||
|
- [Where do I start?](#where-do-i-start)
|
||||||
|
- [Testing](#testing)
|
||||||
|
- [Contact](#contact)
|
||||||
|
|
||||||
|
# Looking for Help?
|
||||||
|
|
||||||
|
Here is a list of helpful resources you can consult:
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
- [Matrix Federation API specification](https://matrix.org/docs/spec/server_server/latest)
|
||||||
|
- Documentation to other Ruma modules:
|
||||||
|
- [ruma-events](https://docs.rs/ruma-events/)
|
||||||
|
- [ruma-api](https://docs.rs/ruma-api/)
|
||||||
|
- [ruma-client](https://docs.rs/ruma-client/)
|
||||||
|
|
||||||
|
## Chat Rooms
|
||||||
|
|
||||||
|
- Ruma Matrix room: [#ruma:matrix.org](https://matrix.to/#/#ruma:matrix.org)
|
||||||
|
- Matrix Developer room: [#matrix-dev:matrix.org](https://matrix.to/#/#matrix-dev:matrix.org)
|
||||||
|
- Matrix Homeserver developers room: [#homeservers-dev:matrix.org](https://matrix.to/#/#homerservers-dev:matrix.org)
|
||||||
|
|
||||||
|
# Reporting Issues
|
||||||
|
|
||||||
|
If you find any bugs, inconsistencies or other problems, feel free to submit
|
||||||
|
a GitHub [issue](issues).
|
||||||
|
|
||||||
|
If you have a quick question, it may be easier to leave a message on
|
||||||
|
[#ruma:matrix.org](https://matrix.to/#/#ruma:matrix.org).
|
||||||
|
|
||||||
|
Also, if you have trouble getting on board, let us know so we can help future
|
||||||
|
contributors to the project overcome that hurdle too.
|
||||||
|
|
||||||
|
# Submitting Code
|
||||||
|
|
||||||
|
Ready to write some code? Great! Here are some guidelines to follow to
|
||||||
|
help you on your way:
|
||||||
|
|
||||||
|
## Coding Style
|
||||||
|
|
||||||
|
### Import Formatting
|
||||||
|
|
||||||
|
Organize your imports into three groups separated by blank lines:
|
||||||
|
|
||||||
|
1. `std` imports
|
||||||
|
1. External imports (from other crates)
|
||||||
|
1. Local imports (`self::`, `super::`, `crate::` and things like `LocalType::*`)
|
||||||
|
|
||||||
|
For example,
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use ruma_api::ruma_api;
|
||||||
|
|
||||||
|
use super::MyType;
|
||||||
|
```
|
||||||
|
|
||||||
|
Also, group imports by module. For example, do this:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
convert::TryFrom,
|
||||||
|
fmt::{Debug, Display, Error as FmtError, Formatter},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
as opposed to:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
use std::fmt::{Debug, Display, Error as FmtError, Formatter};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Code Formatting and Linting
|
||||||
|
|
||||||
|
Use `rustfmt` to format your code and `clippy` to lint your code. Before
|
||||||
|
committing your changes, go ahead and run `cargo fmt` and `cargo clippy
|
||||||
|
--all-targets --all-features` on the repository to make sure that the
|
||||||
|
formatting and linting checks pass in CI. Note that `clippy` warnings are
|
||||||
|
reported as errors in CI builds, so make sure to handle those before
|
||||||
|
comitting as well. (To install the tools, run `rustup component add rustfmt
|
||||||
|
clippy`.)
|
||||||
|
|
||||||
|
#### Git hooks
|
||||||
|
Tip: You may want to add these commands to your pre-commit git hook so you don't get bitten by CI.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# ./git/hooks/pre-commit
|
||||||
|
cargo fmt && cargo clippy --all-targets --allfeatures
|
||||||
|
```
|
||||||
|
|
||||||
|
### Commit Messages
|
||||||
|
|
||||||
|
Write commit messages using the imperative mood, as if completing the sentence:
|
||||||
|
"If applied, this commit will \_\_\_." For example, use "Fix some bug" instead
|
||||||
|
of "Fixed some bug" or "Add a feature" instead of "Added a feature".
|
||||||
|
|
||||||
|
(Take a look at this
|
||||||
|
[blog post](https://www.freecodecamp.org/news/writing-good-commit-messages-a-practical-guide/)
|
||||||
|
for more information on writing good commit messages.)
|
||||||
|
|
||||||
|
## Modifying Endpoints
|
||||||
|
|
||||||
|
### Matrix Spec Version
|
||||||
|
|
||||||
|
Use the latest r0.x.x documentation when adding or modifying code. We target
|
||||||
|
the latest minor version of the Matrix specification. (Note: We might
|
||||||
|
reconsider this when the Federation API hits r1.0.0.)
|
||||||
|
|
||||||
|
### Endpoint Documentation Header
|
||||||
|
|
||||||
|
Add a comment to the top of each endpoint file that includes the path
|
||||||
|
and a link to the documentation of the spec. You can use the latest
|
||||||
|
version at the time of the commit. For example:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
//! [GET /.well-known/matrix/server](https://matrix.org/docs/spec/server_server/r0.1.3#get-well-known-matrix-server)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Naming Endpoints
|
||||||
|
|
||||||
|
When adding new endpoints, select the module that fits the purpose of the
|
||||||
|
endpoint. When naming the endpoint itself, you can use the following
|
||||||
|
guidelines:
|
||||||
|
- The name should be a verb describing what the client is requesting, e.g.
|
||||||
|
`get_some_resource`.
|
||||||
|
- Endpoints which are basic CRUD operations should use the prefixes
|
||||||
|
`create`, `get`, `update`, and `delete`.
|
||||||
|
- The prefix `set` is preferred to create if the resource is a singleton.
|
||||||
|
In other words, when there's no distinction between `create` and `update`.
|
||||||
|
- Try to use names that are as descriptive as possible and distinct from
|
||||||
|
other endpoints in all other modules. (For example, instead of
|
||||||
|
`membership::create_event::v1`, use `membership::create_join_event::v1`).
|
||||||
|
- If you're not sure what to name it, pick any name and we can help you
|
||||||
|
with it.
|
||||||
|
|
||||||
|
### Tracking Changes
|
||||||
|
|
||||||
|
Add your changes to the [change log](CHANGELOG.md). If possible, try to
|
||||||
|
find and denote the version of the spec that included the change you are
|
||||||
|
making.
|
||||||
|
|
||||||
|
## Submitting PRs
|
||||||
|
|
||||||
|
Once you're ready to submit your code, create a pull request, and one of our
|
||||||
|
maintainers will review it. Once your PR has passed review, a maintainer will
|
||||||
|
merge the request and you're done! 🎉
|
||||||
|
|
||||||
|
## Where do I start?
|
||||||
|
|
||||||
|
If this is your first contribution to the project, we recommend taking a look
|
||||||
|
at one of the [open issues][] we've marked for new contributors.
|
||||||
|
|
||||||
|
It may be helpful to peruse some of the documentation for `ruma-events` and
|
||||||
|
`ruma-api` listed above for some context.
|
||||||
|
|
||||||
|
[open issues]: https://github.com/ruma/ruma-federation-api/issues?q=is%3Aopen+is%3Aissue+label%3Aeffort%2Feasy
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
|
||||||
|
Before committing, run `cargo check` to make sure that your changes can build, as well as running the formatting and linting tools [mentioned above](#code-formatting-and-linting).
|
||||||
|
|
||||||
|
# Contact
|
||||||
|
|
||||||
|
Thanks again for being a contributor! If you have any questions, join us at
|
||||||
|
[#ruma:matrix.org](https://matrix.to/#/#ruma:matrix.org).
|
23
ruma-federation-api/Cargo.toml
Normal file
23
ruma-federation-api/Cargo.toml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
[package]
|
||||||
|
authors = ["Jonas Platte <jplatte+git@posteo.de>"]
|
||||||
|
categories = ["api-bindings", "web-programming"]
|
||||||
|
description = "Types for the endpoints in the Matrix server-server API."
|
||||||
|
documentation = "https://docs.rs/ruma-federation-api"
|
||||||
|
edition = "2018"
|
||||||
|
homepage = "https://github.com/ruma/ruma-federation-api"
|
||||||
|
keywords = ["matrix", "chat", "messaging", "ruma"]
|
||||||
|
license = "MIT"
|
||||||
|
name = "ruma-federation-api"
|
||||||
|
readme = "README.md"
|
||||||
|
repository = "https://github.com/ruma/ruma-federation-api"
|
||||||
|
version = "0.0.2"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
js_int = "0.1.5"
|
||||||
|
matches = "0.1.8"
|
||||||
|
ruma-api = "0.16.1"
|
||||||
|
ruma-events = "0.21.3"
|
||||||
|
ruma-identifiers = "0.16.2"
|
||||||
|
ruma-serde = "0.2.2"
|
||||||
|
serde = { version = "1.0.111", features = ["derive"] }
|
||||||
|
serde_json = "1.0.53"
|
19
ruma-federation-api/LICENSE
Normal file
19
ruma-federation-api/LICENSE
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2019 Jimmy Cuadra
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
18
ruma-federation-api/README.md
Normal file
18
ruma-federation-api/README.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# ruma-federation-api
|
||||||
|
|
||||||
|
**ruma-federation-api** contains serializable types for the requests and responses for each endpoint in the [Matrix](https://matrix.org/) Federation API specification.
|
||||||
|
These types can be shared by client and server code.
|
||||||
|
|
||||||
|
## Minimum Rust version
|
||||||
|
|
||||||
|
ruma-federation-api requires Rust 1.40.0 or later.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
[https://docs.rs/ruma-federation-api](https://docs.rs/ruma-federation-api)
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[MIT](http://opensource.org/licenses/MIT)
|
3
ruma-federation-api/src/directory.rs
Normal file
3
ruma-federation-api/src/directory.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
//! Room directory endpoints.
|
||||||
|
|
||||||
|
pub mod get_public_rooms;
|
@ -0,0 +1,3 @@
|
|||||||
|
//! Endpoint to query a homeserver's public rooms.
|
||||||
|
|
||||||
|
pub mod v1;
|
177
ruma-federation-api/src/directory/get_public_rooms/v1.rs
Normal file
177
ruma-federation-api/src/directory/get_public_rooms/v1.rs
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
//! [GET /_matrix/federation/v1/publicRooms](https://matrix.org/docs/spec/server_server/r0.1.3#get-matrix-federation-v1-publicrooms)
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use js_int::UInt;
|
||||||
|
use ruma_api::ruma_api;
|
||||||
|
use ruma_identifiers::{RoomAliasId, RoomId};
|
||||||
|
use serde::{
|
||||||
|
de::{MapAccess, Visitor},
|
||||||
|
ser::SerializeStruct,
|
||||||
|
Deserialize, Deserializer, Serialize, Serializer,
|
||||||
|
};
|
||||||
|
|
||||||
|
ruma_api! {
|
||||||
|
metadata {
|
||||||
|
description: "Gets all the public rooms for the homeserver.",
|
||||||
|
method: GET,
|
||||||
|
name: "get_public_rooms",
|
||||||
|
path: "/_matrix/federation/v1/publicRooms",
|
||||||
|
rate_limited: false,
|
||||||
|
requires_authentication: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
request {
|
||||||
|
/// The maximum number of rooms to return. Default is no limit.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[ruma_api(query)]
|
||||||
|
pub limit: Option<UInt>,
|
||||||
|
/// Pagination token from a previous request.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[ruma_api(query)]
|
||||||
|
pub since: Option<String>,
|
||||||
|
/// Network to fetch the public room lists from.
|
||||||
|
#[serde(flatten, skip_serializing_if = "ruma_serde::is_default")]
|
||||||
|
#[ruma_api(query)]
|
||||||
|
pub room_network: RoomNetwork,
|
||||||
|
}
|
||||||
|
|
||||||
|
response {
|
||||||
|
/// A paginated chunk of public rooms.
|
||||||
|
pub chunk: Vec<PublicRoomsChunk>,
|
||||||
|
/// A pagination token for the response.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub next_batch: Option<String>,
|
||||||
|
/// A pagination token that allows fetching previous results.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub prev_batch: Option<String>,
|
||||||
|
/// An estimate on the total number of public rooms, if the server has an estimate.
|
||||||
|
pub total_room_count_estimate: Option<UInt>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A chunk of a room list response, describing one room
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
pub struct PublicRoomsChunk {
|
||||||
|
/// Aliases of the room.
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub aliases: Vec<RoomAliasId>,
|
||||||
|
/// The canonical alias of the room, if any.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub canonical_alias: Option<String>,
|
||||||
|
/// The name of the room, if any.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub name: Option<String>,
|
||||||
|
/// The number of members joined to the room.
|
||||||
|
pub num_joined_members: UInt,
|
||||||
|
/// The ID of the room.
|
||||||
|
pub room_id: RoomId,
|
||||||
|
/// The topic of the room, if any.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub topic: Option<String>,
|
||||||
|
/// Whether the room may be viewed by guest users without joining.
|
||||||
|
pub world_readable: bool,
|
||||||
|
/// Whether guest users may join the room and participate in it.
|
||||||
|
///
|
||||||
|
/// If they can, they will be subject to ordinary power level rules like any other user.
|
||||||
|
pub guest_can_join: bool,
|
||||||
|
/// The URL for the room's avatar, if one is set.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub avatar_url: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information about which networks/protocols from application services on the
|
||||||
|
/// homeserver from which to request rooms.
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum RoomNetwork {
|
||||||
|
/// Return rooms from the Matrix network.
|
||||||
|
Matrix,
|
||||||
|
/// Return rooms from all the networks/protocols the homeserver knows about.
|
||||||
|
All,
|
||||||
|
/// Return rooms from a specific third party network/protocol.
|
||||||
|
ThirdParty(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RoomNetwork {
|
||||||
|
fn default() -> Self {
|
||||||
|
RoomNetwork::Matrix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for RoomNetwork {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
let mut state;
|
||||||
|
match self {
|
||||||
|
Self::Matrix => {
|
||||||
|
state = serializer.serialize_struct("RoomNetwork", 0)?;
|
||||||
|
}
|
||||||
|
Self::All => {
|
||||||
|
state = serializer.serialize_struct("RoomNetwork", 1)?;
|
||||||
|
state.serialize_field("include_all_networks", &true)?;
|
||||||
|
}
|
||||||
|
Self::ThirdParty(network) => {
|
||||||
|
state = serializer.serialize_struct("RoomNetwork", 1)?;
|
||||||
|
state.serialize_field("third_party_instance_id", network)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for RoomNetwork {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
deserializer.deserialize_map(RoomNetworkVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RoomNetworkVisitor;
|
||||||
|
impl<'de> Visitor<'de> for RoomNetworkVisitor {
|
||||||
|
type Value = RoomNetwork;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str("Network selection")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
|
||||||
|
where
|
||||||
|
M: MapAccess<'de>,
|
||||||
|
{
|
||||||
|
let mut include_all_networks = false;
|
||||||
|
let mut third_party_instance_id = None;
|
||||||
|
while let Some((key, value)) = access.next_entry::<String, serde_json::Value>()? {
|
||||||
|
match key.as_str() {
|
||||||
|
"include_all_networks" => {
|
||||||
|
include_all_networks = match value.as_bool() {
|
||||||
|
Some(b) => b,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"third_party_instance_id" => {
|
||||||
|
third_party_instance_id = value.as_str().map(|v| v.to_owned())
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if include_all_networks {
|
||||||
|
if third_party_instance_id.is_none() {
|
||||||
|
Ok(RoomNetwork::All)
|
||||||
|
} else {
|
||||||
|
Err(M::Error::custom(
|
||||||
|
"`include_all_networks = true` and `third_party_instance_id` are mutually exclusive.",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(match third_party_instance_id {
|
||||||
|
Some(network) => RoomNetwork::ThirdParty(network),
|
||||||
|
None => RoomNetwork::Matrix,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
ruma-federation-api/src/discovery.rs
Normal file
5
ruma-federation-api/src/discovery.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
//! Server discovery endpoints.
|
||||||
|
|
||||||
|
pub mod discover_homeserver;
|
||||||
|
pub mod get_server_keys;
|
||||||
|
pub mod get_server_version;
|
22
ruma-federation-api/src/discovery/discover_homeserver.rs
Normal file
22
ruma-federation-api/src/discovery/discover_homeserver.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
//! [GET /.well-known/matrix/server](https://matrix.org/docs/spec/server_server/r0.1.3#get-well-known-matrix-server)
|
||||||
|
|
||||||
|
use ruma_api::ruma_api;
|
||||||
|
|
||||||
|
ruma_api! {
|
||||||
|
metadata {
|
||||||
|
description: "Get discovery information about the domain.",
|
||||||
|
method: GET,
|
||||||
|
name: "discover_homeserver",
|
||||||
|
path: "/.well-known/matrix/server",
|
||||||
|
rate_limited: false,
|
||||||
|
requires_authentication: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
request {}
|
||||||
|
|
||||||
|
response {
|
||||||
|
/// The server name to delegate server-server communciations to, with optional port.
|
||||||
|
#[serde(rename = "m.homeserver")]
|
||||||
|
pub homeserver: String,
|
||||||
|
}
|
||||||
|
}
|
3
ruma-federation-api/src/discovery/get_server_keys/mod.rs
Normal file
3
ruma-federation-api/src/discovery/get_server_keys/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
//! Endpdoint for retrieving a server's published signing keys.
|
||||||
|
|
||||||
|
pub mod v2;
|
54
ruma-federation-api/src/discovery/get_server_keys/v2.rs
Normal file
54
ruma-federation-api/src/discovery/get_server_keys/v2.rs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
//! [GET /_matrix/key/v2/server](https://matrix.org/docs/spec/server_server/r0.1.3#get-matrix-key-v2-server-keyid)
|
||||||
|
|
||||||
|
use std::{collections::BTreeMap, time::SystemTime};
|
||||||
|
|
||||||
|
use ruma_api::ruma_api;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
ruma_api! {
|
||||||
|
metadata {
|
||||||
|
description: "Gets the homeserver's published signing keys.",
|
||||||
|
method: GET,
|
||||||
|
name: "get_server_keys",
|
||||||
|
path: "/_matrix/key/v2/server",
|
||||||
|
rate_limited: false,
|
||||||
|
requires_authentication: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
request {}
|
||||||
|
|
||||||
|
response {
|
||||||
|
// Spec is wrong, all fields are required (see
|
||||||
|
// https://github.com/matrix-org/matrix-doc/issues/2508)
|
||||||
|
|
||||||
|
/// DNS name of the homeserver.
|
||||||
|
pub server_name: String,
|
||||||
|
/// Public keys of the homeserver for verifying digital signatures.
|
||||||
|
pub verify_keys: BTreeMap<String, VerifyKey>,
|
||||||
|
/// Public keys that the homeserver used to use and when it stopped using them.
|
||||||
|
pub old_verify_keys: BTreeMap<String, OldVerifyKey>,
|
||||||
|
/// Digital signatures of this object signed using the verify_keys.
|
||||||
|
pub signatures: BTreeMap<String, BTreeMap<String, String>>,
|
||||||
|
/// Timestamp when the keys should be refreshed. This field MUST be ignored in room
|
||||||
|
/// versions 1, 2, 3, and 4.
|
||||||
|
#[serde(with = "ruma_serde::time::ms_since_unix_epoch")]
|
||||||
|
pub valid_until_ts: SystemTime,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Public key of the homeserver for verifying digital signatures.
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
pub struct VerifyKey {
|
||||||
|
/// The Unpadded Base64 encoded key.
|
||||||
|
pub key: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A key the server used to use, but stopped using.
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
pub struct OldVerifyKey {
|
||||||
|
/// Timestamp when this key expired.
|
||||||
|
#[serde(with = "ruma_serde::time::ms_since_unix_epoch")]
|
||||||
|
pub expired_ts: SystemTime,
|
||||||
|
/// The Unpadded Base64 encoded key.
|
||||||
|
pub key: String,
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
//! Endpoint to retrieve metadata about a server implementation.
|
||||||
|
|
||||||
|
pub mod v1;
|
34
ruma-federation-api/src/discovery/get_server_version/v1.rs
Normal file
34
ruma-federation-api/src/discovery/get_server_version/v1.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
//! [GET /_matrix/federation/v1/version](https://matrix.org/docs/spec/server_server/r0.1.3#get-matrix-federation-v1-version)
|
||||||
|
|
||||||
|
use ruma_api::ruma_api;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
ruma_api! {
|
||||||
|
metadata {
|
||||||
|
description: "Get the implementation name and version of this homeserver.",
|
||||||
|
method: GET,
|
||||||
|
name: "discover_homeserver",
|
||||||
|
path: "/.well-known/matrix/server",
|
||||||
|
rate_limited: false,
|
||||||
|
requires_authentication: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
request {}
|
||||||
|
|
||||||
|
response {
|
||||||
|
/// Information about the homeserver implementation
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub server: Option<Server>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
/// Arbitrary values that identify this implementation.
|
||||||
|
pub struct Server {
|
||||||
|
/// Arbitrary name that identifies this implementation.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub name: Option<String>,
|
||||||
|
/// Version of this implementation. The version format depends on the implementation.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub version: Option<String>,
|
||||||
|
}
|
70
ruma-federation-api/src/lib.rs
Normal file
70
ruma-federation-api/src/lib.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
//! (De)serializable types for the Matrix Federation API.
|
||||||
|
|
||||||
|
#![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,
|
||||||
|
}
|
4
ruma-federation-api/src/membership.rs
Normal file
4
ruma-federation-api/src/membership.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
//! Room membership endpoints.
|
||||||
|
|
||||||
|
pub mod create_join_event;
|
||||||
|
pub mod create_join_event_template;
|
20
ruma-federation-api/src/membership/create_join_event/mod.rs
Normal file
20
ruma-federation-api/src/membership/create_join_event/mod.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
//! Endpoint to send join events to remote homeservers.
|
||||||
|
|
||||||
|
pub mod v1;
|
||||||
|
|
||||||
|
use ruma_events::EventJson;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::RoomV3Pdu;
|
||||||
|
|
||||||
|
/// 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>>,
|
||||||
|
}
|
104
ruma-federation-api/src/membership/create_join_event/v1.rs
Normal file
104
ruma-federation-api/src/membership/create_join_event/v1.rs
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
//! [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 super::RoomState;
|
||||||
|
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.
|
||||||
|
#[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>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
//! Endpoint to request a template for join events.
|
||||||
|
|
||||||
|
pub mod v1;
|
@ -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>,
|
||||||
|
}
|
||||||
|
}
|
3
ruma-federation-api/src/query.rs
Normal file
3
ruma-federation-api/src/query.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
//! Endpoints to retrieve information from a homeserver about a resource.
|
||||||
|
|
||||||
|
pub mod get_room_information;
|
@ -0,0 +1,2 @@
|
|||||||
|
//! Endpoint to query room information with a room alias.
|
||||||
|
pub mod v1;
|
28
ruma-federation-api/src/query/get_room_information/v1.rs
Normal file
28
ruma-federation-api/src/query/get_room_information/v1.rs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
//! [GET /_matrix/federation/v1/query/directory](https://matrix.org/docs/spec/server_server/r0.1.3#get-matrix-federation-v1-query-directory)
|
||||||
|
|
||||||
|
use ruma_api::ruma_api;
|
||||||
|
use ruma_identifiers::RoomId;
|
||||||
|
|
||||||
|
ruma_api! {
|
||||||
|
metadata {
|
||||||
|
description: "Get mapped room ID and resident homeservers for a given room alias.",
|
||||||
|
name: "get_room_information",
|
||||||
|
method: GET,
|
||||||
|
path: "/_matrix/federation/v1/query/directory",
|
||||||
|
rate_limited: false,
|
||||||
|
requires_authentication: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
request {
|
||||||
|
/// Room alias to query.
|
||||||
|
#[ruma_api(query)]
|
||||||
|
pub room_alias: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
response {
|
||||||
|
/// Room ID mapped to queried alias.
|
||||||
|
pub room_id: RoomId,
|
||||||
|
/// An array of server names that are likely to hold the given room.
|
||||||
|
pub servers: Vec<String>,
|
||||||
|
}
|
||||||
|
}
|
3
ruma-federation-api/src/serde.rs
Normal file
3
ruma-federation-api/src/serde.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
//! Modules for custom serde de/-serialization implementations.
|
||||||
|
|
||||||
|
pub mod room_state;
|
150
ruma-federation-api/src/serde/room_state.rs
Normal file
150
ruma-federation-api/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::membership::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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user