[
https://issues.apache.org/jira/browse/AIRFLOW-2744?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16660917#comment-16660917
]
ASF GitHub Bot commented on AIRFLOW-2744:
-----------------------------------------
ashb closed pull request #4036: [AIRFLOW-2744] Allow RBAC to accept plugins for
views and links.
URL: https://github.com/apache/incubator-airflow/pull/4036
This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:
As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):
diff --git a/airflow/plugins_manager.py b/airflow/plugins_manager.py
index ad630701de..c589e9b5a9 100644
--- a/airflow/plugins_manager.py
+++ b/airflow/plugins_manager.py
@@ -49,6 +49,8 @@ class AirflowPlugin(object):
admin_views = []
flask_blueprints = []
menu_links = []
+ appbuilder_views = []
+ appbuilder_menu_items = []
@classmethod
def validate(cls):
@@ -120,6 +122,8 @@ def make_module(name, objects):
admin_views = []
flask_blueprints = []
menu_links = []
+flask_appbuilder_views = []
+flask_appbuilder_menu_links = []
for p in plugins:
operators_modules.append(
@@ -135,3 +139,5 @@ def make_module(name, objects):
admin_views.extend(p.admin_views)
flask_blueprints.extend(p.flask_blueprints)
menu_links.extend(p.menu_links)
+ flask_appbuilder_views.extend(p.appbuilder_views)
+ flask_appbuilder_menu_links.extend(p.appbuilder_menu_items)
diff --git a/airflow/www_rbac/app.py b/airflow/www_rbac/app.py
index 392dce1b31..e7f5a1e976 100644
--- a/airflow/www_rbac/app.py
+++ b/airflow/www_rbac/app.py
@@ -17,6 +17,7 @@
# specific language governing permissions and limitations
# under the License.
#
+import logging
import socket
import six
@@ -37,6 +38,7 @@
appbuilder = None
csrf = CSRFProtect()
+log = logging.getLogger(__name__)
def create_app(config=None, session=None, testing=False, app_name="Airflow"):
global app, appbuilder
@@ -135,6 +137,24 @@ def init_views(appbuilder):
category='About',
category_icon='fa-th')
+ def integrate_plugins():
+ """Integrate plugins to the context"""
+ from airflow.plugins_manager import (
+ flask_appbuilder_views, flask_appbuilder_menu_links)
+
+ for v in flask_appbuilder_views:
+ log.debug("Adding view %s", v["name"])
+ appbuilder.add_view(v["view"],
+ v["name"],
+ category=v["category"])
+ for ml in sorted(flask_appbuilder_menu_links, key=lambda x:
x["name"]):
+ log.debug("Adding menu link %s", ml["name"])
+ appbuilder.add_link(ml["name"],
+ href=ml["href"],
+ category=ml["category"],
+ category_icon=ml["category_icon"])
+
+ integrate_plugins()
# Garbage collect old permissions/views after they have been
modified.
# Otherwise, when the name of a view or menu is changed, the
framework
# will add the new Views and Menus names to the backend, but will
not
diff --git a/docs/plugins.rst b/docs/plugins.rst
index 3f1f7ee804..eb44247950 100644
--- a/docs/plugins.rst
+++ b/docs/plugins.rst
@@ -72,10 +72,15 @@ looks like:
# A list of objects created from a class derived
# from flask_admin.BaseView
admin_views = []
- # A list of Blueprint object created from flask.Blueprint
+ # A list of Blueprint object created from flask.Blueprint. For use
with the flask_admin based GUI
flask_blueprints = []
- # A list of menu links (flask_admin.base.MenuLink)
+ # A list of menu links (flask_admin.base.MenuLink). For use with the
flask_admin based GUI
menu_links = []
+ # A list of dictionaries containing FlaskAppBuilder BaseView object
and some metadata. See example below
+ appbuilder_views = []
+ # A list of dictionaries containing FlaskAppBuilder BaseView object
and some metadata. See example below
+ appbuilder_menu_items = []
+
You can derive it by inheritance (please refer to the example below).
@@ -159,6 +164,22 @@ definitions in Airflow.
name='Test Menu Link',
url='https://airflow.incubator.apache.org/')
+ # Creating a flask appbuilder BaseView
+ class TestAppBuilderBaseView(AppBuilderBaseView):
+ @expose("/")
+ def test(self):
+ return self.render("test_plugin/test.html", content="Hello
galaxy!")
+ v_appbuilder_view = TestAppBuilderBaseView()
+ v_appbuilder_package = {"name": "Test View",
+ "category": "Test Plugin",
+ "view": v_appbuilder_view}
+
+ # Creating a flask appbuilder Menu Item
+ appbuilder_mitem = {"name": "Google",
+ "category": "Search",
+ "category_icon": "fa-th",
+ "href": "https://www.google.com"}
+
# Defining the plugin class
class AirflowTestPlugin(AirflowPlugin):
name = "test_plugin"
@@ -170,3 +191,13 @@ definitions in Airflow.
admin_views = [v]
flask_blueprints = [bp]
menu_links = [ml]
+ appbuilder_views = [v_appbuilder_package]
+ appbuilder_menu_items = [appbuilder_mitem]
+
+
+Note on role based views
+------------------------
+
+Airflow 1.10 introduced role based views using FlaskAppBuilder. You can
configure which UI is used by setting
+rbac = True. To support plugin views and links for both versions of the UI and
maintain backwards compatibility,
+the fields appbuilder_views and appbuilder_menu_items were added to the
AirflowTestPlugin class.
diff --git a/tests/plugins/test_plugin.py b/tests/plugins/test_plugin.py
index 7b8bc4f61c..aaf1796e68 100644
--- a/tests/plugins/test_plugin.py
+++ b/tests/plugins/test_plugin.py
@@ -23,6 +23,7 @@
from flask import Blueprint
from flask_admin import BaseView, expose
from flask_admin.base import MenuLink
+from flask_appbuilder import BaseView as AppBuilderBaseView
# Importing base classes that we need to derive
from airflow.hooks.base_hook import BaseHook
@@ -67,6 +68,28 @@ def test(self):
v = TestView(category="Test Plugin", name="Test View")
+
+# Creating a flask appbuilder BaseView
+class TestAppBuilderBaseView(AppBuilderBaseView):
+ default_view = "test"
+
+ @expose("/")
+ def test(self):
+ return self.render("test_plugin/test.html", content="Hello galaxy!")
+
+
+v_appbuilder_view = TestAppBuilderBaseView()
+v_appbuilder_package = {"name": "Test View",
+ "category": "Test Plugin",
+ "view": v_appbuilder_view}
+
+# Creating a flask appbuilder Menu Item
+appbuilder_mitem = {"name": "Google",
+ "category": "Search",
+ "category_icon": "fa-th",
+ "href": "https://www.google.com"}
+
+
# Creating a flask blueprint to intergrate the templates and static folder
bp = Blueprint(
"test_plugin", __name__,
@@ -74,10 +97,11 @@ def test(self):
static_folder='static',
static_url_path='/static/test_plugin')
+
ml = MenuLink(
category='Test Plugin',
- name='Test Menu Link',
- url='https://airflow.incubator.apache.org/')
+ name="Test Menu Link",
+ url="https://airflow.incubator.apache.org/")
# Defining the plugin class
@@ -91,3 +115,5 @@ class AirflowTestPlugin(AirflowPlugin):
admin_views = [v]
flask_blueprints = [bp]
menu_links = [ml]
+ appbuilder_views = [v_appbuilder_package]
+ appbuilder_menu_items = [appbuilder_mitem]
diff --git a/tests/plugins_manager.py b/tests/plugins_manager.py
index 9f939c37a0..eeb6494100 100644
--- a/tests/plugins_manager.py
+++ b/tests/plugins_manager.py
@@ -27,11 +27,13 @@
from flask.blueprints import Blueprint
from flask_admin.menu import MenuLink, MenuView
+from airflow.configuration import conf
from airflow.hooks.base_hook import BaseHook
from airflow.models import BaseOperator
from airflow.sensors.base_sensor_operator import BaseSensorOperator
from airflow.executors.base_executor import BaseExecutor
from airflow.www.app import cached_app
+from airflow.www_rbac import app as application
class PluginsTest(unittest.TestCase):
@@ -83,3 +85,42 @@ def test_menu_links(self):
[menu_link] = [ml for ml in category.get_children()
if isinstance(ml, MenuLink)]
self.assertEqual('Test Menu Link', menu_link.name)
+
+
+class PluginsTestRBAC(unittest.TestCase):
+ def setUp(self):
+ conf.load_test_config()
+ self.app, self.appbuilder = application.create_app(testing=True)
+
+ def test_flaskappbuilder_views(self):
+ from tests.plugins.test_plugin import v_appbuilder_package
+ appbuilder_class_name =
str(v_appbuilder_package['view'].__class__.__name__)
+ plugin_views = [view for view in self.appbuilder.baseviews
+ if view.blueprint.name == appbuilder_class_name]
+
+ self.assertTrue(len(plugin_views) == 1)
+
+ # view should have a menu item matching category of
v_appbuilder_package
+ links = [menu_item for menu_item in self.appbuilder.menu.menu
+ if menu_item.name == v_appbuilder_package['category']]
+
+ self.assertTrue(len(links) == 1)
+
+ # menu link should also have a link matching the name of the package.
+ link = links[0]
+ self.assertEqual(link.name, v_appbuilder_package['category'])
+ self.assertEqual(link.childs[0].name, v_appbuilder_package['name'])
+
+ def test_flaskappbuilder_menu_links(self):
+ from tests.plugins.test_plugin import appbuilder_mitem
+
+ # menu item should exist matching appbuilder_mitem
+ links = [menu_item for menu_item in self.appbuilder.menu.menu
+ if menu_item.name == appbuilder_mitem['category']]
+
+ self.assertTrue(len(links) == 1)
+
+ # menu link should also have a link matching the name of the package.
+ link = links[0]
+ self.assertEqual(link.name, appbuilder_mitem['category'])
+ self.assertEqual(link.childs[0].name, appbuilder_mitem['name'])
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]
> RBAC app doesn't integrate plugins (blueprints etc)
> ---------------------------------------------------
>
> Key: AIRFLOW-2744
> URL: https://issues.apache.org/jira/browse/AIRFLOW-2744
> Project: Apache Airflow
> Issue Type: Bug
> Components: webapp, webserver
> Affects Versions: 2.0.0
> Reporter: David Dossett
> Priority: Major
> Fix For: 2.0.0, 1.10.1
>
>
> In the current 1.10.0rc tag, the new RBAC app doesn't integrate any plugins
> created by a user extending Airflow. In the old www/app.py you had the
> [integrate_plugins|https://github.com/apache/incubator-airflow/blob/f1083cbada337731ed0b7e27b09eee7a26c8189a/airflow/www/app.py#L126]
> function. But currently the
> [www_rbac/app.py|https://github.com/apache/incubator-airflow/blob/f1083cbada337731ed0b7e27b09eee7a26c8189a/airflow/www_rbac/app.py]
> doesn't pull in any plugins from the plugin_manager. So nothing you do to
> extend Airflow's webapp will work.
> I think adding the code for registering the blueprints and menu links is a
> pretty simple fix. I'm not sure how the FAB system is handling the same
> functionality as Flask-Admin views though.
>
--
This message was sent by Atlassian JIRA
(v7.6.3#76005)