2011-09-11 22:11:36 +02:00
|
|
|
class RESTResource(object):
|
|
|
|
"""Represents a REST resource, with the different HTTP verbs"""
|
|
|
|
_NEED_ID = ["get", "update", "delete"]
|
|
|
|
_VERBS = {"get": "GET",
|
|
|
|
"update": "PUT",
|
|
|
|
"delete": "DELETE",
|
|
|
|
"list": "GET",
|
|
|
|
"add": "POST",}
|
|
|
|
|
2011-09-11 23:00:32 +02:00
|
|
|
def __init__(self, name, route, app, handler, authentifier=None,
|
|
|
|
actions=None, inject_name=None):
|
2011-09-11 22:11:36 +02:00
|
|
|
"""
|
|
|
|
:name:
|
|
|
|
name of the resource. This is being used when registering
|
|
|
|
the route, for its name and for the name of the id parameter
|
|
|
|
that will be passed to the views
|
|
|
|
|
|
|
|
:route:
|
|
|
|
Default route for this resource
|
|
|
|
|
|
|
|
:app:
|
|
|
|
Application to register the routes onto
|
|
|
|
|
|
|
|
:actions:
|
2011-09-11 23:00:32 +02:00
|
|
|
Authorized actions. Optional. None means all.
|
2011-09-11 22:11:36 +02:00
|
|
|
|
|
|
|
:handler:
|
|
|
|
The handler instance which will handle the requests
|
|
|
|
|
|
|
|
:authentifier:
|
|
|
|
callable checking the authentication. If specified, all the
|
|
|
|
methods will be checked against it.
|
|
|
|
"""
|
2011-09-11 23:00:32 +02:00
|
|
|
if not actions:
|
|
|
|
actions = self._VERBS.keys()
|
2011-09-11 22:11:36 +02:00
|
|
|
|
|
|
|
self._route = route
|
|
|
|
self._handler = handler
|
|
|
|
self._name = name
|
|
|
|
self._identifier = "%s_id" % name
|
|
|
|
self._authentifier = authentifier
|
2011-09-11 23:00:32 +02:00
|
|
|
self._inject_name = inject_name # FIXME
|
2011-09-11 22:11:36 +02:00
|
|
|
|
|
|
|
for action in actions:
|
|
|
|
self.add_url_rule(app, action)
|
|
|
|
|
|
|
|
def _get_route_for(self, action):
|
|
|
|
"""Return the complete URL for this action.
|
|
|
|
|
|
|
|
Basically:
|
|
|
|
|
|
|
|
- get, update and delete need an id
|
|
|
|
- add and list does not
|
|
|
|
"""
|
|
|
|
route = self._route
|
|
|
|
|
|
|
|
if action in self._NEED_ID:
|
|
|
|
route += "/<%s>" % self._identifier
|
|
|
|
|
|
|
|
return route
|
|
|
|
|
|
|
|
def add_url_rule(self, app, action):
|
|
|
|
"""Registers a new url to the given application, regarding
|
|
|
|
the action.
|
|
|
|
"""
|
|
|
|
method = getattr(self._handler, action)
|
|
|
|
|
|
|
|
# decorate the view
|
|
|
|
if self._authentifier:
|
2011-09-11 23:00:32 +02:00
|
|
|
method = need_auth(self._authentifier,
|
|
|
|
self._inject_name or self._name)(method)
|
2011-09-11 22:11:36 +02:00
|
|
|
|
|
|
|
app.add_url_rule(
|
|
|
|
self._get_route_for(action),
|
|
|
|
"%s_%s" % (self._name, action),
|
|
|
|
method,
|
|
|
|
methods=[self._VERBS.get(action, "GET")])
|
|
|
|
|
|
|
|
|
2011-09-11 23:00:32 +02:00
|
|
|
def need_auth(authentifier, name=None, remove_attr=True):
|
2011-09-11 22:11:36 +02:00
|
|
|
"""Decorator checking that the authentifier does not returns false in
|
|
|
|
the current context.
|
|
|
|
|
|
|
|
If the request is authorized, the object returned by the authentifier
|
|
|
|
is added to the kwargs of the method.
|
|
|
|
|
|
|
|
If not, issue a 403 Forbidden error
|
|
|
|
|
|
|
|
:authentifier:
|
|
|
|
The callable to check the context onto.
|
|
|
|
|
|
|
|
:name:
|
|
|
|
**Optional**, name of the argument to put the object into.
|
|
|
|
If it is not provided, nothing will be added to the kwargs
|
|
|
|
of the decorated function
|
2011-09-11 23:00:32 +02:00
|
|
|
|
|
|
|
:remove_attr:
|
|
|
|
Remove or not the `*name*_id` from the kwargs before calling the
|
|
|
|
function
|
2011-09-11 22:11:36 +02:00
|
|
|
"""
|
|
|
|
def wrapper(func):
|
|
|
|
def wrapped(*args, **kwargs):
|
|
|
|
result = authentifier(*args, **kwargs)
|
|
|
|
if result:
|
|
|
|
if name:
|
|
|
|
kwargs[name] = result
|
2011-09-11 23:00:32 +02:00
|
|
|
if remove_attr:
|
|
|
|
del kwargs["%s_id" % name]
|
2011-09-11 22:11:36 +02:00
|
|
|
return func(*args, **kwargs)
|
|
|
|
else:
|
|
|
|
raise werkzeug.exceptions.Forbidden()
|
|
|
|
return wrapped
|
|
|
|
return wrapper
|