apache-airflow-providers-fab 3.0.1__py3-none-any.whl → 3.1.0rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. airflow/providers/fab/__init__.py +1 -1
  2. airflow/providers/fab/auth_manager/api_endpoints/user_endpoint.py +2 -2
  3. airflow/providers/fab/auth_manager/api_fastapi/datamodels/login.py +0 -7
  4. airflow/providers/fab/auth_manager/api_fastapi/datamodels/roles.py +63 -0
  5. airflow/providers/fab/auth_manager/api_fastapi/openapi/v2-fab-auth-manager-generated.yaml +416 -16
  6. airflow/providers/fab/auth_manager/api_fastapi/parameters.py +55 -0
  7. airflow/providers/fab/auth_manager/api_fastapi/routes/login.py +37 -5
  8. airflow/providers/fab/auth_manager/api_fastapi/routes/roles.py +137 -0
  9. airflow/providers/fab/auth_manager/api_fastapi/security.py +32 -0
  10. airflow/providers/fab/auth_manager/api_fastapi/services/login.py +12 -25
  11. airflow/providers/fab/auth_manager/api_fastapi/services/roles.py +158 -0
  12. airflow/providers/fab/auth_manager/api_fastapi/sorting.py +49 -0
  13. airflow/providers/fab/auth_manager/fab_auth_manager.py +33 -3
  14. airflow/providers/fab/auth_manager/models/__init__.py +3 -8
  15. airflow/providers/fab/auth_manager/models/db.py +1 -1
  16. airflow/providers/fab/auth_manager/security_manager/override.py +52 -9
  17. airflow/providers/fab/www/api_connexion/parameters.py +1 -46
  18. airflow/providers/fab/www/app.py +13 -10
  19. airflow/providers/fab/www/extensions/init_appbuilder.py +5 -2
  20. airflow/providers/fab/www/extensions/init_security.py +1 -1
  21. airflow/providers/fab/www/package-lock.json +315 -217
  22. airflow/providers/fab/www/package.json +9 -9
  23. airflow/providers/fab/www/session.py +5 -8
  24. airflow/providers/fab/www/static/dist/{743.935ed3d26e56ed8f63d3.js → 743.0c0bf201ae17e66a9a3f.js} +1 -1
  25. airflow/providers/fab/www/static/dist/{main.3cf3be1a0c5439bb640d.js → main.bc1f701c3d133e2a3bab.js} +1 -1
  26. airflow/providers/fab/www/static/dist/manifest.json +13 -13
  27. airflow/providers/fab/www/views.py +18 -14
  28. {apache_airflow_providers_fab-3.0.1.dist-info → apache_airflow_providers_fab-3.1.0rc1.dist-info}/METADATA +15 -14
  29. {apache_airflow_providers_fab-3.0.1.dist-info → apache_airflow_providers_fab-3.1.0rc1.dist-info}/RECORD +48 -42
  30. /airflow/providers/fab/migrations/versions/{0001_1_4_0_create_ab_tables_if_missing.py → 0000_1_4_0_create_ab_tables_if_missing.py} +0 -0
  31. /airflow/providers/fab/www/static/dist/{743.935ed3d26e56ed8f63d3.js.LICENSE.txt → 743.0c0bf201ae17e66a9a3f.js.LICENSE.txt} +0 -0
  32. /airflow/providers/fab/www/static/dist/{airflowDefaultTheme.ff5a35f322070b094aa2.css → airflowDefaultTheme.ef6fc04c9b6920cd75c9.css} +0 -0
  33. /airflow/providers/fab/www/static/dist/{airflowDefaultTheme.ff5a35f322070b094aa2.js → airflowDefaultTheme.ef6fc04c9b6920cd75c9.js} +0 -0
  34. /airflow/providers/fab/www/static/dist/{flash.5583a9e0cf11f2be93da.css → flash.eaaf777ec1b3628cf7be.css} +0 -0
  35. /airflow/providers/fab/www/static/dist/{flash.5583a9e0cf11f2be93da.js → flash.eaaf777ec1b3628cf7be.js} +0 -0
  36. /airflow/providers/fab/www/static/dist/{loadingDots.2e5f555f0753107b0300.css → loadingDots.76f4332c0a932c3dc08f.css} +0 -0
  37. /airflow/providers/fab/www/static/dist/{loadingDots.2e5f555f0753107b0300.js → loadingDots.76f4332c0a932c3dc08f.js} +0 -0
  38. /airflow/providers/fab/www/static/dist/{main.3cf3be1a0c5439bb640d.css → main.bc1f701c3d133e2a3bab.css} +0 -0
  39. /airflow/providers/fab/www/static/dist/{main.3cf3be1a0c5439bb640d.js.LICENSE.txt → main.bc1f701c3d133e2a3bab.js.LICENSE.txt} +0 -0
  40. /airflow/providers/fab/www/static/dist/{materialIcons.3e67dd6fbfcc4f3b5105.css → materialIcons.ad07a489b2f0fc1a96bf.css} +0 -0
  41. /airflow/providers/fab/www/static/dist/{materialIcons.3e67dd6fbfcc4f3b5105.js → materialIcons.ad07a489b2f0fc1a96bf.js} +0 -0
  42. /airflow/providers/fab/www/static/dist/{moment.9baee5ec3d7639a10897.js → moment.5b85b4f6be2fe9c405ac.js} +0 -0
  43. /airflow/providers/fab/www/static/dist/{runtime.6ad9da077ea169d60db9.js → runtime.254c277d91ce3ac79c64.js} +0 -0
  44. {apache_airflow_providers_fab-3.0.1.dist-info → apache_airflow_providers_fab-3.1.0rc1.dist-info}/WHEEL +0 -0
  45. {apache_airflow_providers_fab-3.0.1.dist-info → apache_airflow_providers_fab-3.1.0rc1.dist-info}/entry_points.txt +0 -0
  46. {apache_airflow_providers_fab-3.0.1.dist-info → apache_airflow_providers_fab-3.1.0rc1.dist-info}/licenses/3rd-party-licenses/LICENSES-ui.txt +0 -0
  47. {airflow/providers/fab → apache_airflow_providers_fab-3.1.0rc1.dist-info/licenses}/LICENSE +0 -0
  48. {apache_airflow_providers_fab-3.0.1.dist-info → apache_airflow_providers_fab-3.1.0rc1.dist-info}/licenses/NOTICE +0 -0
@@ -17,21 +17,16 @@
17
17
  from __future__ import annotations
18
18
 
19
19
  import logging
20
- from collections.abc import Callable, Container
20
+ from collections.abc import Callable
21
21
  from functools import wraps
22
22
  from typing import TYPE_CHECKING, Any, TypeVar, cast
23
23
 
24
- from pendulum.parsing import ParserError
25
- from sqlalchemy import text
26
-
27
24
  from airflow.configuration import conf
28
25
  from airflow.providers.fab.www.api_connexion.exceptions import BadRequest
29
- from airflow.utils import timezone
30
26
 
31
27
  if TYPE_CHECKING:
32
28
  from datetime import datetime
33
29
 
34
- from sqlalchemy.sql import Select
35
30
 
36
31
  log = logging.getLogger(__name__)
37
32
 
@@ -42,24 +37,6 @@ def validate_istimezone(value: datetime) -> None:
42
37
  raise BadRequest("Invalid datetime format", detail="Naive datetime is disallowed")
43
38
 
44
39
 
45
- def format_datetime(value: str) -> datetime:
46
- """
47
- Format datetime objects.
48
-
49
- Datetime format parser for args since connexion doesn't parse datetimes
50
- https://github.com/zalando/connexion/issues/476
51
-
52
- This should only be used within connection views because it raises 400
53
- """
54
- value = value.strip()
55
- if value[-1] != "Z":
56
- value = value.replace(" ", "+")
57
- try:
58
- return timezone.parse(value)
59
- except (ParserError, TypeError) as err:
60
- raise BadRequest("Incorrect datetime argument", detail=str(err))
61
-
62
-
63
40
  def check_limit(value: int) -> int:
64
41
  """
65
42
  Check the limit does not exceed configured value.
@@ -107,25 +84,3 @@ def format_parameters(params_formatters: dict[str, Callable[[Any], Any]]) -> Cal
107
84
  return cast("T", wrapped_function)
108
85
 
109
86
  return format_parameters_decorator
110
-
111
-
112
- def apply_sorting(
113
- query: Select,
114
- order_by: str,
115
- to_replace: dict[str, str] | None = None,
116
- allowed_attrs: Container[str] | None = None,
117
- ) -> Select:
118
- """Apply sorting to query."""
119
- lstriped_orderby = order_by.lstrip("-")
120
- if allowed_attrs and lstriped_orderby not in allowed_attrs:
121
- raise BadRequest(
122
- detail=f"Ordering with '{lstriped_orderby}' is disallowed or "
123
- f"the attribute does not exist on the model"
124
- )
125
- if to_replace:
126
- lstriped_orderby = to_replace.get(lstriped_orderby, lstriped_orderby)
127
- if order_by[0] == "-":
128
- order_by = f"{lstriped_orderby} desc"
129
- else:
130
- order_by = f"{lstriped_orderby} asc"
131
- return query.order_by(text(order_by))
@@ -18,6 +18,7 @@
18
18
  from __future__ import annotations
19
19
 
20
20
  from datetime import timedelta
21
+ from functools import cache
21
22
  from os.path import isabs
22
23
 
23
24
  from flask import Flask
@@ -30,7 +31,7 @@ from airflow.api_fastapi.app import get_auth_manager
30
31
  from airflow.configuration import conf
31
32
  from airflow.exceptions import AirflowConfigException
32
33
  from airflow.logging_config import configure_logging
33
- from airflow.providers.fab.www.extensions.init_appbuilder import init_appbuilder
34
+ from airflow.providers.fab.www.extensions.init_appbuilder import AirflowAppBuilder
34
35
  from airflow.providers.fab.www.extensions.init_jinja_globals import init_jinja_globals
35
36
  from airflow.providers.fab.www.extensions.init_manifest_files import configure_manifest_files
36
37
  from airflow.providers.fab.www.extensions.init_security import init_api_auth
@@ -44,8 +45,6 @@ from airflow.providers.fab.www.extensions.init_views import (
44
45
  from airflow.providers.fab.www.extensions.init_wsgi_middlewares import init_wsgi_middleware
45
46
  from airflow.providers.fab.www.utils import get_session_lifetime_config
46
47
 
47
- app: Flask | None = None
48
-
49
48
  # Initializes at the module level, so plugins can access it.
50
49
  # See: /docs/plugins.rst
51
50
  csrf = CSRFProtect()
@@ -85,6 +84,8 @@ def create_app(enable_plugins: bool):
85
84
  csrf.init_app(flask_app)
86
85
 
87
86
  db = SQLAlchemy(flask_app)
87
+ if settings.Session is None:
88
+ raise RuntimeError("Session not configured. Call configure_orm() first.")
88
89
  db.session = settings.Session
89
90
 
90
91
  configure_logging()
@@ -92,7 +93,12 @@ def create_app(enable_plugins: bool):
92
93
  init_api_auth(flask_app)
93
94
 
94
95
  with flask_app.app_context():
95
- init_appbuilder(flask_app, enable_plugins=enable_plugins)
96
+ AirflowAppBuilder(
97
+ app=flask_app,
98
+ session=db.session(),
99
+ base_template="airflow/main.html",
100
+ enable_plugins=enable_plugins,
101
+ )
96
102
  init_error_handlers(flask_app)
97
103
  # In two scenarios a Flask application can be created:
98
104
  # - To support Airflow 2 plugins relying on Flask (``enable_plugins`` is True)
@@ -112,15 +118,12 @@ def create_app(enable_plugins: bool):
112
118
  return flask_app
113
119
 
114
120
 
121
+ @cache
115
122
  def cached_app():
116
123
  """Return cached instance of Airflow WWW app."""
117
- global app
118
- if not app:
119
- app = create_app()
120
- return app
124
+ return create_app()
121
125
 
122
126
 
123
127
  def purge_cached_app():
124
128
  """Remove the cached version of the app in global state."""
125
- global app
126
- app = None
129
+ cached_app.cache_clear()
@@ -42,7 +42,7 @@ from airflow import settings
42
42
  from airflow.api_fastapi.app import create_auth_manager, get_auth_manager
43
43
  from airflow.configuration import conf
44
44
  from airflow.providers.fab.www.security_manager import AirflowSecurityManagerV2
45
- from airflow.providers.fab.www.views import FabIndexView
45
+ from airflow.providers.fab.www.views import FabIndexView, redirect
46
46
 
47
47
  if TYPE_CHECKING:
48
48
  from flask import Flask
@@ -216,6 +216,7 @@ class AirflowAppBuilder:
216
216
  from airflow.providers.fab.www.views import get_safe_url
217
217
 
218
218
  fab_sec_views.get_safe_redirect = get_safe_url
219
+ fab_sec_views.redirect = redirect
219
220
 
220
221
  def _init_extension(self, app):
221
222
  app.appbuilder = self
@@ -473,7 +474,7 @@ class AirflowAppBuilder:
473
474
  baseview=baseview,
474
475
  cond=cond,
475
476
  )
476
- if self.app:
477
+ if current_app:
477
478
  self._add_permissions_menu(name)
478
479
  if category:
479
480
  self._add_permissions_menu(category)
@@ -592,6 +593,8 @@ class AirflowAppBuilder:
592
593
 
593
594
  def init_appbuilder(app: Flask, enable_plugins: bool) -> AirflowAppBuilder:
594
595
  """Init `Flask App Builder <https://flask-appbuilder.readthedocs.io/en/latest/>`__."""
596
+ if settings.Session is None:
597
+ raise RuntimeError("Session not configured. Call configure_orm() first.")
595
598
  return AirflowAppBuilder(
596
599
  app=app,
597
600
  session=settings.Session(),
@@ -20,7 +20,7 @@ import logging
20
20
  from importlib import import_module
21
21
 
22
22
  from airflow.configuration import conf
23
- from airflow.exceptions import AirflowException
23
+ from airflow.providers.common.compat.sdk import AirflowException
24
24
 
25
25
  log = logging.getLogger(__name__)
26
26