Add static routes for serving files, and template rendering built into endpoints

This commit is contained in:
Raoul Snyman 2016-06-15 23:52:46 +02:00
parent 30ee384a9b
commit 968a0ab87a
3 changed files with 89 additions and 26 deletions

View File

@ -42,7 +42,13 @@ def register_endpoint(end_point):
"""
Register an endpoint with the app
"""
for url, view_func, method, secure in end_point.routes:
for url, view_func, method in end_point.routes:
# Set the view functions
route = _route_from_url(end_point.url_prefix, url)
application.add_route(route, view_func, method, secure)
application.add_route(route, view_func, method)
# Add a static route if necessary
if end_point.static_dir:
static_route = _route_from_url(end_point.url_prefix, 'static')
static_route += '(.*)'
application.add_static_route(static_route, end_point.static_dir)

View File

@ -20,29 +20,34 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
openlp/core/api/endpoint.py: Endpoint stuff
The Endpoint class, which provides plugins with a way to serve their own portion of the API
"""
import os
from mako.template import Template
class Endpoint(object):
"""
This is an endpoint for the HTTP API
"""
def __init__(self, url_prefix):
def __init__(self, url_prefix, template_dir=None, static_dir=None):
"""
Create an endpoint with a URL prefix
"""
print("init")
self.url_prefix = url_prefix
self.static_dir = static_dir
self.template_dir = template_dir
self.routes = []
def add_url_route(self, url, view_func, method, secure):
def add_url_route(self, url, view_func, method):
"""
Add a url route to the list of routes
"""
self.routes.append((url, view_func, method, secure))
self.routes.append((url, view_func, method))
def route(self, rule, method='GET', secure=False):
def route(self, rule, method='GET'):
"""
Set up a URL route
"""
@ -50,6 +55,15 @@ class Endpoint(object):
"""
Make this a decorator
"""
self.add_url_route(rule, func, method, secure)
self.add_url_route(rule, func, method)
return func
return decorator
def render_template(self, filename, **kwargs):
"""
Render a mako template
"""
if not self.template_dir:
raise Exception('No template directory specified')
path = os.path.abspath(os.path.join(self.template_dir, filename))
return Template(filename=path, input_encoding='utf-8').render(**kwargs)

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
# pylint: disable=logging-format-interpolation
###############################################################################
# OpenLP - Open Source Lyrics Projection #
@ -22,17 +23,48 @@
"""
App stuff
"""
import logging
import json
import logging
import os
import re
from webob import Request, Response
from webob.static import DirectoryApp
from openlp.core.api.http.errors import HttpError, NotFound, ServerError
ARGS_REGEX = re.compile(r'''\{(\w+)(?::([^}]+))?\}''', re.VERBOSE)
log = logging.getLogger(__name__)
def _route_to_regex(route):
"""
Convert a route to a regular expression
For example:
'songs/{song_id}' becomes 'songs/(?P<song_id>[^/]+)'
and
'songs/{song_id:\d+}' becomes 'songs/(?P<song_id>\d+)'
"""
route_regex = ''
last_pos = 0
for match in ARGS_REGEX.finditer(route):
route_regex += re.escape(route[last_pos:match.start()])
arg_name = match.group(1)
expr = match.group(2) or '[^/]+'
expr = '(?P<%s>%s)' % (arg_name, expr)
route_regex += expr
last_pos = match.end()
route_regex += re.escape(route[last_pos:])
route_regex = '^%s$' % route_regex
return route_regex
def _make_response(view_result):
"""
Create a Response object from response
@ -49,18 +81,11 @@ def _make_response(view_result):
response = Response(body=body, status=view_result[1],
content_type=content_type, charset='utf8')
if len(view_result) >= 3:
response.headers = view_result[2]
response.headers.update(view_result[2])
return response
elif isinstance(view_result, dict):
return Response(body=json.dumps(view_result), status=200,
content_type='application/json', charset='utf8')
elif isinstance(view_result, str):
if 'body {' in view_result:
return Response(body=view_result, status=200,
content_type='text/css', charset='utf8')
else:
return Response(body=view_result, status=200,
content_type='text/html', charset='utf8')
def _handle_exception(error):
@ -83,26 +108,44 @@ class WSGIApplication(object):
Create the app object
"""
self.name = name
self.static_routes = {}
self.route_map = {}
def add_route(self, route, view_func, method, secure):
def add_route(self, route, view_func, method):
"""
Add a route
"""
if route not in self.route_map:
self.route_map[route] = {}
self.route_map[route][method.upper()] = {'function': view_func, 'secure': secure}
route_regex = _route_to_regex(route)
if route_regex not in self.route_map:
self.route_map[route_regex] = {}
self.route_map[route_regex][method.upper()] = view_func
def add_static_route(self, route, static_dir):
"""
Add a static directory as a route
"""
if not route in self.static_routes:
self.static_routes[route] = DirectoryApp(os.path.abspath(static_dir))
def dispatch(self, request):
"""
Find the appropriate URL and run the view function
"""
for route, views in self.route_map.items():
# First look to see if this is a static file request
for route, static_app in self.static_routes.items():
if re.match(route, request.path):
if request.method.upper() in views:
log.debug('Found {method} {url}'.format(method=request.method, url=request.path))
view_func = views[request.method.upper()]['function']
return _make_response(view_func(request))
# Pop the path info twice in order to get rid of the "/<plugin>/static"
request.path_info_pop()
request.path_info_pop()
return request.get_response(static_app)
# If not a static route, try the views
for route, views in self.route_map.items():
match = re.match(route, request.path)
if match and request.method.upper() in views:
kwargs = match.groupdict()
log.debug('Found {method} {url}'.format(method=request.method, url=request.path))
view_func = views[request.method.upper()]
return _make_response(view_func(request, **kwargs))
log.error('Not Found url {url} '.format(url=request.path))
raise NotFound()