From b70a9ae524ce4ec66695bbc62a0991d98e78e6df Mon Sep 17 00:00:00 2001 From: Christine Chambers Date: Tue, 29 Jan 2019 20:23:13 -0800 Subject: [PATCH] Merge default feature flags and user defined feature flags - Rename the default feature flags key in `config.py` to DEFAULT_FEATURE_FLAGS - Merge default feature flags with user defined ones allowing the latter to overwrite the former - Expose feature_flags for both server and client to use - Add a utility method for checking whether a feature flag is on on server side --- CONTRIBUTING.md | 4 ++++ superset/__init__.py | 4 ++++ superset/config.py | 9 ++++++--- superset/utils/feature_flags.py | 25 +++++++++++++++++++++++++ superset/views/base.py | 4 ++-- tests/utils_tests.py | 9 +++++++++ 6 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 superset/utils/feature_flags.py diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dcc79f7f33..9f0fd5caa0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -410,6 +410,10 @@ export enum FeatureFlag { } ``` +`superset/config.py` contains `DEFAULT_FEATURE_FLAGS` which will be overwritten by +those specified under FEATURE_FLAGS in `superset_config.py`. For example, `DEFAULT_FEATURE_FLAGS = { 'FOO': True, 'BAR': False }` in `superset/config.py` and `FEATURE_FLAGS = { 'BAR': True, 'BAZ': True }` in `superset_config.py` will result +in combined feature flags of `{ 'FOO': True, 'BAR': True, 'BAZ': True }`. + ## Linting Lint the project with: diff --git a/superset/__init__.py b/superset/__init__.py index 791e20bcad..82d75761e1 100644 --- a/superset/__init__.py +++ b/superset/__init__.py @@ -208,6 +208,10 @@ security_manager = appbuilder.sm results_backend = app.config.get('RESULTS_BACKEND') +# Merge user defined feature flags with default feature flags +feature_flags = app.config.get('DEFAULT_FEATURE_FLAGS') +feature_flags.update(app.config.get('FEATURE_FLAGS') or {}) + # Registering sources module_datasource_map = app.config.get('DEFAULT_MODULE_DS_MAP') module_datasource_map.update(app.config.get('ADDITIONAL_MODULE_DS_MAP')) diff --git a/superset/config.py b/superset/config.py index 2d32f973a8..71fbb5d241 100644 --- a/superset/config.py +++ b/superset/config.py @@ -186,9 +186,12 @@ LANGUAGES = { # --------------------------------------------------- # Feature flags # --------------------------------------------------- -# Feature flags that are on by default go here. Their -# values can be overridden by those in super_config.py -FEATURE_FLAGS = {} +# Feature flags that are set by default go here. Their values can be +# overwritten by those specified under FEATURE_FLAGS in super_config.py +# For example, DEFAULT_FEATURE_FLAGS = { 'FOO': True, 'BAR': False } here +# and FEATURE_FLAGS = { 'BAR': True, 'BAZ': True } in superset_config.py +# will result in combined feature flags of { 'FOO': True, 'BAR': True, 'BAZ': True } +DEFAULT_FEATURE_FLAGS = {} # --------------------------------------------------- # Image and file configuration diff --git a/superset/utils/feature_flags.py b/superset/utils/feature_flags.py new file mode 100644 index 0000000000..cac9d80235 --- /dev/null +++ b/superset/utils/feature_flags.py @@ -0,0 +1,25 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# pylint: disable=C,R,W +from superset import feature_flags + + +def is_feature_enabled(feature): + """ + Utility function for checking whether a feature is turned on + """ + return feature_flags.get(feature) diff --git a/superset/views/base.py b/superset/views/base.py index 6b71ff0c92..f0f5fbf7e6 100644 --- a/superset/views/base.py +++ b/superset/views/base.py @@ -31,7 +31,7 @@ from flask_babel import lazy_gettext as _ import simplejson as json import yaml -from superset import conf, db, security_manager +from superset import conf, db, feature_flags, security_manager from superset.exceptions import SupersetException, SupersetSecurityException from superset.translations.utils import get_language_pack from superset.utils import core as utils @@ -157,7 +157,7 @@ class BaseSupersetView(BaseView): 'conf': {k: conf.get(k) for k in FRONTEND_CONF_KEYS}, 'locale': locale, 'language_pack': get_language_pack(locale), - 'feature_flags': conf.get('FEATURE_FLAGS'), + 'feature_flags': feature_flags, } diff --git a/tests/utils_tests.py b/tests/utils_tests.py index 587e07fc83..fbee59fe51 100644 --- a/tests/utils_tests.py +++ b/tests/utils_tests.py @@ -39,6 +39,7 @@ from superset.utils.core import ( zlib_compress, zlib_decompress_to_string, ) +from superset.utils.feature_flags import is_feature_enabled def mock_parse_human_datetime(s): @@ -756,3 +757,11 @@ class UtilsTestCase(unittest.TestCase): } convert_legacy_filters_into_adhoc(form_data) self.assertEquals(form_data, expected) + + @patch.dict('superset.feature_flags', {'FOO': True}, clear=True) + def test_existing_feature_flags(self): + self.assertTrue(is_feature_enabled('FOO')) + + @patch.dict('superset.feature_flags', {}, clear=True) + def test_nonexistent_feature_flags(self): + self.assertFalse(is_feature_enabled('FOO'))