From 0dbf93fdbe8fd0f81c5fda031d76d5e6e3a08179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=96=86=E5=AE=B8?= Date: Sat, 2 Aug 2025 02:09:10 +0800 Subject: [PATCH 01/22] feat(ui): Add filters for Key, DAG, Task, and Run ID to XCom page --- .../ui/public/i18n/locales/en/browse.json | 6 + .../src/airflow/ui/src/pages/XCom/XCom.tsx | 125 +++++++++++++++++- 2 files changed, 125 insertions(+), 6 deletions(-) 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 4a2ab97354b16..6ede12d44bc14 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 @@ -18,6 +18,12 @@ "key": "Key", "value": "Value" }, + "filters": { + "dagFilter": "Filter by Dag ID", + "keyFilter": "Filter by XCom key", + "runIdFilter": "Filter by Run ID", + "taskIdFilter": "Filter by Task ID" + }, "title": "XCom" } } diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx b/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx index 842bd7ac7d4f0..d241ac0cb4bdb 100644 --- a/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx +++ b/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx @@ -16,16 +16,18 @@ * specific language governing permissions and limitations * under the License. */ -import { Box, Heading, Link } from "@chakra-ui/react"; +import { Box, Heading, Link, HStack } from "@chakra-ui/react"; import type { ColumnDef } from "@tanstack/react-table"; +import { useCallback } from "react"; import { useTranslation } from "react-i18next"; -import { Link as RouterLink, useParams } from "react-router-dom"; +import { Link as RouterLink, useParams, useSearchParams } from "react-router-dom"; import { useXcomServiceGetXcomEntries } from "openapi/queries"; import type { XComResponse } 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 { SearchBar } from "src/components/SearchBar"; import { TruncatedText } from "src/components/TruncatedText"; import { getTaskInstanceLinkFromObj } from "src/utils/links"; @@ -102,26 +104,137 @@ export const XCom = () => { const { dagId = "~", mapIndex = "-1", runId = "~", taskId = "~" } = useParams(); const { t: translate } = useTranslation(["browse", "common"]); const { setTableURLState, tableURLState } = useTableURLState(); - const { pagination } = tableURLState; + const { pagination, sorting } = tableURLState; + const [searchParams, setSearchParams] = useSearchParams(); + + const filteredKey = searchParams.get("key"); + const filteredDagId = searchParams.get("dag_id"); + const filteredRunId = searchParams.get("run_id"); + const filteredTaskId = searchParams.get("task_id"); const { data, error, isFetching, isLoading } = useXcomServiceGetXcomEntries( { - dagId, - dagRunId: runId, + dagId: filteredDagId ?? dagId, + dagRunId: filteredRunId ?? runId, limit: pagination.pageSize, mapIndex: mapIndex === "-1" ? undefined : parseInt(mapIndex, 10), offset: pagination.pageIndex * pagination.pageSize, - taskId, + taskId: filteredTaskId ?? taskId, + xcomKey: filteredKey ?? undefined, }, undefined, { enabled: !isNaN(pagination.pageSize) }, ); + const handleKeyFilterChange = useCallback( + (value: string) => { + if (value === "") { + searchParams.delete("key"); + } else { + searchParams.set("key", value); + } + setTableURLState({ + pagination: { ...pagination, pageIndex: 0 }, + sorting, + }); + setSearchParams(searchParams); + }, + [pagination, searchParams, setSearchParams, setTableURLState, sorting], + ); + + const handleDagIdFilterChange = useCallback( + (value: string) => { + if (value === "") { + searchParams.delete("dag_id"); + } else { + searchParams.set("dag_id", value); + } + setTableURLState({ + pagination: { ...pagination, pageIndex: 0 }, + sorting, + }); + setSearchParams(searchParams); + }, + [pagination, searchParams, setSearchParams, setTableURLState, sorting], + ); + + const handleRunIdFilterChange = useCallback( + (value: string) => { + if (value === "") { + searchParams.delete("run_id"); + } else { + searchParams.set("run_id", value); + } + setTableURLState({ + pagination: { ...pagination, pageIndex: 0 }, + sorting, + }); + setSearchParams(searchParams); + }, + [pagination, searchParams, setSearchParams, setTableURLState, sorting], + ); + + const handleTaskIdFilterChange = useCallback( + (value: string) => { + if (value === "") { + searchParams.delete("task_id"); + } else { + searchParams.set("task_id", value); + } + setTableURLState({ + pagination: { ...pagination, pageIndex: 0 }, + sorting, + }); + setSearchParams(searchParams); + }, + [pagination, searchParams, setSearchParams, setTableURLState, sorting], + ); + return ( {dagId === "~" && runId === "~" && taskId === "~" ? ( {translate("xcom.title")} ) : undefined} + + + + + + + + + + + + + + + + Date: Mon, 4 Aug 2025 00:22:20 +0800 Subject: [PATCH 02/22] feat: implement basic search with filter pill --- .../ui/src/pages/XCom/FilterManager.tsx | 155 ++++++++++++++++++ .../airflow/ui/src/pages/XCom/FilterPill.tsx | 112 +++++++++++++ .../src/airflow/ui/src/pages/XCom/XCom.tsx | 117 +++---------- 3 files changed, 294 insertions(+), 90 deletions(-) create mode 100644 airflow-core/src/airflow/ui/src/pages/XCom/FilterManager.tsx create mode 100644 airflow-core/src/airflow/ui/src/pages/XCom/FilterPill.tsx diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/FilterManager.tsx b/airflow-core/src/airflow/ui/src/pages/XCom/FilterManager.tsx new file mode 100644 index 0000000000000..da1bdd7f02944 --- /dev/null +++ b/airflow-core/src/airflow/ui/src/pages/XCom/FilterManager.tsx @@ -0,0 +1,155 @@ +/*! + * 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 { Button, HStack } from "@chakra-ui/react"; +import { useCallback, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { MdAdd } from "react-icons/md"; + +import { Menu } from "src/components/ui"; + +import { FilterPill, type FilterType } from "./FilterPill"; + +type FilterState = { + id: string; + type: FilterType; + value: string; +}; + +type FilterManagerProps = { + readonly initialFilters?: Record; + readonly onFiltersChange: (filters: Record) => void; +}; + +const FILTER_LABELS: Record = { + dag_id: "DAG ID", + key: "Key", + run_id: "Run ID", + task_id: "Task ID", +}; + +const defaultInitialFilters: Record = { + dag_id: "", + key: "", + run_id: "", + task_id: "", +}; + +export const FilterManager = ({ + initialFilters = defaultInitialFilters, + onFiltersChange, +}: FilterManagerProps) => { + const { t: translate } = useTranslation(); + const [filters, setFilters] = useState>(() => + Object.entries(initialFilters) + .filter(([, value]) => value !== "") + .map(([type, value]) => ({ + id: `${type}-${Date.now()}`, + type: type as FilterType, + value, + })), + ); + + const addFilter = useCallback((filterType: FilterType) => { + const newFilter: FilterState = { + id: `${filterType}-${Date.now()}`, + type: filterType, + value: "", + }; + + setFilters((previous) => [...previous, newFilter]); + }, []); + + const updateFilter = useCallback( + (id: string, value: string) => { + setFilters((previous) => previous.map((filter) => (filter.id === id ? { ...filter, value } : filter))); + + const updatedFilters = filters.reduce( + (accumulator, filter) => { + accumulator[filter.type] = filter.id === id ? value : filter.value; + + return accumulator; + }, + {} as Record, + ); + + onFiltersChange(updatedFilters); + }, + [filters, onFiltersChange], + ); + + const removeFilter = useCallback( + (id: string) => { + const filterToRemove = filters.find((filter) => filter.id === id); + + setFilters((previous) => previous.filter((filter) => filter.id !== id)); + + if (filterToRemove) { + const updatedFilters = filters + .filter((filter) => filter.id !== id) + .reduce( + (accumulator, filter) => { + accumulator[filter.type] = filter.value; + + return accumulator; + }, + {} as Record, + ); + + onFiltersChange(updatedFilters); + } + }, + [filters, onFiltersChange], + ); + + const availableFilterTypes = Object.keys(FILTER_LABELS).filter( + (type) => !filters.some((filter) => filter.type === type), + ) as Array; + + return ( + + {filters.map((filter) => ( + removeFilter(filter.id)} + onValueChange={(value) => updateFilter(filter.id, value)} + value={filter.value} + /> + ))} + {availableFilterTypes.length > 0 && ( + + + + + + {availableFilterTypes.map((filterType) => ( + addFilter(filterType)} value={filterType}> + {FILTER_LABELS[filterType]} + + ))} + + + )} + + ); +}; diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/FilterPill.tsx b/airflow-core/src/airflow/ui/src/pages/XCom/FilterPill.tsx new file mode 100644 index 0000000000000..104eed646c6bc --- /dev/null +++ b/airflow-core/src/airflow/ui/src/pages/XCom/FilterPill.tsx @@ -0,0 +1,112 @@ +/*! + * 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, HStack, IconButton, Input } from "@chakra-ui/react"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { MdClose } from "react-icons/md"; + +export type FilterType = "dag_id" | "key" | "run_id" | "task_id"; + +type FilterPillProps = { + readonly filterType: FilterType; + readonly label: string; + readonly onRemove: () => void; + readonly onValueChange: (value: string) => void; + readonly value: string; +}; + +export const FilterPill = ({ label, onRemove, onValueChange, value }: FilterPillProps) => { + const [isEditing, setIsEditing] = useState(value === ""); + const [inputValue, setInputValue] = useState(value); + const inputRef = useRef(null); + + const handlePillClick = useCallback(() => { + setIsEditing(true); + }, []); + + const handleInputKeyDown = useCallback( + (event: React.KeyboardEvent) => { + if (event.key === "Enter") { + setIsEditing(false); + onValueChange(inputValue); + } else if (event.key === "Escape") { + setInputValue(value); + setIsEditing(false); + } + }, + [inputValue, onValueChange, value], + ); + + const handleInputBlur = useCallback(() => { + setIsEditing(false); + onValueChange(inputValue); + }, [inputValue, onValueChange]); + + const handleInputChange = useCallback((event: React.ChangeEvent) => { + setInputValue(event.target.value); + }, []); + + useEffect(() => { + if (isEditing && inputRef.current) { + inputRef.current.focus(); + } + }, [isEditing]); + + useEffect(() => { + setInputValue(value); + }, [value]); + + if (isEditing) { + return ( + + ); + } + + return ( + + + {label}: {value || ""} + { + event.stopPropagation(); + onRemove(); + }} + size="xs" + variant="ghost" + > + + + + + ); +}; diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx b/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx index d241ac0cb4bdb..d6717bf7a6645 100644 --- a/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx +++ b/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { Box, Heading, Link, HStack } from "@chakra-ui/react"; +import { Box, Heading, Link } from "@chakra-ui/react"; import type { ColumnDef } from "@tanstack/react-table"; import { useCallback } from "react"; import { useTranslation } from "react-i18next"; @@ -27,10 +27,11 @@ import type { XComResponse } 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 { SearchBar } from "src/components/SearchBar"; import { TruncatedText } from "src/components/TruncatedText"; import { getTaskInstanceLinkFromObj } from "src/utils/links"; +import { FilterManager } from "./FilterManager"; +import type { FilterType } from "./FilterPill"; import { XComEntry } from "./XComEntry"; const columns = (translate: (key: string) => string): Array> => [ @@ -126,61 +127,24 @@ export const XCom = () => { { enabled: !isNaN(pagination.pageSize) }, ); - const handleKeyFilterChange = useCallback( - (value: string) => { - if (value === "") { - searchParams.delete("key"); - } else { - searchParams.set("key", value); - } - setTableURLState({ - pagination: { ...pagination, pageIndex: 0 }, - sorting, - }); - setSearchParams(searchParams); - }, - [pagination, searchParams, setSearchParams, setTableURLState, sorting], - ); + const handleFiltersChange = useCallback( + (filters: Record) => { + Object.keys(filters).forEach((filterType) => { + const value = filters[filterType as FilterType]; - const handleDagIdFilterChange = useCallback( - (value: string) => { - if (value === "") { - searchParams.delete("dag_id"); - } else { - searchParams.set("dag_id", value); - } - setTableURLState({ - pagination: { ...pagination, pageIndex: 0 }, - sorting, + if (value === "") { + searchParams.delete(filterType); + } else { + searchParams.set(filterType, value); + } }); - setSearchParams(searchParams); - }, - [pagination, searchParams, setSearchParams, setTableURLState, sorting], - ); - const handleRunIdFilterChange = useCallback( - (value: string) => { - if (value === "") { - searchParams.delete("run_id"); - } else { - searchParams.set("run_id", value); - } - setTableURLState({ - pagination: { ...pagination, pageIndex: 0 }, - sorting, + ["key", "dag_id", "task_id", "run_id"].forEach((param) => { + if (!(param in filters)) { + searchParams.delete(param); + } }); - setSearchParams(searchParams); - }, - [pagination, searchParams, setSearchParams, setTableURLState, sorting], - ); - const handleTaskIdFilterChange = useCallback( - (value: string) => { - if (value === "") { - searchParams.delete("task_id"); - } else { - searchParams.set("task_id", value); - } setTableURLState({ pagination: { ...pagination, pageIndex: 0 }, sorting, @@ -196,44 +160,17 @@ export const XCom = () => { {translate("xcom.title")} ) : undefined} - - - - - - - - - - - - - - + + + Date: Mon, 4 Aug 2025 02:17:30 +0800 Subject: [PATCH 03/22] chore(ui): modify filterpill appearance --- .../airflow/ui/src/pages/XCom/FilterPill.tsx | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/FilterPill.tsx b/airflow-core/src/airflow/ui/src/pages/XCom/FilterPill.tsx index 104eed646c6bc..fa59ac0b31788 100644 --- a/airflow-core/src/airflow/ui/src/pages/XCom/FilterPill.tsx +++ b/airflow-core/src/airflow/ui/src/pages/XCom/FilterPill.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { Badge, HStack, IconButton, Input } from "@chakra-ui/react"; +import { Box, Button, HStack, IconButton, Input } from "@chakra-ui/react"; import { useCallback, useEffect, useRef, useState } from "react"; import { MdClose } from "react-icons/md"; @@ -87,26 +87,41 @@ export const FilterPill = ({ label, onRemove, onValueChange, value }: FilterPill } return ( - - - {label}: {value || ""} + + + {label}: {value || ""} + + { event.stopPropagation(); onRemove(); }} + rounded="full" size="xs" + transition="all 0.2s" variant="ghost" > - + ); }; From 92dec9f324f4ba1d7a5b3f8618eb744e6a37df9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=96=86=E5=AE=B8?= Date: Mon, 4 Aug 2025 03:43:03 +0800 Subject: [PATCH 04/22] style(ui): Polish appearance of filter components --- .../src/airflow/ui/public/i18n/locales/en/common.json | 1 + .../src/airflow/ui/src/pages/XCom/FilterManager.tsx | 7 ++++++- airflow-core/src/airflow/ui/src/pages/XCom/FilterPill.tsx | 4 +++- 3 files changed, 10 insertions(+), 2 deletions(-) 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 fe8b605dc93d6..251ef36d0757b 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 @@ -91,6 +91,7 @@ "any": "Any", "or": "OR" }, + "filter": "Filter", "logicalDate": "Logical Date", "logout": "Logout", "logoutConfirmation": "You are about to logout from the application.", diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/FilterManager.tsx b/airflow-core/src/airflow/ui/src/pages/XCom/FilterManager.tsx index da1bdd7f02944..4f6d9e6819d47 100644 --- a/airflow-core/src/airflow/ui/src/pages/XCom/FilterManager.tsx +++ b/airflow-core/src/airflow/ui/src/pages/XCom/FilterManager.tsx @@ -136,7 +136,12 @@ export const FilterManager = ({ {availableFilterTypes.length > 0 && ( - diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/FilterPill.tsx b/airflow-core/src/airflow/ui/src/pages/XCom/FilterPill.tsx index fa59ac0b31788..15f84c3622074 100644 --- a/airflow-core/src/airflow/ui/src/pages/XCom/FilterPill.tsx +++ b/airflow-core/src/airflow/ui/src/pages/XCom/FilterPill.tsx @@ -74,6 +74,7 @@ export const FilterPill = ({ label, onRemove, onValueChange, value }: FilterPill if (isEditing) { return ( ); } @@ -95,6 +96,7 @@ export const FilterPill = ({ label, onRemove, onValueChange, value }: FilterPill colorPalette={value ? "blue" : "gray"} cursor="pointer" onClick={handlePillClick} + size="sm" > From 806126fcd0aa0f751a3667e80a212fa3d8795d3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=96=86=E5=AE=B8?= Date: Tue, 5 Aug 2025 22:44:15 +0800 Subject: [PATCH 05/22] feat(api, ui): Add wildcard search to XComs and simplify filter UI --- .../airflow/api_fastapi/common/parameters.py | 6 + .../openapi/v2-rest-api-generated.yaml | 20 +-- .../core_api/routes/public/xcom.py | 8 +- .../airflow/ui/openapi-gen/queries/common.ts | 6 +- .../ui/openapi-gen/queries/ensureQueryData.ts | 8 +- .../ui/openapi-gen/queries/prefetch.ts | 8 +- .../airflow/ui/openapi-gen/queries/queries.ts | 8 +- .../ui/openapi-gen/queries/suspense.ts | 8 +- .../ui/openapi-gen/requests/services.gen.ts | 6 +- .../ui/openapi-gen/requests/types.gen.ts | 5 +- .../ui/public/i18n/locales/en/browse.json | 3 +- .../airflow/ui/src/constants/searchParams.ts | 1 + .../src/airflow/ui/src/pages/XCom/XCom.tsx | 124 ++++++++++++++---- 13 files changed, 145 insertions(+), 66 deletions(-) diff --git a/airflow-core/src/airflow/api_fastapi/common/parameters.py b/airflow-core/src/airflow/api_fastapi/common/parameters.py index 72666135bed93..131121e849435 100644 --- a/airflow-core/src/airflow/api_fastapi/common/parameters.py +++ b/airflow-core/src/airflow/api_fastapi/common/parameters.py @@ -57,6 +57,7 @@ from airflow.models.pool import Pool from airflow.models.taskinstance import TaskInstance from airflow.models.variable import Variable +from airflow.models.xcom import XComModel from airflow.typing_compat import Self from airflow.utils.state import DagRunState, TaskInstanceState from airflow.utils.types import DagRunType @@ -724,6 +725,11 @@ def _transform_ti_states(states: list[str] | None) -> list[TaskInstanceState | N ), ] +# XCom +QueryXComKeyPatternSearch = Annotated[ + _SearchParam, Depends(search_param_factory(XComModel.key, "xcom_key_pattern")) +] + # Assets QueryAssetNamePatternSearch = Annotated[ _SearchParam, Depends(search_param_factory(AssetModel.name, "name_pattern")) 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 fa1ecae844c5c..8626d6032e23e 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 @@ -4606,14 +4606,6 @@ paths: schema: type: string title: Task Id - - name: xcom_key - in: query - required: false - schema: - anyOf: - - type: string - - type: 'null' - title: Xcom Key - name: map_index in: query required: false @@ -4639,6 +4631,18 @@ paths: minimum: 0 default: 0 title: Offset + - name: xcom_key_pattern + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: "SQL LIKE expression \u2014 use `%` / `_` wildcards (e.g. `%customer_%`).\ + \ Regular expressions are **not** supported." + title: Xcom Key Pattern + description: "SQL LIKE expression \u2014 use `%` / `_` wildcards (e.g. `%customer_%`).\ + \ Regular expressions are **not** supported." responses: '200': description: Successful Response diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py index b886ce8f95093..4e0e0a4cd2e5c 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py @@ -26,7 +26,7 @@ from airflow.api_fastapi.auth.managers.models.resource_details import DagAccessEntity from airflow.api_fastapi.common.dagbag import DagBagDep from airflow.api_fastapi.common.db.common import SessionDep, paginated_select -from airflow.api_fastapi.common.parameters import QueryLimit, QueryOffset +from airflow.api_fastapi.common.parameters import QueryLimit, QueryOffset, QueryXComKeyPatternSearch from airflow.api_fastapi.common.router import AirflowRouter from airflow.api_fastapi.core_api.datamodels.xcom import ( XComCollectionResponse, @@ -127,7 +127,7 @@ def get_xcom_entries( offset: QueryOffset, readable_xcom_filter: ReadableXComFilterDep, session: SessionDep, - xcom_key: Annotated[str | None, Query()] = None, + xcom_key_pattern: QueryXComKeyPatternSearch, map_index: Annotated[int | None, Query(ge=-1)] = None, ) -> XComCollectionResponse: """ @@ -148,12 +148,10 @@ def get_xcom_entries( query = query.where(DR.run_id == dag_run_id) if map_index is not None: query = query.where(XComModel.map_index == map_index) - if xcom_key is not None: - query = query.where(XComModel.key == xcom_key) query, total_entries = paginated_select( statement=query, - filters=[readable_xcom_filter], + filters=[readable_xcom_filter, xcom_key_pattern], offset=offset, limit=limit, session=session, diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts index bcf1b4a6f03d1..d7a3ce28823cd 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts @@ -569,15 +569,15 @@ export const UseXcomServiceGetXcomEntryKeyFn = ({ dagId, dagRunId, deserialize, export type XcomServiceGetXcomEntriesDefaultResponse = Awaited>; export type XcomServiceGetXcomEntriesQueryResult = UseQueryResult; export const useXcomServiceGetXcomEntriesKey = "XcomServiceGetXcomEntries"; -export const UseXcomServiceGetXcomEntriesKeyFn = ({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKey }: { +export const UseXcomServiceGetXcomEntriesKeyFn = ({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }: { dagId: string; dagRunId: string; limit?: number; mapIndex?: number; offset?: number; taskId: string; - xcomKey?: string; -}, queryKey?: Array) => [useXcomServiceGetXcomEntriesKey, ...(queryKey ?? [{ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKey }])]; + xcomKeyPattern?: string; +}, queryKey?: Array) => [useXcomServiceGetXcomEntriesKey, ...(queryKey ?? [{ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }])]; export type TaskServiceGetTasksDefaultResponse = Awaited>; export type TaskServiceGetTasksQueryResult = UseQueryResult; export const useTaskServiceGetTasksKey = "TaskServiceGetTasks"; diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts index 0cb2c1b89a548..f5607dbee11d9 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts @@ -1074,22 +1074,22 @@ export const ensureUseXcomServiceGetXcomEntryData = (queryClient: QueryClient, { * @param data.dagId * @param data.dagRunId * @param data.taskId -* @param data.xcomKey * @param data.mapIndex * @param data.limit * @param data.offset +* @param data.xcomKeyPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @returns XComCollectionResponse Successful Response * @throws ApiError */ -export const ensureUseXcomServiceGetXcomEntriesData = (queryClient: QueryClient, { dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKey }: { +export const ensureUseXcomServiceGetXcomEntriesData = (queryClient: QueryClient, { dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }: { dagId: string; dagRunId: string; limit?: number; mapIndex?: number; offset?: number; taskId: string; - xcomKey?: string; -}) => queryClient.ensureQueryData({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKey }), queryFn: () => XcomService.getXcomEntries({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKey }) }); + xcomKeyPattern?: string; +}) => queryClient.ensureQueryData({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }), queryFn: () => XcomService.getXcomEntries({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }) }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts index 47db9950f9ddc..45904c82c900f 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts @@ -1074,22 +1074,22 @@ export const prefetchUseXcomServiceGetXcomEntry = (queryClient: QueryClient, { d * @param data.dagId * @param data.dagRunId * @param data.taskId -* @param data.xcomKey * @param data.mapIndex * @param data.limit * @param data.offset +* @param data.xcomKeyPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @returns XComCollectionResponse Successful Response * @throws ApiError */ -export const prefetchUseXcomServiceGetXcomEntries = (queryClient: QueryClient, { dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKey }: { +export const prefetchUseXcomServiceGetXcomEntries = (queryClient: QueryClient, { dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }: { dagId: string; dagRunId: string; limit?: number; mapIndex?: number; offset?: number; taskId: string; - xcomKey?: string; -}) => queryClient.prefetchQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKey }), queryFn: () => XcomService.getXcomEntries({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKey }) }); + xcomKeyPattern?: string; +}) => queryClient.prefetchQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }), queryFn: () => XcomService.getXcomEntries({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }) }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts index ced5cf635f5a6..9cd86bf573a33 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts @@ -1074,22 +1074,22 @@ export const useXcomServiceGetXcomEntry = = unknown[]>({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKey }: { +export const useXcomServiceGetXcomEntries = = unknown[]>({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }: { dagId: string; dagRunId: string; limit?: number; mapIndex?: number; offset?: number; taskId: string; - xcomKey?: string; -}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKey }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKey }) as TData, ...options }); + xcomKeyPattern?: string; +}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }) as TData, ...options }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts index ce1a1a8c473b3..bdce8741fbe4c 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts @@ -1074,22 +1074,22 @@ export const useXcomServiceGetXcomEntrySuspense = = unknown[]>({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKey }: { +export const useXcomServiceGetXcomEntriesSuspense = = unknown[]>({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }: { dagId: string; dagRunId: string; limit?: number; mapIndex?: number; offset?: number; taskId: string; - xcomKey?: string; -}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKey }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKey }) as TData, ...options }); + xcomKeyPattern?: string; +}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }) as TData, ...options }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts b/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts index 6a1b619e7d303..1a20cdd7e1807 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts @@ -3004,10 +3004,10 @@ export class XcomService { * @param data.dagId * @param data.dagRunId * @param data.taskId - * @param data.xcomKey * @param data.mapIndex * @param data.limit * @param data.offset + * @param data.xcomKeyPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @returns XComCollectionResponse Successful Response * @throws ApiError */ @@ -3021,10 +3021,10 @@ export class XcomService { task_id: data.taskId }, query: { - xcom_key: data.xcomKey, map_index: data.mapIndex, limit: data.limit, - offset: data.offset + offset: data.offset, + xcom_key_pattern: data.xcomKeyPattern }, errors: { 400: 'Bad Request', 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 563b35ca3d753..9d03f2646a0bc 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 @@ -2805,7 +2805,10 @@ export type GetXcomEntriesData = { mapIndex?: number | null; offset?: number; taskId: string; - xcomKey?: string | null; + /** + * SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. + */ + xcomKeyPattern?: string | null; }; export type GetXcomEntriesResponse = XComCollectionResponse; 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 6ede12d44bc14..1256734086278 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 @@ -20,10 +20,11 @@ }, "filters": { "dagFilter": "Filter by Dag ID", - "keyFilter": "Filter by XCom key", + "keyFilter": "Filter by XCom key (use % for wildcards)", "runIdFilter": "Filter by Run ID", "taskIdFilter": "Filter by Task ID" }, + "searchPlaceholder": "Search XCom keys...", "title": "XCom" } } diff --git a/airflow-core/src/airflow/ui/src/constants/searchParams.ts b/airflow-core/src/airflow/ui/src/constants/searchParams.ts index e5e05cec60060..99590d1ab52df 100644 --- a/airflow-core/src/airflow/ui/src/constants/searchParams.ts +++ b/airflow-core/src/airflow/ui/src/constants/searchParams.ts @@ -20,6 +20,7 @@ export enum SearchParamsKeys { DEPENDENCIES = "dependencies", END_DATE = "end_date", FAVORITE = "favorite", + KEY_PATTERN = "key_pattern", LAST_DAG_RUN_STATE = "last_dag_run_state", LIMIT = "limit", LOG_LEVEL = "log_level", diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx b/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx index d6717bf7a6645..708e81565e0fa 100644 --- a/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx +++ b/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { Box, Heading, Link } from "@chakra-ui/react"; +import { Box, Heading, Link, HStack } from "@chakra-ui/react"; import type { ColumnDef } from "@tanstack/react-table"; import { useCallback } from "react"; import { useTranslation } from "react-i18next"; @@ -27,13 +27,15 @@ import type { XComResponse } 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 { SearchBar } from "src/components/SearchBar"; import { TruncatedText } from "src/components/TruncatedText"; +import { SearchParamsKeys, type SearchParamsKeysType } from "src/constants/searchParams"; import { getTaskInstanceLinkFromObj } from "src/utils/links"; -import { FilterManager } from "./FilterManager"; -import type { FilterType } from "./FilterPill"; import { XComEntry } from "./XComEntry"; +const { KEY_PATTERN: KEY_PATTERN_PARAM }: SearchParamsKeysType = SearchParamsKeys; + const columns = (translate: (key: string) => string): Array> => [ { accessorKey: "key", @@ -108,7 +110,7 @@ export const XCom = () => { const { pagination, sorting } = tableURLState; const [searchParams, setSearchParams] = useSearchParams(); - const filteredKey = searchParams.get("key"); + const filteredKey = searchParams.get(KEY_PATTERN_PARAM); const filteredDagId = searchParams.get("dag_id"); const filteredRunId = searchParams.get("run_id"); const filteredTaskId = searchParams.get("task_id"); @@ -121,30 +123,67 @@ export const XCom = () => { mapIndex: mapIndex === "-1" ? undefined : parseInt(mapIndex, 10), offset: pagination.pageIndex * pagination.pageSize, taskId: filteredTaskId ?? taskId, - xcomKey: filteredKey ?? undefined, + xcomKeyPattern: filteredKey ?? undefined, }, undefined, { enabled: !isNaN(pagination.pageSize) }, ); - const handleFiltersChange = useCallback( - (filters: Record) => { - Object.keys(filters).forEach((filterType) => { - const value = filters[filterType as FilterType]; + const handleKeyFilterChange = useCallback( + (value: string) => { + if (value === "") { + searchParams.delete(KEY_PATTERN_PARAM); + } else { + searchParams.set(KEY_PATTERN_PARAM, value); + } + setTableURLState({ + pagination: { ...pagination, pageIndex: 0 }, + sorting, + }); + setSearchParams(searchParams); + }, + [pagination, searchParams, setSearchParams, setTableURLState, sorting], + ); - if (value === "") { - searchParams.delete(filterType); - } else { - searchParams.set(filterType, value); - } + const handleDagIdFilterChange = useCallback( + (value: string) => { + if (value === "") { + searchParams.delete("dag_id"); + } else { + searchParams.set("dag_id", value); + } + setTableURLState({ + pagination: { ...pagination, pageIndex: 0 }, + sorting, }); + setSearchParams(searchParams); + }, + [pagination, searchParams, setSearchParams, setTableURLState, sorting], + ); - ["key", "dag_id", "task_id", "run_id"].forEach((param) => { - if (!(param in filters)) { - searchParams.delete(param); - } + const handleRunIdFilterChange = useCallback( + (value: string) => { + if (value === "") { + searchParams.delete("run_id"); + } else { + searchParams.set("run_id", value); + } + setTableURLState({ + pagination: { ...pagination, pageIndex: 0 }, + sorting, }); + setSearchParams(searchParams); + }, + [pagination, searchParams, setSearchParams, setTableURLState, sorting], + ); + const handleTaskIdFilterChange = useCallback( + (value: string) => { + if (value === "") { + searchParams.delete("task_id"); + } else { + searchParams.set("task_id", value); + } setTableURLState({ pagination: { ...pagination, pageIndex: 0 }, sorting, @@ -160,17 +199,44 @@ export const XCom = () => { {translate("xcom.title")} ) : undefined} - - - + + + + + + + + + + + + + + Date: Tue, 5 Aug 2025 23:41:38 +0800 Subject: [PATCH 06/22] feat(api): Add dag, run, task wildcard search to XComs fix(api): Refactor XCom entries filter fix(api): Correct XCom endpoint filtering --- .../airflow/api_fastapi/common/parameters.py | 9 ++ .../openapi/v2-rest-api-generated.yaml | 36 +++++ .../core_api/routes/public/xcom.py | 22 ++- .../airflow/ui/openapi-gen/queries/common.ts | 7 +- .../ui/openapi-gen/queries/ensureQueryData.ts | 10 +- .../ui/openapi-gen/queries/prefetch.ts | 10 +- .../airflow/ui/openapi-gen/queries/queries.ts | 10 +- .../ui/openapi-gen/queries/suspense.ts | 10 +- .../ui/openapi-gen/requests/services.gen.ts | 8 +- .../ui/openapi-gen/requests/types.gen.ts | 12 ++ .../airflow/ui/src/constants/searchParams.ts | 2 + .../src/airflow/ui/src/pages/XCom/XCom.tsx | 134 +++--------------- .../airflow/ui/src/pages/XCom/XComFilters.tsx | 107 ++++++++++++++ 13 files changed, 250 insertions(+), 127 deletions(-) create mode 100644 airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx diff --git a/airflow-core/src/airflow/api_fastapi/common/parameters.py b/airflow-core/src/airflow/api_fastapi/common/parameters.py index 131121e849435..101816054d525 100644 --- a/airflow-core/src/airflow/api_fastapi/common/parameters.py +++ b/airflow-core/src/airflow/api_fastapi/common/parameters.py @@ -729,6 +729,15 @@ def _transform_ti_states(states: list[str] | None) -> list[TaskInstanceState | N QueryXComKeyPatternSearch = Annotated[ _SearchParam, Depends(search_param_factory(XComModel.key, "xcom_key_pattern")) ] +QueryXComDagIdPatternSearch = Annotated[ + _SearchParam, Depends(search_param_factory(XComModel.dag_id, "dag_id_pattern")) +] +QueryXComRunIdPatternSearch = Annotated[ + _SearchParam, Depends(search_param_factory(XComModel.run_id, "run_id_pattern")) +] +QueryXComTaskIdPatternSearch = Annotated[ + _SearchParam, Depends(search_param_factory(XComModel.task_id, "task_id_pattern")) +] # Assets QueryAssetNamePatternSearch = Annotated[ 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 8626d6032e23e..147728162d9f4 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 @@ -4643,6 +4643,42 @@ paths: title: Xcom Key Pattern description: "SQL LIKE expression \u2014 use `%` / `_` wildcards (e.g. `%customer_%`).\ \ Regular expressions are **not** supported." + - name: dag_id_pattern + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: "SQL LIKE expression \u2014 use `%` / `_` wildcards (e.g. `%customer_%`).\ + \ Regular expressions are **not** supported." + title: Dag Id Pattern + description: "SQL LIKE expression \u2014 use `%` / `_` wildcards (e.g. `%customer_%`).\ + \ Regular expressions are **not** supported." + - name: run_id_pattern + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: "SQL LIKE expression \u2014 use `%` / `_` wildcards (e.g. `%customer_%`).\ + \ Regular expressions are **not** supported." + title: Run Id Pattern + description: "SQL LIKE expression \u2014 use `%` / `_` wildcards (e.g. `%customer_%`).\ + \ Regular expressions are **not** supported." + - name: task_id_pattern + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: "SQL LIKE expression \u2014 use `%` / `_` wildcards (e.g. `%customer_%`).\ + \ Regular expressions are **not** supported." + title: Task Id Pattern + description: "SQL LIKE expression \u2014 use `%` / `_` wildcards (e.g. `%customer_%`).\ + \ Regular expressions are **not** supported." responses: '200': description: Successful Response diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py index 4e0e0a4cd2e5c..5cd6fa813f678 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py @@ -26,7 +26,14 @@ from airflow.api_fastapi.auth.managers.models.resource_details import DagAccessEntity from airflow.api_fastapi.common.dagbag import DagBagDep from airflow.api_fastapi.common.db.common import SessionDep, paginated_select -from airflow.api_fastapi.common.parameters import QueryLimit, QueryOffset, QueryXComKeyPatternSearch +from airflow.api_fastapi.common.parameters import ( + QueryLimit, + QueryOffset, + QueryXComDagIdPatternSearch, + QueryXComKeyPatternSearch, + QueryXComRunIdPatternSearch, + QueryXComTaskIdPatternSearch, +) from airflow.api_fastapi.common.router import AirflowRouter from airflow.api_fastapi.core_api.datamodels.xcom import ( XComCollectionResponse, @@ -128,6 +135,9 @@ def get_xcom_entries( readable_xcom_filter: ReadableXComFilterDep, session: SessionDep, xcom_key_pattern: QueryXComKeyPatternSearch, + dag_id_pattern: QueryXComDagIdPatternSearch, + run_id_pattern: QueryXComRunIdPatternSearch, + task_id_pattern: QueryXComTaskIdPatternSearch, map_index: Annotated[int | None, Query(ge=-1)] = None, ) -> XComCollectionResponse: """ @@ -144,14 +154,22 @@ def get_xcom_entries( if task_id != "~": query = query.where(XComModel.task_id == task_id) + if dag_run_id != "~": query = query.where(DR.run_id == dag_run_id) + if map_index is not None: query = query.where(XComModel.map_index == map_index) query, total_entries = paginated_select( statement=query, - filters=[readable_xcom_filter, xcom_key_pattern], + filters=[ + readable_xcom_filter, + xcom_key_pattern, + dag_id_pattern, + run_id_pattern, + task_id_pattern, + ], offset=offset, limit=limit, session=session, diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts index d7a3ce28823cd..389dd243ed524 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts @@ -569,15 +569,18 @@ export const UseXcomServiceGetXcomEntryKeyFn = ({ dagId, dagRunId, deserialize, export type XcomServiceGetXcomEntriesDefaultResponse = Awaited>; export type XcomServiceGetXcomEntriesQueryResult = UseQueryResult; export const useXcomServiceGetXcomEntriesKey = "XcomServiceGetXcomEntries"; -export const UseXcomServiceGetXcomEntriesKeyFn = ({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }: { +export const UseXcomServiceGetXcomEntriesKeyFn = ({ dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { dagId: string; + dagIdPattern?: string; dagRunId: string; limit?: number; mapIndex?: number; offset?: number; + runIdPattern?: string; taskId: string; + taskIdPattern?: string; xcomKeyPattern?: string; -}, queryKey?: Array) => [useXcomServiceGetXcomEntriesKey, ...(queryKey ?? [{ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }])]; +}, queryKey?: Array) => [useXcomServiceGetXcomEntriesKey, ...(queryKey ?? [{ dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }])]; export type TaskServiceGetTasksDefaultResponse = Awaited>; export type TaskServiceGetTasksQueryResult = UseQueryResult; export const useTaskServiceGetTasksKey = "TaskServiceGetTasks"; diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts index f5607dbee11d9..c88048c3245c4 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts @@ -1078,18 +1078,24 @@ export const ensureUseXcomServiceGetXcomEntryData = (queryClient: QueryClient, { * @param data.limit * @param data.offset * @param data.xcomKeyPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. +* @param data.dagIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. +* @param data.runIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. +* @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @returns XComCollectionResponse Successful Response * @throws ApiError */ -export const ensureUseXcomServiceGetXcomEntriesData = (queryClient: QueryClient, { dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }: { +export const ensureUseXcomServiceGetXcomEntriesData = (queryClient: QueryClient, { dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { dagId: string; + dagIdPattern?: string; dagRunId: string; limit?: number; mapIndex?: number; offset?: number; + runIdPattern?: string; taskId: string; + taskIdPattern?: string; xcomKeyPattern?: string; -}) => queryClient.ensureQueryData({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }), queryFn: () => XcomService.getXcomEntries({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }) }); +}) => queryClient.ensureQueryData({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }), queryFn: () => XcomService.getXcomEntries({ dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts index 45904c82c900f..568fcdfe0452d 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts @@ -1078,18 +1078,24 @@ export const prefetchUseXcomServiceGetXcomEntry = (queryClient: QueryClient, { d * @param data.limit * @param data.offset * @param data.xcomKeyPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. +* @param data.dagIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. +* @param data.runIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. +* @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @returns XComCollectionResponse Successful Response * @throws ApiError */ -export const prefetchUseXcomServiceGetXcomEntries = (queryClient: QueryClient, { dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }: { +export const prefetchUseXcomServiceGetXcomEntries = (queryClient: QueryClient, { dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { dagId: string; + dagIdPattern?: string; dagRunId: string; limit?: number; mapIndex?: number; offset?: number; + runIdPattern?: string; taskId: string; + taskIdPattern?: string; xcomKeyPattern?: string; -}) => queryClient.prefetchQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }), queryFn: () => XcomService.getXcomEntries({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }) }); +}) => queryClient.prefetchQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }), queryFn: () => XcomService.getXcomEntries({ dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts index 9cd86bf573a33..0174091a34134 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts @@ -1078,18 +1078,24 @@ export const useXcomServiceGetXcomEntry = = unknown[]>({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }: { +export const useXcomServiceGetXcomEntries = = unknown[]>({ dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { dagId: string; + dagIdPattern?: string; dagRunId: string; limit?: number; mapIndex?: number; offset?: number; + runIdPattern?: string; taskId: string; + taskIdPattern?: string; xcomKeyPattern?: string; -}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }) as TData, ...options }); +}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) as TData, ...options }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts index bdce8741fbe4c..2d7fe881eb46e 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts @@ -1078,18 +1078,24 @@ export const useXcomServiceGetXcomEntrySuspense = = unknown[]>({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }: { +export const useXcomServiceGetXcomEntriesSuspense = = unknown[]>({ dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { dagId: string; + dagIdPattern?: string; dagRunId: string; limit?: number; mapIndex?: number; offset?: number; + runIdPattern?: string; taskId: string; + taskIdPattern?: string; xcomKeyPattern?: string; -}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagId, dagRunId, limit, mapIndex, offset, taskId, xcomKeyPattern }) as TData, ...options }); +}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) as TData, ...options }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts b/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts index 1a20cdd7e1807..84fe5b888983b 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts @@ -3008,6 +3008,9 @@ export class XcomService { * @param data.limit * @param data.offset * @param data.xcomKeyPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. + * @param data.dagIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. + * @param data.runIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. + * @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @returns XComCollectionResponse Successful Response * @throws ApiError */ @@ -3024,7 +3027,10 @@ export class XcomService { map_index: data.mapIndex, limit: data.limit, offset: data.offset, - xcom_key_pattern: data.xcomKeyPattern + xcom_key_pattern: data.xcomKeyPattern, + dag_id_pattern: data.dagIdPattern, + run_id_pattern: data.runIdPattern, + task_id_pattern: data.taskIdPattern }, errors: { 400: 'Bad Request', 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 9d03f2646a0bc..3f32086c436c6 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 @@ -2800,11 +2800,23 @@ export type UpdateXcomEntryResponse = XComResponseNative; export type GetXcomEntriesData = { dagId: string; + /** + * SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. + */ + dagIdPattern?: string | null; dagRunId: string; limit?: number; mapIndex?: number | null; offset?: number; + /** + * SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. + */ + runIdPattern?: string | null; taskId: string; + /** + * SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. + */ + taskIdPattern?: string | null; /** * SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. */ diff --git a/airflow-core/src/airflow/ui/src/constants/searchParams.ts b/airflow-core/src/airflow/ui/src/constants/searchParams.ts index 99590d1ab52df..5b76b2f651938 100644 --- a/airflow-core/src/airflow/ui/src/constants/searchParams.ts +++ b/airflow-core/src/airflow/ui/src/constants/searchParams.ts @@ -17,6 +17,7 @@ * under the License. */ export enum SearchParamsKeys { + DAG_ID_PATTERN = "dag_id_pattern", DEPENDENCIES = "dependencies", END_DATE = "end_date", FAVORITE = "favorite", @@ -37,6 +38,7 @@ export enum SearchParamsKeys { STATE = "state", TAGS = "tags", TAGS_MATCH_MODE = "tags_match_mode", + TASK_ID_PATTERN = "task_id_pattern", TRY_NUMBER = "try_number", VERSION_NUMBER = "version_number", } diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx b/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx index 708e81565e0fa..5f1a14f96529e 100644 --- a/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx +++ b/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx @@ -16,9 +16,8 @@ * specific language governing permissions and limitations * under the License. */ -import { Box, Heading, Link, HStack } from "@chakra-ui/react"; +import { Box, Heading, Link } from "@chakra-ui/react"; import type { ColumnDef } from "@tanstack/react-table"; -import { useCallback } from "react"; import { useTranslation } from "react-i18next"; import { Link as RouterLink, useParams, useSearchParams } from "react-router-dom"; @@ -27,14 +26,19 @@ import type { XComResponse } 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 { SearchBar } from "src/components/SearchBar"; import { TruncatedText } from "src/components/TruncatedText"; import { SearchParamsKeys, type SearchParamsKeysType } from "src/constants/searchParams"; import { getTaskInstanceLinkFromObj } from "src/utils/links"; import { XComEntry } from "./XComEntry"; +import { XComFilters } from "./XComFilters"; -const { KEY_PATTERN: KEY_PATTERN_PARAM }: SearchParamsKeysType = SearchParamsKeys; +const { + DAG_ID_PATTERN: DAG_ID_PATTERN_PARAM, + KEY_PATTERN: KEY_PATTERN_PARAM, + RUN_ID_PATTERN: RUN_ID_PATTERN_PARAM, + TASK_ID_PATTERN: TASK_ID_PATTERN_PARAM, +}: SearchParamsKeysType = SearchParamsKeys; const columns = (translate: (key: string) => string): Array> => [ { @@ -107,136 +111,38 @@ export const XCom = () => { const { dagId = "~", mapIndex = "-1", runId = "~", taskId = "~" } = useParams(); const { t: translate } = useTranslation(["browse", "common"]); const { setTableURLState, tableURLState } = useTableURLState(); - const { pagination, sorting } = tableURLState; - const [searchParams, setSearchParams] = useSearchParams(); + const { pagination } = tableURLState; + const [searchParams] = useSearchParams(); const filteredKey = searchParams.get(KEY_PATTERN_PARAM); - const filteredDagId = searchParams.get("dag_id"); - const filteredRunId = searchParams.get("run_id"); - const filteredTaskId = searchParams.get("task_id"); + const filteredDagId = searchParams.get(DAG_ID_PATTERN_PARAM); + const filteredRunId = searchParams.get(RUN_ID_PATTERN_PARAM); + const filteredTaskId = searchParams.get(TASK_ID_PATTERN_PARAM); const { data, error, isFetching, isLoading } = useXcomServiceGetXcomEntries( { - dagId: filteredDagId ?? dagId, - dagRunId: filteredRunId ?? runId, + dagId: filteredDagId !== null && filteredDagId !== "" ? "~" : dagId, + dagIdPattern: filteredDagId ?? undefined, + dagRunId: filteredRunId !== null && filteredRunId !== "" ? "~" : runId, limit: pagination.pageSize, mapIndex: mapIndex === "-1" ? undefined : parseInt(mapIndex, 10), offset: pagination.pageIndex * pagination.pageSize, - taskId: filteredTaskId ?? taskId, + runIdPattern: filteredRunId ?? undefined, + taskId: filteredTaskId !== null && filteredTaskId !== "" ? "~" : taskId, + taskIdPattern: filteredTaskId ?? undefined, xcomKeyPattern: filteredKey ?? undefined, }, undefined, { enabled: !isNaN(pagination.pageSize) }, ); - const handleKeyFilterChange = useCallback( - (value: string) => { - if (value === "") { - searchParams.delete(KEY_PATTERN_PARAM); - } else { - searchParams.set(KEY_PATTERN_PARAM, value); - } - setTableURLState({ - pagination: { ...pagination, pageIndex: 0 }, - sorting, - }); - setSearchParams(searchParams); - }, - [pagination, searchParams, setSearchParams, setTableURLState, sorting], - ); - - const handleDagIdFilterChange = useCallback( - (value: string) => { - if (value === "") { - searchParams.delete("dag_id"); - } else { - searchParams.set("dag_id", value); - } - setTableURLState({ - pagination: { ...pagination, pageIndex: 0 }, - sorting, - }); - setSearchParams(searchParams); - }, - [pagination, searchParams, setSearchParams, setTableURLState, sorting], - ); - - const handleRunIdFilterChange = useCallback( - (value: string) => { - if (value === "") { - searchParams.delete("run_id"); - } else { - searchParams.set("run_id", value); - } - setTableURLState({ - pagination: { ...pagination, pageIndex: 0 }, - sorting, - }); - setSearchParams(searchParams); - }, - [pagination, searchParams, setSearchParams, setTableURLState, sorting], - ); - - const handleTaskIdFilterChange = useCallback( - (value: string) => { - if (value === "") { - searchParams.delete("task_id"); - } else { - searchParams.set("task_id", value); - } - setTableURLState({ - pagination: { ...pagination, pageIndex: 0 }, - sorting, - }); - setSearchParams(searchParams); - }, - [pagination, searchParams, setSearchParams, setTableURLState, sorting], - ); - return ( {dagId === "~" && runId === "~" && taskId === "~" ? ( {translate("xcom.title")} ) : undefined} - - - - - - - - - - - - - - + { + const [searchParams, setSearchParams] = useSearchParams(); + const { setTableURLState, tableURLState } = useTableURLState(); + const { pagination, sorting } = tableURLState; + const { t: translate } = useTranslation(["browse"]); + + const keyPattern = searchParams.get(KEY_PATTERN_PARAM); + const dagIdPattern = searchParams.get(DAG_ID_PATTERN_PARAM); + const runIdPattern = searchParams.get(RUN_ID_PATTERN_PARAM); + const taskIdPattern = searchParams.get(TASK_ID_PATTERN_PARAM); + + const createFilterHandler = useCallback( + (paramKey: string) => (value: string) => { + if (value === "") { + searchParams.delete(paramKey); + } else { + searchParams.set(paramKey, value); + } + setTableURLState({ + pagination: { ...pagination, pageIndex: 0 }, + sorting, + }); + setSearchParams(searchParams); + }, + [pagination, searchParams, setSearchParams, setTableURLState, sorting], + ); + + const handleKeyFilterChange = createFilterHandler(KEY_PATTERN_PARAM); + const handleDagIdFilterChange = createFilterHandler(DAG_ID_PATTERN_PARAM); + const handleRunIdFilterChange = createFilterHandler(RUN_ID_PATTERN_PARAM); + const handleTaskIdFilterChange = createFilterHandler(TASK_ID_PATTERN_PARAM); + + return ( + + + + + + + + + + + + + + + ); +}; From 37472f9fa612c114909afce11f8594bdf8eb7124 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=96=86=E5=AE=B8?= Date: Wed, 6 Aug 2025 05:48:55 +0800 Subject: [PATCH 07/22] refactor(ui): Simplify XCom filters --- .../ui/public/i18n/locales/en/browse.json | 3 +- .../ui/public/i18n/locales/en/common.json | 1 - .../ui/src/pages/XCom/FilterManager.tsx | 160 ------------------ .../airflow/ui/src/pages/XCom/FilterPill.tsx | 129 -------------- .../src/airflow/ui/src/pages/XCom/XCom.tsx | 6 +- .../airflow/ui/src/pages/XCom/XComFilters.tsx | 71 ++------ 6 files changed, 22 insertions(+), 348 deletions(-) delete mode 100644 airflow-core/src/airflow/ui/src/pages/XCom/FilterManager.tsx delete mode 100644 airflow-core/src/airflow/ui/src/pages/XCom/FilterPill.tsx 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 1256734086278..6ede12d44bc14 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 @@ -20,11 +20,10 @@ }, "filters": { "dagFilter": "Filter by Dag ID", - "keyFilter": "Filter by XCom key (use % for wildcards)", + "keyFilter": "Filter by XCom key", "runIdFilter": "Filter by Run ID", "taskIdFilter": "Filter by Task ID" }, - "searchPlaceholder": "Search XCom keys...", "title": "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 251ef36d0757b..fe8b605dc93d6 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 @@ -91,7 +91,6 @@ "any": "Any", "or": "OR" }, - "filter": "Filter", "logicalDate": "Logical Date", "logout": "Logout", "logoutConfirmation": "You are about to logout from the application.", diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/FilterManager.tsx b/airflow-core/src/airflow/ui/src/pages/XCom/FilterManager.tsx deleted file mode 100644 index 4f6d9e6819d47..0000000000000 --- a/airflow-core/src/airflow/ui/src/pages/XCom/FilterManager.tsx +++ /dev/null @@ -1,160 +0,0 @@ -/*! - * 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 { Button, HStack } from "@chakra-ui/react"; -import { useCallback, useState } from "react"; -import { useTranslation } from "react-i18next"; -import { MdAdd } from "react-icons/md"; - -import { Menu } from "src/components/ui"; - -import { FilterPill, type FilterType } from "./FilterPill"; - -type FilterState = { - id: string; - type: FilterType; - value: string; -}; - -type FilterManagerProps = { - readonly initialFilters?: Record; - readonly onFiltersChange: (filters: Record) => void; -}; - -const FILTER_LABELS: Record = { - dag_id: "DAG ID", - key: "Key", - run_id: "Run ID", - task_id: "Task ID", -}; - -const defaultInitialFilters: Record = { - dag_id: "", - key: "", - run_id: "", - task_id: "", -}; - -export const FilterManager = ({ - initialFilters = defaultInitialFilters, - onFiltersChange, -}: FilterManagerProps) => { - const { t: translate } = useTranslation(); - const [filters, setFilters] = useState>(() => - Object.entries(initialFilters) - .filter(([, value]) => value !== "") - .map(([type, value]) => ({ - id: `${type}-${Date.now()}`, - type: type as FilterType, - value, - })), - ); - - const addFilter = useCallback((filterType: FilterType) => { - const newFilter: FilterState = { - id: `${filterType}-${Date.now()}`, - type: filterType, - value: "", - }; - - setFilters((previous) => [...previous, newFilter]); - }, []); - - const updateFilter = useCallback( - (id: string, value: string) => { - setFilters((previous) => previous.map((filter) => (filter.id === id ? { ...filter, value } : filter))); - - const updatedFilters = filters.reduce( - (accumulator, filter) => { - accumulator[filter.type] = filter.id === id ? value : filter.value; - - return accumulator; - }, - {} as Record, - ); - - onFiltersChange(updatedFilters); - }, - [filters, onFiltersChange], - ); - - const removeFilter = useCallback( - (id: string) => { - const filterToRemove = filters.find((filter) => filter.id === id); - - setFilters((previous) => previous.filter((filter) => filter.id !== id)); - - if (filterToRemove) { - const updatedFilters = filters - .filter((filter) => filter.id !== id) - .reduce( - (accumulator, filter) => { - accumulator[filter.type] = filter.value; - - return accumulator; - }, - {} as Record, - ); - - onFiltersChange(updatedFilters); - } - }, - [filters, onFiltersChange], - ); - - const availableFilterTypes = Object.keys(FILTER_LABELS).filter( - (type) => !filters.some((filter) => filter.type === type), - ) as Array; - - return ( - - {filters.map((filter) => ( - removeFilter(filter.id)} - onValueChange={(value) => updateFilter(filter.id, value)} - value={filter.value} - /> - ))} - {availableFilterTypes.length > 0 && ( - - - - - - {availableFilterTypes.map((filterType) => ( - addFilter(filterType)} value={filterType}> - {FILTER_LABELS[filterType]} - - ))} - - - )} - - ); -}; diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/FilterPill.tsx b/airflow-core/src/airflow/ui/src/pages/XCom/FilterPill.tsx deleted file mode 100644 index 15f84c3622074..0000000000000 --- a/airflow-core/src/airflow/ui/src/pages/XCom/FilterPill.tsx +++ /dev/null @@ -1,129 +0,0 @@ -/*! - * 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 { Box, Button, HStack, IconButton, Input } from "@chakra-ui/react"; -import { useCallback, useEffect, useRef, useState } from "react"; -import { MdClose } from "react-icons/md"; - -export type FilterType = "dag_id" | "key" | "run_id" | "task_id"; - -type FilterPillProps = { - readonly filterType: FilterType; - readonly label: string; - readonly onRemove: () => void; - readonly onValueChange: (value: string) => void; - readonly value: string; -}; - -export const FilterPill = ({ label, onRemove, onValueChange, value }: FilterPillProps) => { - const [isEditing, setIsEditing] = useState(value === ""); - const [inputValue, setInputValue] = useState(value); - const inputRef = useRef(null); - - const handlePillClick = useCallback(() => { - setIsEditing(true); - }, []); - - const handleInputKeyDown = useCallback( - (event: React.KeyboardEvent) => { - if (event.key === "Enter") { - setIsEditing(false); - onValueChange(inputValue); - } else if (event.key === "Escape") { - setInputValue(value); - setIsEditing(false); - } - }, - [inputValue, onValueChange, value], - ); - - const handleInputBlur = useCallback(() => { - setIsEditing(false); - onValueChange(inputValue); - }, [inputValue, onValueChange]); - - const handleInputChange = useCallback((event: React.ChangeEvent) => { - setInputValue(event.target.value); - }, []); - - useEffect(() => { - if (isEditing && inputRef.current) { - inputRef.current.focus(); - } - }, [isEditing]); - - useEffect(() => { - setInputValue(value); - }, [value]); - - if (isEditing) { - return ( - - ); - } - - return ( - - ); -}; diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx b/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx index 5f1a14f96529e..933a276cc663c 100644 --- a/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx +++ b/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx @@ -121,14 +121,14 @@ export const XCom = () => { const { data, error, isFetching, isLoading } = useXcomServiceGetXcomEntries( { - dagId: filteredDagId !== null && filteredDagId !== "" ? "~" : dagId, + dagId, dagIdPattern: filteredDagId ?? undefined, - dagRunId: filteredRunId !== null && filteredRunId !== "" ? "~" : runId, + dagRunId: runId, limit: pagination.pageSize, mapIndex: mapIndex === "-1" ? undefined : parseInt(mapIndex, 10), offset: pagination.pageIndex * pagination.pageSize, runIdPattern: filteredRunId ?? undefined, - taskId: filteredTaskId !== null && filteredTaskId !== "" ? "~" : taskId, + taskId, taskIdPattern: filteredTaskId ?? undefined, xcomKeyPattern: filteredKey ?? undefined, }, diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx b/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx index 70385f04afb0c..075f2bdab9c9d 100644 --- a/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx +++ b/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx @@ -23,14 +23,14 @@ import { useSearchParams } from "react-router-dom"; import { useTableURLState } from "src/components/DataTable/useTableUrlState"; import { SearchBar } from "src/components/SearchBar"; -import { SearchParamsKeys, type SearchParamsKeysType } from "src/constants/searchParams"; +import { SearchParamsKeys } from "src/constants/searchParams"; -const { - DAG_ID_PATTERN: DAG_ID_PATTERN_PARAM, - KEY_PATTERN: KEY_PATTERN_PARAM, - RUN_ID_PATTERN: RUN_ID_PATTERN_PARAM, - TASK_ID_PATTERN: TASK_ID_PATTERN_PARAM, -}: SearchParamsKeysType = SearchParamsKeys; +const FILTERS = [ + { hotkeyDisabled: false, key: SearchParamsKeys.KEY_PATTERN, translationKey: "keyFilter" }, + { hotkeyDisabled: true, key: SearchParamsKeys.DAG_ID_PATTERN, translationKey: "dagFilter" }, + { hotkeyDisabled: true, key: SearchParamsKeys.RUN_ID_PATTERN, translationKey: "runIdFilter" }, + { hotkeyDisabled: true, key: SearchParamsKeys.TASK_ID_PATTERN, translationKey: "taskIdFilter" }, +] as const; export const XComFilters = () => { const [searchParams, setSearchParams] = useSearchParams(); @@ -38,11 +38,6 @@ export const XComFilters = () => { const { pagination, sorting } = tableURLState; const { t: translate } = useTranslation(["browse"]); - const keyPattern = searchParams.get(KEY_PATTERN_PARAM); - const dagIdPattern = searchParams.get(DAG_ID_PATTERN_PARAM); - const runIdPattern = searchParams.get(RUN_ID_PATTERN_PARAM); - const taskIdPattern = searchParams.get(TASK_ID_PATTERN_PARAM); - const createFilterHandler = useCallback( (paramKey: string) => (value: string) => { if (value === "") { @@ -59,49 +54,19 @@ export const XComFilters = () => { [pagination, searchParams, setSearchParams, setTableURLState, sorting], ); - const handleKeyFilterChange = createFilterHandler(KEY_PATTERN_PARAM); - const handleDagIdFilterChange = createFilterHandler(DAG_ID_PATTERN_PARAM); - const handleRunIdFilterChange = createFilterHandler(RUN_ID_PATTERN_PARAM); - const handleTaskIdFilterChange = createFilterHandler(TASK_ID_PATTERN_PARAM); - return ( - - - - - - - - - - - - + {FILTERS.map(({ hotkeyDisabled, key, translationKey }) => ( + + + + ))} ); }; From e119a12eb92d65247fc52edb18d47fcfa8775bc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=96=86=E5=AE=B8?= Date: Wed, 6 Aug 2025 17:39:33 +0800 Subject: [PATCH 08/22] feat(api, ui): Add Logical Date Range, Run After Range filter to Xcoms --- .../openapi/v2-rest-api-generated.yaml | 36 ++++++++ .../core_api/routes/public/xcom.py | 6 ++ .../airflow/ui/openapi-gen/queries/common.ts | 8 +- .../ui/openapi-gen/queries/ensureQueryData.ts | 12 ++- .../ui/openapi-gen/queries/prefetch.ts | 12 ++- .../airflow/ui/openapi-gen/queries/queries.ts | 12 ++- .../ui/openapi-gen/queries/suspense.ts | 12 ++- .../ui/openapi-gen/requests/services.gen.ts | 10 ++- .../ui/openapi-gen/requests/types.gen.ts | 4 + .../ui/public/i18n/locales/en/browse.json | 5 ++ .../ui/public/i18n/locales/zh-TW/browse.json | 11 +++ .../airflow/ui/src/constants/searchParams.ts | 4 + .../src/airflow/ui/src/pages/XCom/XCom.tsx | 43 ++++++---- .../airflow/ui/src/pages/XCom/XComFilters.tsx | 84 ++++++++++++++++--- 14 files changed, 220 insertions(+), 39 deletions(-) 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 147728162d9f4..a5744bf0e7844 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 @@ -4679,6 +4679,42 @@ paths: title: Task Id Pattern description: "SQL LIKE expression \u2014 use `%` / `_` wildcards (e.g. `%customer_%`).\ \ Regular expressions are **not** supported." + - name: logical_date_gte + in: query + required: false + schema: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Logical Date Gte + - name: logical_date_lte + in: query + required: false + schema: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Logical Date Lte + - name: run_after_gte + in: query + required: false + schema: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Run After Gte + - name: run_after_lte + in: query + required: false + schema: + anyOf: + - type: string + format: date-time + - type: 'null' + title: Run After Lte responses: '200': description: Successful Response diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py index 5cd6fa813f678..9fcbef63d4697 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py @@ -33,6 +33,8 @@ QueryXComKeyPatternSearch, QueryXComRunIdPatternSearch, QueryXComTaskIdPatternSearch, + RangeFilter, + datetime_range_filter_factory, ) from airflow.api_fastapi.common.router import AirflowRouter from airflow.api_fastapi.core_api.datamodels.xcom import ( @@ -138,6 +140,8 @@ def get_xcom_entries( dag_id_pattern: QueryXComDagIdPatternSearch, run_id_pattern: QueryXComRunIdPatternSearch, task_id_pattern: QueryXComTaskIdPatternSearch, + logical_date_range: Annotated[RangeFilter, Depends(datetime_range_filter_factory("logical_date", DR))], + run_after_range: Annotated[RangeFilter, Depends(datetime_range_filter_factory("run_after", DR))], map_index: Annotated[int | None, Query(ge=-1)] = None, ) -> XComCollectionResponse: """ @@ -169,6 +173,8 @@ def get_xcom_entries( dag_id_pattern, run_id_pattern, task_id_pattern, + logical_date_range, + run_after_range, ], offset=offset, limit=limit, diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts index 389dd243ed524..d02685165716f 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts @@ -569,18 +569,22 @@ export const UseXcomServiceGetXcomEntryKeyFn = ({ dagId, dagRunId, deserialize, export type XcomServiceGetXcomEntriesDefaultResponse = Awaited>; export type XcomServiceGetXcomEntriesQueryResult = UseQueryResult; export const useXcomServiceGetXcomEntriesKey = "XcomServiceGetXcomEntries"; -export const UseXcomServiceGetXcomEntriesKeyFn = ({ dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { +export const UseXcomServiceGetXcomEntriesKeyFn = ({ dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { dagId: string; dagIdPattern?: string; dagRunId: string; limit?: number; + logicalDateGte?: string; + logicalDateLte?: string; mapIndex?: number; offset?: number; + runAfterGte?: string; + runAfterLte?: string; runIdPattern?: string; taskId: string; taskIdPattern?: string; xcomKeyPattern?: string; -}, queryKey?: Array) => [useXcomServiceGetXcomEntriesKey, ...(queryKey ?? [{ dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }])]; +}, queryKey?: Array) => [useXcomServiceGetXcomEntriesKey, ...(queryKey ?? [{ dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }])]; export type TaskServiceGetTasksDefaultResponse = Awaited>; export type TaskServiceGetTasksQueryResult = UseQueryResult; export const useTaskServiceGetTasksKey = "TaskServiceGetTasks"; diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts index c88048c3245c4..6be8b1f37da9b 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts @@ -1081,21 +1081,29 @@ export const ensureUseXcomServiceGetXcomEntryData = (queryClient: QueryClient, { * @param data.dagIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.runIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. +* @param data.logicalDateGte +* @param data.logicalDateLte +* @param data.runAfterGte +* @param data.runAfterLte * @returns XComCollectionResponse Successful Response * @throws ApiError */ -export const ensureUseXcomServiceGetXcomEntriesData = (queryClient: QueryClient, { dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { +export const ensureUseXcomServiceGetXcomEntriesData = (queryClient: QueryClient, { dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { dagId: string; dagIdPattern?: string; dagRunId: string; limit?: number; + logicalDateGte?: string; + logicalDateLte?: string; mapIndex?: number; offset?: number; + runAfterGte?: string; + runAfterLte?: string; runIdPattern?: string; taskId: string; taskIdPattern?: string; xcomKeyPattern?: string; -}) => queryClient.ensureQueryData({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }), queryFn: () => XcomService.getXcomEntries({ dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) }); +}) => queryClient.ensureQueryData({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }), queryFn: () => XcomService.getXcomEntries({ dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts index 568fcdfe0452d..98ea860a063fa 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts @@ -1081,21 +1081,29 @@ export const prefetchUseXcomServiceGetXcomEntry = (queryClient: QueryClient, { d * @param data.dagIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.runIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. +* @param data.logicalDateGte +* @param data.logicalDateLte +* @param data.runAfterGte +* @param data.runAfterLte * @returns XComCollectionResponse Successful Response * @throws ApiError */ -export const prefetchUseXcomServiceGetXcomEntries = (queryClient: QueryClient, { dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { +export const prefetchUseXcomServiceGetXcomEntries = (queryClient: QueryClient, { dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { dagId: string; dagIdPattern?: string; dagRunId: string; limit?: number; + logicalDateGte?: string; + logicalDateLte?: string; mapIndex?: number; offset?: number; + runAfterGte?: string; + runAfterLte?: string; runIdPattern?: string; taskId: string; taskIdPattern?: string; xcomKeyPattern?: string; -}) => queryClient.prefetchQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }), queryFn: () => XcomService.getXcomEntries({ dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) }); +}) => queryClient.prefetchQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }), queryFn: () => XcomService.getXcomEntries({ dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts index 0174091a34134..26cfdccdcd793 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts @@ -1081,21 +1081,29 @@ export const useXcomServiceGetXcomEntry = = unknown[]>({ dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { +export const useXcomServiceGetXcomEntries = = unknown[]>({ dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { dagId: string; dagIdPattern?: string; dagRunId: string; limit?: number; + logicalDateGte?: string; + logicalDateLte?: string; mapIndex?: number; offset?: number; + runAfterGte?: string; + runAfterLte?: string; runIdPattern?: string; taskId: string; taskIdPattern?: string; xcomKeyPattern?: string; -}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) as TData, ...options }); +}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) as TData, ...options }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts index 2d7fe881eb46e..7bd6ed46996d2 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts @@ -1081,21 +1081,29 @@ export const useXcomServiceGetXcomEntrySuspense = = unknown[]>({ dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { +export const useXcomServiceGetXcomEntriesSuspense = = unknown[]>({ dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { dagId: string; dagIdPattern?: string; dagRunId: string; limit?: number; + logicalDateGte?: string; + logicalDateLte?: string; mapIndex?: number; offset?: number; + runAfterGte?: string; + runAfterLte?: string; runIdPattern?: string; taskId: string; taskIdPattern?: string; xcomKeyPattern?: string; -}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagId, dagIdPattern, dagRunId, limit, mapIndex, offset, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) as TData, ...options }); +}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) as TData, ...options }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts b/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts index 84fe5b888983b..20642ff081cc3 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts @@ -3011,6 +3011,10 @@ export class XcomService { * @param data.dagIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.runIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. + * @param data.logicalDateGte + * @param data.logicalDateLte + * @param data.runAfterGte + * @param data.runAfterLte * @returns XComCollectionResponse Successful Response * @throws ApiError */ @@ -3030,7 +3034,11 @@ export class XcomService { xcom_key_pattern: data.xcomKeyPattern, dag_id_pattern: data.dagIdPattern, run_id_pattern: data.runIdPattern, - task_id_pattern: data.taskIdPattern + task_id_pattern: data.taskIdPattern, + logical_date_gte: data.logicalDateGte, + logical_date_lte: data.logicalDateLte, + run_after_gte: data.runAfterGte, + run_after_lte: data.runAfterLte }, errors: { 400: 'Bad Request', 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 3f32086c436c6..d0e8f2abed37f 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 @@ -2806,8 +2806,12 @@ export type GetXcomEntriesData = { dagIdPattern?: string | null; dagRunId: string; limit?: number; + logicalDateGte?: string | null; + logicalDateLte?: string | null; mapIndex?: number | null; offset?: number; + runAfterGte?: string | null; + runAfterLte?: string | null; /** * SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. */ 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 6ede12d44bc14..c230a80099c3e 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 @@ -20,7 +20,12 @@ }, "filters": { "dagFilter": "Filter by Dag ID", + "dateTimeRanges": "Date & Time Filters", "keyFilter": "Filter by XCom key", + "logicalDateFrom": "Logical Date From", + "logicalDateTo": "Logical Date To", + "runAfterFrom": "Run After From", + "runAfterTo": "Run After To", "runIdFilter": "Filter by Run ID", "taskIdFilter": "Filter by Task ID" }, diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/zh-TW/browse.json b/airflow-core/src/airflow/ui/public/i18n/locales/zh-TW/browse.json index 8cd520c09cb48..8e1f78c8c55c1 100644 --- a/airflow-core/src/airflow/ui/public/i18n/locales/zh-TW/browse.json +++ b/airflow-core/src/airflow/ui/public/i18n/locales/zh-TW/browse.json @@ -18,6 +18,17 @@ "key": "鍵", "value": "值" }, + "filters": { + "dagFilter": "依 Dag ID 過濾", + "dateTimeRanges": "日期時間過濾器", + "keyFilter": "依 XCom 鍵過濾", + "logicalDateFrom": "邏輯日期起始", + "logicalDateTo": "邏輯日期結束", + "runAfterFrom": "運行時間起始", + "runAfterTo": "運行時間結束", + "runIdFilter": "依運行 ID 過濾", + "taskIdFilter": "依任務 ID 過濾" + }, "title": "XCom" } } diff --git a/airflow-core/src/airflow/ui/src/constants/searchParams.ts b/airflow-core/src/airflow/ui/src/constants/searchParams.ts index 5b76b2f651938..e3581396ff1fc 100644 --- a/airflow-core/src/airflow/ui/src/constants/searchParams.ts +++ b/airflow-core/src/airflow/ui/src/constants/searchParams.ts @@ -25,11 +25,15 @@ export enum SearchParamsKeys { LAST_DAG_RUN_STATE = "last_dag_run_state", LIMIT = "limit", LOG_LEVEL = "log_level", + LOGICAL_DATE_GTE = "logical_date_gte", + LOGICAL_DATE_LTE = "logical_date_lte", NAME_PATTERN = "name_pattern", OFFSET = "offset", OWNERS = "owners", PAUSED = "paused", POOL = "pool", + RUN_AFTER_GTE = "run_after_gte", + RUN_AFTER_LTE = "run_after_lte", RUN_ID_PATTERN = "run_id_pattern", RUN_TYPE = "run_type", SORT = "sort", diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx b/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx index 933a276cc663c..bda3ca766a7f8 100644 --- a/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx +++ b/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx @@ -119,22 +119,33 @@ export const XCom = () => { const filteredRunId = searchParams.get(RUN_ID_PATTERN_PARAM); const filteredTaskId = searchParams.get(TASK_ID_PATTERN_PARAM); - const { data, error, isFetching, isLoading } = useXcomServiceGetXcomEntries( - { - dagId, - dagIdPattern: filteredDagId ?? undefined, - dagRunId: runId, - limit: pagination.pageSize, - mapIndex: mapIndex === "-1" ? undefined : parseInt(mapIndex, 10), - offset: pagination.pageIndex * pagination.pageSize, - runIdPattern: filteredRunId ?? undefined, - taskId, - taskIdPattern: filteredTaskId ?? undefined, - xcomKeyPattern: filteredKey ?? undefined, - }, - undefined, - { enabled: !isNaN(pagination.pageSize) }, - ); + const { LOGICAL_DATE_GTE, LOGICAL_DATE_LTE, RUN_AFTER_GTE, RUN_AFTER_LTE } = SearchParamsKeys; + + const logicalDateGte = searchParams.get(LOGICAL_DATE_GTE); + const logicalDateLte = searchParams.get(LOGICAL_DATE_LTE); + const runAfterGte = searchParams.get(RUN_AFTER_GTE); + const runAfterLte = searchParams.get(RUN_AFTER_LTE); + + const apiParams = { + dagId, + dagIdPattern: filteredDagId ?? undefined, + dagRunId: runId, + limit: pagination.pageSize, + logicalDateGte: logicalDateGte ?? undefined, + logicalDateLte: logicalDateLte ?? undefined, + mapIndex: mapIndex === "-1" ? undefined : parseInt(mapIndex, 10), + offset: pagination.pageIndex * pagination.pageSize, + runAfterGte: runAfterGte ?? undefined, + runAfterLte: runAfterLte ?? undefined, + runIdPattern: filteredRunId ?? undefined, + taskId, + taskIdPattern: filteredTaskId ?? undefined, + xcomKeyPattern: filteredKey ?? undefined, + }; + + const { data, error, isFetching, isLoading } = useXcomServiceGetXcomEntries(apiParams, undefined, { + enabled: !isNaN(pagination.pageSize), + }); return ( diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx b/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx index 075f2bdab9c9d..cbd750b1c29d7 100644 --- a/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx +++ b/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx @@ -16,12 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -import { Box, HStack } from "@chakra-ui/react"; +import { Box, HStack, Text, VStack } from "@chakra-ui/react"; import { useCallback } from "react"; import { useTranslation } from "react-i18next"; import { useSearchParams } from "react-router-dom"; import { useTableURLState } from "src/components/DataTable/useTableUrlState"; +import { DateTimeInput } from "src/components/DateTimeInput"; import { SearchBar } from "src/components/SearchBar"; import { SearchParamsKeys } from "src/constants/searchParams"; @@ -54,19 +55,78 @@ export const XComFilters = () => { [pagination, searchParams, setSearchParams, setTableURLState, sorting], ); + const createDateTimeFilterHandler = useCallback( + (paramKey: string) => (event: React.ChangeEvent) => { + const { value } = event.target; + + if (value === "") { + searchParams.delete(paramKey); + } else { + searchParams.set(paramKey, value); + } + setTableURLState({ + pagination: { ...pagination, pageIndex: 0 }, + sorting, + }); + setSearchParams(searchParams); + }, + [pagination, searchParams, setSearchParams, setTableURLState, sorting], + ); + return ( - - {FILTERS.map(({ hotkeyDisabled, key, translationKey }) => ( - - + + {FILTERS.map(({ hotkeyDisabled, key, translationKey }) => ( + + +   + + + + ))} + + + {translate("xcom.filters.logicalDateFrom")} + + + + + + {translate("xcom.filters.logicalDateTo")} + + + + + + {translate("xcom.filters.runAfterFrom")} + + + + + + {translate("xcom.filters.runAfterTo")} + + - ))} - + + ); }; From f583c701a6cbfef3f92e5f874719ce274891dcf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=96=86=E5=AE=B8?= Date: Wed, 6 Aug 2025 19:32:03 +0800 Subject: [PATCH 09/22] fix(i18n): Modify translations --- .../src/airflow/ui/public/i18n/locales/en/browse.json | 1 - .../src/airflow/ui/public/i18n/locales/zh-TW/browse.json | 7 +++---- 2 files changed, 3 insertions(+), 5 deletions(-) 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 c230a80099c3e..45fa735d14f82 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 @@ -20,7 +20,6 @@ }, "filters": { "dagFilter": "Filter by Dag ID", - "dateTimeRanges": "Date & Time Filters", "keyFilter": "Filter by XCom key", "logicalDateFrom": "Logical Date From", "logicalDateTo": "Logical Date To", diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/zh-TW/browse.json b/airflow-core/src/airflow/ui/public/i18n/locales/zh-TW/browse.json index 8e1f78c8c55c1..d69db0eade95e 100644 --- a/airflow-core/src/airflow/ui/public/i18n/locales/zh-TW/browse.json +++ b/airflow-core/src/airflow/ui/public/i18n/locales/zh-TW/browse.json @@ -20,13 +20,12 @@ }, "filters": { "dagFilter": "依 Dag ID 過濾", - "dateTimeRanges": "日期時間過濾器", "keyFilter": "依 XCom 鍵過濾", "logicalDateFrom": "邏輯日期起始", "logicalDateTo": "邏輯日期結束", - "runAfterFrom": "運行時間起始", - "runAfterTo": "運行時間結束", - "runIdFilter": "依運行 ID 過濾", + "runAfterFrom": "執行時間起始", + "runAfterTo": "執行時間結束", + "runIdFilter": "依執行 ID 過濾", "taskIdFilter": "依任務 ID 過濾" }, "title": "XCom" From 5bec53feaf8c80e9ec242872b96b46bbd3918773 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=96=86=E5=AE=B8?= Date: Fri, 8 Aug 2025 05:17:19 +0800 Subject: [PATCH 10/22] fix: Update test to use xcom_key_pattern parameter --- .../tests/unit/api_fastapi/core_api/routes/public/test_xcom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_xcom.py b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_xcom.py index 361b3c6e97a14..db001fe2d117a 100644 --- a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_xcom.py +++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_xcom.py @@ -369,7 +369,7 @@ def test_should_respond_200_with_xcom_key(self, key, expected_entries, test_clie self._create_xcom_entries(TEST_DAG_ID, run_id, logical_date_parsed, TEST_TASK_ID, mapped_ti=True) response = test_client.get( "/dags/~/dagRuns/~/taskInstances/~/xcomEntries", - params={"xcom_key": key} if key is not None else None, + params={"xcom_key_pattern": key} if key is not None else None, ) assert response.status_code == 200 From f3424db36cdb301741b143d14b6172b844614819 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=96=86=E5=AE=B8?= Date: Fri, 8 Aug 2025 08:46:31 +0800 Subject: [PATCH 11/22] refactor(XCom): Unifying handlers and config --- .../airflow/ui/src/pages/XCom/XComFilters.tsx | 147 +++++++++--------- 1 file changed, 76 insertions(+), 71 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx b/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx index cbd750b1c29d7..1a5f8f426a147 100644 --- a/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx +++ b/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx @@ -27,10 +27,50 @@ import { SearchBar } from "src/components/SearchBar"; import { SearchParamsKeys } from "src/constants/searchParams"; const FILTERS = [ - { hotkeyDisabled: false, key: SearchParamsKeys.KEY_PATTERN, translationKey: "keyFilter" }, - { hotkeyDisabled: true, key: SearchParamsKeys.DAG_ID_PATTERN, translationKey: "dagFilter" }, - { hotkeyDisabled: true, key: SearchParamsKeys.RUN_ID_PATTERN, translationKey: "runIdFilter" }, - { hotkeyDisabled: true, key: SearchParamsKeys.TASK_ID_PATTERN, translationKey: "taskIdFilter" }, + { + hotkeyDisabled: false, + key: SearchParamsKeys.KEY_PATTERN, + translationKey: "keyFilter", + type: "search", + }, + { + hotkeyDisabled: true, + key: SearchParamsKeys.DAG_ID_PATTERN, + translationKey: "dagFilter", + type: "search", + }, + { + hotkeyDisabled: true, + key: SearchParamsKeys.RUN_ID_PATTERN, + translationKey: "runIdFilter", + type: "search", + }, + { + hotkeyDisabled: true, + key: SearchParamsKeys.TASK_ID_PATTERN, + translationKey: "taskIdFilter", + type: "search", + }, + { + key: SearchParamsKeys.LOGICAL_DATE_GTE, + translationKey: "logicalDateFrom", + type: "datetime", + }, + { + key: SearchParamsKeys.LOGICAL_DATE_LTE, + translationKey: "logicalDateTo", + type: "datetime", + }, + { + key: SearchParamsKeys.RUN_AFTER_GTE, + translationKey: "runAfterFrom", + type: "datetime", + }, + { + key: SearchParamsKeys.RUN_AFTER_LTE, + translationKey: "runAfterTo", + type: "datetime", + }, ] as const; export const XComFilters = () => { @@ -39,7 +79,7 @@ export const XComFilters = () => { const { pagination, sorting } = tableURLState; const { t: translate } = useTranslation(["browse"]); - const createFilterHandler = useCallback( + const handleFilterChange = useCallback( (paramKey: string) => (value: string) => { if (value === "") { searchParams.delete(paramKey); @@ -55,77 +95,42 @@ export const XComFilters = () => { [pagination, searchParams, setSearchParams, setTableURLState, sorting], ); - const createDateTimeFilterHandler = useCallback( - (paramKey: string) => (event: React.ChangeEvent) => { - const { value } = event.target; + const renderFilterInput = (filter: (typeof FILTERS)[number]) => { + const { key, translationKey, type } = filter; - if (value === "") { - searchParams.delete(paramKey); - } else { - searchParams.set(paramKey, value); - } - setTableURLState({ - pagination: { ...pagination, pageIndex: 0 }, - sorting, - }); - setSearchParams(searchParams); - }, - [pagination, searchParams, setSearchParams, setTableURLState, sorting], - ); + return ( + + + {type === "search" ? "\u00A0" : translate(`xcom.filters.${translationKey}`)} + + {type === "search" ? ( + (() => { + const { hotkeyDisabled } = filter; + + return ( + + ); + })() + ) : ( + handleFilterChange(key)(event.target.value)} + value={searchParams.get(key) ?? ""} + /> + )} + + ); + }; return ( - {FILTERS.map(({ hotkeyDisabled, key, translationKey }) => ( - - -   - - - - ))} - - - {translate("xcom.filters.logicalDateFrom")} - - - - - - {translate("xcom.filters.logicalDateTo")} - - - - - - {translate("xcom.filters.runAfterFrom")} - - - - - - {translate("xcom.filters.runAfterTo")} - - - + {FILTERS.map(renderFilterInput)} ); From bd0d59af77f35a183217c0535009ac4e568bca20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=96=86=E5=AE=B8?= Date: Fri, 8 Aug 2025 08:59:14 +0800 Subject: [PATCH 12/22] fix(XCom): Hide filters in task instance Xcom tab --- airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx b/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx index bda3ca766a7f8..4c93b2b30dc0a 100644 --- a/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx +++ b/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx @@ -153,7 +153,7 @@ export const XCom = () => { {translate("xcom.title")} ) : undefined} - + {dagId === "~" && runId === "~" && taskId === "~" ? : undefined} Date: Fri, 8 Aug 2025 10:47:46 +0800 Subject: [PATCH 13/22] feat(UI): Add reset button to XCom filters --- .../airflow/ui/src/pages/XCom/XComFilters.tsx | 45 +++++++++++++++++-- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx b/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx index 1a5f8f426a147..98afac2e0943e 100644 --- a/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx +++ b/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx @@ -16,9 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -import { Box, HStack, Text, VStack } from "@chakra-ui/react"; -import { useCallback } from "react"; +import { Box, Button, HStack, Text, VStack } from "@chakra-ui/react"; +import { useCallback, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; +import { LuX } from "react-icons/lu"; import { useSearchParams } from "react-router-dom"; import { useTableURLState } from "src/components/DataTable/useTableUrlState"; @@ -77,7 +78,8 @@ export const XComFilters = () => { const [searchParams, setSearchParams] = useSearchParams(); const { setTableURLState, tableURLState } = useTableURLState(); const { pagination, sorting } = tableURLState; - const { t: translate } = useTranslation(["browse"]); + const { t: translate } = useTranslation(["browse", "common"]); + const [resetKey, setResetKey] = useState(0); const handleFilterChange = useCallback( (paramKey: string) => (value: string) => { @@ -95,11 +97,33 @@ export const XComFilters = () => { [pagination, searchParams, setSearchParams, setTableURLState, sorting], ); + const filterCount = useMemo( + () => + FILTERS.filter((filter) => { + const value = searchParams.get(filter.key); + + return value !== null && value !== ""; + }).length, + [searchParams], + ); + + const handleResetFilters = useCallback(() => { + FILTERS.forEach((filter) => { + searchParams.delete(filter.key); + }); + setTableURLState({ + pagination: { ...pagination, pageIndex: 0 }, + sorting, + }); + setSearchParams(searchParams); + setResetKey((prev) => prev + 1); + }, [pagination, searchParams, setSearchParams, setTableURLState, sorting]); + const renderFilterInput = (filter: (typeof FILTERS)[number]) => { const { key, translationKey, type } = filter; return ( - + {type === "search" ? "\u00A0" : translate(`xcom.filters.${translationKey}`)} @@ -112,6 +136,7 @@ export const XComFilters = () => { defaultValue={searchParams.get(key) ?? ""} hideAdvanced hotkeyDisabled={hotkeyDisabled} + key={`${key}-${resetKey}`} onChange={handleFilterChange(key)} placeHolder={translate(`xcom.filters.${translationKey}`)} /> @@ -119,6 +144,7 @@ export const XComFilters = () => { })() ) : ( handleFilterChange(key)(event.target.value)} value={searchParams.get(key) ?? ""} /> @@ -131,6 +157,17 @@ export const XComFilters = () => { {FILTERS.map(renderFilterInput)} + + +   + + {filterCount > 0 && ( + + )} + ); From 2494849e4e7f72fb1756d4e5393744f257a28123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=96=86=E5=AE=B8?= Date: Sat, 9 Aug 2025 02:53:13 +0800 Subject: [PATCH 14/22] fix(ui, api): filter by dag display name & regroup placeholder translations --- .../airflow/api_fastapi/common/parameters.py | 5 +++-- .../openapi/v2-rest-api-generated.yaml | 4 ++-- .../core_api/routes/public/xcom.py | 13 ++++++----- .../airflow/ui/openapi-gen/queries/common.ts | 6 ++--- .../ui/openapi-gen/queries/ensureQueryData.ts | 8 +++---- .../ui/openapi-gen/queries/prefetch.ts | 8 +++---- .../airflow/ui/openapi-gen/queries/queries.ts | 8 +++---- .../ui/openapi-gen/queries/suspense.ts | 8 +++---- .../ui/openapi-gen/requests/services.gen.ts | 4 ++-- .../ui/openapi-gen/requests/types.gen.ts | 4 ++-- .../ui/public/i18n/locales/en/browse.json | 10 --------- .../ui/public/i18n/locales/en/common.json | 10 +++++++++ .../ui/public/i18n/locales/zh-TW/browse.json | 10 --------- .../ui/public/i18n/locales/zh-TW/common.json | 10 +++++++++ .../airflow/ui/src/constants/searchParams.ts | 1 + .../src/airflow/ui/src/pages/XCom/XCom.tsx | 8 +++---- .../airflow/ui/src/pages/XCom/XComFilters.tsx | 22 +++++++++---------- 17 files changed, 72 insertions(+), 67 deletions(-) diff --git a/airflow-core/src/airflow/api_fastapi/common/parameters.py b/airflow-core/src/airflow/api_fastapi/common/parameters.py index 33bd88d5c1f09..f94be818c7d86 100644 --- a/airflow-core/src/airflow/api_fastapi/common/parameters.py +++ b/airflow-core/src/airflow/api_fastapi/common/parameters.py @@ -737,8 +737,9 @@ def _transform_ti_states(states: list[str] | None) -> list[TaskInstanceState | N QueryXComKeyPatternSearch = Annotated[ _SearchParam, Depends(search_param_factory(XComModel.key, "xcom_key_pattern")) ] -QueryXComDagIdPatternSearch = Annotated[ - _SearchParam, Depends(search_param_factory(XComModel.dag_id, "dag_id_pattern")) + +QueryXComDagDisplayNamePatternSearch = Annotated[ + _SearchParam, Depends(search_param_factory(DagModel.dag_display_name, "dag_display_name_pattern")) ] QueryXComRunIdPatternSearch = Annotated[ _SearchParam, Depends(search_param_factory(XComModel.run_id, "run_id_pattern")) 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 11ff6d3e98b94..4f63cf47a2945 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 @@ -4671,7 +4671,7 @@ paths: title: Xcom Key Pattern description: "SQL LIKE expression \u2014 use `%` / `_` wildcards (e.g. `%customer_%`).\ \ Regular expressions are **not** supported." - - name: dag_id_pattern + - name: dag_display_name_pattern in: query required: false schema: @@ -4680,7 +4680,7 @@ paths: - type: 'null' description: "SQL LIKE expression \u2014 use `%` / `_` wildcards (e.g. `%customer_%`).\ \ Regular expressions are **not** supported." - title: Dag Id Pattern + title: Dag Display Name Pattern description: "SQL LIKE expression \u2014 use `%` / `_` wildcards (e.g. `%customer_%`).\ \ Regular expressions are **not** supported." - name: run_id_pattern diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py index 474b92cf5870c..767853dda4a25 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py @@ -29,7 +29,7 @@ from airflow.api_fastapi.common.parameters import ( QueryLimit, QueryOffset, - QueryXComDagIdPatternSearch, + QueryXComDagDisplayNamePatternSearch, QueryXComKeyPatternSearch, QueryXComRunIdPatternSearch, QueryXComTaskIdPatternSearch, @@ -49,6 +49,7 @@ from airflow.api_fastapi.logging.decorators import action_logging from airflow.exceptions import TaskNotFound from airflow.models import DagRun as DR +from airflow.models.dag import DagModel from airflow.models.xcom import XComModel xcom_router = AirflowRouter( @@ -137,7 +138,7 @@ def get_xcom_entries( readable_xcom_filter: ReadableXComFilterDep, session: SessionDep, xcom_key_pattern: QueryXComKeyPatternSearch, - dag_id_pattern: QueryXComDagIdPatternSearch, + dag_display_name_pattern: QueryXComDagDisplayNamePatternSearch, run_id_pattern: QueryXComRunIdPatternSearch, task_id_pattern: QueryXComTaskIdPatternSearch, logical_date_range: Annotated[RangeFilter, Depends(datetime_range_filter_factory("logical_date", DR))], @@ -152,8 +153,10 @@ def get_xcom_entries( query = select(XComModel) if dag_id != "~": query = query.where(XComModel.dag_id == dag_id) - query = query.join(DR, and_(XComModel.dag_id == DR.dag_id, XComModel.run_id == DR.run_id)).options( - joinedload(XComModel.dag_run).joinedload(DR.dag_model) + query = ( + query.join(DR, and_(XComModel.dag_id == DR.dag_id, XComModel.run_id == DR.run_id)) + .join(DagModel, DR.dag_id == DagModel.dag_id) + .options(joinedload(XComModel.dag_run).joinedload(DR.dag_model)) ) if task_id != "~": @@ -170,7 +173,7 @@ def get_xcom_entries( filters=[ readable_xcom_filter, xcom_key_pattern, - dag_id_pattern, + dag_display_name_pattern, run_id_pattern, task_id_pattern, logical_date_range, diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts index a21dd18a98521..d55af783ea7ea 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts @@ -574,9 +574,9 @@ export const UseXcomServiceGetXcomEntryKeyFn = ({ dagId, dagRunId, deserialize, export type XcomServiceGetXcomEntriesDefaultResponse = Awaited>; export type XcomServiceGetXcomEntriesQueryResult = UseQueryResult; export const useXcomServiceGetXcomEntriesKey = "XcomServiceGetXcomEntries"; -export const UseXcomServiceGetXcomEntriesKeyFn = ({ dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { +export const UseXcomServiceGetXcomEntriesKeyFn = ({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { + dagDisplayNamePattern?: string; dagId: string; - dagIdPattern?: string; dagRunId: string; limit?: number; logicalDateGte?: string; @@ -589,7 +589,7 @@ export const UseXcomServiceGetXcomEntriesKeyFn = ({ dagId, dagIdPattern, dagRunI taskId: string; taskIdPattern?: string; xcomKeyPattern?: string; -}, queryKey?: Array) => [useXcomServiceGetXcomEntriesKey, ...(queryKey ?? [{ dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }])]; +}, queryKey?: Array) => [useXcomServiceGetXcomEntriesKey, ...(queryKey ?? [{ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }])]; export type TaskServiceGetTasksDefaultResponse = Awaited>; export type TaskServiceGetTasksQueryResult = UseQueryResult; export const useTaskServiceGetTasksKey = "TaskServiceGetTasks"; diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts index 0035c579d4c3c..18b6bc0811ef0 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts @@ -1088,7 +1088,7 @@ export const ensureUseXcomServiceGetXcomEntryData = (queryClient: QueryClient, { * @param data.limit * @param data.offset * @param data.xcomKeyPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. -* @param data.dagIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. +* @param data.dagDisplayNamePattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.runIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.logicalDateGte @@ -1098,9 +1098,9 @@ export const ensureUseXcomServiceGetXcomEntryData = (queryClient: QueryClient, { * @returns XComCollectionResponse Successful Response * @throws ApiError */ -export const ensureUseXcomServiceGetXcomEntriesData = (queryClient: QueryClient, { dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { +export const ensureUseXcomServiceGetXcomEntriesData = (queryClient: QueryClient, { dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { + dagDisplayNamePattern?: string; dagId: string; - dagIdPattern?: string; dagRunId: string; limit?: number; logicalDateGte?: string; @@ -1113,7 +1113,7 @@ export const ensureUseXcomServiceGetXcomEntriesData = (queryClient: QueryClient, taskId: string; taskIdPattern?: string; xcomKeyPattern?: string; -}) => queryClient.ensureQueryData({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }), queryFn: () => XcomService.getXcomEntries({ dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) }); +}) => queryClient.ensureQueryData({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts index 9c4fd585407b9..71ec91a988432 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts @@ -1088,7 +1088,7 @@ export const prefetchUseXcomServiceGetXcomEntry = (queryClient: QueryClient, { d * @param data.limit * @param data.offset * @param data.xcomKeyPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. -* @param data.dagIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. +* @param data.dagDisplayNamePattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.runIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.logicalDateGte @@ -1098,9 +1098,9 @@ export const prefetchUseXcomServiceGetXcomEntry = (queryClient: QueryClient, { d * @returns XComCollectionResponse Successful Response * @throws ApiError */ -export const prefetchUseXcomServiceGetXcomEntries = (queryClient: QueryClient, { dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { +export const prefetchUseXcomServiceGetXcomEntries = (queryClient: QueryClient, { dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { + dagDisplayNamePattern?: string; dagId: string; - dagIdPattern?: string; dagRunId: string; limit?: number; logicalDateGte?: string; @@ -1113,7 +1113,7 @@ export const prefetchUseXcomServiceGetXcomEntries = (queryClient: QueryClient, { taskId: string; taskIdPattern?: string; xcomKeyPattern?: string; -}) => queryClient.prefetchQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }), queryFn: () => XcomService.getXcomEntries({ dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) }); +}) => queryClient.prefetchQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts index 8839d96adbde5..5cfdf8d0e1efa 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts @@ -1088,7 +1088,7 @@ export const useXcomServiceGetXcomEntry = = unknown[]>({ dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { +export const useXcomServiceGetXcomEntries = = unknown[]>({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { + dagDisplayNamePattern?: string; dagId: string; - dagIdPattern?: string; dagRunId: string; limit?: number; logicalDateGte?: string; @@ -1113,7 +1113,7 @@ export const useXcomServiceGetXcomEntries = , "queryKey" | "queryFn">) => useQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) as TData, ...options }); +}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) as TData, ...options }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts index dbe41c00d671a..a1009c0e67ec3 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts @@ -1088,7 +1088,7 @@ export const useXcomServiceGetXcomEntrySuspense = = unknown[]>({ dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { +export const useXcomServiceGetXcomEntriesSuspense = = unknown[]>({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { + dagDisplayNamePattern?: string; dagId: string; - dagIdPattern?: string; dagRunId: string; limit?: number; logicalDateGte?: string; @@ -1113,7 +1113,7 @@ export const useXcomServiceGetXcomEntriesSuspense = , "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagId, dagIdPattern, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) as TData, ...options }); +}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) as TData, ...options }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts b/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts index 69d751ab370ac..cdd95c0492995 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts @@ -3018,7 +3018,7 @@ export class XcomService { * @param data.limit * @param data.offset * @param data.xcomKeyPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. - * @param data.dagIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. + * @param data.dagDisplayNamePattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.runIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.logicalDateGte @@ -3042,7 +3042,7 @@ export class XcomService { limit: data.limit, offset: data.offset, xcom_key_pattern: data.xcomKeyPattern, - dag_id_pattern: data.dagIdPattern, + dag_display_name_pattern: data.dagDisplayNamePattern, run_id_pattern: data.runIdPattern, task_id_pattern: data.taskIdPattern, logical_date_gte: data.logicalDateGte, 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 003052e841f4a..86430e8c94003 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 @@ -2809,11 +2809,11 @@ export type UpdateXcomEntryData = { export type UpdateXcomEntryResponse = XComResponseNative; export type GetXcomEntriesData = { - dagId: string; /** * SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. */ - dagIdPattern?: string | null; + dagDisplayNamePattern?: string | null; + dagId: string; dagRunId: string; limit?: number; logicalDateGte?: string | null; 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 45fa735d14f82..4a2ab97354b16 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 @@ -18,16 +18,6 @@ "key": "Key", "value": "Value" }, - "filters": { - "dagFilter": "Filter by Dag ID", - "keyFilter": "Filter by XCom key", - "logicalDateFrom": "Logical Date From", - "logicalDateTo": "Logical Date To", - "runAfterFrom": "Run After From", - "runAfterTo": "Run After To", - "runIdFilter": "Filter by Run ID", - "taskIdFilter": "Filter by Task ID" - }, "title": "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 8dda576063e0b..21c0372a849c8 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 @@ -92,6 +92,16 @@ "any": "Any", "or": "OR" }, + "filters": { + "dagDisplayNamePlaceholder": "Filter by Dag Display Name", + "keyPlaceholder": "Filter by XCom key", + "logicalDateFromPlaceholder": "Logical Date From", + "logicalDateToPlaceholder": "Logical Date To", + "runAfterFromPlaceholder": "Run After From", + "runAfterToPlaceholder": "Run After To", + "runIdPlaceholder": "Filter by Run ID", + "taskIdPlaceholder": "Filter by Task ID" + }, "logicalDate": "Logical Date", "logout": "Logout", "logoutConfirmation": "You are about to logout from the application.", diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/zh-TW/browse.json b/airflow-core/src/airflow/ui/public/i18n/locales/zh-TW/browse.json index d69db0eade95e..8cd520c09cb48 100644 --- a/airflow-core/src/airflow/ui/public/i18n/locales/zh-TW/browse.json +++ b/airflow-core/src/airflow/ui/public/i18n/locales/zh-TW/browse.json @@ -18,16 +18,6 @@ "key": "鍵", "value": "值" }, - "filters": { - "dagFilter": "依 Dag ID 過濾", - "keyFilter": "依 XCom 鍵過濾", - "logicalDateFrom": "邏輯日期起始", - "logicalDateTo": "邏輯日期結束", - "runAfterFrom": "執行時間起始", - "runAfterTo": "執行時間結束", - "runIdFilter": "依執行 ID 過濾", - "taskIdFilter": "依任務 ID 過濾" - }, "title": "XCom" } } diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/zh-TW/common.json b/airflow-core/src/airflow/ui/public/i18n/locales/zh-TW/common.json index 1423588781385..abfab7a7a9a42 100644 --- a/airflow-core/src/airflow/ui/public/i18n/locales/zh-TW/common.json +++ b/airflow-core/src/airflow/ui/public/i18n/locales/zh-TW/common.json @@ -92,6 +92,16 @@ "any": "任何", "or": "或" }, + "filters": { + "dagDisplayNamePlaceholder": "依 Dag 顯示名稱過濾", + "keyPlaceholder": "依 XCom 鍵過濾", + "logicalDateFromPlaceholder": "邏輯日期起始", + "logicalDateToPlaceholder": "邏輯日期結束", + "runAfterFromPlaceholder": "執行時間起始", + "runAfterToPlaceholder": "執行時間結束", + "runIdPlaceholder": "依執行 ID 過濾", + "taskIdPlaceholder": "依任務 ID 過濾" + }, "logicalDate": "邏輯日期", "logout": "登出", "logoutConfirmation": "確定要登出嗎?", diff --git a/airflow-core/src/airflow/ui/src/constants/searchParams.ts b/airflow-core/src/airflow/ui/src/constants/searchParams.ts index c11a28568eabe..31b651a5fb008 100644 --- a/airflow-core/src/airflow/ui/src/constants/searchParams.ts +++ b/airflow-core/src/airflow/ui/src/constants/searchParams.ts @@ -17,6 +17,7 @@ * under the License. */ export enum SearchParamsKeys { + DAG_DISPLAY_NAME_PATTERN = "dag_display_name_pattern", DAG_ID_PATTERN = "dag_id_pattern", DEPENDENCIES = "dependencies", END_DATE = "end_date", diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx b/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx index 4c93b2b30dc0a..401aade0117ee 100644 --- a/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx +++ b/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx @@ -34,7 +34,7 @@ import { XComEntry } from "./XComEntry"; import { XComFilters } from "./XComFilters"; const { - DAG_ID_PATTERN: DAG_ID_PATTERN_PARAM, + DAG_DISPLAY_NAME_PATTERN: DAG_DISPLAY_NAME_PATTERN_PARAM, KEY_PATTERN: KEY_PATTERN_PARAM, RUN_ID_PATTERN: RUN_ID_PATTERN_PARAM, TASK_ID_PATTERN: TASK_ID_PATTERN_PARAM, @@ -115,7 +115,7 @@ export const XCom = () => { const [searchParams] = useSearchParams(); const filteredKey = searchParams.get(KEY_PATTERN_PARAM); - const filteredDagId = searchParams.get(DAG_ID_PATTERN_PARAM); + const filteredDagDisplayName = searchParams.get(DAG_DISPLAY_NAME_PATTERN_PARAM); const filteredRunId = searchParams.get(RUN_ID_PATTERN_PARAM); const filteredTaskId = searchParams.get(TASK_ID_PATTERN_PARAM); @@ -127,8 +127,8 @@ export const XCom = () => { const runAfterLte = searchParams.get(RUN_AFTER_LTE); const apiParams = { + dagDisplayNamePattern: filteredDagDisplayName ?? undefined, dagId, - dagIdPattern: filteredDagId ?? undefined, dagRunId: runId, limit: pagination.pageSize, logicalDateGte: logicalDateGte ?? undefined, @@ -153,7 +153,7 @@ export const XCom = () => { {translate("xcom.title")} ) : undefined} - {dagId === "~" && runId === "~" && taskId === "~" ? : undefined} + { return ( - {type === "search" ? "\u00A0" : translate(`xcom.filters.${translationKey}`)} + {type === "search" ? "\u00A0" : translate(`common:filters.${translationKey}`)} {type === "search" ? ( (() => { @@ -138,7 +138,7 @@ export const XComFilters = () => { hotkeyDisabled={hotkeyDisabled} key={`${key}-${resetKey}`} onChange={handleFilterChange(key)} - placeHolder={translate(`xcom.filters.${translationKey}`)} + placeHolder={translate(`common:filters.${translationKey}`)} /> ); })() From 784b1e13c8a0b1093838fc710298f0ed3ecbc336 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=96=86=E5=AE=B8?= Date: Sat, 9 Aug 2025 03:01:50 +0800 Subject: [PATCH 15/22] fix(ui): modify translations --- .../src/airflow/ui/public/i18n/locales/en/common.json | 2 +- .../src/airflow/ui/public/i18n/locales/zh-TW/common.json | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) 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 21c0372a849c8..f4f71931ea09c 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 @@ -93,7 +93,7 @@ "or": "OR" }, "filters": { - "dagDisplayNamePlaceholder": "Filter by Dag Display Name", + "dagDisplayNamePlaceholder": "Filter by Dag", "keyPlaceholder": "Filter by XCom key", "logicalDateFromPlaceholder": "Logical Date From", "logicalDateToPlaceholder": "Logical Date To", diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/zh-TW/common.json b/airflow-core/src/airflow/ui/public/i18n/locales/zh-TW/common.json index abfab7a7a9a42..62dce303ca608 100644 --- a/airflow-core/src/airflow/ui/public/i18n/locales/zh-TW/common.json +++ b/airflow-core/src/airflow/ui/public/i18n/locales/zh-TW/common.json @@ -93,14 +93,14 @@ "or": "或" }, "filters": { - "dagDisplayNamePlaceholder": "依 Dag 顯示名稱過濾", - "keyPlaceholder": "依 XCom 鍵過濾", + "dagDisplayNamePlaceholder": "依 Dag 名稱篩選", + "keyPlaceholder": "依 XCom 鍵篩選", "logicalDateFromPlaceholder": "邏輯日期起始", "logicalDateToPlaceholder": "邏輯日期結束", "runAfterFromPlaceholder": "執行時間起始", "runAfterToPlaceholder": "執行時間結束", - "runIdPlaceholder": "依執行 ID 過濾", - "taskIdPlaceholder": "依任務 ID 過濾" + "runIdPlaceholder": "依執行 ID 篩選", + "taskIdPlaceholder": "依任務 ID 篩選" }, "logicalDate": "邏輯日期", "logout": "登出", From c42de31d419c97cd8c07c0d96754108de4ed920a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=96=86=E5=AE=B8?= Date: Sat, 9 Aug 2025 03:15:35 +0800 Subject: [PATCH 16/22] feat(ui,api): Add mapIndex filter --- .../src/airflow/api_fastapi/common/parameters.py | 3 +++ .../core_api/openapi/v2-rest-api-generated.yaml | 12 ++++++++++++ .../api_fastapi/core_api/routes/public/xcom.py | 3 +++ .../src/airflow/ui/openapi-gen/queries/common.ts | 5 +++-- .../ui/openapi-gen/queries/ensureQueryData.ts | 6 ++++-- .../src/airflow/ui/openapi-gen/queries/prefetch.ts | 6 ++++-- .../src/airflow/ui/openapi-gen/queries/queries.ts | 6 ++++-- .../src/airflow/ui/openapi-gen/queries/suspense.ts | 6 ++++-- .../airflow/ui/openapi-gen/requests/services.gen.ts | 2 ++ .../src/airflow/ui/openapi-gen/requests/types.gen.ts | 4 ++++ .../airflow/ui/public/i18n/locales/en/common.json | 1 + .../src/airflow/ui/src/constants/searchParams.ts | 1 + airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx | 3 +++ .../src/airflow/ui/src/pages/XCom/XComFilters.tsx | 6 ++++++ 14 files changed, 54 insertions(+), 10 deletions(-) diff --git a/airflow-core/src/airflow/api_fastapi/common/parameters.py b/airflow-core/src/airflow/api_fastapi/common/parameters.py index f94be818c7d86..c54b6c7078166 100644 --- a/airflow-core/src/airflow/api_fastapi/common/parameters.py +++ b/airflow-core/src/airflow/api_fastapi/common/parameters.py @@ -747,6 +747,9 @@ def _transform_ti_states(states: list[str] | None) -> list[TaskInstanceState | N QueryXComTaskIdPatternSearch = Annotated[ _SearchParam, Depends(search_param_factory(XComModel.task_id, "task_id_pattern")) ] +QueryXComMapIndexPatternSearch = Annotated[ + _SearchParam, Depends(search_param_factory(XComModel.map_index, "map_index_pattern")) +] # Assets QueryAssetNamePatternSearch = Annotated[ 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 4f63cf47a2945..1eb5b9ad6bbb9 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 @@ -4707,6 +4707,18 @@ paths: title: Task Id Pattern description: "SQL LIKE expression \u2014 use `%` / `_` wildcards (e.g. `%customer_%`).\ \ Regular expressions are **not** supported." + - name: map_index_pattern + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: "SQL LIKE expression \u2014 use `%` / `_` wildcards (e.g. `%customer_%`).\ + \ Regular expressions are **not** supported." + title: Map Index Pattern + description: "SQL LIKE expression \u2014 use `%` / `_` wildcards (e.g. `%customer_%`).\ + \ Regular expressions are **not** supported." - name: logical_date_gte in: query required: false diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py index 767853dda4a25..62487e67f0b57 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py @@ -31,6 +31,7 @@ QueryOffset, QueryXComDagDisplayNamePatternSearch, QueryXComKeyPatternSearch, + QueryXComMapIndexPatternSearch, QueryXComRunIdPatternSearch, QueryXComTaskIdPatternSearch, RangeFilter, @@ -141,6 +142,7 @@ def get_xcom_entries( dag_display_name_pattern: QueryXComDagDisplayNamePatternSearch, run_id_pattern: QueryXComRunIdPatternSearch, task_id_pattern: QueryXComTaskIdPatternSearch, + map_index_pattern: QueryXComMapIndexPatternSearch, logical_date_range: Annotated[RangeFilter, Depends(datetime_range_filter_factory("logical_date", DR))], run_after_range: Annotated[RangeFilter, Depends(datetime_range_filter_factory("run_after", DR))], map_index: Annotated[int | None, Query(ge=-1)] = None, @@ -176,6 +178,7 @@ def get_xcom_entries( dag_display_name_pattern, run_id_pattern, task_id_pattern, + map_index_pattern, logical_date_range, run_after_range, ], diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts index d55af783ea7ea..fd4bbc86f065e 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts @@ -574,7 +574,7 @@ export const UseXcomServiceGetXcomEntryKeyFn = ({ dagId, dagRunId, deserialize, export type XcomServiceGetXcomEntriesDefaultResponse = Awaited>; export type XcomServiceGetXcomEntriesQueryResult = UseQueryResult; export const useXcomServiceGetXcomEntriesKey = "XcomServiceGetXcomEntries"; -export const UseXcomServiceGetXcomEntriesKeyFn = ({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { +export const UseXcomServiceGetXcomEntriesKeyFn = ({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { dagDisplayNamePattern?: string; dagId: string; dagRunId: string; @@ -582,6 +582,7 @@ export const UseXcomServiceGetXcomEntriesKeyFn = ({ dagDisplayNamePattern, dagId logicalDateGte?: string; logicalDateLte?: string; mapIndex?: number; + mapIndexPattern?: string; offset?: number; runAfterGte?: string; runAfterLte?: string; @@ -589,7 +590,7 @@ export const UseXcomServiceGetXcomEntriesKeyFn = ({ dagDisplayNamePattern, dagId taskId: string; taskIdPattern?: string; xcomKeyPattern?: string; -}, queryKey?: Array) => [useXcomServiceGetXcomEntriesKey, ...(queryKey ?? [{ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }])]; +}, queryKey?: Array) => [useXcomServiceGetXcomEntriesKey, ...(queryKey ?? [{ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }])]; export type TaskServiceGetTasksDefaultResponse = Awaited>; export type TaskServiceGetTasksQueryResult = UseQueryResult; export const useTaskServiceGetTasksKey = "TaskServiceGetTasks"; diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts index 18b6bc0811ef0..16eebffdbbec8 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts @@ -1091,6 +1091,7 @@ export const ensureUseXcomServiceGetXcomEntryData = (queryClient: QueryClient, { * @param data.dagDisplayNamePattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.runIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. +* @param data.mapIndexPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.logicalDateGte * @param data.logicalDateLte * @param data.runAfterGte @@ -1098,7 +1099,7 @@ export const ensureUseXcomServiceGetXcomEntryData = (queryClient: QueryClient, { * @returns XComCollectionResponse Successful Response * @throws ApiError */ -export const ensureUseXcomServiceGetXcomEntriesData = (queryClient: QueryClient, { dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { +export const ensureUseXcomServiceGetXcomEntriesData = (queryClient: QueryClient, { dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { dagDisplayNamePattern?: string; dagId: string; dagRunId: string; @@ -1106,6 +1107,7 @@ export const ensureUseXcomServiceGetXcomEntriesData = (queryClient: QueryClient, logicalDateGte?: string; logicalDateLte?: string; mapIndex?: number; + mapIndexPattern?: string; offset?: number; runAfterGte?: string; runAfterLte?: string; @@ -1113,7 +1115,7 @@ export const ensureUseXcomServiceGetXcomEntriesData = (queryClient: QueryClient, taskId: string; taskIdPattern?: string; xcomKeyPattern?: string; -}) => queryClient.ensureQueryData({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) }); +}) => queryClient.ensureQueryData({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts index 71ec91a988432..aedbb73f4abc3 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts @@ -1091,6 +1091,7 @@ export const prefetchUseXcomServiceGetXcomEntry = (queryClient: QueryClient, { d * @param data.dagDisplayNamePattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.runIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. +* @param data.mapIndexPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.logicalDateGte * @param data.logicalDateLte * @param data.runAfterGte @@ -1098,7 +1099,7 @@ export const prefetchUseXcomServiceGetXcomEntry = (queryClient: QueryClient, { d * @returns XComCollectionResponse Successful Response * @throws ApiError */ -export const prefetchUseXcomServiceGetXcomEntries = (queryClient: QueryClient, { dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { +export const prefetchUseXcomServiceGetXcomEntries = (queryClient: QueryClient, { dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { dagDisplayNamePattern?: string; dagId: string; dagRunId: string; @@ -1106,6 +1107,7 @@ export const prefetchUseXcomServiceGetXcomEntries = (queryClient: QueryClient, { logicalDateGte?: string; logicalDateLte?: string; mapIndex?: number; + mapIndexPattern?: string; offset?: number; runAfterGte?: string; runAfterLte?: string; @@ -1113,7 +1115,7 @@ export const prefetchUseXcomServiceGetXcomEntries = (queryClient: QueryClient, { taskId: string; taskIdPattern?: string; xcomKeyPattern?: string; -}) => queryClient.prefetchQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) }); +}) => queryClient.prefetchQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts index 5cfdf8d0e1efa..33c804fcfaa9c 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts @@ -1091,6 +1091,7 @@ export const useXcomServiceGetXcomEntry = = unknown[]>({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { +export const useXcomServiceGetXcomEntries = = unknown[]>({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { dagDisplayNamePattern?: string; dagId: string; dagRunId: string; @@ -1106,6 +1107,7 @@ export const useXcomServiceGetXcomEntries = , "queryKey" | "queryFn">) => useQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) as TData, ...options }); +}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) as TData, ...options }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts index a1009c0e67ec3..9df6d80657d4f 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts @@ -1091,6 +1091,7 @@ export const useXcomServiceGetXcomEntrySuspense = = unknown[]>({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { +export const useXcomServiceGetXcomEntriesSuspense = = unknown[]>({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { dagDisplayNamePattern?: string; dagId: string; dagRunId: string; @@ -1106,6 +1107,7 @@ export const useXcomServiceGetXcomEntriesSuspense = , "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) as TData, ...options }); +}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) as TData, ...options }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts b/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts index cdd95c0492995..eb303b5eb1f67 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts @@ -3021,6 +3021,7 @@ export class XcomService { * @param data.dagDisplayNamePattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.runIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. + * @param data.mapIndexPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.logicalDateGte * @param data.logicalDateLte * @param data.runAfterGte @@ -3045,6 +3046,7 @@ export class XcomService { dag_display_name_pattern: data.dagDisplayNamePattern, run_id_pattern: data.runIdPattern, task_id_pattern: data.taskIdPattern, + map_index_pattern: data.mapIndexPattern, logical_date_gte: data.logicalDateGte, logical_date_lte: data.logicalDateLte, run_after_gte: data.runAfterGte, 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 86430e8c94003..5681133589505 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 @@ -2819,6 +2819,10 @@ export type GetXcomEntriesData = { logicalDateGte?: string | null; logicalDateLte?: string | null; mapIndex?: number | null; + /** + * SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. + */ + mapIndexPattern?: string | null; offset?: number; runAfterGte?: string | null; runAfterLte?: string | null; 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 f4f71931ea09c..74950ad83f572 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 @@ -97,6 +97,7 @@ "keyPlaceholder": "Filter by XCom key", "logicalDateFromPlaceholder": "Logical Date From", "logicalDateToPlaceholder": "Logical Date To", + "mapIndexPlaceholder": "Filter by Map Index", "runAfterFromPlaceholder": "Run After From", "runAfterToPlaceholder": "Run After To", "runIdPlaceholder": "Filter by Run ID", diff --git a/airflow-core/src/airflow/ui/src/constants/searchParams.ts b/airflow-core/src/airflow/ui/src/constants/searchParams.ts index 31b651a5fb008..8f4321d16bdeb 100644 --- a/airflow-core/src/airflow/ui/src/constants/searchParams.ts +++ b/airflow-core/src/airflow/ui/src/constants/searchParams.ts @@ -28,6 +28,7 @@ export enum SearchParamsKeys { LOG_LEVEL = "log_level", LOGICAL_DATE_GTE = "logical_date_gte", LOGICAL_DATE_LTE = "logical_date_lte", + MAP_INDEX_PATTERN = "map_index_pattern", NAME_PATTERN = "name_pattern", OFFSET = "offset", OWNERS = "owners", diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx b/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx index 401aade0117ee..f0bcca41c3fdd 100644 --- a/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx +++ b/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx @@ -36,6 +36,7 @@ import { XComFilters } from "./XComFilters"; const { DAG_DISPLAY_NAME_PATTERN: DAG_DISPLAY_NAME_PATTERN_PARAM, KEY_PATTERN: KEY_PATTERN_PARAM, + MAP_INDEX_PATTERN: MAP_INDEX_PATTERN_PARAM, RUN_ID_PATTERN: RUN_ID_PATTERN_PARAM, TASK_ID_PATTERN: TASK_ID_PATTERN_PARAM, }: SearchParamsKeysType = SearchParamsKeys; @@ -116,6 +117,7 @@ export const XCom = () => { const filteredKey = searchParams.get(KEY_PATTERN_PARAM); const filteredDagDisplayName = searchParams.get(DAG_DISPLAY_NAME_PATTERN_PARAM); + const filteredMapIndex = searchParams.get(MAP_INDEX_PATTERN_PARAM); const filteredRunId = searchParams.get(RUN_ID_PATTERN_PARAM); const filteredTaskId = searchParams.get(TASK_ID_PATTERN_PARAM); @@ -134,6 +136,7 @@ export const XCom = () => { logicalDateGte: logicalDateGte ?? undefined, logicalDateLte: logicalDateLte ?? undefined, mapIndex: mapIndex === "-1" ? undefined : parseInt(mapIndex, 10), + mapIndexPattern: filteredMapIndex ?? undefined, offset: pagination.pageIndex * pagination.pageSize, runAfterGte: runAfterGte ?? undefined, runAfterLte: runAfterLte ?? undefined, diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx b/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx index 9e6cc6baa5d8b..08210ce1931a6 100644 --- a/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx +++ b/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx @@ -52,6 +52,12 @@ const FILTERS = [ translationKey: "taskIdPlaceholder", type: "search", }, + { + hotkeyDisabled: true, + key: SearchParamsKeys.MAP_INDEX_PATTERN, + translationKey: "mapIndexPlaceholder", + type: "search", + }, { key: SearchParamsKeys.LOGICAL_DATE_GTE, translationKey: "logicalDateFromPlaceholder", From 3c25d1411318a6b06971fec73c639de6b2a50b0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=96=86=E5=AE=B8?= Date: Sat, 9 Aug 2025 03:41:05 +0800 Subject: [PATCH 17/22] fix(api,ui): Change map_index filter to exact match --- .../src/airflow/api_fastapi/common/parameters.py | 5 +++-- .../core_api/openapi/v2-rest-api-generated.yaml | 13 ------------- .../api_fastapi/core_api/routes/public/xcom.py | 6 +++--- .../src/airflow/ui/openapi-gen/queries/common.ts | 5 ++--- .../ui/openapi-gen/queries/ensureQueryData.ts | 6 ++---- .../src/airflow/ui/openapi-gen/queries/prefetch.ts | 6 ++---- .../src/airflow/ui/openapi-gen/queries/queries.ts | 6 ++---- .../src/airflow/ui/openapi-gen/queries/suspense.ts | 6 ++---- .../airflow/ui/openapi-gen/requests/services.gen.ts | 2 -- .../airflow/ui/openapi-gen/requests/types.gen.ts | 4 ---- .../src/airflow/ui/src/constants/searchParams.ts | 2 +- airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx | 12 ++++++++---- .../src/airflow/ui/src/pages/XCom/XComFilters.tsx | 2 +- 13 files changed, 26 insertions(+), 49 deletions(-) diff --git a/airflow-core/src/airflow/api_fastapi/common/parameters.py b/airflow-core/src/airflow/api_fastapi/common/parameters.py index c54b6c7078166..2907dd9cb99f8 100644 --- a/airflow-core/src/airflow/api_fastapi/common/parameters.py +++ b/airflow-core/src/airflow/api_fastapi/common/parameters.py @@ -747,8 +747,9 @@ def _transform_ti_states(states: list[str] | None) -> list[TaskInstanceState | N QueryXComTaskIdPatternSearch = Annotated[ _SearchParam, Depends(search_param_factory(XComModel.task_id, "task_id_pattern")) ] -QueryXComMapIndexPatternSearch = Annotated[ - _SearchParam, Depends(search_param_factory(XComModel.map_index, "map_index_pattern")) +QueryXComMapIndexFilter = Annotated[ + FilterParam[int | None], + Depends(filter_param_factory(XComModel.map_index, int | None, filter_name="map_index")), ] # Assets 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 1eb5b9ad6bbb9..7ce698e5b5c90 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 @@ -4640,7 +4640,6 @@ paths: schema: anyOf: - type: integer - minimum: -1 - type: 'null' title: Map Index - name: limit @@ -4707,18 +4706,6 @@ paths: title: Task Id Pattern description: "SQL LIKE expression \u2014 use `%` / `_` wildcards (e.g. `%customer_%`).\ \ Regular expressions are **not** supported." - - name: map_index_pattern - in: query - required: false - schema: - anyOf: - - type: string - - type: 'null' - description: "SQL LIKE expression \u2014 use `%` / `_` wildcards (e.g. `%customer_%`).\ - \ Regular expressions are **not** supported." - title: Map Index Pattern - description: "SQL LIKE expression \u2014 use `%` / `_` wildcards (e.g. `%customer_%`).\ - \ Regular expressions are **not** supported." - name: logical_date_gte in: query required: false diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py index 62487e67f0b57..7cc2abd9bdafa 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py @@ -31,7 +31,7 @@ QueryOffset, QueryXComDagDisplayNamePatternSearch, QueryXComKeyPatternSearch, - QueryXComMapIndexPatternSearch, + QueryXComMapIndexFilter, QueryXComRunIdPatternSearch, QueryXComTaskIdPatternSearch, RangeFilter, @@ -142,7 +142,7 @@ def get_xcom_entries( dag_display_name_pattern: QueryXComDagDisplayNamePatternSearch, run_id_pattern: QueryXComRunIdPatternSearch, task_id_pattern: QueryXComTaskIdPatternSearch, - map_index_pattern: QueryXComMapIndexPatternSearch, + map_index_filter: QueryXComMapIndexFilter, logical_date_range: Annotated[RangeFilter, Depends(datetime_range_filter_factory("logical_date", DR))], run_after_range: Annotated[RangeFilter, Depends(datetime_range_filter_factory("run_after", DR))], map_index: Annotated[int | None, Query(ge=-1)] = None, @@ -178,7 +178,7 @@ def get_xcom_entries( dag_display_name_pattern, run_id_pattern, task_id_pattern, - map_index_pattern, + map_index_filter, logical_date_range, run_after_range, ], diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts index fd4bbc86f065e..d55af783ea7ea 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts @@ -574,7 +574,7 @@ export const UseXcomServiceGetXcomEntryKeyFn = ({ dagId, dagRunId, deserialize, export type XcomServiceGetXcomEntriesDefaultResponse = Awaited>; export type XcomServiceGetXcomEntriesQueryResult = UseQueryResult; export const useXcomServiceGetXcomEntriesKey = "XcomServiceGetXcomEntries"; -export const UseXcomServiceGetXcomEntriesKeyFn = ({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { +export const UseXcomServiceGetXcomEntriesKeyFn = ({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { dagDisplayNamePattern?: string; dagId: string; dagRunId: string; @@ -582,7 +582,6 @@ export const UseXcomServiceGetXcomEntriesKeyFn = ({ dagDisplayNamePattern, dagId logicalDateGte?: string; logicalDateLte?: string; mapIndex?: number; - mapIndexPattern?: string; offset?: number; runAfterGte?: string; runAfterLte?: string; @@ -590,7 +589,7 @@ export const UseXcomServiceGetXcomEntriesKeyFn = ({ dagDisplayNamePattern, dagId taskId: string; taskIdPattern?: string; xcomKeyPattern?: string; -}, queryKey?: Array) => [useXcomServiceGetXcomEntriesKey, ...(queryKey ?? [{ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }])]; +}, queryKey?: Array) => [useXcomServiceGetXcomEntriesKey, ...(queryKey ?? [{ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }])]; export type TaskServiceGetTasksDefaultResponse = Awaited>; export type TaskServiceGetTasksQueryResult = UseQueryResult; export const useTaskServiceGetTasksKey = "TaskServiceGetTasks"; diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts index 16eebffdbbec8..18b6bc0811ef0 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts @@ -1091,7 +1091,6 @@ export const ensureUseXcomServiceGetXcomEntryData = (queryClient: QueryClient, { * @param data.dagDisplayNamePattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.runIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. -* @param data.mapIndexPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.logicalDateGte * @param data.logicalDateLte * @param data.runAfterGte @@ -1099,7 +1098,7 @@ export const ensureUseXcomServiceGetXcomEntryData = (queryClient: QueryClient, { * @returns XComCollectionResponse Successful Response * @throws ApiError */ -export const ensureUseXcomServiceGetXcomEntriesData = (queryClient: QueryClient, { dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { +export const ensureUseXcomServiceGetXcomEntriesData = (queryClient: QueryClient, { dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { dagDisplayNamePattern?: string; dagId: string; dagRunId: string; @@ -1107,7 +1106,6 @@ export const ensureUseXcomServiceGetXcomEntriesData = (queryClient: QueryClient, logicalDateGte?: string; logicalDateLte?: string; mapIndex?: number; - mapIndexPattern?: string; offset?: number; runAfterGte?: string; runAfterLte?: string; @@ -1115,7 +1113,7 @@ export const ensureUseXcomServiceGetXcomEntriesData = (queryClient: QueryClient, taskId: string; taskIdPattern?: string; xcomKeyPattern?: string; -}) => queryClient.ensureQueryData({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) }); +}) => queryClient.ensureQueryData({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts index aedbb73f4abc3..71ec91a988432 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts @@ -1091,7 +1091,6 @@ export const prefetchUseXcomServiceGetXcomEntry = (queryClient: QueryClient, { d * @param data.dagDisplayNamePattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.runIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. -* @param data.mapIndexPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.logicalDateGte * @param data.logicalDateLte * @param data.runAfterGte @@ -1099,7 +1098,7 @@ export const prefetchUseXcomServiceGetXcomEntry = (queryClient: QueryClient, { d * @returns XComCollectionResponse Successful Response * @throws ApiError */ -export const prefetchUseXcomServiceGetXcomEntries = (queryClient: QueryClient, { dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { +export const prefetchUseXcomServiceGetXcomEntries = (queryClient: QueryClient, { dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { dagDisplayNamePattern?: string; dagId: string; dagRunId: string; @@ -1107,7 +1106,6 @@ export const prefetchUseXcomServiceGetXcomEntries = (queryClient: QueryClient, { logicalDateGte?: string; logicalDateLte?: string; mapIndex?: number; - mapIndexPattern?: string; offset?: number; runAfterGte?: string; runAfterLte?: string; @@ -1115,7 +1113,7 @@ export const prefetchUseXcomServiceGetXcomEntries = (queryClient: QueryClient, { taskId: string; taskIdPattern?: string; xcomKeyPattern?: string; -}) => queryClient.prefetchQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) }); +}) => queryClient.prefetchQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts index 33c804fcfaa9c..5cfdf8d0e1efa 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts @@ -1091,7 +1091,6 @@ export const useXcomServiceGetXcomEntry = = unknown[]>({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { +export const useXcomServiceGetXcomEntries = = unknown[]>({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { dagDisplayNamePattern?: string; dagId: string; dagRunId: string; @@ -1107,7 +1106,6 @@ export const useXcomServiceGetXcomEntries = , "queryKey" | "queryFn">) => useQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) as TData, ...options }); +}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) as TData, ...options }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts index 9df6d80657d4f..a1009c0e67ec3 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts @@ -1091,7 +1091,6 @@ export const useXcomServiceGetXcomEntrySuspense = = unknown[]>({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { +export const useXcomServiceGetXcomEntriesSuspense = = unknown[]>({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { dagDisplayNamePattern?: string; dagId: string; dagRunId: string; @@ -1107,7 +1106,6 @@ export const useXcomServiceGetXcomEntriesSuspense = , "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexPattern, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) as TData, ...options }); +}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) as TData, ...options }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts b/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts index eb303b5eb1f67..cdd95c0492995 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts @@ -3021,7 +3021,6 @@ export class XcomService { * @param data.dagDisplayNamePattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.runIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. - * @param data.mapIndexPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.logicalDateGte * @param data.logicalDateLte * @param data.runAfterGte @@ -3046,7 +3045,6 @@ export class XcomService { dag_display_name_pattern: data.dagDisplayNamePattern, run_id_pattern: data.runIdPattern, task_id_pattern: data.taskIdPattern, - map_index_pattern: data.mapIndexPattern, logical_date_gte: data.logicalDateGte, logical_date_lte: data.logicalDateLte, run_after_gte: data.runAfterGte, 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 5681133589505..86430e8c94003 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 @@ -2819,10 +2819,6 @@ export type GetXcomEntriesData = { logicalDateGte?: string | null; logicalDateLte?: string | null; mapIndex?: number | null; - /** - * SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. - */ - mapIndexPattern?: string | null; offset?: number; runAfterGte?: string | null; runAfterLte?: string | null; diff --git a/airflow-core/src/airflow/ui/src/constants/searchParams.ts b/airflow-core/src/airflow/ui/src/constants/searchParams.ts index 8f4321d16bdeb..9b58fa99197ba 100644 --- a/airflow-core/src/airflow/ui/src/constants/searchParams.ts +++ b/airflow-core/src/airflow/ui/src/constants/searchParams.ts @@ -28,7 +28,7 @@ export enum SearchParamsKeys { LOG_LEVEL = "log_level", LOGICAL_DATE_GTE = "logical_date_gte", LOGICAL_DATE_LTE = "logical_date_lte", - MAP_INDEX_PATTERN = "map_index_pattern", + MAP_INDEX = "map_index", NAME_PATTERN = "name_pattern", OFFSET = "offset", OWNERS = "owners", diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx b/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx index f0bcca41c3fdd..eaf9a5ecaba56 100644 --- a/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx +++ b/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx @@ -36,7 +36,7 @@ import { XComFilters } from "./XComFilters"; const { DAG_DISPLAY_NAME_PATTERN: DAG_DISPLAY_NAME_PATTERN_PARAM, KEY_PATTERN: KEY_PATTERN_PARAM, - MAP_INDEX_PATTERN: MAP_INDEX_PATTERN_PARAM, + MAP_INDEX: MAP_INDEX_PARAM, RUN_ID_PATTERN: RUN_ID_PATTERN_PARAM, TASK_ID_PATTERN: TASK_ID_PATTERN_PARAM, }: SearchParamsKeysType = SearchParamsKeys; @@ -117,7 +117,7 @@ export const XCom = () => { const filteredKey = searchParams.get(KEY_PATTERN_PARAM); const filteredDagDisplayName = searchParams.get(DAG_DISPLAY_NAME_PATTERN_PARAM); - const filteredMapIndex = searchParams.get(MAP_INDEX_PATTERN_PARAM); + const filteredMapIndex = searchParams.get(MAP_INDEX_PARAM); const filteredRunId = searchParams.get(RUN_ID_PATTERN_PARAM); const filteredTaskId = searchParams.get(TASK_ID_PATTERN_PARAM); @@ -135,8 +135,12 @@ export const XCom = () => { limit: pagination.pageSize, logicalDateGte: logicalDateGte ?? undefined, logicalDateLte: logicalDateLte ?? undefined, - mapIndex: mapIndex === "-1" ? undefined : parseInt(mapIndex, 10), - mapIndexPattern: filteredMapIndex ?? undefined, + mapIndexFilter: + filteredMapIndex !== null && filteredMapIndex !== "" + ? parseInt(filteredMapIndex, 10) + : mapIndex === "-1" + ? undefined + : parseInt(mapIndex, 10), offset: pagination.pageIndex * pagination.pageSize, runAfterGte: runAfterGte ?? undefined, runAfterLte: runAfterLte ?? undefined, diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx b/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx index 08210ce1931a6..74b49e6ffa4bc 100644 --- a/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx +++ b/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx @@ -54,7 +54,7 @@ const FILTERS = [ }, { hotkeyDisabled: true, - key: SearchParamsKeys.MAP_INDEX_PATTERN, + key: SearchParamsKeys.MAP_INDEX, translationKey: "mapIndexPlaceholder", type: "search", }, From 306da8206cdfd887230cbb132f7d4cc0438afc55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=96=86=E5=AE=B8?= Date: Sat, 9 Aug 2025 04:04:22 +0800 Subject: [PATCH 18/22] fix(api,ui): Change map_index filter to exact match --- .../src/airflow/api_fastapi/common/parameters.py | 4 ---- .../core_api/openapi/v2-rest-api-generated.yaml | 16 ++++++++-------- .../api_fastapi/core_api/routes/public/xcom.py | 14 +++++++------- .../ui/openapi-gen/queries/ensureQueryData.ts | 2 +- .../airflow/ui/openapi-gen/queries/prefetch.ts | 2 +- .../airflow/ui/openapi-gen/queries/queries.ts | 2 +- .../airflow/ui/openapi-gen/queries/suspense.ts | 2 +- .../ui/openapi-gen/requests/services.gen.ts | 4 ++-- .../src/airflow/ui/src/pages/XCom/XCom.tsx | 2 +- 9 files changed, 22 insertions(+), 26 deletions(-) diff --git a/airflow-core/src/airflow/api_fastapi/common/parameters.py b/airflow-core/src/airflow/api_fastapi/common/parameters.py index 2907dd9cb99f8..f94be818c7d86 100644 --- a/airflow-core/src/airflow/api_fastapi/common/parameters.py +++ b/airflow-core/src/airflow/api_fastapi/common/parameters.py @@ -747,10 +747,6 @@ def _transform_ti_states(states: list[str] | None) -> list[TaskInstanceState | N QueryXComTaskIdPatternSearch = Annotated[ _SearchParam, Depends(search_param_factory(XComModel.task_id, "task_id_pattern")) ] -QueryXComMapIndexFilter = Annotated[ - FilterParam[int | None], - Depends(filter_param_factory(XComModel.map_index, int | None, filter_name="map_index")), -] # Assets QueryAssetNamePatternSearch = Annotated[ 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 7ce698e5b5c90..43614b04d0ba8 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 @@ -4634,14 +4634,6 @@ paths: schema: type: string title: Task Id - - name: map_index - in: query - required: false - schema: - anyOf: - - type: integer - - type: 'null' - title: Map Index - name: limit in: query required: false @@ -4706,6 +4698,14 @@ paths: title: Task Id Pattern description: "SQL LIKE expression \u2014 use `%` / `_` wildcards (e.g. `%customer_%`).\ \ Regular expressions are **not** supported." + - name: map_index + in: query + required: false + schema: + anyOf: + - type: integer + - type: 'null' + title: Map Index - name: logical_date_gte in: query required: false diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py index 7cc2abd9bdafa..e7bf1678bc2a2 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py @@ -27,15 +27,16 @@ from airflow.api_fastapi.common.dagbag import DagBagDep, get_dag_for_run_or_latest_version from airflow.api_fastapi.common.db.common import SessionDep, paginated_select from airflow.api_fastapi.common.parameters import ( + FilterParam, QueryLimit, QueryOffset, QueryXComDagDisplayNamePatternSearch, QueryXComKeyPatternSearch, - QueryXComMapIndexFilter, QueryXComRunIdPatternSearch, QueryXComTaskIdPatternSearch, RangeFilter, datetime_range_filter_factory, + filter_param_factory, ) from airflow.api_fastapi.common.router import AirflowRouter from airflow.api_fastapi.core_api.datamodels.xcom import ( @@ -142,10 +143,12 @@ def get_xcom_entries( dag_display_name_pattern: QueryXComDagDisplayNamePatternSearch, run_id_pattern: QueryXComRunIdPatternSearch, task_id_pattern: QueryXComTaskIdPatternSearch, - map_index_filter: QueryXComMapIndexFilter, + map_index: Annotated[ + FilterParam[int | None], + Depends(filter_param_factory(XComModel.map_index, int | None, filter_name="map_index")), + ], logical_date_range: Annotated[RangeFilter, Depends(datetime_range_filter_factory("logical_date", DR))], run_after_range: Annotated[RangeFilter, Depends(datetime_range_filter_factory("run_after", DR))], - map_index: Annotated[int | None, Query(ge=-1)] = None, ) -> XComCollectionResponse: """ Get all XCom entries. @@ -167,9 +170,6 @@ def get_xcom_entries( if dag_run_id != "~": query = query.where(DR.run_id == dag_run_id) - if map_index is not None: - query = query.where(XComModel.map_index == map_index) - query, total_entries = paginated_select( statement=query, filters=[ @@ -178,7 +178,7 @@ def get_xcom_entries( dag_display_name_pattern, run_id_pattern, task_id_pattern, - map_index_filter, + map_index, logical_date_range, run_after_range, ], diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts index 18b6bc0811ef0..5847ba11de644 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts @@ -1084,13 +1084,13 @@ export const ensureUseXcomServiceGetXcomEntryData = (queryClient: QueryClient, { * @param data.dagId * @param data.dagRunId * @param data.taskId -* @param data.mapIndex * @param data.limit * @param data.offset * @param data.xcomKeyPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.dagDisplayNamePattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.runIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. +* @param data.mapIndex * @param data.logicalDateGte * @param data.logicalDateLte * @param data.runAfterGte diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts index 71ec91a988432..9491b36c01702 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts @@ -1084,13 +1084,13 @@ export const prefetchUseXcomServiceGetXcomEntry = (queryClient: QueryClient, { d * @param data.dagId * @param data.dagRunId * @param data.taskId -* @param data.mapIndex * @param data.limit * @param data.offset * @param data.xcomKeyPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.dagDisplayNamePattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.runIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. +* @param data.mapIndex * @param data.logicalDateGte * @param data.logicalDateLte * @param data.runAfterGte diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts index 5cfdf8d0e1efa..aba65e05de205 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts @@ -1084,13 +1084,13 @@ export const useXcomServiceGetXcomEntry = { limit: pagination.pageSize, logicalDateGte: logicalDateGte ?? undefined, logicalDateLte: logicalDateLte ?? undefined, - mapIndexFilter: + mapIndex: filteredMapIndex !== null && filteredMapIndex !== "" ? parseInt(filteredMapIndex, 10) : mapIndex === "-1" From 19534e878dc5001986bc6f3e19f81179a82db19f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=96=86=E5=AE=B8?= Date: Mon, 11 Aug 2025 23:32:29 +0800 Subject: [PATCH 19/22] fix(api): keep xcom_key and map_index --- .../openapi/v2-rest-api-generated.yaml | 21 +++++++++++++++++-- .../core_api/routes/public/xcom.py | 13 ++++++++---- .../airflow/ui/openapi-gen/queries/common.ts | 6 ++++-- .../ui/openapi-gen/queries/ensureQueryData.ts | 10 ++++++--- .../ui/openapi-gen/queries/prefetch.ts | 10 ++++++--- .../airflow/ui/openapi-gen/queries/queries.ts | 10 ++++++--- .../ui/openapi-gen/queries/suspense.ts | 10 ++++++--- .../ui/openapi-gen/requests/services.gen.ts | 8 +++++-- .../ui/openapi-gen/requests/types.gen.ts | 2 ++ 9 files changed, 68 insertions(+), 22 deletions(-) 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 43614b04d0ba8..e29a6edc27240 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 @@ -4634,6 +4634,23 @@ paths: schema: type: string title: Task Id + - name: xcom_key + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + title: Xcom Key + - name: map_index + in: query + required: false + schema: + anyOf: + - type: integer + minimum: -1 + - type: 'null' + title: Map Index - name: limit in: query required: false @@ -4698,14 +4715,14 @@ paths: title: Task Id Pattern description: "SQL LIKE expression \u2014 use `%` / `_` wildcards (e.g. `%customer_%`).\ \ Regular expressions are **not** supported." - - name: map_index + - name: map_index_filter in: query required: false schema: anyOf: - type: integer - type: 'null' - title: Map Index + title: Map Index Filter - name: logical_date_gte in: query required: false diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py index e7bf1678bc2a2..2cdb89fcc88d9 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/xcom.py @@ -143,12 +143,14 @@ def get_xcom_entries( dag_display_name_pattern: QueryXComDagDisplayNamePatternSearch, run_id_pattern: QueryXComRunIdPatternSearch, task_id_pattern: QueryXComTaskIdPatternSearch, - map_index: Annotated[ + map_index_filter: Annotated[ FilterParam[int | None], - Depends(filter_param_factory(XComModel.map_index, int | None, filter_name="map_index")), + Depends(filter_param_factory(XComModel.map_index, int | None, filter_name="map_index_filter")), ], logical_date_range: Annotated[RangeFilter, Depends(datetime_range_filter_factory("logical_date", DR))], run_after_range: Annotated[RangeFilter, Depends(datetime_range_filter_factory("run_after", DR))], + xcom_key: Annotated[str | None, Query()] = None, + map_index: Annotated[int | None, Query(ge=-1)] = None, ) -> XComCollectionResponse: """ Get all XCom entries. @@ -166,9 +168,12 @@ def get_xcom_entries( if task_id != "~": query = query.where(XComModel.task_id == task_id) - if dag_run_id != "~": query = query.where(DR.run_id == dag_run_id) + if map_index is not None: + query = query.where(XComModel.map_index == map_index) + if xcom_key is not None: + query = query.where(XComModel.key == xcom_key) query, total_entries = paginated_select( statement=query, @@ -178,7 +183,7 @@ def get_xcom_entries( dag_display_name_pattern, run_id_pattern, task_id_pattern, - map_index, + map_index_filter, logical_date_range, run_after_range, ], diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts index d55af783ea7ea..f3ebb16c038df 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts @@ -574,7 +574,7 @@ export const UseXcomServiceGetXcomEntryKeyFn = ({ dagId, dagRunId, deserialize, export type XcomServiceGetXcomEntriesDefaultResponse = Awaited>; export type XcomServiceGetXcomEntriesQueryResult = UseQueryResult; export const useXcomServiceGetXcomEntriesKey = "XcomServiceGetXcomEntries"; -export const UseXcomServiceGetXcomEntriesKeyFn = ({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { +export const UseXcomServiceGetXcomEntriesKeyFn = ({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexFilter, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern }: { dagDisplayNamePattern?: string; dagId: string; dagRunId: string; @@ -582,14 +582,16 @@ export const UseXcomServiceGetXcomEntriesKeyFn = ({ dagDisplayNamePattern, dagId logicalDateGte?: string; logicalDateLte?: string; mapIndex?: number; + mapIndexFilter?: number; offset?: number; runAfterGte?: string; runAfterLte?: string; runIdPattern?: string; taskId: string; taskIdPattern?: string; + xcomKey?: string; xcomKeyPattern?: string; -}, queryKey?: Array) => [useXcomServiceGetXcomEntriesKey, ...(queryKey ?? [{ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }])]; +}, queryKey?: Array) => [useXcomServiceGetXcomEntriesKey, ...(queryKey ?? [{ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexFilter, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern }])]; export type TaskServiceGetTasksDefaultResponse = Awaited>; export type TaskServiceGetTasksQueryResult = UseQueryResult; export const useTaskServiceGetTasksKey = "TaskServiceGetTasks"; diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts index 5847ba11de644..d6c6b5b8eb29d 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts @@ -1084,13 +1084,15 @@ export const ensureUseXcomServiceGetXcomEntryData = (queryClient: QueryClient, { * @param data.dagId * @param data.dagRunId * @param data.taskId +* @param data.xcomKey +* @param data.mapIndex * @param data.limit * @param data.offset * @param data.xcomKeyPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.dagDisplayNamePattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.runIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. -* @param data.mapIndex +* @param data.mapIndexFilter * @param data.logicalDateGte * @param data.logicalDateLte * @param data.runAfterGte @@ -1098,7 +1100,7 @@ export const ensureUseXcomServiceGetXcomEntryData = (queryClient: QueryClient, { * @returns XComCollectionResponse Successful Response * @throws ApiError */ -export const ensureUseXcomServiceGetXcomEntriesData = (queryClient: QueryClient, { dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { +export const ensureUseXcomServiceGetXcomEntriesData = (queryClient: QueryClient, { dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexFilter, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern }: { dagDisplayNamePattern?: string; dagId: string; dagRunId: string; @@ -1106,14 +1108,16 @@ export const ensureUseXcomServiceGetXcomEntriesData = (queryClient: QueryClient, logicalDateGte?: string; logicalDateLte?: string; mapIndex?: number; + mapIndexFilter?: number; offset?: number; runAfterGte?: string; runAfterLte?: string; runIdPattern?: string; taskId: string; taskIdPattern?: string; + xcomKey?: string; xcomKeyPattern?: string; -}) => queryClient.ensureQueryData({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) }); +}) => queryClient.ensureQueryData({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexFilter, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern }), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexFilter, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern }) }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts index 9491b36c01702..78575beaf66bb 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts @@ -1084,13 +1084,15 @@ export const prefetchUseXcomServiceGetXcomEntry = (queryClient: QueryClient, { d * @param data.dagId * @param data.dagRunId * @param data.taskId +* @param data.xcomKey +* @param data.mapIndex * @param data.limit * @param data.offset * @param data.xcomKeyPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.dagDisplayNamePattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.runIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. -* @param data.mapIndex +* @param data.mapIndexFilter * @param data.logicalDateGte * @param data.logicalDateLte * @param data.runAfterGte @@ -1098,7 +1100,7 @@ export const prefetchUseXcomServiceGetXcomEntry = (queryClient: QueryClient, { d * @returns XComCollectionResponse Successful Response * @throws ApiError */ -export const prefetchUseXcomServiceGetXcomEntries = (queryClient: QueryClient, { dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { +export const prefetchUseXcomServiceGetXcomEntries = (queryClient: QueryClient, { dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexFilter, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern }: { dagDisplayNamePattern?: string; dagId: string; dagRunId: string; @@ -1106,14 +1108,16 @@ export const prefetchUseXcomServiceGetXcomEntries = (queryClient: QueryClient, { logicalDateGte?: string; logicalDateLte?: string; mapIndex?: number; + mapIndexFilter?: number; offset?: number; runAfterGte?: string; runAfterLte?: string; runIdPattern?: string; taskId: string; taskIdPattern?: string; + xcomKey?: string; xcomKeyPattern?: string; -}) => queryClient.prefetchQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) }); +}) => queryClient.prefetchQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexFilter, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern }), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexFilter, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern }) }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts index aba65e05de205..98b9b27af4f83 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts @@ -1084,13 +1084,15 @@ export const useXcomServiceGetXcomEntry = = unknown[]>({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { +export const useXcomServiceGetXcomEntries = = unknown[]>({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexFilter, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern }: { dagDisplayNamePattern?: string; dagId: string; dagRunId: string; @@ -1106,14 +1108,16 @@ export const useXcomServiceGetXcomEntries = , "queryKey" | "queryFn">) => useQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) as TData, ...options }); +}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexFilter, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexFilter, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern }) as TData, ...options }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts index 8dd2817cd0cb6..addbb057357ab 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts @@ -1084,13 +1084,15 @@ export const useXcomServiceGetXcomEntrySuspense = = unknown[]>({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }: { +export const useXcomServiceGetXcomEntriesSuspense = = unknown[]>({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexFilter, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern }: { dagDisplayNamePattern?: string; dagId: string; dagRunId: string; @@ -1106,14 +1108,16 @@ export const useXcomServiceGetXcomEntriesSuspense = , "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKeyPattern }) as TData, ...options }); +}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexFilter, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern }, queryKey), queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGte, logicalDateLte, mapIndex, mapIndexFilter, offset, runAfterGte, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern }) as TData, ...options }); /** * Get Tasks * Get tasks for DAG. diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts b/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts index 27d7f4f57007a..8db3850c4dc79 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts @@ -3014,13 +3014,15 @@ export class XcomService { * @param data.dagId * @param data.dagRunId * @param data.taskId + * @param data.xcomKey + * @param data.mapIndex * @param data.limit * @param data.offset * @param data.xcomKeyPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.dagDisplayNamePattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.runIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. - * @param data.mapIndex + * @param data.mapIndexFilter * @param data.logicalDateGte * @param data.logicalDateLte * @param data.runAfterGte @@ -3038,13 +3040,15 @@ export class XcomService { task_id: data.taskId }, query: { + xcom_key: data.xcomKey, + map_index: data.mapIndex, limit: data.limit, offset: data.offset, xcom_key_pattern: data.xcomKeyPattern, dag_display_name_pattern: data.dagDisplayNamePattern, run_id_pattern: data.runIdPattern, task_id_pattern: data.taskIdPattern, - map_index: data.mapIndex, + map_index_filter: data.mapIndexFilter, logical_date_gte: data.logicalDateGte, logical_date_lte: data.logicalDateLte, run_after_gte: data.runAfterGte, 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 86430e8c94003..715b4c99444e7 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 @@ -2819,6 +2819,7 @@ export type GetXcomEntriesData = { logicalDateGte?: string | null; logicalDateLte?: string | null; mapIndex?: number | null; + mapIndexFilter?: number | null; offset?: number; runAfterGte?: string | null; runAfterLte?: string | null; @@ -2831,6 +2832,7 @@ export type GetXcomEntriesData = { * SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. */ taskIdPattern?: string | null; + xcomKey?: string | null; /** * SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. */ From fa320ed0c946a76de7e15efa0dbcc14c1d22f31f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=96=86=E5=AE=B8?= Date: Tue, 12 Aug 2025 05:53:38 +0800 Subject: [PATCH 20/22] fix(ui): improve XCom filter UX with NumberInput --- .../airflow/ui/src/pages/XCom/XComFilters.tsx | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx b/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx index 74b49e6ffa4bc..aad6d6c306d47 100644 --- a/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx +++ b/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx @@ -25,6 +25,7 @@ import { useSearchParams } from "react-router-dom"; import { useTableURLState } from "src/components/DataTable/useTableUrlState"; import { DateTimeInput } from "src/components/DateTimeInput"; import { SearchBar } from "src/components/SearchBar"; +import { NumberInputField, NumberInputRoot } from "src/components/ui/NumberInput"; import { SearchParamsKeys } from "src/constants/searchParams"; const FILTERS = [ @@ -56,7 +57,7 @@ const FILTERS = [ hotkeyDisabled: true, key: SearchParamsKeys.MAP_INDEX, translationKey: "mapIndexPlaceholder", - type: "search", + type: "number", }, { key: SearchParamsKeys.LOGICAL_DATE_GTE, @@ -78,7 +79,12 @@ const FILTERS = [ translationKey: "runAfterToPlaceholder", type: "datetime", }, -] as const; +] as const satisfies ReadonlyArray<{ + readonly hotkeyDisabled?: boolean; + readonly key: string; + readonly translationKey: string; + readonly type: "datetime" | "number" | "search"; +}>; export const XComFilters = () => { const [searchParams, setSearchParams] = useSearchParams(); @@ -148,12 +154,21 @@ export const XComFilters = () => { /> ); })() - ) : ( + ) : type === "datetime" ? ( handleFilterChange(key)(event.target.value)} value={searchParams.get(key) ?? ""} /> + ) : ( + handleFilterChange(key)(details.value)} + value={searchParams.get(key) ?? ""} + > + + )} ); From d5a4ab69f4b31ff5e0aff9dfb58f7978977c787f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=96=86=E5=AE=B8?= Date: Tue, 12 Aug 2025 06:58:23 +0800 Subject: [PATCH 21/22] fix(ui): showing only relevant filters in TI Xcom tab --- .../airflow/ui/src/pages/XCom/XComFilters.tsx | 38 ++++++++++++++++--- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx b/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx index aad6d6c306d47..00759b0489cf6 100644 --- a/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx +++ b/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx @@ -20,7 +20,7 @@ import { Box, Button, HStack, Text, VStack } from "@chakra-ui/react"; import { useCallback, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { LuX } from "react-icons/lu"; -import { useSearchParams } from "react-router-dom"; +import { useSearchParams, useParams } from "react-router-dom"; import { useTableURLState } from "src/components/DataTable/useTableUrlState"; import { DateTimeInput } from "src/components/DateTimeInput"; @@ -88,11 +88,37 @@ const FILTERS = [ export const XComFilters = () => { const [searchParams, setSearchParams] = useSearchParams(); + const { dagId = "~", mapIndex = "-1", runId = "~", taskId = "~" } = useParams(); const { setTableURLState, tableURLState } = useTableURLState(); const { pagination, sorting } = tableURLState; const { t: translate } = useTranslation(["browse", "common"]); const [resetKey, setResetKey] = useState(0); + const visibleFilters = useMemo( + () => + FILTERS.filter((filter) => { + switch (filter.key) { + case SearchParamsKeys.DAG_DISPLAY_NAME_PATTERN: + return dagId === "~"; + case SearchParamsKeys.KEY_PATTERN: + case SearchParamsKeys.LOGICAL_DATE_GTE: + case SearchParamsKeys.LOGICAL_DATE_LTE: + case SearchParamsKeys.RUN_AFTER_GTE: + case SearchParamsKeys.RUN_AFTER_LTE: + return true; + case SearchParamsKeys.MAP_INDEX: + return mapIndex === "-1"; + case SearchParamsKeys.RUN_ID_PATTERN: + return runId === "~"; + case SearchParamsKeys.TASK_ID_PATTERN: + return taskId === "~"; + default: + return true; + } + }), + [dagId, mapIndex, runId, taskId], + ); + const handleFilterChange = useCallback( (paramKey: string) => (value: string) => { if (value === "") { @@ -111,16 +137,16 @@ export const XComFilters = () => { const filterCount = useMemo( () => - FILTERS.filter((filter) => { + visibleFilters.filter((filter) => { const value = searchParams.get(filter.key); return value !== null && value !== ""; }).length, - [searchParams], + [searchParams, visibleFilters], ); const handleResetFilters = useCallback(() => { - FILTERS.forEach((filter) => { + visibleFilters.forEach((filter) => { searchParams.delete(filter.key); }); setTableURLState({ @@ -129,7 +155,7 @@ export const XComFilters = () => { }); setSearchParams(searchParams); setResetKey((prev) => prev + 1); - }, [pagination, searchParams, setSearchParams, setTableURLState, sorting]); + }, [pagination, searchParams, setSearchParams, setTableURLState, sorting, visibleFilters]); const renderFilterInput = (filter: (typeof FILTERS)[number]) => { const { key, translationKey, type } = filter; @@ -177,7 +203,7 @@ export const XComFilters = () => { return ( - {FILTERS.map(renderFilterInput)} + {visibleFilters.map(renderFilterInput)}   From c68729e79b8e4cdac9a0e729a59533b38befd5b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=96=86=E5=AE=B8?= Date: Tue, 12 Aug 2025 07:04:27 +0800 Subject: [PATCH 22/22] fix(ui): replace unicode placeholder with CSS layout --- airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx b/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx index 00759b0489cf6..c7f36cfdd1c9f 100644 --- a/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx +++ b/airflow-core/src/airflow/ui/src/pages/XCom/XComFilters.tsx @@ -162,9 +162,9 @@ export const XComFilters = () => { return ( - - {type === "search" ? "\u00A0" : translate(`common:filters.${translationKey}`)} - + + {type !== "search" && {translate(`common:filters.${translationKey}`)}} + {type === "search" ? ( (() => { const { hotkeyDisabled } = filter;