| # 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 __future__ import annotations |
| |
| import logging |
| import os |
| from typing import Any, Callable, Dict, TYPE_CHECKING |
| |
| import wtforms_json |
| from deprecation import deprecated |
| from flask import Flask, redirect |
| from flask_appbuilder import expose, IndexView |
| from flask_babel import gettext as __, lazy_gettext as _ |
| from flask_compress import Compress |
| from werkzeug.middleware.proxy_fix import ProxyFix |
| |
| from superset.connectors.connector_registry import ConnectorRegistry |
| from superset.extensions import ( |
| _event_logger, |
| APP_DIR, |
| appbuilder, |
| async_query_manager, |
| cache_manager, |
| celery_app, |
| csrf, |
| db, |
| encrypted_field_factory, |
| feature_flag_manager, |
| machine_auth_provider_factory, |
| manifest_processor, |
| migrate, |
| profiling, |
| results_backend_manager, |
| talisman, |
| ) |
| from superset.security import SupersetSecurityManager |
| from superset.typing import FlaskResponse |
| from superset.utils.core import pessimistic_connection_handling |
| from superset.utils.log import DBEventLogger, get_event_logger_from_cfg_value |
| |
| if TYPE_CHECKING: |
| from superset.app import SupersetApp |
| |
| logger = logging.getLogger(__name__) |
| |
| |
| # pylint: disable=R0904 |
| class SupersetAppInitializer: |
| def __init__(self, app: SupersetApp) -> None: |
| super().__init__() |
| |
| self.superset_app = app |
| self.config = app.config |
| self.manifest: Dict[Any, Any] = {} |
| |
| @deprecated(details="use self.superset_app instead of self.flask_app") # type: ignore # pylint: disable=line-too-long,useless-suppression |
| @property |
| def flask_app(self) -> SupersetApp: |
| return self.superset_app |
| |
| def pre_init(self) -> None: |
| """ |
| Called before all other init tasks are complete |
| """ |
| wtforms_json.init() |
| |
| if not os.path.exists(self.config["DATA_DIR"]): |
| os.makedirs(self.config["DATA_DIR"]) |
| |
| def post_init(self) -> None: |
| """ |
| Called after any other init tasks |
| """ |
| |
| def configure_celery(self) -> None: |
| celery_app.config_from_object(self.config["CELERY_CONFIG"]) |
| celery_app.set_default() |
| superset_app = self.superset_app |
| |
| # Here, we want to ensure that every call into Celery task has an app context |
| # setup properly |
| task_base = celery_app.Task |
| |
| class AppContextTask(task_base): # type: ignore |
| # pylint: disable=too-few-public-methods |
| abstract = True |
| |
| # Grab each call into the task and set up an app context |
| def __call__(self, *args: Any, **kwargs: Any) -> Any: |
| with superset_app.app_context(): |
| return task_base.__call__(self, *args, **kwargs) |
| |
| celery_app.Task = AppContextTask |
| |
| def init_views(self) -> None: |
| # |
| # We're doing local imports, as several of them import |
| # models which in turn try to import |
| # the global Flask app |
| # |
| # pylint: disable=import-outside-toplevel,too-many-locals,too-many-statements |
| from superset.annotation_layers.api import AnnotationLayerRestApi |
| from superset.annotation_layers.annotations.api import AnnotationRestApi |
| from superset.async_events.api import AsyncEventsRestApi |
| from superset.cachekeys.api import CacheRestApi |
| from superset.charts.api import ChartRestApi |
| from superset.connectors.druid.views import ( |
| Druid, |
| DruidClusterModelView, |
| DruidColumnInlineView, |
| DruidDatasourceModelView, |
| DruidMetricInlineView, |
| ) |
| from superset.connectors.sqla.views import ( |
| RowLevelSecurityFiltersModelView, |
| SqlMetricInlineView, |
| TableColumnInlineView, |
| TableModelView, |
| ) |
| from superset.css_templates.api import CssTemplateRestApi |
| from superset.dashboards.api import DashboardRestApi |
| from superset.databases.api import DatabaseRestApi |
| from superset.datasets.api import DatasetRestApi |
| from superset.datasets.columns.api import DatasetColumnsRestApi |
| from superset.datasets.metrics.api import DatasetMetricRestApi |
| from superset.queries.api import QueryRestApi |
| from superset.security.api import SecurityRestApi |
| from superset.queries.saved_queries.api import SavedQueryRestApi |
| from superset.reports.api import ReportScheduleRestApi |
| from superset.reports.logs.api import ReportExecutionLogRestApi |
| from superset.views.access_requests import AccessRequestsModelView |
| from superset.views.alerts import ( |
| AlertLogModelView, |
| AlertModelView, |
| AlertObservationModelView, |
| AlertView, |
| ReportView, |
| ) |
| from superset.views.annotations import ( |
| AnnotationLayerModelView, |
| AnnotationModelView, |
| ) |
| from superset.views.api import Api |
| from superset.views.chart.views import SliceAsync, SliceModelView |
| from superset.views.core import Superset |
| from superset.views.css_templates import ( |
| CssTemplateAsyncModelView, |
| CssTemplateModelView, |
| ) |
| from superset.views.dashboard.views import ( |
| Dashboard, |
| DashboardModelView, |
| DashboardModelViewAsync, |
| ) |
| from superset.views.database.views import ( |
| ColumnarToDatabaseView, |
| CsvToDatabaseView, |
| DatabaseView, |
| ExcelToDatabaseView, |
| ) |
| from superset.views.datasource.views import Datasource |
| from superset.views.dynamic_plugins import DynamicPluginsView |
| from superset.views.key_value import KV |
| from superset.views.log.api import LogRestApi |
| from superset.views.log.views import LogModelView |
| from superset.views.redirects import R |
| from superset.views.schedules import ( |
| DashboardEmailScheduleView, |
| SliceEmailScheduleView, |
| ) |
| from superset.views.sql_lab import ( |
| SavedQueryView, |
| SavedQueryViewApi, |
| SqlLab, |
| TableSchemaView, |
| TabStateView, |
| ) |
| from superset.views.tags import TagView |
| |
| # |
| # Setup API views |
| # |
| appbuilder.add_api(AnnotationRestApi) |
| appbuilder.add_api(AnnotationLayerRestApi) |
| appbuilder.add_api(AsyncEventsRestApi) |
| appbuilder.add_api(CacheRestApi) |
| appbuilder.add_api(ChartRestApi) |
| appbuilder.add_api(CssTemplateRestApi) |
| appbuilder.add_api(DashboardRestApi) |
| appbuilder.add_api(DatabaseRestApi) |
| appbuilder.add_api(DatasetRestApi) |
| appbuilder.add_api(DatasetColumnsRestApi) |
| appbuilder.add_api(DatasetMetricRestApi) |
| appbuilder.add_api(QueryRestApi) |
| appbuilder.add_api(SavedQueryRestApi) |
| appbuilder.add_api(ReportScheduleRestApi) |
| appbuilder.add_api(ReportExecutionLogRestApi) |
| # |
| # Setup regular views |
| # |
| appbuilder.add_link( |
| "Home", |
| label=__("Home"), |
| href="/superset/welcome/", |
| cond=lambda: bool(appbuilder.app.config["LOGO_TARGET_PATH"]), |
| ) |
| appbuilder.add_view( |
| AnnotationLayerModelView, |
| "Annotation Layers", |
| label=__("Annotation Layers"), |
| icon="fa-comment", |
| category="Manage", |
| category_label=__("Manage"), |
| category_icon="", |
| ) |
| appbuilder.add_view( |
| DashboardModelView, |
| "Dashboards", |
| label=__("Dashboards"), |
| icon="fa-dashboard", |
| category="", |
| category_icon="", |
| ) |
| appbuilder.add_view( |
| SliceModelView, |
| "Charts", |
| label=__("Charts"), |
| icon="fa-bar-chart", |
| category="", |
| category_icon="", |
| ) |
| 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", |
| label=__("CSS Templates"), |
| icon="fa-css3", |
| category="Manage", |
| category_label=__("Manage"), |
| category_icon="", |
| ) |
| appbuilder.add_view( |
| RowLevelSecurityFiltersModelView, |
| "Row Level Security", |
| label=__("Row level security"), |
| category="Security", |
| category_label=__("Security"), |
| icon="fa-lock", |
| menu_cond=lambda: feature_flag_manager.is_feature_enabled( |
| "ROW_LEVEL_SECURITY" |
| ), |
| ) |
| |
| # |
| # Setup views with no menu |
| # |
| appbuilder.add_view_no_menu(Api) |
| appbuilder.add_view_no_menu(CssTemplateAsyncModelView) |
| appbuilder.add_view_no_menu(CsvToDatabaseView) |
| appbuilder.add_view_no_menu(ExcelToDatabaseView) |
| appbuilder.add_view_no_menu(ColumnarToDatabaseView) |
| appbuilder.add_view_no_menu(Dashboard) |
| appbuilder.add_view_no_menu(DashboardModelViewAsync) |
| appbuilder.add_view_no_menu(Datasource) |
| appbuilder.add_view_no_menu(KV) |
| appbuilder.add_view_no_menu(R) |
| appbuilder.add_view_no_menu(SavedQueryView) |
| appbuilder.add_view_no_menu(SavedQueryViewApi) |
| appbuilder.add_view_no_menu(SliceAsync) |
| appbuilder.add_view_no_menu(SqlLab) |
| appbuilder.add_view_no_menu(SqlMetricInlineView) |
| appbuilder.add_view_no_menu(AnnotationModelView) |
| appbuilder.add_view_no_menu(Superset) |
| appbuilder.add_view_no_menu(TableColumnInlineView) |
| appbuilder.add_view_no_menu(TableModelView) |
| appbuilder.add_view_no_menu(TableSchemaView) |
| appbuilder.add_view_no_menu(TabStateView) |
| appbuilder.add_view_no_menu(TagView) |
| |
| # |
| # Add links |
| # |
| appbuilder.add_link( |
| "Import Dashboards", |
| label=__("Import Dashboards"), |
| href="/superset/import_dashboards/", |
| icon="fa-cloud-upload", |
| category="Manage", |
| category_label=__("Manage"), |
| category_icon="fa-wrench", |
| cond=lambda: not feature_flag_manager.is_feature_enabled( |
| "VERSIONED_EXPORT" |
| ), |
| ) |
| appbuilder.add_link( |
| "SQL Editor", |
| label=_("SQL Editor"), |
| href="/superset/sqllab/", |
| category_icon="fa-flask", |
| icon="fa-flask", |
| category="SQL Lab", |
| category_label=__("SQL Lab"), |
| ) |
| appbuilder.add_link( |
| __("Saved Queries"), |
| href="/savedqueryview/list/", |
| icon="fa-save", |
| category="SQL Lab", |
| ) |
| appbuilder.add_link( |
| "Query Search", |
| label=_("Query History"), |
| href="/superset/sqllab/history/", |
| icon="fa-search", |
| category_icon="fa-flask", |
| category="SQL Lab", |
| category_label=__("SQL Lab"), |
| ) |
| appbuilder.add_view( |
| DatabaseView, |
| "Databases", |
| label=__("Databases"), |
| icon="fa-database", |
| category="Data", |
| category_label=__("Data"), |
| category_icon="fa-database", |
| ) |
| appbuilder.add_link( |
| "Datasets", |
| label=__("Datasets"), |
| href="/tablemodelview/list/", |
| icon="fa-table", |
| category="Data", |
| category_label=__("Data"), |
| category_icon="fa-table", |
| ) |
| appbuilder.add_separator("Data") |
| appbuilder.add_link( |
| "Upload a CSV", |
| label=__("Upload a CSV"), |
| href="/csvtodatabaseview/form", |
| icon="fa-upload", |
| category="Data", |
| category_label=__("Data"), |
| category_icon="fa-wrench", |
| cond=lambda: bool( |
| self.config["CSV_EXTENSIONS"].intersection( |
| self.config["ALLOWED_EXTENSIONS"] |
| ) |
| ), |
| ) |
| appbuilder.add_link( |
| "Upload a Columnar file", |
| label=__("Upload a Columnar file"), |
| href="/columnartodatabaseview/form", |
| icon="fa-upload", |
| category="Data", |
| category_label=__("Data"), |
| category_icon="fa-wrench", |
| cond=lambda: bool( |
| self.config["COLUMNAR_EXTENSIONS"].intersection( |
| self.config["ALLOWED_EXTENSIONS"] |
| ) |
| ), |
| ) |
| try: |
| import xlrd # pylint: disable=unused-import |
| |
| appbuilder.add_link( |
| "Upload Excel", |
| label=__("Upload Excel"), |
| href="/exceltodatabaseview/form", |
| icon="fa-upload", |
| category="Data", |
| category_label=__("Data"), |
| category_icon="fa-wrench", |
| cond=lambda: bool( |
| self.config["EXCEL_EXTENSIONS"].intersection( |
| self.config["ALLOWED_EXTENSIONS"] |
| ) |
| ), |
| ) |
| except ImportError: |
| pass |
| |
| appbuilder.add_api(LogRestApi) |
| appbuilder.add_view( |
| LogModelView, |
| "Action Log", |
| label=__("Action Log"), |
| category="Security", |
| category_label=__("Security"), |
| icon="fa-list-ol", |
| menu_cond=lambda: ( |
| self.config["FAB_ADD_SECURITY_VIEWS"] |
| and self.config["SUPERSET_LOG_VIEW"] |
| ), |
| ) |
| appbuilder.add_api(SecurityRestApi) |
| # |
| # Conditionally setup email views |
| # |
| if self.config["ENABLE_SCHEDULED_EMAIL_REPORTS"]: |
| logging.warning( |
| "ENABLE_SCHEDULED_EMAIL_REPORTS " |
| "is deprecated and will be removed in version 2.0.0" |
| ) |
| |
| appbuilder.add_separator( |
| "Manage", cond=lambda: self.config["ENABLE_SCHEDULED_EMAIL_REPORTS"] |
| ) |
| appbuilder.add_view( |
| DashboardEmailScheduleView, |
| "Dashboard Email Schedules", |
| label=__("Dashboard Emails"), |
| category="Manage", |
| category_label=__("Manage"), |
| icon="fa-search", |
| menu_cond=lambda: self.config["ENABLE_SCHEDULED_EMAIL_REPORTS"], |
| ) |
| appbuilder.add_view( |
| SliceEmailScheduleView, |
| "Chart Emails", |
| label=__("Chart Email Schedules"), |
| category="Manage", |
| category_label=__("Manage"), |
| icon="fa-search", |
| menu_cond=lambda: self.config["ENABLE_SCHEDULED_EMAIL_REPORTS"], |
| ) |
| |
| if self.config["ENABLE_ALERTS"]: |
| logging.warning( |
| "ENABLE_ALERTS is deprecated and will be removed in version 2.0.0" |
| ) |
| |
| appbuilder.add_view( |
| AlertModelView, |
| "Alerts", |
| label=__("Alerts"), |
| category="Manage", |
| category_label=__("Manage"), |
| icon="fa-exclamation-triangle", |
| menu_cond=lambda: bool(self.config["ENABLE_ALERTS"]), |
| ) |
| appbuilder.add_view_no_menu(AlertLogModelView) |
| appbuilder.add_view_no_menu(AlertObservationModelView) |
| |
| appbuilder.add_view( |
| AlertView, |
| "Alerts & Report", |
| label=__("Alerts & Reports"), |
| category="Manage", |
| category_label=__("Manage"), |
| icon="fa-exclamation-triangle", |
| menu_cond=lambda: feature_flag_manager.is_feature_enabled("ALERT_REPORTS"), |
| ) |
| appbuilder.add_view_no_menu(ReportView) |
| |
| appbuilder.add_view( |
| AccessRequestsModelView, |
| "Access requests", |
| label=__("Access requests"), |
| category="Security", |
| category_label=__("Security"), |
| icon="fa-table", |
| menu_cond=lambda: bool(self.config["ENABLE_ACCESS_REQUEST"]), |
| ) |
| |
| # |
| # Druid Views |
| # |
| appbuilder.add_separator( |
| "Data", cond=lambda: bool(self.config["DRUID_IS_ACTIVE"]) |
| ) |
| appbuilder.add_view( |
| DruidDatasourceModelView, |
| "Druid Datasources", |
| label=__("Druid Datasources"), |
| category="Data", |
| category_label=__("Data"), |
| icon="fa-cube", |
| menu_cond=lambda: bool(self.config["DRUID_IS_ACTIVE"]), |
| ) |
| appbuilder.add_view( |
| DruidClusterModelView, |
| name="Druid Clusters", |
| label=__("Druid Clusters"), |
| icon="fa-cubes", |
| category="Data", |
| category_label=__("Data"), |
| category_icon="fa-database", |
| menu_cond=lambda: bool(self.config["DRUID_IS_ACTIVE"]), |
| ) |
| appbuilder.add_view_no_menu(DruidMetricInlineView) |
| appbuilder.add_view_no_menu(DruidColumnInlineView) |
| appbuilder.add_view_no_menu(Druid) |
| |
| appbuilder.add_link( |
| "Scan New Datasources", |
| label=__("Scan New Datasources"), |
| href="/druid/scan_new_datasources/", |
| category="Data", |
| category_label=__("Data"), |
| category_icon="fa-database", |
| icon="fa-refresh", |
| cond=lambda: bool( |
| self.config["DRUID_IS_ACTIVE"] |
| and self.config["DRUID_METADATA_LINKS_ENABLED"] |
| ), |
| ) |
| appbuilder.add_link( |
| "Refresh Druid Metadata", |
| label=__("Refresh Druid Metadata"), |
| href="/druid/refresh_datasources/", |
| category="Data", |
| category_label=__("Data"), |
| category_icon="fa-database", |
| icon="fa-cog", |
| cond=lambda: bool( |
| self.config["DRUID_IS_ACTIVE"] |
| and self.config["DRUID_METADATA_LINKS_ENABLED"] |
| ), |
| ) |
| appbuilder.add_separator( |
| "Data", cond=lambda: bool(self.config["DRUID_IS_ACTIVE"]) |
| ) |
| |
| def init_app_in_ctx(self) -> None: |
| """ |
| Runs init logic in the context of the app |
| """ |
| self.configure_fab() |
| self.configure_url_map_converters() |
| self.configure_data_sources() |
| self.configure_auth_provider() |
| self.configure_async_queries() |
| |
| # Hook that provides administrators a handle on the Flask APP |
| # after initialization |
| flask_app_mutator = self.config["FLASK_APP_MUTATOR"] |
| if flask_app_mutator: |
| flask_app_mutator(self.superset_app) |
| |
| self.init_views() |
| |
| def init_app(self) -> None: |
| """ |
| Main entry point which will delegate to other methods in |
| order to fully init the app |
| """ |
| self.pre_init() |
| # Configuration of logging must be done first to apply the formatter properly |
| self.configure_logging() |
| # Configuration of feature_flags must be done first to allow init features |
| # conditionally |
| self.configure_feature_flags() |
| self.configure_db_encrypt() |
| self.setup_db() |
| self.configure_celery() |
| self.enable_profiling() |
| self.setup_event_logger() |
| self.setup_bundle_manifest() |
| self.register_blueprints() |
| self.configure_wtf() |
| self.configure_middlewares() |
| self.configure_cache() |
| |
| with self.superset_app.app_context(): |
| self.init_app_in_ctx() |
| |
| self.post_init() |
| |
| def configure_auth_provider(self) -> None: |
| machine_auth_provider_factory.init_app(self.superset_app) |
| |
| def setup_event_logger(self) -> None: |
| _event_logger["event_logger"] = get_event_logger_from_cfg_value( |
| self.superset_app.config.get("EVENT_LOGGER", DBEventLogger()) |
| ) |
| |
| def configure_data_sources(self) -> None: |
| # Registering sources |
| module_datasource_map = self.config["DEFAULT_MODULE_DS_MAP"] |
| module_datasource_map.update(self.config["ADDITIONAL_MODULE_DS_MAP"]) |
| ConnectorRegistry.register_sources(module_datasource_map) |
| |
| def configure_cache(self) -> None: |
| cache_manager.init_app(self.superset_app) |
| results_backend_manager.init_app(self.superset_app) |
| |
| def configure_feature_flags(self) -> None: |
| feature_flag_manager.init_app(self.superset_app) |
| |
| def configure_fab(self) -> None: |
| if self.config["SILENCE_FAB"]: |
| logging.getLogger("flask_appbuilder").setLevel(logging.ERROR) |
| |
| custom_sm = self.config["CUSTOM_SECURITY_MANAGER"] or SupersetSecurityManager |
| if not issubclass(custom_sm, SupersetSecurityManager): |
| raise Exception( |
| """Your CUSTOM_SECURITY_MANAGER must now extend SupersetSecurityManager, |
| not FAB's security manager. |
| See [4565] in UPDATING.md""" |
| ) |
| |
| appbuilder.indexview = SupersetIndexView |
| appbuilder.base_template = "superset/base.html" |
| appbuilder.security_manager_class = custom_sm |
| appbuilder.init_app(self.superset_app, db.session) |
| |
| def configure_url_map_converters(self) -> None: |
| # |
| # Doing local imports here as model importing causes a reference to |
| # app.config to be invoked and we need the current_app to have been setup |
| # |
| # pylint: disable=import-outside-toplevel |
| from superset.utils.url_map_converters import ( |
| ObjectTypeConverter, |
| RegexConverter, |
| ) |
| |
| self.superset_app.url_map.converters["regex"] = RegexConverter |
| self.superset_app.url_map.converters["object_type"] = ObjectTypeConverter |
| |
| def configure_middlewares(self) -> None: |
| if self.config["ENABLE_CORS"]: |
| # pylint: disable=import-outside-toplevel |
| from flask_cors import CORS |
| |
| CORS(self.superset_app, **self.config["CORS_OPTIONS"]) |
| |
| if self.config["ENABLE_PROXY_FIX"]: |
| self.superset_app.wsgi_app = ProxyFix( # type: ignore |
| self.superset_app.wsgi_app, **self.config["PROXY_FIX_CONFIG"] |
| ) |
| |
| if self.config["ENABLE_CHUNK_ENCODING"]: |
| |
| class ChunkedEncodingFix: # pylint: disable=too-few-public-methods |
| def __init__(self, app: Flask) -> None: |
| self.app = app |
| |
| def __call__( |
| self, environ: Dict[str, Any], start_response: Callable[..., Any] |
| ) -> Any: |
| # Setting wsgi.input_terminated tells werkzeug.wsgi to ignore |
| # content-length and read the stream till the end. |
| if environ.get("HTTP_TRANSFER_ENCODING", "").lower() == "chunked": |
| environ["wsgi.input_terminated"] = True |
| return self.app(environ, start_response) |
| |
| self.superset_app.wsgi_app = ChunkedEncodingFix( # type: ignore |
| self.superset_app.wsgi_app # type: ignore |
| ) |
| |
| if self.config["UPLOAD_FOLDER"]: |
| try: |
| os.makedirs(self.config["UPLOAD_FOLDER"]) |
| except OSError: |
| pass |
| |
| for middleware in self.config["ADDITIONAL_MIDDLEWARE"]: |
| self.superset_app.wsgi_app = middleware( # type: ignore |
| self.superset_app.wsgi_app |
| ) |
| |
| # Flask-Compress |
| Compress(self.superset_app) |
| |
| if self.config["TALISMAN_ENABLED"]: |
| talisman.init_app(self.superset_app, **self.config["TALISMAN_CONFIG"]) |
| |
| def configure_logging(self) -> None: |
| self.config["LOGGING_CONFIGURATOR"].configure_logging( |
| self.config, self.superset_app.debug |
| ) |
| |
| def configure_db_encrypt(self) -> None: |
| encrypted_field_factory.init_app(self.superset_app) |
| |
| def setup_db(self) -> None: |
| db.init_app(self.superset_app) |
| |
| with self.superset_app.app_context(): |
| pessimistic_connection_handling(db.engine) |
| |
| migrate.init_app(self.superset_app, db=db, directory=APP_DIR + "/migrations") |
| |
| def configure_wtf(self) -> None: |
| if self.config["WTF_CSRF_ENABLED"]: |
| csrf.init_app(self.superset_app) |
| csrf_exempt_list = self.config["WTF_CSRF_EXEMPT_LIST"] |
| for ex in csrf_exempt_list: |
| csrf.exempt(ex) |
| |
| def configure_async_queries(self) -> None: |
| if feature_flag_manager.is_feature_enabled("GLOBAL_ASYNC_QUERIES"): |
| async_query_manager.init_app(self.superset_app) |
| |
| def register_blueprints(self) -> None: |
| for bp in self.config["BLUEPRINTS"]: |
| try: |
| logger.info("Registering blueprint: %s", bp.name) |
| self.superset_app.register_blueprint(bp) |
| except Exception: # pylint: disable=broad-except |
| logger.exception("blueprint registration failed") |
| |
| def setup_bundle_manifest(self) -> None: |
| manifest_processor.init_app(self.superset_app) |
| |
| def enable_profiling(self) -> None: |
| if self.config["PROFILING"]: |
| profiling.init_app(self.superset_app) |
| |
| |
| class SupersetIndexView(IndexView): |
| @expose("/") |
| def index(self) -> FlaskResponse: |
| return redirect("/superset/welcome/") |