This is an automated email from the ASF dual-hosted git repository.

vatsrahul1001 pushed a commit to branch v3-3-test
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/v3-3-test by this push:
     new 3040370c4e7 [v3-3-test] Fix hierarchical_alphabetical sort order 
breaking the graph and grid (#72137) (#72618)
3040370c4e7 is described below

commit 3040370c4e714dc7622859a55138c7077b8ba5e1
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Tue Sep 8 11:45:25 2026 +0530

    [v3-3-test] Fix hierarchical_alphabetical sort order breaking the graph and 
grid (#72137) (#72618)
    
    The serialized task group the UI renders only re-implemented the 
topological sort order, so selecting [api] grid_view_sorting_order = 
hierarchical_alphabetical called a method that does not exist on it and raised 
AttributeError, failing the whole graph/grid render instead of ordering groups 
and tasks alphabetically.
    (cherry picked from commit adaaf3a01ecaf958b60ec696b5486e0013a9bb85)
    
    Co-authored-by: Pierre Jeambrun <[email protected]>
---
 .../airflow/serialization/definitions/taskgroup.py | 14 ++++++++++
 airflow-core/tests/unit/utils/test_task_group.py   | 32 +++++++++++++++++++++-
 2 files changed, 45 insertions(+), 1 deletion(-)

diff --git a/airflow-core/src/airflow/serialization/definitions/taskgroup.py 
b/airflow-core/src/airflow/serialization/definitions/taskgroup.py
index 0e0ae06572d..b82e1c0b933 100644
--- a/airflow-core/src/airflow/serialization/definitions/taskgroup.py
+++ b/airflow-core/src/airflow/serialization/definitions/taskgroup.py
@@ -216,6 +216,20 @@ class SerializedTaskGroup(TaskGroupMixin, DAGNode):
                 yield group
             group = group.parent_group
 
+    def hierarchical_alphabetical_sort(self) -> list[DAGNode]:
+        """
+        Sort children in hierarchical alphabetical order: groups first, then 
tasks, each alphabetical.
+
+        Mirrors ``TaskGroup.hierarchical_alphabetical_sort`` in task-sdk. This 
orders one group's
+        direct children; the server-side graph/grid builder in
+        ``api_fastapi.core_api.services.ui.task_group`` walks the tree and 
re-applies it at every
+        level, so the API response is fully ordered at all nesting levels and 
the UI renders it as-is.
+        """
+        return sorted(
+            self.children.values(),
+            key=lambda node: (not isinstance(node, SerializedTaskGroup), 
node.node_id),
+        )
+
     def topological_sort(
         self, *, group_dict: dict[str | None, SerializedTaskGroup] | None = 
None
     ) -> list[DAGNode]:
diff --git a/airflow-core/tests/unit/utils/test_task_group.py 
b/airflow-core/tests/unit/utils/test_task_group.py
index 452c302348a..35172da2368 100644
--- a/airflow-core/tests/unit/utils/test_task_group.py
+++ b/airflow-core/tests/unit/utils/test_task_group.py
@@ -20,7 +20,11 @@ from __future__ import annotations
 import pendulum
 import pytest
 
-from airflow.api_fastapi.core_api.services.ui.task_group import 
task_group_to_dict, task_group_to_dict_grid
+from airflow.api_fastapi.core_api.services.ui.task_group import (
+    get_task_group_children_getter,
+    task_group_to_dict,
+    task_group_to_dict_grid,
+)
 from airflow.providers.standard.operators.bash import BashOperator
 from airflow.providers.standard.operators.empty import EmptyOperator
 from airflow.providers.standard.operators.python import PythonOperator
@@ -36,6 +40,7 @@ from airflow.sdk import (
 from airflow.serialization.definitions.taskgroup import SerializedTaskGroup
 from airflow.utils.dag_edges import dag_edges
 
+from tests_common.test_utils.config import conf_vars
 from tests_common.test_utils.dag import create_scheduler_dag
 from unit.models import DEFAULT_DATE
 
@@ -244,6 +249,31 @@ def test_task_group_to_dict_alternative_syntax():
     assert task_group_to_dict(serialized_dag.task_group) == EXPECTED_JSON
 
 
+@conf_vars({("api", "grid_view_sorting_order"): "hierarchical_alphabetical"})
+def test_task_group_to_dict_hierarchical_alphabetical_sort():
+    """``hierarchical_alphabetical`` orders serialized children: groups first, 
then tasks, each alphabetical."""
+    # The getter caches the resolved sort order, so force a re-read under the 
patched config.
+    get_task_group_children_getter.cache_clear()
+    try:
+        logical_date = pendulum.parse("20200101")
+        dag = DAG("test_tg_hier_alpha", schedule=None, start_date=logical_date)
+        # Added in a non-alphabetical order, mixing tasks and groups.
+        EmptyOperator(task_id="zeta", dag=dag)
+        group_b = TaskGroup("group_b", dag=dag)
+        EmptyOperator(task_id="b1", dag=dag, task_group=group_b)
+        group_a = TaskGroup("group_a", dag=dag)
+        EmptyOperator(task_id="a1", dag=dag, task_group=group_a)
+        EmptyOperator(task_id="alpha", dag=dag)
+
+        serialized_dag = create_scheduler_dag(dag)
+        node = task_group_to_dict(serialized_dag.task_group)
+
+        assert [child["id"] for child in node["children"]] == ["group_a", 
"group_b", "alpha", "zeta"]
+    finally:
+        # Don't leak the patched sort order to other tests.
+        get_task_group_children_getter.cache_clear()
+
+
 def test_task_group_to_dict_builds_group_dict_once(monkeypatch):
     """Rendering the whole tree threads one group_dict; it is not rebuilt per 
nested group."""
     with DAG("test_group_dict_once", schedule=None, start_date=DEFAULT_DATE) 
as dag:

Reply via email to