diff options
author | Lars Wirzenius <liw@liw.fi> | 2017-10-12 12:30:17 +0300 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2017-10-12 12:30:17 +0300 |
commit | 5d2e5bf12d93954f512cf34a6f4dcaa4146f2a6c (patch) | |
tree | 76d8a155354a0693b81d908e5badb9b6a5101ef3 | |
parent | b15e089854b5015ac01e41c41365df8cbbc98c00 (diff) | |
download | qvisqve-5d2e5bf12d93954f512cf34a6f4dcaa4146f2a6c.tar.gz |
Refactor: add SubresourceRouter
-rw-r--r-- | qvarn/api.py | 59 | ||||
-rw-r--r-- | qvarn/subresource_router.py | 86 |
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) |