This is an automated email from the ASF dual-hosted git repository.
vincbeck pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 6b847411de1 Stop rebuilding the FAB app on every users and roles API
call (#73104)
6b847411de1 is described below
commit 6b847411de100e4029edc69141a107f7e676abe2
Author: namanjain24-sudo <[email protected]>
AuthorDate: Thu Oct 1 18:43:49 2026 +0530
Stop rebuilding the FAB app on every users and roles API call (#73104)
The users, roles and permissions routes wrapped each request in the CLI
helper get_application_builder(), which builds a new Flask app and
AppBuilder every time and keeps each one alive through its cache. Use the
auth manager's own Flask app instead, the same way the login routes do.
---
.../fab/auth_manager/api_fastapi/routes/roles.py | 14 +-
.../fab/auth_manager/api_fastapi/routes/users.py | 12 +-
.../unit/fab/auth_manager/api_fastapi/conftest.py | 21 +++
.../auth_manager/api_fastapi/routes/test_roles.py | 161 +++++++++------------
.../auth_manager/api_fastapi/routes/test_users.py | 128 ++++++++--------
5 files changed, 166 insertions(+), 170 deletions(-)
diff --git
a/providers/fab/src/airflow/providers/fab/auth_manager/api_fastapi/routes/roles.py
b/providers/fab/src/airflow/providers/fab/auth_manager/api_fastapi/routes/roles.py
index 3b799905a56..654f6f97a4e 100644
---
a/providers/fab/src/airflow/providers/fab/auth_manager/api_fastapi/routes/roles.py
+++
b/providers/fab/src/airflow/providers/fab/auth_manager/api_fastapi/routes/roles.py
@@ -26,10 +26,10 @@ from
airflow.providers.fab.auth_manager.api_fastapi.datamodels.roles import (
RoleResponse,
)
from airflow.providers.fab.auth_manager.api_fastapi.parameters import
get_effective_limit
+from airflow.providers.fab.auth_manager.api_fastapi.routes.login import
_get_flask_app
from airflow.providers.fab.auth_manager.api_fastapi.routes.router import
fab_router
from airflow.providers.fab.auth_manager.api_fastapi.security import
requires_fab_custom_view
from airflow.providers.fab.auth_manager.api_fastapi.services.roles import
FABAuthManagerRoles
-from airflow.providers.fab.auth_manager.cli_commands.utils import
get_application_builder
from airflow.providers.fab.www.security import permissions
@@ -48,7 +48,7 @@ from airflow.providers.fab.www.security import permissions
)
def create_role(body: RoleBody) -> RoleResponse:
"""Create a new role (actions can be empty)."""
- with get_application_builder():
+ with _get_flask_app().app_context():
return FABAuthManagerRoles.create_role(body=body)
@@ -71,7 +71,7 @@ def get_roles(
offset: int = Query(0, ge=0, description="Number of items to skip before
starting to collect results."),
) -> RoleCollectionResponse:
"""List roles with pagination and ordering."""
- with get_application_builder():
+ with _get_flask_app().app_context():
return FABAuthManagerRoles.get_roles(order_by=order_by, limit=limit,
offset=offset)
@@ -88,7 +88,7 @@ def get_roles(
)
def delete_role(name: str = Path(..., min_length=1)) -> None:
"""Delete an existing role."""
- with get_application_builder():
+ with _get_flask_app().app_context():
return FABAuthManagerRoles.delete_role(name=name)
@@ -101,7 +101,7 @@ def delete_role(name: str = Path(..., min_length=1)) ->
None:
)
def get_role(name: str = Path(..., min_length=1)) -> RoleResponse:
"""Get an existing role."""
- with get_application_builder():
+ with _get_flask_app().app_context():
return FABAuthManagerRoles.get_role(name=name)
@@ -123,7 +123,7 @@ def patch_role(
update_mask: str | None = Query(None, description="Comma-separated list of
fields to update"),
) -> RoleResponse:
"""Update an existing role."""
- with get_application_builder():
+ with _get_flask_app().app_context():
return FABAuthManagerRoles.patch_role(name=name, body=body,
update_mask=update_mask)
@@ -146,5 +146,5 @@ def get_permissions(
offset: int = Query(0, ge=0, description="Number of items to skip before
starting to collect results."),
) -> PermissionCollectionResponse:
"""List all action-resource (permission) pairs."""
- with get_application_builder():
+ with _get_flask_app().app_context():
return FABAuthManagerRoles.get_permissions(order_by=order_by,
limit=limit, offset=offset)
diff --git
a/providers/fab/src/airflow/providers/fab/auth_manager/api_fastapi/routes/users.py
b/providers/fab/src/airflow/providers/fab/auth_manager/api_fastapi/routes/users.py
index a42c3dd5baa..3d6312704e5 100644
---
a/providers/fab/src/airflow/providers/fab/auth_manager/api_fastapi/routes/users.py
+++
b/providers/fab/src/airflow/providers/fab/auth_manager/api_fastapi/routes/users.py
@@ -26,10 +26,10 @@ from
airflow.providers.fab.auth_manager.api_fastapi.datamodels.users import (
UserResponse,
)
from airflow.providers.fab.auth_manager.api_fastapi.parameters import
get_effective_limit
+from airflow.providers.fab.auth_manager.api_fastapi.routes.login import
_get_flask_app
from airflow.providers.fab.auth_manager.api_fastapi.routes.router import
fab_router
from airflow.providers.fab.auth_manager.api_fastapi.security import
requires_fab_custom_view
from airflow.providers.fab.auth_manager.api_fastapi.services.users import
FABAuthManagerUsers
-from airflow.providers.fab.auth_manager.cli_commands.utils import
get_application_builder
from airflow.providers.fab.www.security import permissions
@@ -47,7 +47,7 @@ from airflow.providers.fab.www.security import permissions
dependencies=[Depends(requires_fab_custom_view("POST",
permissions.RESOURCE_USER))],
)
def create_user(body: UserBody) -> UserResponse:
- with get_application_builder():
+ with _get_flask_app().app_context():
return FABAuthManagerUsers.create_user(body=body)
@@ -69,7 +69,7 @@ def get_users(
offset: int = Query(0, ge=0, description="Number of items to skip before
starting to collect results."),
) -> UserCollectionResponse:
"""List users with pagination and ordering."""
- with get_application_builder():
+ with _get_flask_app().app_context():
return FABAuthManagerUsers.get_users(order_by=order_by, limit=limit,
offset=offset)
@@ -86,7 +86,7 @@ def get_users(
)
def get_user(username: str = Path(..., min_length=1)) -> UserResponse:
"""Get a user by username."""
- with get_application_builder():
+ with _get_flask_app().app_context():
return FABAuthManagerUsers.get_user(username=username)
@@ -109,7 +109,7 @@ def update_user(
update_mask: str | None = Query(None, description="Comma-separated list of
fields to update"),
) -> UserResponse:
"""Update an existing user."""
- with get_application_builder():
+ with _get_flask_app().app_context():
return FABAuthManagerUsers.update_user(username=username, body=body,
update_mask=update_mask)
@@ -127,5 +127,5 @@ def update_user(
)
def delete_user(username: str = Path(..., min_length=1)):
"""Delete a user by username."""
- with get_application_builder():
+ with _get_flask_app().app_context():
FABAuthManagerUsers.delete_user(username=username)
diff --git a/providers/fab/tests/unit/fab/auth_manager/api_fastapi/conftest.py
b/providers/fab/tests/unit/fab/auth_manager/api_fastapi/conftest.py
index af2615f7581..1bd8f788cf7 100644
--- a/providers/fab/tests/unit/fab/auth_manager/api_fastapi/conftest.py
+++ b/providers/fab/tests/unit/fab/auth_manager/api_fastapi/conftest.py
@@ -18,13 +18,17 @@ from __future__ import annotations
import types
from contextlib import contextmanager
+from unittest.mock import patch
import pytest
from fastapi.testclient import TestClient
+from airflow.api_fastapi.app import create_auth_manager
from airflow.api_fastapi.core_api.security import get_user as get_user_dep
from airflow.providers.fab.auth_manager.fab_auth_manager import FabAuthManager
+from tests_common.test_utils.config import conf_vars
+
@pytest.fixture(scope="module")
def fab_auth_manager():
@@ -70,3 +74,20 @@ def as_user(override_deps):
yield u
return _as
+
+
[email protected]
+def real_app_client():
+ """
+ Client for the API of the configured FabAuthManager, backed by its real
Flask app and the database.
+
+ Only the user and the authorization check are faked, so requests go
through ``_get_flask_app()``.
+ """
+ with conf_vars(
+ {("core", "auth_manager"):
"airflow.providers.fab.auth_manager.fab_auth_manager.FabAuthManager"}
+ ):
+ auth_manager = create_auth_manager()
+ app = auth_manager.get_fastapi_app()
+ app.dependency_overrides[get_user_dep] = lambda:
types.SimpleNamespace(id=1, username="tester")
+ with patch.object(auth_manager, "is_authorized_custom_view",
return_value=True):
+ yield auth_manager, TestClient(app)
diff --git
a/providers/fab/tests/unit/fab/auth_manager/api_fastapi/routes/test_roles.py
b/providers/fab/tests/unit/fab/auth_manager/api_fastapi/routes/test_roles.py
index a1ed2e453f7..8f79c1ccbbf 100644
--- a/providers/fab/tests/unit/fab/auth_manager/api_fastapi/routes/test_roles.py
+++ b/providers/fab/tests/unit/fab/auth_manager/api_fastapi/routes/test_roles.py
@@ -17,7 +17,6 @@
from __future__ import annotations
-from contextlib import nullcontext as _noop_cm
from unittest.mock import ANY, MagicMock, patch
import pytest
@@ -31,6 +30,7 @@ from
airflow.providers.fab.auth_manager.api_fastapi.datamodels.roles import (
RoleCollectionResponse,
RoleResponse,
)
+from airflow.providers.fab.www.extensions.init_appbuilder import
AirflowAppBuilder
@pytest.mark.db_test
@@ -40,12 +40,9 @@ class TestRoles:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.roles.FABAuthManagerRoles")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles._get_flask_app",
)
- def test_create_role(
- self, mock_get_application_builder, mock_get_auth_manager, mock_roles,
test_client, as_user
- ):
+ def test_create_role(self, mock_get_flask_app, mock_get_auth_manager,
mock_roles, test_client, as_user):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
mock_get_auth_manager.return_value = mgr
@@ -62,11 +59,10 @@ class TestRoles:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.roles.FABAuthManagerRoles")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles._get_flask_app",
)
def test_create_role_forbidden(
- self, mock_get_application_builder, mock_get_auth_manager, mock_roles,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_roles,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = False
@@ -80,11 +76,10 @@ class TestRoles:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.roles.FABAuthManagerRoles")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles._get_flask_app",
)
def test_create_role_validation_422_empty_name(
- self, mock_get_application_builder, mock_get_auth_manager, mock_roles,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_roles,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
@@ -98,11 +93,10 @@ class TestRoles:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.roles.FABAuthManagerRoles")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles._get_flask_app",
)
def test_create_role_validation_422_missing_name(
- self, mock_get_application_builder, mock_get_auth_manager, mock_roles,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_roles,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
@@ -118,14 +112,13 @@ class TestRoles:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.roles.FABAuthManagerRoles")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles._get_flask_app",
)
@patch("airflow.providers.fab.auth_manager.api_fastapi.parameters.conf")
def test_get_roles_success_defaults(
self,
conf_mock,
- mock_get_application_builder,
+ mock_get_flask_app,
mock_get_auth_manager,
mock_roles,
test_client,
@@ -153,18 +146,18 @@ class TestRoles:
assert resp.status_code == 200
assert resp.json() == dummy.model_dump(by_alias=True)
mock_roles.get_roles.assert_called_once_with(order_by="name",
limit=100, offset=0)
+ mock_get_flask_app.return_value.app_context.assert_called_once()
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.roles.FABAuthManagerRoles")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles._get_flask_app",
)
@patch("airflow.providers.fab.auth_manager.api_fastapi.parameters.conf")
def test_get_roles_passes_params_and_clamps_limit(
self,
conf_mock,
- mock_get_application_builder,
+ mock_get_flask_app,
mock_get_auth_manager,
mock_roles,
test_client,
@@ -191,14 +184,13 @@ class TestRoles:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.roles.FABAuthManagerRoles")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles._get_flask_app",
)
@patch("airflow.providers.fab.auth_manager.api_fastapi.parameters.conf")
def test_get_roles_uses_fallback_when_limit_zero(
self,
conf_mock,
- mock_get_application_builder,
+ mock_get_flask_app,
mock_get_auth_manager,
mock_roles,
test_client,
@@ -225,11 +217,10 @@ class TestRoles:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.roles.FABAuthManagerRoles")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles._get_flask_app",
)
def test_get_roles_forbidden(
- self, mock_get_application_builder, mock_get_auth_manager, mock_roles,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_roles,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = False
@@ -243,11 +234,10 @@ class TestRoles:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.roles.FABAuthManagerRoles")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles._get_flask_app",
)
def test_get_roles_validation_422_negative_offset(
- self, mock_get_application_builder, mock_get_auth_manager, mock_roles,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_roles,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
@@ -263,12 +253,9 @@ class TestRoles:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.roles.FABAuthManagerRoles")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles._get_flask_app",
)
- def test_delete_role(
- self, mock_get_application_builder, mock_get_auth_manager, mock_roles,
test_client, as_user
- ):
+ def test_delete_role(self, mock_get_flask_app, mock_get_auth_manager,
mock_roles, test_client, as_user):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
mock_roles.delete_role.return_value = None
@@ -282,11 +269,10 @@ class TestRoles:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.roles.FABAuthManagerRoles")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles._get_flask_app",
)
def test_delete_role_forbidden(
- self, mock_get_application_builder, mock_get_auth_manager, mock_roles,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_roles,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = False
@@ -300,11 +286,10 @@ class TestRoles:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.roles.FABAuthManagerRoles")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles._get_flask_app",
)
def test_delete_role_validation_404_not_found(
- self, mock_get_application_builder, mock_get_auth_manager, mock_roles,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_roles,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
@@ -322,11 +307,10 @@ class TestRoles:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.roles.FABAuthManagerRoles")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles._get_flask_app",
)
def test_delete_role_validation_404_empty_name(
- self, mock_get_application_builder, mock_get_auth_manager, mock_roles,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_roles,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
@@ -340,12 +324,9 @@ class TestRoles:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.roles.FABAuthManagerRoles")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles._get_flask_app",
)
- def test_get_role(
- self, mock_get_application_builder, mock_get_auth_manager, mock_roles,
test_client, as_user
- ):
+ def test_get_role(self, mock_get_flask_app, mock_get_auth_manager,
mock_roles, test_client, as_user):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
mock_get_auth_manager.return_value = mgr
@@ -362,11 +343,10 @@ class TestRoles:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.roles.FABAuthManagerRoles")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles._get_flask_app",
)
def test_get_role_forbidden(
- self, mock_get_application_builder, mock_get_auth_manager, mock_roles,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_roles,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = False
@@ -380,11 +360,10 @@ class TestRoles:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.roles.FABAuthManagerRoles")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles._get_flask_app",
)
def test_get_role_validation_404_not_found(
- self, mock_get_application_builder, mock_get_auth_manager, mock_roles,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_roles,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
@@ -402,11 +381,10 @@ class TestRoles:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.roles.FABAuthManagerRoles")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles._get_flask_app",
)
def test_get_role_validation_404_empty_name(
- self, mock_get_application_builder, mock_get_auth_manager, mock_roles,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_roles,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
@@ -420,12 +398,9 @@ class TestRoles:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.roles.FABAuthManagerRoles")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles._get_flask_app",
)
- def test_patch_role(
- self, mock_get_application_builder, mock_get_auth_manager, mock_roles,
test_client, as_user
- ):
+ def test_patch_role(self, mock_get_flask_app, mock_get_auth_manager,
mock_roles, test_client, as_user):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
mock_get_auth_manager.return_value = mgr
@@ -442,11 +417,10 @@ class TestRoles:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.roles.FABAuthManagerRoles")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles._get_flask_app",
)
def test_patch_role_with_update_mask(
- self, mock_get_application_builder, mock_get_auth_manager, mock_roles,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_roles,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
@@ -468,11 +442,10 @@ class TestRoles:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.roles.FABAuthManagerRoles")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles._get_flask_app",
)
def test_path_role_forbidden(
- self, mock_get_application_builder, mock_get_auth_manager, mock_roles,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_roles,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = False
@@ -486,11 +459,10 @@ class TestRoles:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.roles.FABAuthManagerRoles")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles._get_flask_app",
)
def test_patch_role_validation_404_not_found(
- self, mock_get_application_builder, mock_get_auth_manager, mock_roles,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_roles,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
@@ -512,11 +484,10 @@ class TestRoles:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.roles.FABAuthManagerRoles")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles._get_flask_app",
)
def test_patch_role_validation_404_empty_name(
- self, mock_get_application_builder, mock_get_auth_manager, mock_roles,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_roles,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
@@ -533,11 +504,10 @@ class TestRoles:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.roles.FABAuthManagerRoles")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles._get_flask_app",
)
def test_path_role_unknown_update_mask(
- self, mock_get_application_builder, mock_get_auth_manager, mock_roles,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_roles,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
@@ -560,11 +530,10 @@ class TestRoles:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.roles.FABAuthManagerRoles")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles._get_flask_app",
)
def test_get_permissions_success(
- self, mock_get_application_builder, mock_get_auth_manager,
mock_permissions, test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_permissions,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
@@ -585,11 +554,10 @@ class TestRoles:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.roles.FABAuthManagerRoles")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles._get_flask_app",
)
def test_get_permissions_forbidden(
- self, mock_get_application_builder, mock_get_auth_manager,
mock_permissions, test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_permissions,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = False
@@ -603,14 +571,13 @@ class TestRoles:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.roles.FABAuthManagerRoles")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles._get_flask_app",
)
@patch("airflow.providers.fab.auth_manager.api_fastapi.parameters.conf")
def test_get_permissions_passes_params_and_clamps_limit(
self,
conf_mock,
- mock_get_application_builder,
+ mock_get_flask_app,
mock_get_auth_manager,
mock_permissions,
test_client,
@@ -639,14 +606,13 @@ class TestRoles:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.roles.FABAuthManagerRoles")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.roles._get_flask_app",
)
@patch("airflow.providers.fab.auth_manager.api_fastapi.parameters.conf")
def test_get_permissions_uses_fallback_when_limit_zero(
self,
conf_mock,
- mock_get_application_builder,
+ mock_get_flask_app,
mock_get_auth_manager,
mock_permissions,
test_client,
@@ -668,3 +634,18 @@ class TestRoles:
resp = test_client.get("/fab/v1/permissions", params={"limit": 0})
assert resp.status_code == 200
mock_permissions.get_permissions.assert_called_once_with(order_by="id",
limit=33, offset=0)
+
+ def test_requests_reuse_the_auth_manager_flask_app(self, real_app_client):
+ auth_manager, client = real_app_client
+ flask_app, appbuilder = auth_manager.flask_app, auth_manager.appbuilder
+
+ with patch.object(
+ AirflowAppBuilder, "init_app", autospec=True,
side_effect=AirflowAppBuilder.init_app
+ ) as init_app:
+ responses = [client.get("/fab/v1/roles") for _ in range(2)]
+
+ init_app.assert_not_called()
+ assert auth_manager.flask_app is flask_app
+ assert auth_manager.appbuilder is appbuilder
+ assert [response.status_code for response in responses] == [200, 200]
+ assert "Admin" in {role["name"] for role in
responses[1].json()["roles"]}
diff --git
a/providers/fab/tests/unit/fab/auth_manager/api_fastapi/routes/test_users.py
b/providers/fab/tests/unit/fab/auth_manager/api_fastapi/routes/test_users.py
index 18bbb8ff729..d44f9cf1f71 100644
--- a/providers/fab/tests/unit/fab/auth_manager/api_fastapi/routes/test_users.py
+++ b/providers/fab/tests/unit/fab/auth_manager/api_fastapi/routes/test_users.py
@@ -16,7 +16,6 @@
# under the License.
from __future__ import annotations
-from contextlib import nullcontext as _noop_cm
from unittest.mock import ANY, MagicMock, patch
import pytest
@@ -26,6 +25,7 @@ from
airflow.providers.fab.auth_manager.api_fastapi.datamodels.users import (
UserCollectionResponse,
UserResponse,
)
+from airflow.providers.fab.www.extensions.init_appbuilder import
AirflowAppBuilder
@pytest.mark.db_test
@@ -33,11 +33,10 @@ class TestUsers:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.users.FABAuthManagerUsers")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.users.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.users._get_flask_app",
)
def test_create_user_ok(
- self, mock_get_application_builder, mock_get_auth_manager, mock_users,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_users,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
@@ -72,11 +71,10 @@ class TestUsers:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.users.FABAuthManagerUsers")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.users.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.users._get_flask_app",
)
def test_create_user_forbidden(
- self, mock_get_application_builder, mock_get_auth_manager, mock_users,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_users,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = False
@@ -99,11 +97,10 @@ class TestUsers:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.users.FABAuthManagerUsers")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.users.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.users._get_flask_app",
)
def test_create_user_validation_422_empty_username(
- self, mock_get_application_builder, mock_get_auth_manager, mock_users,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_users,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
@@ -126,11 +123,10 @@ class TestUsers:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.users.FABAuthManagerUsers")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.users.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.users._get_flask_app",
)
def test_create_user_validation_422_missing_username(
- self, mock_get_application_builder, mock_get_auth_manager, mock_users,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_users,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
@@ -152,11 +148,10 @@ class TestUsers:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.users.FABAuthManagerUsers")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.users.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.users._get_flask_app",
)
def test_create_user_validation_422_missing_password(
- self, mock_get_application_builder, mock_get_auth_manager, mock_users,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_users,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
@@ -179,14 +174,13 @@ class TestUsers:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.users.FABAuthManagerUsers")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.users.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.users._get_flask_app",
)
@patch("airflow.providers.fab.auth_manager.api_fastapi.parameters.conf")
def test_get_users_success_defaults(
self,
conf_mock,
- mock_get_application_builder,
+ mock_get_flask_app,
mock_get_auth_manager,
mock_users,
test_client,
@@ -219,18 +213,18 @@ class TestUsers:
assert resp.status_code == 200
assert resp.json() == dummy.model_dump(by_alias=True)
mock_users.get_users.assert_called_once_with(order_by="id",
limit=100, offset=0)
+ mock_get_flask_app.return_value.app_context.assert_called_once()
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.users.FABAuthManagerUsers")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.users.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.users._get_flask_app",
)
@patch("airflow.providers.fab.auth_manager.api_fastapi.parameters.conf")
def test_get_users_with_params(
self,
conf_mock,
- mock_get_application_builder,
+ mock_get_flask_app,
mock_get_auth_manager,
mock_users,
test_client,
@@ -258,11 +252,10 @@ class TestUsers:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.users.FABAuthManagerUsers")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.users.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.users._get_flask_app",
)
def test_get_users_forbidden(
- self, mock_get_application_builder, mock_get_auth_manager, mock_users,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_users,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = False
@@ -276,11 +269,10 @@ class TestUsers:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.users.FABAuthManagerUsers")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.users.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.users._get_flask_app",
)
def test_get_users_validation_422_negative_offset(
- self, mock_get_application_builder, mock_get_auth_manager, mock_users,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_users,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
@@ -294,11 +286,10 @@ class TestUsers:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.users.FABAuthManagerUsers")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.users.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.users._get_flask_app",
)
def test_get_user_success(
- self, mock_get_application_builder, mock_get_auth_manager, mock_users,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_users,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
@@ -321,11 +312,10 @@ class TestUsers:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.users.FABAuthManagerUsers")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.users.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.users._get_flask_app",
)
def test_get_user_forbidden(
- self, mock_get_application_builder, mock_get_auth_manager, mock_users,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_users,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = False
@@ -339,11 +329,10 @@ class TestUsers:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.users.FABAuthManagerUsers")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.users.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.users._get_flask_app",
)
def test_get_user_not_found(
- self, mock_get_application_builder, mock_get_auth_manager, mock_users,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_users,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
@@ -361,11 +350,10 @@ class TestUsers:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.users.FABAuthManagerUsers")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.users.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.users._get_flask_app",
)
def test_get_user_empty_username_404(
- self, mock_get_application_builder, mock_get_auth_manager, mock_users,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_users,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
@@ -379,11 +367,10 @@ class TestUsers:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.users.FABAuthManagerUsers")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.users.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.users._get_flask_app",
)
def test_update_user_success(
- self, mock_get_application_builder, mock_get_auth_manager, mock_users,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_users,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
@@ -409,11 +396,10 @@ class TestUsers:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.users.FABAuthManagerUsers")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.users.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.users._get_flask_app",
)
def test_update_user_with_update_mask(
- self, mock_get_application_builder, mock_get_auth_manager, mock_users,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_users,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
@@ -441,11 +427,10 @@ class TestUsers:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.users.FABAuthManagerUsers")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.users.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.users._get_flask_app",
)
def test_update_user_forbidden(
- self, mock_get_application_builder, mock_get_auth_manager, mock_users,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_users,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = False
@@ -459,11 +444,10 @@ class TestUsers:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.users.FABAuthManagerUsers")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.users.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.users._get_flask_app",
)
def test_update_user_not_found(
- self, mock_get_application_builder, mock_get_auth_manager, mock_users,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_users,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
@@ -481,11 +465,10 @@ class TestUsers:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.users.FABAuthManagerUsers")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.users.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.users._get_flask_app",
)
def test_update_user_unknown_update_mask(
- self, mock_get_application_builder, mock_get_auth_manager, mock_users,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_users,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
@@ -507,11 +490,10 @@ class TestUsers:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.users.FABAuthManagerUsers")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.users.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.users._get_flask_app",
)
def test_delete_user_success(
- self, mock_get_application_builder, mock_get_auth_manager, mock_users,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_users,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
@@ -526,11 +508,10 @@ class TestUsers:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.users.FABAuthManagerUsers")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.users.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.users._get_flask_app",
)
def test_delete_user_forbidden(
- self, mock_get_application_builder, mock_get_auth_manager, mock_users,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_users,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = False
@@ -544,11 +525,10 @@ class TestUsers:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.users.FABAuthManagerUsers")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.users.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.users._get_flask_app",
)
def test_delete_user_not_found(
- self, mock_get_application_builder, mock_get_auth_manager, mock_users,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_users,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
@@ -566,11 +546,10 @@ class TestUsers:
@patch("airflow.providers.fab.auth_manager.api_fastapi.routes.users.FABAuthManagerUsers")
@patch("airflow.providers.fab.auth_manager.api_fastapi.security.get_auth_manager")
@patch(
-
"airflow.providers.fab.auth_manager.api_fastapi.routes.users.get_application_builder",
- return_value=_noop_cm(),
+
"airflow.providers.fab.auth_manager.api_fastapi.routes.users._get_flask_app",
)
def test_delete_user_empty_username_404(
- self, mock_get_application_builder, mock_get_auth_manager, mock_users,
test_client, as_user
+ self, mock_get_flask_app, mock_get_auth_manager, mock_users,
test_client, as_user
):
mgr = MagicMock()
mgr.is_authorized_custom_view.return_value = True
@@ -580,3 +559,18 @@ class TestUsers:
resp = test_client.delete("/fab/v1/users/")
assert resp.status_code == 404
mock_users.delete_user.assert_not_called()
+
+ def test_requests_reuse_the_auth_manager_flask_app(self, real_app_client):
+ auth_manager, client = real_app_client
+ flask_app, appbuilder = auth_manager.flask_app, auth_manager.appbuilder
+
+ with patch.object(
+ AirflowAppBuilder, "init_app", autospec=True,
side_effect=AirflowAppBuilder.init_app
+ ) as init_app:
+ responses = [client.get("/fab/v1/users") for _ in range(2)]
+
+ init_app.assert_not_called()
+ assert auth_manager.flask_app is flask_app
+ assert auth_manager.appbuilder is appbuilder
+ assert [response.status_code for response in responses] == [200, 200]
+ assert "users" in responses[1].json()