summaryrefslogtreecommitdiff
path: root/src/mailman/rest/helpers.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/mailman/rest/helpers.py')
-rw-r--r--src/mailman/rest/helpers.py172
1 files changed, 95 insertions, 77 deletions
diff --git a/src/mailman/rest/helpers.py b/src/mailman/rest/helpers.py
index 025ad1779..acc5106be 100644
--- a/src/mailman/rest/helpers.py
+++ b/src/mailman/rest/helpers.py
@@ -21,28 +21,30 @@ from __future__ import absolute_import, print_function, unicode_literals
__metaclass__ = type
__all__ = [
+ 'BadRequest',
+ 'ChildError',
'GetterSetter',
- 'PATCH',
+ 'NotFound',
+ 'bad_request',
+ 'child',
+ 'conflict',
+ 'created',
'etag',
+ 'forbidden',
'no_content',
+ 'not_found',
+ 'okay',
'path_to',
- 'restish_matcher',
]
-import cgi
import json
+import falcon
import hashlib
-from cStringIO import StringIO
from datetime import datetime, timedelta
from enum import Enum
from lazr.config import as_boolean
-from restish import http
-from restish.http import Response
-from restish.resource import MethodDecorator
-from webob.multidict import MultiDict
-
from mailman.config import config
@@ -119,22 +121,15 @@ def paginate(method):
arguments.
"""
def wrapper(self, request, *args, **kwargs):
- try:
- count = int(request.GET['count'])
- page = int(request.GET['page'])
- if count < 0 or page < 0:
- return http.bad_request([], b'Invalid parameters')
- # Wrong parameter types or no GET attribute in request object.
- except (AttributeError, ValueError, TypeError):
- return http.bad_request([], b'Invalid parameters')
- # No count/page params.
- except KeyError:
- count = page = None
+ # Allow falcon's HTTPBadRequest exceptions to percolate up. They'll
+ # get turned into HTTP 400 errors.
+ count = request.get_param_as_int('count', min=0)
+ page = request.get_param_as_int('page', min=1)
result = method(self, request, *args, **kwargs)
if count is None and page is None:
return result
- list_start = int((page - 1) * count)
- list_end = int(page * count)
+ list_start = (page - 1) * count
+ list_end = page * count
return result[list_start:list_end]
return wrapper
@@ -164,14 +159,14 @@ class CollectionMixin:
This must be implemented by subclasses.
- :param request: A restish request.
+ :param request: An http request.
:return: The collection
:rtype: list
"""
raise NotImplementedError
def _make_collection(self, request):
- """Provide the collection to restish."""
+ """Provide the collection to the REST layer."""
collection = self._get_collection(request)
if len(collection) == 0:
return dict(start=0, total_size=0)
@@ -189,59 +184,6 @@ class CollectionMixin:
-# XXX 2010-02-24 barry Seems like contrary to the documentation, matchers
-# cannot be plain functions, because matchers must have a .score attribute.
-# OTOH, I think they support regexps, so that might be a better way to go.
-def restish_matcher(function):
- """Decorator for restish matchers."""
- function.score = ()
- return function
-
-
-# restish doesn't support HTTP response code 204.
-def no_content():
- """204 No Content."""
- return Response('204 No Content', [], None)
-
-
-# These two classes implement an ugly, dirty hack to work around the fact that
-# neither WebOb nor really the stdlib cgi module support non-standard HTTP
-# verbs such as PATCH. Note that restish handles it just fine in the sense
-# that the right method gets called, but without the following kludge, the
-# body of the request will never get decoded, so the method won't see any
-# data.
-#
-# Stuffing the MultiDict on request.PATCH is pretty ugly, but it mirrors
-# WebOb's use of request.POST and request.PUT for those standard verbs.
-# Besides, WebOb refuses to allow us to set request.POST. This does make
-# validators.py a bit more complicated. :(
-
-class PATCHWrapper:
- """Hack to decode the request body for PATCH."""
- def __init__(self, func):
- self.func = func
-
- def __call__(self, resource, request):
- # We can't use request.body_file because that's a socket that's
- # already had its data read off of. IOW, if we use that directly,
- # we'll block here.
- field_storage = cgi.FieldStorage(
- fp=StringIO(request.body),
- # Yes, lie about the method so cgi will do the right thing.
- environ=dict(REQUEST_METHOD='POST'),
- keep_blank_values=True)
- request.PATCH = MultiDict.from_fieldstorage(field_storage)
- return self.func(resource, request)
-
-
-class PATCH(MethodDecorator):
- method = 'PATCH'
-
- def __call__(self, func):
- really_wrapped_func = PATCHWrapper(func)
- return super(PATCH, self).__call__(really_wrapped_func)
-
-
class GetterSetter:
"""Get and set attributes on an object.
@@ -300,3 +242,79 @@ class GetterSetter:
if self.decoder is None:
return value
return self.decoder(value)
+
+
+
+# Falcon REST framework add-ons.
+
+def child(matcher=None):
+ def decorator(func):
+ if matcher is None:
+ func.__matcher__ = func.__name__
+ else:
+ func.__matcher__ = matcher
+ return func
+ return decorator
+
+
+class ChildError:
+ def __init__(self, status):
+ self._status = status
+
+ def _oops(self, request, response):
+ raise falcon.HTTPError(self._status, None)
+
+ on_get = _oops
+ on_post = _oops
+ on_put = _oops
+ on_patch = _oops
+ on_delete = _oops
+
+
+class BadRequest(ChildError):
+ def __init__(self):
+ super(BadRequest, self).__init__(falcon.HTTP_400)
+
+
+class NotFound(ChildError):
+ def __init__(self):
+ super(NotFound, self).__init__(falcon.HTTP_404)
+
+
+def okay(response, body=None):
+ response.status = falcon.HTTP_200
+ if body is not None:
+ response.body = body
+
+
+def no_content(response):
+ response.status = falcon.HTTP_204
+
+
+def not_found(response, body=b'404 Not Found'):
+ response.status = falcon.HTTP_404
+ if body is not None:
+ response.body = body
+
+
+def bad_request(response, body='400 Bad Request'):
+ response.status = falcon.HTTP_400
+ if body is not None:
+ response.body = body
+
+
+def created(response, location):
+ response.status = falcon.HTTP_201
+ response.location = location
+
+
+def conflict(response, body=b'409 Conflict'):
+ response.status = falcon.HTTP_409
+ if body is not None:
+ response.body = body
+
+
+def forbidden(response, body=b'403 Forbidden'):
+ response.status = falcon.HTTP_403
+ if body is not None:
+ response.body = body