summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2017-10-12 12:30:17 +0300
committerLars Wirzenius <liw@liw.fi>2017-10-12 12:30:17 +0300
commit5d2e5bf12d93954f512cf34a6f4dcaa4146f2a6c (patch)
tree76d8a155354a0693b81d908e5badb9b6a5101ef3
parentb15e089854b5015ac01e41c41365df8cbbc98c00 (diff)
downloadqvisqve-5d2e5bf12d93954f512cf34a6f4dcaa4146f2a6c.tar.gz
Refactor: add SubresourceRouter
-rw-r--r--qvarn/api.py59
-rw-r--r--qvarn/subresource_router.py86
2 files changed, 100 insertions, 45 deletions
diff --git a/qvarn/api.py b/qvarn/api.py
index 639a554..e88417a 100644
--- a/qvarn/api.py
+++ b/qvarn/api.py
@@ -136,14 +136,14 @@ class QvarnAPI:
'error',
msg_text='There is no resource type for path',
path=path)
- raise NoSuchResourceType(path)
+ raise qvarn.NoSuchResourceType(path)
elif len(objs) > 1: # pragma: no cover
qvarn.log.log(
'error',
msg_text='There are more than one resource types for path',
path=path,
objs=objs)
- raise TooManyResourceTypes(path)
+ raise qvarn.TooManyResourceTypes(path)
rt = qvarn.ResourceType()
rt.from_spec(objs[0]['spec'])
return rt
@@ -156,9 +156,9 @@ class QvarnAPI:
objs = [obj for _, obj in results]
qvarn.log.log('debug', objs=objs)
if len(objs) == 0: # pragma: no cover
- raise NoSuchResourceType('listener')
+ raise qvarn.NoSuchResourceType('listener')
elif len(objs) > 1: # pragma: no cover
- raise TooManyResourceTypes('listener')
+ raise qvarn.TooManyResourceTypes('listener')
rt = qvarn.ResourceType()
rt.from_spec(objs[0]['spec'])
return rt
@@ -171,9 +171,9 @@ class QvarnAPI:
objs = [obj for _, obj in results]
qvarn.log.log('debug', objs=objs)
if len(objs) == 0: # pragma: no cover
- raise NoSuchResourceType('listener')
+ raise qvarn.NoSuchResourceType('listener')
elif len(objs) > 1: # pragma: no cover
- raise TooManyResourceTypes('listener')
+ raise qvarn.TooManyResourceTypes('listener')
rt = qvarn.ResourceType()
rt.from_spec(objs[0]['spec'])
return rt
@@ -188,7 +188,7 @@ class QvarnAPI:
try:
rt = self.get_resource_type(path)
- except NoSuchResourceType:
+ except qvarn.NoSuchResourceType:
qvarn.log.log('warning', msg_text='No such route', path=path)
return []
@@ -323,7 +323,7 @@ class QvarnAPI:
def get_post_listener_callback(self, coll, listeners): # pragma: no cover
def wrapper(content_type, body, **kwargs):
if content_type != 'application/json':
- raise NotJson(content_type)
+ raise qvarn.NotJson(content_type)
rt = listeners.get_type()
try:
@@ -364,7 +364,7 @@ class QvarnAPI:
def put_listener_callback(self, listeners): # pragma: no cover
def wrapper(content_type, body, **kwargs):
if content_type != 'application/json':
- raise NotJson(content_type)
+ raise qvarn.NotJson(content_type)
if 'type' not in body:
body['type'] = 'listener'
@@ -437,7 +437,7 @@ class QvarnAPI:
if len(pairs) == 0:
return qvarn.no_such_resource_response(notification_id)
if len(pairs) > 1:
- raise TooManyResources(notification_id)
+ raise qvarn.TooManyResources(notification_id)
return qvarn.ok_response(pairs[0][1])
return wrapper
@@ -521,7 +521,7 @@ class QvarnAPI:
def get_post_callback(self, coll): # pragma: no cover
def wrapper(content_type, body, **kwargs):
if content_type != 'application/json':
- raise NotJson(content_type)
+ raise qvarn.NotJson(content_type)
if 'type' not in body:
body['type'] = coll.get_type_name()
try:
@@ -542,7 +542,7 @@ class QvarnAPI:
def get_put_callback(self, coll): # pragma: no cover
def wrapper(content_type, body, **kwargs):
if content_type != 'application/json':
- raise NotJson(content_type)
+ raise qvarn.NotJson(content_type)
if 'type' not in body:
body['type'] = coll.get_type_name()
@@ -561,7 +561,7 @@ class QvarnAPI:
# FIXME: the following test should be enabled once we
# no longer need test-api.
if False and body['id'] != obj_id:
- raise IdMismatch(body['id'], obj_id)
+ raise qvarn.IdMismatch(body['id'], obj_id)
try:
result_body = coll.put(body)
@@ -581,7 +581,7 @@ class QvarnAPI:
def put_subpath_callback(self, coll, subpath): # pragma: no cover
def wrapper(content_type, body, **kwargs):
if content_type != 'application/json':
- raise NotJson(content_type)
+ raise qvarn.NotJson(content_type)
obj_id = kwargs['id']
if 'revision' not in body:
@@ -655,34 +655,3 @@ class QvarnAPI:
self.notify(obj_id, None, 'deleted')
return qvarn.ok_response({})
return wrapper
-
-
-class NoSuchResourceType(Exception): # pragma: no cover
-
- def __init__(self, path):
- super().__init__('No resource type for path {}'.format(path))
-
-
-class TooManyResourceTypes(Exception): # pragma: no cover
-
- def __init__(self, path):
- super().__init__('Too many resource types for path {}'.format(path))
-
-
-class TooManyResources(Exception): # pragma: no cover
-
- def __init__(self, resource_id):
- super().__init__('Too many resources with id {}'.format(resource_id))
-
-
-class NotJson(Exception): # pragma: no cover
-
- def __init__(self, ct):
- super().__init__('Was expecting application/json, not {}'.format(ct))
-
-
-class IdMismatch(Exception): # pragma: no cover
-
- def __init__(self, obj_id, id_from_path):
- super().__init__(
- 'Resource has id {} but path says {}'.format(obj_id, id_from_path))
diff --git a/qvarn/subresource_router.py b/qvarn/subresource_router.py
new file mode 100644
index 0000000..b53d2f7
--- /dev/null
+++ b/qvarn/subresource_router.py
@@ -0,0 +1,86 @@
+# Copyright (C) 2017 Lars Wirzenius
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+import qvarn
+
+
+class SubresourceRouter(qvarn.Router):
+
+ def __init__(self):
+ super().__init__()
+ self._store = None
+ self._parent_coll = None
+ self._subpath = None
+
+ def set_subpath(self, subpath):
+ self._subpath = subpath
+
+ def set_parent_collection(self, parent_coll):
+ self._parent_coll = parent_coll
+
+ def set_object_store(self, store):
+ self._store = store
+
+ def get_routes(self):
+ rt = self._parent_coll.get_type()
+ path = '{}/<id>/{}'.format(rt.get_path(), self._subpath)
+ return [
+ {
+ 'method': 'GET',
+ 'path': path,
+ 'callback': self._get_subresource,
+ },
+ {
+ 'method': 'PUT',
+ 'path': path,
+ 'callback': self._put_subresource,
+ },
+ ]
+
+ def _get_subresource(self, *args, **kwargs):
+ obj_id = kwargs['id']
+ try:
+ obj = self._parent_coll.get_subresource(obj_id, self._subpath)
+ except qvarn.NoSuchResource as e:
+ return qvarn.no_such_resource_response(str(e))
+ return qvarn.ok_response(obj)
+
+ def put_subpath_callback(self, content_type, body, *args, **kwargs):
+ if content_type != 'application/json':
+ raise qvarn.NotJson(content_type)
+
+ obj_id = kwargs['id']
+ if 'revision' not in body:
+ return qvarn.bad_request_response('must have revision')
+ revision = body.pop('revision')
+
+ rt = self._parent_coll.get_type()
+ validator = qvarn.Validator()
+ try:
+ validator.validate_subresource(self._subpath, rt, body)
+ except qvarn.ValidationError as e:
+ qvarn.log.log('error', msg_text=str(e), body=body)
+ return qvarn.bad_request_response(str(e))
+
+ try:
+ result_body = self._parent_coll.put_subresource(
+ body, subpath=self._subpath, obj_id=obj_id, revision=revision)
+ except qvarn.WrongRevision as e:
+ return qvarn.conflict_response(str(e))
+ except qvarn.NoSuchResource as e:
+ return qvarn.no_such_resource_response(str(e))
+
+ return qvarn.ok_response(result_body)