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 351088a3f2f80..f8e48b9391791 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 @@ -7406,137 +7406,6 @@ paths: - 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 - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateHITLDetailPayload' - responses: - '200': - description: Successful Response - content: - application/json: - schema: - $ref: '#/components/schemas/HITLDetailResponse' - '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 - '409': - content: - application/json: - schema: - $ref: '#/components/schemas/HTTPExceptionResponse' - description: Conflict - '422': - description: Validation Error - content: - application/json: - schema: - $ref: '#/components/schemas/HTTPValidationError' - get: - tags: - - HumanInTheLoop - summary: Get Hitl Detail - description: Get a Human-in-the-loop detail of a specific task instance. - operationId: get_hitl_detail - 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 - responses: - '200': - description: Successful Response - content: - application/json: - schema: - $ref: '#/components/schemas/HITLDetail' - '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/hitlDetails/{dag_id}/{dag_run_id}/{task_id}/{map_index}: - patch: - tags: - - HumanInTheLoop - summary: Update Mapped Ti Hitl Detail - description: Update a Human-in-the-loop detail. - operationId: update_mapped_ti_hitl_detail - security: - - OAuth2PasswordBearer: [] - - HTTPBearer: [] - parameters: - name: dag_id in: path required: true @@ -7556,10 +7425,11 @@ paths: type: string title: Task Id - name: map_index - in: path - required: true + in: query + required: false schema: type: integer + default: -1 title: Map Index requestBody: required: true @@ -7607,9 +7477,9 @@ paths: get: tags: - HumanInTheLoop - summary: Get Mapped Ti Hitl Detail + summary: Get Hitl Detail description: Get a Human-in-the-loop detail of a specific task instance. - operationId: get_mapped_ti_hitl_detail + operationId: get_hitl_detail security: - OAuth2PasswordBearer: [] - HTTPBearer: [] @@ -7633,10 +7503,11 @@ paths: type: string title: Task Id - name: map_index - in: path - required: true + in: query + required: false schema: type: integer + default: -1 title: Map Index 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 da165fe1ae75a..e901ad6123440 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 @@ -63,7 +63,7 @@ def _get_task_instance( dag_run_id: str, task_id: str, session: SessionDep, - map_index: int | None = None, + map_index: int, ) -> TI: query = select(TI).where( TI.dag_id == dag_id, @@ -83,11 +83,6 @@ def _get_task_instance( f"task_id: `{task_id}` and map_index: `{map_index}` was not found" ), ) - if map_index is None and task_instance.map_index != -1: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail="Task instance is mapped, add the map_index value to the URL", - ) return task_instance @@ -99,7 +94,7 @@ def _update_hitl_detail( update_hitl_detail_payload: UpdateHITLDetailPayload, user: GetUserDep, session: SessionDep, - map_index: int | None = None, + map_index: int, ) -> HITLDetailResponse: task_instance = _get_task_instance( dag_id=dag_id, @@ -151,7 +146,7 @@ def _get_hitl_detail( dag_run_id: str, task_id: str, session: SessionDep, - map_index: int | None = None, + map_index: int, ) -> HITLDetail: """Get a Human-in-the-loop detail of a specific task instance.""" task_instance = _get_task_instance( @@ -195,38 +190,7 @@ def update_hitl_detail( update_hitl_detail_payload: UpdateHITLDetailPayload, user: GetUserDep, session: SessionDep, -) -> HITLDetailResponse: - """Update a Human-in-the-loop detail.""" - return _update_hitl_detail( - dag_id=dag_id, - dag_run_id=dag_run_id, - task_id=task_id, - session=session, - update_hitl_detail_payload=update_hitl_detail_payload, - user=user, - map_index=None, - ) - - -@hitl_router.patch( - "/{dag_id}/{dag_run_id}/{task_id}/{map_index}", - responses=create_openapi_http_exception_doc( - [ - status.HTTP_403_FORBIDDEN, - status.HTTP_404_NOT_FOUND, - status.HTTP_409_CONFLICT, - ] - ), - dependencies=[Depends(requires_access_dag(method="PUT", access_entity=DagAccessEntity.HITL_DETAIL))], -) -def update_mapped_ti_hitl_detail( - dag_id: str, - dag_run_id: str, - task_id: str, - update_hitl_detail_payload: UpdateHITLDetailPayload, - user: GetUserDep, - session: SessionDep, - map_index: int, + map_index: int = -1, ) -> HITLDetailResponse: """Update a Human-in-the-loop detail.""" return _update_hitl_detail( @@ -251,29 +215,7 @@ def get_hitl_detail( dag_run_id: str, task_id: str, session: SessionDep, -) -> HITLDetail: - """Get a Human-in-the-loop detail of a specific task instance.""" - return _get_hitl_detail( - dag_id=dag_id, - dag_run_id=dag_run_id, - task_id=task_id, - session=session, - map_index=None, - ) - - -@hitl_router.get( - "/{dag_id}/{dag_run_id}/{task_id}/{map_index}", - 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_mapped_ti_hitl_detail( - dag_id: str, - dag_run_id: str, - task_id: str, - session: SessionDep, - map_index: int, + map_index: int = -1, ) -> HITLDetail: """Get a Human-in-the-loop detail of a specific task instance.""" return _get_hitl_detail( 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 1c7ded5f8f422..c23f1dc8c83d9 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts @@ -643,20 +643,12 @@ export const UseDagVersionServiceGetDagVersionsKeyFn = ({ bundleName, bundleVers export type HumanInTheLoopServiceGetHitlDetailDefaultResponse = Awaited>; export type HumanInTheLoopServiceGetHitlDetailQueryResult = UseQueryResult; export const useHumanInTheLoopServiceGetHitlDetailKey = "HumanInTheLoopServiceGetHitlDetail"; -export const UseHumanInTheLoopServiceGetHitlDetailKeyFn = ({ dagId, dagRunId, taskId }: { +export const UseHumanInTheLoopServiceGetHitlDetailKeyFn = ({ dagId, dagRunId, mapIndex, taskId }: { dagId: string; dagRunId: string; + mapIndex?: number; taskId: string; -}, queryKey?: Array) => [useHumanInTheLoopServiceGetHitlDetailKey, ...(queryKey ?? [{ dagId, dagRunId, taskId }])]; -export type HumanInTheLoopServiceGetMappedTiHitlDetailDefaultResponse = Awaited>; -export type HumanInTheLoopServiceGetMappedTiHitlDetailQueryResult = UseQueryResult; -export const useHumanInTheLoopServiceGetMappedTiHitlDetailKey = "HumanInTheLoopServiceGetMappedTiHitlDetail"; -export const UseHumanInTheLoopServiceGetMappedTiHitlDetailKeyFn = ({ dagId, dagRunId, mapIndex, taskId }: { - dagId: string; - dagRunId: string; - mapIndex: number; - taskId: string; -}, queryKey?: Array) => [useHumanInTheLoopServiceGetMappedTiHitlDetailKey, ...(queryKey ?? [{ dagId, dagRunId, mapIndex, taskId }])]; +}, queryKey?: Array) => [useHumanInTheLoopServiceGetHitlDetailKey, ...(queryKey ?? [{ dagId, dagRunId, mapIndex, taskId }])]; export type HumanInTheLoopServiceGetHitlDetailsDefaultResponse = Awaited>; export type HumanInTheLoopServiceGetHitlDetailsQueryResult = UseQueryResult; export const useHumanInTheLoopServiceGetHitlDetailsKey = "HumanInTheLoopServiceGetHitlDetails"; @@ -808,7 +800,6 @@ export type XcomServiceUpdateXcomEntryMutationResult = Awaited>; export type VariableServiceBulkVariablesMutationResult = Awaited>; export type HumanInTheLoopServiceUpdateHitlDetailMutationResult = Awaited>; -export type HumanInTheLoopServiceUpdateMappedTiHitlDetailMutationResult = Awaited>; export type AssetServiceDeleteAssetQueuedEventsMutationResult = Awaited>; export type AssetServiceDeleteDagAssetQueuedEventsMutationResult = Awaited>; export type AssetServiceDeleteDagAssetQueuedEventMutationResult = Awaited>; 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 7586b0167bed6..512239d6fe870 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts @@ -1217,31 +1217,16 @@ export const ensureUseDagVersionServiceGetDagVersionsData = (queryClient: QueryC * @param data.dagId * @param data.dagRunId * @param data.taskId -* @returns HITLDetail Successful Response -* @throws ApiError -*/ -export const ensureUseHumanInTheLoopServiceGetHitlDetailData = (queryClient: QueryClient, { dagId, dagRunId, taskId }: { - dagId: string; - dagRunId: string; - taskId: string; -}) => queryClient.ensureQueryData({ queryKey: Common.UseHumanInTheLoopServiceGetHitlDetailKeyFn({ dagId, dagRunId, taskId }), queryFn: () => HumanInTheLoopService.getHitlDetail({ dagId, dagRunId, taskId }) }); -/** -* Get Mapped Ti Hitl 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 * @returns HITLDetail Successful Response * @throws ApiError */ -export const ensureUseHumanInTheLoopServiceGetMappedTiHitlDetailData = (queryClient: QueryClient, { dagId, dagRunId, mapIndex, taskId }: { +export const ensureUseHumanInTheLoopServiceGetHitlDetailData = (queryClient: QueryClient, { dagId, dagRunId, mapIndex, taskId }: { dagId: string; dagRunId: string; - mapIndex: number; + mapIndex?: number; taskId: string; -}) => queryClient.ensureQueryData({ queryKey: Common.UseHumanInTheLoopServiceGetMappedTiHitlDetailKeyFn({ dagId, dagRunId, mapIndex, taskId }), queryFn: () => HumanInTheLoopService.getMappedTiHitlDetail({ dagId, dagRunId, mapIndex, taskId }) }); +}) => queryClient.ensureQueryData({ queryKey: Common.UseHumanInTheLoopServiceGetHitlDetailKeyFn({ dagId, dagRunId, mapIndex, taskId }), queryFn: () => HumanInTheLoopService.getHitlDetail({ dagId, dagRunId, mapIndex, taskId }) }); /** * 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 68efa90db53f9..e2189434a0794 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts @@ -1217,31 +1217,16 @@ export const prefetchUseDagVersionServiceGetDagVersions = (queryClient: QueryCli * @param data.dagId * @param data.dagRunId * @param data.taskId -* @returns HITLDetail Successful Response -* @throws ApiError -*/ -export const prefetchUseHumanInTheLoopServiceGetHitlDetail = (queryClient: QueryClient, { dagId, dagRunId, taskId }: { - dagId: string; - dagRunId: string; - taskId: string; -}) => queryClient.prefetchQuery({ queryKey: Common.UseHumanInTheLoopServiceGetHitlDetailKeyFn({ dagId, dagRunId, taskId }), queryFn: () => HumanInTheLoopService.getHitlDetail({ dagId, dagRunId, taskId }) }); -/** -* Get Mapped Ti Hitl 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 * @returns HITLDetail Successful Response * @throws ApiError */ -export const prefetchUseHumanInTheLoopServiceGetMappedTiHitlDetail = (queryClient: QueryClient, { dagId, dagRunId, mapIndex, taskId }: { +export const prefetchUseHumanInTheLoopServiceGetHitlDetail = (queryClient: QueryClient, { dagId, dagRunId, mapIndex, taskId }: { dagId: string; dagRunId: string; - mapIndex: number; + mapIndex?: number; taskId: string; -}) => queryClient.prefetchQuery({ queryKey: Common.UseHumanInTheLoopServiceGetMappedTiHitlDetailKeyFn({ dagId, dagRunId, mapIndex, taskId }), queryFn: () => HumanInTheLoopService.getMappedTiHitlDetail({ dagId, dagRunId, mapIndex, taskId }) }); +}) => queryClient.prefetchQuery({ queryKey: Common.UseHumanInTheLoopServiceGetHitlDetailKeyFn({ dagId, dagRunId, mapIndex, taskId }), queryFn: () => HumanInTheLoopService.getHitlDetail({ dagId, dagRunId, mapIndex, taskId }) }); /** * 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 3557a30f759c4..bb0afe4e25e92 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts @@ -1217,31 +1217,16 @@ export const useDagVersionServiceGetDagVersions = = unknown[]>({ dagId, dagRunId, taskId }: { - dagId: string; - dagRunId: string; - taskId: string; -}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useQuery({ queryKey: Common.UseHumanInTheLoopServiceGetHitlDetailKeyFn({ dagId, dagRunId, taskId }, queryKey), queryFn: () => HumanInTheLoopService.getHitlDetail({ dagId, dagRunId, taskId }) as TData, ...options }); -/** -* Get Mapped Ti Hitl 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 * @returns HITLDetail Successful Response * @throws ApiError */ -export const useHumanInTheLoopServiceGetMappedTiHitlDetail = = unknown[]>({ dagId, dagRunId, mapIndex, taskId }: { +export const useHumanInTheLoopServiceGetHitlDetail = = unknown[]>({ dagId, dagRunId, mapIndex, taskId }: { dagId: string; dagRunId: string; - mapIndex: number; + mapIndex?: number; taskId: string; -}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useQuery({ queryKey: Common.UseHumanInTheLoopServiceGetMappedTiHitlDetailKeyFn({ dagId, dagRunId, mapIndex, taskId }, queryKey), queryFn: () => HumanInTheLoopService.getMappedTiHitlDetail({ dagId, dagRunId, mapIndex, taskId }) as TData, ...options }); +}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useQuery({ queryKey: Common.UseHumanInTheLoopServiceGetHitlDetailKeyFn({ dagId, dagRunId, mapIndex, taskId }, queryKey), queryFn: () => HumanInTheLoopService.getHitlDetail({ dagId, dagRunId, mapIndex, taskId }) as TData, ...options }); /** * Get Hitl Details * Get Human-in-the-loop details. @@ -2100,45 +2085,23 @@ export const useVariableServiceBulkVariables = (options?: Omit, "mutationFn">) => useMutation({ mutationFn: ({ dagId, dagRunId, requestBody, taskId }) => HumanInTheLoopService.updateHitlDetail({ dagId, dagRunId, requestBody, taskId }) as unknown as Promise, ...options }); -/** -* Update Mapped Ti Hitl Detail -* Update a Human-in-the-loop detail. -* @param data The data for the request. -* @param data.dagId -* @param data.dagRunId -* @param data.taskId * @param data.mapIndex -* @param data.requestBody * @returns HITLDetailResponse Successful Response * @throws ApiError */ -export const useHumanInTheLoopServiceUpdateMappedTiHitlDetail = (options?: Omit(options?: Omit, "mutationFn">) => useMutation({ mutationFn: ({ dagId, dagRunId, mapIndex, requestBody, taskId }) => HumanInTheLoopService.updateMappedTiHitlDetail({ dagId, dagRunId, mapIndex, requestBody, taskId }) as unknown as Promise, ...options }); +}, TContext>({ mutationFn: ({ dagId, dagRunId, mapIndex, requestBody, taskId }) => HumanInTheLoopService.updateHitlDetail({ dagId, dagRunId, mapIndex, requestBody, taskId }) as unknown as Promise, ...options }); /** * Delete Asset Queued Events * Delete queued asset events for an asset. 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 f3e532afa4c14..4abb3f7b91608 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts @@ -1217,31 +1217,16 @@ export const useDagVersionServiceGetDagVersionsSuspense = = unknown[]>({ dagId, dagRunId, taskId }: { - dagId: string; - dagRunId: string; - taskId: string; -}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseHumanInTheLoopServiceGetHitlDetailKeyFn({ dagId, dagRunId, taskId }, queryKey), queryFn: () => HumanInTheLoopService.getHitlDetail({ dagId, dagRunId, taskId }) as TData, ...options }); -/** -* Get Mapped Ti Hitl 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 * @returns HITLDetail Successful Response * @throws ApiError */ -export const useHumanInTheLoopServiceGetMappedTiHitlDetailSuspense = = unknown[]>({ dagId, dagRunId, mapIndex, taskId }: { +export const useHumanInTheLoopServiceGetHitlDetailSuspense = = unknown[]>({ dagId, dagRunId, mapIndex, taskId }: { dagId: string; dagRunId: string; - mapIndex: number; + mapIndex?: number; taskId: string; -}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseHumanInTheLoopServiceGetMappedTiHitlDetailKeyFn({ dagId, dagRunId, mapIndex, taskId }, queryKey), queryFn: () => HumanInTheLoopService.getMappedTiHitlDetail({ dagId, dagRunId, mapIndex, taskId }) as TData, ...options }); +}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: Common.UseHumanInTheLoopServiceGetHitlDetailKeyFn({ dagId, dagRunId, mapIndex, taskId }, queryKey), queryFn: () => HumanInTheLoopService.getHitlDetail({ dagId, dagRunId, mapIndex, taskId }) as TData, ...options }); /** * Get Hitl Details * Get Human-in-the-loop details. 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 d23551154d52c..106193c55b85c 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, GetDagReportsData, GetDagReportsResponse, 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, 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, UpdateHitlDetailData, UpdateHitlDetailResponse, GetHitlDetailData, GetHitlDetailResponse, UpdateMappedTiHitlDetailData, UpdateMappedTiHitlDetailResponse, GetMappedTiHitlDetailData, GetMappedTiHitlDetailResponse, GetHitlDetailsData, GetHitlDetailsResponse, GetHealthResponse, GetVersionResponse, LoginData, LoginResponse, LogoutData, LogoutResponse, RefreshData, RefreshResponse, GetAuthMenusResponse, GetDependenciesData, GetDependenciesResponse, HistoricalMetricsData, HistoricalMetricsResponse, DagStatsResponse2, StructureDataData, StructureDataResponse2, GetDagStructureData, GetDagStructureResponse, GetGridRunsData, GetGridRunsResponse, GetGridTiSummariesData, GetGridTiSummariesResponse, GetCalendarData, GetCalendarResponse } 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, GetDagReportsData, GetDagReportsResponse, 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, 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, UpdateHitlDetailData, UpdateHitlDetailResponse, GetHitlDetailData, GetHitlDetailResponse, GetHitlDetailsData, GetHitlDetailsResponse, GetHealthResponse, GetVersionResponse, LoginData, LoginResponse, LogoutData, LogoutResponse, RefreshData, RefreshResponse, GetAuthMenusResponse, GetDependenciesData, GetDependenciesResponse, HistoricalMetricsData, HistoricalMetricsResponse, DagStatsResponse2, StructureDataData, StructureDataResponse2, GetDagStructureData, GetDagStructureResponse, GetGridRunsData, GetGridRunsResponse, GetGridTiSummariesData, GetGridTiSummariesResponse, GetCalendarData, GetCalendarResponse } from './types.gen'; export class AssetService { /** @@ -3419,6 +3419,7 @@ export class HumanInTheLoopService { * @param data.dagRunId * @param data.taskId * @param data.requestBody + * @param data.mapIndex * @returns HITLDetailResponse Successful Response * @throws ApiError */ @@ -3431,6 +3432,9 @@ export class HumanInTheLoopService { dag_run_id: data.dagRunId, task_id: data.taskId }, + query: { + map_index: data.mapIndex + }, body: data.requestBody, mediaType: 'application/json', errors: { @@ -3450,6 +3454,7 @@ export class HumanInTheLoopService { * @param data.dagId * @param data.dagRunId * @param data.taskId + * @param data.mapIndex * @returns HITLDetail Successful Response * @throws ApiError */ @@ -3462,68 +3467,7 @@ export class HumanInTheLoopService { dag_run_id: data.dagRunId, task_id: data.taskId }, - errors: { - 401: 'Unauthorized', - 403: 'Forbidden', - 404: 'Not Found', - 422: 'Validation Error' - } - }); - } - - /** - * Update Mapped Ti Hitl Detail - * Update a Human-in-the-loop detail. - * @param data The data for the request. - * @param data.dagId - * @param data.dagRunId - * @param data.taskId - * @param data.mapIndex - * @param data.requestBody - * @returns HITLDetailResponse Successful Response - * @throws ApiError - */ - public static updateMappedTiHitlDetail(data: UpdateMappedTiHitlDetailData): CancelablePromise { - return __request(OpenAPI, { - method: 'PATCH', - url: '/api/v2/hitlDetails/{dag_id}/{dag_run_id}/{task_id}/{map_index}', - path: { - dag_id: data.dagId, - dag_run_id: data.dagRunId, - task_id: data.taskId, - map_index: data.mapIndex - }, - body: data.requestBody, - mediaType: 'application/json', - errors: { - 401: 'Unauthorized', - 403: 'Forbidden', - 404: 'Not Found', - 409: 'Conflict', - 422: 'Validation Error' - } - }); - } - - /** - * Get Mapped Ti Hitl 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 - * @returns HITLDetail Successful Response - * @throws ApiError - */ - public static getMappedTiHitlDetail(data: GetMappedTiHitlDetailData): CancelablePromise { - return __request(OpenAPI, { - method: 'GET', - url: '/api/v2/hitlDetails/{dag_id}/{dag_run_id}/{task_id}/{map_index}', - path: { - dag_id: data.dagId, - dag_run_id: data.dagRunId, - task_id: data.taskId, + query: { map_index: data.mapIndex }, errors: { 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 4af01fb11606f..2ed7cb779855c 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 @@ -2939,6 +2939,7 @@ export type GetDagVersionsResponse = DAGVersionCollectionResponse; export type UpdateHitlDetailData = { dagId: string; dagRunId: string; + mapIndex?: number; requestBody: UpdateHITLDetailPayload; taskId: string; }; @@ -2948,30 +2949,12 @@ export type UpdateHitlDetailResponse = HITLDetailResponse; export type GetHitlDetailData = { dagId: string; dagRunId: string; + mapIndex?: number; taskId: string; }; export type GetHitlDetailResponse = HITLDetail; -export type UpdateMappedTiHitlDetailData = { - dagId: string; - dagRunId: string; - mapIndex: number; - requestBody: UpdateHITLDetailPayload; - taskId: string; -}; - -export type UpdateMappedTiHitlDetailResponse = HITLDetailResponse; - -export type GetMappedTiHitlDetailData = { - dagId: string; - dagRunId: string; - mapIndex: number; - taskId: string; -}; - -export type GetMappedTiHitlDetailResponse = HITLDetail; - export type GetHitlDetailsData = { /** * SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. @@ -6023,62 +6006,6 @@ export type $OpenApiTs = { }; }; }; - '/api/v2/hitlDetails/{dag_id}/{dag_run_id}/{task_id}/{map_index}': { - patch: { - req: UpdateMappedTiHitlDetailData; - res: { - /** - * Successful Response - */ - 200: HITLDetailResponse; - /** - * Unauthorized - */ - 401: HTTPExceptionResponse; - /** - * Forbidden - */ - 403: HTTPExceptionResponse; - /** - * Not Found - */ - 404: HTTPExceptionResponse; - /** - * Conflict - */ - 409: HTTPExceptionResponse; - /** - * Validation Error - */ - 422: HTTPValidationError; - }; - }; - get: { - req: GetMappedTiHitlDetailData; - res: { - /** - * Successful Response - */ - 200: HITLDetail; - /** - * Unauthorized - */ - 401: HTTPExceptionResponse; - /** - * Forbidden - */ - 403: HTTPExceptionResponse; - /** - * Not Found - */ - 404: HTTPExceptionResponse; - /** - * Validation Error - */ - 422: HTTPValidationError; - }; - }; - }; '/api/v2/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 37f13caefeb16..b15f07aa1aa9f 100644 --- a/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLResponseForm.tsx +++ b/airflow-core/src/airflow/ui/src/pages/HITLTaskInstances/HITLResponseForm.tsx @@ -21,7 +21,7 @@ import { useState } from "react"; import { useTranslation } from "react-i18next"; import { FiSend } from "react-icons/fi"; -import type { HITLDetail } from "openapi/requests/types.gen"; +import type { HITLDetail, 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"; @@ -29,7 +29,9 @@ import { useUpdateHITLDetail } from "src/queries/useUpdateHITLDetail"; import { getHITLParamsDict, getHITLFormData } from "src/utils/hitl"; type HITLResponseFormProps = { - readonly hitlDetail?: HITLDetail; + readonly hitlDetail: { + task_instance: TaskInstanceResponse; + } & Omit; }; const isHighlightOption = (option: string, hitlDetail: HITLDetail) => { @@ -49,10 +51,10 @@ export const HITLResponseForm = ({ hitlDetail }: HITLResponseFormProps) => { const [isSubmitting, setIsSubmitting] = useState(false); const { paramsDict } = useParamStore("hitl"); const { updateHITLResponse } = useUpdateHITLDetail({ - dagId: hitlDetail?.task_instance.dag_id ?? "", - dagRunId: hitlDetail?.task_instance.dag_run_id ?? "", - mapIndex: hitlDetail?.task_instance.map_index ?? -1, - taskId: hitlDetail?.task_instance.task_id ?? "", + dagId: hitlDetail.task_instance.dag_id, + dagRunId: hitlDetail.task_instance.dag_run_id, + mapIndex: hitlDetail.task_instance.map_index, + taskId: hitlDetail.task_instance.task_id, }); const handleSubmit = (option?: string) => { @@ -73,10 +75,6 @@ export const HITLResponseForm = ({ hitlDetail }: HITLResponseFormProps) => { } }; - if (!hitlDetail) { - return undefined; - } - return ( {hitlDetail.response_received ? ( 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 442954d4614e3..95e52b145a203 100644 --- a/airflow-core/src/airflow/ui/src/pages/TaskInstance/HITLResponse.tsx +++ b/airflow-core/src/airflow/ui/src/pages/TaskInstance/HITLResponse.tsx @@ -19,7 +19,7 @@ import { Box } from "@chakra-ui/react"; import { useParams } from "react-router-dom"; -import { useHumanInTheLoopServiceGetMappedTiHitlDetail } from "openapi/queries"; +import { useHumanInTheLoopServiceGetHitlDetail } from "openapi/queries"; import { ProgressBar } from "src/components/ui"; import { HITLResponseForm } from "../HITLTaskInstances/HITLResponseForm"; @@ -27,14 +27,14 @@ import { HITLResponseForm } from "../HITLTaskInstances/HITLResponseForm"; export const HITLResponse = () => { const { dagId, mapIndex, runId, taskId } = useParams(); - const { data: hitlDetail } = useHumanInTheLoopServiceGetMappedTiHitlDetail({ + const { data: hitlDetail } = useHumanInTheLoopServiceGetHitlDetail({ dagId: dagId ?? "~", dagRunId: runId ?? "~", mapIndex: Number(mapIndex ?? -1), taskId: taskId ?? "~", }); - if (!hitlDetail) { + if (!hitlDetail?.task_instance) { return ( diff --git a/airflow-core/src/airflow/ui/src/queries/useUpdateHITLDetail.ts b/airflow-core/src/airflow/ui/src/queries/useUpdateHITLDetail.ts index 3740bdba7c4c3..18d26ad80a50f 100644 --- a/airflow-core/src/airflow/ui/src/queries/useUpdateHITLDetail.ts +++ b/airflow-core/src/airflow/ui/src/queries/useUpdateHITLDetail.ts @@ -24,8 +24,8 @@ import { UseDagRunServiceGetDagRunKeyFn, useDagRunServiceGetDagRunsKey, useHumanInTheLoopServiceGetHitlDetailsKey, - useHumanInTheLoopServiceGetMappedTiHitlDetailKey, - useHumanInTheLoopServiceUpdateMappedTiHitlDetail, + useHumanInTheLoopServiceGetHitlDetailKey, + useHumanInTheLoopServiceUpdateHitlDetail, useTaskInstanceServiceGetTaskInstanceKey, useTaskInstanceServiceGetTaskInstancesKey, } from "openapi/queries"; @@ -53,7 +53,7 @@ export const useUpdateHITLDetail = ({ [useTaskInstanceServiceGetTaskInstancesKey, { dagId, dagRunId }], [useTaskInstanceServiceGetTaskInstanceKey, { dagId, dagRunId, mapIndex, taskId }], [useHumanInTheLoopServiceGetHitlDetailsKey, { dagIdPattern: dagId, dagRunId }], - [useHumanInTheLoopServiceGetMappedTiHitlDetailKey, { dagId, dagRunId }], + [useHumanInTheLoopServiceGetHitlDetailKey, { dagId, dagRunId }], ]; await Promise.all(queryKeys.map((key) => queryClient.invalidateQueries({ queryKey: key }))); @@ -72,7 +72,7 @@ export const useUpdateHITLDetail = ({ }); }; - const { isPending, mutate } = useHumanInTheLoopServiceUpdateMappedTiHitlDetail({ + const { isPending, mutate } = useHumanInTheLoopServiceUpdateHitlDetail({ onError, onSuccess, }); @@ -82,7 +82,7 @@ export const useUpdateHITLDetail = ({ mutate({ dagId, dagRunId, - mapIndex: mapIndex ?? -1, + mapIndex, requestBody: { chosen_options: updateHITLResponseRequestBody.chosen_options ?? [], params_input: updateHITLResponseRequestBody.params_input ?? {}, 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 5636e70a1896d..8169d3d05de82 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 @@ -181,10 +181,6 @@ def sample_hitl_details(sample_tis: list[TaskInstance], session: Session) -> lis expected_ti_not_found_error_msg = ( - f"The Task Instance with dag_id: `{DAG_ID}`," - f" run_id: `test`, task_id: `{TASK_ID}` and map_index: `None` was not found" -) -expected_mapped_ti_not_found_error_msg = ( f"The Task Instance with dag_id: `{DAG_ID}`," f" run_id: `test`, task_id: `{TASK_ID}` and map_index: `-1` was not found" ) @@ -261,15 +257,17 @@ def expected_sample_hitl_detail_dict(sample_ti: TaskInstance) -> dict[str, Any]: class TestUpdateHITLDetailEndpoint: + @pytest.mark.parametrize("query_param", ["", "?map_index=-1"]) @time_machine.travel(datetime(2025, 7, 3, 0, 0, 0), tick=False) @pytest.mark.usefixtures("sample_hitl_detail") def test_should_respond_200_with_existing_response( self, test_client: TestClient, sample_ti_url_identifier: str, + query_param: str, ) -> None: response = test_client.patch( - f"/hitlDetails/{sample_ti_url_identifier}", + f"/hitlDetails/{sample_ti_url_identifier}{query_param}", json={"chosen_options": ["Approve"], "params_input": {"input_1": 2}}, ) @@ -281,16 +279,18 @@ def test_should_respond_200_with_existing_response( "response_at": "2025-07-03T00:00:00Z", } + @pytest.mark.parametrize("query_param", ["", "?map_index=-1"]) @time_machine.travel(datetime(2025, 7, 3, 0, 0, 0), tick=False) @pytest.mark.usefixtures("sample_hitl_detail_respondent") def test_should_respond_200_to_respondent_user( self, test_client: TestClient, sample_ti_url_identifier: str, + query_param: str, ): """Test with an authorized user and the user is a respondent to the task.""" response = test_client.patch( - f"/hitlDetails/{sample_ti_url_identifier}", + f"/hitlDetails/{sample_ti_url_identifier}{query_param}", json={"chosen_options": ["Approve"], "params_input": {"input_1": 2}}, ) @@ -302,62 +302,79 @@ def test_should_respond_200_to_respondent_user( "response_at": "2025-07-03T00:00:00Z", } + @pytest.mark.parametrize("query_param", ["", "?map_index=-1"]) def test_should_respond_401( self, unauthenticated_test_client: TestClient, sample_ti_url_identifier: str, + query_param: str, ) -> None: - response = unauthenticated_test_client.get(f"/hitlDetails/{sample_ti_url_identifier}") + response = unauthenticated_test_client.patch( + f"/hitlDetails/{sample_ti_url_identifier}{query_param}", + json={"chosen_options": ["Approve"], "params_input": {"input_1": 2}}, + ) assert response.status_code == 401 + @pytest.mark.parametrize("query_param", ["", "?map_index=-1"]) def test_should_respond_403( self, unauthorized_test_client: TestClient, sample_ti_url_identifier: str, + query_param: str, ) -> None: - response = unauthorized_test_client.get(f"/hitlDetails/{sample_ti_url_identifier}") + response = unauthorized_test_client.patch( + f"/hitlDetails/{sample_ti_url_identifier}{query_param}", + json={"chosen_options": ["Approve"], "params_input": {"input_1": 2}}, + ) assert response.status_code == 403 + @pytest.mark.parametrize("query_param", ["", "?map_index=-1"]) @time_machine.travel(datetime(2025, 7, 3, 0, 0, 0), tick=False) @pytest.mark.usefixtures("sample_hitl_detail_non_respondent") def test_should_respond_403_to_non_respondent_user( self, test_client: TestClient, sample_ti_url_identifier: str, + query_param: str, ): """Test with an authorized user but the user is not a respondent to the task.""" response = test_client.patch( - f"/hitlDetails/{sample_ti_url_identifier}", + f"/hitlDetails/{sample_ti_url_identifier}{query_param}", json={"chosen_options": ["Approve"], "params_input": {"input_1": 2}}, ) assert response.status_code == 403 + @pytest.mark.parametrize("query_param", ["", "?map_index=-1"]) def test_should_respond_404_without_ti( self, test_client: TestClient, sample_ti_url_identifier: str, + query_param: str, ) -> None: response = test_client.patch( - f"/hitlDetails/{sample_ti_url_identifier}", + f"/hitlDetails/{sample_ti_url_identifier}{query_param}", json={"chosen_options": ["Approve"], "params_input": {"input_1": 2}}, ) assert response.status_code == 404 assert response.json() == {"detail": expected_ti_not_found_error_msg} + @pytest.mark.parametrize("query_param", ["", "?map_index=-1"]) def test_should_respond_404_without_hitl_detail( self, test_client: TestClient, sample_ti_url_identifier: str, expected_hitl_detail_not_found_error_msg: str, + query_param: str, ) -> None: response = test_client.patch( - f"/hitlDetails/{sample_ti_url_identifier}", + f"/hitlDetails/{sample_ti_url_identifier}{query_param}", json={"chosen_options": ["Approve"], "params_input": {"input_1": 2}}, ) assert response.status_code == 404 assert response.json() == {"detail": expected_hitl_detail_not_found_error_msg} + @pytest.mark.parametrize("query_param", ["", "?map_index=-1"]) @time_machine.travel(datetime(2025, 7, 3, 0, 0, 0), tick=False) @pytest.mark.usefixtures("sample_hitl_detail") def test_should_respond_409( @@ -365,9 +382,10 @@ def test_should_respond_409( test_client: TestClient, sample_ti_url_identifier: str, sample_ti: TaskInstance, + query_param: str, ) -> None: response = test_client.patch( - f"/hitlDetails/{sample_ti_url_identifier}", + f"/hitlDetails/{sample_ti_url_identifier}{query_param}", json={"chosen_options": ["Approve"], "params_input": {"input_1": 2}}, ) @@ -381,7 +399,7 @@ def test_should_respond_409( assert response.json() == expected_response response = test_client.patch( - f"/hitlDetails/{sample_ti_url_identifier}", + f"/hitlDetails/{sample_ti_url_identifier}{query_param}", json={"chosen_options": ["Approve"], "params_input": {"input_1": 2}}, ) assert response.status_code == 409 @@ -393,154 +411,16 @@ def test_should_respond_409( ) } + @pytest.mark.parametrize("query_param", ["", "?map_index=-1"]) @pytest.mark.usefixtures("sample_hitl_detail") def test_should_respond_422_with_empty_option( self, test_client: TestClient, sample_ti_url_identifier: str, + query_param: str, ) -> None: response = test_client.patch( - f"/hitlDetails/{sample_ti_url_identifier}", - json={"chosen_options": [], "params_input": {"input_1": 2}}, - ) - - assert response.status_code == 422 - - -class TestUpdateMappedTIHITLDetail: - @time_machine.travel(datetime(2025, 7, 3, 0, 0, 0), tick=False) - @pytest.mark.usefixtures("sample_hitl_detail") - def test_should_respond_200_with_existing_response( - self, - test_client: TestClient, - sample_ti_url_identifier: str, - ) -> None: - response = test_client.patch( - f"/hitlDetails/{sample_ti_url_identifier}/-1", - json={"chosen_options": ["Approve"], "params_input": {"input_1": 2}}, - ) - - assert response.status_code == 200 - assert response.json() == { - "params_input": {"input_1": 2}, - "chosen_options": ["Approve"], - "user_id": "test", - "response_at": "2025-07-03T00:00:00Z", - } - - @time_machine.travel(datetime(2025, 7, 3, 0, 0, 0), tick=False) - @pytest.mark.usefixtures("sample_hitl_detail_respondent") - def test_should_respond_200_to_respondent_user( - self, - test_client: TestClient, - sample_ti_url_identifier: str, - ): - """Test with an authorized user and the user is a respondent to the task.""" - response = test_client.patch( - f"/hitlDetails/{sample_ti_url_identifier}/-1", - json={"chosen_options": ["Approve"], "params_input": {"input_1": 2}}, - ) - - assert response.status_code == 200 - assert response.json() == { - "params_input": {"input_1": 2}, - "chosen_options": ["Approve"], - "user_id": "test", - "response_at": "2025-07-03T00:00:00Z", - } - - def test_should_respond_401( - self, - unauthenticated_test_client: TestClient, - sample_ti_url_identifier: str, - ) -> None: - response = unauthenticated_test_client.get(f"/hitlDetails/{sample_ti_url_identifier}/-1") - assert response.status_code == 401 - - def test_should_respond_403( - self, - unauthorized_test_client: TestClient, - sample_ti_url_identifier: str, - ) -> None: - response = unauthorized_test_client.get(f"/hitlDetails/{sample_ti_url_identifier}/-1") - assert response.status_code == 403 - - @time_machine.travel(datetime(2025, 7, 3, 0, 0, 0), tick=False) - @pytest.mark.usefixtures("sample_hitl_detail_non_respondent") - def test_should_respond_403_to_non_respondent_user( - self, - test_client: TestClient, - sample_ti_url_identifier: str, - ): - """Test with an authorized user but the user is not a respondent to the task.""" - response = test_client.patch( - f"/hitlDetails/{sample_ti_url_identifier}/-1", - json={"chosen_options": ["Approve"], "params_input": {"input_1": 2}}, - ) - assert response.status_code == 403 - - def test_should_respond_404_without_ti( - self, - test_client: TestClient, - sample_ti_url_identifier: str, - ) -> None: - response = test_client.get(f"/hitlDetails/{sample_ti_url_identifier}/-1") - assert response.status_code == 404 - assert response.json() == {"detail": expected_mapped_ti_not_found_error_msg} - - def test_should_respond_404_without_hitl_detail( - self, - test_client: TestClient, - sample_ti_url_identifier: str, - expected_hitl_detail_not_found_error_msg: str, - ) -> None: - response = test_client.get(f"/hitlDetails/{sample_ti_url_identifier}/-1") - assert response.status_code == 404 - assert response.json() == {"detail": expected_hitl_detail_not_found_error_msg} - - @time_machine.travel(datetime(2025, 7, 3, 0, 0, 0), tick=False) - @pytest.mark.usefixtures("sample_hitl_detail") - def test_should_respond_409( - self, - test_client: TestClient, - sample_ti_url_identifier: str, - sample_ti: TaskInstance, - ) -> None: - response = test_client.patch( - f"/hitlDetails/{sample_ti_url_identifier}/-1", - json={"chosen_options": ["Approve"], "params_input": {"input_1": 2}}, - ) - - expected_response = { - "params_input": {"input_1": 2}, - "chosen_options": ["Approve"], - "user_id": "test", - "response_at": "2025-07-03T00:00:00Z", - } - assert response.status_code == 200 - assert response.json() == expected_response - - response = test_client.patch( - f"/hitlDetails/{sample_ti_url_identifier}/-1", - json={"chosen_options": ["Approve"], "params_input": {"input_1": 2}}, - ) - assert response.status_code == 409 - assert response.json() == { - "detail": ( - "Human-in-the-loop detail has already been updated for Task Instance " - f"with id {sample_ti.id} " - "and is not allowed to write again." - ) - } - - @pytest.mark.usefixtures("sample_hitl_detail") - def test_should_respond_422_with_empty_option( - self, - test_client: TestClient, - sample_ti_url_identifier: str, - ) -> None: - response = test_client.patch( - f"/hitlDetails/{sample_ti_url_identifier}/-1", + f"/hitlDetails/{sample_ti_url_identifier}{query_param}", json={"chosen_options": [], "params_input": {"input_1": 2}}, ) @@ -548,97 +428,59 @@ def test_should_respond_422_with_empty_option( class TestGetHITLDetailEndpoint: + @pytest.mark.parametrize("query_param", ["", "?map_index=-1"]) @pytest.mark.usefixtures("sample_hitl_detail") def test_should_respond_200_with_existing_response( self, test_client: TestClient, sample_ti_url_identifier: str, expected_sample_hitl_detail_dict: dict[str, Any], + query_param: str, ) -> None: - response = test_client.get(f"/hitlDetails/{sample_ti_url_identifier}") + response = test_client.get(f"/hitlDetails/{sample_ti_url_identifier}{query_param}") assert response.status_code == 200 assert response.json() == expected_sample_hitl_detail_dict + @pytest.mark.parametrize("query_param", ["", "?map_index=-1"]) def test_should_respond_401( self, unauthenticated_test_client: TestClient, sample_ti_url_identifier: str, + query_param: str, ) -> None: - response = unauthenticated_test_client.get(f"/hitlDetails/{sample_ti_url_identifier}") + response = unauthenticated_test_client.get(f"/hitlDetails/{sample_ti_url_identifier}{query_param}") assert response.status_code == 401 + @pytest.mark.parametrize("query_param", ["", "?map_index=-1"]) def test_should_respond_403( self, unauthorized_test_client: TestClient, sample_ti_url_identifier: str, + query_param: str, ) -> None: - response = unauthorized_test_client.get(f"/hitlDetails/{sample_ti_url_identifier}") + response = unauthorized_test_client.get(f"/hitlDetails/{sample_ti_url_identifier}{query_param}") assert response.status_code == 403 + @pytest.mark.parametrize("query_param", ["", "?map_index=-1"]) def test_should_respond_404_without_ti( self, test_client: TestClient, sample_ti_url_identifier: str, + query_param: str, ) -> None: - response = test_client.get(f"/hitlDetails/{sample_ti_url_identifier}") + response = test_client.get(f"/hitlDetails/{sample_ti_url_identifier}{query_param}") assert response.status_code == 404 assert response.json() == {"detail": expected_ti_not_found_error_msg} + @pytest.mark.parametrize("query_param", ["", "?map_index=-1"]) def test_should_respond_404_without_hitl_detail( self, test_client: TestClient, sample_ti_url_identifier: str, expected_hitl_detail_not_found_error_msg: str, + query_param: str, ) -> None: - response = test_client.get(f"/hitlDetails/{sample_ti_url_identifier}") - assert response.status_code == 404 - assert response.json() == {"detail": expected_hitl_detail_not_found_error_msg} - - -class TestGetMappedTIHITLDetail: - @pytest.mark.usefixtures("sample_hitl_detail") - def test_should_respond_200_with_existing_response( - self, - test_client: TestClient, - sample_ti_url_identifier: str, - expected_sample_hitl_detail_dict: dict[str, Any], - ) -> None: - response = test_client.get(f"/hitlDetails/{sample_ti_url_identifier}/-1") - assert response.status_code == 200 - assert response.json() == expected_sample_hitl_detail_dict - - def test_should_respond_401( - self, - unauthenticated_test_client: TestClient, - sample_ti_url_identifier: str, - ) -> None: - response = unauthenticated_test_client.get(f"/hitlDetails/{sample_ti_url_identifier}/-1") - assert response.status_code == 401 - - def test_should_respond_403( - self, - unauthorized_test_client: TestClient, - sample_ti_url_identifier: str, - ) -> None: - response = unauthorized_test_client.get(f"/hitlDetails/{sample_ti_url_identifier}/-1") - assert response.status_code == 403 - - def test_should_respond_404_without_ti( - self, - test_client: TestClient, - sample_ti_url_identifier: str, - ) -> None: - response = test_client.get(f"/hitlDetails/{sample_ti_url_identifier}/-1") - assert response.status_code == 404 - assert response.json() == {"detail": expected_mapped_ti_not_found_error_msg} - - def test_should_respond_404_without_hitl_detail( - self, - test_client: TestClient, - sample_ti_url_identifier: str, - expected_hitl_detail_not_found_error_msg: str, - ) -> None: - response = test_client.get(f"/hitlDetails/{sample_ti_url_identifier}/-1") + response = test_client.get(f"/hitlDetails/{sample_ti_url_identifier}{query_param}") assert response.status_code == 404 assert response.json() == {"detail": expected_hitl_detail_not_found_error_msg}