diff --git a/superset/app.py b/superset/app.py index 8cf9f7e604..a01d19afee 100644 --- a/superset/app.py +++ b/superset/app.py @@ -274,15 +274,17 @@ class SupersetAppInitializer: category="", category_icon="", ) - if feature_flag_manager.is_feature_enabled("DYNAMIC_PLUGINS"): - appbuilder.add_view( - DynamicPluginsView, - "Plugins", - label=__("Plugins"), - category="Manage", - category_label=__("Manage"), - icon="fa-puzzle-piece", - ) + appbuilder.add_view( + DynamicPluginsView, + "Plugins", + label=__("Plugins"), + category="Manage", + category_label=__("Manage"), + icon="fa-puzzle-piece", + menu_cond=lambda: feature_flag_manager.is_feature_enabled( + "DYNAMIC_PLUGINS" + ), + ) appbuilder.add_view( CssTemplateModelView, "CSS Templates", diff --git a/superset/views/dynamic_plugins.py b/superset/views/dynamic_plugins.py index ce2cf2a51f..37432b648b 100644 --- a/superset/views/dynamic_plugins.py +++ b/superset/views/dynamic_plugins.py @@ -14,10 +14,15 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. +from typing import Optional + +from flask import make_response, Response from flask_appbuilder import ModelView +from flask_appbuilder.hooks import before_request from flask_appbuilder.models.sqla.interface import SQLAInterface from flask_babel import lazy_gettext as _ +from superset import is_feature_enabled from superset.constants import MODEL_API_RW_METHOD_PERMISSION_MAP from superset.models.dynamic_plugins import DynamicPlugin @@ -54,3 +59,10 @@ class DynamicPluginsView(ModelView): show_title = _("Custom Plugin") add_title = _("Add a Plugin") edit_title = _("Edit Plugin") + + @before_request + # pylint: disable=R0201 + def ensure_dynamic_plugins_enabled(self) -> Optional[Response]: + if not is_feature_enabled("DYNAMIC_PLUGINS"): + return make_response("Not found", 404) + return None diff --git a/tests/dynamic_plugins_tests.py b/tests/dynamic_plugins_tests.py new file mode 100644 index 0000000000..bdc9f61552 --- /dev/null +++ b/tests/dynamic_plugins_tests.py @@ -0,0 +1,40 @@ +# 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. +from .base_tests import SupersetTestCase +from .conftest import with_feature_flags + + +class TestDynamicPlugins(SupersetTestCase): + @with_feature_flags(DYNAMIC_PLUGINS=False) + def test_dynamic_plugins_disabled(self): + """ + Dynamic Plugins: Responds not found when disabled + """ + self.login(username="admin") + uri = "/dynamic-plugins/api" + rv = self.client.get(uri) + self.assertEqual(rv.status_code, 404) + + @with_feature_flags(DYNAMIC_PLUGINS=True) + def test_dynamic_plugins_enabled(self): + """ + Dynamic Plugins: Responds successfully when enabled + """ + self.login(username="admin") + uri = "/dynamic-plugins/api" + rv = self.client.get(uri) + self.assertEqual(rv.status_code, 200)