This is an automated email from the ASF dual-hosted git repository.
shubhamraj pushed a commit to branch v3-1-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/v3-1-test by this push:
new e3af5c47e10 Add readable dags checks for the dependencies endpoint
(#62046) (#62586)
e3af5c47e10 is described below
commit e3af5c47e103a7bd8b6f49d056e38a6d02807484
Author: Shubham Raj <[email protected]>
AuthorDate: Sat Feb 28 10:53:04 2026 +0530
Add readable dags checks for the dependencies endpoint (#62046) (#62586)
(cherry picked from commit 9d7b2a1c19ef4adce9e9aadd1f7a036794f4483e)
---
.../api_fastapi/core_api/routes/ui/dependencies.py | 14 +++++++---
.../core_api/routes/ui/test_dependencies.py | 31 +++++++++++++++++++---
2 files changed, 39 insertions(+), 6 deletions(-)
diff --git
a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/dependencies.py
b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/dependencies.py
index 8a0d8bbacd1..ce2ff1ccdf6 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/dependencies.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/dependencies.py
@@ -25,7 +25,7 @@ from airflow.api_fastapi.common.db.common import SessionDep
from airflow.api_fastapi.common.router import AirflowRouter
from airflow.api_fastapi.core_api.datamodels.ui.common import BaseGraphResponse
from airflow.api_fastapi.core_api.openapi.exceptions import
create_openapi_http_exception_doc
-from airflow.api_fastapi.core_api.security import requires_access_dag
+from airflow.api_fastapi.core_api.security import ReadableDagsFilterDep,
requires_access_dag
from airflow.api_fastapi.core_api.services.ui.dependencies import
extract_single_connected_component
from airflow.models.serialized_dag import SerializedDagModel
@@ -41,12 +41,20 @@ dependencies_router = AirflowRouter(tags=["Dependencies"])
),
dependencies=[Depends(requires_access_dag("GET",
DagAccessEntity.DEPENDENCIES))],
)
-def get_dependencies(session: SessionDep, node_id: str | None = None) ->
BaseGraphResponse:
+def get_dependencies(
+ session: SessionDep,
+ readable_dags_filter: ReadableDagsFilterDep,
+ node_id: str | None = None,
+) -> BaseGraphResponse:
"""Dependencies graph."""
nodes_dict: dict[str, dict] = {}
edge_tuples: set[tuple[str, str]] = set()
- for dag, dependencies in
sorted(SerializedDagModel.get_dag_dependencies().items()):
+ dag_dependencies = SerializedDagModel.get_dag_dependencies()
+ readable_dag_ids = readable_dags_filter.value
+ for dag, dependencies in sorted(dag_dependencies.items()):
+ if readable_dag_ids is not None and dag not in readable_dag_ids:
+ continue
dag_node_id = f"dag:{dag}"
if dag_node_id not in nodes_dict:
for dep in dependencies:
diff --git
a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_dependencies.py
b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_dependencies.py
index c18ffe92d4a..fbc427f1382 100644
---
a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_dependencies.py
+++
b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_dependencies.py
@@ -16,6 +16,8 @@
# under the License.
from __future__ import annotations
+from unittest import mock
+
import pendulum
import pytest
from sqlalchemy import select
@@ -204,7 +206,7 @@ def expected_secondary_component_response(asset2_id):
class TestGetDependencies:
@pytest.mark.usefixtures("make_primary_connected_component")
def test_should_response_200(self, test_client,
expected_primary_component_response):
- with assert_queries_count(5):
+ with assert_queries_count(6):
response = test_client.get("/dependencies")
assert response.status_code == 200
@@ -240,7 +242,7 @@ class TestGetDependencies:
@pytest.mark.usefixtures("make_primary_connected_component",
"make_secondary_connected_component")
def test_with_node_id_filter(self, test_client, node_id,
expected_response_fixture, request):
expected_response = request.getfixturevalue(expected_response_fixture)
- with assert_queries_count(5):
+ with assert_queries_count(6):
response = test_client.get("/dependencies", params={"node_id":
node_id})
assert response.status_code == 200
@@ -258,7 +260,7 @@ class TestGetDependencies:
(asset1_id, expected_primary_component_response),
(asset2_id, expected_secondary_component_response),
):
- with assert_queries_count(5):
+ with assert_queries_count(6):
response = test_client.get("/dependencies", params={"node_id":
f"asset:{asset_id}"})
assert response.status_code == 200
@@ -272,3 +274,26 @@ class TestGetDependencies:
assert response.json() == {
"detail": "Unique connected component not found, got [] for
connected components of node missing_node_id, expected only 1 connected
component.",
}
+
+ @mock.patch(
+
"airflow.api_fastapi.auth.managers.base_auth_manager.BaseAuthManager.get_authorized_dag_ids",
+ return_value={"upstream", "downstream"},
+ )
+ @pytest.mark.usefixtures("make_primary_connected_component",
"make_secondary_connected_component")
+ def test_scheduling_dependencies_respects_readable_dags_filter(self, _,
test_client):
+ response = test_client.get("/dependencies")
+ assert response.status_code == 200
+
+ result = response.json()
+ dag_node_ids = {node["id"] for node in result["nodes"] if node["type"]
== "dag"}
+ expected_present = ["dag:upstream", "dag:downstream"]
+ expected_absent = [
+ "dag:other_dag",
+ "dag:external_trigger_dag_id",
+ "dag:upstream_secondary",
+ "dag:downstream_secondary",
+ ]
+ for node_id in expected_present:
+ assert node_id in dag_node_ids
+ for node_id in expected_absent:
+ assert node_id not in dag_node_ids