mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-05-15 19:45:34 -04:00
sdk: Allow to update notification settings (#2135)
* sdk: Allow to update notification settings * Add an event listener for push rule events * Fix: simplify insertion and deletion of rules * Fix: Limit rules cloning * Fix: Unit tests * Fix: set_room_notification_mode * Fix: potential race condition when updating the local ruleset * Refactor RuleCommands * RuleCommands Unit Tests * Fix: limit lock usage * nit: use expression assignment by default * nit: pass Ruleset by ownership in RuleCommands' ctor * a few nits: use to_owned() for &str -> String; use free function for notify actions; use clone() where it's more obvious and use explicit variants so we know we have to consider this match if we were to add a new variant later * nit: rename RuleCommands::set_enabled to set_enabled_internal * nit: test modules don't need to be publicized * tidy tests * nit: pass an owned RuleCommands in Rules::apply * add comments/questions in Rules tests * nit: no need to publicize the tests module * tweak NotificationSettings tests too * rustfmt --------- Co-authored-by: Benjamin Bouvier <public@benj.me>
This commit is contained in:
@@ -62,8 +62,10 @@ use ruma::{
|
||||
error::FromHttpResponseError,
|
||||
MatrixVersion, OutgoingRequest,
|
||||
},
|
||||
assign, DeviceId, OwnedDeviceId, OwnedRoomId, OwnedServerName, RoomAliasId, RoomId,
|
||||
RoomOrAliasId, ServerName, UInt, UserId,
|
||||
assign,
|
||||
push::Ruleset,
|
||||
DeviceId, OwnedDeviceId, OwnedRoomId, OwnedServerName, RoomAliasId, RoomId, RoomOrAliasId,
|
||||
ServerName, UInt, UserId,
|
||||
};
|
||||
use serde::de::DeserializeOwned;
|
||||
use tokio::sync::{broadcast, Mutex, OnceCell, RwLock, RwLockReadGuard};
|
||||
@@ -81,6 +83,7 @@ use crate::{
|
||||
},
|
||||
http_client::HttpClient,
|
||||
matrix_auth::MatrixAuth,
|
||||
notification_settings::NotificationSettings,
|
||||
room,
|
||||
sync::{RoomUpdate, SyncResponse},
|
||||
Account, AuthApi, AuthSession, Error, Media, RefreshTokenError, Result, TransmissionProgress,
|
||||
@@ -1926,6 +1929,12 @@ impl Client {
|
||||
let request = get_profile::v3::Request::new(user_id.to_owned());
|
||||
Ok(self.send(request, Some(RequestConfig::short_retry())).await?)
|
||||
}
|
||||
|
||||
/// Get the notification settings of the current owner of the client.
|
||||
pub async fn notification_settings(&self) -> NotificationSettings {
|
||||
let ruleset = self.account().push_rules().await.unwrap_or_else(|_| Ruleset::new());
|
||||
NotificationSettings::new(self.clone(), ruleset)
|
||||
}
|
||||
}
|
||||
|
||||
// The http mocking library is not supported for wasm32
|
||||
|
||||
@@ -30,7 +30,7 @@ use ruma::{
|
||||
error::{FromHttpResponseError, IntoHttpError},
|
||||
},
|
||||
events::tag::InvalidUserTagName,
|
||||
push::{InsertPushRuleError, RuleNotFoundError},
|
||||
push::{InsertPushRuleError, RemovePushRuleError, RuleNotFoundError},
|
||||
IdParseError,
|
||||
};
|
||||
use serde_json::Error as JsonError;
|
||||
@@ -430,12 +430,21 @@ pub enum NotificationSettingsError {
|
||||
/// Invalid parameter.
|
||||
#[error("Invalid parameter `{0}`")]
|
||||
InvalidParameter(String),
|
||||
/// Rule not found
|
||||
#[error("Rule not found")]
|
||||
RuleNotFound,
|
||||
/// Unable to add push rule.
|
||||
#[error("Unable to add push rule")]
|
||||
UnableToAddPushRule,
|
||||
/// Unable to remove push rule.
|
||||
#[error("Unable to remove push rule")]
|
||||
UnableToRemovePushRule,
|
||||
/// Unable to update push rule.
|
||||
#[error("Unable to update push rule")]
|
||||
UnableToUpdatePushRule,
|
||||
/// Rule not found
|
||||
#[error("Rule not found")]
|
||||
RuleNotFound,
|
||||
/// Unable to save the push rules
|
||||
#[error("Unable to save push rules")]
|
||||
UnableToSavePushRules,
|
||||
}
|
||||
|
||||
impl From<InsertPushRuleError> for NotificationSettingsError {
|
||||
@@ -444,6 +453,12 @@ impl From<InsertPushRuleError> for NotificationSettingsError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RemovePushRuleError> for NotificationSettingsError {
|
||||
fn from(_: RemovePushRuleError) -> Self {
|
||||
Self::UnableToRemovePushRule
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RuleNotFoundError> for NotificationSettingsError {
|
||||
fn from(_: RuleNotFoundError) -> Self {
|
||||
Self::RuleNotFound
|
||||
|
||||
@@ -56,7 +56,10 @@ pub use authentication::{AuthApi, AuthSession};
|
||||
pub use client::{Client, ClientBuildError, ClientBuilder, LoopCtrl, SendRequest, UnknownToken};
|
||||
#[cfg(feature = "image-proc")]
|
||||
pub use error::ImageError;
|
||||
pub use error::{Error, HttpError, HttpResult, RefreshTokenError, Result, RumaApiError};
|
||||
pub use error::{
|
||||
Error, HttpError, HttpResult, NotificationSettingsError, RefreshTokenError, Result,
|
||||
RumaApiError,
|
||||
};
|
||||
pub use http_client::TransmissionProgress;
|
||||
pub use media::Media;
|
||||
pub use ruma::{IdParseError, OwnedServerName, ServerName};
|
||||
|
||||
65
crates/matrix-sdk/src/notification_settings/command.rs
Normal file
65
crates/matrix-sdk/src/notification_settings/command.rs
Normal file
@@ -0,0 +1,65 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use ruma::{
|
||||
api::client::push::RuleScope,
|
||||
push::{
|
||||
Action, NewConditionalPushRule, NewPushRule, NewSimplePushRule, PushCondition, RuleKind,
|
||||
Tweak,
|
||||
},
|
||||
OwnedRoomId,
|
||||
};
|
||||
|
||||
use crate::NotificationSettingsError;
|
||||
|
||||
/// Enum describing the commands required to modify the owner's account data.
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) enum Command {
|
||||
/// Set a new `Room` push rule
|
||||
SetRoomPushRule { scope: RuleScope, room_id: OwnedRoomId, notify: bool },
|
||||
/// Set a new `Override` push rule matching a `RoomId`
|
||||
SetOverridePushRule { scope: RuleScope, rule_id: String, room_id: OwnedRoomId, notify: bool },
|
||||
/// Set whether a push rule is enabled
|
||||
SetPushRuleEnabled { scope: RuleScope, kind: RuleKind, rule_id: String, enabled: bool },
|
||||
/// Delete a push rule
|
||||
DeletePushRule { scope: RuleScope, kind: RuleKind, rule_id: String },
|
||||
}
|
||||
|
||||
fn get_notify_actions(notify: bool) -> Vec<Action> {
|
||||
if notify {
|
||||
vec![Action::Notify, Action::SetTweak(Tweak::Sound("default".into()))]
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
impl Command {
|
||||
/// Tries to create a push rule corresponding to this command
|
||||
pub(crate) fn to_push_rule(&self) -> Result<NewPushRule, NotificationSettingsError> {
|
||||
match self {
|
||||
Self::SetRoomPushRule { scope: _, room_id, notify } => {
|
||||
// `Room` push rule for this `room_id`
|
||||
let new_rule = NewSimplePushRule::new(room_id.clone(), get_notify_actions(*notify));
|
||||
Ok(NewPushRule::Room(new_rule))
|
||||
}
|
||||
|
||||
Self::SetOverridePushRule { scope: _, rule_id, room_id, notify } => {
|
||||
// `Override` push rule matching this `room_id`
|
||||
let new_rule = NewConditionalPushRule::new(
|
||||
rule_id.clone(),
|
||||
vec![PushCondition::EventMatch {
|
||||
key: "room_id".to_owned(),
|
||||
pattern: room_id.to_string(),
|
||||
}],
|
||||
get_notify_actions(*notify),
|
||||
);
|
||||
Ok(NewPushRule::Override(new_rule))
|
||||
}
|
||||
|
||||
Self::SetPushRuleEnabled { .. } | Self::DeletePushRule { .. } => {
|
||||
Err(NotificationSettingsError::InvalidParameter(
|
||||
"cannot create a push rule from this command.".to_owned(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,23 @@
|
||||
//! High-level push notification settings API
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use ruma::{
|
||||
api::client::push::{delete_pushrule, set_pushrule, set_pushrule_enabled},
|
||||
events::push_rules::PushRulesEvent,
|
||||
push::{RuleKind, Ruleset},
|
||||
RoomId,
|
||||
};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use self::{command::Command, rule_commands::RuleCommands, rules::Rules};
|
||||
|
||||
mod command;
|
||||
mod rule_commands;
|
||||
mod rules;
|
||||
|
||||
use crate::{error::NotificationSettingsError, event_handler::EventHandlerHandle, Client, Result};
|
||||
|
||||
/// Enum representing the push notification modes for a room.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum RoomNotificationMode {
|
||||
@@ -12,3 +28,804 @@ pub enum RoomNotificationMode {
|
||||
/// Do not receive any notifications.
|
||||
Mute,
|
||||
}
|
||||
|
||||
/// A high-level API to manage the client owner's push notification settings.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NotificationSettings {
|
||||
/// The underlying HTTP client.
|
||||
client: Client,
|
||||
/// Owner's account push rules. They will be updated on sync.
|
||||
rules: Arc<RwLock<Rules>>,
|
||||
/// Event handler for push rules event
|
||||
push_rules_event_handler: EventHandlerHandle,
|
||||
}
|
||||
|
||||
impl Drop for NotificationSettings {
|
||||
fn drop(&mut self) {
|
||||
self.client.remove_event_handler(self.push_rules_event_handler.clone());
|
||||
}
|
||||
}
|
||||
|
||||
impl NotificationSettings {
|
||||
/// Build a new `NotificationSettings``
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `client` - A `Client` used to perform API calls
|
||||
/// * `ruleset` - A `Ruleset` containing account's owner push rules
|
||||
pub fn new(client: Client, ruleset: Ruleset) -> Self {
|
||||
let rules = Arc::new(RwLock::new(Rules::new(ruleset)));
|
||||
|
||||
// Listen for PushRulesEvent
|
||||
let rules_clone = rules.clone();
|
||||
let push_rules_event_handler = client.add_event_handler(move |ev: PushRulesEvent| {
|
||||
let rules = rules_clone.to_owned();
|
||||
async move {
|
||||
*rules.write().await = Rules::new(ev.content.global);
|
||||
}
|
||||
});
|
||||
|
||||
Self { client, rules, push_rules_event_handler }
|
||||
}
|
||||
|
||||
/// Gets the user defined notification mode for a room.
|
||||
pub async fn get_user_defined_room_notification_mode(
|
||||
&self,
|
||||
room_id: &RoomId,
|
||||
) -> Option<RoomNotificationMode> {
|
||||
self.rules.read().await.get_user_defined_room_notification_mode(room_id)
|
||||
}
|
||||
|
||||
/// Gets the default notification mode for a room.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `is_encrypted` - `true` if the room is encrypted
|
||||
/// * `members_count` - the room members count
|
||||
pub async fn get_default_room_notification_mode(
|
||||
&self,
|
||||
is_encrypted: bool,
|
||||
members_count: u64,
|
||||
) -> RoomNotificationMode {
|
||||
self.rules.read().await.get_default_room_notification_mode(is_encrypted, members_count)
|
||||
}
|
||||
|
||||
/// Get whether the given ruleset contains some enabled keywords rules.
|
||||
pub async fn contains_keyword_rules(&self) -> bool {
|
||||
self.rules.read().await.contains_keyword_rules()
|
||||
}
|
||||
|
||||
/// Get whether a push rule is enabled.
|
||||
pub async fn is_push_rule_enabled(
|
||||
&self,
|
||||
kind: RuleKind,
|
||||
rule_id: &str,
|
||||
) -> Result<bool, NotificationSettingsError> {
|
||||
self.rules.read().await.is_enabled(kind, rule_id)
|
||||
}
|
||||
|
||||
/// Set whether a push rule is enabled.
|
||||
pub async fn set_push_rule_enabled(
|
||||
&self,
|
||||
kind: RuleKind,
|
||||
rule_id: &str,
|
||||
enabled: bool,
|
||||
) -> Result<(), NotificationSettingsError> {
|
||||
let rules = self.rules.read().await.clone();
|
||||
|
||||
let mut rule_commands = RuleCommands::new(rules.ruleset);
|
||||
rule_commands.set_rule_enabled(kind, rule_id, enabled)?;
|
||||
|
||||
self.run_server_commands(&rule_commands).await?;
|
||||
|
||||
let rules = &mut *self.rules.write().await;
|
||||
rules.apply(rule_commands);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets the notification mode for a room.
|
||||
pub async fn set_room_notification_mode(
|
||||
&self,
|
||||
room_id: &RoomId,
|
||||
mode: RoomNotificationMode,
|
||||
) -> Result<(), NotificationSettingsError> {
|
||||
let rules = self.rules.read().await.clone();
|
||||
|
||||
// Check that the current mode is not already the target mode.
|
||||
if rules.get_user_defined_room_notification_mode(room_id) == Some(mode.clone()) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Build the command list to set the new mode
|
||||
let (new_rule_kind, notify) = match mode {
|
||||
RoomNotificationMode::AllMessages => {
|
||||
// insert a `Room` rule which notifies
|
||||
(RuleKind::Room, true)
|
||||
}
|
||||
RoomNotificationMode::MentionsAndKeywordsOnly => {
|
||||
// insert a `Room` rule which doesn't notify
|
||||
(RuleKind::Room, false)
|
||||
}
|
||||
RoomNotificationMode::Mute => {
|
||||
// insert an `Override` rule which doesn't notify
|
||||
(RuleKind::Override, false)
|
||||
}
|
||||
};
|
||||
|
||||
// Extract all the custom rules except the one we just created.
|
||||
let new_rule_id = room_id.as_str();
|
||||
let custom_rules: Vec<(RuleKind, String)> = rules
|
||||
.get_custom_rules_for_room(room_id)
|
||||
.into_iter()
|
||||
.filter(|(kind, rule_id)| kind != &new_rule_kind || rule_id != new_rule_id)
|
||||
.collect();
|
||||
|
||||
// Build the command list to delete all other custom rules, with the exception
|
||||
// of the newly inserted rule.
|
||||
let mut rule_commands = RuleCommands::new(rules.ruleset);
|
||||
rule_commands.insert_rule(new_rule_kind.clone(), room_id, notify)?;
|
||||
for (kind, rule_id) in custom_rules {
|
||||
rule_commands.delete_rule(kind, rule_id)?;
|
||||
}
|
||||
|
||||
self.run_server_commands(&rule_commands).await?;
|
||||
|
||||
let rules = &mut *self.rules.write().await;
|
||||
rules.apply(rule_commands);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete all user defined rules for a room.
|
||||
pub async fn delete_user_defined_room_rules(
|
||||
&self,
|
||||
room_id: &RoomId,
|
||||
) -> Result<(), NotificationSettingsError> {
|
||||
let rules = self.rules.read().await.clone();
|
||||
|
||||
let custom_rules = rules.get_custom_rules_for_room(room_id);
|
||||
if custom_rules.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut rule_commands = RuleCommands::new(rules.ruleset);
|
||||
for (kind, rule_id) in custom_rules {
|
||||
rule_commands.delete_rule(kind, rule_id)?;
|
||||
}
|
||||
|
||||
self.run_server_commands(&rule_commands).await?;
|
||||
|
||||
let rules = &mut *self.rules.write().await;
|
||||
rules.apply(rule_commands);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Unmute a room.
|
||||
pub async fn unmute_room(
|
||||
&self,
|
||||
room_id: &RoomId,
|
||||
is_encrypted: bool,
|
||||
members_count: u64,
|
||||
) -> Result<(), NotificationSettingsError> {
|
||||
let rules = self.rules.read().await.clone();
|
||||
|
||||
// Check if there is a user defined mode
|
||||
if let Some(room_mode) = rules.get_user_defined_room_notification_mode(room_id) {
|
||||
if room_mode != RoomNotificationMode::Mute {
|
||||
// Already unmuted
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Get default mode for this room
|
||||
let default_mode =
|
||||
rules.get_default_room_notification_mode(is_encrypted, members_count);
|
||||
|
||||
// If the default mode is `Mute`, set it to `AllMessages`
|
||||
if default_mode == RoomNotificationMode::Mute {
|
||||
self.set_room_notification_mode(room_id, RoomNotificationMode::AllMessages).await
|
||||
} else {
|
||||
// Otherwise, delete user defined rules to use the default mode
|
||||
self.delete_user_defined_room_rules(room_id).await
|
||||
}
|
||||
} else {
|
||||
// This is the default mode, create a custom rule to unmute this room by setting
|
||||
// the mode to `AllMessages`
|
||||
self.set_room_notification_mode(room_id, RoomNotificationMode::AllMessages).await
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert commands into requests to the server, and run them.
|
||||
async fn run_server_commands(
|
||||
&self,
|
||||
rule_commands: &RuleCommands,
|
||||
) -> Result<(), NotificationSettingsError> {
|
||||
for command in &rule_commands.commands {
|
||||
match command {
|
||||
Command::DeletePushRule { scope, kind, rule_id } => {
|
||||
let request = delete_pushrule::v3::Request::new(
|
||||
scope.to_owned(),
|
||||
kind.to_owned(),
|
||||
rule_id.to_owned(),
|
||||
);
|
||||
self.client
|
||||
.send(request, None)
|
||||
.await
|
||||
.map_err(|_| NotificationSettingsError::UnableToRemovePushRule)?;
|
||||
}
|
||||
Command::SetRoomPushRule { scope, room_id: _, notify: _ } => {
|
||||
let push_rule = command.to_push_rule()?;
|
||||
let request = set_pushrule::v3::Request::new(scope.to_owned(), push_rule);
|
||||
self.client
|
||||
.send(request, None)
|
||||
.await
|
||||
.map_err(|_| NotificationSettingsError::UnableToAddPushRule)?;
|
||||
}
|
||||
Command::SetOverridePushRule { scope, rule_id: _, room_id: _, notify: _ } => {
|
||||
let push_rule = command.to_push_rule()?;
|
||||
let request = set_pushrule::v3::Request::new(scope.to_owned(), push_rule);
|
||||
self.client
|
||||
.send(request, None)
|
||||
.await
|
||||
.map_err(|_| NotificationSettingsError::UnableToAddPushRule)?;
|
||||
}
|
||||
Command::SetPushRuleEnabled { scope, kind, rule_id, enabled } => {
|
||||
let request = set_pushrule_enabled::v3::Request::new(
|
||||
scope.to_owned(),
|
||||
kind.to_owned(),
|
||||
rule_id.to_owned(),
|
||||
*enabled,
|
||||
);
|
||||
self.client
|
||||
.send(request, None)
|
||||
.await
|
||||
.map_err(|_| NotificationSettingsError::UnableToUpdatePushRule)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// The http mocking library is not supported for wasm32
|
||||
#[cfg(all(test, not(target_arch = "wasm32")))]
|
||||
mod tests {
|
||||
use assert_matches::assert_matches;
|
||||
use matrix_sdk_test::async_test;
|
||||
use ruma::{
|
||||
push::{
|
||||
Action, AnyPushRuleRef, NewPatternedPushRule, NewPushRule, PredefinedOverrideRuleId,
|
||||
PredefinedUnderrideRuleId, RuleKind, Ruleset,
|
||||
},
|
||||
OwnedRoomId, RoomId, UserId,
|
||||
};
|
||||
use wiremock::{http::Method, matchers::method, Mock, MockServer, ResponseTemplate};
|
||||
|
||||
use super::rule_commands::RuleCommands;
|
||||
use crate::{
|
||||
error::NotificationSettingsError,
|
||||
notification_settings::{NotificationSettings, RoomNotificationMode},
|
||||
test_utils::logged_in_client,
|
||||
Client,
|
||||
};
|
||||
|
||||
fn get_test_room_id() -> OwnedRoomId {
|
||||
RoomId::parse("!AAAaAAAAAaaAAaaaaa:matrix.org").unwrap()
|
||||
}
|
||||
|
||||
fn get_server_default_ruleset() -> Ruleset {
|
||||
let user_id = UserId::parse("@user:matrix.org").unwrap();
|
||||
Ruleset::server_default(&user_id)
|
||||
}
|
||||
|
||||
fn from_insert_rules(
|
||||
client: &Client,
|
||||
rules: Vec<(RuleKind, &RoomId, bool)>,
|
||||
) -> NotificationSettings {
|
||||
let ruleset = get_server_default_ruleset();
|
||||
// XXX would be slightly better to only use `Ruleset` here too, and no
|
||||
// `RuleCommands` so we're testing things more in isolation.
|
||||
let mut rule_commands = RuleCommands::new(ruleset);
|
||||
for (kind, room_id, notify) in rules {
|
||||
rule_commands.insert_rule(kind, room_id, notify).unwrap();
|
||||
}
|
||||
NotificationSettings::new(client.to_owned(), rule_commands.rules)
|
||||
}
|
||||
|
||||
async fn get_custom_rules_for_room(
|
||||
settings: &NotificationSettings,
|
||||
room_id: &RoomId,
|
||||
) -> Vec<(RuleKind, String)> {
|
||||
settings.rules.read().await.get_custom_rules_for_room(room_id)
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_get_custom_rules_for_room() {
|
||||
let server = MockServer::start().await;
|
||||
let client = logged_in_client(Some(server.uri())).await;
|
||||
let room_id = get_test_room_id();
|
||||
|
||||
let settings = from_insert_rules(&client, vec![(RuleKind::Room, &room_id, true)]);
|
||||
|
||||
let custom_rules = get_custom_rules_for_room(&settings, &room_id).await;
|
||||
assert_eq!(custom_rules.len(), 1);
|
||||
assert_eq!(custom_rules[0], (RuleKind::Room, room_id.to_string()));
|
||||
|
||||
let settings = from_insert_rules(
|
||||
&client,
|
||||
vec![(RuleKind::Room, &room_id, true), (RuleKind::Override, &room_id, true)],
|
||||
);
|
||||
let custom_rules = get_custom_rules_for_room(&settings, &room_id).await;
|
||||
assert_eq!(custom_rules.len(), 2);
|
||||
assert_eq!(custom_rules[0], (RuleKind::Override, room_id.to_string()));
|
||||
assert_eq!(custom_rules[1], (RuleKind::Room, room_id.to_string()));
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_get_user_defined_room_notification_mode_none() {
|
||||
let server = MockServer::start().await;
|
||||
let client = logged_in_client(Some(server.uri())).await;
|
||||
let room_id = get_test_room_id();
|
||||
|
||||
let settings = client.notification_settings().await;
|
||||
assert!(settings.get_user_defined_room_notification_mode(&room_id).await.is_none());
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_get_user_defined_room_notification_mode_all_messages() {
|
||||
let server = MockServer::start().await;
|
||||
let client = logged_in_client(Some(server.uri())).await;
|
||||
let room_id = get_test_room_id();
|
||||
|
||||
// Initialize with a notifying `Room` rule to be in `AllMessages`
|
||||
let settings = from_insert_rules(&client, vec![(RuleKind::Room, &room_id, true)]);
|
||||
|
||||
assert_eq!(
|
||||
settings.get_user_defined_room_notification_mode(&room_id).await.unwrap(),
|
||||
RoomNotificationMode::AllMessages
|
||||
);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_get_user_defined_room_notification_mode_mentions_and_keywords() {
|
||||
let server = MockServer::start().await;
|
||||
let client = logged_in_client(Some(server.uri())).await;
|
||||
let room_id = get_test_room_id();
|
||||
|
||||
// Initialize with a muted `Room` rule to be in `MentionsAndKeywordsOnly`
|
||||
let settings = from_insert_rules(&client, vec![(RuleKind::Room, &room_id, false)]);
|
||||
assert_eq!(
|
||||
settings.get_user_defined_room_notification_mode(&room_id).await.unwrap(),
|
||||
RoomNotificationMode::MentionsAndKeywordsOnly
|
||||
);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_get_user_defined_room_notification_mode_mute() {
|
||||
let server = MockServer::start().await;
|
||||
let client = logged_in_client(Some(server.uri())).await;
|
||||
let room_id = get_test_room_id();
|
||||
|
||||
// Initialize with a muted `Override` rule to be in `Mute`
|
||||
let settings = from_insert_rules(&client, vec![(RuleKind::Override, &room_id, false)]);
|
||||
assert_eq!(
|
||||
settings.get_user_defined_room_notification_mode(&room_id).await.unwrap(),
|
||||
RoomNotificationMode::Mute
|
||||
);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_get_default_room_notification_mode_all_messages() {
|
||||
let server = MockServer::start().await;
|
||||
let client = logged_in_client(Some(server.uri())).await;
|
||||
|
||||
let mut ruleset = get_server_default_ruleset();
|
||||
ruleset
|
||||
.set_actions(
|
||||
RuleKind::Underride,
|
||||
PredefinedUnderrideRuleId::RoomOneToOne,
|
||||
vec![Action::Notify],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let settings = NotificationSettings::new(client, ruleset);
|
||||
assert_eq!(
|
||||
settings.get_default_room_notification_mode(false, 2).await,
|
||||
RoomNotificationMode::AllMessages
|
||||
);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_get_default_room_notification_mode_mentions_and_keywords() {
|
||||
let server = MockServer::start().await;
|
||||
let client = logged_in_client(Some(server.uri())).await;
|
||||
|
||||
// The default mode must be `MentionsAndKeywords` if the corresponding Underride
|
||||
// rule doesn't notify
|
||||
let mut ruleset = get_server_default_ruleset();
|
||||
ruleset
|
||||
.set_actions(RuleKind::Underride, PredefinedUnderrideRuleId::RoomOneToOne, vec![])
|
||||
.unwrap();
|
||||
|
||||
let settings = NotificationSettings::new(client.to_owned(), ruleset.to_owned());
|
||||
assert_eq!(
|
||||
settings.get_default_room_notification_mode(false, 2).await,
|
||||
RoomNotificationMode::MentionsAndKeywordsOnly
|
||||
);
|
||||
|
||||
// The default mode must be `MentionsAndKeywords` if the corresponding Underride
|
||||
// rule is disabled
|
||||
ruleset
|
||||
.set_enabled(RuleKind::Underride, PredefinedUnderrideRuleId::RoomOneToOne, false)
|
||||
.unwrap();
|
||||
|
||||
let settings = NotificationSettings::new(client, ruleset);
|
||||
assert_eq!(
|
||||
settings.get_default_room_notification_mode(false, 2).await,
|
||||
RoomNotificationMode::MentionsAndKeywordsOnly
|
||||
);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_contains_keyword_rules() {
|
||||
let server = MockServer::start().await;
|
||||
let client = logged_in_client(Some(server.uri())).await;
|
||||
|
||||
let mut ruleset = get_server_default_ruleset();
|
||||
let settings = NotificationSettings::new(client.to_owned(), ruleset.to_owned());
|
||||
|
||||
// By default, no keywords rules should be present
|
||||
let contains_keywords_rules = settings.contains_keyword_rules().await;
|
||||
assert!(!contains_keywords_rules);
|
||||
|
||||
// Initialize with a keyword rule
|
||||
let rule = NewPatternedPushRule::new(
|
||||
"keyword_rule_id".into(),
|
||||
"keyword".into(),
|
||||
vec![Action::Notify],
|
||||
);
|
||||
ruleset.insert(NewPushRule::Content(rule), None, None).unwrap();
|
||||
|
||||
let settings = NotificationSettings::new(client, ruleset);
|
||||
let contains_keywords_rules = settings.contains_keyword_rules().await;
|
||||
assert!(contains_keywords_rules);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_is_push_rule_enabled() {
|
||||
let server = MockServer::start().await;
|
||||
let client = logged_in_client(Some(server.uri())).await;
|
||||
|
||||
// Initial state: Reaction disabled
|
||||
let mut ruleset = get_server_default_ruleset();
|
||||
ruleset.set_enabled(RuleKind::Override, PredefinedOverrideRuleId::Reaction, false).unwrap();
|
||||
|
||||
let settings = NotificationSettings::new(client.clone(), ruleset);
|
||||
|
||||
let enabled = settings
|
||||
.is_push_rule_enabled(RuleKind::Override, PredefinedOverrideRuleId::Reaction.as_str())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(!enabled);
|
||||
|
||||
// Initial state: Reaction enabled
|
||||
let mut ruleset = get_server_default_ruleset();
|
||||
ruleset.set_enabled(RuleKind::Override, PredefinedOverrideRuleId::Reaction, true).unwrap();
|
||||
|
||||
let settings = NotificationSettings::new(client, ruleset);
|
||||
|
||||
let enabled = settings
|
||||
.is_push_rule_enabled(RuleKind::Override, PredefinedOverrideRuleId::Reaction.as_str())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(enabled);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_set_push_rule_enabled() {
|
||||
let server = MockServer::start().await;
|
||||
let client = logged_in_client(Some(server.uri())).await;
|
||||
let mut ruleset = client.account().push_rules().await.unwrap();
|
||||
// Initial state
|
||||
ruleset.set_enabled(RuleKind::Override, PredefinedOverrideRuleId::Reaction, false).unwrap();
|
||||
|
||||
let settings = NotificationSettings::new(client, ruleset);
|
||||
|
||||
Mock::given(method("PUT")).respond_with(ResponseTemplate::new(200)).mount(&server).await;
|
||||
|
||||
settings
|
||||
.set_push_rule_enabled(
|
||||
RuleKind::Override,
|
||||
PredefinedOverrideRuleId::Reaction.as_str(),
|
||||
true,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Test the request sent
|
||||
let requests = server.received_requests().await.unwrap();
|
||||
assert_eq!(requests.len(), 1);
|
||||
assert_eq!(requests[0].method, Method::Put);
|
||||
assert_eq!(
|
||||
requests[0].url.path(),
|
||||
"/_matrix/client/r0/pushrules/global/override/.m.rule.reaction/enabled"
|
||||
);
|
||||
|
||||
// The ruleset must have been updated
|
||||
let rules = settings.rules.read().await;
|
||||
let rule =
|
||||
rules.ruleset.get(RuleKind::Override, PredefinedOverrideRuleId::Reaction).unwrap();
|
||||
assert!(rule.enabled());
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_set_push_rule_enabled_api_error() {
|
||||
let server = MockServer::start().await;
|
||||
let client = logged_in_client(Some(server.uri())).await;
|
||||
let mut ruleset = client.account().push_rules().await.unwrap();
|
||||
// Initial state
|
||||
ruleset
|
||||
.set_enabled(RuleKind::Override, PredefinedOverrideRuleId::IsUserMention, false)
|
||||
.unwrap();
|
||||
|
||||
let settings = NotificationSettings::new(client, ruleset);
|
||||
|
||||
// If the server returns an error
|
||||
Mock::given(method("PUT")).respond_with(ResponseTemplate::new(500)).mount(&server).await;
|
||||
|
||||
// When enabling the push rule
|
||||
assert_eq!(
|
||||
settings
|
||||
.set_push_rule_enabled(
|
||||
RuleKind::Override,
|
||||
PredefinedOverrideRuleId::IsUserMention.as_str(),
|
||||
true,
|
||||
)
|
||||
.await,
|
||||
Err(NotificationSettingsError::UnableToUpdatePushRule)
|
||||
);
|
||||
|
||||
// The ruleset must not have been updated
|
||||
let rules = settings.rules.read().await;
|
||||
let rule =
|
||||
rules.ruleset.get(RuleKind::Override, PredefinedOverrideRuleId::IsUserMention).unwrap();
|
||||
assert!(!rule.enabled());
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_set_room_notification_mode() {
|
||||
let server = MockServer::start().await;
|
||||
let client = logged_in_client(Some(server.uri())).await;
|
||||
|
||||
Mock::given(method("PUT")).respond_with(ResponseTemplate::new(200)).mount(&server).await;
|
||||
Mock::given(method("DELETE")).respond_with(ResponseTemplate::new(200)).mount(&server).await;
|
||||
|
||||
let settings = client.notification_settings().await;
|
||||
let room_id = get_test_room_id();
|
||||
|
||||
let mode = settings.get_user_defined_room_notification_mode(&room_id).await;
|
||||
assert!(mode.is_none());
|
||||
|
||||
let new_modes = &[
|
||||
RoomNotificationMode::AllMessages,
|
||||
RoomNotificationMode::MentionsAndKeywordsOnly,
|
||||
RoomNotificationMode::Mute,
|
||||
];
|
||||
for new_mode in new_modes {
|
||||
settings.set_room_notification_mode(&room_id, new_mode.clone()).await.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
new_mode.clone(),
|
||||
settings.get_user_defined_room_notification_mode(&room_id).await.unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_set_room_notification_mode_requests_order() {
|
||||
let server = MockServer::start().await;
|
||||
let client = logged_in_client(Some(server.uri())).await;
|
||||
|
||||
Mock::given(method("PUT")).respond_with(ResponseTemplate::new(200)).mount(&server).await;
|
||||
Mock::given(method("DELETE")).respond_with(ResponseTemplate::new(200)).mount(&server).await;
|
||||
|
||||
let room_id = get_test_room_id();
|
||||
|
||||
// Set the initial state to `AllMessages` by setting a `Room` rule that notifies
|
||||
let settings = from_insert_rules(&client, vec![(RuleKind::Room, &room_id, true)]);
|
||||
|
||||
// Set the new mode to `Mute`, this will add a new `Override` rule without
|
||||
// action and remove the `Room` rule.
|
||||
settings.set_room_notification_mode(&room_id, RoomNotificationMode::Mute).await.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
RoomNotificationMode::Mute,
|
||||
settings.get_user_defined_room_notification_mode(&room_id).await.unwrap()
|
||||
);
|
||||
|
||||
// Test that the PUT is executed before the DELETE, so that the following sync
|
||||
// results will give the following transitions: `AllMessages` ->
|
||||
// `AllMessages` -> `Mute` by sending the DELETE before the PUT, we
|
||||
// would have `AllMessages` -> `Default` -> `Mute`
|
||||
let requests = server.received_requests().await.unwrap();
|
||||
assert_eq!(requests.len(), 2);
|
||||
assert_eq!(requests[0].method, Method::Put);
|
||||
assert_eq!(requests[1].method, Method::Delete);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_set_room_notification_mode_put_api_error() {
|
||||
let server = MockServer::start().await;
|
||||
let client = logged_in_client(Some(server.uri())).await;
|
||||
|
||||
// If the server returns an error
|
||||
Mock::given(method("PUT")).respond_with(ResponseTemplate::new(500)).mount(&server).await;
|
||||
Mock::given(method("DELETE")).respond_with(ResponseTemplate::new(200)).mount(&server).await;
|
||||
|
||||
let room_id = get_test_room_id();
|
||||
|
||||
// Set the initial state to `AllMessages` by setting a `Room` rule that notifies
|
||||
let settings = from_insert_rules(&client, vec![(RuleKind::Room, &room_id, true)]);
|
||||
|
||||
assert_eq!(
|
||||
settings.get_user_defined_room_notification_mode(&room_id).await.unwrap(),
|
||||
RoomNotificationMode::AllMessages
|
||||
);
|
||||
|
||||
// Setting the new mode should fail
|
||||
assert_eq!(
|
||||
settings.set_room_notification_mode(&room_id, RoomNotificationMode::Mute).await,
|
||||
Err(NotificationSettingsError::UnableToAddPushRule)
|
||||
);
|
||||
|
||||
// The ruleset must not have been updated
|
||||
assert_eq!(
|
||||
settings.get_user_defined_room_notification_mode(&room_id).await.unwrap(),
|
||||
RoomNotificationMode::AllMessages
|
||||
);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_set_room_notification_mode_delete_api_error() {
|
||||
let server = MockServer::start().await;
|
||||
let client = logged_in_client(Some(server.uri())).await;
|
||||
|
||||
// If the server returns an error
|
||||
Mock::given(method("PUT")).respond_with(ResponseTemplate::new(200)).mount(&server).await;
|
||||
Mock::given(method("DELETE")).respond_with(ResponseTemplate::new(500)).mount(&server).await;
|
||||
|
||||
let room_id = get_test_room_id();
|
||||
|
||||
// Set the initial state to `AllMessages` by setting a `Room` rule that notifies
|
||||
let settings = from_insert_rules(&client, vec![(RuleKind::Room, &room_id, true)]);
|
||||
|
||||
assert_eq!(
|
||||
settings.get_user_defined_room_notification_mode(&room_id).await.unwrap(),
|
||||
RoomNotificationMode::AllMessages
|
||||
);
|
||||
|
||||
// Setting the new mode should fail
|
||||
assert_eq!(
|
||||
settings.set_room_notification_mode(&room_id, RoomNotificationMode::Mute).await,
|
||||
Err(NotificationSettingsError::UnableToRemovePushRule)
|
||||
);
|
||||
|
||||
// The ruleset must not have been updated
|
||||
assert_eq!(
|
||||
settings.get_user_defined_room_notification_mode(&room_id).await.unwrap(),
|
||||
RoomNotificationMode::AllMessages
|
||||
);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_delete_user_defined_room_rules() {
|
||||
let server = MockServer::start().await;
|
||||
let client = logged_in_client(Some(server.uri())).await;
|
||||
let room_id_a = RoomId::parse("!AAAaAAAAAaaAAaaaaa:matrix.org").unwrap();
|
||||
let room_id_b = RoomId::parse("!BBBbBBBBBbbBBbbbbb:matrix.org").unwrap();
|
||||
|
||||
Mock::given(method("DELETE")).respond_with(ResponseTemplate::new(200)).mount(&server).await;
|
||||
|
||||
// Initialize with some of custom rules
|
||||
let settings = from_insert_rules(
|
||||
&client,
|
||||
vec![
|
||||
(RuleKind::Room, &room_id_a, true),
|
||||
(RuleKind::Room, &room_id_b, true),
|
||||
(RuleKind::Override, &room_id_b, true),
|
||||
],
|
||||
);
|
||||
|
||||
// Delete all user defined rules for room_id_a
|
||||
settings.delete_user_defined_room_rules(&room_id_a).await.unwrap();
|
||||
|
||||
// Only the rules for room_id_b should remain
|
||||
let updated_rules = settings.rules.read().await;
|
||||
assert_eq!(updated_rules.get_custom_rules_for_room(&room_id_b).len(), 2);
|
||||
assert!(updated_rules.get_custom_rules_for_room(&room_id_a).is_empty());
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_unmute_room_not_muted() {
|
||||
let server = MockServer::start().await;
|
||||
let client = logged_in_client(Some(server.uri())).await;
|
||||
let room_id = get_test_room_id();
|
||||
|
||||
// Initialize with a `MentionsAndKeywordsOnly` mode
|
||||
let settings = from_insert_rules(&client, vec![(RuleKind::Room, &room_id, false)]);
|
||||
assert_eq!(
|
||||
settings.get_user_defined_room_notification_mode(&room_id).await.unwrap(),
|
||||
RoomNotificationMode::MentionsAndKeywordsOnly
|
||||
);
|
||||
|
||||
// Unmute the room
|
||||
settings.unmute_room(&room_id, true, 2).await.unwrap();
|
||||
|
||||
// The ruleset must not be modified
|
||||
assert_eq!(
|
||||
settings.get_user_defined_room_notification_mode(&room_id).await.unwrap(),
|
||||
RoomNotificationMode::MentionsAndKeywordsOnly
|
||||
);
|
||||
|
||||
let room_rules = get_custom_rules_for_room(&settings, &room_id).await;
|
||||
assert_eq!(room_rules.len(), 1);
|
||||
assert_matches!(settings.rules.read().await.ruleset.get(RuleKind::Room, &room_id),
|
||||
Some(AnyPushRuleRef::Room(rule)) => {
|
||||
assert_eq!(rule.rule_id, room_id);
|
||||
assert!(rule.actions.is_empty());
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_unmute_room() {
|
||||
let server = MockServer::start().await;
|
||||
Mock::given(method("PUT")).respond_with(ResponseTemplate::new(200)).mount(&server).await;
|
||||
Mock::given(method("DELETE")).respond_with(ResponseTemplate::new(200)).mount(&server).await;
|
||||
let client = logged_in_client(Some(server.uri())).await;
|
||||
let room_id = get_test_room_id();
|
||||
|
||||
// Start with the room muted
|
||||
let settings = from_insert_rules(&client, vec![(RuleKind::Override, &room_id, false)]);
|
||||
assert_eq!(
|
||||
settings.get_user_defined_room_notification_mode(&room_id).await,
|
||||
Some(RoomNotificationMode::Mute)
|
||||
);
|
||||
|
||||
// Unmute the room
|
||||
settings.unmute_room(&room_id, false, 2).await.unwrap();
|
||||
|
||||
// The user defined mode must have been removed
|
||||
assert!(settings.get_user_defined_room_notification_mode(&room_id).await.is_none());
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_unmute_room_default_mode() {
|
||||
let server = MockServer::start().await;
|
||||
Mock::given(method("PUT")).respond_with(ResponseTemplate::new(200)).mount(&server).await;
|
||||
let client = logged_in_client(Some(server.uri())).await;
|
||||
let room_id = get_test_room_id();
|
||||
let settings = client.notification_settings().await;
|
||||
|
||||
// Unmute the room
|
||||
settings.unmute_room(&room_id, false, 2).await.unwrap();
|
||||
|
||||
// The new mode must be `AllMessages`
|
||||
assert_eq!(
|
||||
Some(RoomNotificationMode::AllMessages),
|
||||
settings.get_user_defined_room_notification_mode(&room_id).await
|
||||
);
|
||||
|
||||
let room_rules = get_custom_rules_for_room(&settings, &room_id).await;
|
||||
assert_eq!(room_rules.len(), 1);
|
||||
assert_matches!(settings.rules.read().await.ruleset.get(RuleKind::Room, &room_id),
|
||||
Some(AnyPushRuleRef::Room(rule)) => {
|
||||
assert_eq!(rule.rule_id, room_id);
|
||||
assert!(!rule.actions.is_empty());
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
485
crates/matrix-sdk/src/notification_settings/rule_commands.rs
Normal file
485
crates/matrix-sdk/src/notification_settings/rule_commands.rs
Normal file
@@ -0,0 +1,485 @@
|
||||
use ruma::{
|
||||
api::client::push::RuleScope,
|
||||
push::{
|
||||
PredefinedContentRuleId, PredefinedOverrideRuleId, RemovePushRuleError, RuleKind, Ruleset,
|
||||
},
|
||||
RoomId,
|
||||
};
|
||||
|
||||
use super::command::Command;
|
||||
use crate::NotificationSettingsError;
|
||||
|
||||
/// A `RuleCommand` allows to generate a list of `Command` needed to modify a
|
||||
/// `Ruleset`
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct RuleCommands {
|
||||
pub(crate) commands: Vec<Command>,
|
||||
pub(crate) rules: Ruleset,
|
||||
}
|
||||
|
||||
impl RuleCommands {
|
||||
pub(crate) fn new(rules: Ruleset) -> Self {
|
||||
RuleCommands { commands: vec![], rules }
|
||||
}
|
||||
|
||||
/// Insert a new rule
|
||||
pub(crate) fn insert_rule(
|
||||
&mut self,
|
||||
kind: RuleKind,
|
||||
room_id: &RoomId,
|
||||
notify: bool,
|
||||
) -> Result<(), NotificationSettingsError> {
|
||||
let command = match kind {
|
||||
RuleKind::Room => Command::SetRoomPushRule {
|
||||
scope: RuleScope::Global,
|
||||
room_id: room_id.to_owned(),
|
||||
notify,
|
||||
},
|
||||
RuleKind::Override => Command::SetOverridePushRule {
|
||||
scope: RuleScope::Global,
|
||||
rule_id: room_id.to_string(),
|
||||
room_id: room_id.to_owned(),
|
||||
notify,
|
||||
},
|
||||
_ => {
|
||||
return Err(NotificationSettingsError::InvalidParameter(
|
||||
"cannot insert a rule for this kind.".to_owned(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
self.rules.insert(command.to_push_rule()?, None, None)?;
|
||||
self.commands.push(command);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete a rule
|
||||
pub(crate) fn delete_rule(
|
||||
&mut self,
|
||||
kind: RuleKind,
|
||||
rule_id: String,
|
||||
) -> Result<(), RemovePushRuleError> {
|
||||
self.rules.remove(kind.clone(), &rule_id)?;
|
||||
self.commands.push(Command::DeletePushRule { scope: RuleScope::Global, kind, rule_id });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_enabled_internal(
|
||||
&mut self,
|
||||
kind: RuleKind,
|
||||
rule_id: &str,
|
||||
enabled: bool,
|
||||
) -> Result<(), NotificationSettingsError> {
|
||||
self.rules.set_enabled(kind.clone(), rule_id, enabled)?;
|
||||
self.commands.push(Command::SetPushRuleEnabled {
|
||||
scope: RuleScope::Global,
|
||||
kind,
|
||||
rule_id: rule_id.to_owned(),
|
||||
enabled,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set whether a rule is enabled
|
||||
pub(crate) fn set_rule_enabled(
|
||||
&mut self,
|
||||
kind: RuleKind,
|
||||
rule_id: &str,
|
||||
enabled: bool,
|
||||
) -> Result<(), NotificationSettingsError> {
|
||||
if rule_id == PredefinedOverrideRuleId::IsRoomMention.as_str() {
|
||||
// Handle specific case for `PredefinedOverrideRuleId::IsRoomMention`
|
||||
self.set_room_mention_enabled(enabled)
|
||||
} else if rule_id == PredefinedOverrideRuleId::IsUserMention.as_str() {
|
||||
// Handle specific case for `PredefinedOverrideRuleId::IsUserMention`
|
||||
self.set_user_mention_enabled(enabled)
|
||||
} else {
|
||||
self.set_enabled_internal(kind, rule_id, enabled)
|
||||
}
|
||||
}
|
||||
|
||||
/// Set whether `IsUserMention` is enabled
|
||||
fn set_user_mention_enabled(&mut self, enabled: bool) -> Result<(), NotificationSettingsError> {
|
||||
// Add a command for the `IsUserMention` `Override` rule (MSC3952).
|
||||
// This is a new push rule that may not yet be present.
|
||||
self.set_enabled_internal(
|
||||
RuleKind::Override,
|
||||
PredefinedOverrideRuleId::IsUserMention.as_str(),
|
||||
enabled,
|
||||
)?;
|
||||
|
||||
// For compatibility purpose, we still need to add commands for
|
||||
// `ContainsUserName` and `ContainsDisplayName` (deprecated rules).
|
||||
#[allow(deprecated)]
|
||||
{
|
||||
// `ContainsUserName`
|
||||
self.set_enabled_internal(
|
||||
RuleKind::Content,
|
||||
PredefinedContentRuleId::ContainsUserName.as_str(),
|
||||
enabled,
|
||||
)?;
|
||||
|
||||
// `ContainsDisplayName`
|
||||
self.set_enabled_internal(
|
||||
RuleKind::Override,
|
||||
PredefinedOverrideRuleId::ContainsDisplayName.as_str(),
|
||||
enabled,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set whether `IsRoomMention` is enabled
|
||||
fn set_room_mention_enabled(&mut self, enabled: bool) -> Result<(), NotificationSettingsError> {
|
||||
// Sets the `IsRoomMention` `Override` rule (MSC3952).
|
||||
// This is a new push rule that may not yet be present.
|
||||
self.set_enabled_internal(
|
||||
RuleKind::Override,
|
||||
PredefinedOverrideRuleId::IsRoomMention.as_str(),
|
||||
enabled,
|
||||
)?;
|
||||
|
||||
// For compatibility purpose, we still need to set `RoomNotif` (deprecated
|
||||
// rule).
|
||||
#[allow(deprecated)]
|
||||
self.set_enabled_internal(
|
||||
RuleKind::Override,
|
||||
PredefinedOverrideRuleId::RoomNotif.as_str(),
|
||||
enabled,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use assert_matches::assert_matches;
|
||||
use matrix_sdk_test::async_test;
|
||||
use ruma::{
|
||||
api::client::push::RuleScope,
|
||||
push::{
|
||||
NewPushRule, NewSimplePushRule, PredefinedContentRuleId, PredefinedOverrideRuleId,
|
||||
RemovePushRuleError, RuleKind, Ruleset,
|
||||
},
|
||||
OwnedRoomId, RoomId, UserId,
|
||||
};
|
||||
|
||||
use super::RuleCommands;
|
||||
use crate::{error::NotificationSettingsError, notification_settings::command::Command};
|
||||
|
||||
fn get_server_default_ruleset() -> Ruleset {
|
||||
let user_id = UserId::parse("@user:matrix.org").unwrap();
|
||||
Ruleset::server_default(&user_id)
|
||||
}
|
||||
|
||||
fn get_test_room_id() -> OwnedRoomId {
|
||||
RoomId::parse("!AAAaAAAAAaaAAaaaaa:matrix.org").unwrap()
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_insert_rule_room() {
|
||||
let room_id = get_test_room_id();
|
||||
let mut rule_commands = RuleCommands::new(get_server_default_ruleset());
|
||||
rule_commands.insert_rule(RuleKind::Room, &room_id, true).unwrap();
|
||||
|
||||
// A rule must have been inserted in the ruleset.
|
||||
assert!(rule_commands.rules.get(RuleKind::Room, &room_id).is_some());
|
||||
|
||||
// Exactly one command must have been created.
|
||||
assert_eq!(rule_commands.commands.len(), 1);
|
||||
assert_matches!(&rule_commands.commands[0],
|
||||
Command::SetRoomPushRule { scope, room_id: command_room_id, notify } => {
|
||||
assert_eq!(scope, &RuleScope::Global);
|
||||
assert_eq!(command_room_id, &room_id);
|
||||
assert!(notify);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_insert_rule_override() {
|
||||
let room_id = get_test_room_id();
|
||||
let mut rule_commands = RuleCommands::new(get_server_default_ruleset());
|
||||
rule_commands.insert_rule(RuleKind::Override, &room_id, true).unwrap();
|
||||
|
||||
// A rule must have been inserted in the ruleset.
|
||||
assert!(rule_commands.rules.get(RuleKind::Override, &room_id).is_some());
|
||||
|
||||
// Exactly one command must have been created.
|
||||
assert_eq!(rule_commands.commands.len(), 1);
|
||||
assert_matches!(&rule_commands.commands[0],
|
||||
Command::SetOverridePushRule { scope, room_id: command_room_id, rule_id, notify } => {
|
||||
assert_eq!(scope, &RuleScope::Global);
|
||||
assert_eq!(command_room_id, &room_id);
|
||||
assert_eq!(rule_id, room_id.as_str());
|
||||
assert!(notify);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_insert_rule_unsupported() {
|
||||
let room_id = get_test_room_id();
|
||||
let mut rule_commands = RuleCommands::new(get_server_default_ruleset());
|
||||
|
||||
assert_matches!(
|
||||
rule_commands.insert_rule(RuleKind::Underride, &room_id, true),
|
||||
Err(NotificationSettingsError::InvalidParameter(_)) => {}
|
||||
);
|
||||
|
||||
assert_matches!(
|
||||
rule_commands.insert_rule(RuleKind::Content, &room_id, true),
|
||||
Err(NotificationSettingsError::InvalidParameter(_)) => {}
|
||||
);
|
||||
|
||||
assert_matches!(
|
||||
rule_commands.insert_rule(RuleKind::Sender, &room_id, true),
|
||||
Err(NotificationSettingsError::InvalidParameter(_)) => {}
|
||||
);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_delete_rule() {
|
||||
let room_id = get_test_room_id();
|
||||
let mut ruleset = get_server_default_ruleset();
|
||||
|
||||
let new_rule = NewSimplePushRule::new(room_id.to_owned(), vec![]);
|
||||
ruleset.insert(NewPushRule::Room(new_rule), None, None).unwrap();
|
||||
|
||||
let mut rule_commands = RuleCommands::new(ruleset);
|
||||
|
||||
// Delete must succeed.
|
||||
rule_commands.delete_rule(RuleKind::Room, room_id.to_string()).unwrap();
|
||||
|
||||
// The ruleset must have been updated.
|
||||
assert!(rule_commands.rules.get(RuleKind::Room, &room_id).is_none());
|
||||
|
||||
// Exactly one command must have been created.
|
||||
assert_eq!(rule_commands.commands.len(), 1);
|
||||
assert_matches!(&rule_commands.commands[0],
|
||||
Command::DeletePushRule { scope, kind, rule_id } => {
|
||||
assert_eq!(scope, &RuleScope::Global);
|
||||
assert_eq!(kind, &RuleKind::Room);
|
||||
assert_eq!(rule_id, room_id.as_str());
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_delete_rule_errors() {
|
||||
let room_id = get_test_room_id();
|
||||
let ruleset = get_server_default_ruleset();
|
||||
|
||||
let mut rule_commands = RuleCommands::new(ruleset);
|
||||
|
||||
// Deletion should fail if an attempt is made to delete a rule that does not
|
||||
// exist.
|
||||
assert_matches!(
|
||||
rule_commands.delete_rule(RuleKind::Room, room_id.to_string()),
|
||||
Err(RemovePushRuleError::NotFound) => {}
|
||||
);
|
||||
|
||||
// Deletion should fail if an attempt is made to delete a default server rule.
|
||||
assert_matches!(
|
||||
rule_commands.delete_rule(RuleKind::Override, PredefinedOverrideRuleId::IsUserMention.to_string()),
|
||||
Err(RemovePushRuleError::ServerDefault) => {}
|
||||
);
|
||||
|
||||
assert!(rule_commands.commands.is_empty());
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_set_rule_enabled() {
|
||||
let mut ruleset = get_server_default_ruleset();
|
||||
|
||||
// Initialize with `Reaction` rule disabled.
|
||||
ruleset.set_enabled(RuleKind::Override, PredefinedOverrideRuleId::Reaction, false).unwrap();
|
||||
|
||||
let mut rule_commands = RuleCommands::new(ruleset);
|
||||
rule_commands
|
||||
.set_rule_enabled(RuleKind::Override, PredefinedOverrideRuleId::Reaction.as_str(), true)
|
||||
.unwrap();
|
||||
|
||||
// The ruleset must have been updated
|
||||
let rule = rule_commands
|
||||
.rules
|
||||
.get(RuleKind::Override, PredefinedOverrideRuleId::Reaction.as_str())
|
||||
.unwrap();
|
||||
assert!(rule.enabled());
|
||||
|
||||
// Exactly one command must have been created.
|
||||
assert_eq!(rule_commands.commands.len(), 1);
|
||||
assert_matches!(&rule_commands.commands[0],
|
||||
Command::SetPushRuleEnabled { scope, kind, rule_id, enabled } => {
|
||||
assert_eq!(scope, &RuleScope::Global);
|
||||
assert_eq!(kind, &RuleKind::Override);
|
||||
assert_eq!(rule_id, PredefinedOverrideRuleId::Reaction.as_str());
|
||||
assert!(enabled);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_set_rule_enabled_not_found() {
|
||||
let ruleset = get_server_default_ruleset();
|
||||
let mut rule_commands = RuleCommands::new(ruleset);
|
||||
assert_eq!(
|
||||
rule_commands.set_rule_enabled(RuleKind::Room, "unknown_rule_id", true),
|
||||
Err(NotificationSettingsError::RuleNotFound)
|
||||
);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_set_rule_enabled_user_mention() {
|
||||
let mut ruleset = get_server_default_ruleset();
|
||||
let mut rule_commands = RuleCommands::new(ruleset.clone());
|
||||
|
||||
ruleset
|
||||
.set_enabled(RuleKind::Override, PredefinedOverrideRuleId::IsUserMention, false)
|
||||
.unwrap();
|
||||
|
||||
#[allow(deprecated)]
|
||||
{
|
||||
ruleset
|
||||
.set_enabled(
|
||||
RuleKind::Override,
|
||||
PredefinedOverrideRuleId::ContainsDisplayName,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
ruleset
|
||||
.set_enabled(RuleKind::Content, PredefinedContentRuleId::ContainsUserName, false)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// Enable the user mention rule.
|
||||
rule_commands
|
||||
.set_rule_enabled(
|
||||
RuleKind::Override,
|
||||
PredefinedOverrideRuleId::IsUserMention.as_str(),
|
||||
true,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// The ruleset must have been updated.
|
||||
assert!(rule_commands
|
||||
.rules
|
||||
.get(RuleKind::Override, PredefinedOverrideRuleId::IsUserMention)
|
||||
.unwrap()
|
||||
.enabled());
|
||||
#[allow(deprecated)]
|
||||
{
|
||||
assert!(rule_commands
|
||||
.rules
|
||||
.get(RuleKind::Override, PredefinedOverrideRuleId::ContainsDisplayName)
|
||||
.unwrap()
|
||||
.enabled());
|
||||
assert!(rule_commands
|
||||
.rules
|
||||
.get(RuleKind::Content, PredefinedContentRuleId::ContainsUserName)
|
||||
.unwrap()
|
||||
.enabled());
|
||||
}
|
||||
|
||||
// Three commands are expected.
|
||||
assert_eq!(rule_commands.commands.len(), 3);
|
||||
|
||||
assert_matches!(&rule_commands.commands[0],
|
||||
Command::SetPushRuleEnabled { scope, kind, rule_id, enabled } => {
|
||||
assert_eq!(scope, &RuleScope::Global);
|
||||
assert_eq!(kind, &RuleKind::Override);
|
||||
assert_eq!(rule_id, PredefinedOverrideRuleId::IsUserMention.as_str());
|
||||
assert!(enabled);
|
||||
}
|
||||
);
|
||||
|
||||
#[allow(deprecated)]
|
||||
{
|
||||
assert_matches!(&rule_commands.commands[1],
|
||||
Command::SetPushRuleEnabled { scope, kind, rule_id, enabled } => {
|
||||
assert_eq!(scope, &RuleScope::Global);
|
||||
assert_eq!(kind, &RuleKind::Content);
|
||||
assert_eq!(rule_id, PredefinedContentRuleId::ContainsUserName.as_str());
|
||||
assert!(enabled);
|
||||
}
|
||||
);
|
||||
|
||||
assert_matches!(&rule_commands.commands[2],
|
||||
Command::SetPushRuleEnabled { scope, kind, rule_id, enabled } => {
|
||||
assert_eq!(scope, &RuleScope::Global);
|
||||
assert_eq!(kind, &RuleKind::Override);
|
||||
assert_eq!(rule_id, PredefinedOverrideRuleId::ContainsDisplayName.as_str());
|
||||
assert!(enabled);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_set_rule_enabled_room_mention() {
|
||||
let mut ruleset = get_server_default_ruleset();
|
||||
let mut rule_commands = RuleCommands::new(ruleset.clone());
|
||||
|
||||
ruleset
|
||||
.set_enabled(RuleKind::Override, PredefinedOverrideRuleId::IsRoomMention, false)
|
||||
.unwrap();
|
||||
|
||||
#[allow(deprecated)]
|
||||
{
|
||||
ruleset
|
||||
.set_enabled(RuleKind::Override, PredefinedOverrideRuleId::RoomNotif, false)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
rule_commands
|
||||
.set_rule_enabled(
|
||||
RuleKind::Override,
|
||||
PredefinedOverrideRuleId::IsRoomMention.as_str(),
|
||||
true,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// The ruleset must have been updated.
|
||||
assert!(rule_commands
|
||||
.rules
|
||||
.get(RuleKind::Override, PredefinedOverrideRuleId::IsRoomMention)
|
||||
.unwrap()
|
||||
.enabled());
|
||||
#[allow(deprecated)]
|
||||
{
|
||||
assert!(rule_commands
|
||||
.rules
|
||||
.get(RuleKind::Override, PredefinedOverrideRuleId::RoomNotif)
|
||||
.unwrap()
|
||||
.enabled());
|
||||
}
|
||||
|
||||
// Two commands are expected.
|
||||
assert_eq!(rule_commands.commands.len(), 2);
|
||||
|
||||
assert_matches!(&rule_commands.commands[0],
|
||||
Command::SetPushRuleEnabled { scope, kind, rule_id, enabled } => {
|
||||
assert_eq!(scope, &RuleScope::Global);
|
||||
assert_eq!(kind, &RuleKind::Override);
|
||||
assert_eq!(rule_id, PredefinedOverrideRuleId::IsRoomMention.as_str());
|
||||
assert!(enabled);
|
||||
}
|
||||
);
|
||||
|
||||
#[allow(deprecated)]
|
||||
{
|
||||
assert_matches!(&rule_commands.commands[1],
|
||||
Command::SetPushRuleEnabled { scope, kind, rule_id, enabled } => {
|
||||
assert_eq!(scope, &RuleScope::Global);
|
||||
assert_eq!(kind, &RuleKind::Override);
|
||||
assert_eq!(rule_id, PredefinedOverrideRuleId::RoomNotif.as_str());
|
||||
assert!(enabled);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +1,21 @@
|
||||
//! Ruleset utility struct
|
||||
|
||||
use ruma::{
|
||||
api::client::push::RuleScope,
|
||||
push::{
|
||||
Action, NewConditionalPushRule, NewPushRule, NewSimplePushRule, PredefinedContentRuleId,
|
||||
PredefinedOverrideRuleId, PredefinedUnderrideRuleId, PushCondition, RemovePushRuleError,
|
||||
RuleKind, Ruleset, Tweak,
|
||||
PredefinedContentRuleId, PredefinedOverrideRuleId, PredefinedUnderrideRuleId,
|
||||
PushCondition, RuleKind, Ruleset,
|
||||
},
|
||||
RoomId,
|
||||
};
|
||||
|
||||
use super::RoomNotificationMode;
|
||||
use super::{command::Command, rule_commands::RuleCommands, RoomNotificationMode};
|
||||
use crate::error::NotificationSettingsError;
|
||||
|
||||
/// enum describing the commands required to modify the owner's account data.
|
||||
#[derive(Clone, Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) enum Command {
|
||||
/// Set a new push rule
|
||||
SetPushRule { scope: RuleScope, rule: NewPushRule },
|
||||
/// Set whether a push rule is enabled
|
||||
SetPushRuleEnabled { scope: RuleScope, kind: RuleKind, rule_id: String, enabled: bool },
|
||||
/// Delete a push rule
|
||||
DeletePushRule { scope: RuleScope, kind: RuleKind, rule_id: String },
|
||||
}
|
||||
|
||||
pub(crate) struct Rules {
|
||||
pub ruleset: Ruleset,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Rules {
|
||||
pub(crate) fn new(ruleset: Ruleset) -> Self {
|
||||
Rules { ruleset }
|
||||
@@ -81,6 +67,7 @@ impl Rules {
|
||||
// enabled
|
||||
x.enabled &&
|
||||
// with a condition of type `EventMatch` for this `room_id`
|
||||
// XXX why not checking against x.rule_id here?
|
||||
x.conditions.iter().any(|x| matches!(
|
||||
x,
|
||||
PushCondition::EventMatch { key, pattern } if key == "room_id" && pattern == room_id
|
||||
@@ -132,7 +119,7 @@ impl Rules {
|
||||
}
|
||||
|
||||
/// Get whether the `IsUserMention` rule is enabled.
|
||||
pub(crate) fn is_user_mention_enabled(&self) -> bool {
|
||||
fn is_user_mention_enabled(&self) -> bool {
|
||||
// Search for an enabled `Override` rule `IsUserMention` (MSC3952).
|
||||
// This is a new push rule that may not yet be present.
|
||||
if let Some(rule) =
|
||||
@@ -166,7 +153,7 @@ impl Rules {
|
||||
}
|
||||
|
||||
/// Get whether the `IsRoomMention` rule is enabled.
|
||||
pub(crate) fn is_room_mention_enabled(&self) -> bool {
|
||||
fn is_room_mention_enabled(&self) -> bool {
|
||||
// Search for an enabled `Override` rule `IsRoomMention` (MSC3952).
|
||||
// This is a new push rule that may not yet be present.
|
||||
if let Some(rule) =
|
||||
@@ -210,197 +197,26 @@ impl Rules {
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert a new `Room` push rule for a given `room_id` and return an
|
||||
/// optional `Command` describing the action to be performed on the
|
||||
/// user's account data.
|
||||
pub(crate) fn insert_room_rule(
|
||||
&mut self,
|
||||
kind: RuleKind,
|
||||
room_id: &RoomId,
|
||||
notify: bool,
|
||||
) -> Result<Option<Command>, NotificationSettingsError> {
|
||||
let command;
|
||||
let actions = if notify {
|
||||
vec![Action::Notify, Action::SetTweak(Tweak::Sound("default".into()))]
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
match kind {
|
||||
RuleKind::Override => {
|
||||
// Insert a new push rule matching this `room_id`
|
||||
let new_rule = NewConditionalPushRule::new(
|
||||
room_id.to_string(),
|
||||
vec![PushCondition::EventMatch {
|
||||
key: "room_id".into(),
|
||||
pattern: room_id.to_string(),
|
||||
}],
|
||||
actions,
|
||||
);
|
||||
let new_rule = NewPushRule::Override(new_rule);
|
||||
self.ruleset.insert(new_rule.clone(), None, None)?;
|
||||
command = Some(Command::SetPushRule { scope: RuleScope::Global, rule: new_rule });
|
||||
}
|
||||
RuleKind::Room => {
|
||||
// Insert a new `Room` push rule for this `room_id`
|
||||
let new_rule = NewSimplePushRule::new(room_id.to_owned(), actions);
|
||||
let new_rule = NewPushRule::Room(new_rule);
|
||||
self.ruleset.insert(new_rule.clone(), None, None)?;
|
||||
command = Some(Command::SetPushRule { scope: RuleScope::Global, rule: new_rule });
|
||||
}
|
||||
_ => {
|
||||
return Err(NotificationSettingsError::InvalidParameter(
|
||||
"kind must be either Override or Room.".to_owned(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(command)
|
||||
}
|
||||
|
||||
/// Deletes a list of rules and returns a list of `Command` describing the
|
||||
/// actions to be performed on the user's account data.
|
||||
/// Apply a group of commands to the managed ruleset.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `rules` - A list of rules to delete
|
||||
/// * `exceptions` - A list of rules to ignore
|
||||
pub(crate) fn delete_rules(
|
||||
&mut self,
|
||||
rules: &[(RuleKind, String)],
|
||||
exceptions: &[(RuleKind, String)],
|
||||
) -> Result<Vec<Command>, RemovePushRuleError> {
|
||||
let mut commands = vec![];
|
||||
|
||||
for (rule_kind, rule_id) in rules {
|
||||
// Ignore rules present in exceptions
|
||||
if exceptions.iter().any(|e| e.0 == *rule_kind && e.1 == *rule_id) {
|
||||
continue;
|
||||
/// The command may silently fail because the ruleset may have changed
|
||||
/// between the time the command was created and the time it is applied.
|
||||
pub(crate) fn apply(&mut self, commands: RuleCommands) {
|
||||
for command in commands.commands {
|
||||
match command {
|
||||
Command::DeletePushRule { scope: _, kind, rule_id } => {
|
||||
_ = self.ruleset.remove(kind, rule_id);
|
||||
}
|
||||
Command::SetRoomPushRule { .. } | Command::SetOverridePushRule { .. } => {
|
||||
if let Ok(push_rule) = command.to_push_rule() {
|
||||
_ = self.ruleset.insert(push_rule, None, None);
|
||||
}
|
||||
}
|
||||
Command::SetPushRuleEnabled { scope: _, kind, rule_id, enabled } => {
|
||||
_ = self.ruleset.set_enabled(kind, rule_id, enabled);
|
||||
}
|
||||
}
|
||||
|
||||
self.ruleset.remove(rule_kind.clone(), rule_id)?;
|
||||
commands.push(Command::DeletePushRule {
|
||||
scope: RuleScope::Global,
|
||||
kind: rule_kind.clone(),
|
||||
rule_id: rule_id.clone(),
|
||||
})
|
||||
}
|
||||
Ok(commands)
|
||||
}
|
||||
|
||||
/// Sets whether a rule is enabled and returns a list of `Command`
|
||||
/// describing the actions to be performed on the user's account data.
|
||||
pub(crate) fn set_enabled(
|
||||
&mut self,
|
||||
kind: RuleKind,
|
||||
rule_id: &str,
|
||||
enabled: bool,
|
||||
) -> Result<Vec<Command>, NotificationSettingsError> {
|
||||
if rule_id == PredefinedOverrideRuleId::IsRoomMention.as_str() {
|
||||
// Handle specific case for `PredefinedOverrideRuleId::IsRoomMention`
|
||||
self.set_room_mention_enabled(enabled)
|
||||
} else if rule_id == PredefinedOverrideRuleId::IsUserMention.as_str() {
|
||||
// Handle specific case for `PredefinedOverrideRuleId::IsUserMention`
|
||||
self.set_user_mention_enabled(enabled)
|
||||
} else {
|
||||
let mut commands = vec![];
|
||||
self.set_rule_enabled(kind, rule_id, enabled, &mut commands)?;
|
||||
Ok(commands)
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function to enable or disable a rule and update a list of
|
||||
/// commands to be performed on the user's account data to reflect the
|
||||
/// change.
|
||||
fn set_rule_enabled(
|
||||
&mut self,
|
||||
kind: RuleKind,
|
||||
rule_id: &str,
|
||||
enabled: bool,
|
||||
commands: &mut Vec<Command>,
|
||||
) -> Result<(), NotificationSettingsError> {
|
||||
self.ruleset.set_enabled(kind.clone(), rule_id, enabled)?;
|
||||
commands.push(Command::SetPushRuleEnabled {
|
||||
scope: RuleScope::Global,
|
||||
kind,
|
||||
rule_id: rule_id.to_owned(),
|
||||
enabled,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set whether the `IsUserMention` rule is enabled and returns a list of
|
||||
/// `Command` describing the actions to be performed on the user's account
|
||||
/// data.
|
||||
fn set_user_mention_enabled(
|
||||
&mut self,
|
||||
enabled: bool,
|
||||
) -> Result<Vec<Command>, NotificationSettingsError> {
|
||||
let mut commands = vec![];
|
||||
|
||||
// Sets the `IsUserMention` `Override` rule (MSC3952).
|
||||
// This is a new push rule that may not yet be present.
|
||||
self.set_rule_enabled(
|
||||
RuleKind::Override,
|
||||
PredefinedOverrideRuleId::IsUserMention.as_str(),
|
||||
enabled,
|
||||
&mut commands,
|
||||
)?;
|
||||
|
||||
// For compatibility purpose, we still need to set `ContainsUserName` and
|
||||
// `ContainsDisplayName` (deprecated rules).
|
||||
#[allow(deprecated)]
|
||||
{
|
||||
// `ContainsUserName`
|
||||
self.set_rule_enabled(
|
||||
RuleKind::Content,
|
||||
PredefinedContentRuleId::ContainsUserName.as_str(),
|
||||
enabled,
|
||||
&mut commands,
|
||||
)?;
|
||||
|
||||
// `ContainsDisplayName`
|
||||
self.set_rule_enabled(
|
||||
RuleKind::Override,
|
||||
PredefinedOverrideRuleId::ContainsDisplayName.as_str(),
|
||||
enabled,
|
||||
&mut commands,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(commands)
|
||||
}
|
||||
|
||||
/// Set whether the `IsRoomMention` rule is enabled and returns a list of
|
||||
/// `Command` describing the actions to be performed on the user's account
|
||||
/// data.
|
||||
fn set_room_mention_enabled(
|
||||
&mut self,
|
||||
enabled: bool,
|
||||
) -> Result<Vec<Command>, NotificationSettingsError> {
|
||||
let mut commands = vec![];
|
||||
|
||||
// Sets the `IsRoomMention` `Override` rule (MSC3952).
|
||||
// This is a new push rule that may not yet be present.
|
||||
self.set_rule_enabled(
|
||||
RuleKind::Override,
|
||||
PredefinedOverrideRuleId::IsRoomMention.as_str(),
|
||||
enabled,
|
||||
&mut commands,
|
||||
)?;
|
||||
|
||||
// For compatibility purpose, we still need to set `RoomNotif` (deprecated
|
||||
// rule).
|
||||
#[allow(deprecated)]
|
||||
self.set_rule_enabled(
|
||||
RuleKind::Override,
|
||||
PredefinedOverrideRuleId::RoomNotif.as_str(),
|
||||
enabled,
|
||||
&mut commands,
|
||||
)?;
|
||||
|
||||
Ok(commands)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,19 +241,16 @@ fn get_predefined_underride_room_rule_id(
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use assert_matches::assert_matches;
|
||||
use matrix_sdk_test::async_test;
|
||||
use ruma::{
|
||||
api::client::push::RuleScope,
|
||||
push::{
|
||||
Action, AnyPushRule, NewConditionalPushRule, NewPushRule, NewSimplePushRule,
|
||||
PredefinedContentRuleId, PredefinedOverrideRuleId, PredefinedUnderrideRuleId,
|
||||
PushCondition, RuleKind, Ruleset,
|
||||
Action, NewConditionalPushRule, NewPushRule, PredefinedContentRuleId,
|
||||
PredefinedOverrideRuleId, PredefinedUnderrideRuleId, PushCondition, RuleKind, Ruleset,
|
||||
},
|
||||
OwnedRoomId, RoomId, UserId,
|
||||
};
|
||||
|
||||
use super::Command;
|
||||
use super::RuleCommands;
|
||||
use crate::{
|
||||
error::NotificationSettingsError,
|
||||
notification_settings::{
|
||||
@@ -455,19 +268,39 @@ pub(crate) mod tests {
|
||||
RoomId::parse("!AAAaAAAAAaaAAaaaaa:matrix.org").unwrap()
|
||||
}
|
||||
|
||||
fn build_rules(rule_list: Vec<(RuleKind, &RoomId, bool)>) -> Rules {
|
||||
let mut rules = Rules::new(get_server_default_ruleset());
|
||||
let mut commands = RuleCommands::new(rules.ruleset.clone());
|
||||
for (kind, room_id, notify) in rule_list {
|
||||
commands.insert_rule(kind, room_id, notify).unwrap();
|
||||
}
|
||||
// XXX this should not make use of `apply()`, see other comment. Such a helper
|
||||
// should return a `Ruleset`, and not have to do anything with `Rules`.
|
||||
rules.apply(commands);
|
||||
rules
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_get_custom_rules_for_room() {
|
||||
let room_id = get_test_room_id();
|
||||
|
||||
let mut rules = Rules::new(get_server_default_ruleset());
|
||||
let rules = Rules::new(get_server_default_ruleset());
|
||||
assert_eq!(rules.get_custom_rules_for_room(&room_id).len(), 0);
|
||||
|
||||
// Insert an Override rule
|
||||
rules.insert_room_rule(ruma::push::RuleKind::Override, &room_id, false).unwrap();
|
||||
// Initialize with one rule.
|
||||
// XXX this is not testing things in isolation: `build_rules` makes use of
|
||||
// `apply`, and then we use `get_custom_rules_for_room`. Instead, this
|
||||
// test should create a `Ruleset` by hand, then test single functions in
|
||||
// isolation in it. `build_rules` should not use `Rules` method, since
|
||||
// we're testing `Rules` methods here!
|
||||
let rules = build_rules(vec![(RuleKind::Override, &room_id, false)]);
|
||||
assert_eq!(rules.get_custom_rules_for_room(&room_id).len(), 1);
|
||||
|
||||
// Insert a Room rule
|
||||
rules.insert_room_rule(ruma::push::RuleKind::Room, &room_id, false).unwrap();
|
||||
let rules = build_rules(vec![
|
||||
(RuleKind::Override, &room_id, false),
|
||||
(RuleKind::Room, &room_id, false),
|
||||
]);
|
||||
assert_eq!(rules.get_custom_rules_for_room(&room_id).len(), 2);
|
||||
}
|
||||
|
||||
@@ -490,58 +323,40 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_get_user_defined_room_notification_mode_none() {
|
||||
async fn test_get_user_defined_room_notification_mode() {
|
||||
let room_id = get_test_room_id();
|
||||
let rules = Rules::new(get_server_default_ruleset());
|
||||
assert_eq!(rules.get_user_defined_room_notification_mode(&room_id), None);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_get_user_defined_room_notification_mode_mute() {
|
||||
let room_id = get_test_room_id();
|
||||
let mut rules = Rules::new(get_server_default_ruleset());
|
||||
// Initialize with an `Override` rule that doesn't notify
|
||||
let rules = build_rules(vec![(RuleKind::Override, &room_id, false)]);
|
||||
assert_eq!(
|
||||
rules.get_user_defined_room_notification_mode(&room_id),
|
||||
Some(RoomNotificationMode::Mute)
|
||||
);
|
||||
|
||||
// Insert an Override rule that doesn't notify
|
||||
rules.insert_room_rule(ruma::push::RuleKind::Override, &room_id, false).unwrap();
|
||||
let mode = rules.get_user_defined_room_notification_mode(&room_id);
|
||||
assert_eq!(mode, Some(RoomNotificationMode::Mute));
|
||||
}
|
||||
// Initialize with a `Room` rule that doesn't notify
|
||||
let rules = build_rules(vec![(RuleKind::Room, &room_id, false)]);
|
||||
assert_eq!(
|
||||
rules.get_user_defined_room_notification_mode(&room_id),
|
||||
Some(RoomNotificationMode::MentionsAndKeywordsOnly)
|
||||
);
|
||||
|
||||
#[async_test]
|
||||
async fn test_get_user_defined_room_notification_mode_mentions_and_keywords() {
|
||||
let room_id = get_test_room_id();
|
||||
let mut rules = Rules::new(get_server_default_ruleset());
|
||||
// Initialize with a `Room` rule that doesn't notify
|
||||
let rules = build_rules(vec![(RuleKind::Room, &room_id, true)]);
|
||||
assert_eq!(
|
||||
rules.get_user_defined_room_notification_mode(&room_id),
|
||||
Some(RoomNotificationMode::AllMessages)
|
||||
);
|
||||
|
||||
// Insert a Room rule that doesn't notify
|
||||
rules.insert_room_rule(ruma::push::RuleKind::Room, &room_id, false).unwrap();
|
||||
let mode = rules.get_user_defined_room_notification_mode(&room_id);
|
||||
assert_eq!(mode, Some(RoomNotificationMode::MentionsAndKeywordsOnly));
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_get_user_defined_room_notification_mode_all_messages() {
|
||||
let room_id = get_test_room_id();
|
||||
let mut rules = Rules::new(get_server_default_ruleset());
|
||||
|
||||
// Insert a Room rule that notifies
|
||||
rules.insert_room_rule(ruma::push::RuleKind::Room, &room_id, true).unwrap();
|
||||
let mode = rules.get_user_defined_room_notification_mode(&room_id);
|
||||
assert_eq!(mode, Some(RoomNotificationMode::AllMessages));
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_get_user_defined_room_notification_mode_multiple_override_rules() {
|
||||
let room_id_a = RoomId::parse("!AAAaAAAAAaaAAaaaaa:matrix.org").unwrap();
|
||||
let room_id_b = RoomId::parse("!BBBbBBBBBbbBBbbbbb:matrix.org").unwrap();
|
||||
|
||||
let mut rules = Rules::new(get_server_default_ruleset());
|
||||
|
||||
// Insert the muting rule
|
||||
rules.insert_room_rule(ruma::push::RuleKind::Override, &room_id_a, false).unwrap();
|
||||
// Insert another muting rule for another room (it will be inserted before the
|
||||
// previous one)
|
||||
rules.insert_room_rule(ruma::push::RuleKind::Override, &room_id_b, true).unwrap();
|
||||
|
||||
let rules = build_rules(vec![
|
||||
// A mute rule for room_id_a
|
||||
(RuleKind::Override, &room_id_a, false),
|
||||
// A notifying rule for room_id_b
|
||||
(RuleKind::Override, &room_id_b, true),
|
||||
]);
|
||||
let mode = rules.get_user_defined_room_notification_mode(&room_id_a);
|
||||
|
||||
// The mode should be Mute as there is an Override rule that doesn't notify,
|
||||
@@ -605,6 +420,7 @@ pub(crate) mod tests {
|
||||
ruleset
|
||||
.set_enabled(RuleKind::Override, PredefinedOverrideRuleId::IsUserMention, true)
|
||||
.unwrap();
|
||||
|
||||
let rules = Rules::new(ruleset);
|
||||
assert!(rules.is_user_mention_enabled());
|
||||
// is_enabled() should also return `true` for
|
||||
@@ -676,6 +492,7 @@ pub(crate) mod tests {
|
||||
ruleset
|
||||
.set_enabled(RuleKind::Override, PredefinedOverrideRuleId::IsRoomMention, true)
|
||||
.unwrap();
|
||||
|
||||
let rules = Rules::new(ruleset);
|
||||
assert!(rules.is_room_mention_enabled());
|
||||
// is_enabled() should also return `true` for
|
||||
@@ -700,6 +517,7 @@ pub(crate) mod tests {
|
||||
vec![Action::Notify],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let rules = Rules::new(ruleset);
|
||||
assert!(rules.is_room_mention_enabled());
|
||||
// is_enabled() should also return `true` for
|
||||
@@ -739,351 +557,60 @@ pub(crate) mod tests {
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_insert_room_rule_override() {
|
||||
async fn test_apply_delete_command() {
|
||||
let room_id = get_test_room_id();
|
||||
let mut rules = Rules::new(get_server_default_ruleset());
|
||||
// Initialize with a custom rule
|
||||
let mut rules = build_rules(vec![(RuleKind::Override, &room_id, false)]);
|
||||
|
||||
let command = rules.insert_room_rule(RuleKind::Override, &room_id, true).unwrap();
|
||||
// Build a `RuleCommands` deleting this rule
|
||||
let mut rules_commands = RuleCommands::new(rules.ruleset.clone());
|
||||
rules_commands.delete_rule(RuleKind::Override, room_id.to_string()).unwrap();
|
||||
|
||||
// The ruleset should contains the new rule
|
||||
let new_rule = rules.ruleset.get(RuleKind::Override, &room_id).unwrap();
|
||||
rules.apply(rules_commands);
|
||||
|
||||
assert_matches!(
|
||||
new_rule.to_owned(),
|
||||
AnyPushRule::Override(rule) => {
|
||||
assert_eq!(rule.rule_id, room_id);
|
||||
assert!(rule.conditions.iter().any(|x| matches!(
|
||||
x,
|
||||
PushCondition::EventMatch { key, pattern } if key == "room_id" && *pattern == room_id
|
||||
)))
|
||||
}
|
||||
);
|
||||
|
||||
// The command list should contains only a SetPushRule command
|
||||
assert_matches!(
|
||||
command,
|
||||
Some(Command::SetPushRule { scope, rule }) => {
|
||||
assert_eq!(scope, RuleScope::Global);
|
||||
assert_matches!(
|
||||
rule,
|
||||
NewPushRule::Override(rule) => {
|
||||
assert_eq!(rule.rule_id, room_id);
|
||||
assert!(rule.conditions.iter().any(|x| matches!(
|
||||
x,
|
||||
PushCondition::EventMatch { key, pattern } if key == "room_id" && *pattern == room_id
|
||||
)))
|
||||
}
|
||||
)
|
||||
}
|
||||
);
|
||||
// The rule must have been removed from the updated rules
|
||||
assert!(rules.get_custom_rules_for_room(&room_id).is_empty());
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_insert_room_rule_room() {
|
||||
async fn test_apply_set_command() {
|
||||
let room_id = get_test_room_id();
|
||||
let mut rules = Rules::new(get_server_default_ruleset());
|
||||
let mut rules = build_rules(vec![]);
|
||||
|
||||
let command = rules.insert_room_rule(RuleKind::Room, &room_id, true).unwrap();
|
||||
// Build a `RuleCommands` inserting a rule
|
||||
let mut rules_commands = RuleCommands::new(rules.ruleset.clone());
|
||||
rules_commands.insert_rule(RuleKind::Override, &room_id, false).unwrap();
|
||||
|
||||
// The ruleset should contains the new rule
|
||||
let new_rule =
|
||||
rules.ruleset.get(RuleKind::Room, &room_id).expect("a new Room rule is expected.");
|
||||
rules.apply(rules_commands);
|
||||
|
||||
assert_matches!(
|
||||
new_rule.to_owned(),
|
||||
AnyPushRule::Room(rule) => {
|
||||
assert_eq!(rule.rule_id, room_id);
|
||||
}
|
||||
);
|
||||
|
||||
// The command list should contains only a SetPushRule command
|
||||
assert_matches!(
|
||||
command,
|
||||
Some(Command::SetPushRule { scope, rule }) => {
|
||||
assert_eq!(scope, RuleScope::Global);
|
||||
assert_matches!(
|
||||
rule,
|
||||
NewPushRule::Room(rule) => {
|
||||
assert_eq!(rule.rule_id, room_id);
|
||||
}
|
||||
)
|
||||
}
|
||||
);
|
||||
// The rule must have been removed from the updated rules
|
||||
assert_eq!(rules.get_custom_rules_for_room(&room_id).len(), 1);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_insert_room_rule_invalid_kind() {
|
||||
let room_id = get_test_room_id();
|
||||
let mut rules = Rules::new(get_server_default_ruleset());
|
||||
async fn test_apply_set_enabled_command() {
|
||||
let mut rules = build_rules(vec![]);
|
||||
|
||||
rules
|
||||
.insert_room_rule(RuleKind::Content, &room_id, true)
|
||||
.expect_err("An InvalidParameter error is expected");
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_delete_rules() {
|
||||
let room_id = get_test_room_id();
|
||||
let mut rules = Rules::new(get_server_default_ruleset());
|
||||
|
||||
rules.delete_rules(&[(RuleKind::Room, room_id.to_string())], &[]).expect_err(
|
||||
"A RemovePushRuleError is expected while trying to delete an unknown rule.",
|
||||
);
|
||||
|
||||
let new_rule = NewSimplePushRule::new(room_id.to_owned(), vec![]);
|
||||
let new_rule = NewPushRule::Room(new_rule);
|
||||
rules.ruleset.insert(new_rule, None, None).unwrap();
|
||||
|
||||
let commands = rules.delete_rules(&[(RuleKind::Room, room_id.to_string())], &[]).unwrap();
|
||||
// The command list should contains only a SetPushRule command
|
||||
assert_eq!(commands.len(), 1);
|
||||
|
||||
assert_matches!(
|
||||
&commands[0],
|
||||
Command::DeletePushRule { scope, kind, rule_id } => {
|
||||
assert_eq!(scope, &RuleScope::Global);
|
||||
assert_eq!(kind, &RuleKind::Room);
|
||||
assert_eq!(rule_id, room_id.as_str());
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_delete_rules_with_exceptions() {
|
||||
let room_id = get_test_room_id();
|
||||
let mut rules = Rules::new(get_server_default_ruleset());
|
||||
|
||||
let new_rule = NewSimplePushRule::new(room_id.to_owned(), vec![]);
|
||||
let new_rule = NewPushRule::Room(new_rule);
|
||||
rules.ruleset.insert(new_rule, None, None).unwrap();
|
||||
|
||||
let rules_to_delete = vec![(RuleKind::Room, room_id.to_string())];
|
||||
let commands = rules.delete_rules(&rules_to_delete, &rules_to_delete).unwrap();
|
||||
// The command list should be empty as the rule to delete is also in the
|
||||
// exceptions.
|
||||
assert_eq!(commands.len(), 0);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_set_enabled() {
|
||||
let mut rules = Rules::new(get_server_default_ruleset());
|
||||
|
||||
// Initialize the PredefinedOverrideRuleId::Reaction rule to enabled
|
||||
rules
|
||||
.ruleset
|
||||
.set_enabled(RuleKind::Override, PredefinedOverrideRuleId::Reaction, true)
|
||||
.unwrap();
|
||||
// Ensure the initial state is `true`
|
||||
let initial_state = rules
|
||||
.ruleset
|
||||
.get(RuleKind::Override, PredefinedOverrideRuleId::Reaction)
|
||||
.unwrap()
|
||||
.enabled();
|
||||
assert!(initial_state);
|
||||
// Disable the PredefinedOverrideRuleId::Reaction rule
|
||||
let commands = rules
|
||||
.set_enabled(RuleKind::Override, PredefinedOverrideRuleId::Reaction.as_str(), false)
|
||||
.unwrap();
|
||||
let new_enabled_state = rules
|
||||
.ruleset
|
||||
.get(RuleKind::Override, PredefinedOverrideRuleId::Reaction)
|
||||
.unwrap()
|
||||
.enabled();
|
||||
// The new enabled state should be `false`
|
||||
assert!(!new_enabled_state);
|
||||
|
||||
// The command list should contains only a SetPushRuleEnabled command
|
||||
assert_eq!(commands.len(), 1);
|
||||
|
||||
assert_matches!(
|
||||
&commands[0],
|
||||
Command::SetPushRuleEnabled { scope, kind, rule_id, enabled } => {
|
||||
assert_eq!(scope, &RuleScope::Global);
|
||||
assert_eq!(kind, &RuleKind::Override);
|
||||
assert_eq!(rule_id, &PredefinedOverrideRuleId::Reaction.to_string());
|
||||
assert!(!enabled);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_set_is_room_mention_enabled() {
|
||||
let mut rules = Rules::new(get_server_default_ruleset());
|
||||
|
||||
// Initialize PredefinedOverrideRuleId::IsRoomMention and
|
||||
// PredefinedOverrideRuleId::RoomNotif rules to disabled
|
||||
rules
|
||||
.ruleset
|
||||
.set_enabled(RuleKind::Override, PredefinedOverrideRuleId::IsRoomMention, false)
|
||||
.unwrap();
|
||||
#[allow(deprecated)]
|
||||
rules
|
||||
.ruleset
|
||||
.set_enabled(RuleKind::Override, PredefinedOverrideRuleId::RoomNotif, false)
|
||||
// Build a `RuleCommands` disabling the rule
|
||||
let mut rules_commands = RuleCommands::new(rules.ruleset.clone());
|
||||
rules_commands
|
||||
.set_rule_enabled(
|
||||
RuleKind::Override,
|
||||
PredefinedOverrideRuleId::Reaction.as_str(),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Ensure the initial state is `false`
|
||||
let is_room_mention_enabled = rules
|
||||
.ruleset
|
||||
.get(RuleKind::Override, PredefinedOverrideRuleId::IsRoomMention)
|
||||
.unwrap()
|
||||
.enabled();
|
||||
assert!(!is_room_mention_enabled);
|
||||
#[allow(deprecated)]
|
||||
let room_notif_enabled = rules
|
||||
.ruleset
|
||||
.get(RuleKind::Override, PredefinedOverrideRuleId::RoomNotif)
|
||||
.unwrap()
|
||||
.enabled();
|
||||
assert!(!room_notif_enabled);
|
||||
rules.apply(rules_commands);
|
||||
|
||||
// Enable the PredefinedOverrideRuleId::IsRoomMention rule
|
||||
let commands = rules
|
||||
.set_enabled(RuleKind::Override, PredefinedOverrideRuleId::IsRoomMention.as_str(), true)
|
||||
.unwrap();
|
||||
|
||||
// Ensure the new state is `true` for both rules
|
||||
let is_room_mention_enabled = rules
|
||||
.ruleset
|
||||
.get(RuleKind::Override, PredefinedOverrideRuleId::IsRoomMention)
|
||||
.unwrap()
|
||||
.enabled();
|
||||
assert!(is_room_mention_enabled);
|
||||
#[allow(deprecated)]
|
||||
let room_notif_enabled = rules
|
||||
.ruleset
|
||||
.get(RuleKind::Override, PredefinedOverrideRuleId::RoomNotif)
|
||||
.unwrap()
|
||||
.enabled();
|
||||
assert!(room_notif_enabled);
|
||||
|
||||
// The command list should contains two SetPushRuleEnabled
|
||||
assert_eq!(commands.len(), 2);
|
||||
assert_matches!(
|
||||
&commands[0],
|
||||
Command::SetPushRuleEnabled { scope, kind, rule_id, enabled } => {
|
||||
assert_eq!(scope, &RuleScope::Global);
|
||||
assert_eq!(kind, &RuleKind::Override);
|
||||
assert_eq!(rule_id, &PredefinedOverrideRuleId::IsRoomMention.to_string());
|
||||
assert!(enabled);
|
||||
}
|
||||
);
|
||||
|
||||
assert_matches!(
|
||||
&commands[1],
|
||||
Command::SetPushRuleEnabled { scope, kind, rule_id, enabled } => {
|
||||
assert_eq!(scope, &RuleScope::Global);
|
||||
assert_eq!(kind, &RuleKind::Override);
|
||||
#[allow(deprecated)]
|
||||
let expected_rule_id = PredefinedOverrideRuleId::RoomNotif;
|
||||
assert_eq!(rule_id, &expected_rule_id.to_string());
|
||||
assert!(enabled);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[async_test]
|
||||
async fn test_set_is_user_mention_enabled() {
|
||||
let mut rules = Rules::new(get_server_default_ruleset());
|
||||
|
||||
// Initialize PredefinedOverrideRuleId::IsRoomMention and
|
||||
// PredefinedOverrideRuleId::RoomNotif rules to disabled
|
||||
rules
|
||||
.ruleset
|
||||
.set_enabled(RuleKind::Override, PredefinedOverrideRuleId::IsUserMention, false)
|
||||
.unwrap();
|
||||
#[allow(deprecated)]
|
||||
rules
|
||||
.ruleset
|
||||
.set_enabled(RuleKind::Override, PredefinedOverrideRuleId::ContainsDisplayName, false)
|
||||
.unwrap();
|
||||
#[allow(deprecated)]
|
||||
rules
|
||||
.ruleset
|
||||
.set_enabled(RuleKind::Content, PredefinedContentRuleId::ContainsUserName, false)
|
||||
.unwrap();
|
||||
|
||||
// Ensure the initial state is `false`
|
||||
let is_user_mention_enabled = rules
|
||||
.ruleset
|
||||
.get(RuleKind::Override, PredefinedOverrideRuleId::IsUserMention)
|
||||
.unwrap()
|
||||
.enabled();
|
||||
assert!(!is_user_mention_enabled);
|
||||
#[allow(deprecated)]
|
||||
let contains_display_name_enabled = rules
|
||||
.ruleset
|
||||
.get(RuleKind::Override, PredefinedOverrideRuleId::ContainsDisplayName)
|
||||
.unwrap()
|
||||
.enabled();
|
||||
assert!(!contains_display_name_enabled);
|
||||
#[allow(deprecated)]
|
||||
let contains_user_name_enabled = rules
|
||||
.ruleset
|
||||
.get(RuleKind::Content, PredefinedContentRuleId::ContainsUserName)
|
||||
.unwrap()
|
||||
.enabled();
|
||||
assert!(!contains_user_name_enabled);
|
||||
|
||||
// Enable the PredefinedOverrideRuleId::IsUserMention rule
|
||||
let commands = rules
|
||||
.set_enabled(RuleKind::Override, PredefinedOverrideRuleId::IsUserMention.as_str(), true)
|
||||
.unwrap();
|
||||
|
||||
// Ensure the new state is `true` for all corresponding rules
|
||||
let is_user_mention_enabled = rules
|
||||
.ruleset
|
||||
.get(RuleKind::Override, PredefinedOverrideRuleId::IsUserMention)
|
||||
.unwrap()
|
||||
.enabled();
|
||||
assert!(is_user_mention_enabled);
|
||||
#[allow(deprecated)]
|
||||
let contains_display_name_enabled = rules
|
||||
.ruleset
|
||||
.get(RuleKind::Override, PredefinedOverrideRuleId::ContainsDisplayName)
|
||||
.unwrap()
|
||||
.enabled();
|
||||
assert!(contains_display_name_enabled);
|
||||
#[allow(deprecated)]
|
||||
let contains_user_name_enabled = rules
|
||||
.ruleset
|
||||
.get(RuleKind::Content, PredefinedContentRuleId::ContainsUserName)
|
||||
.unwrap()
|
||||
.enabled();
|
||||
assert!(contains_user_name_enabled);
|
||||
|
||||
// The command list should contains 3 SetPushRuleEnabled
|
||||
assert_eq!(commands.len(), 3);
|
||||
assert_matches!(
|
||||
&commands[0],
|
||||
Command::SetPushRuleEnabled { scope, kind, rule_id, enabled } => {
|
||||
assert_eq!(scope, &RuleScope::Global);
|
||||
assert_eq!(kind, &RuleKind::Override);
|
||||
assert_eq!(rule_id, &PredefinedOverrideRuleId::IsUserMention.to_string());
|
||||
assert!(enabled);
|
||||
}
|
||||
);
|
||||
assert_matches!(
|
||||
&commands[1],
|
||||
Command::SetPushRuleEnabled { scope, kind, rule_id, enabled } => {
|
||||
assert_eq!(scope, &RuleScope::Global);
|
||||
assert_eq!(kind, &RuleKind::Content);
|
||||
#[allow(deprecated)]
|
||||
let expected_rule_id = PredefinedContentRuleId::ContainsUserName;
|
||||
assert_eq!(rule_id, &expected_rule_id.to_string());
|
||||
assert!(enabled);
|
||||
}
|
||||
);
|
||||
assert_matches!(
|
||||
&commands[2],
|
||||
Command::SetPushRuleEnabled { scope, kind, rule_id, enabled } => {
|
||||
assert_eq!(scope, &RuleScope::Global);
|
||||
assert_eq!(kind, &RuleKind::Override);
|
||||
#[allow(deprecated)]
|
||||
let expected_rule_id = PredefinedOverrideRuleId::ContainsDisplayName;
|
||||
assert_eq!(rule_id, expected_rule_id.as_str());
|
||||
assert!(enabled);
|
||||
}
|
||||
);
|
||||
// The rule must have been disabled in the updated rules
|
||||
assert!(!rules
|
||||
.is_enabled(RuleKind::Override, PredefinedOverrideRuleId::Reaction.as_str())
|
||||
.unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user