This is an automated email from the ASF dual-hosted git repository. weilee pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push: new d54477fadd4 feat(hitl): allow filtering by taskId in "GET /hitlDetails" (#53923) d54477fadd4 is described below commit d54477fadd4bf81070d21d563fd98170efd38019 Author: Wei Lee <weilee...@gmail.com> AuthorDate: Thu Jul 31 09:43:35 2025 +0800 feat(hitl): allow filtering by taskId in "GET /hitlDetails" (#53923) --- airflow-core/src/airflow/api_fastapi/common/parameters.py | 9 +++++++++ .../api_fastapi/core_api/openapi/v2-rest-api-generated.yaml | 12 ++++++++++++ .../src/airflow/api_fastapi/core_api/routes/public/hitl.py | 3 +++ airflow-core/src/airflow/ui/openapi-gen/queries/common.ts | 5 +++-- .../src/airflow/ui/openapi-gen/queries/ensureQueryData.ts | 6 ++++-- airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts | 6 ++++-- airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts | 6 ++++-- airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts | 6 ++++-- .../src/airflow/ui/openapi-gen/requests/services.gen.ts | 2 ++ .../src/airflow/ui/openapi-gen/requests/types.gen.ts | 4 ++++ .../unit/api_fastapi/core_api/routes/public/test_hitl.py | 9 ++++++--- 11 files changed, 55 insertions(+), 13 deletions(-) diff --git a/airflow-core/src/airflow/api_fastapi/common/parameters.py b/airflow-core/src/airflow/api_fastapi/common/parameters.py index bd3534c6217..72666135bed 100644 --- a/airflow-core/src/airflow/api_fastapi/common/parameters.py +++ b/airflow-core/src/airflow/api_fastapi/common/parameters.py @@ -786,6 +786,15 @@ QueryHITLDetailDagIdPatternSearch = Annotated[ ) ), ] +QueryHITLDetailTaskIdPatternSearch = Annotated[ + _SearchParam, + Depends( + search_param_factory( + TaskInstance.task_id, + "task_id_pattern", + ) + ), +] QueryHITLDetailDagRunIdFilter = Annotated[ FilterParam[str], Depends( 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 4ddb35c2989..fa1ecae844c 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 @@ -7604,6 +7604,18 @@ paths: schema: type: string title: Dag Run Id + - name: task_id_pattern + in: query + required: false + schema: + anyOf: + - type: string + - type: 'null' + description: "SQL LIKE expression \u2014 use `%` / `_` wildcards (e.g. `%customer_%`).\ + \ Regular expressions are **not** supported." + title: Task Id Pattern + description: "SQL LIKE expression \u2014 use `%` / `_` wildcards (e.g. `%customer_%`).\ + \ Regular expressions are **not** supported." - name: state in: query required: false 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 804162ba808..9b9cdfaf0a9 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 @@ -32,6 +32,7 @@ from airflow.api_fastapi.common.parameters import ( QueryHITLDetailDagRunIdFilter, QueryHITLDetailResponseReceivedFilter, QueryHITLDetailSubjectSearch, + QueryHITLDetailTaskIdPatternSearch, QueryHITLDetailUserIdFilter, QueryLimit, QueryOffset, @@ -302,6 +303,7 @@ def get_hitl_details( readable_ti_filter: ReadableTIFilterDep, dag_id_pattern: QueryHITLDetailDagIdPatternSearch, dag_run_id: QueryHITLDetailDagRunIdFilter, + task_id: QueryHITLDetailTaskIdPatternSearch, ti_state: QueryTIStateFilter, # hitl detail related filter response_received: QueryHITLDetailResponseReceivedFilter, @@ -322,6 +324,7 @@ def get_hitl_details( readable_ti_filter, dag_id_pattern, dag_run_id, + task_id, ti_state, # hitl detail related filter response_received, 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 e4e52a6c871..bcf1b4a6f03 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts @@ -646,7 +646,7 @@ export const UseHumanInTheLoopServiceGetMappedTiHitlDetailKeyFn = ({ dagId, dagR export type HumanInTheLoopServiceGetHitlDetailsDefaultResponse = Awaited<ReturnType<typeof HumanInTheLoopService.getHitlDetails>>; export type HumanInTheLoopServiceGetHitlDetailsQueryResult<TData = HumanInTheLoopServiceGetHitlDetailsDefaultResponse, TError = unknown> = UseQueryResult<TData, TError>; export const useHumanInTheLoopServiceGetHitlDetailsKey = "HumanInTheLoopServiceGetHitlDetails"; -export const UseHumanInTheLoopServiceGetHitlDetailsKeyFn = ({ bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, userId }: { +export const UseHumanInTheLoopServiceGetHitlDetailsKeyFn = ({ bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, taskIdPattern, userId }: { bodySearch?: string; dagIdPattern?: string; dagRunId?: string; @@ -656,8 +656,9 @@ export const UseHumanInTheLoopServiceGetHitlDetailsKeyFn = ({ bodySearch, dagIdP responseReceived?: boolean; state?: string[]; subjectSearch?: string; + taskIdPattern?: string; userId?: string[]; -} = {}, queryKey?: Array<unknown>) => [useHumanInTheLoopServiceGetHitlDetailsKey, ...(queryKey ?? [{ bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, userId }])]; +} = {}, queryKey?: Array<unknown>) => [useHumanInTheLoopServiceGetHitlDetailsKey, ...(queryKey ?? [{ bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, taskIdPattern, userId }])]; export type MonitorServiceGetHealthDefaultResponse = Awaited<ReturnType<typeof MonitorService.getHealth>>; export type MonitorServiceGetHealthQueryResult<TData = MonitorServiceGetHealthDefaultResponse, TError = unknown> = UseQueryResult<TData, TError>; export const useMonitorServiceGetHealthKey = "MonitorServiceGetHealth"; 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 e7138c41954..0cb2c1b89a5 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts @@ -1223,6 +1223,7 @@ export const ensureUseHumanInTheLoopServiceGetMappedTiHitlDetailData = (queryCli * @param data.orderBy * @param data.dagIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.dagRunId +* @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.state * @param data.responseReceived * @param data.userId @@ -1231,7 +1232,7 @@ export const ensureUseHumanInTheLoopServiceGetMappedTiHitlDetailData = (queryCli * @returns HITLDetailCollection Successful Response * @throws ApiError */ -export const ensureUseHumanInTheLoopServiceGetHitlDetailsData = (queryClient: QueryClient, { bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, userId }: { +export const ensureUseHumanInTheLoopServiceGetHitlDetailsData = (queryClient: QueryClient, { bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, taskIdPattern, userId }: { bodySearch?: string; dagIdPattern?: string; dagRunId?: string; @@ -1241,8 +1242,9 @@ export const ensureUseHumanInTheLoopServiceGetHitlDetailsData = (queryClient: Qu responseReceived?: boolean; state?: string[]; subjectSearch?: string; + taskIdPattern?: string; userId?: string[]; -} = {}) => queryClient.ensureQueryData({ queryKey: Common.UseHumanInTheLoopServiceGetHitlDetailsKeyFn({ bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, userId }), queryFn: () => HumanInTheLoopService.getHitlDetails({ bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, userId }) }); +} = {}) => queryClient.ensureQueryData({ queryKey: Common.UseHumanInTheLoopServiceGetHitlDetailsKeyFn({ bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, taskIdPattern, userId }), queryFn: () => HumanInTheLoopService.getHitlDetails({ bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, taskIdPattern, userId }) }); /** * Get Health * @returns HealthInfoResponse Successful Response 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 a0c05257f65..47db9950f9d 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts @@ -1223,6 +1223,7 @@ export const prefetchUseHumanInTheLoopServiceGetMappedTiHitlDetail = (queryClien * @param data.orderBy * @param data.dagIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.dagRunId +* @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.state * @param data.responseReceived * @param data.userId @@ -1231,7 +1232,7 @@ export const prefetchUseHumanInTheLoopServiceGetMappedTiHitlDetail = (queryClien * @returns HITLDetailCollection Successful Response * @throws ApiError */ -export const prefetchUseHumanInTheLoopServiceGetHitlDetails = (queryClient: QueryClient, { bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, userId }: { +export const prefetchUseHumanInTheLoopServiceGetHitlDetails = (queryClient: QueryClient, { bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, taskIdPattern, userId }: { bodySearch?: string; dagIdPattern?: string; dagRunId?: string; @@ -1241,8 +1242,9 @@ export const prefetchUseHumanInTheLoopServiceGetHitlDetails = (queryClient: Quer responseReceived?: boolean; state?: string[]; subjectSearch?: string; + taskIdPattern?: string; userId?: string[]; -} = {}) => queryClient.prefetchQuery({ queryKey: Common.UseHumanInTheLoopServiceGetHitlDetailsKeyFn({ bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, userId }), queryFn: () => HumanInTheLoopService.getHitlDetails({ bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, userId }) }); +} = {}) => queryClient.prefetchQuery({ queryKey: Common.UseHumanInTheLoopServiceGetHitlDetailsKeyFn({ bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, taskIdPattern, userId }), queryFn: () => HumanInTheLoopService.getHitlDetails({ bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, taskIdPattern, userId }) }); /** * Get Health * @returns HealthInfoResponse Successful Response 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 2fa2b17fefc..ced5cf635f5 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts @@ -1223,6 +1223,7 @@ export const useHumanInTheLoopServiceGetMappedTiHitlDetail = <TData = Common.Hum * @param data.orderBy * @param data.dagIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.dagRunId +* @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.state * @param data.responseReceived * @param data.userId @@ -1231,7 +1232,7 @@ export const useHumanInTheLoopServiceGetMappedTiHitlDetail = <TData = Common.Hum * @returns HITLDetailCollection Successful Response * @throws ApiError */ -export const useHumanInTheLoopServiceGetHitlDetails = <TData = Common.HumanInTheLoopServiceGetHitlDetailsDefaultResponse, TError = unknown, TQueryKey extends Array<unknown> = unknown[]>({ bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, userId }: { +export const useHumanInTheLoopServiceGetHitlDetails = <TData = Common.HumanInTheLoopServiceGetHitlDetailsDefaultResponse, TError = unknown, TQueryKey extends Array<unknown> = unknown[]>({ bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, taskIdPattern, userId }: { bodySearch?: string; dagIdPattern?: string; dagRunId?: string; @@ -1241,8 +1242,9 @@ export const useHumanInTheLoopServiceGetHitlDetails = <TData = Common.HumanInThe responseReceived?: boolean; state?: string[]; subjectSearch?: string; + taskIdPattern?: string; userId?: string[]; -} = {}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">) => useQuery<TData, TError>({ queryKey: Common.UseHumanInTheLoopServiceGetHitlDetailsKeyFn({ bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, userId }, queryKey), queryFn: () => HumanInTheLoopService.getHitlDetails({ bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, userId }) as TData, ... [...] +} = {}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">) => useQuery<TData, TError>({ queryKey: Common.UseHumanInTheLoopServiceGetHitlDetailsKeyFn({ bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, taskIdPattern, userId }, queryKey), queryFn: () => HumanInTheLoopService.getHitlDetails({ bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, taskIdPa [...] /** * Get Health * @returns HealthInfoResponse Successful Response 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 3eb26bd5377..ce1a1a8c473 100644 --- a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts +++ b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts @@ -1223,6 +1223,7 @@ export const useHumanInTheLoopServiceGetMappedTiHitlDetailSuspense = <TData = Co * @param data.orderBy * @param data.dagIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.dagRunId +* @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.state * @param data.responseReceived * @param data.userId @@ -1231,7 +1232,7 @@ export const useHumanInTheLoopServiceGetMappedTiHitlDetailSuspense = <TData = Co * @returns HITLDetailCollection Successful Response * @throws ApiError */ -export const useHumanInTheLoopServiceGetHitlDetailsSuspense = <TData = Common.HumanInTheLoopServiceGetHitlDetailsDefaultResponse, TError = unknown, TQueryKey extends Array<unknown> = unknown[]>({ bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, userId }: { +export const useHumanInTheLoopServiceGetHitlDetailsSuspense = <TData = Common.HumanInTheLoopServiceGetHitlDetailsDefaultResponse, TError = unknown, TQueryKey extends Array<unknown> = unknown[]>({ bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, taskIdPattern, userId }: { bodySearch?: string; dagIdPattern?: string; dagRunId?: string; @@ -1241,8 +1242,9 @@ export const useHumanInTheLoopServiceGetHitlDetailsSuspense = <TData = Common.Hu responseReceived?: boolean; state?: string[]; subjectSearch?: string; + taskIdPattern?: string; userId?: string[]; -} = {}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">) => useSuspenseQuery<TData, TError>({ queryKey: Common.UseHumanInTheLoopServiceGetHitlDetailsKeyFn({ bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, userId }, queryKey), queryFn: () => HumanInTheLoopService.getHitlDetails({ bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, userId }) as TD [...] +} = {}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">) => useSuspenseQuery<TData, TError>({ queryKey: Common.UseHumanInTheLoopServiceGetHitlDetailsKeyFn({ bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, taskIdPattern, userId }, queryKey), queryFn: () => HumanInTheLoopService.getHitlDetails({ bodySearch, dagIdPattern, dagRunId, limit, offset, orderBy, responseReceived, state, subjectSearch, [...] /** * Get Health * @returns HealthInfoResponse Successful Response 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 89332283ad9..6a1b619e7d3 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 @@ -3516,6 +3516,7 @@ export class HumanInTheLoopService { * @param data.orderBy * @param data.dagIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.dagRunId + * @param data.taskIdPattern SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. * @param data.state * @param data.responseReceived * @param data.userId @@ -3534,6 +3535,7 @@ export class HumanInTheLoopService { order_by: data.orderBy, dag_id_pattern: data.dagIdPattern, dag_run_id: data.dagRunId, + task_id_pattern: data.taskIdPattern, state: data.state, response_received: data.responseReceived, user_id: data.userId, 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 f825bc5ab09..563b35ca3d7 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 @@ -2957,6 +2957,10 @@ export type GetHitlDetailsData = { * SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. */ subjectSearch?: string | null; + /** + * SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). Regular expressions are **not** supported. + */ + taskIdPattern?: string | null; userId?: Array<(string)>; }; 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 f88bf64ae80..237410ab808 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 @@ -40,11 +40,12 @@ pytestmark = pytest.mark.db_test DAG_ID = "test_hitl_dag" ANOTHER_DAG_ID = "another_hitl_dag" +TASK_ID = "sample_task_hitl" @pytest.fixture def sample_ti(create_task_instance: CreateTaskInstance) -> TaskInstance: - return create_task_instance(dag_id=DAG_ID) + return create_task_instance(dag_id=DAG_ID, task_id=TASK_ID) @pytest.fixture @@ -211,8 +212,8 @@ def expected_sample_hitl_detail_dict(sample_ti: TaskInstance) -> dict[str, Any]: "scheduled_when": None, "start_date": None, "state": None, - "task_display_name": "op1", - "task_id": "op1", + "task_display_name": "sample_task_hitl", + "task_id": TASK_ID, "trigger": None, "triggerer_job": None, "try_number": 0, @@ -487,6 +488,7 @@ class TestGetHITLDetailsEndpoint: ({"dag_id_pattern": "hitl_dag"}, 5), ({"dag_id_pattern": "other_Dag_"}, 3), ({"dag_run_id": "hitl_run_0"}, 1), + ({"task_id_pattern": "another_hitl"}, 3), ({"state": "running"}, 5), ({"state": "success"}, 3), # hitl detail related filter @@ -500,6 +502,7 @@ class TestGetHITLDetailsEndpoint: "dag_id_hitl_dag", "dag_id_other_dag", "dag_run_id", + "task_id", "ti_state_running", "ti_state_success", "subject",