mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-05-16 20:49:05 -04:00
Merge remote-tracking branch 'origin/main' into gnunicorn/issue833
This commit is contained in:
27
.github/workflows/prep-crypto-nodejs-release.yml
vendored
27
.github/workflows/prep-crypto-nodejs-release.yml
vendored
@@ -36,10 +36,13 @@ env:
|
||||
|
||||
jobs:
|
||||
prepare-release:
|
||||
name: "Package nodejs package"
|
||||
name: "Preparing crypto-nodejs release tag"
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
tag: "${{ env.TAG_PREFIX }}${{ inputs.version }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
|
||||
# Generate changelog since last tag, if given
|
||||
- name: Generate a changelog for upload
|
||||
@@ -65,16 +68,15 @@ jobs:
|
||||
|
||||
- name: Set version
|
||||
id: package_version
|
||||
uses: KageKirin/set-node-package-version@v0
|
||||
with:
|
||||
version: ${{ inputs.version }}
|
||||
working-directory: ${{ env.PKG_PATH }}
|
||||
run: npm version ${{ inputs.version }}
|
||||
|
||||
- uses: EndBug/add-and-commit@v9
|
||||
with:
|
||||
default_author: github_actions
|
||||
message: "Tagging Crypto-Node.js for release"
|
||||
tag: "${{env.TAG_PREFIX}}${{ inputs.version }}"
|
||||
new_banch: "gh-action/release-${{ env.TAG_PREFIX }}${{ inputs.version }}"
|
||||
new_branch: "gh-action/release-${{ env.TAG_PREFIX }}${{ inputs.version }}"
|
||||
push: true
|
||||
add: |
|
||||
${{ env.PKG_PATH }}/package.json
|
||||
@@ -91,18 +93,25 @@ jobs:
|
||||
|
||||
# no changes, use the default changelog for the body
|
||||
- name: Update Github Release notes
|
||||
if: !inputs.previous_version
|
||||
if: ${{!inputs.previous_version}}
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: true
|
||||
tag_name: ${{ env.TAG_PREFIX }}${{ inputs.version }}
|
||||
body_path: "${{ env.PKG_PATH }}/CHANGELOG.md"
|
||||
|
||||
# finally, let's create a PR for all this, too
|
||||
# let's create a PR for all this, too
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v4
|
||||
with:
|
||||
branch: "gh-action/release-${{ env.TAG_PREFIX }}${{ inputs.version }}"
|
||||
title: "Preparing Release ${{ env.TAG_PREFIX }}${{ inputs.version }}"
|
||||
body: |
|
||||
Automatic Pull-Request to merge release ${{ env.TAG_PREFIX }}${{ inputs.version }} back into main
|
||||
Automatic Pull-Request to merge release ${{ env.TAG_PREFIX }}${{ inputs.version }}
|
||||
|
||||
trigger-release:
|
||||
# and trigger the tagging release workflow
|
||||
uses: matrix-org/matrix-rust-sdk/.github/workflows/release-crypto-nodejs.yml@main
|
||||
needs: ['prepare-release']
|
||||
name: "Trigger release Workflow"
|
||||
with:
|
||||
tag: ${{needs.prepare-release.outputs.tag}}
|
||||
|
||||
25
.github/workflows/release-crypto-nodejs.yml
vendored
25
.github/workflows/release-crypto-nodejs.yml
vendored
@@ -21,7 +21,12 @@ on:
|
||||
push:
|
||||
tags:
|
||||
- matrix-sdk-crypto-nodejs-v[0-9]+.*
|
||||
|
||||
workflow_call:
|
||||
inputs:
|
||||
tag:
|
||||
description: "The tag to build with"
|
||||
required: true
|
||||
type: string
|
||||
jobs:
|
||||
upload-assets:
|
||||
name: "Upload prebuilt libraries"
|
||||
@@ -57,7 +62,15 @@ jobs:
|
||||
os: windows-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
# use the given tag
|
||||
- uses: actions/checkout@v3
|
||||
name: "Checking out ${{ inputs.tag }}"
|
||||
if: "${{ inputs.tag }}"
|
||||
with:
|
||||
ref: ${{ inputs.tag }}
|
||||
# use the default
|
||||
- uses: actions/checkout@v3
|
||||
if: "${{ !inputs.tag }}"
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
@@ -90,7 +103,15 @@ jobs:
|
||||
needs:
|
||||
- upload-assets
|
||||
steps:
|
||||
# use the given tag
|
||||
- uses: actions/checkout@v3
|
||||
name: "Checking out ${{ inputs.tag }}"
|
||||
if: "${{ inputs.tag }}"
|
||||
with:
|
||||
ref: ${{ inputs.tag }}
|
||||
# use the default
|
||||
- uses: actions/checkout@v3
|
||||
if: "${{ !inputs.tag }}"
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
@@ -113,4 +134,6 @@ jobs:
|
||||
- name: Publish to npmjs.com
|
||||
uses: JS-DevTools/npm-publish@v1
|
||||
with:
|
||||
package: ${{env.PKG_PATH}}/package.json
|
||||
access: public
|
||||
token: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
120
CONVENTIONAL_COMMITS.md
Normal file
120
CONVENTIONAL_COMMITS.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# Conventional Commits
|
||||
|
||||
This project uses [Conventional
|
||||
Commits](https://www.conventionalcommits.org/). Read the
|
||||
[Summary](https://www.conventionalcommits.org/en/v1.0.0/#summary) or
|
||||
the [Full
|
||||
Specification](https://www.conventionalcommits.org/en/v1.0.0/#specification)
|
||||
to learn more.
|
||||
|
||||
## Types
|
||||
|
||||
Conventional Commits defines _type_ (as in `type(scope):
|
||||
message`). This section aims at listing the types used inside this
|
||||
project:
|
||||
|
||||
| Type | Definition |
|
||||
|-|-|
|
||||
| `feat` | About a new feature. |
|
||||
| `fix` | About a bug fix. |
|
||||
| `test` | About a test (suite, case, runner…). |
|
||||
| `doc` | About a documentation modification. |
|
||||
| `refactor` | About a refactoring. |
|
||||
| `ci` | About a Continuous Integration modification. |
|
||||
| `chore` | About some cleanup, or regular tasks. |
|
||||
|
||||
## Scopes
|
||||
|
||||
Conventional Commits defines _scope_ (as in `type(scope): message`). This
|
||||
section aims at listing all the scopes used inside this project:
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Group</th>
|
||||
<th>Scope</th>
|
||||
<th>Definition</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td rowspan="10">Crates</td>
|
||||
<td><code>sdk</code></td>
|
||||
<td>About the <code>matrix-sdk</code> crate.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>appservice</code></td>
|
||||
<td>About the <code>matrix-sdk-appservice</code> crate.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>base</code></td>
|
||||
<td>About the <code>matrix-sdk-base</code> crate.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>common</code></td>
|
||||
<td>About the <code>matrix-sdk-common</code> crate.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>crypto</code></td>
|
||||
<td>About the <code>matrix-sdk-crypto</code> crate.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>indexeddb</code></td>
|
||||
<td>About the <code>matrix-sdk-indexeddb</code> crate.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>qrcode</code></td>
|
||||
<td>About the <code>matrix-sdk-qrcode</code> crate.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>sled</code></td>
|
||||
<td>About the <code>matrix-sdk-sled</code> crate.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>store-encryption</code></td>
|
||||
<td>About the <code>matrix-sdk-store-encryption</code> crate.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>test</code></td>
|
||||
<td>About the <code>matrix-sdk-test</code> and <code>matrix-sdk-test-macros</code> crate.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="4">Bindings</td>
|
||||
<td><code>apple</code></td>
|
||||
<td>About the <code>matrix-rust-components-swift</code> binding.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>crypto-nodejs</code></td>
|
||||
<td>About the <code>matrix-sdk-crypto-nodejs</code> binding.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>crypto-js</code></td>
|
||||
<td>About the <code>matrix-sdk-crypto-js</code> binding.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>crypto-ffi</code></td>
|
||||
<td>About the <code>matrix-sdk-crypto-ffi</code> binding.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Labs</td>
|
||||
<td><code>sled-state-inspector</code></td>
|
||||
<td>About the <code>sled-state-inspector</code> project.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Continuous Integration</td>
|
||||
<td><code>xtask</code></td>
|
||||
<td>About the <code>xtask</code> project.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## Generating `CHANGELOG.md`
|
||||
|
||||
The [`git-cliff`](https://github.com/orhun/git-cliff) project is used
|
||||
to generate `CHANGELOG.md` automatically. Hence the various
|
||||
`cliff.toml` files that are present in this project, or the
|
||||
`package.metadata.git-cliff` sections in various `Cargo.toml` files.
|
||||
|
||||
Its companion,
|
||||
[`git-cliff-action`](https://github.com/orhun/git-cliff-action)
|
||||
project, is used inside Github Action workflows.
|
||||
@@ -12,7 +12,7 @@ criterion = { version = "0.3.5", features = ["async", "async_tokio", "html_repor
|
||||
matrix-sdk-crypto = { path = "../crates/matrix-sdk-crypto", version = "0.5.0" }
|
||||
matrix-sdk-sled = { path = "../crates/matrix-sdk-sled", version = "0.1.0", default-features = false, features = ["crypto-store"] }
|
||||
matrix-sdk-test = { path = "../testing/matrix-sdk-test", version = "0.5.0" }
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "7ec599e83d8ba6b2ace9e4fe184fc7560b0ff6ae" }
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "ca8c66c885241a7ba3805399604eda4a38979f6b" }
|
||||
serde_json = "1.0.79"
|
||||
tempfile = "3.3.0"
|
||||
tokio = { version = "1.17.0", default-features = false, features = ["rt-multi-thread"] }
|
||||
|
||||
@@ -34,7 +34,7 @@ REL_TYPE_DIR="debug"
|
||||
|
||||
# iOS Simulator arm64
|
||||
if [ "$ACTIVE_ARCH" = "arm64" ]; then
|
||||
cargo +nightly build -p matrix-sdk-ffi ${REL_FLAG} --target "aarch64-apple-ios-sim"
|
||||
cargo build -p matrix-sdk-ffi ${REL_FLAG} --target "aarch64-apple-ios-sim"
|
||||
|
||||
lipo -create \
|
||||
"${TARGET_DIR}/aarch64-apple-ios-sim/${REL_TYPE_DIR}/libmatrix_sdk_ffi.a" \
|
||||
@@ -42,7 +42,7 @@ if [ "$ACTIVE_ARCH" = "arm64" ]; then
|
||||
|
||||
# iOS Simulator intel
|
||||
else
|
||||
cargo +nightly build -p matrix-sdk-ffi ${REL_FLAG} --target "x86_64-apple-ios"
|
||||
cargo build -p matrix-sdk-ffi ${REL_FLAG} --target "x86_64-apple-ios"
|
||||
|
||||
lipo -create \
|
||||
"${TARGET_DIR}/x86_64-apple-ios/${REL_TYPE_DIR}/libmatrix_sdk_ffi.a" \
|
||||
@@ -84,8 +84,6 @@ if [ "$IS_CI" = false ] ; then
|
||||
echo "Preparing matrix-rust-components-swift"
|
||||
|
||||
# Debug -> Copy generated files over to ../../../matrix-rust-components-swift
|
||||
echo "$(printf "import MatrixSDKFFIWrapper\n\n"; cat "${SWIFT_DIR}/sdk.swift")" > "${SWIFT_DIR}/sdk.swift"
|
||||
|
||||
rsync -a --delete "${GENERATED_DIR}/MatrixSDKFFI.xcframework" "${SRC_ROOT}/../matrix-rust-components-swift/"
|
||||
rsync -a --delete "${GENERATED_DIR}/swift/" "${SRC_ROOT}/../matrix-rust-components-swift/Sources/MatrixRustSDK"
|
||||
fi
|
||||
|
||||
@@ -20,7 +20,7 @@ hmac = "0.12.1"
|
||||
http = "0.2.6"
|
||||
pbkdf2 = "0.11.0"
|
||||
rand = "0.8.5"
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "7ec599e83d8ba6b2ace9e4fe184fc7560b0ff6ae", features = ["client-api-c"] }
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "ca8c66c885241a7ba3805399604eda4a38979f6b", features = ["client-api-c"] }
|
||||
serde = "1.0.136"
|
||||
serde_json = "1.0.79"
|
||||
sha2 = "0.10.2"
|
||||
|
||||
@@ -30,7 +30,7 @@ tracing = []
|
||||
[dependencies]
|
||||
matrix-sdk-common = { version = "0.5.0", path = "../../crates/matrix-sdk-common" }
|
||||
matrix-sdk-crypto = { version = "0.5.0", path = "../../crates/matrix-sdk-crypto" }
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "7ec599e83d8ba6b2ace9e4fe184fc7560b0ff6ae", features = ["client-api-c", "js", "rand", "unstable-msc2676", "unstable-msc2677"] }
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "ca8c66c885241a7ba3805399604eda4a38979f6b", features = ["client-api-c", "js", "rand", "unstable-msc2676", "unstable-msc2677"] }
|
||||
vodozemac = { git = "https://github.com/matrix-org/vodozemac/", rev = "2404f83f7d3a3779c1f518e4d949f7da9677c3dd", features = ["js"] }
|
||||
wasm-bindgen = "0.2.80"
|
||||
wasm-bindgen-futures = "0.4.30"
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
# Matrix-Rust-SDK NodeJS Bindings
|
||||
# Matrix-Rust-SDK Node.js Bindings
|
||||
|
||||
-- Changelog Test --
|
||||
## 0.1.0-beta.1 - 2022-07-14
|
||||
|
||||
- Fixing broken download link, [#842](https://github.com/matrix-org/matrix-rust-sdk/issues/842)
|
||||
|
||||
## 0.1.0-beta.0 - 2022-07-12
|
||||
|
||||
Welcome to the first release of `matrix-sdk-crypto-nodejs`. This is a
|
||||
Node.js binding for the Rust `matrix-sdk-crypto` library. This is a
|
||||
no-network-IO implementation of a state machine, named `OlmMachine`,
|
||||
that handles E2EE (End-to-End Encryption) for Matrix clients.
|
||||
|
||||
The goal of this binding is _not_ to cover the entirety of the
|
||||
`matrix-sdk-crypto` API, but only what's required to build Matrix bots
|
||||
or Matrix bridges (i.e. to connect different networks together via the
|
||||
Matrix protocol).
|
||||
|
||||
This project replaces and deprecates a previous project, with the same
|
||||
name and same goals, inside [the `matrix-rust-sdk-bindings`
|
||||
repository](https://github.com/matrix-org/matrix-rust-sdk-bindings),
|
||||
with the NPM package name `@turt2live/matrix-sdk-crypto-nodejs`. The
|
||||
The new official package name is
|
||||
`@matrix-org/matrix-sdk-crypto-nodejs`.
|
||||
|
||||
Note: All bindings are now part of [the `matrix-rust-sdk`
|
||||
repository](https://github.com/matrix-org/matrix-rust-sdk) (see the
|
||||
`bindings/` root directory).
|
||||
|
||||
[A documentation is available inside the new
|
||||
`matrix-sdk-crypto-nodejs`
|
||||
project](https://github.com/matrix-org/matrix-rust-sdk/tree/0bde5ccf38f8cda3865297a2d12ddcdaf4b80ca7/bindings/matrix-sdk-crypto-nodejs).
|
||||
|
||||
@@ -28,7 +28,7 @@ tracing = ["dep:tracing-subscriber"]
|
||||
matrix-sdk-crypto = { version = "0.5.0", path = "../../crates/matrix-sdk-crypto" }
|
||||
matrix-sdk-common = { version = "0.5.0", path = "../../crates/matrix-sdk-common" }
|
||||
matrix-sdk-sled = { version = "0.1.0", path = "../../crates/matrix-sdk-sled", default-features = false, features = ["crypto-store"] }
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "7ec599e83d8ba6b2ace9e4fe184fc7560b0ff6ae", features = ["client-api-c", "rand", "unstable-msc2676", "unstable-msc2677"] }
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "ca8c66c885241a7ba3805399604eda4a38979f6b", features = ["client-api-c", "rand", "unstable-msc2676", "unstable-msc2677"] }
|
||||
vodozemac = { git = "https://github.com/matrix-org/vodozemac/", rev = "2404f83f7d3a3779c1f518e4d949f7da9677c3dd" }
|
||||
napi = { git = "https://github.com/Hywan/napi-rs", branch = "fix-napi-strict-on-t-and-ref-t", default-features = false, features = ["napi6", "tokio_rt"] }
|
||||
napi-derive = { git = "https://github.com/Hywan/napi-rs", branch = "fix-napi-strict-on-t-and-ref-t" }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@matrix-org/matrix-sdk-crypto-nodejs",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.0-beta.1",
|
||||
"main": "index.js",
|
||||
"types": "index.d.ts",
|
||||
"napi": {
|
||||
|
||||
@@ -31,6 +31,7 @@ thiserror = "1.0.30"
|
||||
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
|
||||
tokio-stream = "0.1.8"
|
||||
tracing = "0.1.32"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
# keep in sync with uniffi dependency in matrix-sdk-crypto-ffi, and uniffi_bindgen in ffi CI job
|
||||
uniffi = "0.18.0"
|
||||
uniffi_macros = "0.18.0"
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace sdk {
|
||||
void setup_tracing(string configuration);
|
||||
|
||||
MediaSource media_source_from_url(string url);
|
||||
MessageEventContent message_event_content_from_markdown(string md);
|
||||
string gen_transaction_id();
|
||||
@@ -156,24 +158,23 @@ interface MediaSource {
|
||||
|
||||
[Error]
|
||||
enum AuthenticationError {
|
||||
"ClientMissing",
|
||||
"Generic",
|
||||
"ClientMissing",
|
||||
"Generic",
|
||||
};
|
||||
|
||||
interface HomeserverLoginDetails {
|
||||
string url();
|
||||
string? authentication_issuer();
|
||||
boolean supports_password_login();
|
||||
};
|
||||
|
||||
interface AuthenticationService {
|
||||
constructor(string base_path);
|
||||
|
||||
[Throws=AuthenticationError]
|
||||
string homeserver();
|
||||
HomeserverLoginDetails? homeserver_details();
|
||||
|
||||
[Throws=AuthenticationError]
|
||||
string? authentication_issuer();
|
||||
|
||||
[Throws=AuthenticationError]
|
||||
boolean supports_password_login();
|
||||
|
||||
[Throws=AuthenticationError]
|
||||
void use_server(string server_name);
|
||||
void configure_homeserver(string server_name);
|
||||
|
||||
[Throws=AuthenticationError]
|
||||
Client login(string username, string password);
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use futures_util::future::join3;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use super::{client::Client, client_builder::ClientBuilder};
|
||||
use super::{client::Client, client_builder::ClientBuilder, RUNTIME};
|
||||
|
||||
pub struct AuthenticationService {
|
||||
base_path: String,
|
||||
client: RwLock<Option<Arc<Client>>>,
|
||||
homeserver_details: RwLock<Option<Arc<HomeserverLoginDetails>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
@@ -23,52 +25,67 @@ impl From<anyhow::Error> for AuthenticationError {
|
||||
}
|
||||
}
|
||||
|
||||
impl AuthenticationService {
|
||||
/// Creates a new service to authenticate a user with.
|
||||
pub fn new(base_path: String) -> Self {
|
||||
AuthenticationService { base_path, client: RwLock::new(None) }
|
||||
}
|
||||
pub struct HomeserverLoginDetails {
|
||||
url: String,
|
||||
authentication_issuer: Option<String>,
|
||||
supports_password_login: bool,
|
||||
}
|
||||
|
||||
/// The currently configured homeserver.
|
||||
pub fn homeserver(&self) -> Result<String, AuthenticationError> {
|
||||
self.client
|
||||
.read()
|
||||
.as_ref()
|
||||
.ok_or(AuthenticationError::ClientMissing)
|
||||
.map(|client| client.homeserver())
|
||||
impl HomeserverLoginDetails {
|
||||
/// The URL of the currently configured homeserver.
|
||||
pub fn url(&self) -> String {
|
||||
self.url.clone()
|
||||
}
|
||||
|
||||
/// The OIDC Provider that is trusted by the homeserver. `None` when
|
||||
/// not configured.
|
||||
pub fn authentication_issuer(&self) -> Result<Option<String>, AuthenticationError> {
|
||||
self.client
|
||||
.read()
|
||||
.as_ref()
|
||||
.ok_or(AuthenticationError::ClientMissing)
|
||||
.map(|client| client.authentication_issuer())
|
||||
pub fn authentication_issuer(&self) -> Option<String> {
|
||||
self.authentication_issuer.clone()
|
||||
}
|
||||
|
||||
/// Whether the current homeserver supports the password login flow.
|
||||
pub fn supports_password_login(&self) -> Result<bool, AuthenticationError> {
|
||||
self.client
|
||||
.read()
|
||||
.as_ref()
|
||||
.ok_or(AuthenticationError::ClientMissing)
|
||||
.and_then(|client| client.supports_password_login().map_err(AuthenticationError::from))
|
||||
pub fn supports_password_login(&self) -> bool {
|
||||
self.supports_password_login
|
||||
}
|
||||
}
|
||||
|
||||
impl AuthenticationService {
|
||||
/// Creates a new service to authenticate a user with.
|
||||
pub fn new(base_path: String) -> Self {
|
||||
AuthenticationService {
|
||||
base_path,
|
||||
client: RwLock::new(None),
|
||||
homeserver_details: RwLock::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates the server to authenticate with the specified homeserver.
|
||||
pub fn use_server(&self, server_name: String) -> Result<(), AuthenticationError> {
|
||||
pub fn homeserver_details(&self) -> Option<Arc<HomeserverLoginDetails>> {
|
||||
self.homeserver_details.read().clone()
|
||||
}
|
||||
|
||||
/// Updates the service to authenticate with the homeserver for the
|
||||
/// specified address.
|
||||
pub fn configure_homeserver(&self, server_name: String) -> Result<(), AuthenticationError> {
|
||||
// Construct a username as the builder currently requires one.
|
||||
let username = format!("@auth:{}", server_name);
|
||||
let client = Arc::new(ClientBuilder::new())
|
||||
.base_path(self.base_path.clone())
|
||||
.username(username)
|
||||
.build()
|
||||
.map_err(AuthenticationError::from)?;
|
||||
|
||||
*self.client.write() = Some(client);
|
||||
Ok(())
|
||||
let mut builder =
|
||||
Arc::new(ClientBuilder::new()).base_path(self.base_path.clone()).username(username);
|
||||
|
||||
if server_name.starts_with("http://") || server_name.starts_with("https://") {
|
||||
builder = builder.homeserver_url(server_name)
|
||||
}
|
||||
|
||||
let client = builder.build().map_err(AuthenticationError::from)?;
|
||||
|
||||
RUNTIME.block_on(async move {
|
||||
let details = Arc::new(self.details_from_client(&client).await?);
|
||||
|
||||
*self.client.write() = Some(client);
|
||||
*self.homeserver_details.write() = Some(details);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// Performs a password login using the current homeserver.
|
||||
@@ -97,4 +114,23 @@ impl AuthenticationService {
|
||||
None => Err(AuthenticationError::ClientMissing),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the homeserver login details from a client.
|
||||
async fn details_from_client(
|
||||
&self,
|
||||
client: &Arc<Client>,
|
||||
) -> Result<HomeserverLoginDetails, AuthenticationError> {
|
||||
let login_details = join3(
|
||||
client.async_homeserver(),
|
||||
client.authentication_issuer(),
|
||||
client.supports_password_login(),
|
||||
)
|
||||
.await;
|
||||
|
||||
let url = login_details.0;
|
||||
let authentication_issuer = login_details.1;
|
||||
let supports_password_login = login_details.2.map_err(AuthenticationError::from)?;
|
||||
|
||||
Ok(HomeserverLoginDetails { url, authentication_issuer, supports_password_login })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,26 +74,27 @@ impl Client {
|
||||
|
||||
/// The homeserver this client is configured to use.
|
||||
pub fn homeserver(&self) -> String {
|
||||
RUNTIME.block_on(async move { self.client.homeserver().await.to_string() })
|
||||
RUNTIME.block_on(async move { self.async_homeserver().await })
|
||||
}
|
||||
|
||||
pub async fn async_homeserver(&self) -> String {
|
||||
self.client.homeserver().await.to_string()
|
||||
}
|
||||
|
||||
/// The OIDC Provider that is trusted by the homeserver. `None` when
|
||||
/// not configured.
|
||||
pub fn authentication_issuer(&self) -> Option<String> {
|
||||
RUNTIME.block_on(async move {
|
||||
self.client.authentication_issuer().await.map(|server| server.to_string())
|
||||
})
|
||||
pub async fn authentication_issuer(&self) -> Option<String> {
|
||||
self.client.authentication_issuer().await.map(|server| server.to_string())
|
||||
}
|
||||
|
||||
/// Whether or not the client's homeserver supports the password login flow.
|
||||
pub fn supports_password_login(&self) -> anyhow::Result<bool> {
|
||||
RUNTIME.block_on(async move {
|
||||
let login_types = self.client.get_login_types().await?;
|
||||
let supports_password = login_types.flows.iter().any(|login_type| {
|
||||
matches!(login_type, get_login_types::v3::LoginType::Password(_))
|
||||
});
|
||||
Ok(supports_password)
|
||||
})
|
||||
pub async fn supports_password_login(&self) -> anyhow::Result<bool> {
|
||||
let login_types = self.client.get_login_types().await?;
|
||||
let supports_password = login_types
|
||||
.flows
|
||||
.iter()
|
||||
.any(|login_type| matches!(login_type, get_login_types::v3::LoginType::Password(_)));
|
||||
Ok(supports_password)
|
||||
}
|
||||
|
||||
pub fn start_sync(&self) {
|
||||
|
||||
@@ -17,6 +17,7 @@ use matrix_sdk::Session;
|
||||
use once_cell::sync::Lazy;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::runtime::Runtime;
|
||||
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
|
||||
pub use uniffi_api::*;
|
||||
|
||||
pub static RUNTIME: Lazy<Runtime> =
|
||||
@@ -55,3 +56,10 @@ impl From<anyhow::Error> for ClientError {
|
||||
ClientError::Generic { msg: e.to_string() }
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_tracing(configuration: String) {
|
||||
tracing_subscriber::registry()
|
||||
.with(EnvFilter::new(configuration))
|
||||
.with(fmt::layer().with_ansi(false))
|
||||
.init();
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ http = "0.2.6"
|
||||
matrix-sdk = { version = "0.5.0", path = "../matrix-sdk", default-features = false, features = ["appservice"] }
|
||||
percent-encoding = "2.1.0"
|
||||
regex = "1.5.5"
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "7ec599e83d8ba6b2ace9e4fe184fc7560b0ff6ae", features = ["client-api-c", "appservice-api-s"] }
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "ca8c66c885241a7ba3805399604eda4a38979f6b", features = ["client-api-c", "appservice-api-s"] }
|
||||
serde = "1.0.136"
|
||||
serde_json = "1.0.79"
|
||||
serde_yaml = "0.8.23"
|
||||
|
||||
@@ -41,41 +41,41 @@ pub enum Error {
|
||||
#[error("uri path is unknown")]
|
||||
UriPathUnknown,
|
||||
|
||||
#[error(transparent)]
|
||||
HttpRequest(#[from] ruma::api::error::FromHttpRequestError),
|
||||
#[error("HTTP request parsing error: {0}")]
|
||||
FromHttpRequest(#[from] ruma::api::error::FromHttpRequestError),
|
||||
|
||||
#[error(transparent)]
|
||||
#[error("identifier failed to parse: {0}")]
|
||||
Identifier(#[from] ruma::IdParseError),
|
||||
|
||||
#[error(transparent)]
|
||||
#[error("HTTP error: {0}")]
|
||||
Http(#[from] http::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
#[error("url parse error: {0}")]
|
||||
Url(#[from] url::ParseError),
|
||||
|
||||
#[error(transparent)]
|
||||
#[error("deserialization error: {0}")]
|
||||
Serde(#[from] serde::de::value::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
#[error("I/O error: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
#[error("http uri invalid error: {0}")]
|
||||
InvalidUri(#[from] http::uri::InvalidUri),
|
||||
|
||||
#[error(transparent)]
|
||||
Matrix(#[from] matrix_sdk::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
#[error("regex error: {0}")]
|
||||
Regex(#[from] regex::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
#[error("serde yaml error: {0}")]
|
||||
SerdeYaml(#[from] serde_yaml::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
#[error("serde json error: {0}")]
|
||||
SerdeJson(#[from] serde_json::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
Utf8Error(#[from] std::str::Utf8Error),
|
||||
#[error("utf8 error: {0}")]
|
||||
Utf8(#[from] std::str::Utf8Error),
|
||||
|
||||
#[error("warp rejection: {0}")]
|
||||
WarpRejection(String),
|
||||
|
||||
@@ -37,9 +37,6 @@
|
||||
//! for the access tokens and because membership states for virtual users are
|
||||
//! determined based on the registered namespaces.
|
||||
//!
|
||||
//! **Note:** Non-exclusive registration namespaces are not yet supported and
|
||||
//! hence might lead to undefined behavior.
|
||||
//!
|
||||
//! # Quickstart
|
||||
//!
|
||||
//! ```no_run
|
||||
@@ -95,7 +92,7 @@ use event_handler::AppserviceFn;
|
||||
pub use matrix_sdk;
|
||||
#[doc(no_inline)]
|
||||
pub use matrix_sdk::ruma;
|
||||
use matrix_sdk::{bytes::Bytes, reqwest::Url, Client, ClientBuilder};
|
||||
use matrix_sdk::{reqwest::Url, Client, ClientBuilder};
|
||||
use ruma::{
|
||||
api::{
|
||||
appservice::{
|
||||
@@ -392,7 +389,7 @@ impl AppService {
|
||||
/// active virtual clients.
|
||||
///
|
||||
/// [transaction]: https://spec.matrix.org/v1.2/application-service-api/#put_matrixappv1transactionstxnid
|
||||
pub async fn receive_transaction(
|
||||
async fn receive_transaction(
|
||||
&self,
|
||||
transaction: push_events::v1::IncomingRequest,
|
||||
) -> Result<()> {
|
||||
@@ -515,66 +512,517 @@ impl AppService {
|
||||
}
|
||||
}
|
||||
|
||||
/// Ruma always expects the path to start with `/_matrix`, so we transform
|
||||
/// accordingly. Handles [legacy routes] and appservice being located on a sub
|
||||
/// path.
|
||||
///
|
||||
/// [legacy routes]: https://matrix.org/docs/spec/application_service/r0.1.2#legacy-routes
|
||||
// TODO: consider ruma PR
|
||||
pub(crate) fn transform_request_path(
|
||||
mut request: http::Request<Bytes>,
|
||||
) -> Result<http::Request<Bytes>> {
|
||||
let uri = request.uri();
|
||||
// remove trailing slash from path
|
||||
let path = uri.path().trim_end_matches('/').to_owned();
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{
|
||||
future,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
if !path.starts_with("/_matrix/app/v1/") {
|
||||
let path = match path {
|
||||
// special-case paths without value at the end
|
||||
_ if path.ends_with("/_matrix/app/unstable/thirdparty/user") => {
|
||||
"/_matrix/app/v1/thirdparty/user".to_owned()
|
||||
}
|
||||
_ if path.ends_with("/_matrix/app/unstable/thirdparty/location") => {
|
||||
"/_matrix/app/v1/thirdparty/location".to_owned()
|
||||
}
|
||||
// regular paths with values at the end
|
||||
_ => {
|
||||
let mut path = path.split('/').into_iter().rev();
|
||||
let value = match path.next() {
|
||||
Some(value) => value,
|
||||
None => return Err(Error::UriEmptyPath),
|
||||
};
|
||||
use matrix_sdk::{
|
||||
config::RequestConfig,
|
||||
ruma::{api::appservice::Registration, events::room::member::OriginalSyncRoomMemberEvent},
|
||||
Client,
|
||||
};
|
||||
use matrix_sdk_test::{appservice::TransactionBuilder, async_test, TimelineTestEvent};
|
||||
use ruma::{
|
||||
api::{appservice::event::push_events, MatrixVersion},
|
||||
events::AnyRoomEvent,
|
||||
room_id,
|
||||
serde::Raw,
|
||||
};
|
||||
use serde_json::json;
|
||||
use warp::{Filter, Reply};
|
||||
use wiremock::{
|
||||
matchers::{body_json, header, method, path},
|
||||
Mock, MockServer, ResponseTemplate,
|
||||
};
|
||||
|
||||
let mut path = match path.next() {
|
||||
Some(path_segment)
|
||||
if ["transactions", "users", "rooms"].contains(&path_segment) =>
|
||||
{
|
||||
format!("/_matrix/app/v1/{}/{}", path_segment, value)
|
||||
}
|
||||
Some(path_segment) => match path.next() {
|
||||
Some(path_segment2) if path_segment2 == "thirdparty" => {
|
||||
format!("/_matrix/app/v1/thirdparty/{}/{}", path_segment, value)
|
||||
}
|
||||
_ => return Err(Error::UriPathUnknown),
|
||||
},
|
||||
None => return Err(Error::UriEmptyPath),
|
||||
};
|
||||
use super::*;
|
||||
|
||||
if let Some(query) = uri.query() {
|
||||
path.push('?');
|
||||
path.push_str(query);
|
||||
}
|
||||
|
||||
path
|
||||
}
|
||||
};
|
||||
|
||||
let mut parts = uri.clone().into_parts();
|
||||
parts.path_and_query = Some(path.parse()?);
|
||||
|
||||
let uri = parts.try_into().map_err(http::Error::from)?;
|
||||
*request.uri_mut() = uri;
|
||||
fn registration_string() -> String {
|
||||
include_str!("../tests/registration.yaml").to_owned()
|
||||
}
|
||||
|
||||
Ok(request)
|
||||
async fn appservice(
|
||||
homeserver_url: Option<String>,
|
||||
registration: Option<Registration>,
|
||||
) -> Result<AppService> {
|
||||
let _ = tracing_subscriber::fmt::try_init();
|
||||
|
||||
let registration = match registration {
|
||||
Some(registration) => registration.into(),
|
||||
None => AppServiceRegistration::try_from_yaml_str(registration_string()).unwrap(),
|
||||
};
|
||||
|
||||
let homeserver_url = homeserver_url.unwrap_or_else(|| "http://localhost:1234".to_owned());
|
||||
let server_name = "localhost";
|
||||
|
||||
let client_builder = Client::builder()
|
||||
.request_config(RequestConfig::default().disable_retry())
|
||||
.server_versions([MatrixVersion::V1_0]);
|
||||
|
||||
AppService::with_client_builder(
|
||||
homeserver_url.as_ref(),
|
||||
server_name,
|
||||
registration,
|
||||
client_builder,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_register_virtual_user() -> Result<()> {
|
||||
let server = MockServer::start().await;
|
||||
let appservice = appservice(Some(server.uri()), None).await?;
|
||||
|
||||
let localpart = "someone";
|
||||
Mock::given(method("POST"))
|
||||
.and(path("/_matrix/client/r0/register"))
|
||||
.and(header(
|
||||
"authorization",
|
||||
format!("Bearer {}", appservice.registration().as_token).as_str(),
|
||||
))
|
||||
.and(body_json(json!({
|
||||
"username": localpart.to_owned(),
|
||||
"type": "m.login.application_service"
|
||||
})))
|
||||
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
|
||||
"access_token": "abc123",
|
||||
"device_id": "GHTYAJCE",
|
||||
"user_id": format!("@{localpart}:localhost"),
|
||||
})))
|
||||
.mount(&server)
|
||||
.await;
|
||||
|
||||
appservice.register_virtual_user(localpart, None).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_put_transaction() -> Result<()> {
|
||||
let uri = "/_matrix/app/v1/transactions/1?access_token=hs_token";
|
||||
|
||||
let mut transaction_builder = TransactionBuilder::new();
|
||||
transaction_builder.add_room_event(TimelineTestEvent::Member);
|
||||
let transaction = transaction_builder.build_json_transaction();
|
||||
|
||||
let appservice = appservice(None, None).await?;
|
||||
|
||||
let status = warp::test::request()
|
||||
.method("PUT")
|
||||
.path(uri)
|
||||
.json(&transaction)
|
||||
.filter(&appservice.warp_filter())
|
||||
.await
|
||||
.unwrap()
|
||||
.into_response()
|
||||
.status();
|
||||
|
||||
assert_eq!(status, 200);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_put_transaction_with_repeating_txn_id() -> Result<()> {
|
||||
let uri = "/_matrix/app/v1/transactions/1?access_token=hs_token";
|
||||
|
||||
let mut transaction_builder = TransactionBuilder::new();
|
||||
transaction_builder.add_room_event(TimelineTestEvent::Member);
|
||||
let transaction = transaction_builder.build_json_transaction();
|
||||
|
||||
let appservice = appservice(None, None).await?;
|
||||
|
||||
#[allow(clippy::mutex_atomic)]
|
||||
let on_state_member = Arc::new(Mutex::new(false));
|
||||
appservice
|
||||
.virtual_user(None)
|
||||
.await?
|
||||
.register_event_handler({
|
||||
let on_state_member = on_state_member.clone();
|
||||
move |_ev: OriginalSyncRoomMemberEvent| {
|
||||
*on_state_member.lock().unwrap() = true;
|
||||
future::ready(())
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
let status = warp::test::request()
|
||||
.method("PUT")
|
||||
.path(uri)
|
||||
.json(&transaction)
|
||||
.filter(&appservice.warp_filter())
|
||||
.await
|
||||
.unwrap()
|
||||
.into_response()
|
||||
.status();
|
||||
|
||||
assert_eq!(status, 200);
|
||||
{
|
||||
let on_room_member_called = *on_state_member.lock().unwrap();
|
||||
assert!(on_room_member_called);
|
||||
}
|
||||
|
||||
// Reset this to check that next time it doesnt get called
|
||||
{
|
||||
let mut on_room_member_called = on_state_member.lock().unwrap();
|
||||
*on_room_member_called = false;
|
||||
}
|
||||
|
||||
let status = warp::test::request()
|
||||
.method("PUT")
|
||||
.path(uri)
|
||||
.json(&transaction)
|
||||
.filter(&appservice.warp_filter())
|
||||
.await
|
||||
.unwrap()
|
||||
.into_response()
|
||||
.status();
|
||||
|
||||
// According to https://spec.matrix.org/v1.2/application-service-api/#pushing-events
|
||||
// This should noop and return 200.
|
||||
assert_eq!(status, 200);
|
||||
{
|
||||
let on_room_member_called = *on_state_member.lock().unwrap();
|
||||
// This time we should not have called the event handler.
|
||||
assert!(!on_room_member_called);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_get_user() -> Result<()> {
|
||||
let appservice = appservice(None, None).await?;
|
||||
appservice.register_user_query(Box::new(|_, _| Box::pin(async move { true }))).await;
|
||||
|
||||
let uri = "/_matrix/app/v1/users/%40_botty_1%3Adev.famedly.local?access_token=hs_token";
|
||||
|
||||
let status = warp::test::request()
|
||||
.method("GET")
|
||||
.path(uri)
|
||||
.filter(&appservice.warp_filter())
|
||||
.await
|
||||
.unwrap()
|
||||
.into_response()
|
||||
.status();
|
||||
|
||||
assert_eq!(status, 200);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_get_room() -> Result<()> {
|
||||
let appservice = appservice(None, None).await?;
|
||||
appservice.register_room_query(Box::new(|_, _| Box::pin(async move { true }))).await;
|
||||
|
||||
let uri = "/_matrix/app/v1/rooms/%23magicforest%3Aexample.com?access_token=hs_token";
|
||||
|
||||
let status = warp::test::request()
|
||||
.method("GET")
|
||||
.path(uri)
|
||||
.filter(&appservice.warp_filter())
|
||||
.await
|
||||
.unwrap()
|
||||
.into_response()
|
||||
.status();
|
||||
|
||||
assert_eq!(status, 200);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_invalid_access_token() -> Result<()> {
|
||||
let uri = "/_matrix/app/v1/transactions/1?access_token=invalid_token";
|
||||
|
||||
let mut transaction_builder = TransactionBuilder::new();
|
||||
let transaction =
|
||||
transaction_builder.add_room_event(TimelineTestEvent::Member).build_json_transaction();
|
||||
|
||||
let appservice = appservice(None, None).await?;
|
||||
|
||||
let status = warp::test::request()
|
||||
.method("PUT")
|
||||
.path(uri)
|
||||
.json(&transaction)
|
||||
.filter(&appservice.warp_filter())
|
||||
.await
|
||||
.unwrap()
|
||||
.into_response()
|
||||
.status();
|
||||
|
||||
assert_eq!(status, 401);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_no_access_token() -> Result<()> {
|
||||
let uri = "/_matrix/app/v1/transactions/1";
|
||||
|
||||
let mut transaction_builder = TransactionBuilder::new();
|
||||
transaction_builder.add_room_event(TimelineTestEvent::Member);
|
||||
let transaction = transaction_builder.build_json_transaction();
|
||||
|
||||
let appservice = appservice(None, None).await?;
|
||||
|
||||
{
|
||||
let status = warp::test::request()
|
||||
.method("PUT")
|
||||
.path(uri)
|
||||
.json(&transaction)
|
||||
.filter(&appservice.warp_filter())
|
||||
.await
|
||||
.unwrap()
|
||||
.into_response()
|
||||
.status();
|
||||
|
||||
assert_eq!(status, 401);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_event_handler() -> Result<()> {
|
||||
let appservice = appservice(None, None).await?;
|
||||
|
||||
#[allow(clippy::mutex_atomic)]
|
||||
let on_state_member = Arc::new(Mutex::new(false));
|
||||
appservice
|
||||
.virtual_user(None)
|
||||
.await?
|
||||
.register_event_handler({
|
||||
let on_state_member = on_state_member.clone();
|
||||
move |_ev: OriginalSyncRoomMemberEvent| {
|
||||
*on_state_member.lock().unwrap() = true;
|
||||
future::ready(())
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
let uri = "/_matrix/app/v1/transactions/1?access_token=hs_token";
|
||||
|
||||
let mut transaction_builder = TransactionBuilder::new();
|
||||
transaction_builder.add_room_event(TimelineTestEvent::Member);
|
||||
let transaction = transaction_builder.build_json_transaction();
|
||||
|
||||
warp::test::request()
|
||||
.method("PUT")
|
||||
.path(uri)
|
||||
.json(&transaction)
|
||||
.filter(&appservice.warp_filter())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let on_room_member_called = *on_state_member.lock().unwrap();
|
||||
assert!(on_room_member_called);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_unrelated_path() -> Result<()> {
|
||||
let appservice = appservice(None, None).await?;
|
||||
|
||||
let status = {
|
||||
let consumer_filter = warp::any()
|
||||
.and(appservice.warp_filter())
|
||||
.or(warp::get().and(warp::path("unrelated").map(warp::reply)));
|
||||
|
||||
let response = warp::test::request()
|
||||
.method("GET")
|
||||
.path("/unrelated")
|
||||
.filter(&consumer_filter)
|
||||
.await?
|
||||
.into_response();
|
||||
|
||||
response.status()
|
||||
};
|
||||
|
||||
assert_eq!(status, 200);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_appservice_on_sub_path() -> Result<()> {
|
||||
let room_id = room_id!("!SVkFJHzfwvuaIEawgC:localhost");
|
||||
let uri_1 = "/sub_path/_matrix/app/v1/transactions/1?access_token=hs_token";
|
||||
let uri_2 = "/sub_path/_matrix/app/v1/transactions/2?access_token=hs_token";
|
||||
|
||||
let mut transaction_builder = TransactionBuilder::new();
|
||||
transaction_builder.add_room_event(TimelineTestEvent::Member);
|
||||
let transaction_1 = transaction_builder.build_json_transaction();
|
||||
|
||||
let mut transaction_builder = TransactionBuilder::new();
|
||||
transaction_builder.add_room_event(TimelineTestEvent::MemberNameChange);
|
||||
let transaction_2 = transaction_builder.build_json_transaction();
|
||||
|
||||
let appservice = appservice(None, None).await?;
|
||||
|
||||
{
|
||||
warp::test::request()
|
||||
.method("PUT")
|
||||
.path(uri_1)
|
||||
.json(&transaction_1)
|
||||
.filter(&warp::path("sub_path").and(appservice.warp_filter()))
|
||||
.await?;
|
||||
|
||||
warp::test::request()
|
||||
.method("PUT")
|
||||
.path(uri_2)
|
||||
.json(&transaction_2)
|
||||
.filter(&warp::path("sub_path").and(appservice.warp_filter()))
|
||||
.await?;
|
||||
};
|
||||
|
||||
let members = appservice
|
||||
.virtual_user(None)
|
||||
.await?
|
||||
.get_room(room_id)
|
||||
.expect("Expected room to be available")
|
||||
.members_no_sync()
|
||||
.await?;
|
||||
|
||||
assert_eq!(members[0].display_name().unwrap(), "changed");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_receive_transaction() -> Result<()> {
|
||||
tracing_subscriber::fmt().try_init().ok();
|
||||
let json = vec![
|
||||
Raw::new(&json!({
|
||||
"content": {
|
||||
"avatar_url": null,
|
||||
"displayname": "Appservice",
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$151800140479rdvjg:localhost",
|
||||
"membership": "join",
|
||||
"origin_server_ts": 151800140,
|
||||
"sender": "@_appservice:localhost",
|
||||
"state_key": "@_appservice:localhost",
|
||||
"type": "m.room.member",
|
||||
"room_id": "!coolplace:localhost",
|
||||
"unsigned": {
|
||||
"age": 2970366
|
||||
}
|
||||
}))?
|
||||
.cast::<AnyRoomEvent>(),
|
||||
Raw::new(&json!({
|
||||
"content": {
|
||||
"avatar_url": null,
|
||||
"displayname": "Appservice",
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$151800140491rfbja:localhost",
|
||||
"membership": "join",
|
||||
"origin_server_ts": 151800140,
|
||||
"sender": "@_appservice:localhost",
|
||||
"state_key": "@_appservice:localhost",
|
||||
"type": "m.room.member",
|
||||
"room_id": "!boringplace:localhost",
|
||||
"unsigned": {
|
||||
"age": 2970366
|
||||
}
|
||||
}))?
|
||||
.cast::<AnyRoomEvent>(),
|
||||
Raw::new(&json!({
|
||||
"content": {
|
||||
"avatar_url": null,
|
||||
"displayname": "Alice",
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$151800140517rfvjc:localhost",
|
||||
"membership": "join",
|
||||
"origin_server_ts": 151800140,
|
||||
"sender": "@_appservice_alice:localhost",
|
||||
"state_key": "@_appservice_alice:localhost",
|
||||
"type": "m.room.member",
|
||||
"room_id": "!coolplace:localhost",
|
||||
"unsigned": {
|
||||
"age": 2970366
|
||||
}
|
||||
}))?
|
||||
.cast::<AnyRoomEvent>(),
|
||||
Raw::new(&json!({
|
||||
"content": {
|
||||
"avatar_url": null,
|
||||
"displayname": "Bob",
|
||||
"membership": "invite"
|
||||
},
|
||||
"event_id": "$151800140594rfvjc:localhost",
|
||||
"membership": "invite",
|
||||
"origin_server_ts": 151800174,
|
||||
"sender": "@_appservice_bob:localhost",
|
||||
"state_key": "@_appservice_bob:localhost",
|
||||
"type": "m.room.member",
|
||||
"room_id": "!boringplace:localhost",
|
||||
"unsigned": {
|
||||
"age": 2970366
|
||||
}
|
||||
}))?
|
||||
.cast::<AnyRoomEvent>(),
|
||||
];
|
||||
let appservice = appservice(None, None).await?;
|
||||
|
||||
let alice = appservice.virtual_user(Some("_appservice_alice")).await?;
|
||||
let bob = appservice.virtual_user(Some("_appservice_bob")).await?;
|
||||
appservice
|
||||
.receive_transaction(push_events::v1::IncomingRequest::new("dontcare".into(), json))
|
||||
.await?;
|
||||
let coolplace = room_id!("!coolplace:localhost");
|
||||
let boringplace = room_id!("!boringplace:localhost");
|
||||
assert!(
|
||||
alice.get_joined_room(coolplace).is_some(),
|
||||
"Alice's membership in coolplace should be join"
|
||||
);
|
||||
assert!(
|
||||
bob.get_invited_room(boringplace).is_some(),
|
||||
"Bob's membership in boringplace should be invite"
|
||||
);
|
||||
assert!(alice.get_room(boringplace).is_none(), "Alice should not know about boringplace");
|
||||
assert!(bob.get_room(coolplace).is_none(), "Bob should not know about coolplace");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
mod registration {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_registration() -> Result<()> {
|
||||
let registration: Registration = serde_yaml::from_str(®istration_string())?;
|
||||
let registration: AppServiceRegistration = registration.into();
|
||||
|
||||
assert_eq!(registration.id, "appservice");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_registration_from_yaml_file() -> Result<()> {
|
||||
let registration =
|
||||
AppServiceRegistration::try_from_yaml_file("./tests/registration.yaml")?;
|
||||
|
||||
assert_eq!(registration.id, "appservice");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_registration_from_yaml_str() -> Result<()> {
|
||||
let registration = AppServiceRegistration::try_from_yaml_str(registration_string())?;
|
||||
|
||||
assert_eq!(registration.id, "appservice");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,10 +107,11 @@ mod filters {
|
||||
warp::any()
|
||||
.and(valid_access_token(appservice.registration().hs_token.clone()))
|
||||
.map(move || appservice.clone())
|
||||
.and(http_request().and_then(|request| async move {
|
||||
let request = crate::transform_request_path(request).map_err(Error::from)?;
|
||||
Ok::<http::Request<Bytes>, Rejection>(request)
|
||||
}))
|
||||
.and(
|
||||
http_request().and_then(|request| async move {
|
||||
Ok::<http::Request<Bytes>, Rejection>(request)
|
||||
}),
|
||||
)
|
||||
.boxed()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,513 +0,0 @@
|
||||
use std::{
|
||||
future,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use matrix_sdk::{
|
||||
config::RequestConfig,
|
||||
ruma::{api::appservice::Registration, events::room::member::OriginalSyncRoomMemberEvent},
|
||||
Client,
|
||||
};
|
||||
use matrix_sdk_appservice::*;
|
||||
use matrix_sdk_test::{appservice::TransactionBuilder, async_test, test_json, TimelineTestEvent};
|
||||
use ruma::{
|
||||
api::{appservice::event::push_events, MatrixVersion},
|
||||
events::AnyRoomEvent,
|
||||
room_id,
|
||||
serde::Raw,
|
||||
};
|
||||
use serde_json::json;
|
||||
use warp::{Filter, Reply};
|
||||
use wiremock::{
|
||||
matchers::{body_json, header, method, path},
|
||||
Mock, MockServer, ResponseTemplate,
|
||||
};
|
||||
|
||||
fn registration_string() -> String {
|
||||
include_str!("../tests/registration.yaml").to_owned()
|
||||
}
|
||||
|
||||
async fn appservice(
|
||||
homeserver_url: Option<String>,
|
||||
registration: Option<Registration>,
|
||||
) -> Result<AppService> {
|
||||
// env::set_var(
|
||||
// "RUST_LOG",
|
||||
// "wiremock=debug,matrix_sdk=debug,ruma=debug,warp=debug",
|
||||
// );
|
||||
let _ = tracing_subscriber::fmt::try_init();
|
||||
|
||||
let registration = match registration {
|
||||
Some(registration) => registration.into(),
|
||||
None => AppServiceRegistration::try_from_yaml_str(registration_string()).unwrap(),
|
||||
};
|
||||
|
||||
let homeserver_url = homeserver_url.unwrap_or_else(|| "http://localhost:1234".to_owned());
|
||||
let server_name = "localhost";
|
||||
|
||||
let client_builder = Client::builder()
|
||||
.request_config(RequestConfig::default().disable_retry())
|
||||
.server_versions([MatrixVersion::V1_0]);
|
||||
|
||||
AppService::with_client_builder(
|
||||
homeserver_url.as_ref(),
|
||||
server_name,
|
||||
registration,
|
||||
client_builder,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_register_virtual_user() -> Result<()> {
|
||||
let server = MockServer::start().await;
|
||||
let appservice = appservice(Some(server.uri()), None).await?;
|
||||
|
||||
let localpart = "someone";
|
||||
Mock::given(method("POST"))
|
||||
.and(path("/_matrix/client/r0/register"))
|
||||
.and(header(
|
||||
"authorization",
|
||||
format!("Bearer {}", appservice.registration().as_token).as_str(),
|
||||
))
|
||||
.and(body_json(json!({
|
||||
"username": localpart.to_owned(),
|
||||
"type": "m.login.application_service"
|
||||
})))
|
||||
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
|
||||
"access_token": "abc123",
|
||||
"device_id": "GHTYAJCE",
|
||||
"user_id": format!("@{localpart}:localhost"),
|
||||
})))
|
||||
.mount(&server)
|
||||
.await;
|
||||
|
||||
appservice.register_virtual_user(localpart, None).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_put_transaction() -> Result<()> {
|
||||
let uri = "/_matrix/app/v1/transactions/1?access_token=hs_token";
|
||||
|
||||
let mut transaction_builder = TransactionBuilder::new();
|
||||
transaction_builder.add_room_event(TimelineTestEvent::Member);
|
||||
let transaction = transaction_builder.build_json_transaction();
|
||||
|
||||
let appservice = appservice(None, None).await?;
|
||||
|
||||
let status = warp::test::request()
|
||||
.method("PUT")
|
||||
.path(uri)
|
||||
.json(&transaction)
|
||||
.filter(&appservice.warp_filter())
|
||||
.await
|
||||
.unwrap()
|
||||
.into_response()
|
||||
.status();
|
||||
|
||||
assert_eq!(status, 200);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_put_transaction_with_repeating_txn_id() -> Result<()> {
|
||||
let uri = "/_matrix/app/v1/transactions/1?access_token=hs_token";
|
||||
|
||||
let mut transaction_builder = TransactionBuilder::new();
|
||||
transaction_builder.add_room_event(TimelineTestEvent::Member);
|
||||
let transaction = transaction_builder.build_json_transaction();
|
||||
|
||||
let appservice = appservice(None, None).await?;
|
||||
|
||||
#[allow(clippy::mutex_atomic)]
|
||||
let on_state_member = Arc::new(Mutex::new(false));
|
||||
appservice
|
||||
.virtual_user(None)
|
||||
.await?
|
||||
.register_event_handler({
|
||||
let on_state_member = on_state_member.clone();
|
||||
move |_ev: OriginalSyncRoomMemberEvent| {
|
||||
*on_state_member.lock().unwrap() = true;
|
||||
future::ready(())
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
let status = warp::test::request()
|
||||
.method("PUT")
|
||||
.path(uri)
|
||||
.json(&transaction)
|
||||
.filter(&appservice.warp_filter())
|
||||
.await
|
||||
.unwrap()
|
||||
.into_response()
|
||||
.status();
|
||||
|
||||
assert_eq!(status, 200);
|
||||
{
|
||||
let on_room_member_called = *on_state_member.lock().unwrap();
|
||||
assert!(on_room_member_called);
|
||||
}
|
||||
|
||||
// Reset this to check that next time it doesnt get called
|
||||
{
|
||||
let mut on_room_member_called = on_state_member.lock().unwrap();
|
||||
*on_room_member_called = false;
|
||||
}
|
||||
|
||||
let status = warp::test::request()
|
||||
.method("PUT")
|
||||
.path(uri)
|
||||
.json(&transaction)
|
||||
.filter(&appservice.warp_filter())
|
||||
.await
|
||||
.unwrap()
|
||||
.into_response()
|
||||
.status();
|
||||
|
||||
// According to https://spec.matrix.org/v1.2/application-service-api/#pushing-events
|
||||
// This should noop and return 200.
|
||||
assert_eq!(status, 200);
|
||||
{
|
||||
let on_room_member_called = *on_state_member.lock().unwrap();
|
||||
// This time we should not have called the event handler.
|
||||
assert!(!on_room_member_called);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_get_user() -> Result<()> {
|
||||
let appservice = appservice(None, None).await?;
|
||||
appservice.register_user_query(Box::new(|_, _| Box::pin(async move { true }))).await;
|
||||
|
||||
let uri = "/_matrix/app/v1/users/%40_botty_1%3Adev.famedly.local?access_token=hs_token";
|
||||
|
||||
let status = warp::test::request()
|
||||
.method("GET")
|
||||
.path(uri)
|
||||
.filter(&appservice.warp_filter())
|
||||
.await
|
||||
.unwrap()
|
||||
.into_response()
|
||||
.status();
|
||||
|
||||
assert_eq!(status, 200);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_get_room() -> Result<()> {
|
||||
let appservice = appservice(None, None).await?;
|
||||
appservice.register_room_query(Box::new(|_, _| Box::pin(async move { true }))).await;
|
||||
|
||||
let uri = "/_matrix/app/v1/rooms/%23magicforest%3Aexample.com?access_token=hs_token";
|
||||
|
||||
let status = warp::test::request()
|
||||
.method("GET")
|
||||
.path(uri)
|
||||
.filter(&appservice.warp_filter())
|
||||
.await
|
||||
.unwrap()
|
||||
.into_response()
|
||||
.status();
|
||||
|
||||
assert_eq!(status, 200);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_invalid_access_token() -> Result<()> {
|
||||
let uri = "/_matrix/app/v1/transactions/1?access_token=invalid_token";
|
||||
|
||||
let mut transaction_builder = TransactionBuilder::new();
|
||||
let transaction =
|
||||
transaction_builder.add_room_event(TimelineTestEvent::Member).build_json_transaction();
|
||||
|
||||
let appservice = appservice(None, None).await?;
|
||||
|
||||
let status = warp::test::request()
|
||||
.method("PUT")
|
||||
.path(uri)
|
||||
.json(&transaction)
|
||||
.filter(&appservice.warp_filter())
|
||||
.await
|
||||
.unwrap()
|
||||
.into_response()
|
||||
.status();
|
||||
|
||||
assert_eq!(status, 401);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_no_access_token() -> Result<()> {
|
||||
let uri = "/_matrix/app/v1/transactions/1";
|
||||
|
||||
let mut transaction_builder = TransactionBuilder::new();
|
||||
transaction_builder.add_room_event(TimelineTestEvent::Member);
|
||||
let transaction = transaction_builder.build_json_transaction();
|
||||
|
||||
let appservice = appservice(None, None).await?;
|
||||
|
||||
{
|
||||
let status = warp::test::request()
|
||||
.method("PUT")
|
||||
.path(uri)
|
||||
.json(&transaction)
|
||||
.filter(&appservice.warp_filter())
|
||||
.await
|
||||
.unwrap()
|
||||
.into_response()
|
||||
.status();
|
||||
|
||||
assert_eq!(status, 401);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_event_handler() -> Result<()> {
|
||||
let appservice = appservice(None, None).await?;
|
||||
|
||||
#[allow(clippy::mutex_atomic)]
|
||||
let on_state_member = Arc::new(Mutex::new(false));
|
||||
appservice
|
||||
.virtual_user(None)
|
||||
.await?
|
||||
.register_event_handler({
|
||||
let on_state_member = on_state_member.clone();
|
||||
move |_ev: OriginalSyncRoomMemberEvent| {
|
||||
*on_state_member.lock().unwrap() = true;
|
||||
future::ready(())
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
let uri = "/_matrix/app/v1/transactions/1?access_token=hs_token";
|
||||
|
||||
let mut transaction_builder = TransactionBuilder::new();
|
||||
transaction_builder.add_room_event(TimelineTestEvent::Member);
|
||||
let transaction = transaction_builder.build_json_transaction();
|
||||
|
||||
warp::test::request()
|
||||
.method("PUT")
|
||||
.path(uri)
|
||||
.json(&transaction)
|
||||
.filter(&appservice.warp_filter())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let on_room_member_called = *on_state_member.lock().unwrap();
|
||||
assert!(on_room_member_called);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_unrelated_path() -> Result<()> {
|
||||
let appservice = appservice(None, None).await?;
|
||||
|
||||
let status = {
|
||||
let consumer_filter = warp::any()
|
||||
.and(appservice.warp_filter())
|
||||
.or(warp::get().and(warp::path("unrelated").map(warp::reply)));
|
||||
|
||||
let response = warp::test::request()
|
||||
.method("GET")
|
||||
.path("/unrelated")
|
||||
.filter(&consumer_filter)
|
||||
.await?
|
||||
.into_response();
|
||||
|
||||
response.status()
|
||||
};
|
||||
|
||||
assert_eq!(status, 200);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_appservice_on_sub_path() -> Result<()> {
|
||||
let room_id = &test_json::DEFAULT_SYNC_ROOM_ID;
|
||||
let uri_1 = "/sub_path/_matrix/app/v1/transactions/1?access_token=hs_token";
|
||||
let uri_2 = "/sub_path/_matrix/app/v1/transactions/2?access_token=hs_token";
|
||||
|
||||
let mut transaction_builder = TransactionBuilder::new();
|
||||
transaction_builder.add_room_event(TimelineTestEvent::Member);
|
||||
let transaction_1 = transaction_builder.build_json_transaction();
|
||||
|
||||
let mut transaction_builder = TransactionBuilder::new();
|
||||
transaction_builder.add_room_event(TimelineTestEvent::MemberNameChange);
|
||||
let transaction_2 = transaction_builder.build_json_transaction();
|
||||
|
||||
let appservice = appservice(None, None).await?;
|
||||
|
||||
{
|
||||
warp::test::request()
|
||||
.method("PUT")
|
||||
.path(uri_1)
|
||||
.json(&transaction_1)
|
||||
.filter(&warp::path("sub_path").and(appservice.warp_filter()))
|
||||
.await?;
|
||||
|
||||
warp::test::request()
|
||||
.method("PUT")
|
||||
.path(uri_2)
|
||||
.json(&transaction_2)
|
||||
.filter(&warp::path("sub_path").and(appservice.warp_filter()))
|
||||
.await?;
|
||||
};
|
||||
|
||||
let members = appservice
|
||||
.virtual_user(None)
|
||||
.await?
|
||||
.get_room(room_id)
|
||||
.expect("Expected room to be available")
|
||||
.members_no_sync()
|
||||
.await?;
|
||||
|
||||
assert_eq!(members[0].display_name().unwrap(), "changed");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_receive_transaction() -> Result<()> {
|
||||
tracing_subscriber::fmt().try_init().ok();
|
||||
let json = vec![
|
||||
Raw::new(&json!({
|
||||
"content": {
|
||||
"avatar_url": null,
|
||||
"displayname": "Appservice",
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$151800140479rdvjg:localhost",
|
||||
"membership": "join",
|
||||
"origin_server_ts": 151800140,
|
||||
"sender": "@_appservice:localhost",
|
||||
"state_key": "@_appservice:localhost",
|
||||
"type": "m.room.member",
|
||||
"room_id": "!coolplace:localhost",
|
||||
"unsigned": {
|
||||
"age": 2970366
|
||||
}
|
||||
}))?
|
||||
.cast::<AnyRoomEvent>(),
|
||||
Raw::new(&json!({
|
||||
"content": {
|
||||
"avatar_url": null,
|
||||
"displayname": "Appservice",
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$151800140491rfbja:localhost",
|
||||
"membership": "join",
|
||||
"origin_server_ts": 151800140,
|
||||
"sender": "@_appservice:localhost",
|
||||
"state_key": "@_appservice:localhost",
|
||||
"type": "m.room.member",
|
||||
"room_id": "!boringplace:localhost",
|
||||
"unsigned": {
|
||||
"age": 2970366
|
||||
}
|
||||
}))?
|
||||
.cast::<AnyRoomEvent>(),
|
||||
Raw::new(&json!({
|
||||
"content": {
|
||||
"avatar_url": null,
|
||||
"displayname": "Alice",
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$151800140517rfvjc:localhost",
|
||||
"membership": "join",
|
||||
"origin_server_ts": 151800140,
|
||||
"sender": "@_appservice_alice:localhost",
|
||||
"state_key": "@_appservice_alice:localhost",
|
||||
"type": "m.room.member",
|
||||
"room_id": "!coolplace:localhost",
|
||||
"unsigned": {
|
||||
"age": 2970366
|
||||
}
|
||||
}))?
|
||||
.cast::<AnyRoomEvent>(),
|
||||
Raw::new(&json!({
|
||||
"content": {
|
||||
"avatar_url": null,
|
||||
"displayname": "Bob",
|
||||
"membership": "invite"
|
||||
},
|
||||
"event_id": "$151800140594rfvjc:localhost",
|
||||
"membership": "invite",
|
||||
"origin_server_ts": 151800174,
|
||||
"sender": "@_appservice_bob:localhost",
|
||||
"state_key": "@_appservice_bob:localhost",
|
||||
"type": "m.room.member",
|
||||
"room_id": "!boringplace:localhost",
|
||||
"unsigned": {
|
||||
"age": 2970366
|
||||
}
|
||||
}))?
|
||||
.cast::<AnyRoomEvent>(),
|
||||
];
|
||||
let appservice = appservice(None, None).await?;
|
||||
|
||||
let alice = appservice.virtual_user(Some("_appservice_alice")).await?;
|
||||
let bob = appservice.virtual_user(Some("_appservice_bob")).await?;
|
||||
appservice
|
||||
.receive_transaction(push_events::v1::IncomingRequest::new("dontcare".into(), json))
|
||||
.await?;
|
||||
let coolplace = room_id!("!coolplace:localhost");
|
||||
let boringplace = room_id!("!boringplace:localhost");
|
||||
assert!(
|
||||
alice.get_joined_room(coolplace).is_some(),
|
||||
"Alice's membership in coolplace should be join"
|
||||
);
|
||||
assert!(
|
||||
bob.get_invited_room(boringplace).is_some(),
|
||||
"Bob's membership in boringplace should be invite"
|
||||
);
|
||||
assert!(alice.get_room(boringplace).is_none(), "Alice should not know about boringplace");
|
||||
assert!(bob.get_room(coolplace).is_none(), "Bob should not know about coolplace");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
mod registration {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_registration() -> Result<()> {
|
||||
let registration: Registration = serde_yaml::from_str(®istration_string())?;
|
||||
let registration: AppServiceRegistration = registration.into();
|
||||
|
||||
assert_eq!(registration.id, "appservice");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_registration_from_yaml_file() -> Result<()> {
|
||||
let registration = AppServiceRegistration::try_from_yaml_file("./tests/registration.yaml")?;
|
||||
|
||||
assert_eq!(registration.id, "appservice");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_registration_from_yaml_str() -> Result<()> {
|
||||
let registration = AppServiceRegistration::try_from_yaml_str(registration_string())?;
|
||||
|
||||
assert_eq!(registration.id, "appservice");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -43,10 +43,10 @@ tracing = "0.1.34"
|
||||
zeroize = { version = "1.3.0", features = ["zeroize_derive"] }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "7ec599e83d8ba6b2ace9e4fe184fc7560b0ff6ae", features = ["client-api-c", "js", "canonical-json"] }
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "ca8c66c885241a7ba3805399604eda4a38979f6b", features = ["client-api-c", "js", "canonical-json"] }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "7ec599e83d8ba6b2ace9e4fe184fc7560b0ff6ae", features = ["client-api-c", "canonical-json"] }
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "ca8c66c885241a7ba3805399604eda4a38979f6b", features = ["client-api-c", "canonical-json"] }
|
||||
|
||||
[dev-dependencies]
|
||||
futures = { version = "0.3.21", default-features = false, features = ["executor"] }
|
||||
|
||||
@@ -66,6 +66,7 @@ use crate::{
|
||||
store::{
|
||||
ambiguity_map::AmbiguityCache, Result as StoreResult, StateChanges, Store, StoreConfig,
|
||||
},
|
||||
StateStore,
|
||||
};
|
||||
|
||||
/// A no IO Client implementation.
|
||||
@@ -135,9 +136,20 @@ impl BaseClient {
|
||||
self.store.session()
|
||||
}
|
||||
|
||||
/// Get all the rooms this client knows about.
|
||||
pub fn get_rooms(&self) -> Vec<Room> {
|
||||
self.store.get_rooms()
|
||||
}
|
||||
|
||||
/// Get all the rooms this client knows about.
|
||||
pub fn get_stripped_rooms(&self) -> Vec<Room> {
|
||||
self.store.get_stripped_rooms()
|
||||
}
|
||||
|
||||
/// Get a reference to the store.
|
||||
pub fn store(&self) -> &Store {
|
||||
&self.store
|
||||
#[allow(unknown_lints, clippy::explicit_auto_deref)]
|
||||
pub fn store(&self) -> &dyn StateStore {
|
||||
&*self.store
|
||||
}
|
||||
|
||||
/// Is the client logged in.
|
||||
|
||||
@@ -43,7 +43,7 @@ pub use http;
|
||||
pub use matrix_sdk_crypto as crypto;
|
||||
pub use once_cell;
|
||||
pub use rooms::{DisplayName, Room, RoomInfo, RoomMember, RoomType};
|
||||
pub use store::{StateChanges, StateStore, Store, StoreError};
|
||||
pub use store::{StateChanges, StateStore, StoreError};
|
||||
pub use utils::{
|
||||
MinimalRoomMemberEvent, MinimalStateEvent, OriginalMinimalStateEvent, RedactedMinimalStateEvent,
|
||||
};
|
||||
|
||||
@@ -103,16 +103,7 @@ impl Room {
|
||||
room_id: &RoomId,
|
||||
room_type: RoomType,
|
||||
) -> Self {
|
||||
let room_info = RoomInfo {
|
||||
room_id: room_id.into(),
|
||||
room_type,
|
||||
notification_counts: Default::default(),
|
||||
summary: Default::default(),
|
||||
members_synced: false,
|
||||
last_prev_batch: None,
|
||||
base_info: BaseRoomInfo::new(),
|
||||
};
|
||||
|
||||
let room_info = RoomInfo::new(room_id, room_type);
|
||||
Self::restore(own_user_id, store, room_info)
|
||||
}
|
||||
|
||||
@@ -655,6 +646,19 @@ pub struct RoomInfo {
|
||||
}
|
||||
|
||||
impl RoomInfo {
|
||||
#[doc(hidden)] // used by store tests, otherwise it would be pub(crate)
|
||||
pub fn new(room_id: &RoomId, room_type: RoomType) -> Self {
|
||||
Self {
|
||||
room_id: room_id.into(),
|
||||
room_type,
|
||||
notification_counts: Default::default(),
|
||||
summary: Default::default(),
|
||||
members_synced: false,
|
||||
last_prev_batch: None,
|
||||
base_info: BaseRoomInfo::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Mark this Room as joined
|
||||
pub fn mark_as_joined(&mut self) {
|
||||
self.room_type = RoomType::Joined;
|
||||
|
||||
@@ -52,7 +52,7 @@ macro_rules! statestore_integration_tests {
|
||||
use matrix_sdk_test::{async_test, test_json};
|
||||
use ruma::{
|
||||
api::client::media::get_content_thumbnail::v3::Method,
|
||||
device_id, event_id,
|
||||
event_id,
|
||||
events::{
|
||||
presence::PresenceEvent,
|
||||
receipt::ReceiptType,
|
||||
@@ -83,14 +83,13 @@ macro_rules! statestore_integration_tests {
|
||||
deserialized_responses::{ RoomEvent, SyncRoomEvent, TimelineSlice},
|
||||
};
|
||||
use $crate::{
|
||||
RoomType, Session,
|
||||
media::{MediaFormat, MediaRequest, MediaThumbnailSize},
|
||||
store::{
|
||||
Store,
|
||||
StateStore,
|
||||
Result as StoreResult,
|
||||
StateChanges
|
||||
}
|
||||
},
|
||||
RoomInfo, RoomType,
|
||||
};
|
||||
|
||||
use super::get_store;
|
||||
@@ -115,22 +114,13 @@ macro_rules! statestore_integration_tests {
|
||||
}
|
||||
|
||||
/// Populate the given `StateStore`.
|
||||
pub(crate) async fn populated_store(inner: Arc<dyn StateStore>) -> StoreResult<Store> {
|
||||
pub async fn populate_store(store: Arc<dyn StateStore>) -> StoreResult<()> {
|
||||
let mut changes = StateChanges::default();
|
||||
let store = Store::new(inner);
|
||||
|
||||
let user_id = user_id();
|
||||
let invited_user_id = invited_user_id();
|
||||
let room_id = room_id();
|
||||
let stripped_room_id = stripped_room_id();
|
||||
let device_id = device_id!("device");
|
||||
|
||||
let session = Session {
|
||||
access_token: "token".to_owned(),
|
||||
user_id: user_id.to_owned(),
|
||||
device_id: device_id.to_owned(),
|
||||
};
|
||||
store.restore_session(session).await.unwrap();
|
||||
|
||||
changes.sync_token = Some("t392-516_47314_0_7_1_1_1_11444_1".to_owned());
|
||||
|
||||
@@ -147,7 +137,7 @@ macro_rules! statestore_integration_tests {
|
||||
let pushrules_event = pushrules_raw.deserialize().unwrap();
|
||||
changes.add_account_data(pushrules_event, pushrules_raw);
|
||||
|
||||
let mut room = store.get_or_create_room(room_id, RoomType::Joined).await.clone_info();
|
||||
let mut room = RoomInfo::new(room_id, RoomType::Joined);
|
||||
room.mark_as_left();
|
||||
|
||||
let tag_json: &JsonValue = &test_json::TAG;
|
||||
@@ -220,8 +210,7 @@ macro_rules! statestore_integration_tests {
|
||||
changes.members.insert(room_id.to_owned(), room_members);
|
||||
changes.add_room(room);
|
||||
|
||||
let mut stripped_room =
|
||||
store.get_or_create_stripped_room(stripped_room_id).await.clone_info();
|
||||
let mut stripped_room = RoomInfo::new(stripped_room_id, RoomType::Invited);
|
||||
|
||||
let stripped_name_json: &JsonValue = &test_json::NAME_STRIPPED;
|
||||
let stripped_name_raw =
|
||||
@@ -249,7 +238,7 @@ macro_rules! statestore_integration_tests {
|
||||
changes.add_stripped_member(stripped_room_id, stripped_member_event);
|
||||
|
||||
store.save_changes(&changes).await?;
|
||||
Ok(store)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn power_level_event() -> Raw<AnySyncStateEvent> {
|
||||
@@ -305,11 +294,12 @@ macro_rules! statestore_integration_tests {
|
||||
let user_id = user_id();
|
||||
let inner_store = get_store().await?;
|
||||
|
||||
let store = populated_store(Arc::new(inner_store)).await?;
|
||||
let store = Arc::new(inner_store);
|
||||
populate_store(store.clone()).await?;
|
||||
|
||||
assert!(store.get_sync_token().await?.is_some());
|
||||
assert!(store.get_presence_event(user_id).await?.is_some());
|
||||
assert_eq!(store.get_room_infos().await?.len(), 1, "Expected to find 1 room info ");
|
||||
assert_eq!(store.get_room_infos().await?.len(), 1, "Expected to find 1 room info");
|
||||
assert_eq!(store.get_stripped_room_infos().await?.len(), 1, "Expected to find 1 stripped room info");
|
||||
assert!(store.get_account_data_event(GlobalAccountDataEventType::PushRules).await?.is_some());
|
||||
|
||||
@@ -580,25 +570,24 @@ macro_rules! statestore_integration_tests {
|
||||
|
||||
#[async_test]
|
||||
async fn test_persist_invited_room() -> StoreResult<()> {
|
||||
let stripped_room_id = stripped_room_id();
|
||||
let inner_store = get_store().await?;
|
||||
let store = populated_store(Arc::new(inner_store)).await?;
|
||||
let store = Arc::new(inner_store);
|
||||
populate_store(store.clone()).await?;
|
||||
|
||||
assert_eq!(store.get_stripped_room_infos().await?.len(), 1);
|
||||
assert!(store.get_stripped_room(stripped_room_id).is_some());
|
||||
|
||||
// populate rooom
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_room_removal() -> StoreResult<()> {
|
||||
async fn test_room_removal() -> StoreResult<()> {
|
||||
let room_id = room_id();
|
||||
let user_id = user_id();
|
||||
let inner_store = get_store().await?;
|
||||
let stripped_room_id = stripped_room_id();
|
||||
|
||||
let store = populated_store(Arc::new(inner_store)).await?;
|
||||
let store = Arc::new(inner_store);
|
||||
populate_store(store.clone()).await?;
|
||||
|
||||
store.remove_room(room_id).await?;
|
||||
|
||||
|
||||
@@ -407,7 +407,7 @@ where
|
||||
/// This adds additional higher level store functionality on top of a
|
||||
/// `StateStore` implementation.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Store {
|
||||
pub(crate) struct Store {
|
||||
pub(super) inner: Arc<dyn StateStore>,
|
||||
session: Arc<OnceCell<Session>>,
|
||||
/// The current sync token that should be used for the next sync call.
|
||||
|
||||
@@ -16,7 +16,7 @@ default-target = "x86_64-unknown-linux-gnu"
|
||||
targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"]
|
||||
|
||||
[dependencies]
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "7ec599e83d8ba6b2ace9e4fe184fc7560b0ff6ae", features = ["client-api-c"] }
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "ca8c66c885241a7ba3805399604eda4a38979f6b", features = ["client-api-c"] }
|
||||
serde = "1.0.136"
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
|
||||
@@ -51,11 +51,11 @@ zeroize = { version = "1.3.0", features = ["zeroize_derive"] }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
tokio = { version = "1.18", default-features = false, features = ["time"] }
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "7ec599e83d8ba6b2ace9e4fe184fc7560b0ff6ae", features = ["client-api-c", "rand", "canonical-json", "unstable-msc2676", "unstable-msc2677"] }
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "ca8c66c885241a7ba3805399604eda4a38979f6b", features = ["client-api-c", "rand", "canonical-json", "unstable-msc2676", "unstable-msc2677"] }
|
||||
vodozemac = { git = "https://github.com/matrix-org/vodozemac/", rev = "2404f83f7d3a3779c1f518e4d949f7da9677c3dd" }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "7ec599e83d8ba6b2ace9e4fe184fc7560b0ff6ae", features = ["client-api-c", "js", "rand", "canonical-json", "unstable-msc2676", "unstable-msc2677"] }
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "ca8c66c885241a7ba3805399604eda4a38979f6b", features = ["client-api-c", "js", "rand", "canonical-json", "unstable-msc2676", "unstable-msc2677"] }
|
||||
vodozemac = { git = "https://github.com/matrix-org/vodozemac/", rev = "2404f83f7d3a3779c1f518e4d949f7da9677c3dd", features = ["js"] }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -1098,12 +1098,10 @@ impl RequestState<Ready> {
|
||||
// before the other side tried to do the same; ignore it if we did and
|
||||
// we're the lexicographically smaller user ID (or device ID if equal).
|
||||
use std::cmp::Ordering;
|
||||
match (sender.cmp(own_user_id), device.device_id().cmp(own_device_id)) {
|
||||
(Ordering::Greater, _) | (Ordering::Equal, Ordering::Greater) => {
|
||||
false
|
||||
}
|
||||
_ => true,
|
||||
}
|
||||
!matches!(
|
||||
(sender.cmp(own_user_id), device.device_id().cmp(own_device_id)),
|
||||
(Ordering::Greater, _) | (Ordering::Equal, Ordering::Greater)
|
||||
)
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
@@ -29,7 +29,7 @@ indexed_db_futures = "0.2.3"
|
||||
matrix-sdk-base = { version = "0.5.0", path = "../matrix-sdk-base" }
|
||||
matrix-sdk-crypto = { version = "0.5.0", path = "../matrix-sdk-crypto", optional = true }
|
||||
matrix-sdk-store-encryption = { version = "0.1.0", path = "../matrix-sdk-store-encryption" }
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "7ec599e83d8ba6b2ace9e4fe184fc7560b0ff6ae" }
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "ca8c66c885241a7ba3805399604eda4a38979f6b" }
|
||||
serde = "1.0.136"
|
||||
serde_json = "1.0.79"
|
||||
thiserror = "1.0.30"
|
||||
|
||||
@@ -25,7 +25,7 @@ byteorder = "1.4.3"
|
||||
image = { version = "0.23.0", optional = true }
|
||||
qrcode = { version = "0.12.0", default-features = false }
|
||||
rqrr = { version = "0.4.0", optional = true }
|
||||
ruma-common = { git = "https://github.com/ruma/ruma", rev = "7ec599e83d8ba6b2ace9e4fe184fc7560b0ff6ae" }
|
||||
ruma-common = { git = "https://github.com/ruma/ruma", rev = "ca8c66c885241a7ba3805399604eda4a38979f6b" }
|
||||
thiserror = "1.0.30"
|
||||
|
||||
[dependencies.vodozemac]
|
||||
|
||||
@@ -37,7 +37,7 @@ matrix-sdk-base = { version = "0.5.0", path = "../matrix-sdk-base", optional = t
|
||||
matrix-sdk-common = { version = "0.5.0", path = "../matrix-sdk-common" }
|
||||
matrix-sdk-crypto = { version = "0.5.0", path = "../matrix-sdk-crypto", optional = true }
|
||||
matrix-sdk-store-encryption = { version = "0.1.0", path = "../matrix-sdk-store-encryption" }
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "7ec599e83d8ba6b2ace9e4fe184fc7560b0ff6ae" }
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "ca8c66c885241a7ba3805399604eda4a38979f6b" }
|
||||
serde = "1.0.136"
|
||||
serde_json = "1.0.79"
|
||||
sled = "0.34.7"
|
||||
|
||||
@@ -110,13 +110,8 @@ default_features = false
|
||||
|
||||
[dependencies.ruma]
|
||||
git = "https://github.com/ruma/ruma"
|
||||
rev = "7ec599e83d8ba6b2ace9e4fe184fc7560b0ff6ae"
|
||||
features = ["client-api-c", "compat", "rand", "unstable-msc2448"]
|
||||
|
||||
[dependencies.ruma-client-api]
|
||||
git = "https://github.com/ruma/ruma"
|
||||
rev = "7ec599e83d8ba6b2ace9e4fe184fc7560b0ff6ae"
|
||||
features = ["unstable-msc2965"]
|
||||
rev = "ca8c66c885241a7ba3805399604eda4a38979f6b"
|
||||
features = ["client-api-c", "compat", "rand", "unstable-msc2448", "unstable-msc2965"]
|
||||
|
||||
[dependencies.tokio-stream]
|
||||
version = "0.1.8"
|
||||
|
||||
@@ -30,7 +30,7 @@ use futures_core::stream::Stream;
|
||||
use matrix_sdk_base::{
|
||||
deserialized_responses::SyncResponse,
|
||||
media::{MediaEventContent, MediaFormat, MediaRequest, MediaThumbnailSize},
|
||||
BaseClient, Session, Store,
|
||||
BaseClient, Session, StateStore,
|
||||
};
|
||||
use matrix_sdk_common::{
|
||||
instant::{Duration, Instant},
|
||||
@@ -320,12 +320,12 @@ impl Client {
|
||||
/// Can be used with [`Client::restore_login`] to restore a previously
|
||||
/// logged-in session.
|
||||
pub fn session(&self) -> Option<&Session> {
|
||||
self.store().session()
|
||||
self.base_client().session()
|
||||
}
|
||||
|
||||
/// Get a reference to the store.
|
||||
pub fn store(&self) -> &Store {
|
||||
self.inner.base_client.store()
|
||||
/// Get a reference to the state store.
|
||||
pub fn store(&self) -> &dyn StateStore {
|
||||
self.base_client().store()
|
||||
}
|
||||
|
||||
/// Get the account of the current owner of the client.
|
||||
@@ -564,7 +564,7 @@ impl Client {
|
||||
///
|
||||
/// This will return the list of joined, invited, and left rooms.
|
||||
pub fn rooms(&self) -> Vec<room::Room> {
|
||||
self.store()
|
||||
self.base_client()
|
||||
.get_rooms()
|
||||
.into_iter()
|
||||
.map(|room| room::Common::new(self.clone(), room).into())
|
||||
@@ -573,7 +573,7 @@ impl Client {
|
||||
|
||||
/// Returns the joined rooms this client knows about.
|
||||
pub fn joined_rooms(&self) -> Vec<room::Joined> {
|
||||
self.store()
|
||||
self.base_client()
|
||||
.get_rooms()
|
||||
.into_iter()
|
||||
.filter_map(|room| room::Joined::new(self.clone(), room))
|
||||
@@ -582,7 +582,7 @@ impl Client {
|
||||
|
||||
/// Returns the invited rooms this client knows about.
|
||||
pub fn invited_rooms(&self) -> Vec<room::Invited> {
|
||||
self.store()
|
||||
self.base_client()
|
||||
.get_stripped_rooms()
|
||||
.into_iter()
|
||||
.filter_map(|room| room::Invited::new(self.clone(), room))
|
||||
@@ -591,7 +591,7 @@ impl Client {
|
||||
|
||||
/// Returns the left rooms this client knows about.
|
||||
pub fn left_rooms(&self) -> Vec<room::Left> {
|
||||
self.store()
|
||||
self.base_client()
|
||||
.get_rooms()
|
||||
.into_iter()
|
||||
.filter_map(|room| room::Left::new(self.clone(), room))
|
||||
@@ -604,7 +604,9 @@ impl Client {
|
||||
///
|
||||
/// `room_id` - The unique id of the room that should be fetched.
|
||||
pub fn get_room(&self, room_id: &RoomId) -> Option<room::Room> {
|
||||
self.store().get_room(room_id).map(|room| room::Common::new(self.clone(), room).into())
|
||||
self.base_client()
|
||||
.get_room(room_id)
|
||||
.map(|room| room::Common::new(self.clone(), room).into())
|
||||
}
|
||||
|
||||
/// Get a joined room with the given room id.
|
||||
@@ -613,7 +615,7 @@ impl Client {
|
||||
///
|
||||
/// `room_id` - The unique id of the room that should be fetched.
|
||||
pub fn get_joined_room(&self, room_id: &RoomId) -> Option<room::Joined> {
|
||||
self.store().get_room(room_id).and_then(|room| room::Joined::new(self.clone(), room))
|
||||
self.base_client().get_room(room_id).and_then(|room| room::Joined::new(self.clone(), room))
|
||||
}
|
||||
|
||||
/// Get an invited room with the given room id.
|
||||
@@ -622,7 +624,7 @@ impl Client {
|
||||
///
|
||||
/// `room_id` - The unique id of the room that should be fetched.
|
||||
pub fn get_invited_room(&self, room_id: &RoomId) -> Option<room::Invited> {
|
||||
self.store().get_room(room_id).and_then(|room| room::Invited::new(self.clone(), room))
|
||||
self.base_client().get_room(room_id).and_then(|room| room::Invited::new(self.clone(), room))
|
||||
}
|
||||
|
||||
/// Get a left room with the given room id.
|
||||
@@ -631,7 +633,7 @@ impl Client {
|
||||
///
|
||||
/// `room_id` - The unique id of the room that should be fetched.
|
||||
pub fn get_left_room(&self, room_id: &RoomId) -> Option<room::Left> {
|
||||
self.store().get_room(room_id).and_then(|room| room::Left::new(self.clone(), room))
|
||||
self.base_client().get_room(room_id).and_then(|room| room::Left::new(self.clone(), room))
|
||||
}
|
||||
|
||||
/// Resolve a room alias to a room id and a list of servers which know
|
||||
|
||||
@@ -176,13 +176,13 @@ impl Common {
|
||||
/// # let homeserver = Url::parse("http://example.com").unwrap();
|
||||
/// # use futures::executor::block_on;
|
||||
/// # block_on(async {
|
||||
/// let request = MessagesOptions::backward("t47429-4392820_219380_26003_2265");
|
||||
/// let options = MessagesOptions::backward().from("t47429-4392820_219380_26003_2265");
|
||||
///
|
||||
/// let mut client = Client::new(homeserver).await.unwrap();
|
||||
/// let room = client
|
||||
/// .get_joined_room(room_id!("!roomid:example.com"))
|
||||
/// .unwrap();
|
||||
/// assert!(room.messages(request).await.is_ok());
|
||||
/// assert!(room.messages(options).await.is_ok());
|
||||
/// # });
|
||||
/// ```
|
||||
pub async fn messages(&self, options: MessagesOptions<'_>) -> Result<Messages> {
|
||||
@@ -264,7 +264,7 @@ impl Common {
|
||||
/// # Examples
|
||||
/// ```no_run
|
||||
/// # use std::convert::TryFrom;
|
||||
/// use matrix_sdk::{room::MessagesOptions, Client};
|
||||
/// use matrix_sdk::Client;
|
||||
/// # use matrix_sdk::ruma::{
|
||||
/// # api::client::filter::RoomEventFilter,
|
||||
/// # room_id,
|
||||
@@ -344,7 +344,7 @@ impl Common {
|
||||
/// # Examples
|
||||
/// ```no_run
|
||||
/// # use std::convert::TryFrom;
|
||||
/// use matrix_sdk::{room::MessagesOptions, Client};
|
||||
/// use matrix_sdk::Client;
|
||||
/// # use matrix_sdk::ruma::{
|
||||
/// # api::client::filter::RoomEventFilter,
|
||||
/// # room_id,
|
||||
@@ -403,7 +403,7 @@ impl Common {
|
||||
/// # Examples
|
||||
/// ```no_run
|
||||
/// # use std::convert::TryFrom;
|
||||
/// use matrix_sdk::{room::MessagesOptions, Client};
|
||||
/// use matrix_sdk::Client;
|
||||
/// # use matrix_sdk::ruma::{
|
||||
/// # api::client::filter::RoomEventFilter,
|
||||
/// # room_id,
|
||||
@@ -461,7 +461,8 @@ impl Common {
|
||||
let filter = assign!(RoomEventFilter::default(), {
|
||||
lazy_load_options: LazyLoadOptions::Enabled { include_redundant_members: false },
|
||||
});
|
||||
let options = assign!(MessagesOptions::backward(token), {
|
||||
let options = assign!(MessagesOptions::backward(), {
|
||||
from: Some(token),
|
||||
limit: uint!(10),
|
||||
filter,
|
||||
});
|
||||
@@ -1074,7 +1075,7 @@ impl Common {
|
||||
|
||||
/// Options for [`messages`][Common::messages].
|
||||
///
|
||||
/// See that method for details.
|
||||
/// See that method and <https://spec.matrix.org/v1.3/client-server-api/#get_matrixclientv3roomsroomidmessages> for details.
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub struct MessagesOptions<'a> {
|
||||
@@ -1083,7 +1084,11 @@ pub struct MessagesOptions<'a> {
|
||||
/// This token can be obtained from a `prev_batch` token returned for each
|
||||
/// room from the sync API, or from a start or end token returned by a
|
||||
/// previous `messages` call.
|
||||
pub from: &'a str,
|
||||
///
|
||||
/// If `from` isn't provided the homeserver shall return a list of messages
|
||||
/// from the first or last (per the value of the dir parameter) visible
|
||||
/// event in the room history for the requesting user.
|
||||
pub from: Option<&'a str>,
|
||||
|
||||
/// The token to stop returning events at.
|
||||
///
|
||||
@@ -1105,27 +1110,42 @@ pub struct MessagesOptions<'a> {
|
||||
}
|
||||
|
||||
impl<'a> MessagesOptions<'a> {
|
||||
/// Creates `MessagesOptions` with the given start token and direction.
|
||||
/// Creates `MessagesOptions` with the given direction.
|
||||
///
|
||||
/// All other parameters will be defaulted.
|
||||
pub fn new(from: &'a str, dir: Direction) -> Self {
|
||||
Self { from, to: None, dir, limit: uint!(10), filter: RoomEventFilter::default() }
|
||||
pub fn new(dir: Direction) -> Self {
|
||||
Self { from: None, to: None, dir, limit: uint!(10), filter: RoomEventFilter::default() }
|
||||
}
|
||||
|
||||
/// Creates `MessagesOptions` with the given start token, and `dir` set to
|
||||
/// `Backward`.
|
||||
pub fn backward(from: &'a str) -> Self {
|
||||
Self::new(from, Direction::Backward)
|
||||
/// Creates `MessagesOptions` with `dir` set to `Backward`.
|
||||
///
|
||||
/// If no `from` token is set afterwards, pagination will start at the
|
||||
/// end of (the accessible part of) the room timeline.
|
||||
pub fn backward() -> Self {
|
||||
Self::new(Direction::Backward)
|
||||
}
|
||||
|
||||
/// Creates `MessagesOptions` with the given start token, and `dir` set to
|
||||
/// `Forward`.
|
||||
pub fn forward(from: &'a str) -> Self {
|
||||
Self::new(from, Direction::Forward)
|
||||
/// Creates `MessagesOptions` with `dir` set to `Forward`.
|
||||
///
|
||||
/// If no `from` token is set afterwards, pagination will start at the
|
||||
/// beginning of (the accessible part of) the room timeline.
|
||||
pub fn forward() -> Self {
|
||||
Self::new(Direction::Forward)
|
||||
}
|
||||
|
||||
/// Creates a new `MessagesOptions` from `self` with the `from` field set to
|
||||
/// the given value.
|
||||
///
|
||||
/// Since the field is public, you can also assign to it directly. This
|
||||
/// method merely acts as a shorthand for that, because it is very
|
||||
/// common to set this field.
|
||||
pub fn from(self, from: impl Into<Option<&'a str>>) -> Self {
|
||||
Self { from: from.into(), ..self }
|
||||
}
|
||||
|
||||
fn into_request(self, room_id: &'a RoomId) -> get_message_events::v3::Request<'_> {
|
||||
assign!(get_message_events::v3::Request::new(room_id, Some(self.from), self.dir), {
|
||||
assign!(get_message_events::v3::Request::new(room_id, self.dir), {
|
||||
from: self.from,
|
||||
to: self.to,
|
||||
limit: self.limit,
|
||||
filter: self.filter,
|
||||
|
||||
@@ -20,7 +20,7 @@ use ruma::{
|
||||
},
|
||||
message::send_message_event,
|
||||
read_marker::set_read_marker,
|
||||
receipt::create_receipt,
|
||||
receipt::create_receipt::{self, v3::ReceiptType},
|
||||
redact::redact_event,
|
||||
state::send_state_event,
|
||||
typing::create_typing_event::v3::{Request as TypingRequest, Typing},
|
||||
@@ -30,7 +30,6 @@ use ruma::{
|
||||
serde::Raw,
|
||||
EventId, OwnedTransactionId, TransactionId, UserId,
|
||||
};
|
||||
use ruma_client_api::receipt::create_receipt::v3::ReceiptType;
|
||||
use serde_json::Value;
|
||||
use tracing::debug;
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
|
||||
@@ -22,7 +22,7 @@ impl Client {
|
||||
rooms,
|
||||
presence,
|
||||
account_data,
|
||||
to_device: _,
|
||||
to_device,
|
||||
device_lists: _,
|
||||
device_one_time_keys_count: _,
|
||||
ambiguity_changes: _,
|
||||
@@ -31,6 +31,7 @@ impl Client {
|
||||
|
||||
self.handle_sync_events(EventKind::GlobalAccountData, &None, &account_data.events).await?;
|
||||
self.handle_sync_events(EventKind::Presence, &None, &presence.events).await?;
|
||||
self.handle_sync_events(EventKind::ToDevice, &None, &to_device.events).await?;
|
||||
|
||||
for (room_id, room_info) in &rooms.join {
|
||||
let room = self.get_room(room_id);
|
||||
|
||||
@@ -10,7 +10,7 @@ clap = "3.2.4"
|
||||
futures = { version = "0.3.21", default-features = false, features = ["executor"] }
|
||||
matrix-sdk-base = { path = "../../crates/matrix-sdk-base", version = "0.5.0" }
|
||||
matrix-sdk-sled = { path = "../../crates/matrix-sdk-sled", version = "0.1.0" }
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "7ec599e83d8ba6b2ace9e4fe184fc7560b0ff6ae" }
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "ca8c66c885241a7ba3805399604eda4a38979f6b" }
|
||||
rustyline = "9.1.2"
|
||||
rustyline-derive = "0.6.0"
|
||||
serde = "1.0.136"
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::{convert::TryFrom, fmt::Debug, sync::Arc};
|
||||
use atty::Stream;
|
||||
use clap::{Arg, ArgMatches, Command as Argparse};
|
||||
use futures::executor::block_on;
|
||||
use matrix_sdk_base::{RoomInfo, Store};
|
||||
use matrix_sdk_base::RoomInfo;
|
||||
use matrix_sdk_sled::StateStore;
|
||||
use ruma::{events::StateEventType, OwnedRoomId, OwnedUserId, RoomId};
|
||||
use rustyline::{
|
||||
@@ -26,13 +26,13 @@ use syntect::{
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Inspector {
|
||||
store: Store,
|
||||
store: Arc<StateStore>,
|
||||
printer: Printer,
|
||||
}
|
||||
|
||||
#[derive(Helper)]
|
||||
struct InspectorHelper {
|
||||
store: Store,
|
||||
store: Arc<StateStore>,
|
||||
_highlighter: MatchingBracketHighlighter,
|
||||
_validator: MatchingBracketValidator,
|
||||
_hinter: HistoryHinter,
|
||||
@@ -54,7 +54,7 @@ impl InspectorHelper {
|
||||
"m.room.topic",
|
||||
];
|
||||
|
||||
fn new(store: Store) -> Self {
|
||||
fn new(store: Arc<StateStore>) -> Self {
|
||||
Self {
|
||||
store,
|
||||
_highlighter: MatchingBracketHighlighter::new(),
|
||||
@@ -72,7 +72,9 @@ impl InspectorHelper {
|
||||
}
|
||||
|
||||
fn complete_rooms(&self, arg: Option<&&str>) -> Vec<Pair> {
|
||||
let rooms: Vec<RoomInfo> = block_on(async { self.store.get_room_infos().await.unwrap() });
|
||||
let rooms: Vec<RoomInfo> = block_on(async {
|
||||
matrix_sdk_base::StateStore::get_room_infos(&*self.store).await.unwrap()
|
||||
});
|
||||
|
||||
rooms
|
||||
.into_iter()
|
||||
@@ -200,9 +202,8 @@ impl Printer {
|
||||
impl Inspector {
|
||||
fn new(database_path: &str, json: bool, color: bool) -> Self {
|
||||
let printer = Printer::new(json, color);
|
||||
let store = Store::new(Arc::new(
|
||||
StateStore::open_with_path(database_path).expect("Can't open sled database"),
|
||||
));
|
||||
let store =
|
||||
Arc::new(StateStore::open_with_path(database_path).expect("Can't open sled database"));
|
||||
|
||||
Self { store, printer }
|
||||
}
|
||||
@@ -237,7 +238,8 @@ impl Inspector {
|
||||
}
|
||||
|
||||
async fn list_rooms(&self) {
|
||||
let rooms: Vec<RoomInfo> = self.store.get_room_infos().await.unwrap();
|
||||
let rooms: Vec<RoomInfo> =
|
||||
matrix_sdk_base::StateStore::get_room_infos(&*self.store).await.unwrap();
|
||||
self.printer.pretty_print_struct(&rooms);
|
||||
}
|
||||
|
||||
@@ -247,7 +249,8 @@ impl Inspector {
|
||||
}
|
||||
|
||||
async fn get_profiles(&self, room_id: OwnedRoomId) {
|
||||
let joined: Vec<OwnedUserId> = self.store.get_joined_user_ids(&room_id).await.unwrap();
|
||||
let joined: Vec<OwnedUserId> =
|
||||
matrix_sdk_base::StateStore::get_joined_user_ids(&*self.store, &room_id).await.unwrap();
|
||||
|
||||
for member in joined {
|
||||
let event = self.store.get_profile(&room_id, &member).await.unwrap();
|
||||
@@ -256,7 +259,8 @@ impl Inspector {
|
||||
}
|
||||
|
||||
async fn get_members(&self, room_id: OwnedRoomId) {
|
||||
let joined: Vec<OwnedUserId> = self.store.get_joined_user_ids(&room_id).await.unwrap();
|
||||
let joined: Vec<OwnedUserId> =
|
||||
matrix_sdk_base::StateStore::get_joined_user_ids(&*self.store, &room_id).await.unwrap();
|
||||
|
||||
for member in joined {
|
||||
let event = self.store.get_member_event(&room_id, &member).await.unwrap();
|
||||
|
||||
@@ -19,6 +19,6 @@ appservice = []
|
||||
http = "0.2.6"
|
||||
matrix-sdk-test-macros = { version = "0.2.0", path = "../matrix-sdk-test-macros" }
|
||||
once_cell = "1.10.0"
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "7ec599e83d8ba6b2ace9e4fe184fc7560b0ff6ae", features = ["client-api-c"] }
|
||||
ruma = { git = "https://github.com/ruma/ruma", rev = "ca8c66c885241a7ba3805399604eda4a38979f6b", features = ["client-api-c"] }
|
||||
serde = "1.0.136"
|
||||
serde_json = "1.0.79"
|
||||
|
||||
Reference in New Issue
Block a user