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

uranusjr pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 50e27b3d463 Expose timetable_partitioned in UI API (#62777)
50e27b3d463 is described below

commit 50e27b3d46336bd6ea5f69cb55324730864dd5d2
Author: Tzu-ping Chung <[email protected]>
AuthorDate: Wed Mar 4 08:36:12 2026 +0800

    Expose timetable_partitioned in UI API (#62777)
---
 .../api_fastapi/core_api/datamodels/dags.py        |  1 +
 .../api_fastapi/core_api/openapi/_private_ui.yaml  |  4 ++
 .../core_api/openapi/v2-rest-api-generated.yaml    |  8 +++
 .../src/airflow/cli/commands/dag_command.py        |  1 +
 .../airflow/ui/openapi-gen/requests/schemas.gen.ts | 18 +++++-
 .../airflow/ui/openapi-gen/requests/types.gen.ts   |  3 +
 .../airflow/ui/src/pages/DagsList/DagCard.test.tsx |  1 +
 .../core_api/routes/public/test_dags.py            | 67 +++++++++++-----------
 .../src/airflowctl/api/datamodels/generated.py     |  2 +
 .../tests/airflow_ctl/api/test_operations.py       |  2 +
 .../airflow_ctl/ctl/commands/test_dag_command.py   |  2 +
 11 files changed, 74 insertions(+), 35 deletions(-)

diff --git a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/dags.py 
b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/dags.py
index 3bf599d6a95..6c07f1ed8a8 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/dags.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/dags.py
@@ -74,6 +74,7 @@ class DAGResponse(BaseModel):
     description: str | None
     timetable_summary: str | None
     timetable_description: str | None
+    timetable_partitioned: bool
     tags: list[DagTagResponse]
     max_active_tasks: int
     max_active_runs: int | None
diff --git 
a/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml 
b/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml
index a0339372b8d..f60a7781130 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml
+++ b/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml
@@ -1847,6 +1847,9 @@ components:
           - type: string
           - type: 'null'
           title: Timetable Description
+        timetable_partitioned:
+          type: boolean
+          title: Timetable Partitioned
         tags:
           items:
             $ref: '#/components/schemas/DagTagResponse'
@@ -1945,6 +1948,7 @@ components:
       - description
       - timetable_summary
       - timetable_description
+      - timetable_partitioned
       - tags
       - max_active_tasks
       - max_active_runs
diff --git 
a/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
 
b/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
index f50499bdb85..92ca0df16b3 100644
--- 
a/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
+++ 
b/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
@@ -10206,6 +10206,9 @@ components:
           - type: string
           - type: 'null'
           title: Timetable Description
+        timetable_partitioned:
+          type: boolean
+          title: Timetable Partitioned
         tags:
           items:
             $ref: '#/components/schemas/DagTagResponse'
@@ -10385,6 +10388,7 @@ components:
       - description
       - timetable_summary
       - timetable_description
+      - timetable_partitioned
       - tags
       - max_active_tasks
       - max_active_runs
@@ -10490,6 +10494,9 @@ components:
           - type: string
           - type: 'null'
           title: Timetable Description
+        timetable_partitioned:
+          type: boolean
+          title: Timetable Partitioned
         tags:
           items:
             $ref: '#/components/schemas/DagTagResponse'
@@ -10569,6 +10576,7 @@ components:
       - description
       - timetable_summary
       - timetable_description
+      - timetable_partitioned
       - tags
       - max_active_tasks
       - max_active_runs
diff --git a/airflow-core/src/airflow/cli/commands/dag_command.py 
b/airflow-core/src/airflow/cli/commands/dag_command.py
index d054f364c16..6572f467ff3 100644
--- a/airflow-core/src/airflow/cli/commands/dag_command.py
+++ b/airflow-core/src/airflow/cli/commands/dag_command.py
@@ -261,6 +261,7 @@ def _get_dagbag_dag_details(dag: DAG) -> dict:
         "description": dag.description,
         "timetable_summary": core_timetable.summary,
         "timetable_description": core_timetable.description,
+        "timetable_partitioned": core_timetable.partitioned,
         "tags": dag.tags,
         "max_active_tasks": dag.max_active_tasks,
         "max_active_runs": dag.max_active_runs,
diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts 
b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts
index a82a6598449..0b41a478c25 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts
@@ -1881,6 +1881,10 @@ export const $DAGDetailsResponse = {
             ],
             title: 'Timetable Description'
         },
+        timetable_partitioned: {
+            type: 'boolean',
+            title: 'Timetable Partitioned'
+        },
         tags: {
             items: {
                 '$ref': '#/components/schemas/DagTagResponse'
@@ -2176,7 +2180,7 @@ Deprecated: Use max_active_tasks instead.`,
         }
     },
     type: 'object',
-    required: ['dag_id', 'dag_display_name', 'is_paused', 'is_stale', 
'last_parsed_time', 'last_parse_duration', 'last_expired', 'bundle_name', 
'bundle_version', 'relative_fileloc', 'fileloc', 'description', 
'timetable_summary', 'timetable_description', 'tags', 'max_active_tasks', 
'max_active_runs', 'max_consecutive_failed_dag_runs', 
'has_task_concurrency_limits', 'has_import_errors', 'next_dagrun_logical_date', 
'next_dagrun_data_interval_start', 'next_dagrun_data_interval_end', 'next_da 
[...]
+    required: ['dag_id', 'dag_display_name', 'is_paused', 'is_stale', 
'last_parsed_time', 'last_parse_duration', 'last_expired', 'bundle_name', 
'bundle_version', 'relative_fileloc', 'fileloc', 'description', 
'timetable_summary', 'timetable_description', 'timetable_partitioned', 'tags', 
'max_active_tasks', 'max_active_runs', 'max_consecutive_failed_dag_runs', 
'has_task_concurrency_limits', 'has_import_errors', 'next_dagrun_logical_date', 
'next_dagrun_data_interval_start', 'next_dagrun_dat [...]
     title: 'DAGDetailsResponse',
     description: 'Specific serializer for DAG Details responses.'
 } as const;
@@ -2318,6 +2322,10 @@ export const $DAGResponse = {
             ],
             title: 'Timetable Description'
         },
+        timetable_partitioned: {
+            type: 'boolean',
+            title: 'Timetable Partitioned'
+        },
         tags: {
             items: {
                 '$ref': '#/components/schemas/DagTagResponse'
@@ -2429,7 +2437,7 @@ export const $DAGResponse = {
         }
     },
     type: 'object',
-    required: ['dag_id', 'dag_display_name', 'is_paused', 'is_stale', 
'last_parsed_time', 'last_parse_duration', 'last_expired', 'bundle_name', 
'bundle_version', 'relative_fileloc', 'fileloc', 'description', 
'timetable_summary', 'timetable_description', 'tags', 'max_active_tasks', 
'max_active_runs', 'max_consecutive_failed_dag_runs', 
'has_task_concurrency_limits', 'has_import_errors', 'next_dagrun_logical_date', 
'next_dagrun_data_interval_start', 'next_dagrun_data_interval_end', 'next_da 
[...]
+    required: ['dag_id', 'dag_display_name', 'is_paused', 'is_stale', 
'last_parsed_time', 'last_parse_duration', 'last_expired', 'bundle_name', 
'bundle_version', 'relative_fileloc', 'fileloc', 'description', 
'timetable_summary', 'timetable_description', 'timetable_partitioned', 'tags', 
'max_active_tasks', 'max_active_runs', 'max_consecutive_failed_dag_runs', 
'has_task_concurrency_limits', 'has_import_errors', 'next_dagrun_logical_date', 
'next_dagrun_data_interval_start', 'next_dagrun_dat [...]
     title: 'DAGResponse',
     description: 'DAG serializer for responses.'
 } as const;
@@ -7679,6 +7687,10 @@ export const $DAGWithLatestDagRunsResponse = {
             ],
             title: 'Timetable Description'
         },
+        timetable_partitioned: {
+            type: 'boolean',
+            title: 'Timetable Partitioned'
+        },
         tags: {
             items: {
                 '$ref': '#/components/schemas/DagTagResponse'
@@ -7820,7 +7832,7 @@ export const $DAGWithLatestDagRunsResponse = {
         }
     },
     type: 'object',
-    required: ['dag_id', 'dag_display_name', 'is_paused', 'is_stale', 
'last_parsed_time', 'last_parse_duration', 'last_expired', 'bundle_name', 
'bundle_version', 'relative_fileloc', 'fileloc', 'description', 
'timetable_summary', 'timetable_description', 'tags', 'max_active_tasks', 
'max_active_runs', 'max_consecutive_failed_dag_runs', 
'has_task_concurrency_limits', 'has_import_errors', 'next_dagrun_logical_date', 
'next_dagrun_data_interval_start', 'next_dagrun_data_interval_end', 'next_da 
[...]
+    required: ['dag_id', 'dag_display_name', 'is_paused', 'is_stale', 
'last_parsed_time', 'last_parse_duration', 'last_expired', 'bundle_name', 
'bundle_version', 'relative_fileloc', 'fileloc', 'description', 
'timetable_summary', 'timetable_description', 'timetable_partitioned', 'tags', 
'max_active_tasks', 'max_active_runs', 'max_consecutive_failed_dag_runs', 
'has_task_concurrency_limits', 'has_import_errors', 'next_dagrun_logical_date', 
'next_dagrun_data_interval_start', 'next_dagrun_dat [...]
     title: 'DAGWithLatestDagRunsResponse',
     description: 'DAG with latest dag runs response serializer.'
 } as const;
diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts 
b/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts
index 53c9e259d64..fe7062b70df 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts
@@ -540,6 +540,7 @@ export type DAGDetailsResponse = {
     description: string | null;
     timetable_summary: string | null;
     timetable_description: string | null;
+    timetable_partitioned: boolean;
     tags: Array<DagTagResponse>;
     max_active_tasks: number;
     max_active_runs: number | null;
@@ -618,6 +619,7 @@ export type DAGResponse = {
     description: string | null;
     timetable_summary: string | null;
     timetable_description: string | null;
+    timetable_partitioned: boolean;
     tags: Array<DagTagResponse>;
     max_active_tasks: number;
     max_active_runs: number | null;
@@ -1893,6 +1895,7 @@ export type DAGWithLatestDagRunsResponse = {
     description: string | null;
     timetable_summary: string | null;
     timetable_description: string | null;
+    timetable_partitioned: boolean;
     tags: Array<DagTagResponse>;
     max_active_tasks: number;
     max_active_runs: number | null;
diff --git a/airflow-core/src/airflow/ui/src/pages/DagsList/DagCard.test.tsx 
b/airflow-core/src/airflow/ui/src/pages/DagsList/DagCard.test.tsx
index e520dce9d81..ad0e34b6607 100644
--- a/airflow-core/src/airflow/ui/src/pages/DagsList/DagCard.test.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/DagsList/DagCard.test.tsx
@@ -109,6 +109,7 @@ const mockDag = {
   relative_fileloc: "nested_task_groups.py",
   tags: [],
   timetable_description: "Every minute",
+  timetable_partitioned: false,
   timetable_summary: "* * * * *",
 } satisfies DAGWithLatestDagRunsResponse;
 
diff --git 
a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dags.py 
b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dags.py
index 41d11545689..55515b76931 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dags.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dags.py
@@ -934,20 +934,21 @@ class TestDagDetails(TestDagEndpoint):
         last_parsed_time = res_json["last_parsed_time"]
         file_token = res_json["file_token"]
         expected = {
+            "active_runs_count": 0,
+            "allowed_run_types": None,
+            "asset_expression": None,
             "bundle_name": "dag_maker",
             "bundle_version": None,
-            "asset_expression": None,
             "catchup": False,
             "concurrency": 16,
-            "dag_id": dag_id,
             "dag_display_name": dag_display_name,
+            "dag_id": dag_id,
             "dag_run_timeout": None,
             "default_args": {
                 "depends_on_past": False,
                 "retries": 1,
                 "retry_delay": "PT5M",
             },
-            "allowed_run_types": None,
             "description": None,
             "doc_md": "details",
             "end_date": None,
@@ -955,9 +956,10 @@ class TestDagDetails(TestDagEndpoint):
             "file_token": file_token,
             "has_import_errors": False,
             "has_task_concurrency_limits": True,
-            "is_stale": False,
+            "is_favorite": False,
             "is_paused": False,
             "is_paused_upon_creation": None,
+            "is_stale": False,
             "latest_dag_version": {
                 "bundle_name": "dag_maker",
                 "bundle_url": "http://test_host.github.com/tree/None/dags";,
@@ -983,22 +985,21 @@ class TestDagDetails(TestDagEndpoint):
             "owner_links": {},
             "params": {
                 "foo": {
-                    "value": 1,
-                    "schema": {},
                     "description": None,
+                    "schema": {},
                     "source": None,
+                    "value": 1,
                 }
             },
             "relative_fileloc": "test_dags.py",
             "render_template_as_native_obj": False,
-            "timetable_summary": None,
             "start_date": start_date,
             "tags": [],
             "template_search_path": None,
             "timetable_description": "Never, external triggers only",
+            "timetable_partitioned": False,
+            "timetable_summary": None,
             "timezone": UTC_JSON_REPR,
-            "is_favorite": False,
-            "active_runs_count": 0,
         }
         assert res_json == expected
 
@@ -1032,20 +1033,21 @@ class TestDagDetails(TestDagEndpoint):
         last_parse_duration = res_json["last_parse_duration"]
         file_token = res_json["file_token"]
         expected = {
+            "active_runs_count": 0,
+            "allowed_run_types": None,
+            "asset_expression": None,
             "bundle_name": "dag_maker",
             "bundle_version": None,
-            "asset_expression": None,
             "catchup": False,
             "concurrency": 16,
-            "dag_id": dag_id,
             "dag_display_name": dag_display_name,
+            "dag_id": dag_id,
             "dag_run_timeout": None,
             "default_args": {
                 "depends_on_past": False,
                 "retries": 1,
                 "retry_delay": "PT5M",
             },
-            "allowed_run_types": None,
             "description": None,
             "doc_md": "details",
             "end_date": None,
@@ -1053,6 +1055,7 @@ class TestDagDetails(TestDagEndpoint):
             "file_token": file_token,
             "has_import_errors": False,
             "has_task_concurrency_limits": True,
+            "is_favorite": False,
             "is_stale": False,
             "is_paused": False,
             "is_paused_upon_creation": None,
@@ -1062,9 +1065,9 @@ class TestDagDetails(TestDagEndpoint):
                 "bundle_version": None,
                 "created_at": mock.ANY,
                 "dag_id": "test_dag2",
+                "dag_display_name": dag_display_name,
                 "id": mock.ANY,
                 "version_number": 1,
-                "dag_display_name": dag_display_name,
             },
             "last_expired": None,
             "last_parsed": last_parsed,
@@ -1081,22 +1084,21 @@ class TestDagDetails(TestDagEndpoint):
             "owner_links": {},
             "params": {
                 "foo": {
-                    "value": 1,
-                    "schema": {},
                     "description": None,
+                    "schema": {},
                     "source": None,
+                    "value": 1,
                 }
             },
             "relative_fileloc": "test_dags.py",
             "render_template_as_native_obj": False,
-            "timetable_summary": None,
             "start_date": start_date,
             "tags": [],
             "template_search_path": None,
+            "timetable_summary": None,
             "timetable_description": "Never, external triggers only",
+            "timetable_partitioned": False,
             "timezone": UTC_JSON_REPR,
-            "is_favorite": False,
-            "active_runs_count": 0,
         }
         assert res_json == expected
 
@@ -1226,30 +1228,31 @@ class TestGetDag(TestDagEndpoint):
             "dag_id": dag_id,
             "dag_display_name": dag_display_name,
             "allowed_run_types": None,
+            "bundle_name": "dag_maker",
+            "bundle_version": None,
             "description": None,
             "fileloc": __file__,
             "file_token": file_token,
+            "has_import_errors": False,
+            "has_task_concurrency_limits": True,
             "is_paused": False,
             "is_stale": False,
-            "owners": ["airflow"],
-            "timetable_summary": None,
-            "tags": tags,
-            "has_task_concurrency_limits": True,
+            "last_expired": None,
+            "last_parse_duration": last_parse_duration,
+            "last_parsed_time": last_parsed_time,
+            "max_active_runs": 16,
+            "max_active_tasks": 16,
+            "max_consecutive_failed_dag_runs": 0,
             "next_dagrun_data_interval_start": None,
             "next_dagrun_data_interval_end": None,
             "next_dagrun_logical_date": None,
             "next_dagrun_run_after": None,
-            "max_active_runs": 16,
-            "max_consecutive_failed_dag_runs": 0,
-            "last_expired": None,
-            "max_active_tasks": 16,
-            "last_parsed_time": last_parsed_time,
-            "last_parse_duration": last_parse_duration,
-            "timetable_description": "Never, external triggers only",
-            "has_import_errors": False,
-            "bundle_name": "dag_maker",
-            "bundle_version": None,
+            "owners": ["airflow"],
             "relative_fileloc": "test_dags.py",
+            "tags": tags,
+            "timetable_description": "Never, external triggers only",
+            "timetable_partitioned": False,
+            "timetable_summary": None,
         }
         assert res_json == expected
 
diff --git a/airflow-ctl/src/airflowctl/api/datamodels/generated.py 
b/airflow-ctl/src/airflowctl/api/datamodels/generated.py
index 0ca5629b532..fde516ef5c7 100644
--- a/airflow-ctl/src/airflowctl/api/datamodels/generated.py
+++ b/airflow-ctl/src/airflowctl/api/datamodels/generated.py
@@ -1357,6 +1357,7 @@ class DAGDetailsResponse(BaseModel):
     description: Annotated[str | None, Field(title="Description")] = None
     timetable_summary: Annotated[str | None, Field(title="Timetable Summary")] 
= None
     timetable_description: Annotated[str | None, Field(title="Timetable 
Description")] = None
+    timetable_partitioned: Annotated[bool, Field(title="Timetable 
Partitioned")]
     tags: Annotated[list[DagTagResponse], Field(title="Tags")]
     max_active_tasks: Annotated[int, Field(title="Max Active Tasks")]
     max_active_runs: Annotated[int | None, Field(title="Max Active Runs")] = 
None
@@ -1421,6 +1422,7 @@ class DAGResponse(BaseModel):
     description: Annotated[str | None, Field(title="Description")] = None
     timetable_summary: Annotated[str | None, Field(title="Timetable Summary")] 
= None
     timetable_description: Annotated[str | None, Field(title="Timetable 
Description")] = None
+    timetable_partitioned: Annotated[bool, Field(title="Timetable 
Partitioned")]
     tags: Annotated[list[DagTagResponse], Field(title="Tags")]
     max_active_tasks: Annotated[int, Field(title="Max Active Tasks")]
     max_active_runs: Annotated[int | None, Field(title="Max Active Runs")] = 
None
diff --git a/airflow-ctl/tests/airflow_ctl/api/test_operations.py 
b/airflow-ctl/tests/airflow_ctl/api/test_operations.py
index 701440eeb5a..65f058d6656 100644
--- a/airflow-ctl/tests/airflow_ctl/api/test_operations.py
+++ b/airflow-ctl/tests/airflow_ctl/api/test_operations.py
@@ -684,6 +684,7 @@ class TestDagOperations:
         description="description",
         timetable_summary="timetable_summary",
         timetable_description="timetable_description",
+        timetable_partitioned=False,
         tags=[],
         max_active_tasks=1,
         max_active_runs=1,
@@ -711,6 +712,7 @@ class TestDagOperations:
         description="description",
         timetable_summary="timetable_summary",
         timetable_description="timetable_description",
+        timetable_partitioned=False,
         tags=[],
         max_active_tasks=1,
         max_active_runs=1,
diff --git a/airflow-ctl/tests/airflow_ctl/ctl/commands/test_dag_command.py 
b/airflow-ctl/tests/airflow_ctl/ctl/commands/test_dag_command.py
index fbc6ee1240b..600a553109b 100644
--- a/airflow-ctl/tests/airflow_ctl/ctl/commands/test_dag_command.py
+++ b/airflow-ctl/tests/airflow_ctl/ctl/commands/test_dag_command.py
@@ -41,6 +41,7 @@ class TestDagCommands:
         description="description",
         timetable_summary="timetable_summary",
         timetable_description="timetable_description",
+        timetable_partitioned=False,
         tags=[],
         max_active_tasks=1,
         max_active_runs=1,
@@ -68,6 +69,7 @@ class TestDagCommands:
         description="description",
         timetable_summary="timetable_summary",
         timetable_description="timetable_description",
+        timetable_partitioned=False,
         tags=[],
         max_active_tasks=1,
         max_active_runs=1,

Reply via email to