Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Commit

Permalink
Merge pull request #609 from matrix-org/markjh/change_action
Browse files Browse the repository at this point in the history
Add support for changing the actions for default rules

See matrix-org/matrix-spec-proposals#283

Works by adding dummy rules to the push rules table with a negative priority class and then using those rules to clobber the default rule actions when adding the default rules in ``list_with_base_rules``
  • Loading branch information
NegativeMjark committed Feb 26, 2016
2 parents 9329cd5 + de27f7f commit 4bf13a8
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 11 deletions.
57 changes: 50 additions & 7 deletions synapse/push/baserules.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,46 +13,67 @@
# limitations under the License.

from synapse.push.rulekinds import PRIORITY_CLASS_MAP, PRIORITY_CLASS_INVERSE_MAP
import copy


def list_with_base_rules(rawrules):
"""Combine the list of rules set by the user with the default push rules
:param list rawrules: The rules the user has modified or set.
:returns: A new list with the rules set by the user combined with the
defaults.
"""
ruleslist = []

# Grab the base rules that the user has modified.
# The modified base rules have a priority_class of -1.
modified_base_rules = {
r['rule_id']: r for r in rawrules if r['priority_class'] < 0
}

# Remove the modified base rules from the list, They'll be added back
# in the default postions in the list.
rawrules = [r for r in rawrules if r['priority_class'] >= 0]

# shove the server default rules for each kind onto the end of each
current_prio_class = PRIORITY_CLASS_INVERSE_MAP.keys()[-1]

ruleslist.extend(make_base_prepend_rules(
PRIORITY_CLASS_INVERSE_MAP[current_prio_class]
PRIORITY_CLASS_INVERSE_MAP[current_prio_class], modified_base_rules
))

for r in rawrules:
if r['priority_class'] < current_prio_class:
while r['priority_class'] < current_prio_class:
ruleslist.extend(make_base_append_rules(
PRIORITY_CLASS_INVERSE_MAP[current_prio_class]
PRIORITY_CLASS_INVERSE_MAP[current_prio_class],
modified_base_rules,
))
current_prio_class -= 1
if current_prio_class > 0:
ruleslist.extend(make_base_prepend_rules(
PRIORITY_CLASS_INVERSE_MAP[current_prio_class]
PRIORITY_CLASS_INVERSE_MAP[current_prio_class],
modified_base_rules,
))

ruleslist.append(r)

while current_prio_class > 0:
ruleslist.extend(make_base_append_rules(
PRIORITY_CLASS_INVERSE_MAP[current_prio_class]
PRIORITY_CLASS_INVERSE_MAP[current_prio_class],
modified_base_rules,
))
current_prio_class -= 1
if current_prio_class > 0:
ruleslist.extend(make_base_prepend_rules(
PRIORITY_CLASS_INVERSE_MAP[current_prio_class]
PRIORITY_CLASS_INVERSE_MAP[current_prio_class],
modified_base_rules,
))

return ruleslist


def make_base_append_rules(kind):
def make_base_append_rules(kind, modified_base_rules):
rules = []

if kind == 'override':
Expand All @@ -62,15 +83,31 @@ def make_base_append_rules(kind):
elif kind == 'content':
rules = BASE_APPEND_CONTENT_RULES

# Copy the rules before modifying them
rules = copy.deepcopy(rules)
for r in rules:
# Only modify the actions, keep the conditions the same.
modified = modified_base_rules.get(r['rule_id'])
if modified:
r['actions'] = modified['actions']

return rules


def make_base_prepend_rules(kind):
def make_base_prepend_rules(kind, modified_base_rules):
rules = []

if kind == 'override':
rules = BASE_PREPEND_OVERRIDE_RULES

# Copy the rules before modifying them
rules = copy.deepcopy(rules)
for r in rules:
# Only modify the actions, keep the conditions the same.
modified = modified_base_rules.get(r['rule_id'])
if modified:
r['actions'] = modified['actions']

return rules


Expand Down Expand Up @@ -263,18 +300,24 @@ def make_base_prepend_rules(kind):
]


BASE_RULE_IDS = set()

for r in BASE_APPEND_CONTENT_RULES:
r['priority_class'] = PRIORITY_CLASS_MAP['content']
r['default'] = True
BASE_RULE_IDS.add(r['rule_id'])

for r in BASE_PREPEND_OVERRIDE_RULES:
r['priority_class'] = PRIORITY_CLASS_MAP['override']
r['default'] = True
BASE_RULE_IDS.add(r['rule_id'])

for r in BASE_APPEND_OVRRIDE_RULES:
r['priority_class'] = PRIORITY_CLASS_MAP['override']
r['default'] = True
BASE_RULE_IDS.add(r['rule_id'])

for r in BASE_APPEND_UNDERRIDE_RULES:
r['priority_class'] = PRIORITY_CLASS_MAP['underride']
r['default'] = True
BASE_RULE_IDS.add(r['rule_id'])
31 changes: 27 additions & 4 deletions synapse/rest/client/v1/push_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from synapse.storage.push_rule import (
InconsistentRuleException, RuleNotFoundException
)
import synapse.push.baserules as baserules
from synapse.push.baserules import list_with_base_rules, BASE_RULE_IDS
from synapse.push.rulekinds import (
PRIORITY_CLASS_MAP, PRIORITY_CLASS_INVERSE_MAP
)
Expand Down Expand Up @@ -55,6 +55,10 @@ def on_PUT(self, request):
yield self.set_rule_attr(requester.user.to_string(), spec, content)
defer.returnValue((200, {}))

if spec['rule_id'].startswith('.'):
# Rule ids starting with '.' are reserved for server default rules.
raise SynapseError(400, "cannot add new rule_ids that start with '.'")

try:
(conditions, actions) = _rule_tuple_from_request_object(
spec['template'],
Expand Down Expand Up @@ -128,7 +132,7 @@ def on_GET(self, request):
ruleslist.append(rule)

# We're going to be mutating this a lot, so do a deep copy
ruleslist = copy.deepcopy(baserules.list_with_base_rules(ruleslist))
ruleslist = copy.deepcopy(list_with_base_rules(ruleslist))

rules = {'global': {}, 'device': {}}

Expand Down Expand Up @@ -197,6 +201,18 @@ def set_rule_attr(self, user_id, spec, val):
return self.hs.get_datastore().set_push_rule_enabled(
user_id, namespaced_rule_id, val
)
elif spec['attr'] == 'actions':
actions = val.get('actions')
_check_actions(actions)
namespaced_rule_id = _namespaced_rule_id_from_spec(spec)
rule_id = spec['rule_id']
is_default_rule = rule_id.startswith(".")
if is_default_rule:
if namespaced_rule_id not in BASE_RULE_IDS:
raise SynapseError(404, "Unknown rule %r" % (namespaced_rule_id,))
return self.hs.get_datastore().set_push_rule_actions(
user_id, namespaced_rule_id, actions, is_default_rule
)
else:
raise UnrecognizedRequestError()

Expand Down Expand Up @@ -274,6 +290,15 @@ def _rule_tuple_from_request_object(rule_template, rule_id, req_obj):
raise InvalidRuleException("No actions found")
actions = req_obj['actions']

_check_actions(actions)

return conditions, actions


def _check_actions(actions):
if not isinstance(actions, list):
raise InvalidRuleException("No actions found")

for a in actions:
if a in ['notify', 'dont_notify', 'coalesce']:
pass
Expand All @@ -282,8 +307,6 @@ def _rule_tuple_from_request_object(rule_template, rule_id, req_obj):
else:
raise InvalidRuleException("Unrecognised action")

return conditions, actions


def _add_empty_priority_class_arrays(d):
for pc in PRIORITY_CLASS_MAP.keys():
Expand Down
25 changes: 25 additions & 0 deletions synapse/storage/push_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,31 @@ def _set_push_rule_enabled_txn(self, txn, user_id, rule_id, enabled):
self.get_push_rules_enabled_for_user.invalidate, (user_id,)
)

def set_push_rule_actions(self, user_id, rule_id, actions, is_default_rule):
actions_json = json.dumps(actions)

def set_push_rule_actions_txn(txn):
if is_default_rule:
# Add a dummy rule to the rules table with the user specified
# actions.
priority_class = -1
priority = 1
self._upsert_push_rule_txn(
txn, user_id, rule_id, priority_class, priority,
"[]", actions_json
)
else:
self._simple_update_one_txn(
txn,
"push_rules",
{'user_name': user_id, 'rule_id': rule_id},
{'actions': actions_json},
)

return self.runInteraction(
"set_push_rule_actions", set_push_rule_actions_txn,
)


class RuleNotFoundException(Exception):
pass
Expand Down

0 comments on commit 4bf13a8

Please sign in to comment.