forked from openlp/openlp
Add static routes for serving files, and template rendering built into endpoints
This commit is contained in:
parent
30ee384a9b
commit
968a0ab87a
@ -42,7 +42,13 @@ def register_endpoint(end_point):
|
|||||||
"""
|
"""
|
||||||
Register an endpoint with the app
|
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)
|
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)
|
||||||
|
|
||||||
|
@ -20,29 +20,34 @@
|
|||||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
# 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):
|
class Endpoint(object):
|
||||||
"""
|
"""
|
||||||
This is an endpoint for the HTTP API
|
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
|
Create an endpoint with a URL prefix
|
||||||
"""
|
"""
|
||||||
print("init")
|
print("init")
|
||||||
self.url_prefix = url_prefix
|
self.url_prefix = url_prefix
|
||||||
|
self.static_dir = static_dir
|
||||||
|
self.template_dir = template_dir
|
||||||
self.routes = []
|
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
|
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
|
Set up a URL route
|
||||||
"""
|
"""
|
||||||
@ -50,6 +55,15 @@ class Endpoint(object):
|
|||||||
"""
|
"""
|
||||||
Make this a decorator
|
Make this a decorator
|
||||||
"""
|
"""
|
||||||
self.add_url_route(rule, func, method, secure)
|
self.add_url_route(rule, func, method)
|
||||||
return func
|
return func
|
||||||
return decorator
|
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)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||||
|
# pylint: disable=logging-format-interpolation
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# OpenLP - Open Source Lyrics Projection #
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
@ -22,17 +23,48 @@
|
|||||||
"""
|
"""
|
||||||
App stuff
|
App stuff
|
||||||
"""
|
"""
|
||||||
import logging
|
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from webob import Request, Response
|
from webob import Request, Response
|
||||||
|
from webob.static import DirectoryApp
|
||||||
|
|
||||||
from openlp.core.api.http.errors import HttpError, NotFound, ServerError
|
from openlp.core.api.http.errors import HttpError, NotFound, ServerError
|
||||||
|
|
||||||
|
ARGS_REGEX = re.compile(r'''\{(\w+)(?::([^}]+))?\}''', re.VERBOSE)
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
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):
|
def _make_response(view_result):
|
||||||
"""
|
"""
|
||||||
Create a Response object from response
|
Create a Response object from response
|
||||||
@ -49,18 +81,11 @@ def _make_response(view_result):
|
|||||||
response = Response(body=body, status=view_result[1],
|
response = Response(body=body, status=view_result[1],
|
||||||
content_type=content_type, charset='utf8')
|
content_type=content_type, charset='utf8')
|
||||||
if len(view_result) >= 3:
|
if len(view_result) >= 3:
|
||||||
response.headers = view_result[2]
|
response.headers.update(view_result[2])
|
||||||
return response
|
return response
|
||||||
elif isinstance(view_result, dict):
|
elif isinstance(view_result, dict):
|
||||||
return Response(body=json.dumps(view_result), status=200,
|
return Response(body=json.dumps(view_result), status=200,
|
||||||
content_type='application/json', charset='utf8')
|
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):
|
def _handle_exception(error):
|
||||||
@ -83,26 +108,44 @@ class WSGIApplication(object):
|
|||||||
Create the app object
|
Create the app object
|
||||||
"""
|
"""
|
||||||
self.name = name
|
self.name = name
|
||||||
|
self.static_routes = {}
|
||||||
self.route_map = {}
|
self.route_map = {}
|
||||||
|
|
||||||
def add_route(self, route, view_func, method, secure):
|
def add_route(self, route, view_func, method):
|
||||||
"""
|
"""
|
||||||
Add a route
|
Add a route
|
||||||
"""
|
"""
|
||||||
if route not in self.route_map:
|
route_regex = _route_to_regex(route)
|
||||||
self.route_map[route] = {}
|
if route_regex not in self.route_map:
|
||||||
self.route_map[route][method.upper()] = {'function': view_func, 'secure': secure}
|
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):
|
def dispatch(self, request):
|
||||||
"""
|
"""
|
||||||
Find the appropriate URL and run the view function
|
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 re.match(route, request.path):
|
||||||
if request.method.upper() in views:
|
# 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))
|
log.debug('Found {method} {url}'.format(method=request.method, url=request.path))
|
||||||
view_func = views[request.method.upper()]['function']
|
view_func = views[request.method.upper()]
|
||||||
return _make_response(view_func(request))
|
return _make_response(view_func(request, **kwargs))
|
||||||
log.error('Not Found url {url} '.format(url=request.path))
|
log.error('Not Found url {url} '.format(url=request.path))
|
||||||
raise NotFound()
|
raise NotFound()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user