1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/ihatemoney_ynh.git synced 2024-09-03 19:26:15 +02:00

Make the rest module a flask extension.

It's now flask_rest.
This commit is contained in:
Alexis Metaireau 2011-10-17 17:30:57 +02:00
parent 3f9022ac13
commit 435ec2626d
3 changed files with 2 additions and 159 deletions

View file

@ -6,7 +6,7 @@ from forms import (ProjectForm, EditProjectForm, MemberForm, BillForm,
get_billform_for)
from utils import for_all_methods
from rest import RESTResource, need_auth# FIXME make it an ext
from flask_rest import RESTResource, need_auth
from werkzeug import Response

View file

@ -3,3 +3,4 @@ flask-wtf
flask-sqlalchemy
flask-mail
flask-babel
flask-rest

View file

@ -1,158 +0,0 @@
import json
from flask import request
import werkzeug
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",}
def __init__(self, name, route, app, handler, authentifier=None,
actions=None, inject_name=None):
"""
: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:
Authorized actions. Optional. None means all.
:handler:
The handler instance which will handle the requests
:authentifier:
callable checking the authentication. If specified, all the
methods will be checked against it.
"""
if not actions:
actions = self._VERBS.keys()
self._route = route
self._handler = handler
self._name = name
self._identifier = "%s_id" % name
self._authentifier = authentifier
self._inject_name = inject_name # FIXME
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:
method = need_auth(self._authentifier,
self._inject_name or self._name)(method)
method = serialize(method)
app.add_url_rule(
self._get_route_for(action),
"%s_%s" % (self._name, action),
method,
methods=[self._VERBS.get(action, "GET")])
def need_auth(authentifier, name=None, remove_attr=True):
"""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 401 Unauthorized 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
:remove_attr:
Remove or not the `*name*_id` from the kwargs before calling the
function
"""
def wrapper(func):
def wrapped(*args, **kwargs):
result = authentifier(*args, **kwargs)
if result:
if name:
kwargs[name] = result
if remove_attr:
del kwargs["%s_id" % name]
return func(*args, **kwargs)
else:
return 401, "Unauthorized"
return wrapped
return wrapper
# serializers
def serialize(func):
"""If the object returned by the view is not already a Response, serialize
it using the ACCEPT header and return it.
"""
def wrapped(*args, **kwargs):
# get the mimetype
mime = request.accept_mimetypes.best_match(SERIALIZERS.keys()) or "text/json"
data = func(*args, **kwargs)
serializer = SERIALIZERS[mime]
status = 200
if len(data) == 2:
status, data = data
# serialize it
return werkzeug.Response(serializer.encode(data),
status=status, mimetype=mime)
return wrapped
class JSONEncoder(json.JSONEncoder):
"""Subclass of the default encoder to support custom objects"""
def default(self, o):
if hasattr(o, "_to_serialize"):
# build up the object
data = {}
for attr in o._to_serialize:
data[attr] = getattr(o, attr)
return data
elif hasattr(o, "isoformat"):
return o.isoformat()
else:
return json.JSONEncoder.default(self, o)
SERIALIZERS = {"text/json": JSONEncoder()}