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 3bf599d6a9506..6c07f1ed8a8ce 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 a0339372b8db0..f60a778113021 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 f50499bdb8590..92ca0df16b3b2 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 d054f364c16b8..6572f467ff34c 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 a82a659844988..0b41a478c253e 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_dagrun_run_after', 'allowed_run_types', 'owners', 'catchup', 'dag_run_timeout', 'asset_expression', 'doc_md', 'start_date', 'end_date', 'is_paused_upon_creation', 'params', 'render_template_as_native_obj', 'template_search_path', 'timezone', 'last_parsed', 'default_args', 'file_token', 'concurrency', 'latest_dag_version'], + 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_data_interval_end', 'next_dagrun_run_after', 'allowed_run_types', 'owners', 'catchup', 'dag_run_timeout', 'asset_expression', 'doc_md', 'start_date', 'end_date', 'is_paused_upon_creation', 'params', 'render_template_as_native_obj', 'template_search_path', 'timezone', 'last_parsed', 'default_args', 'file_token', 'concurrency', 'latest_dag_version'], 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_dagrun_run_after', 'allowed_run_types', 'owners', 'file_token'], + 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_data_interval_end', 'next_dagrun_run_after', 'allowed_run_types', 'owners', 'file_token'], 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_dagrun_run_after', 'allowed_run_types', 'owners', 'asset_expression', 'latest_dag_runs', 'pending_actions', 'is_favorite', 'file_token'], + 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_data_interval_end', 'next_dagrun_run_after', 'allowed_run_types', 'owners', 'asset_expression', 'latest_dag_runs', 'pending_actions', 'is_favorite', 'file_token'], 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 53c9e259d640b..fe7062b70dfab 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; 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; 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; 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 e520dce9d81bc..ad0e34b660786 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 41d11545689dc..55515b76931eb 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 @@ def test_dag_details( 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 @@ def test_dag_details( "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 @@ def test_dag_details( "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 @@ def test_dag_details_with_view_url_template( 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 @@ def test_dag_details_with_view_url_template( "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 @@ def test_dag_details_with_view_url_template( "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 @@ def test_dag_details_with_view_url_template( "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 @@ def test_get_dag( "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 0ca5629b5320d..fde516ef5c7ca 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 701440eeb5abd..65f058d6656af 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 fbc6ee1240b6e..600a553109b88 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,