diff --git a/airflow-core/newsfragments/67586.significant.rst b/airflow-core/newsfragments/67586.significant.rst new file mode 100644 index 0000000000000..4a1c2afb999fe --- /dev/null +++ b/airflow-core/newsfragments/67586.significant.rst @@ -0,0 +1,4 @@ +Add a new **Deadlines** page under the Browse menu. + +The page is accessible to any role that already has ``can_read`` and +``menu_access`` on ``DAG Runs``. diff --git a/airflow-core/src/airflow/api_fastapi/common/types.py b/airflow-core/src/airflow/api_fastapi/common/types.py index 7d2a944c82228..462a0278b4d44 100644 --- a/airflow-core/src/airflow/api_fastapi/common/types.py +++ b/airflow-core/src/airflow/api_fastapi/common/types.py @@ -120,6 +120,7 @@ class MenuItem(Enum): CONFIG = "Config" CONNECTIONS = "Connections" DAGS = "Dags" + DEADLINES = "Deadlines" DOCS = "Docs" JOBS = "Jobs" PLUGINS = "Plugins" 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 52a09dcbb7b8c..e9d280855b529 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 @@ -3144,6 +3144,7 @@ components: - Config - Connections - Dags + - Deadlines - Docs - Jobs - Plugins 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 e1a6cc4790dd0..d280c500275ef 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 @@ -9257,7 +9257,7 @@ export const $LightGridTaskInstanceSummary = { export const $MenuItem = { type: 'string', - enum: ['Required Actions', 'Assets', 'Audit Log', 'Config', 'Connections', 'Dags', 'Docs', 'Jobs', 'Plugins', 'Pools', 'Providers', 'Variables', 'XComs'], + enum: ['Required Actions', 'Assets', 'Audit Log', 'Config', 'Connections', 'Dags', 'Deadlines', 'Docs', 'Jobs', 'Plugins', 'Pools', 'Providers', 'Variables', 'XComs'], title: 'MenuItem', description: 'Define all menu items defined in the menu.' } 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 2fd1f98417940..11b41b04e2617 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 @@ -2337,7 +2337,7 @@ export type LightGridTaskInstanceSummary = { /** * Define all menu items defined in the menu. */ -export type MenuItem = 'Required Actions' | 'Assets' | 'Audit Log' | 'Config' | 'Connections' | 'Dags' | 'Docs' | 'Jobs' | 'Plugins' | 'Pools' | 'Providers' | 'Variables' | 'XComs'; +export type MenuItem = 'Required Actions' | 'Assets' | 'Audit Log' | 'Config' | 'Connections' | 'Dags' | 'Deadlines' | 'Docs' | 'Jobs' | 'Plugins' | 'Pools' | 'Providers' | 'Variables' | 'XComs'; /** * Menu Item Collection serializer for responses. diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/en/browse.json b/airflow-core/src/airflow/ui/public/i18n/locales/en/browse.json index d92d59b9272c8..6c6b1016b5b6d 100644 --- a/airflow-core/src/airflow/ui/public/i18n/locales/en/browse.json +++ b/airflow-core/src/airflow/ui/public/i18n/locales/en/browse.json @@ -11,6 +11,24 @@ }, "title": "Audit Log" }, + "deadlines": { + "columns": { + "alertName": "Alert Name", + "deadlineTime": "Deadline Time", + "status": "Status" + }, + "deadline_one": "Deadline", + "deadline_other": "Deadlines", + "filters": { + "status": "Status", + "statusOptions": { + "all": "All", + "missed": "Missed", + "pending": "Pending" + } + }, + "title": "Deadlines" + }, "xcom": { "add": { "error": "Failed to add XCom", diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/en/common.json b/airflow-core/src/airflow/ui/public/i18n/locales/en/common.json index 4e87c45e30513..7b370ca2d45a4 100644 --- a/airflow-core/src/airflow/ui/public/i18n/locales/en/common.json +++ b/airflow-core/src/airflow/ui/public/i18n/locales/en/common.json @@ -22,6 +22,7 @@ "backfill_other": "Backfills", "browse": { "auditLog": "Audit Log", + "deadlines": "Deadlines", "jobs": "Jobs", "requiredActions": "Required Actions", "xcoms": "XComs" diff --git a/airflow-core/src/airflow/ui/src/constants/filterConfigs.tsx b/airflow-core/src/airflow/ui/src/constants/filterConfigs.tsx index 7d160ad5999b7..1ce5daa23e660 100644 --- a/airflow-core/src/airflow/ui/src/constants/filterConfigs.tsx +++ b/airflow-core/src/airflow/ui/src/constants/filterConfigs.tsx @@ -129,6 +129,13 @@ export const useFilterConfigs = () => { min: 1, type: FilterTypes.NUMBER, }, + [SearchParamsKeys.DEADLINE_TIME_RANGE]: { + endKey: SearchParamsKeys.DEADLINE_TIME_LTE, + icon: , + label: translate("browse:deadlines.columns.deadlineTime"), + startKey: SearchParamsKeys.DEADLINE_TIME_GTE, + type: FilterTypes.DATERANGE, + }, [SearchParamsKeys.DURATION_GTE]: { icon: , label: translate("common:filters.durationFrom"), @@ -209,6 +216,16 @@ export const useFilterConfigs = () => { min: -1, type: FilterTypes.NUMBER, }, + [SearchParamsKeys.MISSED]: { + icon: , + label: translate("browse:deadlines.filters.status"), + options: [ + { label: translate("browse:deadlines.filters.statusOptions.all"), value: "" }, + { label: translate("browse:deadlines.filters.statusOptions.pending"), value: "false" }, + { label: translate("browse:deadlines.filters.statusOptions.missed"), value: "true" }, + ], + type: FilterTypes.SELECT, + }, [SearchParamsKeys.NAME_PATTERN]: { hotkeyDisabled: true, icon: , diff --git a/airflow-core/src/airflow/ui/src/constants/searchParams.ts b/airflow-core/src/airflow/ui/src/constants/searchParams.ts index b49d1cad846ab..f3583d449ced7 100644 --- a/airflow-core/src/airflow/ui/src/constants/searchParams.ts +++ b/airflow-core/src/airflow/ui/src/constants/searchParams.ts @@ -33,6 +33,9 @@ export enum SearchParamsKeys { DAG_ID_PATTERN = "dag_id_pattern", DAG_VERSION = "dag_version", DAG_VIEW = "view", + DEADLINE_TIME_GTE = "deadline_time_gte", + DEADLINE_TIME_LTE = "deadline_time_lte", + DEADLINE_TIME_RANGE = "deadline_time_range", DEPENDENCIES = "dependencies", DURATION_GTE = "duration_gte", DURATION_LTE = "duration_lte", @@ -64,6 +67,7 @@ export enum SearchParamsKeys { LOGICAL_DATE_RANGE = "logical_date_range", MAP_INDEX = "map_index", MAPPED = "mapped", + MISSED = "missed", NAME_PATTERN = "name_pattern", NEEDS_REVIEW = "needs_review", OFFSET = "offset", diff --git a/airflow-core/src/airflow/ui/src/layouts/Nav/BrowseButton.tsx b/airflow-core/src/airflow/ui/src/layouts/Nav/BrowseButton.tsx index a9de356ff9d8d..d95f9e0f31815 100644 --- a/airflow-core/src/airflow/ui/src/layouts/Nav/BrowseButton.tsx +++ b/airflow-core/src/airflow/ui/src/layouts/Nav/BrowseButton.tsx @@ -33,6 +33,11 @@ const links = [ key: "auditLog", title: "Audit Log", }, + { + href: "/deadlines", + key: "deadlines", + title: "Deadlines", + }, { href: "/jobs", key: "jobs", diff --git a/airflow-core/src/airflow/ui/src/pages/Deadlines/index.tsx b/airflow-core/src/airflow/ui/src/pages/Deadlines/index.tsx new file mode 100644 index 0000000000000..720648c4247b0 --- /dev/null +++ b/airflow-core/src/airflow/ui/src/pages/Deadlines/index.tsx @@ -0,0 +1,156 @@ +/*! + * 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. + */ +import { Badge, Box, Heading, Link, VStack } from "@chakra-ui/react"; +import type { ColumnDef } from "@tanstack/react-table"; +import type { TFunction } from "i18next"; +import { useTranslation } from "react-i18next"; +import { Link as RouterLink, useSearchParams } from "react-router-dom"; + +import { useDeadlinesServiceGetDeadlines } from "openapi/queries"; +import type { DeadlineResponse } from "openapi/requests/types.gen"; +import { DataTable } from "src/components/DataTable"; +import { useTableURLState } from "src/components/DataTable/useTableUrlState"; +import { ErrorAlert } from "src/components/ErrorAlert"; +import { FilterBar } from "src/components/FilterBar"; +import Time from "src/components/Time"; +import { TruncatedText } from "src/components/TruncatedText"; +import { SearchParamsKeys } from "src/constants/searchParams"; +import { useFiltersHandler, type FilterableSearchParamsKeys } from "src/utils"; + +type DeadlineRow = { row: { original: DeadlineResponse } }; + +const createColumns = (translate: TFunction): Array> => [ + { + accessorKey: "dag_id", + cell: ({ row: { original } }: DeadlineRow) => ( + + + + + + ), + header: translate("common:dagId"), + }, + { + accessorKey: "dag_run_id", + cell: ({ row: { original } }: DeadlineRow) => ( + + + + + + ), + enableSorting: false, + header: translate("common:dagRunId"), + }, + { + accessorKey: "deadline_time", + cell: ({ row: { original } }: DeadlineRow) =>