This is an automated email from the ASF dual-hosted git repository.

bbovenzi pushed a commit to branch revert-appbuilder-changes
in repository https://gitbox.apache.org/repos/asf/airflow.git

commit 1d2bda0d10e8db0bdd9de0f7c587023ce5469023
Author: Brent Bovenzi <[email protected]>
AuthorDate: Wed Nov 17 12:56:38 2021 -0600

    Revert "Copy AppBuilder Base class verbatim from FAB to Airflow (with 
attribution) (#19322)"
    
    This reverts commit 37a12e9c278209d7e8ea914012a31a91a6c6ccff.
---
 airflow/www/extensions/init_appbuilder.py | 608 +-----------------------------
 1 file changed, 11 insertions(+), 597 deletions(-)

diff --git a/airflow/www/extensions/init_appbuilder.py 
b/airflow/www/extensions/init_appbuilder.py
index 5c81f0c..036315b 100644
--- a/airflow/www/extensions/init_appbuilder.py
+++ b/airflow/www/extensions/init_appbuilder.py
@@ -15,607 +15,11 @@
 # specific language governing permissions and limitations
 # under the License.
 
-# This product contains a modified portion of 'Flask App Builder' developed by 
Daniel Vaz Gaspar.
-# (https://github.com/dpgaspar/Flask-AppBuilder).
-# Copyright 2013, Daniel Vaz Gaspar
-
-
-import logging
-from typing import Dict
-
-from flask import Blueprint, current_app, url_for
-from flask_appbuilder import __version__
-from flask_appbuilder.api.manager import OpenApiManager
-from flask_appbuilder.babel.manager import BabelManager
-from flask_appbuilder.base import dynamic_class_import
-from flask_appbuilder.const import (
-    LOGMSG_ERR_FAB_ADD_PERMISSION_MENU,
-    LOGMSG_ERR_FAB_ADD_PERMISSION_VIEW,
-    LOGMSG_ERR_FAB_ADDON_PROCESS,
-    LOGMSG_INF_FAB_ADD_VIEW,
-    LOGMSG_INF_FAB_ADDON_ADDED,
-    LOGMSG_WAR_FAB_VIEW_EXISTS,
-)
-from flask_appbuilder.filters import TemplateFilters
-from flask_appbuilder.menu import Menu, MenuApiManager
-from flask_appbuilder.views import IndexView, UtilView
+from flask_appbuilder import AppBuilder
 
 from airflow import settings
 from airflow.configuration import conf
 
-log = logging.getLogger(__name__)
-
-
-class AirflowAppBuilder:
-    """
-    This is the base class for all the framework.
-    This is were you will register all your views
-    and create the menu structure.
-    Will hold your flask app object, all your views, and security classes.
-    initialize your application like this for SQLAlchemy::
-        from flask import Flask
-        from flask_appbuilder import SQLA, AirflowAppBuilder
-        app = Flask(__name__)
-        app.config.from_object('config')
-        db = SQLA(app)
-        appbuilder = AirflowAppBuilder(app, db.session)
-    When using MongoEngine::
-        from flask import Flask
-        from flask_appbuilder import AirflowAppBuilder
-        from flask_appbuilder.security.mongoengine.manager import 
SecurityManager
-        from flask_mongoengine import MongoEngine
-        app = Flask(__name__)
-        app.config.from_object('config')
-        dbmongo = MongoEngine(app)
-        appbuilder = AirflowAppBuilder(app, 
security_manager_class=SecurityManager)
-    You can also create everything as an application factory.
-    """
-
-    baseviews = []
-    security_manager_class = None
-    # Flask app
-    app = None
-    # Database Session
-    session = None
-    # Security Manager Class
-    sm = None
-    # Babel Manager Class
-    bm = None
-    # OpenAPI Manager Class
-    openapi_manager = None
-    # dict with addon name has key and intantiated class has value
-    addon_managers = None
-    # temporary list that hold addon_managers config key
-    _addon_managers = None
-
-    menu = None
-    indexview = None
-
-    static_folder = None
-    static_url_path = None
-
-    template_filters = None
-
-    def __init__(
-        self,
-        app=None,
-        session=None,
-        menu=None,
-        indexview=None,
-        base_template="appbuilder/baselayout.html",
-        static_folder="static/appbuilder",
-        static_url_path="/appbuilder",
-        security_manager_class=None,
-        update_perms=True,
-    ):
-        """
-        Class constructor
-        :param app:
-            The flask app object
-        :param session:
-            The SQLAlchemy session object
-        :param menu:
-            optional, a previous constructed menu
-        :param indexview:
-            optional, your customized indexview
-        :param static_folder:
-            optional, your override for the global static folder
-        :param static_url_path:
-            optional, your override for the global static url path
-        :param security_manager_class:
-            optional, pass your own security manager class
-        :param update_perms:
-            optional, update permissions flag (Boolean) you can use
-            FAB_UPDATE_PERMS config key also
-        """
-        self.baseviews = []
-        self._addon_managers = []
-        self.addon_managers = {}
-        self.menu = menu
-        self.base_template = base_template
-        self.security_manager_class = security_manager_class
-        self.indexview = indexview
-        self.static_folder = static_folder
-        self.static_url_path = static_url_path
-        self.app = app
-        self.update_perms = update_perms
-
-        if app is not None:
-            self.init_app(app, session)
-
-    def init_app(self, app, session):
-        """
-        Will initialize the Flask app, supporting the app factory pattern.
-        :param app:
-        :param session: The SQLAlchemy session
-        """
-        app.config.setdefault("APP_NAME", "F.A.B.")
-        app.config.setdefault("APP_THEME", "")
-        app.config.setdefault("APP_ICON", "")
-        app.config.setdefault("LANGUAGES", {"en": {"flag": "gb", "name": 
"English"}})
-        app.config.setdefault("ADDON_MANAGERS", [])
-        app.config.setdefault("FAB_API_MAX_PAGE_SIZE", 100)
-        app.config.setdefault("FAB_BASE_TEMPLATE", self.base_template)
-        app.config.setdefault("FAB_STATIC_FOLDER", self.static_folder)
-        app.config.setdefault("FAB_STATIC_URL_PATH", self.static_url_path)
-
-        self.app = app
-
-        self.base_template = app.config.get("FAB_BASE_TEMPLATE", 
self.base_template)
-        self.static_folder = app.config.get("FAB_STATIC_FOLDER", 
self.static_folder)
-        self.static_url_path = app.config.get("FAB_STATIC_URL_PATH", 
self.static_url_path)
-        _index_view = app.config.get("FAB_INDEX_VIEW", None)
-        if _index_view is not None:
-            self.indexview = dynamic_class_import(_index_view)
-        else:
-            self.indexview = self.indexview or IndexView
-        _menu = app.config.get("FAB_MENU", None)
-        if _menu is not None:
-            self.menu = dynamic_class_import(_menu)
-        else:
-            self.menu = self.menu or Menu()
-
-        if self.update_perms:  # default is True, if False takes precedence 
from config
-            self.update_perms = app.config.get("FAB_UPDATE_PERMS", True)
-        _security_manager_class_name = 
app.config.get("FAB_SECURITY_MANAGER_CLASS", None)
-        if _security_manager_class_name is not None:
-            self.security_manager_class = 
dynamic_class_import(_security_manager_class_name)
-        if self.security_manager_class is None:
-            from flask_appbuilder.security.sqla.manager import SecurityManager
-
-            self.security_manager_class = SecurityManager
-
-        self._addon_managers = app.config["ADDON_MANAGERS"]
-        self.session = session
-        self.sm = self.security_manager_class(self)
-        self.bm = BabelManager(self)
-        self.openapi_manager = OpenApiManager(self)
-        self.menuapi_manager = MenuApiManager(self)
-        self._add_global_static()
-        self._add_global_filters()
-        app.before_request(self.sm.before_request)
-        self._add_admin_views()
-        self._add_addon_views()
-        if self.app:
-            self._add_menu_permissions()
-        else:
-            self.post_init()
-        self._init_extension(app)
-
-    def _init_extension(self, app):
-        app.appbuilder = self
-        if not hasattr(app, "extensions"):
-            app.extensions = {}
-        app.extensions["appbuilder"] = self
-
-    def post_init(self):
-        for baseview in self.baseviews:
-            # instantiate the views and add session
-            self._check_and_init(baseview)
-            # Register the views has blueprints
-            if baseview.__class__.__name__ not in 
self.get_app.blueprints.keys():
-                self.register_blueprint(baseview)
-            # Add missing permissions where needed
-        self.add_permissions()
-
-    @property
-    def get_app(self):
-        """
-        Get current or configured flask app
-        :return: Flask App
-        """
-        if self.app:
-            return self.app
-        else:
-            return current_app
-
-    @property
-    def get_session(self):
-        """
-        Get the current sqlalchemy session.
-        :return: SQLAlchemy Session
-        """
-        return self.session
-
-    @property
-    def app_name(self):
-        """
-        Get the App name
-        :return: String with app name
-        """
-        return self.get_app.config["APP_NAME"]
-
-    @property
-    def app_theme(self):
-        """
-        Get the App theme name
-        :return: String app theme name
-        """
-        return self.get_app.config["APP_THEME"]
-
-    @property
-    def app_icon(self):
-        """
-        Get the App icon location
-        :return: String with relative app icon location
-        """
-        return self.get_app.config["APP_ICON"]
-
-    @property
-    def languages(self):
-        return self.get_app.config["LANGUAGES"]
-
-    @property
-    def version(self):
-        """
-        Get the current F.A.B. version
-        :return: String with the current F.A.B. version
-        """
-        return __version__
-
-    def _add_global_filters(self):
-        self.template_filters = TemplateFilters(self.get_app, self.sm)
-
-    def _add_global_static(self):
-        bp = Blueprint(
-            "appbuilder",
-            __name__,
-            url_prefix="/static",
-            template_folder="templates",
-            static_folder=self.static_folder,
-            static_url_path=self.static_url_path,
-        )
-        self.get_app.register_blueprint(bp)
-
-    def _add_admin_views(self):
-        """Registers indexview, utilview (back function), babel views and 
Security views."""
-        self.indexview = self._check_and_init(self.indexview)
-        self.add_view_no_menu(self.indexview)
-        self.add_view_no_menu(UtilView())
-        self.bm.register_views()
-        self.sm.register_views()
-        self.openapi_manager.register_views()
-        self.menuapi_manager.register_views()
-
-    def _add_addon_views(self):
-        """Registers declared addon's"""
-        for addon in self._addon_managers:
-            addon_class = dynamic_class_import(addon)
-            if addon_class:
-                # Instantiate manager with appbuilder (self)
-                addon_class = addon_class(self)
-                try:
-                    addon_class.pre_process()
-                    addon_class.register_views()
-                    addon_class.post_process()
-                    self.addon_managers[addon] = addon_class
-                    log.info(LOGMSG_INF_FAB_ADDON_ADDED.format(str(addon)))
-                except Exception as e:
-                    log.exception(e)
-                    log.error(LOGMSG_ERR_FAB_ADDON_PROCESS.format(addon, e))
-
-    def _check_and_init(self, baseview):
-        if hasattr(baseview, 'datamodel'):
-            # Delete sessions if initiated previously to limit side effects. 
We want to use
-            # the current session in the current application.
-            baseview.datamodel.session = self.session
-        if hasattr(baseview, "__call__"):
-            baseview = baseview()
-        return baseview
-
-    def add_view(
-        self,
-        baseview,
-        name,
-        href="",
-        icon="",
-        label="",
-        category="",
-        category_icon="",
-        category_label="",
-        menu_cond=None,
-    ):
-        """
-        Add your views associated with menus using this method.
-        :param baseview:
-            A BaseView type class instantiated or not.
-            This method will instantiate the class for you if needed.
-        :param name:
-            The string name that identifies the menu.
-        :param href:
-            Override the generated href for the menu.
-            You can use an url string or an endpoint name
-            if non provided default_view from view will be set as href.
-        :param icon:
-            Font-Awesome icon name, optional.
-        :param label:
-            The label that will be displayed on the menu,
-            if absent param name will be used
-        :param category:
-            The menu category where the menu will be included,
-            if non provided the view will be accessible as a top menu.
-        :param category_icon:
-            Font-Awesome icon name for the category, optional.
-        :param category_label:
-            The label that will be displayed on the menu,
-            if absent param name will be used
-        :param menu_cond:
-            If a callable, :code:`menu_cond` will be invoked when
-            constructing the menu items. If it returns :code:`True`,
-            then this link will be a part of the menu. Otherwise, it
-            will not be included in the menu items. Defaults to
-            :code:`None`, meaning the item will always be present.
-        Examples::
-            appbuilder = AirflowAppBuilder(app, db)
-            # Register a view, rendering a top menu without icon.
-            appbuilder.add_view(MyModelView(), "My View")
-            # or not instantiated
-            appbuilder.add_view(MyModelView, "My View")
-            # Register a view, a submenu "Other View" from "Other" with a 
phone icon.
-            appbuilder.add_view(
-                MyOtherModelView,
-                "Other View",
-                icon='fa-phone',
-                category="Others"
-            )
-            # Register a view, with category icon and translation.
-            appbuilder.add_view(
-                YetOtherModelView,
-                "Other View",
-                icon='fa-phone',
-                label=_('Other View'),
-                category="Others",
-                category_icon='fa-envelop',
-                category_label=_('Other View')
-            )
-            # Register a view whose menu item will be conditionally displayed
-            appbuilder.add_view(
-                YourFeatureView,
-                "Your Feature",
-                icon='fa-feature',
-                label=_('Your Feature'),
-                menu_cond=lambda: is_feature_enabled("your-feature"),
-            )
-            # Add a link
-            appbuilder.add_link("google", href="www.google.com", icon = 
"fa-google-plus")
-        """
-        baseview = self._check_and_init(baseview)
-        log.info(LOGMSG_INF_FAB_ADD_VIEW.format(baseview.__class__.__name__, 
name))
-
-        if not self._view_exists(baseview):
-            baseview.appbuilder = self
-            self.baseviews.append(baseview)
-            self._process_inner_views()
-            if self.app:
-                self.register_blueprint(baseview)
-                self._add_permission(baseview)
-        self.add_link(
-            name=name,
-            href=href,
-            icon=icon,
-            label=label,
-            category=category,
-            category_icon=category_icon,
-            category_label=category_label,
-            baseview=baseview,
-            cond=menu_cond,
-        )
-        return baseview
-
-    def add_link(
-        self,
-        name,
-        href,
-        icon="",
-        label="",
-        category="",
-        category_icon="",
-        category_label="",
-        baseview=None,
-        cond=None,
-    ):
-        """
-        Add your own links to menu using this method
-        :param name:
-            The string name that identifies the menu.
-        :param href:
-            Override the generated href for the menu.
-            You can use an url string or an endpoint name
-        :param icon:
-            Font-Awesome icon name, optional.
-        :param label:
-            The label that will be displayed on the menu,
-            if absent param name will be used
-        :param category:
-            The menu category where the menu will be included,
-            if non provided the view will be accessible as a top menu.
-        :param category_icon:
-            Font-Awesome icon name for the category, optional.
-        :param category_label:
-            The label that will be displayed on the menu,
-            if absent param name will be used
-        :param cond:
-            If a callable, :code:`cond` will be invoked when
-            constructing the menu items. If it returns :code:`True`,
-            then this link will be a part of the menu. Otherwise, it
-            will not be included in the menu items. Defaults to
-            :code:`None`, meaning the item will always be present.
-        """
-        self.menu.add_link(
-            name=name,
-            href=href,
-            icon=icon,
-            label=label,
-            category=category,
-            category_icon=category_icon,
-            category_label=category_label,
-            baseview=baseview,
-            cond=cond,
-        )
-        if self.app:
-            self._add_permissions_menu(name)
-            if category:
-                self._add_permissions_menu(category)
-
-    def add_separator(self, category, cond=None):
-        """
-        Add a separator to the menu, you will sequentially create the menu
-        :param category:
-            The menu category where the separator will be included.
-        :param cond:
-            If a callable, :code:`cond` will be invoked when
-            constructing the menu items. If it returns :code:`True`,
-            then this separator will be a part of the menu. Otherwise,
-            it will not be included in the menu items. Defaults to
-            :code:`None`, meaning the separator will always be present.
-        """
-        self.menu.add_separator(category, cond=cond)
-
-    def add_view_no_menu(self, baseview, endpoint=None, static_folder=None):
-        """
-            Add your views without creating a menu.
-        :param baseview:
-            A BaseView type class instantiated.
-        """
-        baseview = self._check_and_init(baseview)
-        log.info(LOGMSG_INF_FAB_ADD_VIEW.format(baseview.__class__.__name__, 
""))
-
-        if not self._view_exists(baseview):
-            baseview.appbuilder = self
-            self.baseviews.append(baseview)
-            self._process_inner_views()
-            if self.app:
-                self.register_blueprint(baseview, endpoint=endpoint, 
static_folder=static_folder)
-                self._add_permission(baseview)
-        else:
-            
log.warning(LOGMSG_WAR_FAB_VIEW_EXISTS.format(baseview.__class__.__name__))
-        return baseview
-
-    def add_api(self, baseview):
-        """
-            Add a BaseApi class or child to AirflowAppBuilder
-        :param baseview: A BaseApi type class
-        :return: The instantiated base view
-        """
-        return self.add_view_no_menu(baseview)
-
-    def security_cleanup(self):
-        """
-        This method is useful if you have changed
-        the name of your menus or classes,
-        changing them will leave behind permissions
-        that are not associated with anything.
-        You can use it always or just sometimes to
-        perform a security cleanup. Warning this will delete any permission
-        that is no longer part of any registered view or menu.
-        Remember invoke ONLY AFTER YOU HAVE REGISTERED ALL VIEWS
-        """
-        self.sm.security_cleanup(self.baseviews, self.menu)
-
-    def security_converge(self, dry=False) -> Dict:
-        """
-            This method is useful when you use:
-            - `class_permission_name`
-            - `previous_class_permission_name`
-            - `method_permission_name`
-            - `previous_method_permission_name`
-            migrates all permissions to the new names on all the Roles
-        :param dry: If True will not change DB
-        :return: Dict with all computed necessary operations
-        """
-        return self.sm.security_converge(self.baseviews, self.menu, dry)
-
-    @property
-    def get_url_for_login(self):
-        return url_for(f"{self.sm.auth_view.endpoint}.login")
-
-    @property
-    def get_url_for_logout(self):
-        return url_for(f"{self.sm.auth_view.endpoint}.logout")
-
-    @property
-    def get_url_for_index(self):
-        return 
url_for(f"{self.indexview.endpoint}.{self.indexview.default_view}")
-
-    @property
-    def get_url_for_userinfo(self):
-        return url_for(f"{self.sm.user_view.endpoint}.userinfo")
-
-    def get_url_for_locale(self, lang):
-        return url_for(
-            
f"{self.bm.locale_view.endpoint}.{self.bm.locale_view.default_view}",
-            locale=lang,
-        )
-
-    def add_permissions(self, update_perms=False):
-        if self.update_perms or update_perms:
-            for baseview in self.baseviews:
-                self._add_permission(baseview, update_perms=update_perms)
-            self._add_menu_permissions(update_perms=update_perms)
-
-    def _add_permission(self, baseview, update_perms=False):
-        if self.update_perms or update_perms:
-            try:
-                self.sm.add_permissions_view(baseview.base_permissions, 
baseview.class_permission_name)
-            except Exception as e:
-                log.exception(e)
-                log.error(LOGMSG_ERR_FAB_ADD_PERMISSION_VIEW.format(str(e)))
-
-    def _add_permissions_menu(self, name, update_perms=False):
-        if self.update_perms or update_perms:
-            try:
-                self.sm.add_permissions_menu(name)
-            except Exception as e:
-                log.exception(e)
-                log.error(LOGMSG_ERR_FAB_ADD_PERMISSION_MENU.format(str(e)))
-
-    def _add_menu_permissions(self, update_perms=False):
-        if self.update_perms or update_perms:
-            for category in self.menu.get_list():
-                self._add_permissions_menu(category.name, 
update_perms=update_perms)
-                for item in category.childs:
-                    # don't add permission for menu separator
-                    if item.name != "-":
-                        self._add_permissions_menu(item.name, 
update_perms=update_perms)
-
-    def register_blueprint(self, baseview, endpoint=None, static_folder=None):
-        self.get_app.register_blueprint(
-            baseview.create_blueprint(self, endpoint=endpoint, 
static_folder=static_folder)
-        )
-
-    def _view_exists(self, view):
-        for baseview in self.baseviews:
-            if baseview.__class__ == view.__class__:
-                return True
-        return False
-
-    def _process_inner_views(self):
-        for view in self.baseviews:
-            for inner_class in view.get_uninit_inner_views():
-                for v in self.baseviews:
-                    if isinstance(v, inner_class) and v not in 
view.get_init_inner_views():
-                        view.get_init_inner_views().append(v)
-
 
 def init_appbuilder(app):
     """Init `Flask App Builder 
<https://flask-appbuilder.readthedocs.io/en/latest/>`__."""
@@ -629,6 +33,16 @@ def init_appbuilder(app):
              not FAB's security manager."""
         )
 
+    class AirflowAppBuilder(AppBuilder):
+        """Custom class to prevent side effects of the session."""
+
+        def _check_and_init(self, baseview):
+            if hasattr(baseview, 'datamodel'):
+                # Delete sessions if initiated previously to limit side 
effects. We want to use
+                # the current session in the current application.
+                baseview.datamodel.session = None
+            return super()._check_and_init(baseview)
+
     AirflowAppBuilder(
         app=app,
         session=settings.Session,

Reply via email to