This is an automated email from the ASF dual-hosted git repository.
bbovenzi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 1fb2d0e3648 Support ordering XCom entries in the REST API and UI
(#65418)
1fb2d0e3648 is described below
commit 1fb2d0e36487a4fd8daefa7b6a33f0010622a160
Author: Yuseok Jo <[email protected]>
AuthorDate: Tue Apr 21 23:24:22 2026 +0900
Support ordering XCom entries in the REST API and UI (#65418)
* Support ordering XCom entries in the REST API and UI
* Fix CI error in XComs e2e spec
* Replace networkidle wait in XComs sort test
---
.../src/airflow/api_fastapi/common/parameters.py | 13 ++++++++---
.../core_api/openapi/v2-rest-api-generated.yaml | 20 +++++++++++++++++
.../api_fastapi/core_api/routes/public/xcom.py | 15 ++++++++++---
.../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 +++--
.../ui/openapi-gen/requests/services.gen.ts | 4 +++-
.../airflow/ui/openapi-gen/requests/types.gen.ts | 4 ++++
.../src/airflow/ui/src/pages/XCom/XCom.tsx | 14 +++++-------
.../src/airflow/ui/tests/e2e/pages/XComsPage.ts | 26 ++++++++++++++++++++++
.../src/airflow/ui/tests/e2e/specs/xcoms.spec.ts | 8 +++++++
13 files changed, 108 insertions(+), 25 deletions(-)
diff --git a/airflow-core/src/airflow/api_fastapi/common/parameters.py
b/airflow-core/src/airflow/api_fastapi/common/parameters.py
index 0206c9d0ac6..28180b4d81f 100644
--- a/airflow-core/src/airflow/api_fastapi/common/parameters.py
+++ b/airflow-core/src/airflow/api_fastapi/common/parameters.py
@@ -18,7 +18,7 @@
from __future__ import annotations
from abc import ABC, abstractmethod
-from collections.abc import Callable, Iterable
+from collections.abc import Callable, Iterable, Sequence
from datetime import datetime
from enum import Enum
from typing import (
@@ -364,14 +364,21 @@ class SortParam(BaseParam[list[str]]):
def depends(cls, *args: Any, **kwargs: Any) -> Self:
raise NotImplementedError("Use dynamic_depends, depends not
implemented.")
- def dynamic_depends(self, default: str | None = None) -> Callable:
+ def dynamic_depends(self, default: str | Sequence[str] | None = None) ->
Callable:
to_replace_attrs = list(self.to_replace.keys()) if self.to_replace
else []
all_attrs = self.allowed_attrs + to_replace_attrs
+ if default is None:
+ default_list = [self.get_primary_key_string()]
+ elif isinstance(default, str):
+ default_list = [default]
+ else:
+ default_list = list(default)
+
def inner(
order_by: list[str] = Query(
- default=[default] if default is not None else
[self.get_primary_key_string()],
+ default=default_list,
description=f"Attributes to order by, multi criteria sort is
supported. Prefix with `-` for descending order. "
f"Supported attributes: `{', '.join(all_attrs) if all_attrs
else self.get_primary_key_string()}`",
),
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 8f892d0c657..2a4689ac3d3 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
@@ -5348,6 +5348,26 @@ paths:
format: date-time
- type: 'null'
title: Run After Lt
+ - name: order_by
+ in: query
+ required: false
+ schema:
+ type: array
+ items:
+ type: string
+ description: 'Attributes to order by, multi criteria sort is
supported.
+ Prefix with `-` for descending order. Supported attributes: `key,
dag_id,
+ run_id, task_id, map_index, timestamp, run_after`'
+ default:
+ - dag_id
+ - task_id
+ - run_id
+ - map_index
+ - key
+ title: Order By
+ description: 'Attributes to order by, multi criteria sort is
supported. Prefix
+ with `-` for descending order. Supported attributes: `key, dag_id,
run_id,
+ task_id, map_index, timestamp, run_after`'
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 06c67367ff8..df25de728f8 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
@@ -35,6 +35,7 @@ from airflow.api_fastapi.common.parameters import (
QueryXComRunIdPatternSearch,
QueryXComTaskIdPatternSearch,
RangeFilter,
+ SortParam,
datetime_range_filter_factory,
filter_param_factory,
)
@@ -161,6 +162,16 @@ def get_xcom_entries(
],
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))],
+ order_by: Annotated[
+ SortParam,
+ Depends(
+ SortParam(
+ ["key", "dag_id", "run_id", "task_id", "map_index",
"timestamp"],
+ XComModel,
+ to_replace={"run_after": DR.run_after},
+ ).dynamic_depends(default=("dag_id", "task_id", "run_id",
"map_index", "key"))
+ ),
+ ],
xcom_key: Annotated[str | None, Query()] = None,
map_index: Annotated[int | None, Query(ge=-1)] = None,
) -> XComCollectionResponse:
@@ -199,13 +210,11 @@ def get_xcom_entries(
logical_date_range,
run_after_range,
],
+ order_by=order_by,
offset=offset,
limit=limit,
session=session,
)
- query = query.order_by(
- XComModel.dag_id, XComModel.task_id, XComModel.run_id,
XComModel.map_index, XComModel.key
- )
return XComCollectionResponse(xcom_entries=session.scalars(query),
total_entries=total_entries)
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 ab44ca991b0..33f255bb9a9 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts
@@ -696,7 +696,7 @@ export const UseXcomServiceGetXcomEntryKeyFn = ({ dagId,
dagRunId, deserialize,
export type XcomServiceGetXcomEntriesDefaultResponse =
Awaited<ReturnType<typeof XcomService.getXcomEntries>>;
export type XcomServiceGetXcomEntriesQueryResult<TData =
XcomServiceGetXcomEntriesDefaultResponse, TError = unknown> =
UseQueryResult<TData, TError>;
export const useXcomServiceGetXcomEntriesKey = "XcomServiceGetXcomEntries";
-export const UseXcomServiceGetXcomEntriesKeyFn = ({ dagDisplayNamePattern,
dagId, dagRunId, limit, logicalDateGt, logicalDateGte, logicalDateLt,
logicalDateLte, mapIndex, mapIndexFilter, offset, runAfterGt, runAfterGte,
runAfterLt, runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKey,
xcomKeyPattern }: {
+export const UseXcomServiceGetXcomEntriesKeyFn = ({ dagDisplayNamePattern,
dagId, dagRunId, limit, logicalDateGt, logicalDateGte, logicalDateLt,
logicalDateLte, mapIndex, mapIndexFilter, offset, orderBy, runAfterGt,
runAfterGte, runAfterLt, runAfterLte, runIdPattern, taskId, taskIdPattern,
xcomKey, xcomKeyPattern }: {
dagDisplayNamePattern?: string;
dagId: string;
dagRunId: string;
@@ -708,6 +708,7 @@ export const UseXcomServiceGetXcomEntriesKeyFn = ({
dagDisplayNamePattern, dagId
mapIndex?: number;
mapIndexFilter?: number;
offset?: number;
+ orderBy?: string[];
runAfterGt?: string;
runAfterGte?: string;
runAfterLt?: string;
@@ -717,7 +718,7 @@ export const UseXcomServiceGetXcomEntriesKeyFn = ({
dagDisplayNamePattern, dagId
taskIdPattern?: string;
xcomKey?: string;
xcomKeyPattern?: string;
-}, queryKey?: Array<unknown>) => [useXcomServiceGetXcomEntriesKey,
...(queryKey ?? [{ dagDisplayNamePattern, dagId, dagRunId, limit,
logicalDateGt, logicalDateGte, logicalDateLt, logicalDateLte, mapIndex,
mapIndexFilter, offset, runAfterGt, runAfterGte, runAfterLt, runAfterLte,
runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern }])];
+}, queryKey?: Array<unknown>) => [useXcomServiceGetXcomEntriesKey,
...(queryKey ?? [{ dagDisplayNamePattern, dagId, dagRunId, limit,
logicalDateGt, logicalDateGte, logicalDateLt, logicalDateLte, mapIndex,
mapIndexFilter, offset, orderBy, runAfterGt, runAfterGte, runAfterLt,
runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern }])];
export type TaskServiceGetTasksDefaultResponse = Awaited<ReturnType<typeof
TaskService.getTasks>>;
export type TaskServiceGetTasksQueryResult<TData =
TaskServiceGetTasksDefaultResponse, TError = unknown> = UseQueryResult<TData,
TError>;
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 2cf744fc7f2..e9d82da6aac 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts
@@ -1352,10 +1352,11 @@ export const ensureUseXcomServiceGetXcomEntryData =
(queryClient: QueryClient, {
* @param data.runAfterGt
* @param data.runAfterLte
* @param data.runAfterLt
+* @param data.orderBy Attributes to order by, multi criteria sort is
supported. Prefix with `-` for descending order. Supported attributes: `key,
dag_id, run_id, task_id, map_index, timestamp, run_after`
* @returns XComCollectionResponse Successful Response
* @throws ApiError
*/
-export const ensureUseXcomServiceGetXcomEntriesData = (queryClient:
QueryClient, { dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGt,
logicalDateGte, logicalDateLt, logicalDateLte, mapIndex, mapIndexFilter,
offset, runAfterGt, runAfterGte, runAfterLt, runAfterLte, runIdPattern, taskId,
taskIdPattern, xcomKey, xcomKeyPattern }: {
+export const ensureUseXcomServiceGetXcomEntriesData = (queryClient:
QueryClient, { dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGt,
logicalDateGte, logicalDateLt, logicalDateLte, mapIndex, mapIndexFilter,
offset, orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte,
runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern }: {
dagDisplayNamePattern?: string;
dagId: string;
dagRunId: string;
@@ -1367,6 +1368,7 @@ export const ensureUseXcomServiceGetXcomEntriesData =
(queryClient: QueryClient,
mapIndex?: number;
mapIndexFilter?: number;
offset?: number;
+ orderBy?: string[];
runAfterGt?: string;
runAfterGte?: string;
runAfterLt?: string;
@@ -1376,7 +1378,7 @@ export const ensureUseXcomServiceGetXcomEntriesData =
(queryClient: QueryClient,
taskIdPattern?: string;
xcomKey?: string;
xcomKeyPattern?: string;
-}) => queryClient.ensureQueryData({ queryKey:
Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId,
dagRunId, limit, logicalDateGt, logicalDateGte, logicalDateLt, logicalDateLte,
mapIndex, mapIndexFilter, offset, runAfterGt, runAfterGte, runAfterLt,
runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern }),
queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId,
dagRunId, limit, logicalDateGt, logicalDateGte, logicalDateLt, logicalD [...]
+}) => queryClient.ensureQueryData({ queryKey:
Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId,
dagRunId, limit, logicalDateGt, logicalDateGte, logicalDateLt, logicalDateLte,
mapIndex, mapIndexFilter, offset, orderBy, runAfterGt, runAfterGte, runAfterLt,
runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern }),
queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId,
dagRunId, limit, logicalDateGt, logicalDateGte, logicalDateLt, [...]
/**
* 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 dabfe8452b9..af819e0110e 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts
@@ -1352,10 +1352,11 @@ export const prefetchUseXcomServiceGetXcomEntry =
(queryClient: QueryClient, { d
* @param data.runAfterGt
* @param data.runAfterLte
* @param data.runAfterLt
+* @param data.orderBy Attributes to order by, multi criteria sort is
supported. Prefix with `-` for descending order. Supported attributes: `key,
dag_id, run_id, task_id, map_index, timestamp, run_after`
* @returns XComCollectionResponse Successful Response
* @throws ApiError
*/
-export const prefetchUseXcomServiceGetXcomEntries = (queryClient: QueryClient,
{ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGt, logicalDateGte,
logicalDateLt, logicalDateLte, mapIndex, mapIndexFilter, offset, runAfterGt,
runAfterGte, runAfterLt, runAfterLte, runIdPattern, taskId, taskIdPattern,
xcomKey, xcomKeyPattern }: {
+export const prefetchUseXcomServiceGetXcomEntries = (queryClient: QueryClient,
{ dagDisplayNamePattern, dagId, dagRunId, limit, logicalDateGt, logicalDateGte,
logicalDateLt, logicalDateLte, mapIndex, mapIndexFilter, offset, orderBy,
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runIdPattern, taskId,
taskIdPattern, xcomKey, xcomKeyPattern }: {
dagDisplayNamePattern?: string;
dagId: string;
dagRunId: string;
@@ -1367,6 +1368,7 @@ export const prefetchUseXcomServiceGetXcomEntries =
(queryClient: QueryClient, {
mapIndex?: number;
mapIndexFilter?: number;
offset?: number;
+ orderBy?: string[];
runAfterGt?: string;
runAfterGte?: string;
runAfterLt?: string;
@@ -1376,7 +1378,7 @@ export const prefetchUseXcomServiceGetXcomEntries =
(queryClient: QueryClient, {
taskIdPattern?: string;
xcomKey?: string;
xcomKeyPattern?: string;
-}) => queryClient.prefetchQuery({ queryKey:
Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId,
dagRunId, limit, logicalDateGt, logicalDateGte, logicalDateLt, logicalDateLte,
mapIndex, mapIndexFilter, offset, runAfterGt, runAfterGte, runAfterLt,
runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern }),
queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId,
dagRunId, limit, logicalDateGt, logicalDateGte, logicalDateLt, logicalDat [...]
+}) => queryClient.prefetchQuery({ queryKey:
Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId,
dagRunId, limit, logicalDateGt, logicalDateGte, logicalDateLt, logicalDateLte,
mapIndex, mapIndexFilter, offset, orderBy, runAfterGt, runAfterGte, runAfterLt,
runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern }),
queryFn: () => XcomService.getXcomEntries({ dagDisplayNamePattern, dagId,
dagRunId, limit, logicalDateGt, logicalDateGte, logicalDateLt, l [...]
/**
* 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 ff44edc6c8e..87d27071007 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
@@ -1352,10 +1352,11 @@ export const useXcomServiceGetXcomEntry = <TData =
Common.XcomServiceGetXcomEntr
* @param data.runAfterGt
* @param data.runAfterLte
* @param data.runAfterLt
+* @param data.orderBy Attributes to order by, multi criteria sort is
supported. Prefix with `-` for descending order. Supported attributes: `key,
dag_id, run_id, task_id, map_index, timestamp, run_after`
* @returns XComCollectionResponse Successful Response
* @throws ApiError
*/
-export const useXcomServiceGetXcomEntries = <TData =
Common.XcomServiceGetXcomEntriesDefaultResponse, TError = unknown, TQueryKey
extends Array<unknown> = unknown[]>({ dagDisplayNamePattern, dagId, dagRunId,
limit, logicalDateGt, logicalDateGte, logicalDateLt, logicalDateLte, mapIndex,
mapIndexFilter, offset, runAfterGt, runAfterGte, runAfterLt, runAfterLte,
runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern }: {
+export const useXcomServiceGetXcomEntries = <TData =
Common.XcomServiceGetXcomEntriesDefaultResponse, TError = unknown, TQueryKey
extends Array<unknown> = unknown[]>({ dagDisplayNamePattern, dagId, dagRunId,
limit, logicalDateGt, logicalDateGte, logicalDateLt, logicalDateLte, mapIndex,
mapIndexFilter, offset, orderBy, runAfterGt, runAfterGte, runAfterLt,
runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern }: {
dagDisplayNamePattern?: string;
dagId: string;
dagRunId: string;
@@ -1367,6 +1368,7 @@ export const useXcomServiceGetXcomEntries = <TData =
Common.XcomServiceGetXcomEn
mapIndex?: number;
mapIndexFilter?: number;
offset?: number;
+ orderBy?: string[];
runAfterGt?: string;
runAfterGte?: string;
runAfterLt?: string;
@@ -1376,7 +1378,7 @@ export const useXcomServiceGetXcomEntries = <TData =
Common.XcomServiceGetXcomEn
taskIdPattern?: string;
xcomKey?: string;
xcomKeyPattern?: string;
-}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>,
"queryKey" | "queryFn">) => useQuery<TData, TError>({ queryKey:
Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId,
dagRunId, limit, logicalDateGt, logicalDateGte, logicalDateLt, logicalDateLte,
mapIndex, mapIndexFilter, offset, runAfterGt, runAfterGte, runAfterLt,
runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern },
queryKey), queryFn: () => XcomService.getXcomEntries({ d [...]
+}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>,
"queryKey" | "queryFn">) => useQuery<TData, TError>({ queryKey:
Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId,
dagRunId, limit, logicalDateGt, logicalDateGte, logicalDateLt, logicalDateLte,
mapIndex, mapIndexFilter, offset, orderBy, runAfterGt, runAfterGte, runAfterLt,
runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern },
queryKey), queryFn: () => XcomService.getXcomEn [...]
/**
* 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 f842542869e..161186260f7 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts
@@ -1352,10 +1352,11 @@ export const useXcomServiceGetXcomEntrySuspense =
<TData = Common.XcomServiceGet
* @param data.runAfterGt
* @param data.runAfterLte
* @param data.runAfterLt
+* @param data.orderBy Attributes to order by, multi criteria sort is
supported. Prefix with `-` for descending order. Supported attributes: `key,
dag_id, run_id, task_id, map_index, timestamp, run_after`
* @returns XComCollectionResponse Successful Response
* @throws ApiError
*/
-export const useXcomServiceGetXcomEntriesSuspense = <TData =
Common.XcomServiceGetXcomEntriesDefaultResponse, TError = unknown, TQueryKey
extends Array<unknown> = unknown[]>({ dagDisplayNamePattern, dagId, dagRunId,
limit, logicalDateGt, logicalDateGte, logicalDateLt, logicalDateLte, mapIndex,
mapIndexFilter, offset, runAfterGt, runAfterGte, runAfterLt, runAfterLte,
runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern }: {
+export const useXcomServiceGetXcomEntriesSuspense = <TData =
Common.XcomServiceGetXcomEntriesDefaultResponse, TError = unknown, TQueryKey
extends Array<unknown> = unknown[]>({ dagDisplayNamePattern, dagId, dagRunId,
limit, logicalDateGt, logicalDateGte, logicalDateLt, logicalDateLte, mapIndex,
mapIndexFilter, offset, orderBy, runAfterGt, runAfterGte, runAfterLt,
runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern }: {
dagDisplayNamePattern?: string;
dagId: string;
dagRunId: string;
@@ -1367,6 +1368,7 @@ export const useXcomServiceGetXcomEntriesSuspense =
<TData = Common.XcomServiceG
mapIndex?: number;
mapIndexFilter?: number;
offset?: number;
+ orderBy?: string[];
runAfterGt?: string;
runAfterGte?: string;
runAfterLt?: string;
@@ -1376,7 +1378,7 @@ export const useXcomServiceGetXcomEntriesSuspense =
<TData = Common.XcomServiceG
taskIdPattern?: string;
xcomKey?: string;
xcomKeyPattern?: string;
-}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>,
"queryKey" | "queryFn">) => useSuspenseQuery<TData, TError>({ queryKey:
Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId,
dagRunId, limit, logicalDateGt, logicalDateGte, logicalDateLt, logicalDateLte,
mapIndex, mapIndexFilter, offset, runAfterGt, runAfterGte, runAfterLt,
runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern },
queryKey), queryFn: () => XcomService.getXcomEnt [...]
+}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>,
"queryKey" | "queryFn">) => useSuspenseQuery<TData, TError>({ queryKey:
Common.UseXcomServiceGetXcomEntriesKeyFn({ dagDisplayNamePattern, dagId,
dagRunId, limit, logicalDateGt, logicalDateGte, logicalDateLt, logicalDateLte,
mapIndex, mapIndexFilter, offset, orderBy, runAfterGt, runAfterGte, runAfterLt,
runAfterLte, runIdPattern, taskId, taskIdPattern, xcomKey, xcomKeyPattern },
queryKey), queryFn: () => XcomService.g [...]
/**
* 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 bdb02550fa9..9d40444f7c4 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
@@ -3384,6 +3384,7 @@ export class XcomService {
* @param data.runAfterGt
* @param data.runAfterLte
* @param data.runAfterLt
+ * @param data.orderBy Attributes to order by, multi criteria sort is
supported. Prefix with `-` for descending order. Supported attributes: `key,
dag_id, run_id, task_id, map_index, timestamp, run_after`
* @returns XComCollectionResponse Successful Response
* @throws ApiError
*/
@@ -3413,7 +3414,8 @@ export class XcomService {
run_after_gte: data.runAfterGte,
run_after_gt: data.runAfterGt,
run_after_lte: data.runAfterLte,
- run_after_lt: data.runAfterLt
+ run_after_lt: data.runAfterLt,
+ order_by: data.orderBy
},
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 4b3efcaabeb..cd987dfd198 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
@@ -3512,6 +3512,10 @@ export type GetXcomEntriesData = {
mapIndex?: number | null;
mapIndexFilter?: number | null;
offset?: number;
+ /**
+ * Attributes to order by, multi criteria sort is supported. Prefix with
`-` for descending order. Supported attributes: `key, dag_id, run_id, task_id,
map_index, timestamp, run_after`
+ */
+ orderBy?: Array<(string)>;
runAfterGt?: string | null;
runAfterGte?: string | null;
runAfterLt?: string | null;
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 66895c76cc6..4bf1a5a8229 100644
--- a/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/XCom/XCom.tsx
@@ -54,7 +54,6 @@ type ColumnsProps = {
const getColumns = ({ open, translate }: ColumnsProps):
Array<ColumnDef<XComResponse>> => [
{
accessorKey: "key",
- enableSorting: false,
header: translate("xcom.columns.key"),
},
{
@@ -64,7 +63,6 @@ const getColumns = ({ open, translate }: ColumnsProps):
Array<ColumnDef<XComResp
<RouterLink
to={`/dags/${original.dag_id}`}>{original.dag_display_name}</RouterLink>
</Link>
),
- enableSorting: false,
header: translate("xcom.columns.dag"),
},
{
@@ -76,7 +74,6 @@ const getColumns = ({ open, translate }: ColumnsProps):
Array<ColumnDef<XComResp
</RouterLink>
</Link>
),
- enableSorting: false,
header: translate("common:dagRunId"),
},
{
@@ -88,7 +85,6 @@ const getColumns = ({ open, translate }: ColumnsProps):
Array<ColumnDef<XComResp
</RouterLink>
</Link>
),
- enableSorting: false,
header: translate("common:dagRun.runAfter"),
},
{
@@ -107,18 +103,15 @@ const getColumns = ({ open, translate }: ColumnsProps):
Array<ColumnDef<XComResp
</RouterLink>
</Link>
),
- enableSorting: false,
header: translate("common:task_one"),
},
{
accessorKey: "map_index",
- enableSorting: false,
header: translate("common:mapIndex"),
},
{
accessorKey: "timestamp",
cell: ({ row: { original } }) => <Time datetime={original.timestamp} />,
- enableSorting: false,
header: translate("dashboard:timestamp"),
},
{
@@ -152,7 +145,11 @@ 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 [sort] = sorting;
+ const orderBy = sort
+ ? [`${sort.desc ? "-" : ""}${sort.id === "task_display_name" ? "task_id" :
sort.id}`]
+ : undefined;
const [searchParams] = useSearchParams();
const { onClose, onOpen, open } = useDisclosure();
@@ -182,6 +179,7 @@ export const XCom = () => {
? undefined
: parseInt(mapIndex, 10),
offset: pagination.pageIndex * pagination.pageSize,
+ orderBy,
runAfterGte: runAfterGte ?? undefined,
runAfterLte: runAfterLte ?? undefined,
runIdPattern: filteredRunId ?? undefined,
diff --git a/airflow-core/src/airflow/ui/tests/e2e/pages/XComsPage.ts
b/airflow-core/src/airflow/ui/tests/e2e/pages/XComsPage.ts
index 16e850741e2..d32d787b82c 100644
--- a/airflow-core/src/airflow/ui/tests/e2e/pages/XComsPage.ts
+++ b/airflow-core/src/airflow/ui/tests/e2e/pages/XComsPage.ts
@@ -125,6 +125,32 @@ export class XComsPage extends BasePage {
}
}
+ public async verifySortByColumn(columnName: string): Promise<void> {
+ await this.navigate();
+
+ const columnHeader = this.xcomsTable.locator("thead th").filter({ hasText:
columnName });
+
+ await expect(columnHeader).toBeVisible({ timeout: 10_000 });
+
+ const firstSortResponse = this.page.waitForResponse(
+ (response) => response.url().includes("xcomEntries") && response.ok(),
+ { timeout: 10_000 },
+ );
+
+ await columnHeader.click();
+ await firstSortResponse;
+ await expect(this.page).toHaveURL(/sorting/);
+
+ const secondSortResponse = this.page.waitForResponse(
+ (response) => response.url().includes("xcomEntries") && response.ok(),
+ { timeout: 10_000 },
+ );
+
+ await columnHeader.click();
+ await secondSortResponse;
+ await expect(this.page).toHaveURL(/sorting/);
+ }
+
public async verifyXComDetailsDisplay(): Promise<void> {
const firstRow = this.xcomsTable.locator("tbody tr").first();
diff --git a/airflow-core/src/airflow/ui/tests/e2e/specs/xcoms.spec.ts
b/airflow-core/src/airflow/ui/tests/e2e/specs/xcoms.spec.ts
index 19fdb0bee77..dc3281652dc 100644
--- a/airflow-core/src/airflow/ui/tests/e2e/specs/xcoms.spec.ts
+++ b/airflow-core/src/airflow/ui/tests/e2e/specs/xcoms.spec.ts
@@ -57,4 +57,12 @@ test.describe("XComs Page", () => {
test("verify filtering by DAG display name", async ({ xcomRunsData,
xcomsPage }) => {
await xcomsPage.verifyDagDisplayNameFiltering(xcomRunsData.dagId);
});
+
+ test("verify sorting by key column", async ({ xcomsPage }) => {
+ await xcomsPage.verifySortByColumn("Key");
+ });
+
+ test("verify sorting by timestamp column", async ({ xcomsPage }) => {
+ await xcomsPage.verifySortByColumn("Timestamp");
+ });
});