As yet fairly untested GET API for push rules

This commit is contained in:
David Baker 2015-01-22 19:32:17 +00:00
parent 673773b217
commit 8a850573c9
3 changed files with 145 additions and 15 deletions

View File

@ -87,13 +87,25 @@ class UnrecognizedRequestError(SynapseError):
"""An error indicating we don't understand the request you're trying to make""" """An error indicating we don't understand the request you're trying to make"""
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
if "errcode" not in kwargs: if "errcode" not in kwargs:
kwargs["errcode"] = Codes.NOT_FOUND kwargs["errcode"] = Codes.UNRECOGNIZED
super(UnrecognizedRequestError, self).__init__( super(UnrecognizedRequestError, self).__init__(
400, 400,
"Unrecognized request", "Unrecognized request",
**kwargs **kwargs
) )
class NotFoundError(SynapseError):
"""An error indicating we can't find the thing you asked for"""
def __init__(self, *args, **kwargs):
if "errcode" not in kwargs:
kwargs["errcode"] = Codes.NOT_FOUND
super(UnrecognizedRequestError, self).__init__(
404,
"Not found",
**kwargs
)
class AuthError(SynapseError): class AuthError(SynapseError):
"""An error raised when there was a problem authorising an event.""" """An error raised when there was a problem authorising an event."""

View File

@ -15,7 +15,7 @@
from twisted.internet import defer from twisted.internet import defer
from synapse.api.errors import SynapseError, Codes, UnrecognizedRequestError from synapse.api.errors import SynapseError, Codes, UnrecognizedRequestError, NotFoundError
from base import RestServlet, client_path_pattern from base import RestServlet, client_path_pattern
from synapse.storage.push_rule import InconsistentRuleException, RuleNotFoundException from synapse.storage.push_rule import InconsistentRuleException, RuleNotFoundException
@ -24,6 +24,14 @@ import json
class PushRuleRestServlet(RestServlet): class PushRuleRestServlet(RestServlet):
PATTERN = client_path_pattern("/pushrules/.*$") PATTERN = client_path_pattern("/pushrules/.*$")
PRIORITY_CLASS_MAP = {
'underride': 0,
'sender': 1,
'room': 2,
'content': 3,
'override': 4
}
PRIORITY_CLASS_INVERSE_MAP = {v: k for k,v in PRIORITY_CLASS_MAP.items()}
def rule_spec_from_path(self, path): def rule_spec_from_path(self, path):
if len(path) < 2: if len(path) < 2:
@ -109,15 +117,7 @@ class PushRuleRestServlet(RestServlet):
return (conditions, actions) return (conditions, actions)
def priority_class_from_spec(self, spec): def priority_class_from_spec(self, spec):
map = { if spec['template'] not in PushRuleRestServlet.PRIORITY_CLASS_MAP.keys():
'underride': 0,
'sender': 1,
'room': 2,
'content': 3,
'override': 4
}
if spec['template'] not in map.keys():
raise InvalidRuleException("Unknown template: %s" % (spec['kind'])) raise InvalidRuleException("Unknown template: %s" % (spec['kind']))
pc = map[spec['template']] pc = map[spec['template']]
@ -171,10 +171,128 @@ class PushRuleRestServlet(RestServlet):
defer.returnValue((200, {})) defer.returnValue((200, {}))
@defer.inlineCallbacks
def on_GET(self, request):
user = yield self.auth.get_user_by_req(request)
# we build up the full structure and then decide which bits of it
# to send which means doing unnecessary work sometimes but is
# is probably not going to make a whole lot of difference
rawrules = yield self.hs.get_datastore().get_push_rules_for_user_name(user.to_string())
rules = {'global': {}, 'device': {}}
rules['global'] = _add_empty_priority_class_arrays(rules['global'])
for r in rawrules:
rulearray = None
r["conditions"] = json.loads(r["conditions"])
r["actions"] = json.loads(r["actions"])
template_name = _priority_class_to_template_name(r['priority_class'])
if r['priority_class'] > PushRuleRestServlet.PRIORITY_CLASS_MAP['override']:
# per-device rule
instance_handle = _instance_handle_from_conditions(r["conditions"])
if not instance_handle:
continue
if instance_handle not in rules['device']:
rules['device'][instance_handle] = []
rules['device'][instance_handle] = \
_add_empty_priority_class_arrays(rules['device'][instance_handle])
rulearray = rules['device'][instance_handle]
else:
rulearray = rules['global'][template_name]
template_rule = _rule_to_template(r)
if template_rule:
rulearray.append(template_rule)
path = request.postpath[1:]
if path == []:
defer.returnValue((200, rules))
if path[0] == 'global':
path = path[1:]
result = _filter_ruleset_with_path(rules['global'], path)
defer.returnValue((200, result))
elif path[0] == 'device':
path = path[1:]
if path == []:
raise UnrecognizedRequestError
instance_handle = path[0]
if instance_handle not in rules['device']:
ret = {}
ret = _add_empty_priority_class_arrays(ret)
defer.returnValue((200, ret))
ruleset = rules['device'][instance_handle]
result = _filter_ruleset_with_path(ruleset, path)
defer.returnValue((200, result))
else:
raise UnrecognizedRequestError()
def on_OPTIONS(self, _): def on_OPTIONS(self, _):
return 200, {} return 200, {}
def _add_empty_priority_class_arrays(d):
for pc in PushRuleRestServlet.PRIORITY_CLASS_MAP.keys():
d[pc] = []
return d
def _instance_handle_from_conditions(conditions):
"""
Given a list of conditions, return the instance handle of the
device rule if there is one
"""
for c in conditions:
if c['kind'] == 'device':
return c['instance_handle']
return None
def _filter_ruleset_with_path(ruleset, path):
if path == []:
return ruleset
template_kind = path[0]
if template_kind not in ruleset:
raise UnrecognizedRequestError()
path = path[1:]
if path == []:
return ruleset[template_kind]
rule_id = path[0]
for r in ruleset[template_kind]:
if r['rule_id'] == rule_id:
return r
raise NotFoundError
def _priority_class_to_template_name(pc):
if pc > PushRuleRestServlet.PRIORITY_CLASS_MAP['override']:
# per-device
prio_class_index = pc - PushRuleRestServlet.PRIORITY_CLASS_MAP['override']
return PushRuleRestServlet.PRIORITY_CLASS_INVERSE_MAP[prio_class_index]
else:
return PushRuleRestServlet.PRIORITY_CLASS_INVERSE_MAP[pc]
def _rule_to_template(rule):
template_name = _priority_class_to_template_name(rule['priority_class'])
if template_name in ['override', 'underride']:
return {k:rule[k] for k in ["rule_id", "conditions", "actions"]}
elif template_name in ["sender", "room"]:
return {k:rule[k] for k in ["rule_id", "actions"]}
elif template_name == 'content':
if len(rule["conditions"]) != 1:
return None
thecond = rule["conditions"][0]
if "pattern" not in thecond:
return None
ret = {k:rule[k] for k in ["rule_id", "actions"]}
ret["pattern"] = thecond["pattern"]
return ret
class InvalidRuleException(Exception): class InvalidRuleException(Exception):
pass pass

View File

@ -29,11 +29,11 @@ class PushRuleStore(SQLBaseStore):
@defer.inlineCallbacks @defer.inlineCallbacks
def get_push_rules_for_user_name(self, user_name): def get_push_rules_for_user_name(self, user_name):
sql = ( sql = (
"SELECT "+",".join(PushRuleTable.fields)+ "SELECT "+",".join(PushRuleTable.fields)+" "
"FROM pushers " "FROM "+PushRuleTable.table_name+" "
"WHERE user_name = ?" "WHERE user_name = ? "
"ORDER BY priority_class DESC, priority DESC"
) )
rows = yield self._execute(None, sql, user_name) rows = yield self._execute(None, sql, user_name)
dicts = [] dicts = []