Feature: add API for setting underride push rule's actions (#2644)

This commit is contained in:
Alfonso Grillo
2023-10-03 10:08:17 +02:00
committed by GitHub
parent 7e8827aec2
commit 08b3c0e47e
6 changed files with 126 additions and 21 deletions

View File

@@ -147,7 +147,7 @@ pub enum NotificationSettingsError {
InvalidRoomId(String),
/// Rule not found
#[error("Rule not found")]
RuleNotFound,
RuleNotFound(String),
/// Unable to add push rule.
#[error("Unable to add push rule")]
UnableToAddPushRule,
@@ -165,7 +165,7 @@ pub enum NotificationSettingsError {
impl From<SdkNotificationSettingsError> for NotificationSettingsError {
fn from(value: SdkNotificationSettingsError) -> Self {
match value {
SdkNotificationSettingsError::RuleNotFound => Self::RuleNotFound,
SdkNotificationSettingsError::RuleNotFound(rule_id) => Self::RuleNotFound(rule_id),
SdkNotificationSettingsError::UnableToAddPushRule => Self::UnableToAddPushRule,
SdkNotificationSettingsError::UnableToRemovePushRule => Self::UnableToRemovePushRule,
SdkNotificationSettingsError::UnableToSavePushRules => Self::UnableToSavePushRules,

View File

@@ -88,7 +88,7 @@ matrix-sdk-sqlite = { version = "0.1.0", path = "../matrix-sdk-sqlite", default-
mime = "0.3.16"
mime2ext = "0.1.52"
rand = { version = "0.8.5", optional = true }
ruma = { workspace = true, features = ["rand", "unstable-msc2448", "unstable-msc2965"] }
ruma = { workspace = true, features = ["rand", "unstable-msc2448", "unstable-msc2965", "unstable-msc3930"] }
serde = { workspace = true }
serde_html_form = { workspace = true }
serde_json = { workspace = true }

View File

@@ -34,7 +34,7 @@ use ruma::{
error::{FromHttpResponseError, IntoHttpError},
},
events::tag::InvalidUserTagName,
push::{InsertPushRuleError, RemovePushRuleError, RuleNotFoundError},
push::{InsertPushRuleError, RemovePushRuleError},
IdParseError,
};
use serde_json::Error as JsonError;
@@ -453,7 +453,7 @@ pub enum NotificationSettingsError {
UnableToUpdatePushRule,
/// Rule not found
#[error("Rule not found")]
RuleNotFound,
RuleNotFound(String),
/// Unable to save the push rules
#[error("Unable to save push rules")]
UnableToSavePushRules,
@@ -471,12 +471,6 @@ impl From<RemovePushRuleError> for NotificationSettingsError {
}
}
impl From<RuleNotFoundError> for NotificationSettingsError {
fn from(_: RuleNotFoundError) -> Self {
Self::RuleNotFound
}
}
#[derive(Debug, Error)]
#[error("expected: {expected}, got: {got:?}")]
pub struct WrongRoomState {

View File

@@ -7,7 +7,7 @@ use ruma::{
delete_pushrule, set_pushrule, set_pushrule_actions, set_pushrule_enabled,
},
events::push_rules::PushRulesEvent,
push::{Action, RuleKind, Ruleset, Tweak},
push::{Action, PredefinedUnderrideRuleId, RuleKind, Ruleset, Tweak},
RoomId,
};
use tokio::sync::RwLock;
@@ -54,7 +54,7 @@ impl From<bool> for IsEncrypted {
}
/// Whether or not a room is a `one-to-one`
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum IsOneToOne {
/// A room is a `one-to-one` room if it has exactly two members.
Yes,
@@ -186,9 +186,10 @@ impl NotificationSettings {
is_one_to_one: IsOneToOne,
mode: RoomNotificationMode,
) -> Result<(), NotificationSettingsError> {
let rules = self.rules.read().await.clone();
let rule_id = rules::get_predefined_underride_room_rule_id(is_encrypted, is_one_to_one);
let mut rule_commands = RuleCommands::new(rules.ruleset);
let rule_ids = vec![
rules::get_predefined_underride_room_rule_id(is_encrypted, is_one_to_one.clone()),
is_one_to_one.into(),
];
let actions = match mode {
RoomNotificationMode::AllMessages => {
@@ -199,6 +200,28 @@ impl NotificationSettings {
}
};
for rule_id in rule_ids {
self.set_underride_push_rule_actions(rule_id, actions.clone()).await?
}
Ok(())
}
/// Sets the push rule actions for a given underride push rule.
/// [Underride rules](https://spec.matrix.org/v1.8/client-server-api/#push-rules) are the lowest priority push rules
///
/// # Arguments
///
/// * `rule_id` - the identifier of the push rule
/// * `actions` - the actions to set for the push rule
pub async fn set_underride_push_rule_actions(
&self,
rule_id: PredefinedUnderrideRuleId,
actions: Vec<Action>,
) -> Result<(), NotificationSettingsError> {
let rules = self.rules.read().await.clone();
let mut rule_commands = RuleCommands::new(rules.ruleset);
rule_commands.set_rule_actions(RuleKind::Underride, rule_id.as_str(), actions)?;
self.run_server_commands(&rule_commands).await?;
@@ -926,6 +949,67 @@ mod tests {
Mock::given(method("PUT")).respond_with(ResponseTemplate::new(200)).mount(&server).await;
let client = logged_in_client(Some(server.uri())).await;
// If the initial mode is `AllMessages`
let mut ruleset = get_server_default_ruleset();
ruleset
.set_actions(
RuleKind::Underride,
PredefinedUnderrideRuleId::Message,
vec![Action::Notify],
)
.unwrap();
ruleset
.set_actions(
RuleKind::Underride,
PredefinedUnderrideRuleId::PollStart,
vec![Action::Notify],
)
.unwrap();
let settings = NotificationSettings::new(client, ruleset);
assert_eq!(
settings.get_default_room_notification_mode(IsEncrypted::No, IsOneToOne::No).await,
RoomNotificationMode::AllMessages
);
// After setting the default mode to `MentionsAndKeywordsOnly`
settings
.set_default_room_notification_mode(
IsEncrypted::No,
IsOneToOne::No,
RoomNotificationMode::MentionsAndKeywordsOnly,
)
.await
.unwrap();
// The list of actions for this rule must be empty
assert_matches!(settings.rules.read().await.ruleset.get(RuleKind::Underride, PredefinedUnderrideRuleId::Message),
Some(AnyPushRuleRef::Underride(rule)) => {
assert!(rule.actions.is_empty());
}
);
assert_matches!(settings.rules.read().await.ruleset.get(RuleKind::Underride, PredefinedUnderrideRuleId::PollStart),
Some(AnyPushRuleRef::Underride(rule)) => {
assert!(rule.actions.is_empty());
}
);
// and the new mode returned by `get_default_room_notification_mode()` should
// reflect the change.
assert_matches!(
settings.get_default_room_notification_mode(IsEncrypted::No, IsOneToOne::No).await,
RoomNotificationMode::MentionsAndKeywordsOnly
);
}
#[async_test]
async fn test_set_default_room_notification_mode_one_to_one() {
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;
// If the initial mode is `AllMessages`
let mut ruleset = get_server_default_ruleset();
ruleset
@@ -936,6 +1020,14 @@ mod tests {
)
.unwrap();
ruleset
.set_actions(
RuleKind::Underride,
PredefinedUnderrideRuleId::PollStartOneToOne,
vec![Action::Notify],
)
.unwrap();
let settings = NotificationSettings::new(client, ruleset);
assert_eq!(
settings.get_default_room_notification_mode(IsEncrypted::No, IsOneToOne::Yes).await,
@@ -959,6 +1051,12 @@ mod tests {
}
);
assert_matches!(settings.rules.read().await.ruleset.get(RuleKind::Underride, PredefinedUnderrideRuleId::PollStartOneToOne),
Some(AnyPushRuleRef::Underride(rule)) => {
assert!(rule.actions.is_empty());
}
);
// and the new mode returned by `get_default_room_notification_mode()` should
// reflect the change.
assert_matches!(

View File

@@ -73,7 +73,9 @@ impl RuleCommands {
rule_id: &str,
enabled: bool,
) -> Result<(), NotificationSettingsError> {
self.rules.set_enabled(kind.clone(), rule_id, enabled)?;
self.rules
.set_enabled(kind.clone(), rule_id, enabled)
.map_err(|_| NotificationSettingsError::RuleNotFound(rule_id.to_owned()))?;
self.commands.push(Command::SetPushRuleEnabled {
scope: RuleScope::Global,
kind,
@@ -163,7 +165,9 @@ impl RuleCommands {
rule_id: &str,
actions: Vec<Action>,
) -> Result<(), NotificationSettingsError> {
self.rules.set_actions(kind.clone(), rule_id, actions.clone())?;
self.rules
.set_actions(kind.clone(), rule_id, actions.clone())
.map_err(|_| NotificationSettingsError::RuleNotFound(rule_id.to_owned()))?;
self.commands.push(Command::SetPushRuleActions {
scope: RuleScope::Global,
kind,
@@ -349,7 +353,7 @@ mod tests {
let mut rule_commands = RuleCommands::new(ruleset);
assert_eq!(
rule_commands.set_rule_enabled(RuleKind::Room, "unknown_rule_id", true),
Err(NotificationSettingsError::RuleNotFound)
Err(NotificationSettingsError::RuleNotFound("unknown_rule_id".to_string()))
);
}

View File

@@ -227,7 +227,7 @@ impl Rules {
} else if let Some(rule) = self.ruleset.get(kind, rule_id) {
Ok(rule.enabled())
} else {
Err(NotificationSettingsError::RuleNotFound)
Err(NotificationSettingsError::RuleNotFound(rule_id.to_owned()))
}
}
@@ -276,6 +276,15 @@ pub(crate) fn get_predefined_underride_room_rule_id(
}
}
impl From<IsOneToOne> for PredefinedUnderrideRuleId {
fn from(is_one_to_one: IsOneToOne) -> Self {
match is_one_to_one {
IsOneToOne::Yes => Self::PollStartOneToOne,
IsOneToOne::No => Self::PollStart,
}
}
}
#[cfg(test)]
pub(crate) mod tests {
use imbl::HashSet;
@@ -539,7 +548,7 @@ pub(crate) mod tests {
assert_eq!(
rules.is_enabled(RuleKind::Override, "unknown_rule_id"),
Err(NotificationSettingsError::RuleNotFound)
Err(NotificationSettingsError::RuleNotFound("unknown_rule_id".to_string()))
);
}