From 856ddfba79b6ae50f3ae0036dd5974fe1502c047 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Fri, 17 Oct 2025 14:07:28 +0800 Subject: [PATCH 1/9] feat(hitl): hitl history ui --- .../openapi/v2-rest-api-generated.yaml | 72 +++++++++++++++++++ .../core_api/routes/public/hitl.py | 64 +++++++++++++---- .../airflow/ui/openapi-gen/queries/common.ts | 10 +++ .../ui/openapi-gen/queries/ensureQueryData.ts | 19 +++++ .../ui/openapi-gen/queries/prefetch.ts | 19 +++++ .../airflow/ui/openapi-gen/queries/queries.ts | 19 +++++ .../ui/openapi-gen/queries/suspense.ts | 19 +++++ .../ui/openapi-gen/requests/types.gen.ts | 37 ++++++++++ .../HITLTaskInstances/HITLResponseForm.tsx | 4 +- .../src/pages/TaskInstance/HITLResponse.tsx | 60 ++++++++++++++-- 10 files changed, 305 insertions(+), 18 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 1651927427f96..6833cd37bcbf8 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 @@ -8442,6 +8442,78 @@ paths: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' + /api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/{map_index}/hitlDetails/tries/{try_number}: + get: + tags: + - Task Instance + summary: Get Hitl Detail Try + description: Get a Human-in-the-loop detail of a specific task instance. + operationId: get_hitl_detail_try + security: + - OAuth2PasswordBearer: [] + - HTTPBearer: [] + parameters: + - name: dag_id + in: path + required: true + schema: + type: string + title: Dag Id + - name: dag_run_id + in: path + required: true + schema: + type: string + title: Dag Run Id + - name: task_id + in: path + required: true + schema: + type: string + title: Task Id + - name: map_index + in: path + required: true + schema: + type: integer + title: Map Index + - name: try_number + in: path + required: true + schema: + type: integer + title: Try Number + responses: + '200': + description: Successful Response + content: + application/json: + schema: + $ref: '#/components/schemas/HITLDetailHisotry' + '401': + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPExceptionResponse' + description: Unauthorized + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPExceptionResponse' + description: Forbidden + '404': + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPExceptionResponse' + description: Not Found + '422': + description: Validation Error + content: + application/json: + schema: + $ref: '#/components/schemas/HTTPValidationError' /api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/hitlDetails: get: tags: diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py index 5e3439c0938b7..7b69a42b9df1e 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py @@ -50,6 +50,7 @@ HITLDetailResponse, UpdateHITLDetailPayload, ) +from airflow.api_fastapi.core_api.datamodels.task_instance_history import HITLDetailHisotry from airflow.api_fastapi.core_api.openapi.exceptions import create_openapi_http_exception_doc from airflow.api_fastapi.core_api.security import ( GetUserDep, @@ -58,10 +59,12 @@ requires_access_dag, ) from airflow.api_fastapi.logging.decorators import action_logging +from airflow.models.base import Base from airflow.models.dag_version import DagVersion from airflow.models.dagrun import DagRun from airflow.models.hitl import HITLDetail as HITLDetailModel, HITLUser from airflow.models.taskinstance import TaskInstance as TI +from airflow.models.taskinstancehistory import TaskInstanceHistory as TIH task_instances_hitl_router = AirflowRouter( tags=["Task Instance"], @@ -78,21 +81,31 @@ def _get_task_instance_with_hitl_detail( task_id: str, session: SessionDep, map_index: int, -) -> TI: - query = ( - select(TI) - .where( - TI.dag_id == dag_id, - TI.run_id == dag_run_id, - TI.task_id == task_id, + try_number: int | None = None, +) -> TI | TIH: + def _query(orm_object: Base) -> TI | TIH | None: + query = ( + select(orm_object) + .where( + orm_object.dag_id == dag_id, + orm_object.run_id == dag_run_id, + orm_object.task_id == task_id, + orm_object.map_index == map_index, + ) + .options(joinedload(orm_object.hitl_detail)) ) - .options(joinedload(TI.hitl_detail)) - ) - if map_index is not None: - query = query.where(TI.map_index == map_index) + if try_number is not None: + query = query.where(orm_object.try_number == try_number) + + task_instance = session.scalar(query) + return task_instance + + if try_number is None: + task_instance = _query(TI) + else: + task_instance = _query(TIH) or _query(TI) - task_instance = session.scalar(query) if task_instance is None: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, @@ -198,10 +211,37 @@ def get_hitl_detail( task_id=task_id, session=session, map_index=map_index, + try_number=None, ) return task_instance.hitl_detail +@task_instances_hitl_router.get( + task_instance_hitl_path + "/tries/{try_number}", + status_code=status.HTTP_200_OK, + responses=create_openapi_http_exception_doc([status.HTTP_404_NOT_FOUND]), + dependencies=[Depends(requires_access_dag(method="GET", access_entity=DagAccessEntity.HITL_DETAIL))], +) +def get_hitl_detail_try( + dag_id: str, + dag_run_id: str, + task_id: str, + session: SessionDep, + map_index: int = -1, + try_number: int = -1, +) -> HITLDetailHisotry: + """Get a Human-in-the-loop detail of a specific task instance.""" + task_instance_history = _get_task_instance_with_hitl_detail( + dag_id=dag_id, + dag_run_id=dag_run_id, + task_id=task_id, + session=session, + map_index=map_index, + try_number=try_number if try_number != -1 else None, + ) + return task_instance_history.hitl_detail + + @task_instances_hitl_router.get( "/hitlDetails", status_code=status.HTTP_200_OK, 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 a70c2b886882f..22161f165370c 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts @@ -574,6 +574,16 @@ export const UseTaskInstanceServiceGetHitlDetailKeyFn = ({ dagId, dagRunId, mapI mapIndex: number; taskId: string; }, queryKey?: Array) => [useTaskInstanceServiceGetHitlDetailKey, ...(queryKey ?? [{ dagId, dagRunId, mapIndex, taskId }])]; +export type TaskInstanceServiceGetHitlDetailTryDefaultResponse = Awaited>; +export type TaskInstanceServiceGetHitlDetailTryQueryResult = UseQueryResult; +export const useTaskInstanceServiceGetHitlDetailTryKey = "TaskInstanceServiceGetHitlDetailTry"; +export const UseTaskInstanceServiceGetHitlDetailTryKeyFn = ({ dagId, dagRunId, mapIndex, taskId, tryNumber }: { + dagId: string; + dagRunId: string; + mapIndex: number; + taskId: string; + tryNumber: number; +}, queryKey?: Array) => [useTaskInstanceServiceGetHitlDetailTryKey, ...(queryKey ?? [{ dagId, dagRunId, mapIndex, taskId, tryNumber }])]; export type TaskInstanceServiceGetHitlDetailsDefaultResponse = Awaited>; export type TaskInstanceServiceGetHitlDetailsQueryResult = UseQueryResult; export const useTaskInstanceServiceGetHitlDetailsKey = "TaskInstanceServiceGetHitlDetails"; 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 dc540bda1b8e7..d7fe48d9a266a 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts @@ -1093,6 +1093,25 @@ export const ensureUseTaskInstanceServiceGetHitlDetailData = (queryClient: Query taskId: string; }) => queryClient.ensureQueryData({ queryKey: Common.UseTaskInstanceServiceGetHitlDetailKeyFn({ dagId, dagRunId, mapIndex, taskId }), queryFn: () => TaskInstanceService.getHitlDetail({ dagId, dagRunId, mapIndex, taskId }) }); /** +* Get Hitl Detail Try +* Get a Human-in-the-loop detail of a specific task instance. +* @param data The data for the request. +* @param data.dagId +* @param data.dagRunId +* @param data.taskId +* @param data.mapIndex +* @param data.tryNumber +* @returns HITLDetailHisotry Successful Response +* @throws ApiError +*/ +export const ensureUseTaskInstanceServiceGetHitlDetailTryData = (queryClient: QueryClient, { dagId, dagRunId, mapIndex, taskId, tryNumber }: { + dagId: string; + dagRunId: string; + mapIndex: number; + taskId: string; + tryNumber: number; +}) => queryClient.ensureQueryData({ queryKey: Common.UseTaskInstanceServiceGetHitlDetailTryKeyFn({ dagId, dagRunId, mapIndex, taskId, tryNumber }), queryFn: () => TaskInstanceService.getHitlDetailTry({ dagId, dagRunId, mapIndex, taskId, tryNumber }) }); +/** * Get Hitl Details * Get Human-in-the-loop details. * @param data The data for the request. 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 3a7c181f8c690..9e54a15cae0a6 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts @@ -1093,6 +1093,25 @@ export const prefetchUseTaskInstanceServiceGetHitlDetail = (queryClient: QueryCl taskId: string; }) => queryClient.prefetchQuery({ queryKey: Common.UseTaskInstanceServiceGetHitlDetailKeyFn({ dagId, dagRunId, mapIndex, taskId }), queryFn: () => TaskInstanceService.getHitlDetail({ dagId, dagRunId, mapIndex, taskId }) }); /** +* Get Hitl Detail Try +* Get a Human-in-the-loop detail of a specific task instance. +* @param data The data for the request. +* @param data.dagId +* @param data.dagRunId +* @param data.taskId +* @param data.mapIndex +* @param data.tryNumber +* @returns HITLDetailHisotry Successful Response +* @throws ApiError +*/ +export const prefetchUseTaskInstanceServiceGetHitlDetailTry = (queryClient: QueryClient, { dagId, dagRunId, mapIndex, taskId, tryNumber }: { + dagId: string; + dagRunId: string; + mapIndex: number; + taskId: string; + tryNumber: number; +}) => queryClient.prefetchQuery({ queryKey: Common.UseTaskInstanceServiceGetHitlDetailTryKeyFn({ dagId, dagRunId, mapIndex, taskId, tryNumber }), queryFn: () => TaskInstanceService.getHitlDetailTry({ dagId, dagRunId, mapIndex, taskId, tryNumber }) }); +/** * Get Hitl Details * Get Human-in-the-loop details. * @param data The data for the request. 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 f7ba76ecf7153..031a8cd98ec24 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts @@ -1093,6 +1093,25 @@ export const useTaskInstanceServiceGetHitlDetail = , "queryKey" | "queryFn">) => useQuery({ queryKey: Common.UseTaskInstanceServiceGetHitlDetailKeyFn({ dagId, dagRunId, mapIndex, taskId }, queryKey), queryFn: () => TaskInstanceService.getHitlDetail({ dagId, dagRunId, mapIndex, taskId }) as TData, ...options }); /** +* Get Hitl Detail Try +* Get a Human-in-the-loop detail of a specific task instance. +* @param data The data for the request. +* @param data.dagId +* @param data.dagRunId +* @param data.taskId +* @param data.mapIndex +* @param data.tryNumber +* @returns HITLDetailHisotry Successful Response +* @throws ApiError +*/ +export const useTaskInstanceServiceGetHitlDetailTry = = unknown[]>({ dagId, dagRunId, mapIndex, taskId, tryNumber }: { + dagId: string; + dagRunId: string; + mapIndex: number; + taskId: string; + tryNumber: number; +}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useQuery({ queryKey: Common.UseTaskInstanceServiceGetHitlDetailTryKeyFn({ dagId, dagRunId, mapIndex, taskId, tryNumber }, queryKey), queryFn: () => TaskInstanceService.getHitlDetailTry({ dagId, dagRunId, mapIndex, taskId, tryNumber }) as TData, ...options }); +/** * Get Hitl Details * Get Human-in-the-loop details. * @param data The data for the request. 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 40aa50fbc07ff..e3d560f9ae05f 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts @@ -1093,6 +1093,25 @@ export const useTaskInstanceServiceGetHitlDetailSuspense = , "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseTaskInstanceServiceGetHitlDetailKeyFn({ dagId, dagRunId, mapIndex, taskId }, queryKey), queryFn: () => TaskInstanceService.getHitlDetail({ dagId, dagRunId, mapIndex, taskId }) as TData, ...options }); /** +* Get Hitl Detail Try +* Get a Human-in-the-loop detail of a specific task instance. +* @param data The data for the request. +* @param data.dagId +* @param data.dagRunId +* @param data.taskId +* @param data.mapIndex +* @param data.tryNumber +* @returns HITLDetailHisotry Successful Response +* @throws ApiError +*/ +export const useTaskInstanceServiceGetHitlDetailTrySuspense = = unknown[]>({ dagId, dagRunId, mapIndex, taskId, tryNumber }: { + dagId: string; + dagRunId: string; + mapIndex: number; + taskId: string; + tryNumber: number; +}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseTaskInstanceServiceGetHitlDetailTryKeyFn({ dagId, dagRunId, mapIndex, taskId, tryNumber }, queryKey), queryFn: () => TaskInstanceService.getHitlDetailTry({ dagId, dagRunId, mapIndex, taskId, tryNumber }) as TData, ...options }); +/** * Get Hitl Details * Get Human-in-the-loop details. * @param data The data for the 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 af14ba786e646..c72bf3aade6c8 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 @@ -3054,6 +3054,16 @@ export type GetHitlDetailData = { export type GetHitlDetailResponse = HITLDetail; +export type GetHitlDetailTryData = { + dagId: string; + dagRunId: string; + mapIndex: number; + taskId: string; + tryNumber: number; +}; + +export type GetHitlDetailTryResponse = HITLDetailHisotry; + export type GetHitlDetailsData = { /** * SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. @@ -5686,6 +5696,33 @@ export type $OpenApiTs = { }; }; }; + '/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/{map_index}/hitlDetails/tries/{try_number}': { + get: { + req: GetHitlDetailTryData; + res: { + /** + * Successful Response + */ + 200: HITLDetailHisotry; + /** + * Unauthorized + */ + 401: HTTPExceptionResponse; + /** + * Forbidden + */ + 403: HTTPExceptionResponse; + /** + * Not Found + */ + 404: HTTPExceptionResponse; + /** + * Validation Error + */ + 422: HTTPValidationError; + }; + }; + }; '/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/hitlDetails': { get: { req: GetHitlDetailsData; diff --git a/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLResponseForm.tsx b/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLResponseForm.tsx index 6b7861ba72a5f..d4e1ce43991a7 100644 --- a/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLResponseForm.tsx +++ b/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLResponseForm.tsx @@ -22,7 +22,7 @@ import { useTranslation } from "react-i18next"; import { FiSend } from "react-icons/fi"; import { useSearchParams } from "react-router-dom"; -import type { HITLDetail, TaskInstanceResponse } from "openapi/requests/types.gen"; +import type { HITLDetail, HITLDetailHisotry,TaskInstanceResponse } from "openapi/requests/types.gen"; import { FlexibleForm } from "src/components/FlexibleForm/FlexibleForm"; import Time from "src/components/Time"; import { useParamStore } from "src/queries/useParamStore"; @@ -33,7 +33,7 @@ import { getHITLParamsDict, getHITLFormData, getPreloadHITLFormData } from "src/ type HITLResponseFormProps = { readonly hitlDetail: { task_instance: TaskInstanceResponse; - } & Omit; + } & Omit; }; const isHighlightOption = (option: string, hitlDetail: HITLDetail, preloadedHITLOptions: Array) => { diff --git a/airflow-core/src/airflow/ui/src/pages/TaskInstance/HITLResponse.tsx b/airflow-core/src/airflow/ui/src/pages/TaskInstance/HITLResponse.tsx index 11072fe862e13..6df8a9aee8a08 100644 --- a/airflow-core/src/airflow/ui/src/pages/TaskInstance/HITLResponse.tsx +++ b/airflow-core/src/airflow/ui/src/pages/TaskInstance/HITLResponse.tsx @@ -18,26 +18,70 @@ */ import { Box } from "@chakra-ui/react"; import { useParams } from "react-router-dom"; +import { useSearchParams } from "react-router-dom"; -import { useTaskInstanceServiceGetHitlDetail } from "openapi/queries"; +import { + useTaskInstanceServiceGetHitlDetailTry, + useTaskInstanceServiceGetMappedTaskInstance, +} from "openapi/queries"; +import { TaskTrySelect } from "src/components/TaskTrySelect"; import { ProgressBar } from "src/components/ui"; +import { SearchParamsKeys } from "src/constants/searchParams"; import { HITLResponseForm } from "../HITLTaskInstances/HITLResponseForm"; export const HITLResponse = () => { const { dagId, mapIndex, runId, taskId } = useParams(); - const { data: hitlDetail } = useTaskInstanceServiceGetHitlDetail( + const [searchParams, setSearchParams] = useSearchParams(); + const tryNumberParam = searchParams.get(SearchParamsKeys.TRY_NUMBER); + + const parsedMapIndex = Number(mapIndex ?? -1); + + const { data: taskInstance } = useTaskInstanceServiceGetMappedTaskInstance( + { + dagId: dagId ?? "", + dagRunId: runId ?? "", + mapIndex: parsedMapIndex, + taskId: taskId ?? "", + }, + undefined, + { + enabled: !isNaN(parsedMapIndex), + }, + ); + + if (!taskInstance) { + return ( + + + + ); + } + + const onSelectTryNumber = (newTryNumber: number) => { + if (newTryNumber === taskInstance.try_number) { + searchParams.delete(SearchParamsKeys.TRY_NUMBER); + } else { + searchParams.set(SearchParamsKeys.TRY_NUMBER, newTryNumber.toString()); + } + setSearchParams(searchParams); + }; + + const tryNumber = tryNumberParam === null ? -1 : parseInt(tryNumberParam, 10); + + const { data: hitlDetail } = useTaskInstanceServiceGetHitlDetailTry( { dagId: dagId ?? "", dagRunId: runId ?? "", - mapIndex: Number(mapIndex ?? -1), + mapIndex: parsedMapIndex, taskId: taskId ?? "", + tryNumber }, undefined, ); - if (!hitlDetail?.task_instance) { + if (!hitlDetail) { return ( @@ -45,8 +89,16 @@ export const HITLResponse = () => { ); } + return ( + {taskInstance.try_number <= 1 ? undefined : ( + + )} ); From 504162c9b1ba230b0f57ab01904b3483376b2048 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Thu, 23 Oct 2025 08:36:22 +0800 Subject: [PATCH 2/9] fix(hitl): remove hitl from task_instance_history but create a separate hitl_try endpoint instead --- .../api_fastapi/core_api/datamodels/hitl.py | 7 +++++++ .../core_api/datamodels/task_instance_history.py | 6 ------ .../core_api/openapi/v2-rest-api-generated.yaml | 12 +++++------- .../api_fastapi/core_api/routes/public/hitl.py | 2 +- .../core_api/routes/public/task_instances.py | 16 ++++++---------- .../ui/openapi-gen/requests/schemas.gen.ts | 13 ++++++++++++- .../airflow/ui/openapi-gen/requests/types.gen.ts | 4 ++++ 7 files changed, 35 insertions(+), 25 deletions(-) diff --git a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/hitl.py b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/hitl.py index 9f60f971f941e..a397a2b27a983 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/hitl.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/hitl.py @@ -23,6 +23,7 @@ from pydantic import Field, field_validator from airflow.api_fastapi.core_api.base import BaseModel +from airflow.api_fastapi.core_api.datamodels.task_instance_history import TaskInstanceHistoryResponse from airflow.api_fastapi.core_api.datamodels.task_instances import TaskInstanceResponse @@ -101,3 +102,9 @@ class HITLDetailCollection(BaseModel): hitl_details: Iterable[HITLDetail] total_entries: int + + +class HITLDetailHisotry(BaseHITLDetail): + """Schema for Human-in-the-loop detail history.""" + + task_instance: TaskInstanceHistoryResponse diff --git a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/task_instance_history.py b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/task_instance_history.py index c0237126e93e1..c913d91c1e607 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/task_instance_history.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/task_instance_history.py @@ -27,14 +27,9 @@ from airflow.api_fastapi.core_api.base import BaseModel from airflow.api_fastapi.core_api.datamodels.dag_versions import DagVersionResponse -from airflow.api_fastapi.core_api.datamodels.hitl import BaseHITLDetail from airflow.utils.state import TaskInstanceState -class HITLDetailHistory(BaseHITLDetail): - """Schema for Human-in-the-loop detail history.""" - - class TaskInstanceHistoryResponse(BaseModel): """TaskInstanceHistory serializer for responses.""" @@ -67,7 +62,6 @@ class TaskInstanceHistoryResponse(BaseModel): executor: str | None executor_config: Annotated[str, BeforeValidator(str)] dag_version: DagVersionResponse | None - hitl_detail: HITLDetailHistory | None class TaskInstanceHistoryCollectionResponse(BaseModel): 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 6833cd37bcbf8..1b10431ea575c 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 @@ -11483,7 +11483,7 @@ components: - total_entries title: HITLDetailCollection description: Schema for a collection of Human-in-the-loop details. - HITLDetailHistory: + HITLDetailHisotry: properties: options: items: @@ -11548,12 +11548,15 @@ components: type: boolean title: Response Received default: false + task_instance: + $ref: '#/components/schemas/TaskInstanceHistoryResponse' type: object required: - options - subject - created_at - title: HITLDetailHistory + - task_instance + title: HITLDetailHisotry description: Schema for Human-in-the-loop detail history. HITLDetailResponse: properties: @@ -12447,10 +12450,6 @@ components: anyOf: - $ref: '#/components/schemas/DagVersionResponse' - type: 'null' - hitl_detail: - anyOf: - - $ref: '#/components/schemas/HITLDetailHistory' - - type: 'null' type: object required: - task_id @@ -12479,7 +12478,6 @@ components: - executor - executor_config - dag_version - - hitl_detail title: TaskInstanceHistoryResponse description: TaskInstanceHistory serializer for responses. TaskInstanceResponse: diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py index 7b69a42b9df1e..fef51f720709f 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py @@ -47,10 +47,10 @@ from airflow.api_fastapi.core_api.datamodels.hitl import ( HITLDetail, HITLDetailCollection, + HITLDetailHisotry, HITLDetailResponse, UpdateHITLDetailPayload, ) -from airflow.api_fastapi.core_api.datamodels.task_instance_history import HITLDetailHisotry from airflow.api_fastapi.core_api.openapi.exceptions import create_openapi_http_exception_doc from airflow.api_fastapi.core_api.security import ( GetUserDep, diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/task_instances.py b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/task_instances.py index 7ccebd396dd53..29c5f10a3ff26 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/task_instances.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/task_instances.py @@ -669,16 +669,12 @@ def get_task_instance_try_details( """Get task instance details by try number.""" def _query(orm_object: Base) -> TI | TIH | None: - query = ( - select(orm_object) - .where( - orm_object.dag_id == dag_id, - orm_object.run_id == dag_run_id, - orm_object.task_id == task_id, - orm_object.try_number == task_try_number, - orm_object.map_index == map_index, - ) - .options(joinedload(orm_object.hitl_detail)) + query = select(orm_object).where( + orm_object.dag_id == dag_id, + orm_object.run_id == dag_run_id, + orm_object.task_id == task_id, + orm_object.try_number == task_try_number, + orm_object.map_index == map_index, ) task_instance = session.scalar(query) diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts index b44c2883a9551..8fbba25bed56e 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts @@ -4019,11 +4019,19 @@ export const $HITLDetailHistory = { type: 'boolean', title: 'Response Received', default: false + }, + task_instance: { + '$ref': '#/components/schemas/TaskInstanceHistoryResponse' } }, type: 'object', +<<<<<<< HEAD required: ['options', 'subject', 'created_at'], title: 'HITLDetailHistory', +======= + required: ['options', 'subject', 'created_at', 'task_instance'], + title: 'HITLDetailHisotry', +>>>>>>> accf09b17d (fix(hitl): remove hitl from task_instance_history but create a separate hitl_try endpoint instead) description: 'Schema for Human-in-the-loop detail history.' } as const; @@ -5307,6 +5315,7 @@ export const $TaskInstanceHistoryResponse = { type: 'null' } ] +<<<<<<< HEAD }, hitl_detail: { anyOf: [ @@ -5317,10 +5326,12 @@ export const $TaskInstanceHistoryResponse = { type: 'null' } ] +======= +>>>>>>> accf09b17d (fix(hitl): remove hitl from task_instance_history but create a separate hitl_try endpoint instead) } }, type: 'object', - required: ['task_id', 'dag_id', 'dag_run_id', 'map_index', 'start_date', 'end_date', 'duration', 'state', 'try_number', 'max_tries', 'task_display_name', 'dag_display_name', 'hostname', 'unixname', 'pool', 'pool_slots', 'queue', 'priority_weight', 'operator', 'operator_name', 'queued_when', 'scheduled_when', 'pid', 'executor', 'executor_config', 'dag_version', 'hitl_detail'], + required: ['task_id', 'dag_id', 'dag_run_id', 'map_index', 'start_date', 'end_date', 'duration', 'state', 'try_number', 'max_tries', 'task_display_name', 'dag_display_name', 'hostname', 'unixname', 'pool', 'pool_slots', 'queue', 'priority_weight', 'operator', 'operator_name', 'queued_when', 'scheduled_when', 'pid', 'executor', 'executor_config', 'dag_version'], title: 'TaskInstanceHistoryResponse', description: 'TaskInstanceHistory serializer for responses.' } as const; diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts b/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts index c72bf3aade6c8..48abfbd4ae690 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 @@ -1026,6 +1026,7 @@ export type HITLDetailHistory = { [key: string]: unknown; }; response_received?: boolean; + task_instance: TaskInstanceHistoryResponse; }; /** @@ -1386,7 +1387,10 @@ export type TaskInstanceHistoryResponse = { executor: string | null; executor_config: string; dag_version: DagVersionResponse | null; +<<<<<<< HEAD hitl_detail: HITLDetailHistory | null; +======= +>>>>>>> accf09b17d (fix(hitl): remove hitl from task_instance_history but create a separate hitl_try endpoint instead) }; /** From e8baeb2ce1f05ee579222ecd7db3aac1e1bf394f Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Thu, 23 Oct 2025 16:42:28 +0800 Subject: [PATCH 3/9] feat(hitl): use HITLHistory in the frontend --- .../HITLTaskInstances/HITLResponseForm.tsx | 12 +++-- .../src/pages/TaskInstance/HITLResponse.tsx | 44 ++++++++++++++----- airflow-core/src/airflow/ui/src/utils/hitl.ts | 8 ++-- 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLResponseForm.tsx b/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLResponseForm.tsx index d4e1ce43991a7..f0138b808ea67 100644 --- a/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLResponseForm.tsx +++ b/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLResponseForm.tsx @@ -22,7 +22,7 @@ import { useTranslation } from "react-i18next"; import { FiSend } from "react-icons/fi"; import { useSearchParams } from "react-router-dom"; -import type { HITLDetail, HITLDetailHisotry,TaskInstanceResponse } from "openapi/requests/types.gen"; +import type { HITLDetailHisotry, TaskInstanceHistoryResponse } from "openapi/requests/types.gen"; import { FlexibleForm } from "src/components/FlexibleForm/FlexibleForm"; import Time from "src/components/Time"; import { useParamStore } from "src/queries/useParamStore"; @@ -32,11 +32,15 @@ import { getHITLParamsDict, getHITLFormData, getPreloadHITLFormData } from "src/ type HITLResponseFormProps = { readonly hitlDetail: { - task_instance: TaskInstanceResponse; - } & Omit; + task_instance: TaskInstanceHistoryResponse; + } & Omit; }; -const isHighlightOption = (option: string, hitlDetail: HITLDetail, preloadedHITLOptions: Array) => { +const isHighlightOption = ( + option: string, + hitlDetail: HITLDetailHisotry, + preloadedHITLOptions: Array, +) => { // preload's priority is higher than default const defaultOptions = preloadedHITLOptions.length > 0 ? preloadedHITLOptions : hitlDetail.defaults; diff --git a/airflow-core/src/airflow/ui/src/pages/TaskInstance/HITLResponse.tsx b/airflow-core/src/airflow/ui/src/pages/TaskInstance/HITLResponse.tsx index 6df8a9aee8a08..df3371398e61b 100644 --- a/airflow-core/src/airflow/ui/src/pages/TaskInstance/HITLResponse.tsx +++ b/airflow-core/src/airflow/ui/src/pages/TaskInstance/HITLResponse.tsx @@ -51,16 +51,8 @@ export const HITLResponse = () => { }, ); - if (!taskInstance) { - return ( - - - - ); - } - const onSelectTryNumber = (newTryNumber: number) => { - if (newTryNumber === taskInstance.try_number) { + if (newTryNumber === taskInstance?.try_number) { searchParams.delete(SearchParamsKeys.TRY_NUMBER); } else { searchParams.set(SearchParamsKeys.TRY_NUMBER, newTryNumber.toString()); @@ -76,12 +68,12 @@ export const HITLResponse = () => { dagRunId: runId ?? "", mapIndex: parsedMapIndex, taskId: taskId ?? "", - tryNumber + tryNumber, }, undefined, ); - if (!hitlDetail) { + if (!taskInstance) { return ( @@ -89,6 +81,13 @@ export const HITLResponse = () => { ); } + if (!hitlDetail) { + return ( + + + + ); + } return ( @@ -102,4 +101,27 @@ export const HITLResponse = () => { ); + // const { data: hitlDetail } = useTaskInstanceServiceGetHitlDetail( + // { + // dagId: dagId ?? "", + // dagRunId: runId ?? "", + // mapIndex: Number(mapIndex ?? -1), + // taskId: taskId ?? "", + // }, + // undefined, + // ); + // + // if (!hitlDetail?.task_instance) { + // return ( + // + // + // + // ); + // } + // + // return ( + // + // + // + // ); }; diff --git a/airflow-core/src/airflow/ui/src/utils/hitl.ts b/airflow-core/src/airflow/ui/src/utils/hitl.ts index 6fb7745ded385..50f36405ad52a 100644 --- a/airflow-core/src/airflow/ui/src/utils/hitl.ts +++ b/airflow-core/src/airflow/ui/src/utils/hitl.ts @@ -18,7 +18,7 @@ */ import type { TFunction } from "i18next"; -import type { HITLDetail } from "openapi/requests/types.gen"; +import type { HITLDetail, HITLDetailHisotry } from "openapi/requests/types.gen"; import type { ParamSchema, ParamsSpec } from "src/queries/useDagParams"; export type HITLResponseParams = { @@ -26,14 +26,14 @@ export type HITLResponseParams = { params_input?: Record; }; -const getChosenOptionsValue = (hitlDetail: HITLDetail) => { +const getChosenOptionsValue = (hitlDetail: HITLDetailHisotry) => { // if response_received is true, display the chosen_options, otherwise display the defaults const sourceValues = hitlDetail.response_received ? hitlDetail.chosen_options : hitlDetail.defaults; return hitlDetail.multiple ? sourceValues : sourceValues?.[0]; }; -export const getPreloadHITLFormData = (searchParams: URLSearchParams, hitlDetail: HITLDetail) => { +export const getPreloadHITLFormData = (searchParams: URLSearchParams, hitlDetail: HITLDetailHisotry) => { const preloadedHITLParams: Record = Object.fromEntries( [...searchParams.entries()] .filter(([key]) => key !== "_options") @@ -65,7 +65,7 @@ export const getPreloadHITLFormData = (searchParams: URLSearchParams, hitlDetail }; export const getHITLParamsDict = ( - hitlDetail: HITLDetail, + hitlDetail: HITLDetailHisotry, translate: TFunction, searchParams: URLSearchParams, ): ParamsSpec => { From 89b3dba0fc5f2903528ee36695edb0b6ce88352a Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Thu, 23 Oct 2025 17:01:12 +0800 Subject: [PATCH 4/9] feat(hitl): add auto refresh --- .../src/pages/TaskInstance/HITLResponse.tsx | 26 +++---------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/pages/TaskInstance/HITLResponse.tsx b/airflow-core/src/airflow/ui/src/pages/TaskInstance/HITLResponse.tsx index df3371398e61b..78b523dcde9f3 100644 --- a/airflow-core/src/airflow/ui/src/pages/TaskInstance/HITLResponse.tsx +++ b/airflow-core/src/airflow/ui/src/pages/TaskInstance/HITLResponse.tsx @@ -29,10 +29,12 @@ import { ProgressBar } from "src/components/ui"; import { SearchParamsKeys } from "src/constants/searchParams"; import { HITLResponseForm } from "../HITLTaskInstances/HITLResponseForm"; +import { isStatePending, useAutoRefresh } from "src/utils"; export const HITLResponse = () => { const { dagId, mapIndex, runId, taskId } = useParams(); + const refetchInterval = useAutoRefresh({ dagId }); const [searchParams, setSearchParams] = useSearchParams(); const tryNumberParam = searchParams.get(SearchParamsKeys.TRY_NUMBER); @@ -48,6 +50,7 @@ export const HITLResponse = () => { undefined, { enabled: !isNaN(parsedMapIndex), + refetchInterval: (query) => (isStatePending(query.state.data?.state) ? refetchInterval : false), }, ); @@ -101,27 +104,4 @@ export const HITLResponse = () => { ); - // const { data: hitlDetail } = useTaskInstanceServiceGetHitlDetail( - // { - // dagId: dagId ?? "", - // dagRunId: runId ?? "", - // mapIndex: Number(mapIndex ?? -1), - // taskId: taskId ?? "", - // }, - // undefined, - // ); - // - // if (!hitlDetail?.task_instance) { - // return ( - // - // - // - // ); - // } - // - // return ( - // - // - // - // ); }; From 06012ba1d85051b3712922ee5b96e1b4ab4276a9 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Thu, 23 Oct 2025 18:14:21 +0800 Subject: [PATCH 5/9] fix: fix ci failure --- .../openapi/v2-rest-api-generated.yaml | 4 +- .../core_api/routes/public/hitl.py | 2 +- .../airflow/ui/openapi-gen/queries/common.ts | 10 +- .../ui/openapi-gen/queries/ensureQueryData.ts | 6 +- .../ui/openapi-gen/queries/prefetch.ts | 6 +- .../airflow/ui/openapi-gen/queries/queries.ts | 6 +- .../ui/openapi-gen/queries/suspense.ts | 6 +- .../ui/openapi-gen/requests/schemas.gen.ts | 20 +- .../ui/openapi-gen/requests/services.gen.ts | 292 ++++++++++-------- .../ui/openapi-gen/requests/types.gen.ts | 12 +- .../src/pages/TaskInstance/HITLResponse.tsx | 16 +- .../core_api/routes/public/test_hitl.py | 4 +- .../routes/public/test_task_instances.py | 164 ---------- .../tests/unit/models/test_taskinstance.py | 38 --- .../airflowctl/api/datamodels/generated.py | 42 +-- 15 files changed, 214 insertions(+), 414 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 1b10431ea575c..06c6471f320da 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 @@ -8446,9 +8446,9 @@ paths: get: tags: - Task Instance - summary: Get Hitl Detail Try + summary: Get Hitl Detail Try Detail description: Get a Human-in-the-loop detail of a specific task instance. - operationId: get_hitl_detail_try + operationId: get_hitl_detail_try_detail security: - OAuth2PasswordBearer: [] - HTTPBearer: [] diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py index fef51f720709f..6b9192d3d523f 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py @@ -222,7 +222,7 @@ def get_hitl_detail( responses=create_openapi_http_exception_doc([status.HTTP_404_NOT_FOUND]), dependencies=[Depends(requires_access_dag(method="GET", access_entity=DagAccessEntity.HITL_DETAIL))], ) -def get_hitl_detail_try( +def get_hitl_detail_try_detail( dag_id: str, dag_run_id: str, task_id: str, 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 22161f165370c..4157583cd3f94 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts @@ -574,16 +574,16 @@ export const UseTaskInstanceServiceGetHitlDetailKeyFn = ({ dagId, dagRunId, mapI mapIndex: number; taskId: string; }, queryKey?: Array) => [useTaskInstanceServiceGetHitlDetailKey, ...(queryKey ?? [{ dagId, dagRunId, mapIndex, taskId }])]; -export type TaskInstanceServiceGetHitlDetailTryDefaultResponse = Awaited>; -export type TaskInstanceServiceGetHitlDetailTryQueryResult = UseQueryResult; -export const useTaskInstanceServiceGetHitlDetailTryKey = "TaskInstanceServiceGetHitlDetailTry"; -export const UseTaskInstanceServiceGetHitlDetailTryKeyFn = ({ dagId, dagRunId, mapIndex, taskId, tryNumber }: { +export type TaskInstanceServiceGetHitlDetailTryDetailDefaultResponse = Awaited>; +export type TaskInstanceServiceGetHitlDetailTryDetailQueryResult = UseQueryResult; +export const useTaskInstanceServiceGetHitlDetailTryDetailKey = "TaskInstanceServiceGetHitlDetailTryDetail"; +export const UseTaskInstanceServiceGetHitlDetailTryDetailKeyFn = ({ dagId, dagRunId, mapIndex, taskId, tryNumber }: { dagId: string; dagRunId: string; mapIndex: number; taskId: string; tryNumber: number; -}, queryKey?: Array) => [useTaskInstanceServiceGetHitlDetailTryKey, ...(queryKey ?? [{ dagId, dagRunId, mapIndex, taskId, tryNumber }])]; +}, queryKey?: Array) => [useTaskInstanceServiceGetHitlDetailTryDetailKey, ...(queryKey ?? [{ dagId, dagRunId, mapIndex, taskId, tryNumber }])]; export type TaskInstanceServiceGetHitlDetailsDefaultResponse = Awaited>; export type TaskInstanceServiceGetHitlDetailsQueryResult = UseQueryResult; export const useTaskInstanceServiceGetHitlDetailsKey = "TaskInstanceServiceGetHitlDetails"; 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 d7fe48d9a266a..03060fe2a30c5 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts @@ -1093,7 +1093,7 @@ export const ensureUseTaskInstanceServiceGetHitlDetailData = (queryClient: Query taskId: string; }) => queryClient.ensureQueryData({ queryKey: Common.UseTaskInstanceServiceGetHitlDetailKeyFn({ dagId, dagRunId, mapIndex, taskId }), queryFn: () => TaskInstanceService.getHitlDetail({ dagId, dagRunId, mapIndex, taskId }) }); /** -* Get Hitl Detail Try +* Get Hitl Detail Try Detail * Get a Human-in-the-loop detail of a specific task instance. * @param data The data for the request. * @param data.dagId @@ -1104,13 +1104,13 @@ export const ensureUseTaskInstanceServiceGetHitlDetailData = (queryClient: Query * @returns HITLDetailHisotry Successful Response * @throws ApiError */ -export const ensureUseTaskInstanceServiceGetHitlDetailTryData = (queryClient: QueryClient, { dagId, dagRunId, mapIndex, taskId, tryNumber }: { +export const ensureUseTaskInstanceServiceGetHitlDetailTryDetailData = (queryClient: QueryClient, { dagId, dagRunId, mapIndex, taskId, tryNumber }: { dagId: string; dagRunId: string; mapIndex: number; taskId: string; tryNumber: number; -}) => queryClient.ensureQueryData({ queryKey: Common.UseTaskInstanceServiceGetHitlDetailTryKeyFn({ dagId, dagRunId, mapIndex, taskId, tryNumber }), queryFn: () => TaskInstanceService.getHitlDetailTry({ dagId, dagRunId, mapIndex, taskId, tryNumber }) }); +}) => queryClient.ensureQueryData({ queryKey: Common.UseTaskInstanceServiceGetHitlDetailTryDetailKeyFn({ dagId, dagRunId, mapIndex, taskId, tryNumber }), queryFn: () => TaskInstanceService.getHitlDetailTryDetail({ dagId, dagRunId, mapIndex, taskId, tryNumber }) }); /** * Get Hitl Details * Get Human-in-the-loop details. 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 9e54a15cae0a6..61fd755fe6ada 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts @@ -1093,7 +1093,7 @@ export const prefetchUseTaskInstanceServiceGetHitlDetail = (queryClient: QueryCl taskId: string; }) => queryClient.prefetchQuery({ queryKey: Common.UseTaskInstanceServiceGetHitlDetailKeyFn({ dagId, dagRunId, mapIndex, taskId }), queryFn: () => TaskInstanceService.getHitlDetail({ dagId, dagRunId, mapIndex, taskId }) }); /** -* Get Hitl Detail Try +* Get Hitl Detail Try Detail * Get a Human-in-the-loop detail of a specific task instance. * @param data The data for the request. * @param data.dagId @@ -1104,13 +1104,13 @@ export const prefetchUseTaskInstanceServiceGetHitlDetail = (queryClient: QueryCl * @returns HITLDetailHisotry Successful Response * @throws ApiError */ -export const prefetchUseTaskInstanceServiceGetHitlDetailTry = (queryClient: QueryClient, { dagId, dagRunId, mapIndex, taskId, tryNumber }: { +export const prefetchUseTaskInstanceServiceGetHitlDetailTryDetail = (queryClient: QueryClient, { dagId, dagRunId, mapIndex, taskId, tryNumber }: { dagId: string; dagRunId: string; mapIndex: number; taskId: string; tryNumber: number; -}) => queryClient.prefetchQuery({ queryKey: Common.UseTaskInstanceServiceGetHitlDetailTryKeyFn({ dagId, dagRunId, mapIndex, taskId, tryNumber }), queryFn: () => TaskInstanceService.getHitlDetailTry({ dagId, dagRunId, mapIndex, taskId, tryNumber }) }); +}) => queryClient.prefetchQuery({ queryKey: Common.UseTaskInstanceServiceGetHitlDetailTryDetailKeyFn({ dagId, dagRunId, mapIndex, taskId, tryNumber }), queryFn: () => TaskInstanceService.getHitlDetailTryDetail({ dagId, dagRunId, mapIndex, taskId, tryNumber }) }); /** * Get Hitl Details * Get Human-in-the-loop details. 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 031a8cd98ec24..9eb6524487c19 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts @@ -1093,7 +1093,7 @@ export const useTaskInstanceServiceGetHitlDetail = , "queryKey" | "queryFn">) => useQuery({ queryKey: Common.UseTaskInstanceServiceGetHitlDetailKeyFn({ dagId, dagRunId, mapIndex, taskId }, queryKey), queryFn: () => TaskInstanceService.getHitlDetail({ dagId, dagRunId, mapIndex, taskId }) as TData, ...options }); /** -* Get Hitl Detail Try +* Get Hitl Detail Try Detail * Get a Human-in-the-loop detail of a specific task instance. * @param data The data for the request. * @param data.dagId @@ -1104,13 +1104,13 @@ export const useTaskInstanceServiceGetHitlDetail = = unknown[]>({ dagId, dagRunId, mapIndex, taskId, tryNumber }: { +export const useTaskInstanceServiceGetHitlDetailTryDetail = = unknown[]>({ dagId, dagRunId, mapIndex, taskId, tryNumber }: { dagId: string; dagRunId: string; mapIndex: number; taskId: string; tryNumber: number; -}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useQuery({ queryKey: Common.UseTaskInstanceServiceGetHitlDetailTryKeyFn({ dagId, dagRunId, mapIndex, taskId, tryNumber }, queryKey), queryFn: () => TaskInstanceService.getHitlDetailTry({ dagId, dagRunId, mapIndex, taskId, tryNumber }) as TData, ...options }); +}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useQuery({ queryKey: Common.UseTaskInstanceServiceGetHitlDetailTryDetailKeyFn({ dagId, dagRunId, mapIndex, taskId, tryNumber }, queryKey), queryFn: () => TaskInstanceService.getHitlDetailTryDetail({ dagId, dagRunId, mapIndex, taskId, tryNumber }) as TData, ...options }); /** * Get Hitl Details * Get Human-in-the-loop details. 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 e3d560f9ae05f..1a9051f4a6c23 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts @@ -1093,7 +1093,7 @@ export const useTaskInstanceServiceGetHitlDetailSuspense = , "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseTaskInstanceServiceGetHitlDetailKeyFn({ dagId, dagRunId, mapIndex, taskId }, queryKey), queryFn: () => TaskInstanceService.getHitlDetail({ dagId, dagRunId, mapIndex, taskId }) as TData, ...options }); /** -* Get Hitl Detail Try +* Get Hitl Detail Try Detail * Get a Human-in-the-loop detail of a specific task instance. * @param data The data for the request. * @param data.dagId @@ -1104,13 +1104,13 @@ export const useTaskInstanceServiceGetHitlDetailSuspense = = unknown[]>({ dagId, dagRunId, mapIndex, taskId, tryNumber }: { +export const useTaskInstanceServiceGetHitlDetailTryDetailSuspense = = unknown[]>({ dagId, dagRunId, mapIndex, taskId, tryNumber }: { dagId: string; dagRunId: string; mapIndex: number; taskId: string; tryNumber: number; -}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseTaskInstanceServiceGetHitlDetailTryKeyFn({ dagId, dagRunId, mapIndex, taskId, tryNumber }, queryKey), queryFn: () => TaskInstanceService.getHitlDetailTry({ dagId, dagRunId, mapIndex, taskId, tryNumber }) as TData, ...options }); +}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseTaskInstanceServiceGetHitlDetailTryDetailKeyFn({ dagId, dagRunId, mapIndex, taskId, tryNumber }, queryKey), queryFn: () => TaskInstanceService.getHitlDetailTryDetail({ dagId, dagRunId, mapIndex, taskId, tryNumber }) as TData, ...options }); /** * Get Hitl Details * Get Human-in-the-loop details. diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts index 8fbba25bed56e..db97e946f60da 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts @@ -3913,7 +3913,7 @@ export const $HITLDetailCollection = { description: 'Schema for a collection of Human-in-the-loop details.' } as const; -export const $HITLDetailHistory = { +export const $HITLDetailHisotry = { properties: { options: { items: { @@ -4025,13 +4025,8 @@ export const $HITLDetailHistory = { } }, type: 'object', -<<<<<<< HEAD - required: ['options', 'subject', 'created_at'], - title: 'HITLDetailHistory', -======= required: ['options', 'subject', 'created_at', 'task_instance'], title: 'HITLDetailHisotry', ->>>>>>> accf09b17d (fix(hitl): remove hitl from task_instance_history but create a separate hitl_try endpoint instead) description: 'Schema for Human-in-the-loop detail history.' } as const; @@ -5315,19 +5310,6 @@ export const $TaskInstanceHistoryResponse = { type: 'null' } ] -<<<<<<< HEAD - }, - hitl_detail: { - anyOf: [ - { - '$ref': '#/components/schemas/HITLDetailHistory' - }, - { - type: 'null' - } - ] -======= ->>>>>>> accf09b17d (fix(hitl): remove hitl from task_instance_history but create a separate hitl_try endpoint instead) } }, type: 'object', 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 dd344abb81421..166718c2f11f7 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 @@ -3,7 +3,7 @@ import type { CancelablePromise } from './core/CancelablePromise'; import { OpenAPI } from './core/OpenAPI'; import { request as __request } from './core/request'; -import type { GetAssetsData, GetAssetsResponse, GetAssetAliasesData, GetAssetAliasesResponse, GetAssetAliasData, GetAssetAliasResponse, GetAssetEventsData, GetAssetEventsResponse, CreateAssetEventData, CreateAssetEventResponse, MaterializeAssetData, MaterializeAssetResponse, GetAssetQueuedEventsData, GetAssetQueuedEventsResponse, DeleteAssetQueuedEventsData, DeleteAssetQueuedEventsResponse, GetAssetData, GetAssetResponse, GetDagAssetQueuedEventsData, GetDagAssetQueuedEventsResponse, DeleteDagAssetQueuedEventsData, DeleteDagAssetQueuedEventsResponse, GetDagAssetQueuedEventData, GetDagAssetQueuedEventResponse, DeleteDagAssetQueuedEventData, DeleteDagAssetQueuedEventResponse, NextRunAssetsData, NextRunAssetsResponse, ListBackfillsData, ListBackfillsResponse, CreateBackfillData, CreateBackfillResponse, GetBackfillData, GetBackfillResponse, PauseBackfillData, PauseBackfillResponse, UnpauseBackfillData, UnpauseBackfillResponse, CancelBackfillData, CancelBackfillResponse, CreateBackfillDryRunData, CreateBackfillDryRunResponse, ListBackfillsUiData, ListBackfillsUiResponse, DeleteConnectionData, DeleteConnectionResponse, GetConnectionData, GetConnectionResponse, PatchConnectionData, PatchConnectionResponse, GetConnectionsData, GetConnectionsResponse, PostConnectionData, PostConnectionResponse, BulkConnectionsData, BulkConnectionsResponse, TestConnectionData, TestConnectionResponse, CreateDefaultConnectionsResponse, HookMetaDataResponse, GetDagRunData, GetDagRunResponse, DeleteDagRunData, DeleteDagRunResponse, PatchDagRunData, PatchDagRunResponse, GetUpstreamAssetEventsData, GetUpstreamAssetEventsResponse, ClearDagRunData, ClearDagRunResponse, GetDagRunsData, GetDagRunsResponse, TriggerDagRunData, TriggerDagRunResponse, WaitDagRunUntilFinishedData, WaitDagRunUntilFinishedResponse, GetListDagRunsBatchData, GetListDagRunsBatchResponse, GetDagSourceData, GetDagSourceResponse, GetDagStatsData, GetDagStatsResponse, GetConfigData, GetConfigResponse, GetConfigValueData, GetConfigValueResponse, GetConfigsResponse, ListDagWarningsData, ListDagWarningsResponse, GetDagsData, GetDagsResponse, PatchDagsData, PatchDagsResponse, GetDagData, GetDagResponse, PatchDagData, PatchDagResponse, DeleteDagData, DeleteDagResponse, GetDagDetailsData, GetDagDetailsResponse, FavoriteDagData, FavoriteDagResponse, UnfavoriteDagData, UnfavoriteDagResponse, GetDagTagsData, GetDagTagsResponse, GetDagsUiData, GetDagsUiResponse, GetLatestRunInfoData, GetLatestRunInfoResponse, GetEventLogData, GetEventLogResponse, GetEventLogsData, GetEventLogsResponse, GetExtraLinksData, GetExtraLinksResponse, GetTaskInstanceData, GetTaskInstanceResponse, PatchTaskInstanceData, PatchTaskInstanceResponse, DeleteTaskInstanceData, DeleteTaskInstanceResponse, GetMappedTaskInstancesData, GetMappedTaskInstancesResponse, GetTaskInstanceDependenciesByMapIndexData, GetTaskInstanceDependenciesByMapIndexResponse, GetTaskInstanceDependenciesData, GetTaskInstanceDependenciesResponse, GetTaskInstanceTriesData, GetTaskInstanceTriesResponse, GetMappedTaskInstanceTriesData, GetMappedTaskInstanceTriesResponse, GetMappedTaskInstanceData, GetMappedTaskInstanceResponse, PatchTaskInstanceByMapIndexData, PatchTaskInstanceByMapIndexResponse, GetTaskInstancesData, GetTaskInstancesResponse, BulkTaskInstancesData, BulkTaskInstancesResponse, GetTaskInstancesBatchData, GetTaskInstancesBatchResponse, GetTaskInstanceTryDetailsData, GetTaskInstanceTryDetailsResponse, GetMappedTaskInstanceTryDetailsData, GetMappedTaskInstanceTryDetailsResponse, PostClearTaskInstancesData, PostClearTaskInstancesResponse, PatchTaskInstanceDryRunByMapIndexData, PatchTaskInstanceDryRunByMapIndexResponse, PatchTaskInstanceDryRunData, PatchTaskInstanceDryRunResponse, GetLogData, GetLogResponse, GetExternalLogUrlData, GetExternalLogUrlResponse, UpdateHitlDetailData, UpdateHitlDetailResponse, GetHitlDetailData, GetHitlDetailResponse, GetHitlDetailsData, GetHitlDetailsResponse, GetImportErrorData, GetImportErrorResponse, GetImportErrorsData, GetImportErrorsResponse, GetJobsData, GetJobsResponse, GetPluginsData, GetPluginsResponse, ImportErrorsResponse, DeletePoolData, DeletePoolResponse, GetPoolData, GetPoolResponse, PatchPoolData, PatchPoolResponse, GetPoolsData, GetPoolsResponse, PostPoolData, PostPoolResponse, BulkPoolsData, BulkPoolsResponse, GetProvidersData, GetProvidersResponse, GetXcomEntryData, GetXcomEntryResponse, UpdateXcomEntryData, UpdateXcomEntryResponse, DeleteXcomEntryData, DeleteXcomEntryResponse, GetXcomEntriesData, GetXcomEntriesResponse, CreateXcomEntryData, CreateXcomEntryResponse, GetTasksData, GetTasksResponse, GetTaskData, GetTaskResponse, DeleteVariableData, DeleteVariableResponse, GetVariableData, GetVariableResponse, PatchVariableData, PatchVariableResponse, GetVariablesData, GetVariablesResponse, PostVariableData, PostVariableResponse, BulkVariablesData, BulkVariablesResponse, ReparseDagFileData, ReparseDagFileResponse, GetDagVersionData, GetDagVersionResponse, GetDagVersionsData, GetDagVersionsResponse, GetHealthResponse, GetVersionResponse, LoginData, LoginResponse, LogoutResponse, GetAuthMenusResponse, GetCurrentUserInfoResponse, GetDependenciesData, GetDependenciesResponse, HistoricalMetricsData, HistoricalMetricsResponse, DagStatsResponse2, StructureDataData, StructureDataResponse2, GetDagStructureData, GetDagStructureResponse, GetGridRunsData, GetGridRunsResponse, GetGridTiSummariesData, GetGridTiSummariesResponse, GetCalendarData, GetCalendarResponse, ListTeamsData, ListTeamsResponse } from './types.gen'; +import type { GetAssetsData, GetAssetsResponse, GetAssetAliasesData, GetAssetAliasesResponse, GetAssetAliasData, GetAssetAliasResponse, GetAssetEventsData, GetAssetEventsResponse, CreateAssetEventData, CreateAssetEventResponse, MaterializeAssetData, MaterializeAssetResponse, GetAssetQueuedEventsData, GetAssetQueuedEventsResponse, DeleteAssetQueuedEventsData, DeleteAssetQueuedEventsResponse, GetAssetData, GetAssetResponse, GetDagAssetQueuedEventsData, GetDagAssetQueuedEventsResponse, DeleteDagAssetQueuedEventsData, DeleteDagAssetQueuedEventsResponse, GetDagAssetQueuedEventData, GetDagAssetQueuedEventResponse, DeleteDagAssetQueuedEventData, DeleteDagAssetQueuedEventResponse, NextRunAssetsData, NextRunAssetsResponse, ListBackfillsData, ListBackfillsResponse, CreateBackfillData, CreateBackfillResponse, GetBackfillData, GetBackfillResponse, PauseBackfillData, PauseBackfillResponse, UnpauseBackfillData, UnpauseBackfillResponse, CancelBackfillData, CancelBackfillResponse, CreateBackfillDryRunData, CreateBackfillDryRunResponse, ListBackfillsUiData, ListBackfillsUiResponse, DeleteConnectionData, DeleteConnectionResponse, GetConnectionData, GetConnectionResponse, PatchConnectionData, PatchConnectionResponse, GetConnectionsData, GetConnectionsResponse, PostConnectionData, PostConnectionResponse, BulkConnectionsData, BulkConnectionsResponse, TestConnectionData, TestConnectionResponse, CreateDefaultConnectionsResponse, HookMetaDataResponse, GetDagRunData, GetDagRunResponse, DeleteDagRunData, DeleteDagRunResponse, PatchDagRunData, PatchDagRunResponse, GetUpstreamAssetEventsData, GetUpstreamAssetEventsResponse, ClearDagRunData, ClearDagRunResponse, GetDagRunsData, GetDagRunsResponse, TriggerDagRunData, TriggerDagRunResponse, WaitDagRunUntilFinishedData, WaitDagRunUntilFinishedResponse, GetListDagRunsBatchData, GetListDagRunsBatchResponse, GetDagSourceData, GetDagSourceResponse, GetDagStatsData, GetDagStatsResponse, GetConfigData, GetConfigResponse, GetConfigValueData, GetConfigValueResponse, GetConfigsResponse, ListDagWarningsData, ListDagWarningsResponse, GetDagsData, GetDagsResponse, PatchDagsData, PatchDagsResponse, GetDagData, GetDagResponse, PatchDagData, PatchDagResponse, DeleteDagData, DeleteDagResponse, GetDagDetailsData, GetDagDetailsResponse, FavoriteDagData, FavoriteDagResponse, UnfavoriteDagData, UnfavoriteDagResponse, GetDagTagsData, GetDagTagsResponse, GetDagsUiData, GetDagsUiResponse, GetLatestRunInfoData, GetLatestRunInfoResponse, GetEventLogData, GetEventLogResponse, GetEventLogsData, GetEventLogsResponse, GetExtraLinksData, GetExtraLinksResponse, GetTaskInstanceData, GetTaskInstanceResponse, PatchTaskInstanceData, PatchTaskInstanceResponse, DeleteTaskInstanceData, DeleteTaskInstanceResponse, GetMappedTaskInstancesData, GetMappedTaskInstancesResponse, GetTaskInstanceDependenciesByMapIndexData, GetTaskInstanceDependenciesByMapIndexResponse, GetTaskInstanceDependenciesData, GetTaskInstanceDependenciesResponse, GetTaskInstanceTriesData, GetTaskInstanceTriesResponse, GetMappedTaskInstanceTriesData, GetMappedTaskInstanceTriesResponse, GetMappedTaskInstanceData, GetMappedTaskInstanceResponse, PatchTaskInstanceByMapIndexData, PatchTaskInstanceByMapIndexResponse, GetTaskInstancesData, GetTaskInstancesResponse, BulkTaskInstancesData, BulkTaskInstancesResponse, GetTaskInstancesBatchData, GetTaskInstancesBatchResponse, GetTaskInstanceTryDetailsData, GetTaskInstanceTryDetailsResponse, GetMappedTaskInstanceTryDetailsData, GetMappedTaskInstanceTryDetailsResponse, PostClearTaskInstancesData, PostClearTaskInstancesResponse, PatchTaskInstanceDryRunByMapIndexData, PatchTaskInstanceDryRunByMapIndexResponse, PatchTaskInstanceDryRunData, PatchTaskInstanceDryRunResponse, GetLogData, GetLogResponse, GetExternalLogUrlData, GetExternalLogUrlResponse, UpdateHitlDetailData, UpdateHitlDetailResponse, GetHitlDetailData, GetHitlDetailResponse, GetHitlDetailTryDetailData, GetHitlDetailTryDetailResponse, GetHitlDetailsData, GetHitlDetailsResponse, GetImportErrorData, GetImportErrorResponse, GetImportErrorsData, GetImportErrorsResponse, GetJobsData, GetJobsResponse, GetPluginsData, GetPluginsResponse, ImportErrorsResponse, DeletePoolData, DeletePoolResponse, GetPoolData, GetPoolResponse, PatchPoolData, PatchPoolResponse, GetPoolsData, GetPoolsResponse, PostPoolData, PostPoolResponse, BulkPoolsData, BulkPoolsResponse, GetProvidersData, GetProvidersResponse, GetXcomEntryData, GetXcomEntryResponse, UpdateXcomEntryData, UpdateXcomEntryResponse, GetXcomEntriesData, GetXcomEntriesResponse, CreateXcomEntryData, CreateXcomEntryResponse, GetTasksData, GetTasksResponse, GetTaskData, GetTaskResponse, DeleteVariableData, DeleteVariableResponse, GetVariableData, GetVariableResponse, PatchVariableData, PatchVariableResponse, GetVariablesData, GetVariablesResponse, PostVariableData, PostVariableResponse, BulkVariablesData, BulkVariablesResponse, ReparseDagFileData, ReparseDagFileResponse, GetDagVersionData, GetDagVersionResponse, GetDagVersionsData, GetDagVersionsResponse, GetHealthResponse, GetVersionResponse, LoginData, LoginResponse, LogoutResponse, GetAuthMenusResponse, GetCurrentUserInfoResponse, GetDependenciesData, GetDependenciesResponse, HistoricalMetricsData, HistoricalMetricsResponse, DagStatsResponse2, StructureDataData, StructureDataResponse2, GetDagStructureData, GetDagStructureResponse, GetGridRunsData, GetGridRunsResponse, GetGridTiSummariesData, GetGridTiSummariesResponse, GetCalendarData, GetCalendarResponse, ListTeamsData, ListTeamsResponse } from './types.gen'; export class AssetService { /** @@ -41,7 +41,7 @@ export class AssetService { } }); } - + /** * Get Asset Aliases * Get asset aliases. @@ -71,7 +71,7 @@ export class AssetService { } }); } - + /** * Get Asset Alias * Get an asset alias. @@ -95,7 +95,7 @@ export class AssetService { } }); } - + /** * Get Asset Events * Get asset events. @@ -143,7 +143,7 @@ export class AssetService { } }); } - + /** * Create Asset Event * Create asset events. @@ -166,7 +166,7 @@ export class AssetService { } }); } - + /** * Materialize Asset * Materialize an asset by triggering a DAG run that produces it. @@ -191,7 +191,7 @@ export class AssetService { } }); } - + /** * Get Asset Queued Events * Get queued asset events for an asset. @@ -219,7 +219,7 @@ export class AssetService { } }); } - + /** * Delete Asset Queued Events * Delete queued asset events for an asset. @@ -247,7 +247,7 @@ export class AssetService { } }); } - + /** * Get Asset * Get an asset. @@ -271,7 +271,7 @@ export class AssetService { } }); } - + /** * Get Dag Asset Queued Events * Get queued asset events for a DAG. @@ -299,7 +299,7 @@ export class AssetService { } }); } - + /** * Delete Dag Asset Queued Events * @param data The data for the request. @@ -327,7 +327,7 @@ export class AssetService { } }); } - + /** * Get Dag Asset Queued Event * Get a queued asset event for a DAG. @@ -357,7 +357,7 @@ export class AssetService { } }); } - + /** * Delete Dag Asset Queued Event * Delete a queued asset event for a DAG. @@ -388,7 +388,7 @@ export class AssetService { } }); } - + /** * Next Run Assets * @param data The data for the request. @@ -408,7 +408,7 @@ export class AssetService { } }); } - + } export class BackfillService { @@ -439,7 +439,7 @@ export class BackfillService { } }); } - + /** * Create Backfill * @param data The data for the request. @@ -462,7 +462,7 @@ export class BackfillService { } }); } - + /** * Get Backfill * @param data The data for the request. @@ -485,7 +485,7 @@ export class BackfillService { } }); } - + /** * Pause Backfill * @param data The data for the request. @@ -509,7 +509,7 @@ export class BackfillService { } }); } - + /** * Unpause Backfill * @param data The data for the request. @@ -533,7 +533,7 @@ export class BackfillService { } }); } - + /** * Cancel Backfill * @param data The data for the request. @@ -557,7 +557,7 @@ export class BackfillService { } }); } - + /** * Create Backfill Dry Run * @param data The data for the request. @@ -580,7 +580,7 @@ export class BackfillService { } }); } - + /** * List Backfills Ui * @param data The data for the request. @@ -609,7 +609,7 @@ export class BackfillService { } }); } - + } export class ConnectionService { @@ -636,7 +636,7 @@ export class ConnectionService { } }); } - + /** * Get Connection * Get a connection entry. @@ -660,7 +660,7 @@ export class ConnectionService { } }); } - + /** * Patch Connection * Update a connection entry. @@ -692,7 +692,7 @@ export class ConnectionService { } }); } - + /** * Get Connections * Get all connection entries. @@ -722,7 +722,7 @@ export class ConnectionService { } }); } - + /** * Post Connection * Create connection entry. @@ -745,7 +745,7 @@ export class ConnectionService { } }); } - + /** * Bulk Connections * Bulk create, update, and delete connections. @@ -767,7 +767,7 @@ export class ConnectionService { } }); } - + /** * Test Connection * Test an API connection. @@ -793,7 +793,7 @@ export class ConnectionService { } }); } - + /** * Create Default Connections * Create default connections. @@ -810,7 +810,7 @@ export class ConnectionService { } }); } - + /** * Hook Meta Data * Retrieve information about available connection types (hook classes) and their parameters. @@ -823,7 +823,7 @@ export class ConnectionService { url: '/ui/connections/hook_meta' }); } - + } export class DagRunService { @@ -851,7 +851,7 @@ export class DagRunService { } }); } - + /** * Delete Dag Run * Delete a DAG Run entry. @@ -878,7 +878,7 @@ export class DagRunService { } }); } - + /** * Patch Dag Run * Modify a DAG Run. @@ -912,7 +912,7 @@ export class DagRunService { } }); } - + /** * Get Upstream Asset Events * If dag run is asset-triggered, return the asset events that triggered it. @@ -938,7 +938,7 @@ export class DagRunService { } }); } - + /** * Clear Dag Run * @param data The data for the request. @@ -966,7 +966,7 @@ export class DagRunService { } }); } - + /** * Get Dag Runs * Get all DAG Runs. @@ -1062,7 +1062,7 @@ export class DagRunService { } }); } - + /** * Trigger Dag Run * Trigger a DAG. @@ -1091,7 +1091,7 @@ export class DagRunService { } }); } - + /** * Experimental: Wait for a dag run to complete, and return task results if requested. * 🚧 This is an experimental endpoint and may change or be removed without notice.Successful response are streamed as newline-delimited JSON (NDJSON). Each line is a JSON object representing the DAG run state. @@ -1123,7 +1123,7 @@ export class DagRunService { } }); } - + /** * Get List Dag Runs Batch * Get a list of DAG Runs. @@ -1150,7 +1150,7 @@ export class DagRunService { } }); } - + } export class ExperimentalService { @@ -1185,7 +1185,7 @@ export class ExperimentalService { } }); } - + } export class DagSourceService { @@ -1222,7 +1222,7 @@ export class DagSourceService { } }); } - + } export class DagStatsService { @@ -1250,7 +1250,7 @@ export class DagStatsService { } }); } - + } export class ConfigService { @@ -1281,7 +1281,7 @@ export class ConfigService { } }); } - + /** * Get Config Value * @param data The data for the request. @@ -1311,7 +1311,7 @@ export class ConfigService { } }); } - + /** * Get Configs * Get configs for UI. @@ -1327,7 +1327,7 @@ export class ConfigService { } }); } - + } export class DagWarningService { @@ -1361,7 +1361,7 @@ export class DagWarningService { } }); } - + } export class DagService { @@ -1439,7 +1439,7 @@ export class DagService { } }); } - + /** * Patch Dags * Patch multiple DAGs. @@ -1483,7 +1483,7 @@ export class DagService { } }); } - + /** * Get Dag * Get basic information about a DAG. @@ -1508,7 +1508,7 @@ export class DagService { } }); } - + /** * Patch Dag * Patch the specific DAG. @@ -1540,7 +1540,7 @@ export class DagService { } }); } - + /** * Delete Dag * Delete the specific DAG. @@ -1565,7 +1565,7 @@ export class DagService { } }); } - + /** * Get Dag Details * Get details of DAG. @@ -1590,7 +1590,7 @@ export class DagService { } }); } - + /** * Favorite Dag * Mark the DAG as favorite. @@ -1614,7 +1614,7 @@ export class DagService { } }); } - + /** * Unfavorite Dag * Unmark the DAG as favorite. @@ -1639,7 +1639,7 @@ export class DagService { } }); } - + /** * Get Dag Tags * Get all DAG tags. @@ -1668,7 +1668,7 @@ export class DagService { } }); } - + /** * Get Dags * Get DAGs with recent DagRun. @@ -1727,7 +1727,7 @@ export class DagService { } }); } - + /** * Get Latest Run Info * Get latest run. @@ -1749,7 +1749,7 @@ export class DagService { } }); } - + } export class EventLogService { @@ -1775,7 +1775,7 @@ export class EventLogService { } }); } - + /** * Get Event Logs * Get all Event Logs. @@ -1834,7 +1834,7 @@ export class EventLogService { } }); } - + } export class ExtraLinksService { @@ -1869,7 +1869,7 @@ export class ExtraLinksService { } }); } - + } export class TaskInstanceService { @@ -1904,7 +1904,7 @@ export class TaskInstanceService { } }); } - + /** * Get Task Instance * Get task instance. @@ -1932,7 +1932,7 @@ export class TaskInstanceService { } }); } - + /** * Patch Task Instance * Update a task instance. @@ -1971,7 +1971,7 @@ export class TaskInstanceService { } }); } - + /** * Delete Task Instance * Delete a task instance. @@ -2003,7 +2003,7 @@ export class TaskInstanceService { } }); } - + /** * Get Mapped Task Instances * Get list of mapped task instances. @@ -2109,7 +2109,7 @@ export class TaskInstanceService { } }); } - + /** * Get Task Instance Dependencies * Get dependencies blocking task from getting scheduled. @@ -2139,7 +2139,7 @@ export class TaskInstanceService { } }); } - + /** * Get Task Instance Dependencies * Get dependencies blocking task from getting scheduled. @@ -2171,7 +2171,7 @@ export class TaskInstanceService { } }); } - + /** * Get Task Instance Tries * Get list of task instances history. @@ -2203,7 +2203,7 @@ export class TaskInstanceService { } }); } - + /** * Get Mapped Task Instance Tries * @param data The data for the request. @@ -2232,7 +2232,7 @@ export class TaskInstanceService { } }); } - + /** * Get Mapped Task Instance * Get task instance. @@ -2262,7 +2262,7 @@ export class TaskInstanceService { } }); } - + /** * Patch Task Instance * Update a task instance. @@ -2301,7 +2301,7 @@ export class TaskInstanceService { } }); } - + /** * Get Task Instances * Get list of task instances. @@ -2418,7 +2418,7 @@ export class TaskInstanceService { } }); } - + /** * Bulk Task Instances * Bulk update, and delete task instances. @@ -2446,7 +2446,7 @@ export class TaskInstanceService { } }); } - + /** * Get Task Instances Batch * Get list of task instances. @@ -2475,7 +2475,7 @@ export class TaskInstanceService { } }); } - + /** * Get Task Instance Try Details * Get task instance details by try number. @@ -2509,7 +2509,7 @@ export class TaskInstanceService { } }); } - + /** * Get Mapped Task Instance Try Details * @param data The data for the request. @@ -2540,7 +2540,7 @@ export class TaskInstanceService { } }); } - + /** * Post Clear Task Instances * Clear task instances. @@ -2568,7 +2568,7 @@ export class TaskInstanceService { } }); } - + /** * Patch Task Instance Dry Run * Update a task instance dry_run mode. @@ -2606,7 +2606,7 @@ export class TaskInstanceService { } }); } - + /** * Patch Task Instance Dry Run * Update a task instance dry_run mode. @@ -2644,7 +2644,7 @@ export class TaskInstanceService { } }); } - + /** * Get Log * Get logs for a specific task instance. @@ -2686,7 +2686,7 @@ export class TaskInstanceService { } }); } - + /** * Get External Log Url * Get external log URL for a specific task instance. @@ -2721,7 +2721,7 @@ export class TaskInstanceService { } }); } - + /** * Update Hitl Detail * Update a Human-in-the-loop detail. @@ -2755,7 +2755,7 @@ export class TaskInstanceService { } }); } - + /** * Get Hitl Detail * Get a Human-in-the-loop detail of a specific task instance. @@ -2785,7 +2785,39 @@ export class TaskInstanceService { } }); } - + + /** + * Get Hitl Detail Try Detail + * Get a Human-in-the-loop detail of a specific task instance. + * @param data The data for the request. + * @param data.dagId + * @param data.dagRunId + * @param data.taskId + * @param data.mapIndex + * @param data.tryNumber + * @returns HITLDetailHisotry Successful Response + * @throws ApiError + */ + public static getHitlDetailTryDetail(data: GetHitlDetailTryDetailData): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/{map_index}/hitlDetails/tries/{try_number}', + path: { + dag_id: data.dagId, + dag_run_id: data.dagRunId, + task_id: data.taskId, + map_index: data.mapIndex, + try_number: data.tryNumber + }, + errors: { + 401: 'Unauthorized', + 403: 'Forbidden', + 404: 'Not Found', + 422: 'Validation Error' + } + }); + } + /** * Get Hitl Details * Get Human-in-the-loop details. @@ -2846,7 +2878,7 @@ export class TaskInstanceService { } }); } - + } export class ImportErrorService { @@ -2873,7 +2905,7 @@ export class ImportErrorService { } }); } - + /** * Get Import Errors * Get all import errors. @@ -2902,7 +2934,7 @@ export class ImportErrorService { } }); } - + } export class JobService { @@ -2959,7 +2991,7 @@ export class JobService { } }); } - + } export class PluginService { @@ -2986,7 +3018,7 @@ export class PluginService { } }); } - + /** * Import Errors * @returns PluginImportErrorCollectionResponse Successful Response @@ -3002,7 +3034,7 @@ export class PluginService { } }); } - + } export class PoolService { @@ -3030,7 +3062,7 @@ export class PoolService { } }); } - + /** * Get Pool * Get a pool. @@ -3054,7 +3086,7 @@ export class PoolService { } }); } - + /** * Patch Pool * Update a Pool. @@ -3086,7 +3118,7 @@ export class PoolService { } }); } - + /** * Get Pools * Get all pools entries. @@ -3116,7 +3148,7 @@ export class PoolService { } }); } - + /** * Post Pool * Create a Pool. @@ -3139,7 +3171,7 @@ export class PoolService { } }); } - + /** * Bulk Pools * Bulk create, update, and delete pools. @@ -3161,7 +3193,7 @@ export class PoolService { } }); } - + } export class ProviderService { @@ -3189,7 +3221,7 @@ export class ProviderService { } }); } - + } export class XcomService { @@ -3231,7 +3263,7 @@ export class XcomService { } }); } - + /** * Update Xcom Entry * Update an existing XCom entry. @@ -3265,7 +3297,7 @@ export class XcomService { } }); } - + /** * Delete Xcom Entry * Delete an XCom entry. @@ -3300,7 +3332,7 @@ export class XcomService { } }); } - + /** * Get Xcom Entries * Get all XCom entries. @@ -3367,7 +3399,7 @@ export class XcomService { } }); } - + /** * Create Xcom Entry * Create an XCom entry. @@ -3399,7 +3431,7 @@ export class XcomService { } }); } - + } export class TaskService { @@ -3431,7 +3463,7 @@ export class TaskService { } }); } - + /** * Get Task * Get simplified representation of a task. @@ -3458,7 +3490,7 @@ export class TaskService { } }); } - + } export class VariableService { @@ -3485,7 +3517,7 @@ export class VariableService { } }); } - + /** * Get Variable * Get a variable entry. @@ -3509,7 +3541,7 @@ export class VariableService { } }); } - + /** * Patch Variable * Update a variable by key. @@ -3541,7 +3573,7 @@ export class VariableService { } }); } - + /** * Get Variables * Get all Variables entries. @@ -3570,7 +3602,7 @@ export class VariableService { } }); } - + /** * Post Variable * Create a variable. @@ -3593,7 +3625,7 @@ export class VariableService { } }); } - + /** * Bulk Variables * Bulk create, update, and delete variables. @@ -3615,7 +3647,7 @@ export class VariableService { } }); } - + } export class DagParsingService { @@ -3642,7 +3674,7 @@ export class DagParsingService { } }); } - + } export class DagVersionService { @@ -3671,7 +3703,7 @@ export class DagVersionService { } }); } - + /** * Get Dag Versions * Get all DAG Versions. @@ -3711,7 +3743,7 @@ export class DagVersionService { } }); } - + } export class MonitorService { @@ -3726,7 +3758,7 @@ export class MonitorService { url: '/api/v2/monitor/health' }); } - + } export class VersionService { @@ -3742,7 +3774,7 @@ export class VersionService { url: '/api/v2/version' }); } - + } export class LoginService { @@ -3767,7 +3799,7 @@ export class LoginService { } }); } - + /** * Logout * Logout the user. @@ -3783,7 +3815,7 @@ export class LoginService { } }); } - + } export class AuthLinksService { @@ -3798,7 +3830,7 @@ export class AuthLinksService { url: '/ui/auth/menus' }); } - + /** * Get Current User Info * Convienently get the current authenticated user information. @@ -3811,7 +3843,7 @@ export class AuthLinksService { url: '/ui/auth/me' }); } - + } export class DependenciesService { @@ -3836,7 +3868,7 @@ export class DependenciesService { } }); } - + } export class DashboardService { @@ -3863,7 +3895,7 @@ export class DashboardService { } }); } - + /** * Dag Stats * Return basic DAG stats with counts of DAGs in various states. @@ -3876,7 +3908,7 @@ export class DashboardService { url: '/ui/dashboard/dag_stats' }); } - + } export class StructureService { @@ -3913,7 +3945,7 @@ export class StructureService { } }); } - + } export class GridService { @@ -3969,7 +4001,7 @@ export class GridService { } }); } - + /** * Get Grid Runs * Get info about a run for the grid. @@ -4014,7 +4046,7 @@ export class GridService { } }); } - + /** * Get Grid Ti Summaries * Get states for TIs / "groups" of TIs. @@ -4050,7 +4082,7 @@ export class GridService { } }); } - + } export class CalendarService { @@ -4086,7 +4118,7 @@ export class CalendarService { } }); } - + } export class TeamsService { @@ -4113,5 +4145,5 @@ export class TeamsService { } }); } - -} \ No newline at end of file + +} 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 48abfbd4ae690..e82fb713e37dc 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 @@ -1008,7 +1008,7 @@ export type HITLDetailCollection = { /** * Schema for Human-in-the-loop detail history. */ -export type HITLDetailHistory = { +export type HITLDetailHisotry = { options: Array<(string)>; subject: string; body?: string | null; @@ -1387,10 +1387,6 @@ export type TaskInstanceHistoryResponse = { executor: string | null; executor_config: string; dag_version: DagVersionResponse | null; -<<<<<<< HEAD - hitl_detail: HITLDetailHistory | null; -======= ->>>>>>> accf09b17d (fix(hitl): remove hitl from task_instance_history but create a separate hitl_try endpoint instead) }; /** @@ -3058,7 +3054,7 @@ export type GetHitlDetailData = { export type GetHitlDetailResponse = HITLDetail; -export type GetHitlDetailTryData = { +export type GetHitlDetailTryDetailData = { dagId: string; dagRunId: string; mapIndex: number; @@ -3066,7 +3062,7 @@ export type GetHitlDetailTryData = { tryNumber: number; }; -export type GetHitlDetailTryResponse = HITLDetailHisotry; +export type GetHitlDetailTryDetailResponse = HITLDetailHisotry; export type GetHitlDetailsData = { /** @@ -5702,7 +5698,7 @@ export type $OpenApiTs = { }; '/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/{task_id}/{map_index}/hitlDetails/tries/{try_number}': { get: { - req: GetHitlDetailTryData; + req: GetHitlDetailTryDetailData; res: { /** * Successful Response diff --git a/airflow-core/src/airflow/ui/src/pages/TaskInstance/HITLResponse.tsx b/airflow-core/src/airflow/ui/src/pages/TaskInstance/HITLResponse.tsx index 78b523dcde9f3..c00de9a96a3a5 100644 --- a/airflow-core/src/airflow/ui/src/pages/TaskInstance/HITLResponse.tsx +++ b/airflow-core/src/airflow/ui/src/pages/TaskInstance/HITLResponse.tsx @@ -21,15 +21,15 @@ import { useParams } from "react-router-dom"; import { useSearchParams } from "react-router-dom"; import { - useTaskInstanceServiceGetHitlDetailTry, + useTaskInstanceServiceGetHitlDetailTryDetail, useTaskInstanceServiceGetMappedTaskInstance, } from "openapi/queries"; import { TaskTrySelect } from "src/components/TaskTrySelect"; import { ProgressBar } from "src/components/ui"; import { SearchParamsKeys } from "src/constants/searchParams"; +import { isStatePending, useAutoRefresh } from "src/utils"; import { HITLResponseForm } from "../HITLTaskInstances/HITLResponseForm"; -import { isStatePending, useAutoRefresh } from "src/utils"; export const HITLResponse = () => { const { dagId, mapIndex, runId, taskId } = useParams(); @@ -65,7 +65,7 @@ export const HITLResponse = () => { const tryNumber = tryNumberParam === null ? -1 : parseInt(tryNumberParam, 10); - const { data: hitlDetail } = useTaskInstanceServiceGetHitlDetailTry( + const { data: hitlDetail } = useTaskInstanceServiceGetHitlDetailTryDetail( { dagId: dagId ?? "", dagRunId: runId ?? "", @@ -76,15 +76,7 @@ export const HITLResponse = () => { undefined, ); - if (!taskInstance) { - return ( - - - - ); - } - - if (!hitlDetail) { + if (!taskInstance || !hitlDetail) { return ( diff --git a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_hitl.py b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_hitl.py index 93d8c4f63d472..3f370760577e9 100644 --- a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_hitl.py +++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_hitl.py @@ -647,7 +647,7 @@ def test_should_respond_200_with_existing_response_and_concrete_query( ("rendered_map_index", lambda x: x["task_instance"]["rendered_map_index"]), ("task_instance_operator", lambda x: x["task_instance"]["operator_name"]), ("task_instance_state", lambda x: x["task_instance"]["state"]), - # htil key + # hitl key ("subject", itemgetter("subject")), ("responded_at", itemgetter("responded_at")), ("created_at", itemgetter("created_at")), @@ -661,7 +661,7 @@ def test_should_respond_200_with_existing_response_and_concrete_query( "rendered_map_index", "task_instance_operator", "task_instance_state", - # htil key + # hitl key "subject", "responded_at", "created_at", diff --git a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_task_instances.py b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_task_instances.py index 63601f5cb02c9..14458f199e193 100644 --- a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_task_instances.py +++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_task_instances.py @@ -35,7 +35,6 @@ from airflow.jobs.triggerer_job_runner import TriggererJobRunner from airflow.models import DagRun, Log, TaskInstance from airflow.models.dag_version import DagVersion -from airflow.models.hitl import HITLDetail from airflow.models.renderedtifields import RenderedTaskInstanceFields as RTIF from airflow.models.taskinstancehistory import TaskInstanceHistory from airflow.models.taskmap import TaskMap @@ -55,9 +54,6 @@ from tests_common.test_utils.mock_operators import MockOperator from tests_common.test_utils.taskinstance import create_task_instance -if TYPE_CHECKING: - from tests_common.pytest_plugin import CreateTaskInstance - pytestmark = pytest.mark.db_test DEFAULT = datetime(2020, 1, 1) @@ -2101,7 +2097,6 @@ def test_should_respond_200(self, test_client, session): "id": response_data["dag_version"]["id"], "version_number": 1, }, - "hitl_detail": None, } @pytest.mark.parametrize("try_number", [1, 2]) @@ -2148,7 +2143,6 @@ def test_should_respond_200_with_different_try_numbers(self, test_client, try_nu "id": response_data["dag_version"]["id"], "version_number": 1, }, - "hitl_detail": None, } @pytest.mark.parametrize("try_number", [1, 2]) @@ -2226,7 +2220,6 @@ def test_should_respond_200_with_mapped_task_at_different_try_numbers( "id": response_data["dag_version"]["id"], "version_number": 1, }, - "hitl_detail": None, } def test_should_respond_200_with_task_state_in_deferred(self, test_client, session): @@ -2299,7 +2292,6 @@ def test_should_respond_200_with_task_state_in_deferred(self, test_client, sessi "id": response_data["dag_version"]["id"], "version_number": 1, }, - "hitl_detail": None, } def test_should_respond_200_with_task_state_in_removed(self, test_client, session): @@ -2347,77 +2339,6 @@ def test_should_respond_200_with_task_state_in_removed(self, test_client, sessio "id": response_data["dag_version"]["id"], "version_number": 1, }, - "hitl_detail": None, - } - - def test_should_respond_200_with_hitl( - self, test_client, create_task_instance: CreateTaskInstance, session - ): - ti = create_task_instance(dag_id="test_hitl_dag", task_id="sample_task_hitl") - ti.try_number = 1 - session.add(ti) - hitl_detail = HITLDetail( - ti_id=ti.id, - options=["Approve", "Reject"], - subject="This is subject", - body="this is body", - defaults=["Approve"], - multiple=False, - params={"input_1": 1}, - assignees=None, - ) - session.add(hitl_detail) - session.commit() - # Record the TaskInstanceHistory - TaskInstanceHistory.record_ti(ti, session=session) - session.flush() - - response = test_client.get( - f"/dags/{ti.dag_id}/dagRuns/{ti.run_id}/taskInstances/{ti.task_id}/tries/1", - ) - assert response.status_code == 200 - assert response.json() == { - "dag_id": "test_hitl_dag", - "dag_display_name": "test_hitl_dag", - "duration": None, - "end_date": mock.ANY, - "executor": None, - "executor_config": "{}", - "hostname": "", - "map_index": -1, - "max_tries": 0, - "operator": "EmptyOperator", - "operator_name": "EmptyOperator", - "pid": None, - "pool": "default_pool", - "pool_slots": 1, - "priority_weight": 1, - "queue": "default", - "queued_when": None, - "scheduled_when": None, - "start_date": None, - "state": None, - "task_id": "sample_task_hitl", - "task_display_name": "sample_task_hitl", - "try_number": 1, - "unixname": getuser(), - "dag_run_id": "test", - "dag_version": mock.ANY, - "hitl_detail": { - "assigned_users": [], - "body": "this is body", - "chosen_options": None, - "created_at": mock.ANY, - "defaults": ["Approve"], - "multiple": False, - "options": ["Approve", "Reject"], - "params": {"input_1": {"value": 1, "description": None, "schema": {}}}, - "params_input": {}, - "responded_at": None, - "responded_by_user": None, - "response_received": False, - "subject": "This is subject", - }, } def test_should_respond_401(self, unauthenticated_test_client): @@ -2497,7 +2418,6 @@ def test_should_respond_200_with_versions( "created_at": mock.ANY, "dag_display_name": "dag_with_multiple_versions", }, - "hitl_detail": None, } @pytest.mark.parametrize( @@ -2552,7 +2472,6 @@ def test_should_respond_200_with_versions_using_url_template( "created_at": mock.ANY, "dag_display_name": "dag_with_multiple_versions", }, - "hitl_detail": None, } def test_should_not_return_duplicate_runs(self, test_client, session): @@ -3636,7 +3555,6 @@ def test_should_respond_200(self, test_client, session): "id": response_data["task_instances"][0]["dag_version"]["id"], "version_number": 1, }, - "hitl_detail": None, }, { "dag_id": "example_python_operator", @@ -3674,88 +3592,11 @@ def test_should_respond_200(self, test_client, session): "id": response_data["task_instances"][1]["dag_version"]["id"], "version_number": 1, }, - "hitl_detail": None, }, ], "total_entries": 2, } - def test_should_respond_200_with_hitl( - self, test_client, create_task_instance: CreateTaskInstance, session - ): - ti = create_task_instance(dag_id="test_hitl_dag", task_id="sample_task_hitl") - ti.try_number = 1 - session.add(ti) - hitl_detail = HITLDetail( - ti_id=ti.id, - options=["Approve", "Reject"], - subject="This is subject", - body="this is body", - defaults=["Approve"], - multiple=False, - params={"input_1": 1}, - assignees=None, - ) - session.add(hitl_detail) - session.commit() - # Record the TaskInstanceHistory - TaskInstanceHistory.record_ti(ti, session=session) - session.flush() - - with assert_queries_count(3): - response = test_client.get( - f"/dags/{ti.dag_id}/dagRuns/{ti.run_id}/taskInstances/{ti.task_id}/tries", - ) - assert response.status_code == 200 - assert response.json() == { - "task_instances": [ - { - "dag_id": "test_hitl_dag", - "dag_display_name": "test_hitl_dag", - "duration": None, - "end_date": mock.ANY, - "executor": None, - "executor_config": "{}", - "hostname": "", - "map_index": -1, - "max_tries": 0, - "operator": "EmptyOperator", - "operator_name": "EmptyOperator", - "pid": None, - "pool": "default_pool", - "pool_slots": 1, - "priority_weight": 1, - "queue": "default", - "queued_when": None, - "scheduled_when": None, - "start_date": None, - "state": None, - "task_id": "sample_task_hitl", - "task_display_name": "sample_task_hitl", - "try_number": 1, - "unixname": getuser(), - "dag_run_id": "test", - "dag_version": mock.ANY, - "hitl_detail": { - "assigned_users": [], - "body": "this is body", - "chosen_options": None, - "created_at": mock.ANY, - "defaults": ["Approve"], - "multiple": False, - "options": ["Approve", "Reject"], - "params": {"input_1": {"value": 1, "description": None, "schema": {}}}, - "params_input": {}, - "responded_at": None, - "responded_by_user": None, - "response_received": False, - "subject": "This is subject", - }, - }, - ], - "total_entries": 1, - } - def test_should_respond_401(self, unauthenticated_test_client): response = unauthenticated_test_client.get( "/dags/example_python_operator/dagRuns/TEST_DAG_RUN_ID/taskInstances/print_the_context/tries" @@ -3822,7 +3663,6 @@ def test_ti_in_retry_state_not_returned(self, test_client, session): "id": response_data["task_instances"][0]["dag_version"]["id"], "version_number": 1, }, - "hitl_detail": None, }, ], "total_entries": 1, @@ -3906,7 +3746,6 @@ def test_mapped_task_should_respond_200(self, test_client, session): "id": response_data["task_instances"][0]["dag_version"]["id"], "version_number": 1, }, - "hitl_detail": None, }, { "dag_id": "example_python_operator", @@ -3944,7 +3783,6 @@ def test_mapped_task_should_respond_200(self, test_client, session): "id": response_data["task_instances"][1]["dag_version"]["id"], "version_number": 1, }, - "hitl_detail": None, }, ], "total_entries": 2, @@ -4016,7 +3854,6 @@ def test_should_respond_200_with_versions( "created_at": mock.ANY, "dag_display_name": "dag_with_multiple_versions", }, - "hitl_detail": None, } @pytest.mark.parametrize( @@ -4071,7 +3908,6 @@ def test_should_respond_200_with_versions_using_url_template( "created_at": mock.ANY, "dag_display_name": "dag_with_multiple_versions", }, - "hitl_detail": None, } diff --git a/airflow-core/tests/unit/models/test_taskinstance.py b/airflow-core/tests/unit/models/test_taskinstance.py index a082cfeb36e4a..050288247013a 100644 --- a/airflow-core/tests/unit/models/test_taskinstance.py +++ b/airflow-core/tests/unit/models/test_taskinstance.py @@ -49,7 +49,6 @@ ) from airflow.models.dag_version import DagVersion from airflow.models.dagrun import DagRun -from airflow.models.hitl_history import HITLDetailHistory from airflow.models.pool import Pool from airflow.models.renderedtifields import RenderedTaskInstanceFields from airflow.models.serialized_dag import SerializedDagModel @@ -66,9 +65,6 @@ from airflow.observability.stats import Stats from airflow.providers.standard.operators.bash import BashOperator from airflow.providers.standard.operators.empty import EmptyOperator -from airflow.providers.standard.operators.hitl import ( - HITLBranchOperator, -) from airflow.providers.standard.operators.python import PythonOperator from airflow.providers.standard.sensors.python import PythonSensor from airflow.sdk import DAG, Asset, AssetAlias, BaseOperator, BaseSensorOperator, Metadata, task, task_group @@ -2549,40 +2545,6 @@ def test_task_instance_history_is_created_when_ti_goes_for_retry(self, dag_maker # the new try_id should be different from what's recorded in tih assert str(tih[0].task_instance_id) == try_id - def test_task_instance_history_with_hitl_history_is_created_when_ti_goes_for_retry( - self, dag_maker: DagMaker, session: Session - ): - with dag_maker(serialized=True): - task = HITLBranchOperator( - task_id="hitl_test", - subject="This is subject", - body="This is body", - options=["1", "2", "3", "4", "5"], - params={"input": 1}, - retries=1, - retry_delay=datetime.timedelta(seconds=2), - notifiers=[None], - ) - - dr = dag_maker.create_dagrun() - ti = dr.task_instances[0] - try_id = ti.id - with pytest.raises(TypeError): - run_task_instance(ti, task) - ti = session.scalar(select(TaskInstance)) - assert ti is not None - # the ti.id should be different from the previous one - assert ti.id != try_id - assert ti.state == State.UP_FOR_RETRY - assert session.scalar(select(func.count()).select_from(TaskInstance)) == 1 - tih = session.scalars(select(TaskInstanceHistory)).all() - assert len(tih) == 1 - # the new try_id should be different from what's recorded in tih - assert str(tih[0].task_instance_id) == try_id - hitl_histories = session.scalars(select(HITLDetailHistory)).all() - assert len(hitl_histories) == 1 - assert str(hitl_histories[0].task_instance.id) == try_id - @pytest.mark.parametrize("pool_override", [None, "test_pool2"]) @pytest.mark.parametrize("queue_by_policy", [None, "forced_queue"]) diff --git a/airflow-ctl/src/airflowctl/api/datamodels/generated.py b/airflow-ctl/src/airflowctl/api/datamodels/generated.py index 6472a722397df..c855e82aefa60 100644 --- a/airflow-ctl/src/airflowctl/api/datamodels/generated.py +++ b/airflow-ctl/src/airflowctl/api/datamodels/generated.py @@ -1544,26 +1544,6 @@ class EventLogCollectionResponse(BaseModel): total_entries: Annotated[int, Field(title="Total Entries")] -class HITLDetailHistory(BaseModel): - """ - Schema for Human-in-the-loop detail history. - """ - - options: Annotated[list[str], Field(min_length=1, title="Options")] - subject: Annotated[str, Field(title="Subject")] - body: Annotated[str | None, Field(title="Body")] = None - defaults: Annotated[list[str] | None, Field(title="Defaults")] = None - multiple: Annotated[bool | None, Field(title="Multiple")] = False - params: Annotated[dict[str, Any] | None, Field(title="Params")] = None - assigned_users: Annotated[list[HITLUser] | None, Field(title="Assigned Users")] = None - created_at: Annotated[datetime, Field(title="Created At")] - responded_by_user: HITLUser | None = None - responded_at: Annotated[datetime | None, Field(title="Responded At")] = None - chosen_options: Annotated[list[str] | None, Field(title="Chosen Options")] = None - params_input: Annotated[dict[str, Any] | None, Field(title="Params Input")] = None - response_received: Annotated[bool | None, Field(title="Response Received")] = False - - class HITLDetailResponse(BaseModel): """ Response of updating a Human-in-the-loop detail. @@ -1728,7 +1708,6 @@ class TaskInstanceHistoryResponse(BaseModel): executor: Annotated[str | None, Field(title="Executor")] = None executor_config: Annotated[str, Field(title="Executor Config")] dag_version: DagVersionResponse | None = None - hitl_detail: HITLDetailHistory | None = None class TaskInstanceResponse(BaseModel): @@ -1978,6 +1957,27 @@ class HITLDetailCollection(BaseModel): total_entries: Annotated[int, Field(title="Total Entries")] +class HITLDetailHisotry(BaseModel): + """ + Schema for Human-in-the-loop detail history. + """ + + options: Annotated[list[str], Field(min_length=1, title="Options")] + subject: Annotated[str, Field(title="Subject")] + body: Annotated[str | None, Field(title="Body")] = None + defaults: Annotated[list[str] | None, Field(title="Defaults")] = None + multiple: Annotated[bool | None, Field(title="Multiple")] = False + params: Annotated[dict[str, Any] | None, Field(title="Params")] = None + assigned_users: Annotated[list[HITLUser] | None, Field(title="Assigned Users")] = None + created_at: Annotated[datetime, Field(title="Created At")] + responded_by_user: HITLUser | None = None + responded_at: Annotated[datetime | None, Field(title="Responded At")] = None + chosen_options: Annotated[list[str] | None, Field(title="Chosen Options")] = None + params_input: Annotated[dict[str, Any] | None, Field(title="Params Input")] = None + response_received: Annotated[bool | None, Field(title="Response Received")] = False + task_instance: TaskInstanceHistoryResponse + + class PluginCollectionResponse(BaseModel): """ Plugin Collection serializer. From 0a0fc773bed58c0b46243b54b49aa9ac93db29f3 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Tue, 2 Dec 2025 10:20:50 +0800 Subject: [PATCH 6/9] fix(hitl): fix typo --- .../src/airflow/api_fastapi/core_api/datamodels/hitl.py | 2 +- .../core_api/openapi/v2-rest-api-generated.yaml | 6 +++--- .../airflow/api_fastapi/core_api/routes/public/hitl.py | 4 ++-- .../src/airflow/ui/openapi-gen/queries/ensureQueryData.ts | 2 +- .../src/airflow/ui/openapi-gen/queries/prefetch.ts | 2 +- .../src/airflow/ui/openapi-gen/queries/queries.ts | 2 +- .../src/airflow/ui/openapi-gen/queries/suspense.ts | 2 +- .../src/airflow/ui/openapi-gen/requests/schemas.gen.ts | 4 ++-- .../src/airflow/ui/openapi-gen/requests/services.gen.ts | 2 +- .../src/airflow/ui/openapi-gen/requests/types.gen.ts | 6 +++--- .../ui/src/pages/HITLTaskInstances/HITLResponseForm.tsx | 6 +++--- airflow-core/src/airflow/ui/src/utils/hitl.ts | 8 ++++---- airflow-ctl/src/airflowctl/api/datamodels/generated.py | 2 +- 13 files changed, 24 insertions(+), 24 deletions(-) diff --git a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/hitl.py b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/hitl.py index a397a2b27a983..98d5d1ee20d14 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/hitl.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/hitl.py @@ -104,7 +104,7 @@ class HITLDetailCollection(BaseModel): total_entries: int -class HITLDetailHisotry(BaseHITLDetail): +class HITLDetailHistory(BaseHITLDetail): """Schema for Human-in-the-loop detail history.""" task_instance: TaskInstanceHistoryResponse 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 06c6471f320da..74e9f28818980 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 @@ -8489,7 +8489,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/HITLDetailHisotry' + $ref: '#/components/schemas/HITLDetailHistory' '401': content: application/json: @@ -11483,7 +11483,7 @@ components: - total_entries title: HITLDetailCollection description: Schema for a collection of Human-in-the-loop details. - HITLDetailHisotry: + HITLDetailHistory: properties: options: items: @@ -11556,7 +11556,7 @@ components: - subject - created_at - task_instance - title: HITLDetailHisotry + title: HITLDetailHistory description: Schema for Human-in-the-loop detail history. HITLDetailResponse: properties: diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py index 6b9192d3d523f..63fb4995cc323 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py @@ -47,7 +47,7 @@ from airflow.api_fastapi.core_api.datamodels.hitl import ( HITLDetail, HITLDetailCollection, - HITLDetailHisotry, + HITLDetailHistory, HITLDetailResponse, UpdateHITLDetailPayload, ) @@ -229,7 +229,7 @@ def get_hitl_detail_try_detail( session: SessionDep, map_index: int = -1, try_number: int = -1, -) -> HITLDetailHisotry: +) -> HITLDetailHistory: """Get a Human-in-the-loop detail of a specific task instance.""" task_instance_history = _get_task_instance_with_hitl_detail( dag_id=dag_id, 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 03060fe2a30c5..dd86e43c51e68 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts @@ -1101,7 +1101,7 @@ export const ensureUseTaskInstanceServiceGetHitlDetailData = (queryClient: Query * @param data.taskId * @param data.mapIndex * @param data.tryNumber -* @returns HITLDetailHisotry Successful Response +* @returns HITLDetailHistory Successful Response * @throws ApiError */ export const ensureUseTaskInstanceServiceGetHitlDetailTryDetailData = (queryClient: QueryClient, { dagId, dagRunId, mapIndex, taskId, tryNumber }: { 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 61fd755fe6ada..1a0495ba776af 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts @@ -1101,7 +1101,7 @@ export const prefetchUseTaskInstanceServiceGetHitlDetail = (queryClient: QueryCl * @param data.taskId * @param data.mapIndex * @param data.tryNumber -* @returns HITLDetailHisotry Successful Response +* @returns HITLDetailHistory Successful Response * @throws ApiError */ export const prefetchUseTaskInstanceServiceGetHitlDetailTryDetail = (queryClient: QueryClient, { dagId, dagRunId, mapIndex, taskId, tryNumber }: { 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 9eb6524487c19..44537a8de850e 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts @@ -1101,7 +1101,7 @@ export const useTaskInstanceServiceGetHitlDetail = = unknown[]>({ dagId, dagRunId, mapIndex, taskId, tryNumber }: { 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 1a9051f4a6c23..1e31c75ededdb 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts @@ -1101,7 +1101,7 @@ export const useTaskInstanceServiceGetHitlDetailSuspense = = unknown[]>({ dagId, dagRunId, mapIndex, taskId, tryNumber }: { diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts index db97e946f60da..06eae729f48b0 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/requests/schemas.gen.ts @@ -3913,7 +3913,7 @@ export const $HITLDetailCollection = { description: 'Schema for a collection of Human-in-the-loop details.' } as const; -export const $HITLDetailHisotry = { +export const $HITLDetailHistory = { properties: { options: { items: { @@ -4026,7 +4026,7 @@ export const $HITLDetailHisotry = { }, type: 'object', required: ['options', 'subject', 'created_at', 'task_instance'], - title: 'HITLDetailHisotry', + title: 'HITLDetailHistory', description: 'Schema for Human-in-the-loop detail history.' } as const; 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 166718c2f11f7..8d968add609e8 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 @@ -2795,7 +2795,7 @@ export class TaskInstanceService { * @param data.taskId * @param data.mapIndex * @param data.tryNumber - * @returns HITLDetailHisotry Successful Response + * @returns HITLDetailHistory Successful Response * @throws ApiError */ public static getHitlDetailTryDetail(data: GetHitlDetailTryDetailData): CancelablePromise { 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 e82fb713e37dc..058a06912ecb3 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 @@ -1008,7 +1008,7 @@ export type HITLDetailCollection = { /** * Schema for Human-in-the-loop detail history. */ -export type HITLDetailHisotry = { +export type HITLDetailHistory = { options: Array<(string)>; subject: string; body?: string | null; @@ -3062,7 +3062,7 @@ export type GetHitlDetailTryDetailData = { tryNumber: number; }; -export type GetHitlDetailTryDetailResponse = HITLDetailHisotry; +export type GetHitlDetailTryDetailResponse = HITLDetailHistory; export type GetHitlDetailsData = { /** @@ -5703,7 +5703,7 @@ export type $OpenApiTs = { /** * Successful Response */ - 200: HITLDetailHisotry; + 200: HITLDetailHistory; /** * Unauthorized */ diff --git a/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLResponseForm.tsx b/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLResponseForm.tsx index f0138b808ea67..b29eb8e404504 100644 --- a/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLResponseForm.tsx +++ b/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLResponseForm.tsx @@ -22,7 +22,7 @@ import { useTranslation } from "react-i18next"; import { FiSend } from "react-icons/fi"; import { useSearchParams } from "react-router-dom"; -import type { HITLDetailHisotry, TaskInstanceHistoryResponse } from "openapi/requests/types.gen"; +import type { HITLDetailHistory, TaskInstanceHistoryResponse } from "openapi/requests/types.gen"; import { FlexibleForm } from "src/components/FlexibleForm/FlexibleForm"; import Time from "src/components/Time"; import { useParamStore } from "src/queries/useParamStore"; @@ -33,12 +33,12 @@ import { getHITLParamsDict, getHITLFormData, getPreloadHITLFormData } from "src/ type HITLResponseFormProps = { readonly hitlDetail: { task_instance: TaskInstanceHistoryResponse; - } & Omit; + } & Omit; }; const isHighlightOption = ( option: string, - hitlDetail: HITLDetailHisotry, + hitlDetail: HITLDetailHistory, preloadedHITLOptions: Array, ) => { // preload's priority is higher than default diff --git a/airflow-core/src/airflow/ui/src/utils/hitl.ts b/airflow-core/src/airflow/ui/src/utils/hitl.ts index 50f36405ad52a..6ea2c88eca521 100644 --- a/airflow-core/src/airflow/ui/src/utils/hitl.ts +++ b/airflow-core/src/airflow/ui/src/utils/hitl.ts @@ -18,7 +18,7 @@ */ import type { TFunction } from "i18next"; -import type { HITLDetail, HITLDetailHisotry } from "openapi/requests/types.gen"; +import type { HITLDetail, HITLDetailHistory } from "openapi/requests/types.gen"; import type { ParamSchema, ParamsSpec } from "src/queries/useDagParams"; export type HITLResponseParams = { @@ -26,14 +26,14 @@ export type HITLResponseParams = { params_input?: Record; }; -const getChosenOptionsValue = (hitlDetail: HITLDetailHisotry) => { +const getChosenOptionsValue = (hitlDetail: HITLDetailHistory) => { // if response_received is true, display the chosen_options, otherwise display the defaults const sourceValues = hitlDetail.response_received ? hitlDetail.chosen_options : hitlDetail.defaults; return hitlDetail.multiple ? sourceValues : sourceValues?.[0]; }; -export const getPreloadHITLFormData = (searchParams: URLSearchParams, hitlDetail: HITLDetailHisotry) => { +export const getPreloadHITLFormData = (searchParams: URLSearchParams, hitlDetail: HITLDetailHistory) => { const preloadedHITLParams: Record = Object.fromEntries( [...searchParams.entries()] .filter(([key]) => key !== "_options") @@ -65,7 +65,7 @@ export const getPreloadHITLFormData = (searchParams: URLSearchParams, hitlDetail }; export const getHITLParamsDict = ( - hitlDetail: HITLDetailHisotry, + hitlDetail: HITLDetailHistory, translate: TFunction, searchParams: URLSearchParams, ): ParamsSpec => { diff --git a/airflow-ctl/src/airflowctl/api/datamodels/generated.py b/airflow-ctl/src/airflowctl/api/datamodels/generated.py index c855e82aefa60..5e42be6c1bd87 100644 --- a/airflow-ctl/src/airflowctl/api/datamodels/generated.py +++ b/airflow-ctl/src/airflowctl/api/datamodels/generated.py @@ -1957,7 +1957,7 @@ class HITLDetailCollection(BaseModel): total_entries: Annotated[int, Field(title="Total Entries")] -class HITLDetailHisotry(BaseModel): +class HITLDetailHistory(BaseModel): """ Schema for Human-in-the-loop detail history. """ From 856754dad458e53b8543878b68b733d88d9eddde Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Tue, 2 Dec 2025 10:24:48 +0800 Subject: [PATCH 7/9] refactor: rename local variable --- .../api_fastapi/core_api/routes/public/hitl.py | 16 ++++++++-------- .../core_api/routes/public/task_instances.py | 10 +++++----- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py index 63fb4995cc323..ddb9770418f84 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py @@ -98,15 +98,15 @@ def _query(orm_object: Base) -> TI | TIH | None: if try_number is not None: query = query.where(orm_object.try_number == try_number) - task_instance = session.scalar(query) - return task_instance + ti_or_tih = session.scalar(query) + return ti_or_tih if try_number is None: - task_instance = _query(TI) + ti_or_tih = _query(TI) else: - task_instance = _query(TIH) or _query(TI) + ti_or_tih = _query(TIH) or _query(TI) - if task_instance is None: + if ti_or_tih is None: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=( @@ -115,13 +115,13 @@ def _query(orm_object: Base) -> TI | TIH | None: ), ) - if not task_instance.hitl_detail: + if not ti_or_tih.hitl_detail: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, - detail=f"Human-in-the-loop detail does not exist for Task Instance with id {task_instance.id}", + detail=f"Human-in-the-loop detail does not exist for Task Instance with id {ti_or_tih.id}", ) - return task_instance + return ti_or_tih @task_instances_hitl_router.patch( diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/task_instances.py b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/task_instances.py index 29c5f10a3ff26..9e9350bb75bc6 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/task_instances.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/task_instances.py @@ -677,16 +677,16 @@ def _query(orm_object: Base) -> TI | TIH | None: orm_object.map_index == map_index, ) - task_instance = session.scalar(query) - return task_instance + ti_or_tih = session.scalar(query) + return ti_or_tih - result = _query(TI) or _query(TIH) - if result is None: + ti_or_tih = _query(TI) or _query(TIH) + if ti_or_tih is None: raise HTTPException( status.HTTP_404_NOT_FOUND, f"The Task Instance with dag_id: `{dag_id}`, run_id: `{dag_run_id}`, task_id: `{task_id}`, try_number: `{task_try_number}` and map_index: `{map_index}` was not found", ) - return result + return ti_or_tih @task_instances_router.get( From 47b7cc2e59c47282182764c9d0f534bccc561c20 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Tue, 2 Dec 2025 17:46:55 +0800 Subject: [PATCH 8/9] refactor(hitl): remove -1 from try Number --- .../openapi/v2-rest-api-generated.yaml | 4 +- .../core_api/routes/public/hitl.py | 4 +- .../ui/openapi-gen/requests/services.gen.ts | 262 +++++++++--------- .../ui/openapi-gen/requests/types.gen.ts | 2 +- .../src/pages/TaskInstance/HITLResponse.tsx | 4 +- 5 files changed, 139 insertions(+), 137 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 74e9f28818980..72a7d04a73020 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 @@ -8481,7 +8481,9 @@ paths: in: path required: true schema: - type: integer + anyOf: + - type: integer + - type: 'null' title: Try Number responses: '200': diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py index ddb9770418f84..55222782806fb 100644 --- a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py +++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/hitl.py @@ -228,7 +228,7 @@ def get_hitl_detail_try_detail( task_id: str, session: SessionDep, map_index: int = -1, - try_number: int = -1, + try_number: int | None = None, ) -> HITLDetailHistory: """Get a Human-in-the-loop detail of a specific task instance.""" task_instance_history = _get_task_instance_with_hitl_detail( @@ -237,7 +237,7 @@ def get_hitl_detail_try_detail( task_id=task_id, session=session, map_index=map_index, - try_number=try_number if try_number != -1 else None, + try_number=try_number, ) return task_instance_history.hitl_detail 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 8d968add609e8..65f80ab9c2343 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 @@ -3,7 +3,7 @@ import type { CancelablePromise } from './core/CancelablePromise'; import { OpenAPI } from './core/OpenAPI'; import { request as __request } from './core/request'; -import type { GetAssetsData, GetAssetsResponse, GetAssetAliasesData, GetAssetAliasesResponse, GetAssetAliasData, GetAssetAliasResponse, GetAssetEventsData, GetAssetEventsResponse, CreateAssetEventData, CreateAssetEventResponse, MaterializeAssetData, MaterializeAssetResponse, GetAssetQueuedEventsData, GetAssetQueuedEventsResponse, DeleteAssetQueuedEventsData, DeleteAssetQueuedEventsResponse, GetAssetData, GetAssetResponse, GetDagAssetQueuedEventsData, GetDagAssetQueuedEventsResponse, DeleteDagAssetQueuedEventsData, DeleteDagAssetQueuedEventsResponse, GetDagAssetQueuedEventData, GetDagAssetQueuedEventResponse, DeleteDagAssetQueuedEventData, DeleteDagAssetQueuedEventResponse, NextRunAssetsData, NextRunAssetsResponse, ListBackfillsData, ListBackfillsResponse, CreateBackfillData, CreateBackfillResponse, GetBackfillData, GetBackfillResponse, PauseBackfillData, PauseBackfillResponse, UnpauseBackfillData, UnpauseBackfillResponse, CancelBackfillData, CancelBackfillResponse, CreateBackfillDryRunData, CreateBackfillDryRunResponse, ListBackfillsUiData, ListBackfillsUiResponse, DeleteConnectionData, DeleteConnectionResponse, GetConnectionData, GetConnectionResponse, PatchConnectionData, PatchConnectionResponse, GetConnectionsData, GetConnectionsResponse, PostConnectionData, PostConnectionResponse, BulkConnectionsData, BulkConnectionsResponse, TestConnectionData, TestConnectionResponse, CreateDefaultConnectionsResponse, HookMetaDataResponse, GetDagRunData, GetDagRunResponse, DeleteDagRunData, DeleteDagRunResponse, PatchDagRunData, PatchDagRunResponse, GetUpstreamAssetEventsData, GetUpstreamAssetEventsResponse, ClearDagRunData, ClearDagRunResponse, GetDagRunsData, GetDagRunsResponse, TriggerDagRunData, TriggerDagRunResponse, WaitDagRunUntilFinishedData, WaitDagRunUntilFinishedResponse, GetListDagRunsBatchData, GetListDagRunsBatchResponse, GetDagSourceData, GetDagSourceResponse, GetDagStatsData, GetDagStatsResponse, GetConfigData, GetConfigResponse, GetConfigValueData, GetConfigValueResponse, GetConfigsResponse, ListDagWarningsData, ListDagWarningsResponse, GetDagsData, GetDagsResponse, PatchDagsData, PatchDagsResponse, GetDagData, GetDagResponse, PatchDagData, PatchDagResponse, DeleteDagData, DeleteDagResponse, GetDagDetailsData, GetDagDetailsResponse, FavoriteDagData, FavoriteDagResponse, UnfavoriteDagData, UnfavoriteDagResponse, GetDagTagsData, GetDagTagsResponse, GetDagsUiData, GetDagsUiResponse, GetLatestRunInfoData, GetLatestRunInfoResponse, GetEventLogData, GetEventLogResponse, GetEventLogsData, GetEventLogsResponse, GetExtraLinksData, GetExtraLinksResponse, GetTaskInstanceData, GetTaskInstanceResponse, PatchTaskInstanceData, PatchTaskInstanceResponse, DeleteTaskInstanceData, DeleteTaskInstanceResponse, GetMappedTaskInstancesData, GetMappedTaskInstancesResponse, GetTaskInstanceDependenciesByMapIndexData, GetTaskInstanceDependenciesByMapIndexResponse, GetTaskInstanceDependenciesData, GetTaskInstanceDependenciesResponse, GetTaskInstanceTriesData, GetTaskInstanceTriesResponse, GetMappedTaskInstanceTriesData, GetMappedTaskInstanceTriesResponse, GetMappedTaskInstanceData, GetMappedTaskInstanceResponse, PatchTaskInstanceByMapIndexData, PatchTaskInstanceByMapIndexResponse, GetTaskInstancesData, GetTaskInstancesResponse, BulkTaskInstancesData, BulkTaskInstancesResponse, GetTaskInstancesBatchData, GetTaskInstancesBatchResponse, GetTaskInstanceTryDetailsData, GetTaskInstanceTryDetailsResponse, GetMappedTaskInstanceTryDetailsData, GetMappedTaskInstanceTryDetailsResponse, PostClearTaskInstancesData, PostClearTaskInstancesResponse, PatchTaskInstanceDryRunByMapIndexData, PatchTaskInstanceDryRunByMapIndexResponse, PatchTaskInstanceDryRunData, PatchTaskInstanceDryRunResponse, GetLogData, GetLogResponse, GetExternalLogUrlData, GetExternalLogUrlResponse, UpdateHitlDetailData, UpdateHitlDetailResponse, GetHitlDetailData, GetHitlDetailResponse, GetHitlDetailTryDetailData, GetHitlDetailTryDetailResponse, GetHitlDetailsData, GetHitlDetailsResponse, GetImportErrorData, GetImportErrorResponse, GetImportErrorsData, GetImportErrorsResponse, GetJobsData, GetJobsResponse, GetPluginsData, GetPluginsResponse, ImportErrorsResponse, DeletePoolData, DeletePoolResponse, GetPoolData, GetPoolResponse, PatchPoolData, PatchPoolResponse, GetPoolsData, GetPoolsResponse, PostPoolData, PostPoolResponse, BulkPoolsData, BulkPoolsResponse, GetProvidersData, GetProvidersResponse, GetXcomEntryData, GetXcomEntryResponse, UpdateXcomEntryData, UpdateXcomEntryResponse, GetXcomEntriesData, GetXcomEntriesResponse, CreateXcomEntryData, CreateXcomEntryResponse, GetTasksData, GetTasksResponse, GetTaskData, GetTaskResponse, DeleteVariableData, DeleteVariableResponse, GetVariableData, GetVariableResponse, PatchVariableData, PatchVariableResponse, GetVariablesData, GetVariablesResponse, PostVariableData, PostVariableResponse, BulkVariablesData, BulkVariablesResponse, ReparseDagFileData, ReparseDagFileResponse, GetDagVersionData, GetDagVersionResponse, GetDagVersionsData, GetDagVersionsResponse, GetHealthResponse, GetVersionResponse, LoginData, LoginResponse, LogoutResponse, GetAuthMenusResponse, GetCurrentUserInfoResponse, GetDependenciesData, GetDependenciesResponse, HistoricalMetricsData, HistoricalMetricsResponse, DagStatsResponse2, StructureDataData, StructureDataResponse2, GetDagStructureData, GetDagStructureResponse, GetGridRunsData, GetGridRunsResponse, GetGridTiSummariesData, GetGridTiSummariesResponse, GetCalendarData, GetCalendarResponse, ListTeamsData, ListTeamsResponse } from './types.gen'; +import type { GetAssetsData, GetAssetsResponse, GetAssetAliasesData, GetAssetAliasesResponse, GetAssetAliasData, GetAssetAliasResponse, GetAssetEventsData, GetAssetEventsResponse, CreateAssetEventData, CreateAssetEventResponse, MaterializeAssetData, MaterializeAssetResponse, GetAssetQueuedEventsData, GetAssetQueuedEventsResponse, DeleteAssetQueuedEventsData, DeleteAssetQueuedEventsResponse, GetAssetData, GetAssetResponse, GetDagAssetQueuedEventsData, GetDagAssetQueuedEventsResponse, DeleteDagAssetQueuedEventsData, DeleteDagAssetQueuedEventsResponse, GetDagAssetQueuedEventData, GetDagAssetQueuedEventResponse, DeleteDagAssetQueuedEventData, DeleteDagAssetQueuedEventResponse, NextRunAssetsData, NextRunAssetsResponse, ListBackfillsData, ListBackfillsResponse, CreateBackfillData, CreateBackfillResponse, GetBackfillData, GetBackfillResponse, PauseBackfillData, PauseBackfillResponse, UnpauseBackfillData, UnpauseBackfillResponse, CancelBackfillData, CancelBackfillResponse, CreateBackfillDryRunData, CreateBackfillDryRunResponse, ListBackfillsUiData, ListBackfillsUiResponse, DeleteConnectionData, DeleteConnectionResponse, GetConnectionData, GetConnectionResponse, PatchConnectionData, PatchConnectionResponse, GetConnectionsData, GetConnectionsResponse, PostConnectionData, PostConnectionResponse, BulkConnectionsData, BulkConnectionsResponse, TestConnectionData, TestConnectionResponse, CreateDefaultConnectionsResponse, HookMetaDataResponse, GetDagRunData, GetDagRunResponse, DeleteDagRunData, DeleteDagRunResponse, PatchDagRunData, PatchDagRunResponse, GetUpstreamAssetEventsData, GetUpstreamAssetEventsResponse, ClearDagRunData, ClearDagRunResponse, GetDagRunsData, GetDagRunsResponse, TriggerDagRunData, TriggerDagRunResponse, WaitDagRunUntilFinishedData, WaitDagRunUntilFinishedResponse, GetListDagRunsBatchData, GetListDagRunsBatchResponse, GetDagSourceData, GetDagSourceResponse, GetDagStatsData, GetDagStatsResponse, GetConfigData, GetConfigResponse, GetConfigValueData, GetConfigValueResponse, GetConfigsResponse, ListDagWarningsData, ListDagWarningsResponse, GetDagsData, GetDagsResponse, PatchDagsData, PatchDagsResponse, GetDagData, GetDagResponse, PatchDagData, PatchDagResponse, DeleteDagData, DeleteDagResponse, GetDagDetailsData, GetDagDetailsResponse, FavoriteDagData, FavoriteDagResponse, UnfavoriteDagData, UnfavoriteDagResponse, GetDagTagsData, GetDagTagsResponse, GetDagsUiData, GetDagsUiResponse, GetLatestRunInfoData, GetLatestRunInfoResponse, GetEventLogData, GetEventLogResponse, GetEventLogsData, GetEventLogsResponse, GetExtraLinksData, GetExtraLinksResponse, GetTaskInstanceData, GetTaskInstanceResponse, PatchTaskInstanceData, PatchTaskInstanceResponse, DeleteTaskInstanceData, DeleteTaskInstanceResponse, GetMappedTaskInstancesData, GetMappedTaskInstancesResponse, GetTaskInstanceDependenciesByMapIndexData, GetTaskInstanceDependenciesByMapIndexResponse, GetTaskInstanceDependenciesData, GetTaskInstanceDependenciesResponse, GetTaskInstanceTriesData, GetTaskInstanceTriesResponse, GetMappedTaskInstanceTriesData, GetMappedTaskInstanceTriesResponse, GetMappedTaskInstanceData, GetMappedTaskInstanceResponse, PatchTaskInstanceByMapIndexData, PatchTaskInstanceByMapIndexResponse, GetTaskInstancesData, GetTaskInstancesResponse, BulkTaskInstancesData, BulkTaskInstancesResponse, GetTaskInstancesBatchData, GetTaskInstancesBatchResponse, GetTaskInstanceTryDetailsData, GetTaskInstanceTryDetailsResponse, GetMappedTaskInstanceTryDetailsData, GetMappedTaskInstanceTryDetailsResponse, PostClearTaskInstancesData, PostClearTaskInstancesResponse, PatchTaskInstanceDryRunByMapIndexData, PatchTaskInstanceDryRunByMapIndexResponse, PatchTaskInstanceDryRunData, PatchTaskInstanceDryRunResponse, GetLogData, GetLogResponse, GetExternalLogUrlData, GetExternalLogUrlResponse, UpdateHitlDetailData, UpdateHitlDetailResponse, GetHitlDetailData, GetHitlDetailResponse, GetHitlDetailTryDetailData, GetHitlDetailTryDetailResponse, GetHitlDetailsData, GetHitlDetailsResponse, GetImportErrorData, GetImportErrorResponse, GetImportErrorsData, GetImportErrorsResponse, GetJobsData, GetJobsResponse, GetPluginsData, GetPluginsResponse, ImportErrorsResponse, DeletePoolData, DeletePoolResponse, GetPoolData, GetPoolResponse, PatchPoolData, PatchPoolResponse, GetPoolsData, GetPoolsResponse, PostPoolData, PostPoolResponse, BulkPoolsData, BulkPoolsResponse, GetProvidersData, GetProvidersResponse, GetXcomEntryData, GetXcomEntryResponse, UpdateXcomEntryData, UpdateXcomEntryResponse, DeleteXcomEntryData, DeleteXcomEntryResponse, GetXcomEntriesData, GetXcomEntriesResponse, CreateXcomEntryData, CreateXcomEntryResponse, GetTasksData, GetTasksResponse, GetTaskData, GetTaskResponse, DeleteVariableData, DeleteVariableResponse, GetVariableData, GetVariableResponse, PatchVariableData, PatchVariableResponse, GetVariablesData, GetVariablesResponse, PostVariableData, PostVariableResponse, BulkVariablesData, BulkVariablesResponse, ReparseDagFileData, ReparseDagFileResponse, GetDagVersionData, GetDagVersionResponse, GetDagVersionsData, GetDagVersionsResponse, GetHealthResponse, GetVersionResponse, LoginData, LoginResponse, LogoutResponse, GetAuthMenusResponse, GetCurrentUserInfoResponse, GetDependenciesData, GetDependenciesResponse, HistoricalMetricsData, HistoricalMetricsResponse, DagStatsResponse2, StructureDataData, StructureDataResponse2, GetDagStructureData, GetDagStructureResponse, GetGridRunsData, GetGridRunsResponse, GetGridTiSummariesData, GetGridTiSummariesResponse, GetCalendarData, GetCalendarResponse, ListTeamsData, ListTeamsResponse } from './types.gen'; export class AssetService { /** @@ -41,7 +41,7 @@ export class AssetService { } }); } - + /** * Get Asset Aliases * Get asset aliases. @@ -71,7 +71,7 @@ export class AssetService { } }); } - + /** * Get Asset Alias * Get an asset alias. @@ -95,7 +95,7 @@ export class AssetService { } }); } - + /** * Get Asset Events * Get asset events. @@ -143,7 +143,7 @@ export class AssetService { } }); } - + /** * Create Asset Event * Create asset events. @@ -166,7 +166,7 @@ export class AssetService { } }); } - + /** * Materialize Asset * Materialize an asset by triggering a DAG run that produces it. @@ -191,7 +191,7 @@ export class AssetService { } }); } - + /** * Get Asset Queued Events * Get queued asset events for an asset. @@ -219,7 +219,7 @@ export class AssetService { } }); } - + /** * Delete Asset Queued Events * Delete queued asset events for an asset. @@ -247,7 +247,7 @@ export class AssetService { } }); } - + /** * Get Asset * Get an asset. @@ -271,7 +271,7 @@ export class AssetService { } }); } - + /** * Get Dag Asset Queued Events * Get queued asset events for a DAG. @@ -299,7 +299,7 @@ export class AssetService { } }); } - + /** * Delete Dag Asset Queued Events * @param data The data for the request. @@ -327,7 +327,7 @@ export class AssetService { } }); } - + /** * Get Dag Asset Queued Event * Get a queued asset event for a DAG. @@ -357,7 +357,7 @@ export class AssetService { } }); } - + /** * Delete Dag Asset Queued Event * Delete a queued asset event for a DAG. @@ -388,7 +388,7 @@ export class AssetService { } }); } - + /** * Next Run Assets * @param data The data for the request. @@ -408,7 +408,7 @@ export class AssetService { } }); } - + } export class BackfillService { @@ -439,7 +439,7 @@ export class BackfillService { } }); } - + /** * Create Backfill * @param data The data for the request. @@ -462,7 +462,7 @@ export class BackfillService { } }); } - + /** * Get Backfill * @param data The data for the request. @@ -485,7 +485,7 @@ export class BackfillService { } }); } - + /** * Pause Backfill * @param data The data for the request. @@ -509,7 +509,7 @@ export class BackfillService { } }); } - + /** * Unpause Backfill * @param data The data for the request. @@ -533,7 +533,7 @@ export class BackfillService { } }); } - + /** * Cancel Backfill * @param data The data for the request. @@ -557,7 +557,7 @@ export class BackfillService { } }); } - + /** * Create Backfill Dry Run * @param data The data for the request. @@ -580,7 +580,7 @@ export class BackfillService { } }); } - + /** * List Backfills Ui * @param data The data for the request. @@ -609,7 +609,7 @@ export class BackfillService { } }); } - + } export class ConnectionService { @@ -636,7 +636,7 @@ export class ConnectionService { } }); } - + /** * Get Connection * Get a connection entry. @@ -660,7 +660,7 @@ export class ConnectionService { } }); } - + /** * Patch Connection * Update a connection entry. @@ -692,7 +692,7 @@ export class ConnectionService { } }); } - + /** * Get Connections * Get all connection entries. @@ -722,7 +722,7 @@ export class ConnectionService { } }); } - + /** * Post Connection * Create connection entry. @@ -745,7 +745,7 @@ export class ConnectionService { } }); } - + /** * Bulk Connections * Bulk create, update, and delete connections. @@ -767,7 +767,7 @@ export class ConnectionService { } }); } - + /** * Test Connection * Test an API connection. @@ -793,7 +793,7 @@ export class ConnectionService { } }); } - + /** * Create Default Connections * Create default connections. @@ -810,7 +810,7 @@ export class ConnectionService { } }); } - + /** * Hook Meta Data * Retrieve information about available connection types (hook classes) and their parameters. @@ -823,7 +823,7 @@ export class ConnectionService { url: '/ui/connections/hook_meta' }); } - + } export class DagRunService { @@ -851,7 +851,7 @@ export class DagRunService { } }); } - + /** * Delete Dag Run * Delete a DAG Run entry. @@ -878,7 +878,7 @@ export class DagRunService { } }); } - + /** * Patch Dag Run * Modify a DAG Run. @@ -912,7 +912,7 @@ export class DagRunService { } }); } - + /** * Get Upstream Asset Events * If dag run is asset-triggered, return the asset events that triggered it. @@ -938,7 +938,7 @@ export class DagRunService { } }); } - + /** * Clear Dag Run * @param data The data for the request. @@ -966,7 +966,7 @@ export class DagRunService { } }); } - + /** * Get Dag Runs * Get all DAG Runs. @@ -1062,7 +1062,7 @@ export class DagRunService { } }); } - + /** * Trigger Dag Run * Trigger a DAG. @@ -1091,7 +1091,7 @@ export class DagRunService { } }); } - + /** * Experimental: Wait for a dag run to complete, and return task results if requested. * 🚧 This is an experimental endpoint and may change or be removed without notice.Successful response are streamed as newline-delimited JSON (NDJSON). Each line is a JSON object representing the DAG run state. @@ -1123,7 +1123,7 @@ export class DagRunService { } }); } - + /** * Get List Dag Runs Batch * Get a list of DAG Runs. @@ -1150,7 +1150,7 @@ export class DagRunService { } }); } - + } export class ExperimentalService { @@ -1185,7 +1185,7 @@ export class ExperimentalService { } }); } - + } export class DagSourceService { @@ -1222,7 +1222,7 @@ export class DagSourceService { } }); } - + } export class DagStatsService { @@ -1250,7 +1250,7 @@ export class DagStatsService { } }); } - + } export class ConfigService { @@ -1281,7 +1281,7 @@ export class ConfigService { } }); } - + /** * Get Config Value * @param data The data for the request. @@ -1311,7 +1311,7 @@ export class ConfigService { } }); } - + /** * Get Configs * Get configs for UI. @@ -1327,7 +1327,7 @@ export class ConfigService { } }); } - + } export class DagWarningService { @@ -1361,7 +1361,7 @@ export class DagWarningService { } }); } - + } export class DagService { @@ -1439,7 +1439,7 @@ export class DagService { } }); } - + /** * Patch Dags * Patch multiple DAGs. @@ -1483,7 +1483,7 @@ export class DagService { } }); } - + /** * Get Dag * Get basic information about a DAG. @@ -1508,7 +1508,7 @@ export class DagService { } }); } - + /** * Patch Dag * Patch the specific DAG. @@ -1540,7 +1540,7 @@ export class DagService { } }); } - + /** * Delete Dag * Delete the specific DAG. @@ -1565,7 +1565,7 @@ export class DagService { } }); } - + /** * Get Dag Details * Get details of DAG. @@ -1590,7 +1590,7 @@ export class DagService { } }); } - + /** * Favorite Dag * Mark the DAG as favorite. @@ -1614,7 +1614,7 @@ export class DagService { } }); } - + /** * Unfavorite Dag * Unmark the DAG as favorite. @@ -1639,7 +1639,7 @@ export class DagService { } }); } - + /** * Get Dag Tags * Get all DAG tags. @@ -1668,7 +1668,7 @@ export class DagService { } }); } - + /** * Get Dags * Get DAGs with recent DagRun. @@ -1727,7 +1727,7 @@ export class DagService { } }); } - + /** * Get Latest Run Info * Get latest run. @@ -1749,7 +1749,7 @@ export class DagService { } }); } - + } export class EventLogService { @@ -1775,7 +1775,7 @@ export class EventLogService { } }); } - + /** * Get Event Logs * Get all Event Logs. @@ -1834,7 +1834,7 @@ export class EventLogService { } }); } - + } export class ExtraLinksService { @@ -1869,7 +1869,7 @@ export class ExtraLinksService { } }); } - + } export class TaskInstanceService { @@ -1904,7 +1904,7 @@ export class TaskInstanceService { } }); } - + /** * Get Task Instance * Get task instance. @@ -1932,7 +1932,7 @@ export class TaskInstanceService { } }); } - + /** * Patch Task Instance * Update a task instance. @@ -1971,7 +1971,7 @@ export class TaskInstanceService { } }); } - + /** * Delete Task Instance * Delete a task instance. @@ -2003,7 +2003,7 @@ export class TaskInstanceService { } }); } - + /** * Get Mapped Task Instances * Get list of mapped task instances. @@ -2109,7 +2109,7 @@ export class TaskInstanceService { } }); } - + /** * Get Task Instance Dependencies * Get dependencies blocking task from getting scheduled. @@ -2139,7 +2139,7 @@ export class TaskInstanceService { } }); } - + /** * Get Task Instance Dependencies * Get dependencies blocking task from getting scheduled. @@ -2171,7 +2171,7 @@ export class TaskInstanceService { } }); } - + /** * Get Task Instance Tries * Get list of task instances history. @@ -2203,7 +2203,7 @@ export class TaskInstanceService { } }); } - + /** * Get Mapped Task Instance Tries * @param data The data for the request. @@ -2232,7 +2232,7 @@ export class TaskInstanceService { } }); } - + /** * Get Mapped Task Instance * Get task instance. @@ -2262,7 +2262,7 @@ export class TaskInstanceService { } }); } - + /** * Patch Task Instance * Update a task instance. @@ -2301,7 +2301,7 @@ export class TaskInstanceService { } }); } - + /** * Get Task Instances * Get list of task instances. @@ -2418,7 +2418,7 @@ export class TaskInstanceService { } }); } - + /** * Bulk Task Instances * Bulk update, and delete task instances. @@ -2446,7 +2446,7 @@ export class TaskInstanceService { } }); } - + /** * Get Task Instances Batch * Get list of task instances. @@ -2475,7 +2475,7 @@ export class TaskInstanceService { } }); } - + /** * Get Task Instance Try Details * Get task instance details by try number. @@ -2509,7 +2509,7 @@ export class TaskInstanceService { } }); } - + /** * Get Mapped Task Instance Try Details * @param data The data for the request. @@ -2540,7 +2540,7 @@ export class TaskInstanceService { } }); } - + /** * Post Clear Task Instances * Clear task instances. @@ -2568,7 +2568,7 @@ export class TaskInstanceService { } }); } - + /** * Patch Task Instance Dry Run * Update a task instance dry_run mode. @@ -2606,7 +2606,7 @@ export class TaskInstanceService { } }); } - + /** * Patch Task Instance Dry Run * Update a task instance dry_run mode. @@ -2644,7 +2644,7 @@ export class TaskInstanceService { } }); } - + /** * Get Log * Get logs for a specific task instance. @@ -2686,7 +2686,7 @@ export class TaskInstanceService { } }); } - + /** * Get External Log Url * Get external log URL for a specific task instance. @@ -2721,7 +2721,7 @@ export class TaskInstanceService { } }); } - + /** * Update Hitl Detail * Update a Human-in-the-loop detail. @@ -2755,7 +2755,7 @@ export class TaskInstanceService { } }); } - + /** * Get Hitl Detail * Get a Human-in-the-loop detail of a specific task instance. @@ -2785,7 +2785,7 @@ export class TaskInstanceService { } }); } - + /** * Get Hitl Detail Try Detail * Get a Human-in-the-loop detail of a specific task instance. @@ -2817,7 +2817,7 @@ export class TaskInstanceService { } }); } - + /** * Get Hitl Details * Get Human-in-the-loop details. @@ -2878,7 +2878,7 @@ export class TaskInstanceService { } }); } - + } export class ImportErrorService { @@ -2905,7 +2905,7 @@ export class ImportErrorService { } }); } - + /** * Get Import Errors * Get all import errors. @@ -2934,7 +2934,7 @@ export class ImportErrorService { } }); } - + } export class JobService { @@ -2991,7 +2991,7 @@ export class JobService { } }); } - + } export class PluginService { @@ -3018,7 +3018,7 @@ export class PluginService { } }); } - + /** * Import Errors * @returns PluginImportErrorCollectionResponse Successful Response @@ -3034,7 +3034,7 @@ export class PluginService { } }); } - + } export class PoolService { @@ -3062,7 +3062,7 @@ export class PoolService { } }); } - + /** * Get Pool * Get a pool. @@ -3086,7 +3086,7 @@ export class PoolService { } }); } - + /** * Patch Pool * Update a Pool. @@ -3118,7 +3118,7 @@ export class PoolService { } }); } - + /** * Get Pools * Get all pools entries. @@ -3148,7 +3148,7 @@ export class PoolService { } }); } - + /** * Post Pool * Create a Pool. @@ -3171,7 +3171,7 @@ export class PoolService { } }); } - + /** * Bulk Pools * Bulk create, update, and delete pools. @@ -3193,7 +3193,7 @@ export class PoolService { } }); } - + } export class ProviderService { @@ -3221,7 +3221,7 @@ export class ProviderService { } }); } - + } export class XcomService { @@ -3263,7 +3263,7 @@ export class XcomService { } }); } - + /** * Update Xcom Entry * Update an existing XCom entry. @@ -3297,7 +3297,7 @@ export class XcomService { } }); } - + /** * Delete Xcom Entry * Delete an XCom entry. @@ -3332,7 +3332,7 @@ export class XcomService { } }); } - + /** * Get Xcom Entries * Get all XCom entries. @@ -3399,7 +3399,7 @@ export class XcomService { } }); } - + /** * Create Xcom Entry * Create an XCom entry. @@ -3431,7 +3431,7 @@ export class XcomService { } }); } - + } export class TaskService { @@ -3463,7 +3463,7 @@ export class TaskService { } }); } - + /** * Get Task * Get simplified representation of a task. @@ -3490,7 +3490,7 @@ export class TaskService { } }); } - + } export class VariableService { @@ -3517,7 +3517,7 @@ export class VariableService { } }); } - + /** * Get Variable * Get a variable entry. @@ -3541,7 +3541,7 @@ export class VariableService { } }); } - + /** * Patch Variable * Update a variable by key. @@ -3573,7 +3573,7 @@ export class VariableService { } }); } - + /** * Get Variables * Get all Variables entries. @@ -3602,7 +3602,7 @@ export class VariableService { } }); } - + /** * Post Variable * Create a variable. @@ -3625,7 +3625,7 @@ export class VariableService { } }); } - + /** * Bulk Variables * Bulk create, update, and delete variables. @@ -3647,7 +3647,7 @@ export class VariableService { } }); } - + } export class DagParsingService { @@ -3674,7 +3674,7 @@ export class DagParsingService { } }); } - + } export class DagVersionService { @@ -3703,7 +3703,7 @@ export class DagVersionService { } }); } - + /** * Get Dag Versions * Get all DAG Versions. @@ -3743,7 +3743,7 @@ export class DagVersionService { } }); } - + } export class MonitorService { @@ -3758,7 +3758,7 @@ export class MonitorService { url: '/api/v2/monitor/health' }); } - + } export class VersionService { @@ -3774,7 +3774,7 @@ export class VersionService { url: '/api/v2/version' }); } - + } export class LoginService { @@ -3799,7 +3799,7 @@ export class LoginService { } }); } - + /** * Logout * Logout the user. @@ -3815,7 +3815,7 @@ export class LoginService { } }); } - + } export class AuthLinksService { @@ -3830,7 +3830,7 @@ export class AuthLinksService { url: '/ui/auth/menus' }); } - + /** * Get Current User Info * Convienently get the current authenticated user information. @@ -3843,7 +3843,7 @@ export class AuthLinksService { url: '/ui/auth/me' }); } - + } export class DependenciesService { @@ -3868,7 +3868,7 @@ export class DependenciesService { } }); } - + } export class DashboardService { @@ -3895,7 +3895,7 @@ export class DashboardService { } }); } - + /** * Dag Stats * Return basic DAG stats with counts of DAGs in various states. @@ -3908,7 +3908,7 @@ export class DashboardService { url: '/ui/dashboard/dag_stats' }); } - + } export class StructureService { @@ -3945,7 +3945,7 @@ export class StructureService { } }); } - + } export class GridService { @@ -4001,7 +4001,7 @@ export class GridService { } }); } - + /** * Get Grid Runs * Get info about a run for the grid. @@ -4046,7 +4046,7 @@ export class GridService { } }); } - + /** * Get Grid Ti Summaries * Get states for TIs / "groups" of TIs. @@ -4082,7 +4082,7 @@ export class GridService { } }); } - + } export class CalendarService { @@ -4118,7 +4118,7 @@ export class CalendarService { } }); } - + } export class TeamsService { @@ -4145,5 +4145,5 @@ export class TeamsService { } }); } - -} + +} \ No newline at end of file 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 058a06912ecb3..db493ee808400 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 @@ -3059,7 +3059,7 @@ export type GetHitlDetailTryDetailData = { dagRunId: string; mapIndex: number; taskId: string; - tryNumber: number; + tryNumber: number | null; }; export type GetHitlDetailTryDetailResponse = HITLDetailHistory; diff --git a/airflow-core/src/airflow/ui/src/pages/TaskInstance/HITLResponse.tsx b/airflow-core/src/airflow/ui/src/pages/TaskInstance/HITLResponse.tsx index c00de9a96a3a5..65bd1042a4fa3 100644 --- a/airflow-core/src/airflow/ui/src/pages/TaskInstance/HITLResponse.tsx +++ b/airflow-core/src/airflow/ui/src/pages/TaskInstance/HITLResponse.tsx @@ -63,7 +63,7 @@ export const HITLResponse = () => { setSearchParams(searchParams); }; - const tryNumber = tryNumberParam === null ? -1 : parseInt(tryNumberParam, 10); + const tryNumber = tryNumberParam === null ? taskInstance?.try_number : parseInt(tryNumberParam, 10); const { data: hitlDetail } = useTaskInstanceServiceGetHitlDetailTryDetail( { @@ -71,7 +71,7 @@ export const HITLResponse = () => { dagRunId: runId ?? "", mapIndex: parsedMapIndex, taskId: taskId ?? "", - tryNumber, + tryNumber: tryNumber ?? 1, }, undefined, ); From c558d5ae226d33ff64d8e8f7dce3efdfdccffea7 Mon Sep 17 00:00:00 2001 From: Wei Lee Date: Tue, 6 Jan 2026 18:01:01 +0800 Subject: [PATCH 9/9] fix(HITL): fix missing diabled in the UI --- .../airflow/ui/src/pages/HITLTaskInstances/HITLResponseForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLResponseForm.tsx b/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLResponseForm.tsx index b29eb8e404504..87f120d7e7fb2 100644 --- a/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLResponseForm.tsx +++ b/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLResponseForm.tsx @@ -148,7 +148,7 @@ export const HITLResponseForm = ({ hitlDetail }: HITLResponseFormProps) => { ) : hitlDetail.response_received ? undefined : (