This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch v2-11-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/v2-11-test by this push:
new b99b8cdfbf9 Run fab provider tests in v2-11-test (#61633)
b99b8cdfbf9 is described below
commit b99b8cdfbf91cb86cd6049389ac2e4ec05d8a926
Author: Jarek Potiuk <[email protected]>
AuthorDate: Fri Feb 13 00:20:27 2026 +0100
Run fab provider tests in v2-11-test (#61633)
Since we are releasing fab provider from v2-11-test we should also
run fab provider tests there.
This chang consists of:
* migrating to FAB 4.5.3 (includes scrypt algorithm fix)
* monkeypatching flask-session so that it works well with new
flask-sqlalchey
* caching initial$aization of Session class from flask-session and
AirflowDatabaseSessionInterface so that DB connection is not
created multiple times during tests
* fixing imports from tests
---
.github/workflows/ci-image-build.yml | 1 -
.github/workflows/ci.yml | 41 ++-
.github/workflows/helm-tests.yml | 2 +-
.github/workflows/prod-image-build.yml | 2 -
.github/workflows/push-image-cache.yml | 2 -
.github/workflows/release_dockerhub_image.yml | 5 +-
.github/workflows/special-tests.yml | 24 --
.github/workflows/test-provider-packages.yml | 7 +-
.pre-commit-config.yaml | 2 +-
Dockerfile.ci | 2 +-
airflow/api_connexion/security.py | 5 +-
.../common/compat/openlineage/utils/utils.py | 50 ++--
airflow/providers/fab/README.rst | 87 ++++++
.../fab/auth_manager/cli_commands/db_command.py | 17 +-
.../fab/auth_manager/cli_commands/utils.py | 2 +-
.../providers/fab/auth_manager/fab_auth_manager.py | 6 +-
airflow/providers/fab/auth_manager/models/db.py | 13 +-
.../fab/auth_manager/security_manager/override.py | 11 +-
airflow/providers/fab/provider.yaml | 2 +-
airflow/settings.py | 4 +
airflow/www/app.py | 2 +-
airflow/www/extensions/init_session.py | 56 +++-
.../prepare_providers/provider_documentation.py | 7 -
.../src/airflow_breeze/utils/selective_checks.py | 73 ++---
dev/breeze/tests/test_packages.py | 6 +-
dev/breeze/tests/test_selective_checks.py | 56 ++--
dev/refresh_images.sh | 3 +-
.../auth-manager/webserver-authentication.rst | 2 +-
docs/apache-airflow-providers-fab/changelog.rst | 3 +-
docs/apache-airflow-providers-fab/commits.rst | 70 ++++-
docs/apache-airflow-providers-fab/index.rst | 52 +++-
generated/provider_dependencies.json | 2 +-
hatch_build.py | 3 +-
scripts/ci/constraints/ci_branch_constraints.sh | 15 +-
scripts/ci/pre_commit/check_system_tests.py | 6 +-
scripts/ci/pre_commit/mypy_folder.py | 16 +-
scripts/docker/entrypoint_ci.sh | 2 +-
.../in_container/install_airflow_and_providers.py | 5 +-
.../in_container/run_mypy_providers.sh | 17 +-
scripts/in_container/verify_providers.py | 2 +-
tests/conftest.py | 13 +-
.../api/auth/backend/test_kerberos_auth.py | 2 +-
.../auth_manager/api/auth/backend/test_session.py | 2 +-
.../api_endpoints/api_connexion_utils.py | 2 +-
.../api_endpoints/test_asset_endpoint.py | 327 ---------------------
.../fab/auth_manager/api_endpoints/test_auth.py | 11 +-
.../fab/auth_manager/api_endpoints/test_cors.py | 7 +-
.../api_endpoints/test_dag_endpoint.py | 8 +-
.../api_endpoints/test_dag_source_endpoint.py | 6 +-
.../api_endpoints/test_dag_warning_endpoint.py | 6 +-
.../api_endpoints/test_event_log_endpoint.py | 6 +-
.../api_endpoints/test_import_error_endpoint.py | 8 +-
.../test_role_and_permission_endpoint.py | 12 +-
.../api_endpoints/test_task_instance_endpoint.py | 14 +-
.../api_endpoints/test_user_endpoint.py | 16 +-
.../api_endpoints/test_variable_endpoint.py | 6 +-
.../api_endpoints/test_xcom_endpoint.py | 6 +-
.../auth_manager/cli_commands/test_db_command.py | 3 +-
.../auth_manager/cli_commands/test_definition.py | 2 +-
.../auth_manager/cli_commands/test_role_command.py | 4 +-
.../cli_commands/test_sync_perm_command.py | 2 +-
.../auth_manager/cli_commands/test_user_command.py | 2 +-
.../fab/auth_manager/cli_commands/test_utils.py | 4 +-
tests/providers/fab/auth_manager/conftest.py | 3 +-
.../fab/auth_manager/decorators/test_auth.py | 2 +-
.../fab/auth_manager/models/test_anonymous_user.py | 2 +-
tests/providers/fab/auth_manager/models/test_db.py | 3 +-
.../schemas/test_role_and_permission_schema.py | 2 +-
.../fab/auth_manager/schemas/test_user_schema.py | 4 +-
.../security_manager/test_constants.py | 2 +-
.../auth_manager/security_manager/test_override.py | 2 +-
.../fab/auth_manager/test_fab_auth_manager.py | 2 +-
tests/providers/fab/auth_manager/test_models.py | 3 +-
tests/providers/fab/auth_manager/test_security.py | 25 +-
.../fab/auth_manager/views/test_permissions.py | 8 +-
.../fab/auth_manager/views/test_roles_list.py | 8 +-
.../providers/fab/auth_manager/views/test_user.py | 8 +-
.../fab/auth_manager/views/test_user_edit.py | 8 +-
.../fab/auth_manager/views/test_user_stats.py | 8 +-
tests/providers/openlineage/plugins/test_utils.py | 2 +-
tests/test_utils/compat.py | 1 +
tests/test_utils/www.py | 4 +-
tests/www/views/conftest.py | 2 +-
tests/www/views/test_session.py | 13 +-
tests/www/views/test_views.py | 3 +-
tests/www/views/test_views_acl.py | 10 +-
tests/www/views/test_views_base.py | 1 +
tests/www/views/test_views_custom_user_views.py | 2 +-
tests/www/views/test_views_rendered.py | 86 ++++--
89 files changed, 628 insertions(+), 738 deletions(-)
diff --git a/.github/workflows/ci-image-build.yml
b/.github/workflows/ci-image-build.yml
index 49ed1fc2761..7be646c52a7 100644
--- a/.github/workflows/ci-image-build.yml
+++ b/.github/workflows/ci-image-build.yml
@@ -116,7 +116,6 @@ jobs:
PYTHON_MAJOR_MINOR_VERSION: ${{ matrix.python-version }}
DEFAULT_BRANCH: ${{ inputs.branch }}
DEFAULT_CONSTRAINTS_BRANCH: ${{ inputs.constraints-branch }}
- VERSION_SUFFIX_FOR_PYPI: "dev0"
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_USERNAME: ${{ github.actor }}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 6af4796ccb1..a18ce06b566 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -466,26 +466,26 @@ jobs:
debug-resources: ${{ needs.build-info.outputs.debug-resources }}
use-uv: ${{ needs.build-info.outputs.use-uv }}
- tests-integration-system:
- name: Integration and System Tests
- needs: [build-info, build-ci-images]
- uses: ./.github/workflows/integration-system-tests.yml
- permissions:
- contents: read
- packages: read
- with:
- runs-on-as-json-public: ${{
needs.build-info.outputs.runs-on-as-json-public }}
- testable-core-integrations: ${{
needs.build-info.outputs.testable-core-integrations }}
- testable-providers-integrations: ${{
needs.build-info.outputs.testable-providers-integrations }}
- run-system-tests: ${{ needs.build-info.outputs.run-tests }}
- default-python-version: ${{
needs.build-info.outputs.default-python-version }}
- default-postgres-version: ${{
needs.build-info.outputs.default-postgres-version }}
- default-mysql-version: ${{
needs.build-info.outputs.default-mysql-version }}
- skip-providers-tests: ${{ needs.build-info.outputs.skip-providers-tests
}}
- run-coverage: ${{ needs.build-info.outputs.run-coverage }}
- debug-resources: ${{ needs.build-info.outputs.debug-resources }}
- use-uv: ${{ needs.build-info.outputs.use-uv }}
- if: needs.build-info.outputs.run-tests == 'true'
+ # tests-integration-system:
+ # name: Integration and System Tests
+ # needs: [build-info, build-ci-images]
+ # uses: ./.github/workflows/integration-system-tests.yml
+ # permissions:
+ # contents: read
+ # packages: read
+ # with:
+ # runs-on-as-json-public: ${{
needs.build-info.outputs.runs-on-as-json-public }}
+ # testable-core-integrations: ${{
needs.build-info.outputs.testable-core-integrations }}
+ # testable-providers-integrations: ${{
needs.build-info.outputs.testable-providers-integrations }}
+ # run-system-tests: ${{ needs.build-info.outputs.run-tests }}
+ # default-python-version: ${{
needs.build-info.outputs.default-python-version }}
+ # default-postgres-version: ${{
needs.build-info.outputs.default-postgres-version }}
+ # default-mysql-version: ${{
needs.build-info.outputs.default-mysql-version }}
+ # skip-providers-tests: ${{
needs.build-info.outputs.skip-providers-tests }}
+ # run-coverage: ${{ needs.build-info.outputs.run-coverage }}
+ # debug-resources: ${{ needs.build-info.outputs.debug-resources }}
+ # use-uv: ${{ needs.build-info.outputs.use-uv }}
+ # if: needs.build-info.outputs.run-tests == 'true'
tests-with-lowest-direct-resolution:
name: "Lowest direct dependency providers tests"
@@ -593,7 +593,6 @@ jobs:
- tests-mysql
- tests-postgres
- tests-non-db
- - tests-integration-system
- build-prod-images
uses: ./.github/workflows/finalize-tests.yml
with:
diff --git a/.github/workflows/helm-tests.yml b/.github/workflows/helm-tests.yml
index 1b4aa19cbe5..2b72e08be7d 100644
--- a/.github/workflows/helm-tests.yml
+++ b/.github/workflows/helm-tests.yml
@@ -117,7 +117,7 @@ jobs:
- name: "Helm release tarball"
run: >
breeze release-management prepare-helm-chart-tarball
--ignore-version-check --override-tag
- --skip-tag-signing --version 0.0.0 --version-suffix dev0
+ --skip-tag-signing --version 0.0.0
- name: Generate GPG key for signing
# Sometimes the key will be already added to the keyring, so we ignore
the error
run: gpg --batch --passphrase '' --quick-gen-key
[email protected] default default || true
diff --git a/.github/workflows/prod-image-build.yml
b/.github/workflows/prod-image-build.yml
index 96e653d10ca..4a662eb4607 100644
--- a/.github/workflows/prod-image-build.yml
+++ b/.github/workflows/prod-image-build.yml
@@ -126,7 +126,6 @@ jobs:
if: inputs.prod-image-build == 'true'
env:
PYTHON_MAJOR_MINOR_VERSION: "${{ inputs.default-python-version }}"
- VERSION_SUFFIX_FOR_PYPI: ${{ inputs.branch == 'main' && 'dev0' || '' }}
steps:
- name: "Cleanup repo"
shell: bash
@@ -197,7 +196,6 @@ jobs:
PYTHON_MAJOR_MINOR_VERSION: "${{ matrix.python-version }}"
DEFAULT_BRANCH: ${{ inputs.branch }}
DEFAULT_CONSTRAINTS_BRANCH: ${{ inputs.constraints-branch }}
- VERSION_SUFFIX_FOR_PYPI: ${{ inputs.branch == 'main' && 'dev0' || '' }}
INCLUDE_NOT_READY_PROVIDERS: "true"
# You can override CONSTRAINTS_GITHUB_REPOSITORY by setting secret in
your repo but by default the
# Airflow one is going to be used
diff --git a/.github/workflows/push-image-cache.yml
b/.github/workflows/push-image-cache.yml
index 60f3be1b181..edaa32e60bc 100644
--- a/.github/workflows/push-image-cache.yml
+++ b/.github/workflows/push-image-cache.yml
@@ -116,7 +116,6 @@ jobs:
PYTHON_MAJOR_MINOR_VERSION: "${{ matrix.python }}"
UPGRADE_TO_NEWER_DEPENDENCIES: "false"
VERBOSE: "true"
- VERSION_SUFFIX_FOR_PYPI: "dev0"
steps:
- name: "Cleanup repo"
shell: bash
@@ -191,7 +190,6 @@ jobs:
PYTHON_MAJOR_MINOR_VERSION: "${{ matrix.python }}"
UPGRADE_TO_NEWER_DEPENDENCIES: "false"
VERBOSE: "true"
- VERSION_SUFFIX_FOR_PYPI: "dev0"
if: inputs.include-prod-images == 'true'
steps:
- name: "Cleanup repo"
diff --git a/.github/workflows/release_dockerhub_image.yml
b/.github/workflows/release_dockerhub_image.yml
index 5fe9eaad7f2..20901d88cb5 100644
--- a/.github/workflows/release_dockerhub_image.yml
+++ b/.github/workflows/release_dockerhub_image.yml
@@ -145,16 +145,13 @@ jobs:
- name: "Create airflow_cache builder"
run: docker buildx create --name airflow_cache
- name: "Prepare chicken-eggs provider packages"
- # In case of provider packages which use latest dev0 version of
providers, we should prepare them
- # from the source code, not from the PyPI because they have
apache-airflow>=X.Y.Z dependency
- # And when we prepare them from sources they will have
apache-airflow>=X.Y.Z.dev0
shell: bash
env:
CHICKEN_EGG_PROVIDERS: ${{
needs.build-info.outputs.chicken-egg-providers }}
run: >
breeze release-management prepare-provider-packages
--package-format wheel
- --version-suffix-for-pypi dev0 ${CHICKEN_EGG_PROVIDERS}
+ ${CHICKEN_EGG_PROVIDERS}
if: needs.build-info.outputs.chicken-egg-providers != ''
- name: "Copy dist packages to docker-context files"
shell: bash
diff --git a/.github/workflows/special-tests.yml
b/.github/workflows/special-tests.yml
index 09e08b7561c..39566a6042c 100644
--- a/.github/workflows/special-tests.yml
+++ b/.github/workflows/special-tests.yml
@@ -200,27 +200,3 @@ jobs:
debug-resources: ${{ inputs.debug-resources }}
use-uv: ${{ inputs.use-uv }}
if: ${{ inputs.default-branch == 'main' }}
-
- # matrix.test-group comes from run-unit-tests.yml
- tests-system:
- name: "System test: ${{ matrix.test-group }}"
- uses: ./.github/workflows/run-unit-tests.yml
- permissions:
- contents: read
- packages: read
- with:
- runs-on-as-json-default: ${{ inputs.runs-on-as-json-default }}
- test-name: "SystemTest"
- test-scope: "System"
- test-groups: ${{ inputs.test-groups }}
- backend: "postgres"
- python-versions: "['${{ inputs.default-python-version }}']"
- backend-versions: "['${{ inputs.default-postgres-version }}']"
- excluded-providers-as-string: ${{ inputs.excluded-providers-as-string }}
- excludes: "[]"
- core-test-types-list-as-string: ${{
inputs.core-test-types-list-as-string }}
- providers-test-types-list-as-string: ${{
inputs.providers-test-types-list-as-string }}
- include-success-outputs: ${{ inputs.include-success-outputs }}
- run-coverage: ${{ inputs.run-coverage }}
- debug-resources: ${{ inputs.debug-resources }}
- use-uv: ${{ inputs.use-uv }}
diff --git a/.github/workflows/test-provider-packages.yml
b/.github/workflows/test-provider-packages.yml
index 4aa2f4116e3..a008f4290f7 100644
--- a/.github/workflows/test-provider-packages.yml
+++ b/.github/workflows/test-provider-packages.yml
@@ -99,15 +99,15 @@ jobs:
- name: "Prepare provider documentation"
run: >
breeze release-management prepare-provider-documentation
--include-not-ready-providers
- --non-interactive
+ --non-interactive fab
if: matrix.package-format == 'wheel'
- name: "Prepare provider packages: ${{ matrix.package-format }}"
run: >
breeze release-management prepare-provider-packages
--include-not-ready-providers
- --version-suffix-for-pypi dev0 --package-format ${{
matrix.package-format }}
+ --package-format ${{ matrix.package-format }} fab common.compat
- name: "Prepare airflow package: ${{ matrix.package-format }}"
run: >
- breeze release-management prepare-airflow-package
--version-suffix-for-pypi dev0
+ breeze release-management prepare-airflow-package
--package-format ${{ matrix.package-format }}
- name: "Verify ${{ matrix.package-format }} packages with twine"
run: |
@@ -167,7 +167,6 @@ jobs:
GITHUB_USERNAME: ${{ github.actor }}
INCLUDE_NOT_READY_PROVIDERS: "true"
PYTHON_MAJOR_MINOR_VERSION: "${{ matrix.python-version }}"
- VERSION_SUFFIX_FOR_PYPI: "dev0"
VERBOSE: "true"
CLEAN_AIRFLOW_INSTALLATION: "${{ inputs.canary-run }}"
if: inputs.skip-providers-tests != 'true'
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index b3bea00cebe..259408d1ae6 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1111,7 +1111,7 @@ repos:
stages: ['manual']
name: Run mypy for providers (manual)
language: python
- entry: ./scripts/ci/pre_commit/mypy_folder.py
providers/src/airflow/providers
+ entry: ./scripts/ci/pre_commit/mypy_folder.py airflow/providers/fab
pass_filenames: false
files: ^.*\.py$
require_serial: true
diff --git a/Dockerfile.ci b/Dockerfile.ci
index 2004160a6f3..04cbe45928f 100644
--- a/Dockerfile.ci
+++ b/Dockerfile.ci
@@ -1041,7 +1041,7 @@ function check_force_lowest_dependencies() {
EXTRA="[devel]"
if [[ ${TEST_TYPE=} =~ Providers\[.*\] ]]; then
# shellcheck disable=SC2001
- EXTRA=$(echo "[${TEST_TYPE},devel]" | sed 's/Providers\[\(.*\)\]/\1/')
+ EXTRA=$(echo "[${TEST_TYPE}]" | sed
's/Providers\[\([^]]*\)\]/\1,devel/')
echo
echo "${COLOR_BLUE}Forcing dependencies to lowest versions for
provider: ${EXTRA}${COLOR_RESET}"
echo
diff --git a/airflow/api_connexion/security.py
b/airflow/api_connexion/security.py
index 660bc6cce23..c68094a5212 100644
--- a/airflow/api_connexion/security.py
+++ b/airflow/api_connexion/security.py
@@ -45,13 +45,14 @@ T = TypeVar("T", bound=Callable)
def check_authentication() -> None:
"""Check that the request has valid authorization information."""
- for auth in get_airflow_app().api_auth:
+ airflow_app = get_airflow_app()
+ for auth in airflow_app.api_auth:
response = auth.requires_authentication(Response)()
if response.status_code == 200:
return
# Even if the current_user is anonymous, the AUTH_ROLE_PUBLIC might still
have permission.
- appbuilder = get_airflow_app().appbuilder
+ appbuilder = airflow_app.appbuilder
if appbuilder.get_app.config.get("AUTH_ROLE_PUBLIC", None):
return
diff --git a/airflow/providers/common/compat/openlineage/utils/utils.py
b/airflow/providers/common/compat/openlineage/utils/utils.py
index 5492c76d55d..0d393b51267 100644
--- a/airflow/providers/common/compat/openlineage/utils/utils.py
+++ b/airflow/providers/common/compat/openlineage/utils/utils.py
@@ -20,24 +20,32 @@ from __future__ import annotations
from functools import wraps
from typing import TYPE_CHECKING
-if TYPE_CHECKING:
- from airflow.providers.openlineage.utils.utils import
translate_airflow_asset
-else:
- try:
- from airflow.providers.openlineage.utils.utils import
translate_airflow_asset
- except ImportError:
- from airflow.providers.openlineage.utils.utils import
translate_airflow_dataset
-
- def rename_asset_as_dataset(function):
- @wraps(function)
- def wrapper(*args, **kwargs):
- if "asset" in kwargs:
- kwargs["dataset"] = kwargs.pop("asset")
- return function(*args, **kwargs)
-
- return wrapper
-
- translate_airflow_asset =
rename_asset_as_dataset(translate_airflow_dataset)
-
-
-__all__ = ["translate_airflow_asset"]
+from airflow.exceptions import AirflowOptionalProviderFeatureException
+
+try:
+ if TYPE_CHECKING:
+ try:
+ from airflow.providers.openlineage.utils.utils import ( #
type:ignore [attr-defined]
+ translate_airflow_asset,
+ )
+ except ImportError:
+ raise AirflowOptionalProviderFeatureException()
+ else:
+ try:
+ from airflow.providers.openlineage.utils.utils import
translate_airflow_asset
+ except ImportError:
+ from airflow.providers.openlineage.utils.utils import
translate_airflow_dataset
+
+ def rename_asset_as_dataset(function):
+ @wraps(function)
+ def wrapper(*args, **kwargs):
+ if "asset" in kwargs:
+ kwargs["dataset"] = kwargs.pop("asset")
+ return function(*args, **kwargs)
+
+ return wrapper
+
+ translate_airflow_asset =
rename_asset_as_dataset(translate_airflow_dataset)
+ __all__ = ["translate_airflow_asset"]
+except ImportError:
+ raise AirflowOptionalProviderFeatureException()
diff --git a/airflow/providers/fab/README.rst b/airflow/providers/fab/README.rst
new file mode 100644
index 00000000000..f1e4eac1ac6
--- /dev/null
+++ b/airflow/providers/fab/README.rst
@@ -0,0 +1,87 @@
+
+ .. 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.
+
+ .. NOTE! THIS FILE IS AUTOMATICALLY GENERATED AND WILL BE OVERWRITTEN!
+
+ .. IF YOU WANT TO MODIFY TEMPLATE FOR THIS FILE, YOU SHOULD MODIFY THE
TEMPLATE
+ `PROVIDER_README_TEMPLATE.rst.jinja2` IN the
`dev/breeze/src/airflow_breeze/templates` DIRECTORY
+
+
+Package ``apache-airflow-providers-fab``
+
+Release: ``1.5.4``
+
+
+`Flask App Builder <https://flask-appbuilder.readthedocs.io/>`__
+
+
+Provider package
+----------------
+
+This is a provider package for ``fab`` provider. All classes for this provider
package
+are in ``airflow.providers.fab`` python package.
+
+You can find package information and changelog for the provider
+in the `documentation
<https://airflow.apache.org/docs/apache-airflow-providers-fab/1.5.4/>`_.
+
+Installation
+------------
+
+You can install this package on top of an existing Airflow 2 installation (see
``Requirements`` below
+for the minimum Airflow version supported) via
+``pip install apache-airflow-providers-fab``
+
+The package supports the following python versions: 3.10,3.11,3.12
+
+Requirements
+------------
+
+========================================== ==================
+PIP package Version required
+========================================== ==================
+``apache-airflow`` ``>=2.9.0``
+``apache-airflow-providers-common-compat`` ``>=1.2.1``
+``flask-login`` ``>=0.6.3``
+``flask-session`` ``>=0.8.0``
+``flask`` ``>=2.2,<3``
+``flask-appbuilder`` ``==4.5.4``
+``google-re2`` ``>=1.0``
+``jmespath`` ``>=0.7.0``
+========================================== ==================
+
+Cross provider package dependencies
+-----------------------------------
+
+Those are dependencies that might be needed in order to use all the features
of the package.
+You need to install the specified provider packages in order to use them.
+
+You can install such cross-provider dependencies when installing from PyPI.
For example:
+
+.. code-block:: bash
+
+ pip install apache-airflow-providers-fab[common.compat]
+
+
+==================================================================================================================
=================
+Dependent package
Extra
+==================================================================================================================
=================
+`apache-airflow-providers-common-compat
<https://airflow.apache.org/docs/apache-airflow-providers-common-compat>`_
``common.compat``
+==================================================================================================================
=================
+
+The changelog for the provider package can be found in the
+`changelog
<https://airflow.apache.org/docs/apache-airflow-providers-fab/1.5.4/changelog.html>`_.
diff --git a/airflow/providers/fab/auth_manager/cli_commands/db_command.py
b/airflow/providers/fab/auth_manager/cli_commands/db_command.py
index 8b41cf4216c..861953a0fe2 100644
--- a/airflow/providers/fab/auth_manager/cli_commands/db_command.py
+++ b/airflow/providers/fab/auth_manager/cli_commands/db_command.py
@@ -17,10 +17,19 @@
from __future__ import annotations
from airflow import settings
-from airflow.cli.commands.db_command import run_db_downgrade_command,
run_db_migrate_command
-from airflow.providers.fab.auth_manager.models.db import _REVISION_HEADS_MAP,
FABDBManager
-from airflow.utils import cli as cli_utils
-from airflow.utils.providers_configuration_loader import
providers_configuration_loaded
+
+try:
+ from airflow.cli.commands.db_command import ( # type:ignore [attr-defined]
+ run_db_downgrade_command,
+ run_db_migrate_command,
+ )
+ from airflow.providers.fab.auth_manager.models.db import
_REVISION_HEADS_MAP, FABDBManager
+ from airflow.utils import cli as cli_utils
+ from airflow.utils.providers_configuration_loader import
providers_configuration_loaded
+except ImportError:
+ from airflow.exceptions import AirflowOptionalProviderFeatureException
+
+ raise AirflowOptionalProviderFeatureException()
@providers_configuration_loaded
diff --git a/airflow/providers/fab/auth_manager/cli_commands/utils.py
b/airflow/providers/fab/auth_manager/cli_commands/utils.py
index e848c2094ce..1a3efa84f97 100644
--- a/airflow/providers/fab/auth_manager/cli_commands/utils.py
+++ b/airflow/providers/fab/auth_manager/cli_commands/utils.py
@@ -42,7 +42,7 @@ def _return_appbuilder(app: Flask) -> AirflowAppBuilder:
"""Return an appbuilder instance for the given app."""
init_appbuilder(app)
init_plugins(app)
- init_airflow_session_interface(app)
+ init_airflow_session_interface(app, None)
return app.appbuilder # type: ignore[attr-defined]
diff --git a/airflow/providers/fab/auth_manager/fab_auth_manager.py
b/airflow/providers/fab/auth_manager/fab_auth_manager.py
index e93e440f5dd..a0f7cdef057 100644
--- a/airflow/providers/fab/auth_manager/fab_auth_manager.py
+++ b/airflow/providers/fab/auth_manager/fab_auth_manager.py
@@ -95,7 +95,7 @@ if TYPE_CHECKING:
)
from airflow.providers.common.compat.assets import AssetDetails
from airflow.providers.fab.auth_manager.security_manager.override import
FabAirflowSecurityManagerOverride
- from airflow.security.permissions import RESOURCE_ASSET
+ from airflow.security.permissions import RESOURCE_ASSET # type:
ignore[attr-defined]
else:
from airflow.providers.common.compat.security.permissions import
RESOURCE_ASSET
@@ -403,9 +403,7 @@ class FabAuthManager(BaseAuthManager):
def get_url_user_profile(self) -> str | None:
"""Return the url to a page displaying info about the current user."""
- if not self.security_manager.user_view or
self.appbuilder.get_app.config.get(
- "AUTH_ROLE_PUBLIC", None
- ):
+ if not self.security_manager.user_view:
return None
return url_for(f"{self.security_manager.user_view.endpoint}.userinfo")
diff --git a/airflow/providers/fab/auth_manager/models/db.py
b/airflow/providers/fab/auth_manager/models/db.py
index ce0efef55a1..ae7d39f7216 100644
--- a/airflow/providers/fab/auth_manager/models/db.py
+++ b/airflow/providers/fab/auth_manager/models/db.py
@@ -18,11 +18,14 @@ from __future__ import annotations
from pathlib import Path
-from airflow import settings
-from airflow.exceptions import AirflowException
-from airflow.providers.fab.auth_manager.models import metadata
-from airflow.utils.db import _offline_migration, print_happy_cat
-from airflow.utils.db_manager import BaseDBManager
+try:
+ from airflow import settings
+ from airflow.exceptions import AirflowException,
AirflowOptionalProviderFeatureException
+ from airflow.providers.fab.auth_manager.models import metadata
+ from airflow.utils.db import _offline_migration, print_happy_cat
+ from airflow.utils.db_manager import BaseDBManager
+except ImportError:
+ raise AirflowOptionalProviderFeatureException()
PACKAGE_DIR = Path(__file__).parents[2]
diff --git a/airflow/providers/fab/auth_manager/security_manager/override.py
b/airflow/providers/fab/auth_manager/security_manager/override.py
index 73b04e0a3ab..3cdaf214038 100644
--- a/airflow/providers/fab/auth_manager/security_manager/override.py
+++ b/airflow/providers/fab/auth_manager/security_manager/override.py
@@ -69,7 +69,6 @@ from flask_jwt_extended import JWTManager, current_user as
current_user_jwt
from flask_login import LoginManager
from itsdangerous import want_bytes
from markupsafe import Markup
-from packaging.version import Version
from sqlalchemy import and_, func, inspect, literal, or_, select
from sqlalchemy.exc import MultipleResultsFound
from sqlalchemy.orm import Session, joinedload
@@ -850,7 +849,8 @@ class
FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
app.config.setdefault("AUTH_ROLES_SYNC_AT_LOGIN", False)
app.config.setdefault("AUTH_API_LOGIN_ALLOW_MULTIPLE_PROVIDERS", False)
- # Werkzeug prior to 3.0.0 does not support scrypt
+ from packaging.version import Version
+
parsed_werkzeug_version =
Version(importlib.metadata.version("werkzeug"))
if parsed_werkzeug_version < Version("3.0.0"):
app.config.setdefault(
@@ -861,9 +861,10 @@ class
FabAirflowSecurityManagerOverride(AirflowSecurityManagerV2):
else:
app.config.setdefault(
"AUTH_DB_FAKE_PASSWORD_HASH_CHECK",
-
"scrypt:32768:8:1$wiDa0ruWlIPhp9LM$6e409d093e62ad54df2af895d0e125b05ff6cf6414"
-
"8350189ffc4bcc71286edf1b8ad94a442c00f890224bf2b32153d0750c89ee9"
- "401e62f9dcee5399065e4e5",
+ "scrypt:32768:8:1$wiDa0ruWlIPhp9LM$6e40"
+ "9d093e62ad54df2af895d0e125b05ff6cf6414"
+ "8350189ffc4bcc71286edf1b8ad94a442c00f8"
+ "90224bf2b32153d0750c89ee9401e62f9dcee5399065e4e5",
)
# LDAP Config
diff --git a/airflow/providers/fab/provider.yaml
b/airflow/providers/fab/provider.yaml
index f36519ff278..dfa4a70025c 100644
--- a/airflow/providers/fab/provider.yaml
+++ b/airflow/providers/fab/provider.yaml
@@ -62,7 +62,7 @@ dependencies:
# Every time we update FAB version here, please make sure that you review
the classes and models in
# `airflow/providers/fab/auth_manager/security_manager/override.py` with
their upstream counterparts.
# In particular, make sure any breaking changes, for example any new
methods, are accounted for.
- - flask-appbuilder==4.5.3
+ - flask-appbuilder==4.5.4
- google-re2>=1.0
- jmespath>=0.7.0
diff --git a/airflow/settings.py b/airflow/settings.py
index 85d56d4be85..259e53078d4 100644
--- a/airflow/settings.py
+++ b/airflow/settings.py
@@ -613,6 +613,10 @@ def dispose_orm():
global engine
global Session
+ from airflow.www.extensions import init_session
+
+ init_session._session_interface = None
+
if Session is not None: # type: ignore[truthy-function]
Session.remove()
Session = None
diff --git a/airflow/www/app.py b/airflow/www/app.py
index 927d3c9b693..79e2dcf6c4b 100644
--- a/airflow/www/app.py
+++ b/airflow/www/app.py
@@ -187,7 +187,7 @@ def create_app(config=None, testing=False):
init_jinja_globals(flask_app)
init_xframe_protection(flask_app)
init_cache_control(flask_app)
- init_airflow_session_interface(flask_app)
+ init_airflow_session_interface(flask_app, db)
init_check_user_active(flask_app)
return flask_app
diff --git a/airflow/www/extensions/init_session.py
b/airflow/www/extensions/init_session.py
index 349990a39a2..c444a7c8586 100644
--- a/airflow/www/extensions/init_session.py
+++ b/airflow/www/extensions/init_session.py
@@ -23,7 +23,52 @@ from airflow.exceptions import AirflowConfigException
from airflow.www.session import AirflowDatabaseSessionInterface,
AirflowSecureCookieSessionInterface
-def init_airflow_session_interface(app):
+# Monkey patch flask-session's create_session_model to fix compatibility with
Flask-SQLAlchemy 2.5.1
+# The issue is that dynamically created Session models don't inherit
query_class from db.Model,
+# which causes AttributeError when flask-session tries to use .query property.
+# This patch ensures query_class is set on the Session model class.
+def _patch_flask_session_create_session_model():
+ """
+ Patch flask-session's create_session_model to ensure query_class
compatibility.
+
+ This fixes the issue where flask-session's Session model doesn't have the
query_class
+ attribute required by Flask-SQLAlchemy's _QueryProperty.
+ """
+ try:
+ from flask_session.sqlalchemy import sqlalchemy as flask_session_module
+
+ _original_create_session_model =
flask_session_module.create_session_model
+ _session_model = None
+
+ def patched_create_session_model(db, table_name, schema=None,
bind_key=None, sequence=None):
+ nonlocal _session_model
+ if _session_model:
+ return _session_model
+
+ # Create new model
+ Session = _original_create_session_model(db, table_name, schema,
bind_key, sequence)
+
+ # Ensure query_class is set for compatibility with Flask-SQLAlchemy
+ # Use db.Query which is always available on the SQLAlchemy instance
+ if not hasattr(Session, "query_class"):
+ Session.query_class = getattr(db, "Query", None) or
getattr(db.Model, "query_class", None)
+
+ _session_model = Session
+ return Session
+
+ flask_session_module.create_session_model =
patched_create_session_model
+ except ImportError:
+ # flask-session not installed, no need to patch
+ pass
+
+
+# Apply the patch immediately when this module is imported
+_patch_flask_session_create_session_model()
+
+_session_interface = None
+
+
+def init_airflow_session_interface(app, sqlalchemy_client):
"""Set airflow session interface."""
config = app.config.copy()
selected_backend = conf.get("webserver", "SESSION_BACKEND")
@@ -42,9 +87,13 @@ def init_airflow_session_interface(app):
app.before_request(make_session_permanent)
elif selected_backend == "database":
- app.session_interface = AirflowDatabaseSessionInterface(
+ global _session_interface
+ if _session_interface:
+ app.session_interface = _session_interface
+ return
+ _session_interface = AirflowDatabaseSessionInterface(
app=app,
- client=None,
+ client=sqlalchemy_client,
permanent=permanent_cookie,
# Typically these would be configurable with Flask-Session,
# but we will set them explicitly instead as they don't make
@@ -53,6 +102,7 @@ def init_airflow_session_interface(app):
key_prefix="",
use_signer=True,
)
+ app.session_interface = _session_interface
else:
raise AirflowConfigException(
"Unrecognized session backend specified in "
diff --git
a/dev/breeze/src/airflow_breeze/prepare_providers/provider_documentation.py
b/dev/breeze/src/airflow_breeze/prepare_providers/provider_documentation.py
index 6e18966416d..15b54dd3ef2 100644
--- a/dev/breeze/src/airflow_breeze/prepare_providers/provider_documentation.py
+++ b/dev/breeze/src/airflow_breeze/prepare_providers/provider_documentation.py
@@ -21,7 +21,6 @@ import difflib
import os
import random
import re
-import shutil
import subprocess
import sys
import tempfile
@@ -1192,12 +1191,6 @@ def _generate_build_files_for_provider(
)
init_py_path = provider_details.base_provider_package_path / "__init__.py"
init_py_path.write_text(init_py_content)
- # TODO(potiuk) - remove this if when we move all providers to new structure
- if provider_details.is_new_structure:
- _generate_get_provider_info_py(context, provider_details)
- _generate_readme_rst(context, provider_details)
- _generate_pyproject(context, provider_details)
- shutil.copy(AIRFLOW_SOURCES_ROOT / "LICENSE",
provider_details.base_provider_package_path / "LICENSE")
def _replace_min_airflow_version_in_provider_yaml(
diff --git a/dev/breeze/src/airflow_breeze/utils/selective_checks.py
b/dev/breeze/src/airflow_breeze/utils/selective_checks.py
index ac6f7d40dee..9b5afdc743c 100644
--- a/dev/breeze/src/airflow_breeze/utils/selective_checks.py
+++ b/dev/breeze/src/airflow_breeze/utils/selective_checks.py
@@ -55,7 +55,6 @@ from airflow_breeze.global_constants import (
SelectiveProvidersTestType,
all_helm_test_packages,
all_selective_core_test_types,
- providers_test_type,
)
from airflow_breeze.utils.console import get_console
from airflow_breeze.utils.exclude_from_matrix import excluded_combos
@@ -88,9 +87,7 @@ USE_SELF_HOSTED_RUNNERS_LABEL = "use self-hosted runners"
ALL_CI_SELECTIVE_TEST_TYPES = "API Always CLI Core Operators Other
Serialization WWW"
-ALL_PROVIDERS_SELECTIVE_TEST_TYPES = (
- "Providers[-amazon,google,standard] Providers[amazon] Providers[google]
Providers[standard]"
-)
+ALL_PROVIDERS_SELECTIVE_TEST_TYPES = "Providers[fab]"
class FileGroupForCi(Enum):
@@ -653,7 +650,7 @@ class SelectiveChecks:
FileGroupForCi.ALL_PROVIDERS_PYTHON_FILES,
CI_FILE_GROUP_MATCHES, CI_FILE_GROUP_EXCLUDES
)
or self._are_all_providers_affected()
- ) and self._default_branch == "main":
+ ):
checks_to_run.append("mypy-providers")
if (
self._matching_files(
@@ -767,7 +764,7 @@ class SelectiveChecks:
def _are_all_providers_affected(self) -> bool:
# if "Providers" test is present in the list of tests, it means that
we should run all providers tests
# prepare all providers packages and build all providers documentation
- return "Providers" in self._get_providers_test_types_to_run()
+ return "Providers[fab]" in self._get_providers_test_types_to_run()
def _fail_if_suspended_providers_affected(self) -> bool:
return "allow suspended provider changes" not in self._pr_labels
@@ -841,13 +838,12 @@ class SelectiveChecks:
return sorted_candidate_test_types
def _get_providers_test_types_to_run(self, split_to_individual_providers:
bool = False) -> list[str]:
- if self._default_branch != "main":
- return []
+ # For v2-11-test branch we always run airflow + fab provider tests
if self.full_tests_needed:
if split_to_individual_providers:
- return list(providers_test_type())
+ return ["Providers[fab]"]
else:
- return ["Providers"]
+ return ["Providers[fab]"]
else:
all_providers_source_files = self._matching_files(
FileGroupForCi.ALL_PROVIDERS_PYTHON_FILES,
CI_FILE_GROUP_MATCHES, CI_FILE_GROUP_EXCLUDES
@@ -864,26 +860,7 @@ class SelectiveChecks:
# IF API tests are needed, that will trigger extra provider
checks
return []
else:
- affected_providers = self._find_all_providers_affected(
- include_docs=False,
- )
- candidate_test_types: set[str] = set()
- if isinstance(affected_providers, AllProvidersSentinel):
- if split_to_individual_providers:
- for provider in get_available_packages():
- candidate_test_types.add(f"Providers[{provider}]")
- else:
- candidate_test_types.add("Providers")
- elif affected_providers:
- if split_to_individual_providers:
- for provider in affected_providers:
- candidate_test_types.add(f"Providers[{provider}]")
- else:
-
candidate_test_types.add(f"Providers[{','.join(sorted(affected_providers))}]")
- sorted_candidate_test_types = sorted(candidate_test_types)
- get_console().print("[warning]Selected providers test type candidates
to run:[/]")
- get_console().print(sorted_candidate_test_types)
- return sorted_candidate_test_types
+ return ["fab"]
@staticmethod
def _extract_long_provider_tests(current_test_types: set[str]):
@@ -930,7 +907,8 @@ class SelectiveChecks:
if self._default_branch != "main":
test_types_to_remove: set[str] = set()
for test_type in current_test_types:
- if test_type.startswith("Providers"):
+ # For v2-11-test branch we always run airflow + fab provider
tests
+ if test_type.startswith("Providers") and not
test_type.startswith("Providers[fab]"):
get_console().print(
f"[warning]Removing {test_type} because the target
branch "
f"is {self._default_branch} and not main[/]"
@@ -1072,7 +1050,8 @@ class SelectiveChecks:
if not self.docs_build:
return None
if self._default_branch != "main":
- return "apache-airflow docker-stack"
+ # For v2-11-test branch we always run airflow + fab provider tests
+ return "apache-airflow docker-stack fab"
if self.full_tests_needed:
return _ALL_DOCS_LIST
providers_affected = self._find_all_providers_affected(
@@ -1164,8 +1143,7 @@ class SelectiveChecks:
@cached_property
def skip_providers_tests(self) -> bool:
- if self._default_branch != "main":
- return True
+ # For v2-11-test branch we always run airflow + fab provider tests
if self.full_tests_needed:
return False
if self._get_providers_test_types_to_run():
@@ -1200,18 +1178,21 @@ class SelectiveChecks:
@cached_property
def selected_providers_list_as_string(self) -> str | None:
- if self._default_branch != "main":
- return None
- if self.full_tests_needed:
- return ""
- if self._are_all_providers_affected():
- return ""
- affected_providers =
self._find_all_providers_affected(include_docs=True)
- if not affected_providers:
- return None
- if isinstance(affected_providers, AllProvidersSentinel):
- return ""
- return " ".join(sorted(affected_providers))
+ # For v2-11-test branch we always test airflow + fab provider
+ return "fab"
+
+ # if self._default_branch != "main":
+ # return None
+ # if self.full_tests_needed:
+ # return ""
+ # if self._are_all_providers_affected():
+ # return ""
+ # affected_providers =
self._find_all_providers_affected(include_docs=True)
+ # if not affected_providers:
+ # return None
+ # if isinstance(affected_providers, AllProvidersSentinel):
+ # return ""
+ # return " ".join(sorted(affected_providers))
@cached_property
def runs_on_as_json_default(self) -> str:
diff --git a/dev/breeze/tests/test_packages.py
b/dev/breeze/tests/test_packages.py
index fd0cb32391c..81df4011ba1 100644
--- a/dev/breeze/tests/test_packages.py
+++ b/dev/breeze/tests/test_packages.py
@@ -157,7 +157,7 @@ def test_find_matching_long_package_name_bad_filter():
"""
"apache-airflow-providers-common-compat>=1.2.1",
"apache-airflow>=2.9.0",
- "flask-appbuilder==4.5.3",
+ "flask-appbuilder==4.5.4",
"flask-login>=0.6.3",
"flask-session>=0.8.0",
"flask>=2.2,<3",
@@ -172,7 +172,7 @@ def test_find_matching_long_package_name_bad_filter():
"""
"apache-airflow-providers-common-compat>=1.2.1.dev0",
"apache-airflow>=2.9.0.dev0",
- "flask-appbuilder==4.5.3",
+ "flask-appbuilder==4.5.4",
"flask-login>=0.6.3",
"flask-session>=0.8.0",
"flask>=2.2,<3",
@@ -187,7 +187,7 @@ def test_find_matching_long_package_name_bad_filter():
"""
"apache-airflow-providers-common-compat>=1.2.1b0",
"apache-airflow>=2.9.0b0",
- "flask-appbuilder==4.5.3",
+ "flask-appbuilder==4.5.4",
"flask-login>=0.6.3",
"flask-session>=0.8.0",
"flask>=2.2,<3",
diff --git a/dev/breeze/tests/test_selective_checks.py
b/dev/breeze/tests/test_selective_checks.py
index 604aec1dcc4..be8febcc70f 100644
--- a/dev/breeze/tests/test_selective_checks.py
+++ b/dev/breeze/tests/test_selective_checks.py
@@ -30,7 +30,6 @@ from airflow_breeze.global_constants import (
GithubEvents,
)
from airflow_breeze.utils.functools_cache import clearable_cache
-from airflow_breeze.utils.packages import get_available_packages
from airflow_breeze.utils.selective_checks import (
ALL_CI_SELECTIVE_TEST_TYPES,
ALL_PROVIDERS_SELECTIVE_TEST_TYPES,
@@ -41,10 +40,8 @@ ANSI_COLORS_MATCHER =
re.compile(r"(?:\x1B[@-_]|[\x80-\x9F])[0-?]*[ -/]*[@-~]")
ALL_DOCS_SELECTED_FOR_BUILD = ""
-ALL_PROVIDERS_AFFECTED = ""
-LIST_OF_ALL_PROVIDER_TESTS = " ".join(
- f"Providers[{provider}]" for provider in
get_available_packages(include_not_ready=True)
-)
+ALL_PROVIDERS_AFFECTED = "fab"
+LIST_OF_ALL_PROVIDER_TESTS = "Providers[fab]"
# commit that is neutral - allows to keep pyproject.toml-changing PRS neutral
for unit tests
@@ -295,7 +292,7 @@ def assert_outputs_are_printed(expected_outputs: dict[str,
str], stderr: str):
"prod-image-build": "true",
"needs-helm-tests": "true",
"run-tests": "true",
- "run-amazon-tests": "true",
+ "run-amazon-tests": "false",
"docs-build": "true",
"full-tests-needed": "true",
"skip-pre-commits":
"identity,mypy-airflow,mypy-dev,mypy-docs,mypy-providers",
@@ -322,7 +319,7 @@ def assert_outputs_are_printed(expected_outputs: dict[str,
str], stderr: str):
"prod-image-build": "true",
"needs-helm-tests": "true",
"run-tests": "true",
- "run-amazon-tests": "true",
+ "run-amazon-tests": "false",
"docs-build": "true",
"full-tests-needed": "true",
"skip-pre-commits":
"identity,mypy-airflow,mypy-dev,mypy-docs,mypy-providers",
@@ -348,7 +345,7 @@ def assert_outputs_are_printed(expected_outputs: dict[str,
str], stderr: str):
"prod-image-build": "true",
"needs-helm-tests": "true",
"run-tests": "true",
- "run-amazon-tests": "true",
+ "run-amazon-tests": "false",
"docs-build": "true",
"full-tests-needed": "true",
"skip-pre-commits":
"identity,mypy-airflow,mypy-dev,mypy-docs,mypy-providers",
@@ -374,7 +371,7 @@ def assert_outputs_are_printed(expected_outputs: dict[str,
str], stderr: str):
"prod-image-build": "true",
"needs-helm-tests": "true",
"run-tests": "true",
- "run-amazon-tests": "true",
+ "run-amazon-tests": "false",
"docs-build": "true",
"full-tests-needed": "true",
"skip-pre-commits":
"identity,mypy-airflow,mypy-dev,mypy-docs,mypy-providers",
@@ -783,19 +780,20 @@ def test_full_test_needed_when_scripts_changes(files:
tuple[str, ...], expected_
"ci-image-build": "true",
"prod-image-build": "true",
"run-tests": "true",
- "skip-providers-tests": "true",
- "test-groups": "['core']",
+ "skip-providers-tests": "false",
+ "providers-test-types-list-as-string": "Providers[fab]",
+ "test-groups": "['core', 'providers']",
"docs-build": "true",
- "docs-list-as-string": "apache-airflow docker-stack",
+ "docs-list-as-string": "apache-airflow docker-stack fab",
"full-tests-needed": "true",
"skip-pre-commits":
"check-airflow-provider-compatibility,check-extra-packages-references,check-provider-yaml-valid,identity,kubeconform,lint-helm-chart,mypy-airflow,mypy-dev,mypy-docs,mypy-providers,validate-operators-init",
"upgrade-to-newer-dependencies": "false",
"core-test-types-list-as-string": "API Always CLI Core
Operators Other "
"Serialization WWW",
"needs-mypy": "true",
- "mypy-checks": "['mypy-airflow', 'mypy-docs', 'mypy-dev']",
+ "mypy-checks": "['mypy-airflow', 'mypy-providers',
'mypy-docs', 'mypy-dev']",
},
- id="Everything should run except Providers and lint pre-commit
"
+ id="Everything should run including Providers[fab] except lint
pre-commit "
"when full tests are needed for non-main branch",
)
),
@@ -853,16 +851,16 @@ def test_expected_output_full_tests_needed(
"ci-image-build": "true",
"prod-image-build": "true",
"run-tests": "true",
- "skip-providers-tests": "true",
- "test-groups": "['core']",
+ "skip-providers-tests": "false",
+ "test-groups": "['core', 'providers']",
"docs-build": "true",
- "docs-list-as-string": "apache-airflow docker-stack",
+ "docs-list-as-string": "apache-airflow docker-stack fab",
"full-tests-needed": "false",
"run-kubernetes-tests": "true",
"upgrade-to-newer-dependencies": "false",
"core-test-types-list-as-string": "Always",
- "needs-mypy": "false",
- "mypy-checks": "[]",
+ "needs-mypy": "true",
+ "mypy-checks": "['mypy-providers']",
},
id="No Helm tests, No providers no lint charts, should run if "
"only chart/providers changed in non-main but PROD image should be
built",
@@ -880,16 +878,16 @@ def test_expected_output_full_tests_needed(
"prod-image-build": "true",
"needs-helm-tests": "false",
"run-tests": "true",
- "skip-providers-tests": "true",
- "test-groups": "['core']",
+ "skip-providers-tests": "false",
+ "test-groups": "['core', 'providers']",
"docs-build": "true",
- "docs-list-as-string": "apache-airflow docker-stack",
+ "docs-list-as-string": "apache-airflow docker-stack fab",
"full-tests-needed": "false",
"run-kubernetes-tests": "true",
"upgrade-to-newer-dependencies": "false",
"core-test-types-list-as-string": "Always CLI",
"needs-mypy": "true",
- "mypy-checks": "['mypy-airflow']",
+ "mypy-checks": "['mypy-airflow', 'mypy-providers']",
},
id="Only CLI tests and Kubernetes tests should run if cli/chart
files changed in non-main branch",
),
@@ -905,16 +903,16 @@ def test_expected_output_full_tests_needed(
"prod-image-build": "false",
"needs-helm-tests": "false",
"run-tests": "true",
- "skip-providers-tests": "true",
- "test-groups": "['core']",
+ "skip-providers-tests": "false",
+ "test-groups": "['core', 'providers']",
"docs-build": "true",
- "docs-list-as-string": "apache-airflow docker-stack",
+ "docs-list-as-string": "apache-airflow docker-stack fab",
"full-tests-needed": "false",
"run-kubernetes-tests": "false",
"upgrade-to-newer-dependencies": "false",
"core-test-types-list-as-string": "API Always CLI Core
Operators Other Serialization WWW",
"needs-mypy": "true",
- "mypy-checks": "['mypy-airflow']",
+ "mypy-checks": "['mypy-airflow', 'mypy-providers']",
},
id="All tests except Providers and helm lint pre-commit "
"should run if core file changed in non-main branch",
@@ -973,11 +971,11 @@ def test_expected_output_pull_request_v2_7(
"run-tests": "true",
"docs-build": "true",
"skip-pre-commits":
"check-airflow-provider-compatibility,check-extra-packages-references,check-provider-yaml-valid,identity,kubeconform,lint-helm-chart,mypy-airflow,mypy-dev,mypy-docs,mypy-providers,validate-operators-init",
- "docs-list-as-string": "apache-airflow docker-stack",
+ "docs-list-as-string": "apache-airflow docker-stack fab",
"upgrade-to-newer-dependencies": "true",
"core-test-types-list-as-string": "API Always CLI Core
Operators Other Serialization WWW",
"needs-mypy": "true",
- "mypy-checks": "['mypy-airflow', 'mypy-docs', 'mypy-dev']",
+ "mypy-checks": "['mypy-airflow', 'mypy-providers',
'mypy-docs', 'mypy-dev']",
},
id="All tests except Providers and Helm run on push"
" even if unimportant file changed in non-main branch",
diff --git a/dev/refresh_images.sh b/dev/refresh_images.sh
index fa0ee5b6e85..80b33d188b5 100755
--- a/dev/refresh_images.sh
+++ b/dev/refresh_images.sh
@@ -40,8 +40,7 @@ rm -fv ./dist/* ./docker-context-files/*
breeze release-management prepare-provider-packages \
--package-list-file ./prod_image_installed_providers.txt \
- --package-format wheel \
- --version-suffix-for-pypi dev0
+ --package-format wheel
breeze release-management prepare-airflow-package --package-format wheel
--version-suffix-for-pypi dev0
diff --git
a/docs/apache-airflow-providers-fab/auth-manager/webserver-authentication.rst
b/docs/apache-airflow-providers-fab/auth-manager/webserver-authentication.rst
index 48c8c8f1b1f..d702d76e191 100644
---
a/docs/apache-airflow-providers-fab/auth-manager/webserver-authentication.rst
+++
b/docs/apache-airflow-providers-fab/auth-manager/webserver-authentication.rst
@@ -64,7 +64,7 @@ methods like OAuth, OpenID, LDAP, REMOTE_USER. It should be
noted that due to th
and Authlib, only a selection of OAuth2 providers is supported. This list
includes ``github``, ``githublocal``, ``twitter``,
``linkedin``, ``google``, ``azure``, ``openshift``, ``okta``, ``keycloak`` and
``keycloak_before_17``.
-The default authentication option described in the :ref:`Web Authentication
<web-authentication>` section is related
+The default authentication option described in the Web Authentication section
of Airflow 2 docs is related
with the following entry in the ``$AIRFLOW_HOME/webserver_config.py``.
.. code-block:: ini
diff --git a/docs/apache-airflow-providers-fab/changelog.rst
b/docs/apache-airflow-providers-fab/changelog.rst
index c6bdcaa11e7..390f94b034d 100644
--- a/docs/apache-airflow-providers-fab/changelog.rst
+++ b/docs/apache-airflow-providers-fab/changelog.rst
@@ -16,8 +16,7 @@
specific language governing permissions and limitations
under the License.
- .. NOTE! THIS FILE IS AUTOMATICALLY GENERATED AND WILL BE
- OVERWRITTEN WHEN PREPARING PACKAGES.
+ .. NOTE! THIS FILE IS AUTOMATICALLY GENERATED AND WILL BE OVERWRITTEN!
.. IF YOU WANT TO MODIFY THIS FILE, YOU SHOULD MODIFY THE TEMPLATE
`PROVIDER_CHANGELOG_TEMPLATE.rst.jinja2` IN the
`dev/breeze/src/airflow_breeze/templates` DIRECTORY
diff --git a/docs/apache-airflow-providers-fab/commits.rst
b/docs/apache-airflow-providers-fab/commits.rst
index 032c64b24da..87fe5c4f917 100644
--- a/docs/apache-airflow-providers-fab/commits.rst
+++ b/docs/apache-airflow-providers-fab/commits.rst
@@ -16,13 +16,12 @@
specific language governing permissions and limitations
under the License.
- .. NOTE! THIS FILE IS AUTOMATICALLY GENERATED AND WILL BE
- OVERWRITTEN WHEN PREPARING PACKAGES.
+ .. NOTE! THIS FILE IS AUTOMATICALLY GENERATED AND WILL BE OVERWRITTEN!
.. IF YOU WANT TO MODIFY THIS FILE, YOU SHOULD MODIFY THE TEMPLATE
`PROVIDER_COMMITS_TEMPLATE.rst.jinja2` IN the
`dev/breeze/src/airflow_breeze/templates` DIRECTORY
- .. THE REMAINDER OF THE FILE IS AUTOMATICALLY GENERATED. IT WILL BE
OVERWRITTEN AT RELEASE TIME!
+ .. THE REMAINDER OF THE FILE IS AUTOMATICALLY GENERATED. IT WILL BE
OVERWRITTEN!
Package apache-airflow-providers-fab
------------------------------------------------------
@@ -35,14 +34,77 @@ For high-level changelog, see :doc:`package information
including changelog <ind
+1.5.0
+.....
+
+Latest change: 2024-10-09
+
+=================================================================================================
===========
========================================================================================
+Commit
Committed Subject
+=================================================================================================
===========
========================================================================================
+`857ca4c06c
<https://github.com/apache/airflow/commit/857ca4c06c9008593674cabdd28d3c30e3e7f97b>`_
2024-10-09 ``Split providers out of the main "airflow/" tree into a UV
workspace project (#42505)``
+=================================================================================================
===========
========================================================================================
+
+1.4.1
+.....
+
+Latest change: 2024-10-09
+
+=================================================================================================
===========
================================================================================================================================
+Commit
Committed Subject
+=================================================================================================
===========
================================================================================================================================
+`2bb8628463
<https://github.com/apache/airflow/commit/2bb862846358d1c5a59b354adb39bc68d5aeae5e>`_
2024-10-09 ``Prepare docs for Oct 1st adhoc wave of providers (#42862)``
+`9536c98a43
<https://github.com/apache/airflow/commit/9536c98a439fc028542bb9b8eb9b76c24e2ee02b>`_
2024-10-01 ``Update Rest API tests to no longer rely on FAB auth manager.
Move tests specific to FAB permissions to FAB provider (#42523)``
+`ede7cb27fd
<https://github.com/apache/airflow/commit/ede7cb27fd39e233889d127490a2255df8c5d27d>`_
2024-09-30 ``Rename dataset related python variable names to asset
(#41348)``
+`2beb6a765d
<https://github.com/apache/airflow/commit/2beb6a765d9af94115a7c010cfbc6f802d28da24>`_
2024-09-25 ``Simplify expression for get_permitted_dag_ids query (#42484)``
+=================================================================================================
===========
================================================================================================================================
+
+1.4.0
+.....
+
+Latest change: 2024-09-21
+
+=================================================================================================
===========
===================================================================================
+Commit
Committed Subject
+=================================================================================================
===========
===================================================================================
+`7628d47d04
<https://github.com/apache/airflow/commit/7628d47d0481966d9a9b25dfd4870b7a6797ebbf>`_
2024-09-21 ``Prepare docs for Sep 1st wave of providers (#42387)``
+`6a527c9fac
<https://github.com/apache/airflow/commit/6a527c9facc649b3d64f36459cd655bcb03a9cb1>`_
2024-09-21 ``Fix pre-commit for auto update of fab migration versions
(#42382)``
+`8741e9c176
<https://github.com/apache/airflow/commit/8741e9c1761931c7cff135d53b589053a04f58c1>`_
2024-09-20 ``Handle 'AUTH_ROLE_PUBLIC' in FAB auth manager (#42280)``
+`9f167bbc34
<https://github.com/apache/airflow/commit/9f167bbc34ba4f0f64a6edab90d436275949fc56>`_
2024-09-19 ``Add FAB migration commands (#41804)``
+`db7f92787a
<https://github.com/apache/airflow/commit/db7f92787ab6f0e9646cc0e2a7ad5044f1d9ade8>`_
2024-09-17 ``Deprecated kerberos auth removed (#41693)``
+`d1e500c450
<https://github.com/apache/airflow/commit/d1e500c45069dc42254d55d8175e2c494cb41167>`_
2024-09-16 ``Deprecated configuration removed (#42129)``
+`a094f9105c
<https://github.com/apache/airflow/commit/a094f9105c649f1aed3524e3c1edf3441ea5eb87>`_
2024-09-12 ``Move 'is_active' user property to FAB auth manager (#42042)``
+`7b6eb92537
<https://github.com/apache/airflow/commit/7b6eb92537c688e446c0489fcdf1f67e86c10813>`_
2024-09-04 ``Move 'register_views' to auth manager interface (#41777)``
+`1379376b66
<https://github.com/apache/airflow/commit/1379376b66da034c2e0c0960bd6efe60e10dfbb9>`_
2024-09-02 ``Add TODOs in providers code for Subdag code removal (#41963)``
+`f16107017c
<https://github.com/apache/airflow/commit/f16107017c02b43e1c161b22106f3bb0529ff996>`_
2024-09-02 ``Revert "Provider fab auth manager deprecated methods removed
(#41720)" (#41960)``
+`b0391838c1
<https://github.com/apache/airflow/commit/b0391838c142bebdf178ba030c45db16b1f1f33b>`_
2024-08-26 ``Provider fab auth manager deprecated methods removed (#41720)``
+`59dc98178b
<https://github.com/apache/airflow/commit/59dc98178bcf36fec41ad104764393dadae3dacf>`_
2024-08-25 ``Separate FAB migration from Core Airflow migration (#41437)``
+`c78a004210
<https://github.com/apache/airflow/commit/c78a0042100ea7330c1fbc7ac234306e09d4678e>`_
2024-08-20 ``Add fixes by breeze/precommit-lint static checks (#41604)
(#41618)``
+`d6df0786cf
<https://github.com/apache/airflow/commit/d6df0786cfe3b7e7ded30c7fd786d685811cac52>`_
2024-08-20 ``Make kerberos an optional and devel dependency for impala and
fab (#41616)``
+=================================================================================================
===========
===================================================================================
+
+1.3.0
+.....
+
+Latest change: 2024-08-19
+
+=================================================================================================
===========
==========================================================================
+Commit
Committed Subject
+=================================================================================================
===========
==========================================================================
+`75fb7acbac
<https://github.com/apache/airflow/commit/75fb7acbaca09a040067f0a5a37637ff44eb9e14>`_
2024-08-19 ``Prepare docs for Aug 2nd wave of providers (#41559)``
+`6570c6d1bb
<https://github.com/apache/airflow/commit/6570c6d1bb620c6a952a16743c7168c775f6ad70>`_
2024-08-13 ``Remove deprecated SubDags (#41390)``
+`090607d92a
<https://github.com/apache/airflow/commit/090607d92a7995c75b9d25f5324d11a3dae683ce>`_
2024-08-08 ``Feature: Allow set Dag Run resource into Dag Level permission
(#40703)``
+=================================================================================================
===========
==========================================================================
+
1.2.2
.....
-Latest change: 2024-07-25
+Latest change: 2024-07-28
=================================================================================================
===========
=====================================================================================
Commit
Committed Subject
=================================================================================================
===========
=====================================================================================
+`7126678e87
<https://github.com/apache/airflow/commit/7126678e87c11665c06ec29595472cfaa0c7fdd6>`_
2024-07-28 ``Prepare Providers docs ad hoc release (#41074)``
`95cab23792
<https://github.com/apache/airflow/commit/95cab23792c80f0ecf980ac0a74b8d08431fb3bb>`_
2024-07-25 ``Bug fix: sync perm command not able to use custom security
manager (#41020)``
`6684481c67
<https://github.com/apache/airflow/commit/6684481c67f6a21a72e7f1512b450a433c5313b5>`_
2024-07-20 ``AIP-44 make database isolation mode work in Breeze (#40894)``
`d029e77f2f
<https://github.com/apache/airflow/commit/d029e77f2fd704bec4f4797b09d54c5c824a8536>`_
2024-07-15 ``Bump version checked by FAB provider on logout CSRF protection
to 2.10.0 (#40784)``
diff --git a/docs/apache-airflow-providers-fab/index.rst
b/docs/apache-airflow-providers-fab/index.rst
index 93da1fa9333..a0928e5ae38 100644
--- a/docs/apache-airflow-providers-fab/index.rst
+++ b/docs/apache-airflow-providers-fab/index.rst
@@ -76,7 +76,7 @@ apache-airflow-providers-fab package
`Flask App Builder <https://flask-appbuilder.readthedocs.io/>`__
-Release: 1.2.2
+Release: 1.5.4
Provider package
----------------
@@ -96,13 +96,43 @@ Requirements
The minimum Apache Airflow version supported by this provider package is
``2.9.0``.
-==================== ==================
-PIP package Version required
-==================== ==================
-``apache-airflow`` ``>=2.9.0``
-``flask`` ``>=2.2,<2.3``
-``flask-appbuilder`` ``==4.5.0``
-``flask-login`` ``>=0.6.2``
-``google-re2`` ``>=1.0``
-``jmespath`` ``>=0.7.0``
-==================== ==================
+========================================== ==================
+PIP package Version required
+========================================== ==================
+``apache-airflow`` ``>=2.9.0``
+``apache-airflow-providers-common-compat`` ``>=1.2.1``
+``flask-login`` ``>=0.6.3``
+``flask-session`` ``>=0.8.0``
+``flask`` ``>=2.2,<3``
+``flask-appbuilder`` ``==4.5.4``
+``google-re2`` ``>=1.0``
+``jmespath`` ``>=0.7.0``
+========================================== ==================
+
+Cross provider package dependencies
+-----------------------------------
+
+Those are dependencies that might be needed in order to use all the features
of the package.
+You need to install the specified provider packages in order to use them.
+
+You can install such cross-provider dependencies when installing from PyPI.
For example:
+
+.. code-block:: bash
+
+ pip install apache-airflow-providers-fab[common.compat]
+
+
+==================================================================================================================
=================
+Dependent package
Extra
+==================================================================================================================
=================
+`apache-airflow-providers-common-compat
<https://airflow.apache.org/docs/apache-airflow-providers-common-compat>`_
``common.compat``
+==================================================================================================================
=================
+
+Downloading official packages
+-----------------------------
+
+You can download officially released packages and verify their checksums and
signatures from the
+`Official Apache Download site
<https://downloads.apache.org/airflow/providers/>`_
+
+* `The apache-airflow-providers-fab 1.5.4 sdist package
<https://downloads.apache.org/airflow/providers/apache_airflow_providers_fab-1.5.4.tar.gz>`_
(`asc
<https://downloads.apache.org/airflow/providers/apache_airflow_providers_fab-1.5.4.tar.gz.asc>`__,
`sha512
<https://downloads.apache.org/airflow/providers/apache_airflow_providers_fab-1.5.4.tar.gz.sha512>`__)
+* `The apache-airflow-providers-fab 1.5.4 wheel package
<https://downloads.apache.org/airflow/providers/apache_airflow_providers_fab-1.5.4-py3-none-any.whl>`_
(`asc
<https://downloads.apache.org/airflow/providers/apache_airflow_providers_fab-1.5.4-py3-none-any.whl.asc>`__,
`sha512
<https://downloads.apache.org/airflow/providers/apache_airflow_providers_fab-1.5.4-py3-none-any.whl.sha512>`__)
diff --git a/generated/provider_dependencies.json
b/generated/provider_dependencies.json
index b02d1d211b5..8a2664d203b 100644
--- a/generated/provider_dependencies.json
+++ b/generated/provider_dependencies.json
@@ -550,7 +550,7 @@
"deps": [
"apache-airflow-providers-common-compat>=1.2.1",
"apache-airflow>=2.9.0",
- "flask-appbuilder==4.5.3",
+ "flask-appbuilder==4.5.4",
"flask-login>=0.6.3",
"flask-session>=0.8.0",
"flask>=2.2,<3",
diff --git a/hatch_build.py b/hatch_build.py
index 494ada00771..44238045fb3 100644
--- a/hatch_build.py
+++ b/hatch_build.py
@@ -161,6 +161,7 @@ DOC_EXTRAS: dict[str, list[str]] = {
"checksumdir>=1.2.0; python_version >= '3.9'",
"click>=8.1.8; python_version >= '3.9'",
"docutils>=0.21; python_version >= '3.9'",
+ "setuptools!=82.0.0", # until
https://github.com/sphinx-contrib/redoc/issues/53 is resolved
"sphinx-airflow-theme>=0.1.0; python_version >= '3.9'",
"sphinx-argparse>=0.4.0; python_version >= '3.9'",
"sphinx-autoapi>=3; python_version >= '3.9'",
@@ -436,7 +437,7 @@ DEPENDENCIES = [
"fsspec>=2023.10.0",
'google-re2>=1.0;python_version<"3.12"',
'google-re2>=1.1;python_version>="3.12"',
- "gunicorn>=20.1.0",
+ "gunicorn>=21.2.0",
"httpx>=0.25.0",
'importlib_metadata>=6.5;python_version<"3.12"',
# Importib_resources 6.2.0-6.3.1 break pytest_rewrite
diff --git a/scripts/ci/constraints/ci_branch_constraints.sh
b/scripts/ci/constraints/ci_branch_constraints.sh
index d4f73e62e59..a99759901b8 100755
--- a/scripts/ci/constraints/ci_branch_constraints.sh
+++ b/scripts/ci/constraints/ci_branch_constraints.sh
@@ -16,14 +16,7 @@
# specific language governing permissions and limitations
# under the License.
# shellcheck disable=SC2086
-if [[ ${GITHUB_REF} == 'refs/heads/main' ]]; then
- echo "branch=constraints-main"
-elif [[ ${GITHUB_REF} =~ refs/heads/v([0-9\-]*)\-(test|stable) ]]; then
- echo "branch=constraints-${BASH_REMATCH[1]}"
-else
- # Assume PR to constraints-main here
- echo >&2
- echo "[${COLOR_YELLOW}Assuming that the PR is to 'main'
branch!${COLOR_RESET}" >&2
- echo >&2
- echo "branch=constraints-main"
-fi
+echo >&2
+echo "[${COLOR_YELLOW}Hard-code constraints-2-11!${COLOR_RESET}" >&2
+echo >&2
+echo "branch=constraints-2-11"
diff --git a/scripts/ci/pre_commit/check_system_tests.py
b/scripts/ci/pre_commit/check_system_tests.py
index 3d5c743b54f..fdc9162143b 100755
--- a/scripts/ci/pre_commit/check_system_tests.py
+++ b/scripts/ci/pre_commit/check_system_tests.py
@@ -38,13 +38,13 @@ WATCHER_APPEND_INSTRUCTION = "list(dag.tasks) >> watcher()"
WATCHER_APPEND_INSTRUCTION_SHORT = " >> watcher()"
PYTEST_FUNCTION = """
-from tests_common.test_utils.system_tests import get_test_run # noqa: E402
+from tests.test_utils.system_tests import get_test_run # noqa: E402
# Needed to run the example DAG with pytest (see:
tests/system/README.md#run_via_pytest)
test_run = get_test_run(dag)
"""
PYTEST_FUNCTION_PATTERN = re.compile(
- r"from tests_common\.test_utils\.system_tests import get_test_run(?: #
noqa: E402)?\s+"
+ r"from tests\.test_utils\.system_tests import get_test_run(?: # noqa:
E402)?\s+"
r"(?:# .+\))?\s+"
r"test_run = get_test_run\(dag\)"
)
@@ -52,7 +52,7 @@ PYTEST_FUNCTION_PATTERN = re.compile(
def _check_file(file: Path):
content = file.read_text()
- if "from tests_common.test_utils.watcher import watcher" in content:
+ if "from tests.test_utils.watcher import watcher" in content:
index = content.find(WATCHER_APPEND_INSTRUCTION_SHORT)
if index == -1:
errors.append(
diff --git a/scripts/ci/pre_commit/mypy_folder.py
b/scripts/ci/pre_commit/mypy_folder.py
index a1f4aa337ba..db48d6f010a 100755
--- a/scripts/ci/pre_commit/mypy_folder.py
+++ b/scripts/ci/pre_commit/mypy_folder.py
@@ -33,7 +33,7 @@ initialize_breeze_precommit(__name__, __file__)
ALLOWED_FOLDERS = [
"airflow",
- "providers/src/airflow/providers",
+ "airflow/providers/fab",
"dev",
"docs",
]
@@ -48,13 +48,9 @@ if mypy_folder not in ALLOWED_FOLDERS:
sys.exit(1)
arguments = [mypy_folder]
-if mypy_folder == "providers/src/airflow/providers":
- arguments.extend(
- [
- "providers/tests",
- "--namespace-packages",
- ]
- )
+script = "/opt/airflow/scripts/in_container/run_mypy.sh"
+if mypy_folder == "airflow/providers/fab":
+ script = "/opt/airflow/scripts/in_container/run_mypy_providers.sh"
if mypy_folder == "airflow":
arguments.extend(
@@ -63,11 +59,11 @@ if mypy_folder == "airflow":
]
)
-print("Running /opt/airflow/scripts/in_container/run_mypy.sh with arguments:
", arguments)
+print(f"Running {script} with arguments: {arguments}")
res = run_command_via_breeze_shell(
[
- "/opt/airflow/scripts/in_container/run_mypy.sh",
+ script,
*arguments,
],
warn_image_upgrade_needed=True,
diff --git a/scripts/docker/entrypoint_ci.sh b/scripts/docker/entrypoint_ci.sh
index c1123303d8e..312cca60d84 100755
--- a/scripts/docker/entrypoint_ci.sh
+++ b/scripts/docker/entrypoint_ci.sh
@@ -390,7 +390,7 @@ function check_force_lowest_dependencies() {
EXTRA="[devel]"
if [[ ${TEST_TYPE=} =~ Providers\[.*\] ]]; then
# shellcheck disable=SC2001
- EXTRA=$(echo "[${TEST_TYPE},devel]" | sed 's/Providers\[\(.*\)\]/\1/')
+ EXTRA=$(echo "[${TEST_TYPE}]" | sed
's/Providers\[\([^]]*\)\]/\1,devel/')
echo
echo "${COLOR_BLUE}Forcing dependencies to lowest versions for
provider: ${EXTRA}${COLOR_RESET}"
echo
diff --git a/scripts/in_container/install_airflow_and_providers.py
b/scripts/in_container/install_airflow_and_providers.py
index 16dfb9279d0..9afaeea87f9 100755
--- a/scripts/in_container/install_airflow_and_providers.py
+++ b/scripts/in_container/install_airflow_and_providers.py
@@ -59,8 +59,9 @@ def find_airflow_package(extension: str) -> str | None:
def find_provider_packages(extension: str, selected_providers: list[str]) ->
list[str]:
candidates =
list(DIST_FOLDER.glob(f"apache_airflow_providers_*.{extension}"))
console.print("\n[bright_blue]Found the following provider packages: ")
+ console.print(f"Filtering by {selected_providers}")
for candidate in sorted(candidates):
- console.print(f" {candidate.as_posix()}")
+ console.print(f" {candidate.as_posix()} ->
{get_provider_name(candidate.name)}")
console.print()
if selected_providers:
candidates = [
@@ -272,7 +273,7 @@ def find_installation_spec(
)
provider_package_list = []
if use_packages_from_dist:
- selected_providers_list = install_selected_providers.split(",") if
install_selected_providers else []
+ selected_providers_list = install_selected_providers.split(" ") if
install_selected_providers else []
if selected_providers_list:
console.print(f"\n[bright_blue]Selected providers:
{selected_providers_list}\n")
else:
diff --git
a/tests/providers/fab/auth_manager/api/auth/backend/test_kerberos_auth.py
b/scripts/in_container/run_mypy_providers.sh
old mode 100644
new mode 100755
similarity index 69%
copy from
tests/providers/fab/auth_manager/api/auth/backend/test_kerberos_auth.py
copy to scripts/in_container/run_mypy_providers.sh
index e57f34ce4b0..462d9a4e96c
--- a/tests/providers/fab/auth_manager/api/auth/backend/test_kerberos_auth.py
+++ b/scripts/in_container/run_mypy_providers.sh
@@ -1,3 +1,4 @@
+#!/usr/bin/env bash
# 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
@@ -14,14 +15,12 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-from __future__ import annotations
+# Script to run mypy on all code. Can be started from any working directory
+# shellcheck source=scripts/in_container/_in_container_script_init.sh
+. "$( dirname "${BASH_SOURCE[0]}" )/_in_container_script_init.sh"
+export PYTHONPATH=${AIRFLOW_SOURCES}
-from tests_common.test_utils.compat import ignore_provider_compatibility_error
+export MYPY_FORCE_COLOR=true
+export TERM=ansi
-with ignore_provider_compatibility_error("2.9.0+", __file__):
- from airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth
import init_app
-
-
-class TestKerberosAuth:
- def test_init_app(self):
- init_app
+mypy --namespace-packages "${@}"
diff --git a/scripts/in_container/verify_providers.py
b/scripts/in_container/verify_providers.py
index a7a97d78ca4..3b94512b0c7 100755
--- a/scripts/in_container/verify_providers.py
+++ b/scripts/in_container/verify_providers.py
@@ -132,7 +132,7 @@ EXPECTED_SUFFIXES: dict[EntityType, str] = {
def get_all_providers() -> list[str]:
- return list(ALL_DEPENDENCIES.keys())
+ return ["fab"]
def import_all_classes(
diff --git a/tests/conftest.py b/tests/conftest.py
index 6c17c6f4036..09a055bdcc0 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -371,10 +371,17 @@ def initial_db_init():
from airflow.www.extensions.init_auth_manager import get_auth_manager
from tests.test_utils.compat import AIRFLOW_V_2_8_PLUS, AIRFLOW_V_2_10_PLUS
- if AIRFLOW_V_2_10_PLUS:
- db.resetdb(use_migration_files=True)
+ sql_alchemy_conn = conf.get("database", "sql_alchemy_conn")
+ if sql_alchemy_conn.startswith("sqlite"):
+ reset_cmd = [sys.executable, "-m", "airflow", "db", "reset", "--yes"]
+ if AIRFLOW_V_2_10_PLUS:
+ reset_cmd.append("--use-migration-files")
+ subprocess.check_call(reset_cmd)
else:
- db.resetdb()
+ if AIRFLOW_V_2_10_PLUS:
+ db.resetdb(use_migration_files=True)
+ else:
+ db.resetdb()
db.bootstrap_dagbag()
# minimal app to add roles
flask_app = Flask(__name__)
diff --git
a/tests/providers/fab/auth_manager/api/auth/backend/test_kerberos_auth.py
b/tests/providers/fab/auth_manager/api/auth/backend/test_kerberos_auth.py
index e57f34ce4b0..c763042e1c9 100644
--- a/tests/providers/fab/auth_manager/api/auth/backend/test_kerberos_auth.py
+++ b/tests/providers/fab/auth_manager/api/auth/backend/test_kerberos_auth.py
@@ -16,7 +16,7 @@
# under the License.
from __future__ import annotations
-from tests_common.test_utils.compat import ignore_provider_compatibility_error
+from tests.test_utils.compat import ignore_provider_compatibility_error
with ignore_provider_compatibility_error("2.9.0+", __file__):
from airflow.providers.fab.auth_manager.api.auth.backend.kerberos_auth
import init_app
diff --git a/tests/providers/fab/auth_manager/api/auth/backend/test_session.py
b/tests/providers/fab/auth_manager/api/auth/backend/test_session.py
index 405eafe11df..4e7eb3a3ae3 100644
--- a/tests/providers/fab/auth_manager/api/auth/backend/test_session.py
+++ b/tests/providers/fab/auth_manager/api/auth/backend/test_session.py
@@ -20,10 +20,10 @@ from unittest.mock import Mock, patch
import pytest
from flask import Response
-from tests_common.test_utils.compat import AIRFLOW_V_2_9_PLUS
from airflow.providers.fab.auth_manager.api.auth.backend.session import
requires_authentication
from airflow.www import app as application
+from tests.test_utils.compat import AIRFLOW_V_2_9_PLUS
pytestmark = [
pytest.mark.skipif(not AIRFLOW_V_2_9_PLUS, reason="Tests for Airflow
2.9.0+ only"),
diff --git
a/tests/providers/fab/auth_manager/api_endpoints/api_connexion_utils.py
b/tests/providers/fab/auth_manager/api_endpoints/api_connexion_utils.py
index b208b845096..61d923d5ff1 100644
--- a/tests/providers/fab/auth_manager/api_endpoints/api_connexion_utils.py
+++ b/tests/providers/fab/auth_manager/api_endpoints/api_connexion_utils.py
@@ -18,7 +18,7 @@ from __future__ import annotations
from contextlib import contextmanager
-from tests_common.test_utils.compat import ignore_provider_compatibility_error
+from tests.test_utils.compat import ignore_provider_compatibility_error
with ignore_provider_compatibility_error("2.9.0+", __file__):
from airflow.providers.fab.auth_manager.security_manager.override import
EXISTING_ROLES
diff --git
a/tests/providers/fab/auth_manager/api_endpoints/test_asset_endpoint.py
b/tests/providers/fab/auth_manager/api_endpoints/test_asset_endpoint.py
deleted file mode 100644
index 4cd76aa2b4a..00000000000
--- a/tests/providers/fab/auth_manager/api_endpoints/test_asset_endpoint.py
+++ /dev/null
@@ -1,327 +0,0 @@
-# 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
-
-from typing import Generator
-
-import pytest
-import time_machine
-from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import
create_user, delete_user
-from tests_common.test_utils.compat import AIRFLOW_V_3_0_PLUS
-from tests_common.test_utils.db import clear_db_assets, clear_db_runs
-from tests_common.test_utils.www import _check_last_log
-
-from airflow.api_connexion.exceptions import EXCEPTIONS_LINK_MAP
-from airflow.security import permissions
-from airflow.utils import timezone
-
-try:
- from airflow.models.asset import AssetDagRunQueue, AssetModel
-except ImportError:
- if AIRFLOW_V_3_0_PLUS:
- raise
- else:
- pass
-
-pytestmark = [
- pytest.mark.db_test,
- pytest.mark.skip_if_database_isolation_mode,
- pytest.mark.skipif(not AIRFLOW_V_3_0_PLUS, reason="Test requires Airflow
3.0+"),
-]
-
-
[email protected](scope="module")
-def configured_app(minimal_app_for_auth_api):
- app = minimal_app_for_auth_api
- create_user(
- app,
- username="test_queued_event",
- role_name="TestQueuedEvent",
- permissions=[
- (permissions.ACTION_CAN_READ, permissions.RESOURCE_DAG),
- (permissions.ACTION_CAN_READ, permissions.RESOURCE_ASSET),
- (permissions.ACTION_CAN_DELETE, permissions.RESOURCE_ASSET),
- ],
- )
-
- yield app
-
- delete_user(app, username="test_queued_event")
-
-
-class TestAssetEndpoint:
- default_time = "2020-06-11T18:00:00+00:00"
-
- @pytest.fixture(autouse=True)
- def setup_attrs(self, configured_app) -> None:
- self.app = configured_app
- self.client = self.app.test_client()
- clear_db_assets()
- clear_db_runs()
-
- def teardown_method(self) -> None:
- clear_db_assets()
- clear_db_runs()
-
- def _create_asset(self, session):
- asset_model = AssetModel(
- id=1,
- uri="s3://bucket/key",
- extra={"foo": "bar"},
- created_at=timezone.parse(self.default_time),
- updated_at=timezone.parse(self.default_time),
- )
- session.add(asset_model)
- session.commit()
- return asset_model
-
-
-class TestQueuedEventEndpoint(TestAssetEndpoint):
- @pytest.fixture
- def time_freezer(self) -> Generator:
- freezer = time_machine.travel(self.default_time, tick=False)
- freezer.start()
-
- yield
-
- freezer.stop()
-
- def _create_asset_dag_run_queues(self, dag_id, asset_id, session):
- ddrq = AssetDagRunQueue(target_dag_id=dag_id, asset_id=asset_id)
- session.add(ddrq)
- session.commit()
- return ddrq
-
-
-class TestGetDagAssetQueuedEvent(TestQueuedEventEndpoint):
- @pytest.mark.usefixtures("time_freezer")
- def test_should_respond_200(self, session, create_dummy_dag):
- dag, _ = create_dummy_dag()
- dag_id = dag.dag_id
- asset_id = self._create_asset(session).id
- self._create_asset_dag_run_queues(dag_id, asset_id, session)
- asset_uri = "s3://bucket/key"
-
- response = self.client.get(
- f"/api/v1/dags/{dag_id}/assets/queuedEvent/{asset_uri}",
- environ_overrides={"REMOTE_USER": "test_queued_event"},
- )
-
- assert response.status_code == 200
- assert response.json == {
- "created_at": self.default_time,
- "uri": "s3://bucket/key",
- "dag_id": "dag",
- }
-
- def test_should_respond_404(self):
- dag_id = "not_exists"
- asset_uri = "not_exists"
-
- response = self.client.get(
- f"/api/v1/dags/{dag_id}/assets/queuedEvent/{asset_uri}",
- environ_overrides={"REMOTE_USER": "test_queued_event"},
- )
-
- assert response.status_code == 404
- assert {
- "detail": "Queue event with dag_id: `not_exists` and asset uri:
`not_exists` was not found",
- "status": 404,
- "title": "Queue event not found",
- "type": EXCEPTIONS_LINK_MAP[404],
- } == response.json
-
-
-class TestDeleteDagAssetQueuedEvent(TestAssetEndpoint):
- def test_delete_should_respond_204(self, session, create_dummy_dag):
- dag, _ = create_dummy_dag()
- dag_id = dag.dag_id
- asset_uri = "s3://bucket/key"
- asset_id = self._create_asset(session).id
-
- ddrq = AssetDagRunQueue(target_dag_id=dag_id, asset_id=asset_id)
- session.add(ddrq)
- session.commit()
- conn = session.query(AssetDagRunQueue).all()
- assert len(conn) == 1
-
- response = self.client.delete(
- f"/api/v1/dags/{dag_id}/assets/queuedEvent/{asset_uri}",
- environ_overrides={"REMOTE_USER": "test_queued_event"},
- )
-
- assert response.status_code == 204
- conn = session.query(AssetDagRunQueue).all()
- assert len(conn) == 0
- _check_last_log(
- session, dag_id=dag_id, event="api.delete_dag_asset_queued_event",
execution_date=None
- )
-
- def test_should_respond_404(self):
- dag_id = "not_exists"
- asset_uri = "not_exists"
-
- response = self.client.delete(
- f"/api/v1/dags/{dag_id}/assets/queuedEvent/{asset_uri}",
- environ_overrides={"REMOTE_USER": "test_queued_event"},
- )
-
- assert response.status_code == 404
- assert {
- "detail": "Queue event with dag_id: `not_exists` and asset uri:
`not_exists` was not found",
- "status": 404,
- "title": "Queue event not found",
- "type": EXCEPTIONS_LINK_MAP[404],
- } == response.json
-
-
-class TestGetDagAssetQueuedEvents(TestQueuedEventEndpoint):
- @pytest.mark.usefixtures("time_freezer")
- def test_should_respond_200(self, session, create_dummy_dag):
- dag, _ = create_dummy_dag()
- dag_id = dag.dag_id
- asset_id = self._create_asset(session).id
- self._create_asset_dag_run_queues(dag_id, asset_id, session)
-
- response = self.client.get(
- f"/api/v1/dags/{dag_id}/assets/queuedEvent",
- environ_overrides={"REMOTE_USER": "test_queued_event"},
- )
-
- assert response.status_code == 200
- assert response.json == {
- "queued_events": [
- {
- "created_at": self.default_time,
- "uri": "s3://bucket/key",
- "dag_id": "dag",
- }
- ],
- "total_entries": 1,
- }
-
- def test_should_respond_404(self):
- dag_id = "not_exists"
-
- response = self.client.get(
- f"/api/v1/dags/{dag_id}/assets/queuedEvent",
- environ_overrides={"REMOTE_USER": "test_queued_event"},
- )
-
- assert response.status_code == 404
- assert {
- "detail": "Queue event with dag_id: `not_exists` was not found",
- "status": 404,
- "title": "Queue event not found",
- "type": EXCEPTIONS_LINK_MAP[404],
- } == response.json
-
-
-class TestDeleteDagDatasetQueuedEvents(TestAssetEndpoint):
- def test_should_respond_404(self):
- dag_id = "not_exists"
-
- response = self.client.delete(
- f"/api/v1/dags/{dag_id}/assets/queuedEvent",
- environ_overrides={"REMOTE_USER": "test_queued_event"},
- )
-
- assert response.status_code == 404
- assert {
- "detail": "Queue event with dag_id: `not_exists` was not found",
- "status": 404,
- "title": "Queue event not found",
- "type": EXCEPTIONS_LINK_MAP[404],
- } == response.json
-
-
-class TestGetDatasetQueuedEvents(TestQueuedEventEndpoint):
- @pytest.mark.usefixtures("time_freezer")
- def test_should_respond_200(self, session, create_dummy_dag):
- dag, _ = create_dummy_dag()
- dag_id = dag.dag_id
- asset_id = self._create_asset(session).id
- self._create_asset_dag_run_queues(dag_id, asset_id, session)
- asset_uri = "s3://bucket/key"
-
- response = self.client.get(
- f"/api/v1/assets/queuedEvent/{asset_uri}",
- environ_overrides={"REMOTE_USER": "test_queued_event"},
- )
-
- assert response.status_code == 200
- assert response.json == {
- "queued_events": [
- {
- "created_at": self.default_time,
- "uri": "s3://bucket/key",
- "dag_id": "dag",
- }
- ],
- "total_entries": 1,
- }
-
- def test_should_respond_404(self):
- asset_uri = "not_exists"
-
- response = self.client.get(
- f"/api/v1/assets/queuedEvent/{asset_uri}",
- environ_overrides={"REMOTE_USER": "test_queued_event"},
- )
-
- assert response.status_code == 404
- assert {
- "detail": "Queue event with asset uri: `not_exists` was not found",
- "status": 404,
- "title": "Queue event not found",
- "type": EXCEPTIONS_LINK_MAP[404],
- } == response.json
-
-
-class TestDeleteDatasetQueuedEvents(TestQueuedEventEndpoint):
- def test_delete_should_respond_204(self, session, create_dummy_dag):
- dag, _ = create_dummy_dag()
- dag_id = dag.dag_id
- asset_id = self._create_asset(session).id
- self._create_asset_dag_run_queues(dag_id, asset_id, session)
- asset_uri = "s3://bucket/key"
-
- response = self.client.delete(
- f"/api/v1/assets/queuedEvent/{asset_uri}",
- environ_overrides={"REMOTE_USER": "test_queued_event"},
- )
-
- assert response.status_code == 204
- conn = session.query(AssetDagRunQueue).all()
- assert len(conn) == 0
- _check_last_log(session, dag_id=None,
event="api.delete_asset_queued_events", execution_date=None)
-
- def test_should_respond_404(self):
- asset_uri = "not_exists"
-
- response = self.client.delete(
- f"/api/v1/assets/queuedEvent/{asset_uri}",
- environ_overrides={"REMOTE_USER": "test_queued_event"},
- )
-
- assert response.status_code == 404
- assert {
- "detail": "Queue event with asset uri: `not_exists` was not found",
- "status": 404,
- "title": "Queue event not found",
- "type": EXCEPTIONS_LINK_MAP[404],
- } == response.json
diff --git a/tests/providers/fab/auth_manager/api_endpoints/test_auth.py
b/tests/providers/fab/auth_manager/api_endpoints/test_auth.py
index 6a90b7ec4b3..fcf3f3cb6cc 100644
--- a/tests/providers/fab/auth_manager/api_endpoints/test_auth.py
+++ b/tests/providers/fab/auth_manager/api_endpoints/test_auth.py
@@ -20,11 +20,12 @@ from base64 import b64encode
import pytest
from flask_login import current_user
-from tests_common.test_utils.api_connexion_utils import assert_401
-from tests_common.test_utils.compat import AIRFLOW_V_3_0_PLUS
-from tests_common.test_utils.config import conf_vars
-from tests_common.test_utils.db import clear_db_pools
-from tests_common.test_utils.www import client_with_login
+
+from tests.test_utils.api_connexion_utils import assert_401
+from tests.test_utils.compat import AIRFLOW_V_3_0_PLUS
+from tests.test_utils.config import conf_vars
+from tests.test_utils.db import clear_db_pools
+from tests.test_utils.www import client_with_login
pytestmark = [
pytest.mark.db_test,
diff --git a/tests/providers/fab/auth_manager/api_endpoints/test_cors.py
b/tests/providers/fab/auth_manager/api_endpoints/test_cors.py
index 3741d71fb8b..b44eab8820e 100644
--- a/tests/providers/fab/auth_manager/api_endpoints/test_cors.py
+++ b/tests/providers/fab/auth_manager/api_endpoints/test_cors.py
@@ -19,9 +19,10 @@ from __future__ import annotations
from base64 import b64encode
import pytest
-from tests_common.test_utils.compat import AIRFLOW_V_3_0_PLUS
-from tests_common.test_utils.config import conf_vars
-from tests_common.test_utils.db import clear_db_pools
+
+from tests.test_utils.compat import AIRFLOW_V_3_0_PLUS
+from tests.test_utils.config import conf_vars
+from tests.test_utils.db import clear_db_pools
pytestmark = [
pytest.mark.db_test,
diff --git
a/tests/providers/fab/auth_manager/api_endpoints/test_dag_endpoint.py
b/tests/providers/fab/auth_manager/api_endpoints/test_dag_endpoint.py
index 198f213aa25..5b1049f54aa 100644
--- a/tests/providers/fab/auth_manager/api_endpoints/test_dag_endpoint.py
+++ b/tests/providers/fab/auth_manager/api_endpoints/test_dag_endpoint.py
@@ -21,10 +21,6 @@ from datetime import datetime
import pendulum
import pytest
-from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import
create_user, delete_user
-from tests_common.test_utils.compat import AIRFLOW_V_3_0_PLUS
-from tests_common.test_utils.db import clear_db_dags, clear_db_runs,
clear_db_serialized_dags
-from tests_common.test_utils.www import _check_last_log
from airflow.api_connexion.exceptions import EXCEPTIONS_LINK_MAP
from airflow.models import DagBag, DagModel
@@ -32,6 +28,10 @@ from airflow.models.dag import DAG
from airflow.operators.empty import EmptyOperator
from airflow.security import permissions
from airflow.utils.session import provide_session
+from tests.providers.fab.auth_manager.api_endpoints.api_connexion_utils import
create_user, delete_user
+from tests.test_utils.compat import AIRFLOW_V_3_0_PLUS
+from tests.test_utils.db import clear_db_dags, clear_db_runs,
clear_db_serialized_dags
+from tests.test_utils.www import _check_last_log
pytestmark = [
pytest.mark.db_test,
diff --git
a/tests/providers/fab/auth_manager/api_endpoints/test_dag_source_endpoint.py
b/tests/providers/fab/auth_manager/api_endpoints/test_dag_source_endpoint.py
index 9eab53aaa0c..f0d9b0da298 100644
--- a/tests/providers/fab/auth_manager/api_endpoints/test_dag_source_endpoint.py
+++ b/tests/providers/fab/auth_manager/api_endpoints/test_dag_source_endpoint.py
@@ -21,12 +21,12 @@ import os
from typing import TYPE_CHECKING
import pytest
-from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import
create_user, delete_user
-from tests_common.test_utils.compat import AIRFLOW_V_3_0_PLUS
-from tests_common.test_utils.db import clear_db_dag_code, clear_db_dags,
clear_db_serialized_dags
from airflow.models import DagBag
from airflow.security import permissions
+from tests.providers.fab.auth_manager.api_endpoints.api_connexion_utils import
create_user, delete_user
+from tests.test_utils.compat import AIRFLOW_V_3_0_PLUS
+from tests.test_utils.db import clear_db_dag_code, clear_db_dags,
clear_db_serialized_dags
pytestmark = [
pytest.mark.db_test,
diff --git
a/tests/providers/fab/auth_manager/api_endpoints/test_dag_warning_endpoint.py
b/tests/providers/fab/auth_manager/api_endpoints/test_dag_warning_endpoint.py
index b42d92d9cac..adfde1cc5b3 100644
---
a/tests/providers/fab/auth_manager/api_endpoints/test_dag_warning_endpoint.py
+++
b/tests/providers/fab/auth_manager/api_endpoints/test_dag_warning_endpoint.py
@@ -17,14 +17,14 @@
from __future__ import annotations
import pytest
-from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import
create_user, delete_user
-from tests_common.test_utils.compat import AIRFLOW_V_3_0_PLUS
-from tests_common.test_utils.db import clear_db_dag_warnings, clear_db_dags
from airflow.models.dag import DagModel
from airflow.models.dagwarning import DagWarning
from airflow.security import permissions
from airflow.utils.session import create_session
+from tests.providers.fab.auth_manager.api_endpoints.api_connexion_utils import
create_user, delete_user
+from tests.test_utils.compat import AIRFLOW_V_3_0_PLUS
+from tests.test_utils.db import clear_db_dag_warnings, clear_db_dags
pytestmark = [
pytest.mark.db_test,
diff --git
a/tests/providers/fab/auth_manager/api_endpoints/test_event_log_endpoint.py
b/tests/providers/fab/auth_manager/api_endpoints/test_event_log_endpoint.py
index 225a79bd9ac..acf3ca62684 100644
--- a/tests/providers/fab/auth_manager/api_endpoints/test_event_log_endpoint.py
+++ b/tests/providers/fab/auth_manager/api_endpoints/test_event_log_endpoint.py
@@ -17,13 +17,13 @@
from __future__ import annotations
import pytest
-from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import
create_user, delete_user
-from tests_common.test_utils.compat import AIRFLOW_V_3_0_PLUS
-from tests_common.test_utils.db import clear_db_logs
from airflow.models import Log
from airflow.security import permissions
from airflow.utils import timezone
+from tests.providers.fab.auth_manager.api_endpoints.api_connexion_utils import
create_user, delete_user
+from tests.test_utils.compat import AIRFLOW_V_3_0_PLUS
+from tests.test_utils.db import clear_db_logs
pytestmark = [
pytest.mark.db_test,
diff --git
a/tests/providers/fab/auth_manager/api_endpoints/test_import_error_endpoint.py
b/tests/providers/fab/auth_manager/api_endpoints/test_import_error_endpoint.py
index 5bac8356af5..a2fa1d028a3 100644
---
a/tests/providers/fab/auth_manager/api_endpoints/test_import_error_endpoint.py
+++
b/tests/providers/fab/auth_manager/api_endpoints/test_import_error_endpoint.py
@@ -17,14 +17,14 @@
from __future__ import annotations
import pytest
-from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import
create_user, delete_user
-from tests_common.test_utils.compat import AIRFLOW_V_3_0_PLUS, ParseImportError
-from tests_common.test_utils.db import clear_db_dags, clear_db_import_errors
-from tests_common.test_utils.permissions import _resource_name
from airflow.models.dag import DagModel
from airflow.security import permissions
from airflow.utils import timezone
+from tests.providers.fab.auth_manager.api_endpoints.api_connexion_utils import
create_user, delete_user
+from tests.test_utils.compat import AIRFLOW_V_3_0_PLUS, ParseImportError
+from tests.test_utils.db import clear_db_dags, clear_db_import_errors
+from tests.test_utils.permissions import _resource_name
pytestmark = [
pytest.mark.db_test,
diff --git
a/tests/providers/fab/auth_manager/api_endpoints/test_role_and_permission_endpoint.py
b/tests/providers/fab/auth_manager/api_endpoints/test_role_and_permission_endpoint.py
index da3e9a06565..be165e2498f 100644
---
a/tests/providers/fab/auth_manager/api_endpoints/test_role_and_permission_endpoint.py
+++
b/tests/providers/fab/auth_manager/api_endpoints/test_role_and_permission_endpoint.py
@@ -17,17 +17,17 @@
from __future__ import annotations
import pytest
-from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import
(
+
+from airflow.api_connexion.exceptions import EXCEPTIONS_LINK_MAP
+from airflow.security import permissions
+from tests.providers.fab.auth_manager.api_endpoints.api_connexion_utils import
(
create_role,
create_user,
delete_role,
delete_user,
)
-from tests_common.test_utils.api_connexion_utils import assert_401
-from tests_common.test_utils.compat import ignore_provider_compatibility_error
-
-from airflow.api_connexion.exceptions import EXCEPTIONS_LINK_MAP
-from airflow.security import permissions
+from tests.test_utils.api_connexion_utils import assert_401
+from tests.test_utils.compat import ignore_provider_compatibility_error
with ignore_provider_compatibility_error("2.9.0+", __file__):
from airflow.providers.fab.auth_manager.models import Role
diff --git
a/tests/providers/fab/auth_manager/api_endpoints/test_task_instance_endpoint.py
b/tests/providers/fab/auth_manager/api_endpoints/test_task_instance_endpoint.py
index aaae228998e..72667bd343c 100644
---
a/tests/providers/fab/auth_manager/api_endpoints/test_task_instance_endpoint.py
+++
b/tests/providers/fab/auth_manager/api_endpoints/test_task_instance_endpoint.py
@@ -20,13 +20,6 @@ import datetime as dt
import urllib
import pytest
-from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import
(
- create_user,
- delete_roles,
- delete_user,
-)
-from tests_common.test_utils.compat import AIRFLOW_V_3_0_PLUS
-from tests_common.test_utils.db import clear_db_runs, clear_rendered_ti_fields
from airflow.api_connexion.exceptions import EXCEPTIONS_LINK_MAP
from airflow.models import DagRun, TaskInstance
@@ -35,6 +28,13 @@ from airflow.utils.session import provide_session
from airflow.utils.state import State
from airflow.utils.timezone import datetime
from airflow.utils.types import DagRunType
+from tests.providers.fab.auth_manager.api_endpoints.api_connexion_utils import
(
+ create_user,
+ delete_roles,
+ delete_user,
+)
+from tests.test_utils.compat import AIRFLOW_V_3_0_PLUS
+from tests.test_utils.db import clear_db_runs, clear_rendered_ti_fields
pytestmark = [
pytest.mark.db_test,
diff --git
a/tests/providers/fab/auth_manager/api_endpoints/test_user_endpoint.py
b/tests/providers/fab/auth_manager/api_endpoints/test_user_endpoint.py
index eb1226d714e..df32c7d814f 100644
--- a/tests/providers/fab/auth_manager/api_endpoints/test_user_endpoint.py
+++ b/tests/providers/fab/auth_manager/api_endpoints/test_user_endpoint.py
@@ -19,20 +19,20 @@ from __future__ import annotations
import unittest.mock
import pytest
-from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import
(
- create_user,
- delete_role,
- delete_user,
-)
from sqlalchemy.sql.functions import count
-from tests_common.test_utils.api_connexion_utils import assert_401
-from tests_common.test_utils.compat import ignore_provider_compatibility_error
-from tests_common.test_utils.config import conf_vars
from airflow.api_connexion.exceptions import EXCEPTIONS_LINK_MAP
from airflow.security import permissions
from airflow.utils import timezone
from airflow.utils.session import create_session
+from tests.providers.fab.auth_manager.api_endpoints.api_connexion_utils import
(
+ create_user,
+ delete_role,
+ delete_user,
+)
+from tests.test_utils.api_connexion_utils import assert_401
+from tests.test_utils.compat import ignore_provider_compatibility_error
+from tests.test_utils.config import conf_vars
with ignore_provider_compatibility_error("2.9.0+", __file__):
from airflow.providers.fab.auth_manager.models import User
diff --git
a/tests/providers/fab/auth_manager/api_endpoints/test_variable_endpoint.py
b/tests/providers/fab/auth_manager/api_endpoints/test_variable_endpoint.py
index 920869d39e3..a8e71e1a824 100644
--- a/tests/providers/fab/auth_manager/api_endpoints/test_variable_endpoint.py
+++ b/tests/providers/fab/auth_manager/api_endpoints/test_variable_endpoint.py
@@ -17,12 +17,12 @@
from __future__ import annotations
import pytest
-from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import
create_user, delete_user
-from tests_common.test_utils.compat import AIRFLOW_V_3_0_PLUS
-from tests_common.test_utils.db import clear_db_variables
from airflow.models import Variable
from airflow.security import permissions
+from tests.providers.fab.auth_manager.api_endpoints.api_connexion_utils import
create_user, delete_user
+from tests.test_utils.compat import AIRFLOW_V_3_0_PLUS
+from tests.test_utils.db import clear_db_variables
pytestmark = [
pytest.mark.db_test,
diff --git
a/tests/providers/fab/auth_manager/api_endpoints/test_xcom_endpoint.py
b/tests/providers/fab/auth_manager/api_endpoints/test_xcom_endpoint.py
index f0ec606038e..2049463cd64 100644
--- a/tests/providers/fab/auth_manager/api_endpoints/test_xcom_endpoint.py
+++ b/tests/providers/fab/auth_manager/api_endpoints/test_xcom_endpoint.py
@@ -19,9 +19,6 @@ from __future__ import annotations
from datetime import timedelta
import pytest
-from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import
create_user, delete_user
-from tests_common.test_utils.compat import AIRFLOW_V_3_0_PLUS
-from tests_common.test_utils.db import clear_db_dags, clear_db_runs,
clear_db_xcom
from airflow.models.dag import DagModel
from airflow.models.dagrun import DagRun
@@ -32,6 +29,9 @@ from airflow.security import permissions
from airflow.utils import timezone
from airflow.utils.session import create_session
from airflow.utils.types import DagRunType
+from tests.providers.fab.auth_manager.api_endpoints.api_connexion_utils import
create_user, delete_user
+from tests.test_utils.compat import AIRFLOW_V_3_0_PLUS
+from tests.test_utils.db import clear_db_dags, clear_db_runs, clear_db_xcom
pytestmark = [
pytest.mark.db_test,
diff --git a/tests/providers/fab/auth_manager/cli_commands/test_db_command.py
b/tests/providers/fab/auth_manager/cli_commands/test_db_command.py
index 030b251a55a..6f0453c0b6b 100644
--- a/tests/providers/fab/auth_manager/cli_commands/test_db_command.py
+++ b/tests/providers/fab/auth_manager/cli_commands/test_db_command.py
@@ -21,6 +21,7 @@ from unittest import mock
import pytest
from airflow.cli import cli_parser
+from airflow.exceptions import AirflowOptionalProviderFeatureException
pytestmark = [pytest.mark.db_test]
try:
@@ -130,5 +131,5 @@ try:
def test_cli_migratedb_failure(self, mock_upgradedb, args, pattern):
with pytest.raises(SystemExit, match=pattern):
db_command.migratedb(self.parser.parse_args(["fab-db",
"migrate", *args]))
-except (ModuleNotFoundError, ImportError):
+except (ModuleNotFoundError, ImportError,
AirflowOptionalProviderFeatureException):
pass
diff --git a/tests/providers/fab/auth_manager/cli_commands/test_definition.py
b/tests/providers/fab/auth_manager/cli_commands/test_definition.py
index 572cbee05e3..2db5d352ecc 100644
--- a/tests/providers/fab/auth_manager/cli_commands/test_definition.py
+++ b/tests/providers/fab/auth_manager/cli_commands/test_definition.py
@@ -16,7 +16,7 @@
# under the License.
from __future__ import annotations
-from tests_common.test_utils.compat import ignore_provider_compatibility_error
+from tests.test_utils.compat import ignore_provider_compatibility_error
with ignore_provider_compatibility_error("2.9.0+", __file__):
from airflow.providers.fab.auth_manager.cli_commands.definition import (
diff --git a/tests/providers/fab/auth_manager/cli_commands/test_role_command.py
b/tests/providers/fab/auth_manager/cli_commands/test_role_command.py
index d07cfc61242..5f12c01860d 100644
--- a/tests/providers/fab/auth_manager/cli_commands/test_role_command.py
+++ b/tests/providers/fab/auth_manager/cli_commands/test_role_command.py
@@ -23,10 +23,10 @@ from io import StringIO
from typing import TYPE_CHECKING
import pytest
-from tests_common.test_utils.compat import ignore_provider_compatibility_error
-from tests_common.test_utils.config import conf_vars
from airflow.cli import cli_parser
+from tests.test_utils.compat import ignore_provider_compatibility_error
+from tests.test_utils.config import conf_vars
with ignore_provider_compatibility_error("2.9.0+", __file__):
from airflow.providers.fab.auth_manager.cli_commands import role_command
diff --git
a/tests/providers/fab/auth_manager/cli_commands/test_sync_perm_command.py
b/tests/providers/fab/auth_manager/cli_commands/test_sync_perm_command.py
index a0345909dc2..9e1817bd561 100644
--- a/tests/providers/fab/auth_manager/cli_commands/test_sync_perm_command.py
+++ b/tests/providers/fab/auth_manager/cli_commands/test_sync_perm_command.py
@@ -20,9 +20,9 @@ from __future__ import annotations
from unittest import mock
import pytest
-from tests_common.test_utils.compat import ignore_provider_compatibility_error
from airflow.cli import cli_parser
+from tests.test_utils.compat import ignore_provider_compatibility_error
with ignore_provider_compatibility_error("2.9.0+", __file__):
from airflow.providers.fab.auth_manager.cli_commands import
sync_perm_command
diff --git a/tests/providers/fab/auth_manager/cli_commands/test_user_command.py
b/tests/providers/fab/auth_manager/cli_commands/test_user_command.py
index 6ccd4c99716..b8ce2f48d6c 100644
--- a/tests/providers/fab/auth_manager/cli_commands/test_user_command.py
+++ b/tests/providers/fab/auth_manager/cli_commands/test_user_command.py
@@ -24,9 +24,9 @@ from contextlib import redirect_stdout
from io import StringIO
import pytest
-from tests_common.test_utils.compat import ignore_provider_compatibility_error
from airflow.cli import cli_parser
+from tests.test_utils.compat import ignore_provider_compatibility_error
with ignore_provider_compatibility_error("2.9.0+", __file__):
from airflow.providers.fab.auth_manager.cli_commands import user_command
diff --git a/tests/providers/fab/auth_manager/cli_commands/test_utils.py
b/tests/providers/fab/auth_manager/cli_commands/test_utils.py
index 394beb88290..e9abdd470b0 100644
--- a/tests/providers/fab/auth_manager/cli_commands/test_utils.py
+++ b/tests/providers/fab/auth_manager/cli_commands/test_utils.py
@@ -19,14 +19,14 @@ from __future__ import annotations
import os
import pytest
-from tests_common.test_utils.compat import ignore_provider_compatibility_error
-from tests_common.test_utils.config import conf_vars
import airflow
from airflow.configuration import conf
from airflow.exceptions import AirflowConfigException
from airflow.www.extensions.init_appbuilder import AirflowAppBuilder
from airflow.www.session import AirflowDatabaseSessionInterface
+from tests.test_utils.compat import ignore_provider_compatibility_error
+from tests.test_utils.config import conf_vars
with ignore_provider_compatibility_error("2.9.0+", __file__):
from airflow.providers.fab.auth_manager.cli_commands.utils import
get_application_builder
diff --git a/tests/providers/fab/auth_manager/conftest.py
b/tests/providers/fab/auth_manager/conftest.py
index d3e14c6a520..671a1c16b9b 100644
--- a/tests/providers/fab/auth_manager/conftest.py
+++ b/tests/providers/fab/auth_manager/conftest.py
@@ -29,6 +29,7 @@ def minimal_app_for_auth_api():
skip_all_except=[
"init_appbuilder",
"init_api_auth",
+ "init_api_experimental_auth",
"init_api_auth_provider",
"init_api_connexion",
"init_api_error_handlers",
@@ -42,7 +43,7 @@ def minimal_app_for_auth_api():
(
"api",
"auth_backends",
- ):
"providers.tests.fab.auth_manager.api_endpoints.remote_user_api_auth_backend,airflow.providers.fab.auth_manager.api.auth.backend.session",
+ ):
"tests.test_utils.remote_user_api_auth_backend,airflow.providers.fab.auth_manager.api.auth.backend.session",
(
"core",
"auth_manager",
diff --git a/tests/providers/fab/auth_manager/decorators/test_auth.py
b/tests/providers/fab/auth_manager/decorators/test_auth.py
index d6f60bf42f1..98f77a4f342 100644
--- a/tests/providers/fab/auth_manager/decorators/test_auth.py
+++ b/tests/providers/fab/auth_manager/decorators/test_auth.py
@@ -19,9 +19,9 @@ from __future__ import annotations
from unittest.mock import Mock, patch
import pytest
-from tests_common.test_utils.compat import ignore_provider_compatibility_error
from airflow.security.permissions import ACTION_CAN_READ, RESOURCE_DAG
+from tests.test_utils.compat import ignore_provider_compatibility_error
permissions = [(ACTION_CAN_READ, RESOURCE_DAG)]
diff --git a/tests/providers/fab/auth_manager/models/test_anonymous_user.py
b/tests/providers/fab/auth_manager/models/test_anonymous_user.py
index eaf6b357f92..4e365e3c8b7 100644
--- a/tests/providers/fab/auth_manager/models/test_anonymous_user.py
+++ b/tests/providers/fab/auth_manager/models/test_anonymous_user.py
@@ -17,7 +17,7 @@
# under the License.
from __future__ import annotations
-from tests_common.test_utils.compat import ignore_provider_compatibility_error
+from tests.test_utils.compat import ignore_provider_compatibility_error
with ignore_provider_compatibility_error("2.9.0+", __file__):
from airflow.providers.fab.auth_manager.models.anonymous_user import
AnonymousUser
diff --git a/tests/providers/fab/auth_manager/models/test_db.py
b/tests/providers/fab/auth_manager/models/test_db.py
index 3af94ceed7b..54c10849e96 100644
--- a/tests/providers/fab/auth_manager/models/test_db.py
+++ b/tests/providers/fab/auth_manager/models/test_db.py
@@ -25,6 +25,7 @@ from alembic.migration import MigrationContext
from sqlalchemy import MetaData
import airflow.providers
+from airflow.exceptions import AirflowOptionalProviderFeatureException
from airflow.settings import engine
from airflow.utils.db import (
compare_server_default,
@@ -128,5 +129,5 @@ try:
mock_initdb.assert_not_called()
else:
mock_initdb.assert_called_once()
-except ModuleNotFoundError:
+except (ModuleNotFoundError, AirflowOptionalProviderFeatureException):
pass
diff --git
a/tests/providers/fab/auth_manager/schemas/test_role_and_permission_schema.py
b/tests/providers/fab/auth_manager/schemas/test_role_and_permission_schema.py
index f8364a2e472..e9e10eb0408 100644
---
a/tests/providers/fab/auth_manager/schemas/test_role_and_permission_schema.py
+++
b/tests/providers/fab/auth_manager/schemas/test_role_and_permission_schema.py
@@ -17,7 +17,6 @@
from __future__ import annotations
import pytest
-from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import
create_role, delete_role
from airflow.providers.fab.auth_manager.schemas.role_and_permission_schema
import (
RoleCollection,
@@ -25,6 +24,7 @@ from
airflow.providers.fab.auth_manager.schemas.role_and_permission_schema impor
role_schema,
)
from airflow.security import permissions
+from tests.providers.fab.auth_manager.api_endpoints.api_connexion_utils import
create_role, delete_role
pytestmark = [pytest.mark.db_test, pytest.mark.skip_if_database_isolation_mode]
diff --git a/tests/providers/fab/auth_manager/schemas/test_user_schema.py
b/tests/providers/fab/auth_manager/schemas/test_user_schema.py
index f6b07327c09..26645e5cdbc 100644
--- a/tests/providers/fab/auth_manager/schemas/test_user_schema.py
+++ b/tests/providers/fab/auth_manager/schemas/test_user_schema.py
@@ -17,10 +17,10 @@
from __future__ import annotations
import pytest
-from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import
create_role, delete_role
-from tests_common.test_utils.compat import ignore_provider_compatibility_error
from airflow.utils import timezone
+from tests.providers.fab.auth_manager.api_endpoints.api_connexion_utils import
create_role, delete_role
+from tests.test_utils.compat import ignore_provider_compatibility_error
with ignore_provider_compatibility_error("2.9.0+", __file__):
from airflow.providers.fab.auth_manager.models import User
diff --git
a/tests/providers/fab/auth_manager/security_manager/test_constants.py
b/tests/providers/fab/auth_manager/security_manager/test_constants.py
index dbe592c59d7..5a718eee4b6 100644
--- a/tests/providers/fab/auth_manager/security_manager/test_constants.py
+++ b/tests/providers/fab/auth_manager/security_manager/test_constants.py
@@ -16,7 +16,7 @@
# under the License.
from __future__ import annotations
-from tests_common.test_utils.compat import ignore_provider_compatibility_error
+from tests.test_utils.compat import ignore_provider_compatibility_error
with ignore_provider_compatibility_error("2.9.0+", __file__):
from airflow.providers.fab.auth_manager.security_manager.constants import
EXISTING_ROLES
diff --git a/tests/providers/fab/auth_manager/security_manager/test_override.py
b/tests/providers/fab/auth_manager/security_manager/test_override.py
index 6ba1ccda292..6d85c0319dc 100644
--- a/tests/providers/fab/auth_manager/security_manager/test_override.py
+++ b/tests/providers/fab/auth_manager/security_manager/test_override.py
@@ -19,7 +19,7 @@ from __future__ import annotations
from unittest import mock
from unittest.mock import Mock
-from tests_common.test_utils.compat import ignore_provider_compatibility_error
+from tests.test_utils.compat import ignore_provider_compatibility_error
with ignore_provider_compatibility_error("2.9.0+", __file__):
from airflow.providers.fab.auth_manager.security_manager.override import
FabAirflowSecurityManagerOverride
diff --git a/tests/providers/fab/auth_manager/test_fab_auth_manager.py
b/tests/providers/fab/auth_manager/test_fab_auth_manager.py
index d298f7667ea..1ccd18c4479 100644
--- a/tests/providers/fab/auth_manager/test_fab_auth_manager.py
+++ b/tests/providers/fab/auth_manager/test_fab_auth_manager.py
@@ -32,7 +32,7 @@ try:
except ImportError:
pass
-from tests_common.test_utils.compat import ignore_provider_compatibility_error
+from tests.test_utils.compat import ignore_provider_compatibility_error
with ignore_provider_compatibility_error("2.9.0+", __file__):
from airflow.providers.fab.auth_manager.fab_auth_manager import
FabAuthManager
diff --git a/tests/providers/fab/auth_manager/test_models.py
b/tests/providers/fab/auth_manager/test_models.py
index 76b69c3dea7..30677d70957 100644
--- a/tests/providers/fab/auth_manager/test_models.py
+++ b/tests/providers/fab/auth_manager/test_models.py
@@ -19,7 +19,8 @@ from __future__ import annotations
from unittest import mock
from sqlalchemy import Column, MetaData, String, Table
-from tests_common.test_utils.compat import ignore_provider_compatibility_error
+
+from tests.test_utils.compat import ignore_provider_compatibility_error
with ignore_provider_compatibility_error("2.9.0+", __file__):
from airflow.providers.fab.auth_manager.models import (
diff --git a/tests/providers/fab/auth_manager/test_security.py
b/tests/providers/fab/auth_manager/test_security.py
index eb99daf9b9b..b0113e3ac61 100644
--- a/tests/providers/fab/auth_manager/test_security.py
+++ b/tests/providers/fab/auth_manager/test_security.py
@@ -30,36 +30,35 @@ import time_machine
from flask_appbuilder import SQLA, Model, expose, has_access
from flask_appbuilder.views import BaseView, ModelView
from sqlalchemy import Column, Date, Float, Integer, String
-from tests_common.test_utils.compat import ignore_provider_compatibility_error
from airflow.configuration import initialize_config
from airflow.exceptions import AirflowException
from airflow.models import DagModel
from airflow.models.dag import DAG
+from tests.test_utils.compat import ignore_provider_compatibility_error
with ignore_provider_compatibility_error("2.9.0+", __file__):
from airflow.providers.fab.auth_manager.fab_auth_manager import
FabAuthManager
from airflow.providers.fab.auth_manager.models import assoc_permission_role
from airflow.providers.fab.auth_manager.models.anonymous_user import
AnonymousUser
-from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import
(
- create_user,
- create_user_scope,
- delete_role,
- delete_user,
- set_user_single_role,
-)
-from tests_common.test_utils.asserts import assert_queries_count
-from tests_common.test_utils.db import clear_db_dags, clear_db_runs
-from tests_common.test_utils.mock_security_manager import MockSecurityManager
-from tests_common.test_utils.permissions import _resource_name
-
from airflow.security import permissions
from airflow.security.permissions import ACTION_CAN_READ
from airflow.www import app as application
from airflow.www.auth import get_access_denied_message
from airflow.www.extensions.init_auth_manager import get_auth_manager
from airflow.www.utils import CustomSQLAInterface
+from tests.providers.fab.auth_manager.api_endpoints.api_connexion_utils import
(
+ create_user,
+ create_user_scope,
+ delete_role,
+ delete_user,
+ set_user_single_role,
+)
+from tests.test_utils.asserts import assert_queries_count
+from tests.test_utils.db import clear_db_dags, clear_db_runs
+from tests.test_utils.mock_security_manager import MockSecurityManager
+from tests.test_utils.permissions import _resource_name
pytestmark = pytest.mark.db_test
diff --git a/tests/providers/fab/auth_manager/views/test_permissions.py
b/tests/providers/fab/auth_manager/views/test_permissions.py
index d80ad66e6e3..4b81e814986 100644
--- a/tests/providers/fab/auth_manager/views/test_permissions.py
+++ b/tests/providers/fab/auth_manager/views/test_permissions.py
@@ -18,13 +18,13 @@
from __future__ import annotations
import pytest
-from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import
create_user, delete_user
-from providers.tests.fab.auth_manager.views import
_assert_dataset_deprecation_warning
-from tests_common.test_utils.compat import AIRFLOW_V_2_9_PLUS
-from tests_common.test_utils.www import client_with_login
from airflow.security import permissions
from airflow.www import app as application
+from tests.providers.fab.auth_manager.api_endpoints.api_connexion_utils import
create_user, delete_user
+from tests.providers.fab.auth_manager.views import
_assert_dataset_deprecation_warning
+from tests.test_utils.compat import AIRFLOW_V_2_9_PLUS
+from tests.test_utils.www import client_with_login
pytestmark = [
pytest.mark.skipif(not AIRFLOW_V_2_9_PLUS, reason="Tests for Airflow
2.9.0+ only"),
diff --git a/tests/providers/fab/auth_manager/views/test_roles_list.py
b/tests/providers/fab/auth_manager/views/test_roles_list.py
index 79b11b55fa5..94e6677fdec 100644
--- a/tests/providers/fab/auth_manager/views/test_roles_list.py
+++ b/tests/providers/fab/auth_manager/views/test_roles_list.py
@@ -18,13 +18,13 @@
from __future__ import annotations
import pytest
-from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import
create_user, delete_user
-from providers.tests.fab.auth_manager.views import
_assert_dataset_deprecation_warning
-from tests_common.test_utils.compat import AIRFLOW_V_2_9_PLUS
-from tests_common.test_utils.www import client_with_login
from airflow.security import permissions
from airflow.www import app as application
+from tests.providers.fab.auth_manager.api_endpoints.api_connexion_utils import
create_user, delete_user
+from tests.providers.fab.auth_manager.views import
_assert_dataset_deprecation_warning
+from tests.test_utils.compat import AIRFLOW_V_2_9_PLUS
+from tests.test_utils.www import client_with_login
pytestmark = [
pytest.mark.skipif(not AIRFLOW_V_2_9_PLUS, reason="Tests for Airflow
2.9.0+ only"),
diff --git a/tests/providers/fab/auth_manager/views/test_user.py
b/tests/providers/fab/auth_manager/views/test_user.py
index 1f33d14cac2..4ccd61b100c 100644
--- a/tests/providers/fab/auth_manager/views/test_user.py
+++ b/tests/providers/fab/auth_manager/views/test_user.py
@@ -18,13 +18,13 @@
from __future__ import annotations
import pytest
-from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import
create_user, delete_user
-from providers.tests.fab.auth_manager.views import
_assert_dataset_deprecation_warning
-from tests_common.test_utils.compat import AIRFLOW_V_2_9_PLUS
-from tests_common.test_utils.www import client_with_login
from airflow.security import permissions
from airflow.www import app as application
+from tests.providers.fab.auth_manager.api_endpoints.api_connexion_utils import
create_user, delete_user
+from tests.providers.fab.auth_manager.views import
_assert_dataset_deprecation_warning
+from tests.test_utils.compat import AIRFLOW_V_2_9_PLUS
+from tests.test_utils.www import client_with_login
pytestmark = [
pytest.mark.skipif(not AIRFLOW_V_2_9_PLUS, reason="Tests for Airflow
2.9.0+ only"),
diff --git a/tests/providers/fab/auth_manager/views/test_user_edit.py
b/tests/providers/fab/auth_manager/views/test_user_edit.py
index 7279ed0b2c6..37f313fde1f 100644
--- a/tests/providers/fab/auth_manager/views/test_user_edit.py
+++ b/tests/providers/fab/auth_manager/views/test_user_edit.py
@@ -18,13 +18,13 @@
from __future__ import annotations
import pytest
-from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import
create_user, delete_user
-from providers.tests.fab.auth_manager.views import
_assert_dataset_deprecation_warning
-from tests_common.test_utils.compat import AIRFLOW_V_2_9_PLUS
-from tests_common.test_utils.www import client_with_login
from airflow.security import permissions
from airflow.www import app as application
+from tests.providers.fab.auth_manager.api_endpoints.api_connexion_utils import
create_user, delete_user
+from tests.providers.fab.auth_manager.views import
_assert_dataset_deprecation_warning
+from tests.test_utils.compat import AIRFLOW_V_2_9_PLUS
+from tests.test_utils.www import client_with_login
pytestmark = [
pytest.mark.skipif(not AIRFLOW_V_2_9_PLUS, reason="Tests for Airflow
2.9.0+ only"),
diff --git a/tests/providers/fab/auth_manager/views/test_user_stats.py
b/tests/providers/fab/auth_manager/views/test_user_stats.py
index 382a8f10984..ee818589fa5 100644
--- a/tests/providers/fab/auth_manager/views/test_user_stats.py
+++ b/tests/providers/fab/auth_manager/views/test_user_stats.py
@@ -18,13 +18,13 @@
from __future__ import annotations
import pytest
-from providers.tests.fab.auth_manager.api_endpoints.api_connexion_utils import
create_user, delete_user
-from providers.tests.fab.auth_manager.views import
_assert_dataset_deprecation_warning
-from tests_common.test_utils.compat import AIRFLOW_V_2_9_PLUS
-from tests_common.test_utils.www import client_with_login
from airflow.security import permissions
from airflow.www import app as application
+from tests.providers.fab.auth_manager.api_endpoints.api_connexion_utils import
create_user, delete_user
+from tests.providers.fab.auth_manager.views import
_assert_dataset_deprecation_warning
+from tests.test_utils.compat import AIRFLOW_V_2_9_PLUS
+from tests.test_utils.www import client_with_login
pytestmark = [
pytest.mark.skipif(not AIRFLOW_V_2_9_PLUS, reason="Tests for Airflow
2.9.0+ only"),
diff --git a/tests/providers/openlineage/plugins/test_utils.py
b/tests/providers/openlineage/plugins/test_utils.py
index 962429e30eb..96b5b6e7d2a 100644
--- a/tests/providers/openlineage/plugins/test_utils.py
+++ b/tests/providers/openlineage/plugins/test_utils.py
@@ -26,7 +26,7 @@ from unittest.mock import MagicMock, patch
import pytest
from attrs import define
from openlineage.client.utils import RedactMixin
-from pkg_resources import parse_version
+from packaging.version import parse as parse_version
from airflow.models import DAG as AIRFLOW_DAG, DagModel
from airflow.operators.bash import BashOperator
diff --git a/tests/test_utils/compat.py b/tests/test_utils/compat.py
index b5e876a626a..fc3e492760f 100644
--- a/tests/test_utils/compat.py
+++ b/tests/test_utils/compat.py
@@ -46,6 +46,7 @@ AIRFLOW_V_2_7_PLUS = Version(AIRFLOW_VERSION.base_version) >=
Version("2.7.0")
AIRFLOW_V_2_8_PLUS = Version(AIRFLOW_VERSION.base_version) >= Version("2.8.0")
AIRFLOW_V_2_9_PLUS = Version(AIRFLOW_VERSION.base_version) >= Version("2.9.0")
AIRFLOW_V_2_10_PLUS = Version(AIRFLOW_VERSION.base_version) >=
Version("2.10.0")
+AIRFLOW_V_3_0_PLUS = Version(AIRFLOW_VERSION.base_version) >= Version("3.0.0")
try:
from airflow.models.baseoperatorlink import BaseOperatorLink
diff --git a/tests/test_utils/www.py b/tests/test_utils/www.py
index 0a19c312fba..6d105384efd 100644
--- a/tests/test_utils/www.py
+++ b/tests/test_utils/www.py
@@ -62,9 +62,9 @@ def check_content_not_in_response(text, resp, resp_code=200):
assert resp_code == resp.status_code
if isinstance(text, list):
for line in text:
- assert line not in resp_html
+ assert line not in resp_html, f"Found {line!r} but it shouldn't be
there"
else:
- assert text not in resp_html
+ assert text not in resp_html, f"Found {text!r} but it shouldn't be
there"
def _check_last_log(session, dag_id, event, execution_date,
expected_extra=None):
diff --git a/tests/www/views/conftest.py b/tests/www/views/conftest.py
index 821f541ef0c..6f5e70ce7dd 100644
--- a/tests/www/views/conftest.py
+++ b/tests/www/views/conftest.py
@@ -35,7 +35,7 @@ from tests.test_utils.www import client_with_login,
client_without_login, client
@pytest.fixture(autouse=True, scope="module")
def session():
- settings.configure_orm()
+ settings.reconfigure_orm()
return settings.Session
diff --git a/tests/www/views/test_session.py b/tests/www/views/test_session.py
index 8a69d4b3525..45c4d3152a4 100644
--- a/tests/www/views/test_session.py
+++ b/tests/www/views/test_session.py
@@ -66,12 +66,13 @@ def test_invalid_session_backend_option():
with conf_vars({("webserver", "session_backend"):
"invalid_value_for_session_backend"}):
return app.create_app(testing=True)
- expected_exc_regex = (
- "^Unrecognized session backend specified in
web_server_session_backend: "
- r"'invalid_value_for_session_backend'\. Please set this to .+\.$"
- )
- with pytest.raises(AirflowConfigException, match=expected_exc_regex):
- poorly_configured_app_factory()
+ with conf_vars({("fab", "auth_rate_limited"): "False"}):
+ expected_exc_regex = (
+ "^Unrecognized session backend specified in
web_server_session_backend: "
+ r"'invalid_value_for_session_backend'\. Please set this to .+\.$"
+ )
+ with pytest.raises(AirflowConfigException, match=expected_exc_regex):
+ poorly_configured_app_factory()
def test_session_id_rotates(app, user_client):
diff --git a/tests/www/views/test_views.py b/tests/www/views/test_views.py
index 01d1bb731e6..5452dc72342 100644
--- a/tests/www/views/test_views.py
+++ b/tests/www/views/test_views.py
@@ -304,7 +304,8 @@ def test_get_safe_url(mock_url_for, app, test_url,
expected_url):
def test_app():
from airflow.www import app
- return app.create_app(testing=True)
+ with conf_vars({("fab", "auth_rate_limited"): "True"}):
+ return app.create_app(testing=True)
def test_mark_task_instance_state(test_app):
diff --git a/tests/www/views/test_views_acl.py
b/tests/www/views/test_views_acl.py
index 9586d81aa65..cb407657732 100644
--- a/tests/www/views/test_views_acl.py
+++ b/tests/www/views/test_views_acl.py
@@ -26,7 +26,6 @@ import pytest
from airflow.models import DagModel
from airflow.security import permissions
from airflow.utils import timezone
-from airflow.utils.session import create_session
from airflow.utils.state import State
from airflow.utils.types import DagRunType
from airflow.www.views import FILTER_STATUS_COOKIE
@@ -315,14 +314,13 @@ def
test_dag_autocomplete_dag_display_name(client_all_dags):
@pytest.fixture
-def setup_paused_dag():
+def setup_paused_dag(app):
"""Pause a DAG so we can test filtering."""
+ session = app.appbuilder.get_session
dag_to_pause = "example_branch_operator"
- with create_session() as session:
- session.query(DagModel).filter(DagModel.dag_id ==
dag_to_pause).update({"is_paused": True})
+ session.query(DagModel).filter(DagModel.dag_id ==
dag_to_pause).update({"is_paused": True})
yield
- with create_session() as session:
- session.query(DagModel).filter(DagModel.dag_id ==
dag_to_pause).update({"is_paused": False})
+ session.query(DagModel).filter(DagModel.dag_id ==
dag_to_pause).update({"is_paused": False})
@pytest.mark.parametrize(
diff --git a/tests/www/views/test_views_base.py
b/tests/www/views/test_views_base.py
index a125ca2d728..1139982945a 100644
--- a/tests/www/views/test_views_base.py
+++ b/tests/www/views/test_views_base.py
@@ -407,6 +407,7 @@ def test_page_instance_name_xss_prevention(admin_client):
instance_name_with_markup_conf = {
("webserver", "instance_name"): "<b>Bold Site Title Test</b>",
("webserver", "instance_name_has_markup"): "True",
+ ("fab", "auth_rate_limited"): "True",
}
diff --git a/tests/www/views/test_views_custom_user_views.py
b/tests/www/views/test_views_custom_user_views.py
index 8182d3ebefa..c04124a9af1 100644
--- a/tests/www/views/test_views_custom_user_views.py
+++ b/tests/www/views/test_views_custom_user_views.py
@@ -166,7 +166,7 @@ class TestSecurity:
)
response = client.post(f"/users/delete/{user_to_delete.id}",
follow_redirects=True)
- check_content_in_response("Deleted Row", response)
+ check_content_in_response("User confirmation needed", response)
check_content_not_in_response(user_to_delete.username, response)
assert bool(self.security_manager.get_user_by_id(user_to_delete.id))
is False
diff --git a/tests/www/views/test_views_rendered.py
b/tests/www/views/test_views_rendered.py
index 79dd4a57fee..b55cf41802b 100644
--- a/tests/www/views/test_views_rendered.py
+++ b/tests/www/views/test_views_rendered.py
@@ -265,86 +265,110 @@ else:
@pytest.mark.enable_redact
@pytest.mark.parametrize(
- "env, expected",
+ "env_spec, expected, var_setup",
[
pytest.param(
{"plain_key": "plain_value"},
"{'plain_key': 'plain_value'}",
+ None,
id="env-plain-key-val",
),
pytest.param(
- {"plain_key": Variable.setdefault("plain_var", "banana")},
+ {"plain_key": ("var", "plain_var", "banana")},
"{'plain_key': 'banana'}",
+ {"plain_var": "banana"},
id="env-plain-key-plain-var",
),
pytest.param(
- {"plain_key": Variable.setdefault("secret_var", "monkey")},
+ {"plain_key": ("var", "secret_var", "monkey")},
"{'plain_key': '***'}",
+ {"secret_var": "monkey"},
id="env-plain-key-sensitive-var",
),
pytest.param(
{"plain_key": "{{ var.value.plain_var }}"},
"{'plain_key': '{{ var.value.plain_var }}'}",
+ {"plain_var": "banana"},
id="env-plain-key-plain-tpld-var",
),
pytest.param(
{"plain_key": "{{ var.value.secret_var }}"},
"{'plain_key': '{{ var.value.secret_var }}'}",
+ {"secret_var": "monkey"},
id="env-plain-key-sensitive-tpld-var",
),
pytest.param(
{"secret_key": "plain_value"},
"{'secret_key': '***'}",
+ None,
id="env-sensitive-key-plain-val",
),
pytest.param(
- {"secret_key": Variable.setdefault("plain_var", "monkey")},
+ {"secret_key": ("var", "plain_var", "monkey")},
"{'secret_key': '***'}",
+ {"plain_var": "monkey"},
id="env-sensitive-key-plain-var",
),
pytest.param(
- {"secret_key": Variable.setdefault("secret_var", "monkey")},
+ {"secret_key": ("var", "secret_var", "monkey")},
"{'secret_key': '***'}",
+ {"secret_var": "monkey"},
id="env-sensitive-key-sensitive-var",
),
pytest.param(
{"secret_key": "{{ var.value.plain_var }}"},
"{'secret_key': '***'}",
+ {"plain_var": "banana"},
id="env-sensitive-key-plain-tpld-var",
),
pytest.param(
{"secret_key": "{{ var.value.secret_var }}"},
"{'secret_key': '***'}",
+ {"secret_var": "monkey"},
id="env-sensitive-key-sensitive-tpld-var",
),
],
)
-def test_rendered_task_detail_env_secret(patch_app, admin_client, request,
env, expected):
- if request.node.callspec.id.endswith("-tpld-var"):
- Variable.set("plain_var", "banana")
- Variable.set("secret_var", "monkey")
-
- dag: DAG = patch_app.dag_bag.get_dag("testdag")
- task_secret: BashOperator = dag.get_task(task_id="task1")
- task_secret.env = env
- date = quote_plus(str(DEFAULT_DATE))
- url = f"task?task_id=task1&dag_id=testdag&execution_date={date}"
-
- with create_session() as session:
- dag.create_dagrun(
- state=DagRunState.RUNNING,
- execution_date=DEFAULT_DATE,
- data_interval=(DEFAULT_DATE, DEFAULT_DATE),
- run_type=DagRunType.SCHEDULED,
- session=session,
- )
-
- resp = admin_client.get(url, follow_redirects=True)
- check_content_in_response(str(escape(expected)), resp)
-
- if request.node.callspec.id.endswith("-tpld-var"):
- Variable.delete("plain_var")
- Variable.delete("secret_var")
+def test_rendered_task_detail_env_secret(patch_app, admin_client, env_spec,
expected, var_setup):
+ # Setup variables if needed
+ if var_setup:
+ for var_name, var_value in var_setup.items():
+ Variable.set(var_name, var_value)
+
+ # Build the actual env dict from spec
+ env = {}
+ for key, value in env_spec.items():
+ if isinstance(value, tuple) and value[0] == "var":
+ # This is a variable reference - call setdefault now
+ _, var_name, default_value = value
+ env[key] = Variable.setdefault(var_name, default_value)
+ else:
+ # Plain value or template string
+ env[key] = value
+
+ try:
+ dag: DAG = patch_app.dag_bag.get_dag("testdag")
+ task_secret: BashOperator = dag.get_task(task_id="task1")
+ task_secret.env = env
+ date = quote_plus(str(DEFAULT_DATE))
+ url = f"task?task_id=task1&dag_id=testdag&execution_date={date}"
+
+ with create_session() as session:
+ dag.create_dagrun(
+ state=DagRunState.RUNNING,
+ execution_date=DEFAULT_DATE,
+ data_interval=(DEFAULT_DATE, DEFAULT_DATE),
+ run_type=DagRunType.SCHEDULED,
+ session=session,
+ )
+
+ resp = admin_client.get(url, follow_redirects=True)
+ check_content_in_response(str(escape(expected)), resp)
+ finally:
+ # Cleanup variables
+ if var_setup:
+ for var_name in var_setup.keys():
+ Variable.delete(var_name)
@pytest.mark.usefixtures("patch_app")