This is an automated email from the ASF dual-hosted git repository.

jscheffl 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 e9ec8929810 Add run type and triggering user filters to grid view 
(#55082)
e9ec8929810 is described below

commit e9ec89298102d2c31bd77f1c2ad1e4e5f2027499
Author: Dheeraj Turaga <dheerajtur...@gmail.com>
AuthorDate: Mon Sep 8 15:52:25 2025 -0500

    Add run type and triggering user filters to grid view (#55082)
    
    * Add run type filter to grid view for DAG run columns
    
      Add a run type filter to the grid view that allows users to filter
      DAG run columns by run type (scheduled, manual, backfill, 
asset_triggered).
      The filter is available in the grid options panel and persists user
      selection per DAG using localStorage.
    
    * Fix translations
    
    * Add triggering user filter to grid view
    
      - Add QueryDagRunTriggeringUserSearch parameter to filter by 
triggering_user_name
      - Update grid API endpoints to accept triggering_user parameter
      - Add triggering user search input to grid options panel
      - Implement state persistence with localStorage per DAG
      - Update OpenAPI generated types and services to support new parameter
      - Modify useGridRuns and useGridStructure hooks to pass triggeringUser
      - Add English translations for "Triggering User Name" label
      - Enable real-time filtering of DAG runs by username
    
      Users can now filter grid view columns to show only runs triggered by
      specific users through a search field in the options panel.
    
    * Fix run type filter display showing raw translation keys
    
      - Update Select.ValueText in PanelButtons to use custom render function
      - Display translated labels instead of raw keys like 
"dags:filters.allRunTypes"
      - Add RunTypeIcon integration for selected run types to match DagRuns page
      - Properly handle "all" selection with translated "All Run Types" text
      - Match visual style and behavior of existing DagRuns.tsx
        implementation
    
    * Refactor translation keys to reuse existing common.json entries
    
    * Pierre's Suggestions to use searchbox
    
    * Add tests!
    
    * consolidating test cases with @pytest.mark.parametrize
---
 .../src/airflow/api_fastapi/common/parameters.py   |  4 +
 .../api_fastapi/core_api/openapi/_private_ui.yaml  | 40 ++++++++++
 .../airflow/api_fastapi/core_api/routes/ui/grid.py | 10 ++-
 .../src/airflow/ui/openapi-gen/queries/common.ts   | 12 ++-
 .../ui/openapi-gen/queries/ensureQueryData.ts      | 16 +++-
 .../src/airflow/ui/openapi-gen/queries/prefetch.ts | 16 +++-
 .../src/airflow/ui/openapi-gen/queries/queries.ts  | 16 +++-
 .../src/airflow/ui/openapi-gen/queries/suspense.ts | 16 +++-
 .../ui/openapi-gen/requests/services.gen.ts        | 12 ++-
 .../airflow/ui/openapi-gen/requests/types.gen.ts   | 10 +++
 .../airflow/ui/public/i18n/locales/en/common.json  |  3 +-
 .../ui/src/layouts/Details/DetailsLayout.tsx       | 20 ++++-
 .../airflow/ui/src/layouts/Details/Grid/Grid.tsx   | 10 ++-
 .../ui/src/layouts/Details/PanelButtons.tsx        | 86 ++++++++++++++++++++++
 .../src/airflow/ui/src/queries/useGridRuns.ts      | 13 +++-
 .../src/airflow/ui/src/queries/useGridStructure.ts |  7 ++
 .../api_fastapi/core_api/routes/ui/test_grid.py    | 37 ++++++++++
 17 files changed, 297 insertions(+), 31 deletions(-)

diff --git a/airflow-core/src/airflow/api_fastapi/common/parameters.py 
b/airflow-core/src/airflow/api_fastapi/common/parameters.py
index cae6bdac0f0..f1d876f29b6 100644
--- a/airflow-core/src/airflow/api_fastapi/common/parameters.py
+++ b/airflow-core/src/airflow/api_fastapi/common/parameters.py
@@ -797,6 +797,10 @@ QueryDagRunRunTypesFilter = Annotated[
     ),
 ]
 
+QueryDagRunTriggeringUserSearch = Annotated[
+    _SearchParam, Depends(search_param_factory(DagRun.triggering_user_name, 
"triggering_user"))
+]
+
 # DagTags
 QueryDagTagPatternSearch = Annotated[
     _SearchParam, Depends(search_param_factory(DagTag.name, 
"tag_name_pattern"))
diff --git 
a/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml 
b/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml
index d4e9553459f..a4d85b16dd3 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml
+++ b/airflow-core/src/airflow/api_fastapi/core_api/openapi/_private_ui.yaml
@@ -664,6 +664,26 @@ paths:
             format: date-time
           - type: 'null'
           title: Run After Lt
+      - name: run_type
+        in: query
+        required: false
+        schema:
+          type: array
+          items:
+            type: string
+          title: Run Type
+      - name: triggering_user
+        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: Triggering User
+        description: "SQL LIKE expression \u2014 use `%` / `_` wildcards (e.g. 
`%customer_%`).\
+          \ Regular expressions are **not** supported."
       responses:
         '200':
           description: Successful Response
@@ -771,6 +791,26 @@ paths:
             format: date-time
           - type: 'null'
           title: Run After Lt
+      - name: run_type
+        in: query
+        required: false
+        schema:
+          type: array
+          items:
+            type: string
+          title: Run Type
+      - name: triggering_user
+        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: Triggering User
+        description: "SQL LIKE expression \u2014 use `%` / `_` wildcards (e.g. 
`%customer_%`).\
+          \ Regular expressions are **not** supported."
       responses:
         '200':
           description: Successful Response
diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/grid.py 
b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/grid.py
index 0f9f4023cbc..7ff49ac27c1 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/grid.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/grid.py
@@ -27,6 +27,8 @@ from sqlalchemy import select
 from airflow.api_fastapi.auth.managers.models.resource_details import 
DagAccessEntity
 from airflow.api_fastapi.common.db.common import SessionDep, paginated_select
 from airflow.api_fastapi.common.parameters import (
+    QueryDagRunRunTypesFilter,
+    QueryDagRunTriggeringUserSearch,
     QueryLimit,
     QueryOffset,
     RangeFilter,
@@ -118,6 +120,8 @@ def get_dag_structure(
         Depends(SortParam(["run_after", "logical_date", "start_date", 
"end_date"], DagRun).dynamic_depends()),
     ],
     run_after: Annotated[RangeFilter, 
Depends(datetime_range_filter_factory("run_after", DagRun))],
+    run_type: QueryDagRunRunTypesFilter,
+    triggering_user: QueryDagRunTriggeringUserSearch,
 ) -> list[GridNodeResponse]:
     """Return dag structure for grid view."""
     latest_serdag = _get_latest_serdag(dag_id, session)
@@ -136,7 +140,7 @@ def get_dag_structure(
         statement=base_query,
         order_by=order_by,
         offset=offset,
-        filters=[run_after],
+        filters=[run_after, run_type, triggering_user],
         limit=limit,
     )
     run_ids = list(session.scalars(dag_runs_select_filter))
@@ -214,6 +218,8 @@ def get_grid_runs(
         ),
     ],
     run_after: Annotated[RangeFilter, 
Depends(datetime_range_filter_factory("run_after", DagRun))],
+    run_type: QueryDagRunRunTypesFilter,
+    triggering_user: QueryDagRunTriggeringUserSearch,
 ) -> list[GridRunsResponse]:
     """Get info about a run for the grid."""
     # Retrieve, sort the previous DAG Runs
@@ -241,7 +247,7 @@ def get_grid_runs(
         statement=base_query,
         order_by=order_by,
         offset=offset,
-        filters=[run_after],
+        filters=[run_after, run_type, triggering_user],
         limit=limit,
     )
     return session.execute(dag_runs_select_filter)
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 6322b959a38..457aac893ee 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts
@@ -794,7 +794,7 @@ export const UseStructureServiceStructureDataKeyFn = ({ 
dagId, externalDependenc
 export type GridServiceGetDagStructureDefaultResponse = 
Awaited<ReturnType<typeof GridService.getDagStructure>>;
 export type GridServiceGetDagStructureQueryResult<TData = 
GridServiceGetDagStructureDefaultResponse, TError = unknown> = 
UseQueryResult<TData, TError>;
 export const useGridServiceGetDagStructureKey = "GridServiceGetDagStructure";
-export const UseGridServiceGetDagStructureKeyFn = ({ dagId, limit, offset, 
orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte }: {
+export const UseGridServiceGetDagStructureKeyFn = ({ dagId, limit, offset, 
orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, 
triggeringUser }: {
   dagId: string;
   limit?: number;
   offset?: number;
@@ -803,11 +803,13 @@ export const UseGridServiceGetDagStructureKeyFn = ({ 
dagId, limit, offset, order
   runAfterGte?: string;
   runAfterLt?: string;
   runAfterLte?: string;
-}, queryKey?: Array<unknown>) => [useGridServiceGetDagStructureKey, 
...(queryKey ?? [{ dagId, limit, offset, orderBy, runAfterGt, runAfterGte, 
runAfterLt, runAfterLte }])];
+  runType?: string[];
+  triggeringUser?: string;
+}, queryKey?: Array<unknown>) => [useGridServiceGetDagStructureKey, 
...(queryKey ?? [{ dagId, limit, offset, orderBy, runAfterGt, runAfterGte, 
runAfterLt, runAfterLte, runType, triggeringUser }])];
 export type GridServiceGetGridRunsDefaultResponse = Awaited<ReturnType<typeof 
GridService.getGridRuns>>;
 export type GridServiceGetGridRunsQueryResult<TData = 
GridServiceGetGridRunsDefaultResponse, TError = unknown> = 
UseQueryResult<TData, TError>;
 export const useGridServiceGetGridRunsKey = "GridServiceGetGridRuns";
-export const UseGridServiceGetGridRunsKeyFn = ({ dagId, limit, offset, 
orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte }: {
+export const UseGridServiceGetGridRunsKeyFn = ({ dagId, limit, offset, 
orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, 
triggeringUser }: {
   dagId: string;
   limit?: number;
   offset?: number;
@@ -816,7 +818,9 @@ export const UseGridServiceGetGridRunsKeyFn = ({ dagId, 
limit, offset, orderBy,
   runAfterGte?: string;
   runAfterLt?: string;
   runAfterLte?: string;
-}, queryKey?: Array<unknown>) => [useGridServiceGetGridRunsKey, ...(queryKey 
?? [{ dagId, limit, offset, orderBy, runAfterGt, runAfterGte, runAfterLt, 
runAfterLte }])];
+  runType?: string[];
+  triggeringUser?: string;
+}, queryKey?: Array<unknown>) => [useGridServiceGetGridRunsKey, ...(queryKey 
?? [{ dagId, limit, offset, orderBy, runAfterGt, runAfterGte, runAfterLt, 
runAfterLte, runType, triggeringUser }])];
 export type GridServiceGetGridTiSummariesDefaultResponse = 
Awaited<ReturnType<typeof GridService.getGridTiSummaries>>;
 export type GridServiceGetGridTiSummariesQueryResult<TData = 
GridServiceGetGridTiSummariesDefaultResponse, TError = unknown> = 
UseQueryResult<TData, TError>;
 export const useGridServiceGetGridTiSummariesKey = 
"GridServiceGetGridTiSummaries";
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 1f3e5dc4e3e..75803561cfa 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts
@@ -1510,10 +1510,12 @@ export const ensureUseStructureServiceStructureDataData 
= (queryClient: QueryCli
 * @param data.runAfterGt
 * @param data.runAfterLte
 * @param data.runAfterLt
+* @param data.runType
+* @param data.triggeringUser SQL LIKE expression — use `%` / `_` wildcards 
(e.g. `%customer_%`). Regular expressions are **not** supported.
 * @returns GridNodeResponse Successful Response
 * @throws ApiError
 */
-export const ensureUseGridServiceGetDagStructureData = (queryClient: 
QueryClient, { dagId, limit, offset, orderBy, runAfterGt, runAfterGte, 
runAfterLt, runAfterLte }: {
+export const ensureUseGridServiceGetDagStructureData = (queryClient: 
QueryClient, { dagId, limit, offset, orderBy, runAfterGt, runAfterGte, 
runAfterLt, runAfterLte, runType, triggeringUser }: {
   dagId: string;
   limit?: number;
   offset?: number;
@@ -1522,7 +1524,9 @@ export const ensureUseGridServiceGetDagStructureData = 
(queryClient: QueryClient
   runAfterGte?: string;
   runAfterLt?: string;
   runAfterLte?: string;
-}) => queryClient.ensureQueryData({ queryKey: 
Common.UseGridServiceGetDagStructureKeyFn({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte }), queryFn: () => 
GridService.getDagStructure({ dagId, limit, offset, orderBy, runAfterGt, 
runAfterGte, runAfterLt, runAfterLte }) });
+  runType?: string[];
+  triggeringUser?: string;
+}) => queryClient.ensureQueryData({ queryKey: 
Common.UseGridServiceGetDagStructureKeyFn({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }), 
queryFn: () => GridService.getDagStructure({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }) });
 /**
 * Get Grid Runs
 * Get info about a run for the grid.
@@ -1535,10 +1539,12 @@ export const ensureUseGridServiceGetDagStructureData = 
(queryClient: QueryClient
 * @param data.runAfterGt
 * @param data.runAfterLte
 * @param data.runAfterLt
+* @param data.runType
+* @param data.triggeringUser SQL LIKE expression — use `%` / `_` wildcards 
(e.g. `%customer_%`). Regular expressions are **not** supported.
 * @returns GridRunsResponse Successful Response
 * @throws ApiError
 */
-export const ensureUseGridServiceGetGridRunsData = (queryClient: QueryClient, 
{ dagId, limit, offset, orderBy, runAfterGt, runAfterGte, runAfterLt, 
runAfterLte }: {
+export const ensureUseGridServiceGetGridRunsData = (queryClient: QueryClient, 
{ dagId, limit, offset, orderBy, runAfterGt, runAfterGte, runAfterLt, 
runAfterLte, runType, triggeringUser }: {
   dagId: string;
   limit?: number;
   offset?: number;
@@ -1547,7 +1553,9 @@ export const ensureUseGridServiceGetGridRunsData = 
(queryClient: QueryClient, {
   runAfterGte?: string;
   runAfterLt?: string;
   runAfterLte?: string;
-}) => queryClient.ensureQueryData({ queryKey: 
Common.UseGridServiceGetGridRunsKeyFn({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte }), queryFn: () => 
GridService.getGridRuns({ dagId, limit, offset, orderBy, runAfterGt, 
runAfterGte, runAfterLt, runAfterLte }) });
+  runType?: string[];
+  triggeringUser?: string;
+}) => queryClient.ensureQueryData({ queryKey: 
Common.UseGridServiceGetGridRunsKeyFn({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }), 
queryFn: () => GridService.getGridRuns({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }) });
 /**
 * Get Grid Ti Summaries
 * Get states for TIs / "groups" of TIs.
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 304b6e37d17..180eb826210 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts
@@ -1510,10 +1510,12 @@ export const prefetchUseStructureServiceStructureData = 
(queryClient: QueryClien
 * @param data.runAfterGt
 * @param data.runAfterLte
 * @param data.runAfterLt
+* @param data.runType
+* @param data.triggeringUser SQL LIKE expression — use `%` / `_` wildcards 
(e.g. `%customer_%`). Regular expressions are **not** supported.
 * @returns GridNodeResponse Successful Response
 * @throws ApiError
 */
-export const prefetchUseGridServiceGetDagStructure = (queryClient: 
QueryClient, { dagId, limit, offset, orderBy, runAfterGt, runAfterGte, 
runAfterLt, runAfterLte }: {
+export const prefetchUseGridServiceGetDagStructure = (queryClient: 
QueryClient, { dagId, limit, offset, orderBy, runAfterGt, runAfterGte, 
runAfterLt, runAfterLte, runType, triggeringUser }: {
   dagId: string;
   limit?: number;
   offset?: number;
@@ -1522,7 +1524,9 @@ export const prefetchUseGridServiceGetDagStructure = 
(queryClient: QueryClient,
   runAfterGte?: string;
   runAfterLt?: string;
   runAfterLte?: string;
-}) => queryClient.prefetchQuery({ queryKey: 
Common.UseGridServiceGetDagStructureKeyFn({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte }), queryFn: () => 
GridService.getDagStructure({ dagId, limit, offset, orderBy, runAfterGt, 
runAfterGte, runAfterLt, runAfterLte }) });
+  runType?: string[];
+  triggeringUser?: string;
+}) => queryClient.prefetchQuery({ queryKey: 
Common.UseGridServiceGetDagStructureKeyFn({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }), 
queryFn: () => GridService.getDagStructure({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }) });
 /**
 * Get Grid Runs
 * Get info about a run for the grid.
@@ -1535,10 +1539,12 @@ export const prefetchUseGridServiceGetDagStructure = 
(queryClient: QueryClient,
 * @param data.runAfterGt
 * @param data.runAfterLte
 * @param data.runAfterLt
+* @param data.runType
+* @param data.triggeringUser SQL LIKE expression — use `%` / `_` wildcards 
(e.g. `%customer_%`). Regular expressions are **not** supported.
 * @returns GridRunsResponse Successful Response
 * @throws ApiError
 */
-export const prefetchUseGridServiceGetGridRuns = (queryClient: QueryClient, { 
dagId, limit, offset, orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte 
}: {
+export const prefetchUseGridServiceGetGridRuns = (queryClient: QueryClient, { 
dagId, limit, offset, orderBy, runAfterGt, runAfterGte, runAfterLt, 
runAfterLte, runType, triggeringUser }: {
   dagId: string;
   limit?: number;
   offset?: number;
@@ -1547,7 +1553,9 @@ export const prefetchUseGridServiceGetGridRuns = 
(queryClient: QueryClient, { da
   runAfterGte?: string;
   runAfterLt?: string;
   runAfterLte?: string;
-}) => queryClient.prefetchQuery({ queryKey: 
Common.UseGridServiceGetGridRunsKeyFn({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte }), queryFn: () => 
GridService.getGridRuns({ dagId, limit, offset, orderBy, runAfterGt, 
runAfterGte, runAfterLt, runAfterLte }) });
+  runType?: string[];
+  triggeringUser?: string;
+}) => queryClient.prefetchQuery({ queryKey: 
Common.UseGridServiceGetGridRunsKeyFn({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }), 
queryFn: () => GridService.getGridRuns({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }) });
 /**
 * Get Grid Ti Summaries
 * Get states for TIs / "groups" of TIs.
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 f727dcdbd4f..1b133994fec 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
@@ -1510,10 +1510,12 @@ export const useStructureServiceStructureData = <TData 
= Common.StructureService
 * @param data.runAfterGt
 * @param data.runAfterLte
 * @param data.runAfterLt
+* @param data.runType
+* @param data.triggeringUser SQL LIKE expression — use `%` / `_` wildcards 
(e.g. `%customer_%`). Regular expressions are **not** supported.
 * @returns GridNodeResponse Successful Response
 * @throws ApiError
 */
-export const useGridServiceGetDagStructure = <TData = 
Common.GridServiceGetDagStructureDefaultResponse, TError = unknown, TQueryKey 
extends Array<unknown> = unknown[]>({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte }: {
+export const useGridServiceGetDagStructure = <TData = 
Common.GridServiceGetDagStructureDefaultResponse, TError = unknown, TQueryKey 
extends Array<unknown> = unknown[]>({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }: {
   dagId: string;
   limit?: number;
   offset?: number;
@@ -1522,7 +1524,9 @@ export const useGridServiceGetDagStructure = <TData = 
Common.GridServiceGetDagSt
   runAfterGte?: string;
   runAfterLt?: string;
   runAfterLte?: string;
-}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, 
"queryKey" | "queryFn">) => useQuery<TData, TError>({ queryKey: 
Common.UseGridServiceGetDagStructureKeyFn({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte }, queryKey), queryFn: () => 
GridService.getDagStructure({ dagId, limit, offset, orderBy, runAfterGt, 
runAfterGte, runAfterLt, runAfterLte }) as TData, ...options });
+  runType?: string[];
+  triggeringUser?: string;
+}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, 
"queryKey" | "queryFn">) => useQuery<TData, TError>({ queryKey: 
Common.UseGridServiceGetDagStructureKeyFn({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }, 
queryKey), queryFn: () => GridService.getDagStructure({ dagId, limit, offset, 
orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, 
triggeringUser }) as TData, ...options });
 /**
 * Get Grid Runs
 * Get info about a run for the grid.
@@ -1535,10 +1539,12 @@ export const useGridServiceGetDagStructure = <TData = 
Common.GridServiceGetDagSt
 * @param data.runAfterGt
 * @param data.runAfterLte
 * @param data.runAfterLt
+* @param data.runType
+* @param data.triggeringUser SQL LIKE expression — use `%` / `_` wildcards 
(e.g. `%customer_%`). Regular expressions are **not** supported.
 * @returns GridRunsResponse Successful Response
 * @throws ApiError
 */
-export const useGridServiceGetGridRuns = <TData = 
Common.GridServiceGetGridRunsDefaultResponse, TError = unknown, TQueryKey 
extends Array<unknown> = unknown[]>({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte }: {
+export const useGridServiceGetGridRuns = <TData = 
Common.GridServiceGetGridRunsDefaultResponse, TError = unknown, TQueryKey 
extends Array<unknown> = unknown[]>({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }: {
   dagId: string;
   limit?: number;
   offset?: number;
@@ -1547,7 +1553,9 @@ export const useGridServiceGetGridRuns = <TData = 
Common.GridServiceGetGridRunsD
   runAfterGte?: string;
   runAfterLt?: string;
   runAfterLte?: string;
-}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, 
"queryKey" | "queryFn">) => useQuery<TData, TError>({ queryKey: 
Common.UseGridServiceGetGridRunsKeyFn({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte }, queryKey), queryFn: () => 
GridService.getGridRuns({ dagId, limit, offset, orderBy, runAfterGt, 
runAfterGte, runAfterLt, runAfterLte }) as TData, ...options });
+  runType?: string[];
+  triggeringUser?: string;
+}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, 
"queryKey" | "queryFn">) => useQuery<TData, TError>({ queryKey: 
Common.UseGridServiceGetGridRunsKeyFn({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }, 
queryKey), queryFn: () => GridService.getGridRuns({ dagId, limit, offset, 
orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, 
triggeringUser }) as TData, ...options });
 /**
 * Get Grid Ti Summaries
 * Get states for TIs / "groups" of TIs.
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 35d89616172..45fa755cb8d 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts
@@ -1510,10 +1510,12 @@ export const useStructureServiceStructureDataSuspense = 
<TData = Common.Structur
 * @param data.runAfterGt
 * @param data.runAfterLte
 * @param data.runAfterLt
+* @param data.runType
+* @param data.triggeringUser SQL LIKE expression — use `%` / `_` wildcards 
(e.g. `%customer_%`). Regular expressions are **not** supported.
 * @returns GridNodeResponse Successful Response
 * @throws ApiError
 */
-export const useGridServiceGetDagStructureSuspense = <TData = 
Common.GridServiceGetDagStructureDefaultResponse, TError = unknown, TQueryKey 
extends Array<unknown> = unknown[]>({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte }: {
+export const useGridServiceGetDagStructureSuspense = <TData = 
Common.GridServiceGetDagStructureDefaultResponse, TError = unknown, TQueryKey 
extends Array<unknown> = unknown[]>({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }: {
   dagId: string;
   limit?: number;
   offset?: number;
@@ -1522,7 +1524,9 @@ export const useGridServiceGetDagStructureSuspense = 
<TData = Common.GridService
   runAfterGte?: string;
   runAfterLt?: string;
   runAfterLte?: string;
-}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, 
"queryKey" | "queryFn">) => useSuspenseQuery<TData, TError>({ queryKey: 
Common.UseGridServiceGetDagStructureKeyFn({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte }, queryKey), queryFn: () => 
GridService.getDagStructure({ dagId, limit, offset, orderBy, runAfterGt, 
runAfterGte, runAfterLt, runAfterLte }) as TData, ...options });
+  runType?: string[];
+  triggeringUser?: string;
+}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, 
"queryKey" | "queryFn">) => useSuspenseQuery<TData, TError>({ queryKey: 
Common.UseGridServiceGetDagStructureKeyFn({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }, 
queryKey), queryFn: () => GridService.getDagStructure({ dagId, limit, offset, 
orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, 
triggeringUser }) as TData, ...options });
 /**
 * Get Grid Runs
 * Get info about a run for the grid.
@@ -1535,10 +1539,12 @@ export const useGridServiceGetDagStructureSuspense = 
<TData = Common.GridService
 * @param data.runAfterGt
 * @param data.runAfterLte
 * @param data.runAfterLt
+* @param data.runType
+* @param data.triggeringUser SQL LIKE expression — use `%` / `_` wildcards 
(e.g. `%customer_%`). Regular expressions are **not** supported.
 * @returns GridRunsResponse Successful Response
 * @throws ApiError
 */
-export const useGridServiceGetGridRunsSuspense = <TData = 
Common.GridServiceGetGridRunsDefaultResponse, TError = unknown, TQueryKey 
extends Array<unknown> = unknown[]>({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte }: {
+export const useGridServiceGetGridRunsSuspense = <TData = 
Common.GridServiceGetGridRunsDefaultResponse, TError = unknown, TQueryKey 
extends Array<unknown> = unknown[]>({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }: {
   dagId: string;
   limit?: number;
   offset?: number;
@@ -1547,7 +1553,9 @@ export const useGridServiceGetGridRunsSuspense = <TData = 
Common.GridServiceGetG
   runAfterGte?: string;
   runAfterLt?: string;
   runAfterLte?: string;
-}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, 
"queryKey" | "queryFn">) => useSuspenseQuery<TData, TError>({ queryKey: 
Common.UseGridServiceGetGridRunsKeyFn({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte }, queryKey), queryFn: () => 
GridService.getGridRuns({ dagId, limit, offset, orderBy, runAfterGt, 
runAfterGte, runAfterLt, runAfterLte }) as TData, ...options });
+  runType?: string[];
+  triggeringUser?: string;
+}, queryKey?: TQueryKey, options?: Omit<UseQueryOptions<TData, TError>, 
"queryKey" | "queryFn">) => useSuspenseQuery<TData, TError>({ queryKey: 
Common.UseGridServiceGetGridRunsKeyFn({ dagId, limit, offset, orderBy, 
runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, triggeringUser }, 
queryKey), queryFn: () => GridService.getGridRuns({ dagId, limit, offset, 
orderBy, runAfterGt, runAfterGte, runAfterLt, runAfterLte, runType, 
triggeringUser }) as TData, ...options });
 /**
 * Get Grid Ti Summaries
 * Get states for TIs / "groups" of TIs.
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 99d6bbb7591..1f074a4d5cc 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
@@ -3888,6 +3888,8 @@ export class GridService {
      * @param data.runAfterGt
      * @param data.runAfterLte
      * @param data.runAfterLt
+     * @param data.runType
+     * @param data.triggeringUser SQL LIKE expression — use `%` / `_` 
wildcards (e.g. `%customer_%`). Regular expressions are **not** supported.
      * @returns GridNodeResponse Successful Response
      * @throws ApiError
      */
@@ -3905,7 +3907,9 @@ export class GridService {
                 run_after_gte: data.runAfterGte,
                 run_after_gt: data.runAfterGt,
                 run_after_lte: data.runAfterLte,
-                run_after_lt: data.runAfterLt
+                run_after_lt: data.runAfterLt,
+                run_type: data.runType,
+                triggering_user: data.triggeringUser
             },
             errors: {
                 400: 'Bad Request',
@@ -3927,6 +3931,8 @@ export class GridService {
      * @param data.runAfterGt
      * @param data.runAfterLte
      * @param data.runAfterLt
+     * @param data.runType
+     * @param data.triggeringUser SQL LIKE expression — use `%` / `_` 
wildcards (e.g. `%customer_%`). Regular expressions are **not** supported.
      * @returns GridRunsResponse Successful Response
      * @throws ApiError
      */
@@ -3944,7 +3950,9 @@ export class GridService {
                 run_after_gte: data.runAfterGte,
                 run_after_gt: data.runAfterGt,
                 run_after_lte: data.runAfterLte,
-                run_after_lt: data.runAfterLt
+                run_after_lt: data.runAfterLt,
+                run_type: data.runType,
+                triggering_user: data.triggeringUser
             },
             errors: {
                 400: 'Bad Request',
diff --git a/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts 
b/airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts
index b2f31c4dce5..c1320c1c635 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
@@ -3176,6 +3176,11 @@ export type GetDagStructureData = {
     runAfterGte?: string | null;
     runAfterLt?: string | null;
     runAfterLte?: string | null;
+    runType?: Array<(string)>;
+    /**
+     * SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). 
Regular expressions are **not** supported.
+     */
+    triggeringUser?: string | null;
 };
 
 export type GetDagStructureResponse = Array<GridNodeResponse>;
@@ -3189,6 +3194,11 @@ export type GetGridRunsData = {
     runAfterGte?: string | null;
     runAfterLt?: string | null;
     runAfterLte?: string | null;
+    runType?: Array<(string)>;
+    /**
+     * SQL LIKE expression — use `%` / `_` wildcards (e.g. `%customer_%`). 
Regular expressions are **not** supported.
+     */
+    triggeringUser?: string | null;
 };
 
 export type GetGridRunsResponse = Array<GridRunsResponse>;
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/en/common.json 
b/airflow-core/src/airflow/ui/public/i18n/locales/en/common.json
index ae58ee278e9..defe64cce63 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/en/common.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/en/common.json
@@ -108,7 +108,8 @@
     "runAfterFromPlaceholder": "Run After From",
     "runAfterToPlaceholder": "Run After To",
     "runIdPlaceholder": "Filter by Run ID",
-    "taskIdPlaceholder": "Filter by Task ID"
+    "taskIdPlaceholder": "Filter by Task ID",
+    "triggeringUserPlaceholder": "Filter by triggering user"
   },
   "logicalDate": "Logical Date",
   "logout": "Logout",
diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/DetailsLayout.tsx 
b/airflow-core/src/airflow/ui/src/layouts/Details/DetailsLayout.tsx
index d79001fa71f..ac20004efe8 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Details/DetailsLayout.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Details/DetailsLayout.tsx
@@ -28,6 +28,7 @@ import { Outlet, useParams } from "react-router-dom";
 import { useLocalStorage } from "usehooks-ts";
 
 import { useDagServiceGetDag, useDagWarningServiceListDagWarnings } from 
"openapi/queries";
+import type { DagRunType } from "openapi/requests/types.gen";
 import BackfillBanner from "src/components/Banner/BackfillBanner";
 import { SearchDagsButton } from "src/components/SearchDags";
 import TriggerDAGButton from "src/components/TriggerDag/TriggerDAGButton";
@@ -59,6 +60,14 @@ export const DetailsLayout = ({ children, error, isLoading, 
tabs }: Props) => {
   const panelGroupRef = useRef(null);
   const [dagView, setDagView] = useLocalStorage<"graph" | 
"grid">(`dag_view-${dagId}`, defaultDagView);
   const [limit, setLimit] = useLocalStorage<number>(`dag_runs_limit-${dagId}`, 
10);
+  const [runTypeFilter, setRunTypeFilter] = useLocalStorage<DagRunType | 
undefined>(
+    `run_type_filter-${dagId}`,
+    undefined,
+  );
+  const [triggeringUserFilter, setTriggeringUserFilter] = 
useLocalStorage<string | undefined>(
+    `triggering_user_filter-${dagId}`,
+    undefined,
+  );
 
   const [showGantt, setShowGantt] = 
useLocalStorage<boolean>(`show_gantt-${dagId}`, true);
   const { fitView, getZoom } = useReactFlow();
@@ -123,16 +132,25 @@ export const DetailsLayout = ({ children, error, 
isLoading, tabs }: Props) => {
                 dagView={dagView}
                 limit={limit}
                 panelGroupRef={panelGroupRef}
+                runTypeFilter={runTypeFilter}
                 setDagView={setDagView}
                 setLimit={setLimit}
+                setRunTypeFilter={setRunTypeFilter}
                 setShowGantt={setShowGantt}
+                setTriggeringUserFilter={setTriggeringUserFilter}
                 showGantt={showGantt}
+                triggeringUserFilter={triggeringUserFilter}
               />
               {dagView === "graph" ? (
                 <Graph />
               ) : (
                 <HStack gap={0}>
-                  <Grid limit={limit} showGantt={Boolean(runId) && showGantt} 
/>
+                  <Grid
+                    limit={limit}
+                    runType={runTypeFilter}
+                    showGantt={Boolean(runId) && showGantt}
+                    triggeringUser={triggeringUserFilter}
+                  />
                   {showGantt ? <Gantt limit={limit} /> : undefined}
                 </HStack>
               )}
diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/Grid/Grid.tsx 
b/airflow-core/src/airflow/ui/src/layouts/Details/Grid/Grid.tsx
index 8a56fc47441..dbf48e0d0df 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Details/Grid/Grid.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Details/Grid/Grid.tsx
@@ -24,7 +24,7 @@ import { useTranslation } from "react-i18next";
 import { FiChevronsRight } from "react-icons/fi";
 import { Link, useParams } from "react-router-dom";
 
-import type { GridRunsResponse } from "openapi/requests";
+import type { DagRunType, GridRunsResponse } from "openapi/requests";
 import { useOpenGroups } from "src/context/openGroups";
 import { useNavigation } from "src/hooks/navigation";
 import { useGridRuns } from "src/queries/useGridRuns.ts";
@@ -41,10 +41,12 @@ dayjs.extend(dayjsDuration);
 
 type Props = {
   readonly limit: number;
+  readonly runType?: DagRunType | undefined;
   readonly showGantt?: boolean;
+  readonly triggeringUser?: string | undefined;
 };
 
-export const Grid = ({ limit, showGantt }: Props) => {
+export const Grid = ({ limit, runType, showGantt, triggeringUser }: Props) => {
   const { t: translate } = useTranslation("dag");
   const gridRef = useRef<HTMLDivElement>(null);
 
@@ -53,7 +55,7 @@ export const Grid = ({ limit, showGantt }: Props) => {
   const { openGroupIds, toggleGroupId } = useOpenGroups();
   const { dagId = "", runId = "" } = useParams();
 
-  const { data: gridRuns, isLoading } = useGridRuns({ limit });
+  const { data: gridRuns, isLoading } = useGridRuns({ limit, runType, 
triggeringUser });
 
   // Check if the selected dag run is inside of the grid response, if not, 
we'll update the grid filters
   // Eventually we should redo the api endpoint to make this work better
@@ -77,7 +79,7 @@ export const Grid = ({ limit, showGantt }: Props) => {
     }
   }, [gridRuns, setHasActiveRun]);
 
-  const { data: dagStructure } = useGridStructure({ hasActiveRun, limit });
+  const { data: dagStructure } = useGridStructure({ hasActiveRun, limit, 
runType, triggeringUser });
   // calculate dag run bar heights relative to max
 
   const max = Math.max.apply(
diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/PanelButtons.tsx 
b/airflow-core/src/airflow/ui/src/layouts/Details/PanelButtons.tsx
index 34c06f6ab4f..68eff209fe0 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Details/PanelButtons.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Details/PanelButtons.tsx
@@ -40,10 +40,14 @@ import { MdOutlineAccountTree } from "react-icons/md";
 import { useParams } from "react-router-dom";
 import { useLocalStorage } from "usehooks-ts";
 
+import type { DagRunType } from "openapi/requests/types.gen";
 import { DagVersionSelect } from "src/components/DagVersionSelect";
 import { directionOptions, type Direction } from 
"src/components/Graph/useGraphLayout";
+import { RunTypeIcon } from "src/components/RunTypeIcon";
+import { SearchBar } from "src/components/SearchBar";
 import { Button, Tooltip } from "src/components/ui";
 import { Checkbox } from "src/components/ui/Checkbox";
+import { dagRunTypeOptions } from "src/constants/stateOptions";
 
 import { DagRunSelect } from "./DagRunSelect";
 import { ToggleGroups } from "./ToggleGroups";
@@ -52,10 +56,14 @@ type Props = {
   readonly dagView: string;
   readonly limit: number;
   readonly panelGroupRef: React.RefObject<{ setLayout?: (layout: 
Array<number>) => void } & HTMLDivElement>;
+  readonly runTypeFilter: DagRunType | undefined;
   readonly setDagView: (x: "graph" | "grid") => void;
   readonly setLimit: React.Dispatch<React.SetStateAction<number>>;
+  readonly setRunTypeFilter: React.Dispatch<React.SetStateAction<DagRunType | 
undefined>>;
   readonly setShowGantt: React.Dispatch<React.SetStateAction<boolean>>;
+  readonly setTriggeringUserFilter: React.Dispatch<React.SetStateAction<string 
| undefined>>;
   readonly showGantt: boolean;
+  readonly triggeringUserFilter: string | undefined;
 };
 
 const getOptions = (translate: (key: string) => string) =>
@@ -86,10 +94,14 @@ export const PanelButtons = ({
   dagView,
   limit,
   panelGroupRef,
+  runTypeFilter,
   setDagView,
   setLimit,
+  setRunTypeFilter,
   setShowGantt,
+  setTriggeringUserFilter,
   showGantt,
+  triggeringUserFilter,
 }: Props) => {
   const { t: translate } = useTranslation(["components", "dag"]);
   const { dagId = "", runId } = useParams();
@@ -122,6 +134,22 @@ export const PanelButtons = ({
     }
   };
 
+  const handleRunTypeChange = (event: SelectValueChangeDetails<string>) => {
+    const [val] = event.value;
+
+    if (val === undefined || val === "all") {
+      setRunTypeFilter(undefined);
+    } else {
+      setRunTypeFilter(val as DagRunType);
+    }
+  };
+
+  const handleTriggeringUserChange = (value: string) => {
+    const trimmedValue = value.trim();
+
+    setTriggeringUserFilter(trimmedValue === "" ? undefined : trimmedValue);
+  };
+
   const handleFocus = (view: string) => {
     if (panelGroupRef.current) {
       const panelGroup = panelGroupRef.current;
@@ -292,6 +320,64 @@ export const PanelButtons = ({
                             </Select.Content>
                           </Select.Positioner>
                         </Select.Root>
+                        <Select.Root
+                          // @ts-expect-error The expected option type is 
incorrect
+                          collection={dagRunTypeOptions}
+                          data-testid="run-type-filter"
+                          onValueChange={handleRunTypeChange}
+                          size="sm"
+                          value={[runTypeFilter ?? "all"]}
+                        >
+                          
<Select.Label>{translate("common:dagRun.runType")}</Select.Label>
+                          <Select.Control>
+                            <Select.Trigger>
+                              <Select.ValueText>
+                                {(runTypeFilter ?? "all") === "all" ? (
+                                  translate("dags:filters.allRunTypes")
+                                ) : (
+                                  <Flex gap={1}>
+                                    <RunTypeIcon runType={runTypeFilter!} />
+                                    {translate(
+                                      dagRunTypeOptions.items.find((item) => 
item.value === runTypeFilter)
+                                        ?.label ?? "",
+                                    )}
+                                  </Flex>
+                                )}
+                              </Select.ValueText>
+                            </Select.Trigger>
+                            <Select.IndicatorGroup>
+                              <Select.Indicator />
+                            </Select.IndicatorGroup>
+                          </Select.Control>
+                          <Select.Positioner>
+                            <Select.Content>
+                              {dagRunTypeOptions.items.map((option) => (
+                                <Select.Item item={option} key={option.value}>
+                                  {option.value === "all" ? (
+                                    translate(option.label)
+                                  ) : (
+                                    <Flex gap={1}>
+                                      <RunTypeIcon runType={option.value as 
DagRunType} />
+                                      {translate(option.label)}
+                                    </Flex>
+                                  )}
+                                </Select.Item>
+                              ))}
+                            </Select.Content>
+                          </Select.Positioner>
+                        </Select.Root>
+                        <VStack alignItems="flex-start">
+                          <Text fontSize="xs" mb={1}>
+                            {translate("common:dagRun.triggeringUser")}
+                          </Text>
+                          <SearchBar
+                            defaultValue={triggeringUserFilter ?? ""}
+                            hideAdvanced
+                            hotkeyDisabled
+                            onChange={handleTriggeringUserChange}
+                            
placeHolder={translate("common:filters.triggeringUserPlaceholder")}
+                          />
+                        </VStack>
                         {shouldShowToggleButtons ? (
                           <VStack alignItems="flex-start" px={1}>
                             <Checkbox checked={showGantt} onChange={() => 
setShowGantt(!showGantt)} size="sm">
diff --git a/airflow-core/src/airflow/ui/src/queries/useGridRuns.ts 
b/airflow-core/src/airflow/ui/src/queries/useGridRuns.ts
index 42807a9a7bc..af077b7b1af 100644
--- a/airflow-core/src/airflow/ui/src/queries/useGridRuns.ts
+++ b/airflow-core/src/airflow/ui/src/queries/useGridRuns.ts
@@ -19,9 +19,18 @@
 import { useParams } from "react-router-dom";
 
 import { useGridServiceGetGridRuns } from "openapi/queries";
+import type { DagRunType } from "openapi/requests/types.gen";
 import { isStatePending, useAutoRefresh } from "src/utils";
 
-export const useGridRuns = ({ limit }: { limit: number }) => {
+export const useGridRuns = ({
+  limit,
+  runType,
+  triggeringUser,
+}: {
+  limit: number;
+  runType?: DagRunType | undefined;
+  triggeringUser?: string | undefined;
+}) => {
   const { dagId = "" } = useParams();
 
   const defaultRefetchInterval = useAutoRefresh({ dagId });
@@ -31,6 +40,8 @@ export const useGridRuns = ({ limit }: { limit: number }) => {
       dagId,
       limit,
       orderBy: ["-run_after"],
+      runType: runType ? [runType] : undefined,
+      triggeringUser: triggeringUser ?? undefined,
     },
     undefined,
     {
diff --git a/airflow-core/src/airflow/ui/src/queries/useGridStructure.ts 
b/airflow-core/src/airflow/ui/src/queries/useGridStructure.ts
index 17db10fffd5..f312b028e67 100644
--- a/airflow-core/src/airflow/ui/src/queries/useGridStructure.ts
+++ b/airflow-core/src/airflow/ui/src/queries/useGridStructure.ts
@@ -19,14 +19,19 @@
 import { useParams } from "react-router-dom";
 
 import { useGridServiceGetDagStructure } from "openapi/queries";
+import type { DagRunType } from "openapi/requests/types.gen";
 import { useAutoRefresh } from "src/utils";
 
 export const useGridStructure = ({
   hasActiveRun = undefined,
   limit,
+  runType,
+  triggeringUser,
 }: {
   hasActiveRun?: boolean;
   limit?: number;
+  runType?: DagRunType | undefined;
+  triggeringUser?: string | undefined;
 }) => {
   const { dagId = "" } = useParams();
   const refetchInterval = useAutoRefresh({ dagId });
@@ -37,6 +42,8 @@ export const useGridStructure = ({
       dagId,
       limit,
       orderBy: ["-run_after"],
+      runType: runType ? [runType] : undefined,
+      triggeringUser: triggeringUser ?? undefined,
     },
     undefined,
     {
diff --git 
a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_grid.py 
b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_grid.py
index 271d90c154b..32f8f2683f0 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_grid.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_grid.py
@@ -156,6 +156,8 @@ def setup(dag_maker, session=None):
         data_interval=data_interval,
         **triggered_by_kwargs,
     )
+    # Set specific triggering users for testing filtering (only for manual 
runs)
+    run_2.triggering_user_name = "user2"
     for ti in run_1.task_instances:
         ti.state = TaskInstanceState.SUCCESS
     for ti in sorted(run_2.task_instances, key=lambda ti: (ti.task_id, 
ti.map_index)):
@@ -494,6 +496,41 @@ class TestGetGridDataEndpoint:
             },
         ]
 
+    @pytest.mark.parametrize(
+        "endpoint,run_type,expected",
+        [
+            ("runs", "scheduled", [GRID_RUN_1]),
+            ("runs", "manual", [GRID_RUN_2]),
+            ("structure", "scheduled", GRID_NODES),
+            ("structure", "manual", GRID_NODES),
+        ],
+    )
+    def test_filter_by_run_type(self, session, test_client, endpoint, 
run_type, expected):
+        session.commit()
+        response = 
test_client.get(f"/grid/{endpoint}/{DAG_ID}?run_type={run_type}")
+        assert response.status_code == 200
+        assert response.json() == expected
+
+    @pytest.mark.parametrize(
+        "endpoint,triggering_user,expected",
+        [
+            ("runs", "user2", [GRID_RUN_2]),
+            ("runs", "nonexistent", []),
+            ("structure", "user2", GRID_NODES),
+        ],
+    )
+    def test_filter_by_triggering_user(self, session, test_client, endpoint, 
triggering_user, expected):
+        session.commit()
+        response = 
test_client.get(f"/grid/{endpoint}/{DAG_ID}?triggering_user={triggering_user}")
+        assert response.status_code == 200
+        assert response.json() == expected
+
+    def test_get_grid_runs_filter_by_run_type_and_triggering_user(self, 
session, test_client):
+        session.commit()
+        response = 
test_client.get(f"/grid/runs/{DAG_ID}?run_type=manual&triggering_user=user2")
+        assert response.status_code == 200
+        assert response.json() == [GRID_RUN_2]
+
     def test_grid_ti_summaries_group(self, session, test_client):
         run_id = "run_4-1"
         session.commit()

Reply via email to