diff --git a/deploystream/__init__.py b/deploystream/__init__.py index 263a040..510ea16 100644 --- a/deploystream/__init__.py +++ b/deploystream/__init__.py @@ -4,6 +4,7 @@ from os.path import join, dirname from flask import Flask + APP_DIR = dirname(__file__) CONFIG_DIR = join(dirname(APP_DIR), 'config') STATIC_DIR = join(APP_DIR, 'static') @@ -44,7 +45,7 @@ } }) -from deploystream.lib import ensure_certifi_certs_installed +from .lib import ensure_certifi_certs_installed ensure_certifi_certs_installed() # set the secret key. Dummy secret for flask. When using in real life, have @@ -52,13 +53,18 @@ app.secret_key = 'mysecret' # Initialise the providers. -from providers import init_providers +from .providers import init_providers classes = init_providers(app.config['PROVIDERS']) # Configure additional routes needed for oauth -from deploystream.apps.oauth.views import configure_oauth_routes +from .apps.oauth.views import configure_oauth_routes configure_oauth_routes(classes) # Import any views we want to register here at the bottom of the file: -import deploystream.views # NOQA -import deploystream.apps.feature.views # NOQA +from . import views # NOQA +from .apps.feature import views as feature_views # NOQA + +# As this requests caching monkey patches requests.Session, it must be done +# at the end, else the Jira provider fails +from .lib import cache +cache.activate_requests_caching() diff --git a/deploystream/apps/feature/views.py b/deploystream/apps/feature/views.py index ad6b34f..31675a8 100644 --- a/deploystream/apps/feature/views.py +++ b/deploystream/apps/feature/views.py @@ -4,6 +4,7 @@ from deploystream import app from deploystream.apps.feature.lib import get_feature_info, get_all_features +from deploystream.lib import cache from deploystream.lib.transforms import nativify from deploystream.decorators import needs_providers @@ -23,6 +24,7 @@ def _wrapped(*args, **kwargs): @app.route('/features', methods=['GET']) @needs_providers +@cache.cached() @as_json def list_features(providers): features = get_all_features(providers) diff --git a/deploystream/lib/cache.py b/deploystream/lib/cache.py new file mode 100644 index 0000000..3a75d07 --- /dev/null +++ b/deploystream/lib/cache.py @@ -0,0 +1,39 @@ +from functools import wraps + +from flask import request +from werkzeug.contrib.cache import SimpleCache + + +cache = SimpleCache() +EXPIRY_SECONDS = 5 * 60 + + +def cached(timeout=EXPIRY_SECONDS, key='view/%s'): + """ + A decorator for caching views. + + Source: http://flask.pocoo.org/docs/patterns/viewdecorators/ + + With a little additional work it could be made into a generic caching + decorator for other functions that return pickleable data. + """ + def decorator(f): + @wraps(f) + def decorated_function(*args, **kwargs): + cache_key = key % request.path + rv = cache.get(cache_key) + if rv is not None: + return rv + rv = f(*args, **kwargs) + cache.set(cache_key, rv, timeout=timeout) + return rv + return decorated_function + return decorator + + +def activate_requests_caching(): + """ + Call once to activate caching for all HTTP GET issued by 'requests' + """ + import requests_cache + requests_cache.install_cache(expire_after=EXPIRY_SECONDS) diff --git a/requirements/runtime.txt b/requirements/runtime.txt index 59abf03..dbbce1f 100644 --- a/requirements/runtime.txt +++ b/requirements/runtime.txt @@ -7,3 +7,4 @@ zope.interface # define and enforce interfaces certifi # Module for Mozilla's CA bundle apitopy # Required to implement the sprint.ly client jira-python>=0.13 # Required to access JIRA api +requests-cache # A transparent caching library for 'requests'