"""Get feature flags on backend.""" from functools import wraps from flask import g from flask import request from owsresponse.adaptors.flask import flaskify from raven import base as sentry_base from owsfeatures.constant import header from owsfeatures.models import ows_features FEATURES_ATTRIBUTE = '_features' UNINITIALIZED_ERROR = 'Attempt to look up feature without initialization.' class FeatureEngine: """Fetches real features from ows-features & stores in flask.g.""" def load(self): """Fetch features from ows-feature and store them.""" orchard_user_id = request.headers.get(header.ORCHARD_USER_ID) if not orchard_user_id: features_response = ows_features.general_features() else: features_response = ows_features.user_features(orchard_user_id) if not features_response: return features_response setattr(g, FEATURES_ATTRIBUTE, features_response.message) return True def get(self): """Get dictionary of features.""" return getattr(g, FEATURES_ATTRIBUTE, None) feature_engine = FeatureEngine() def is_feature_enabled(feature_name): """Check if feature is enabled for request user. Args: feature_name (str): feature to check Returns: bool: whether feature is enabled. """ features = feature_engine.get() if features is None: raise Exception(UNINITIALIZED_ERROR) return features.get(feature_name) == 'enabled' def load_features(func): """Decorator that loads feature flags from ows-features.""" @wraps(func) def _load_features(*args, **kwargs): """Load features from ows-features.""" result = feature_engine.load() if not result: return flaskify(result) return func(*args, **kwargs) return _load_features def check_feature(feature_name): """Check feature flag and load features if necessary. Args: feature_name (str): feature to check Returns: bool: whether feature is enabled. """ try: features_cache = feature_engine.get() if features_cache is None: feature_engine.load() return is_feature_enabled(feature_name) except: ows = getattr(g, 'ows', None) if ows: ows.log.error( 'Error checking feature flag {}'.format(feature_name)) if sentry_base.Raven: sentry_base.Raven.captureException() return False