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

bbovenzi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 51c99ea856e fix(ui): wire up logical date filter on /dagruns page 
(#62799) (#62848)
51c99ea856e is described below

commit 51c99ea856e4a09faca421716bea433514ddd86d
Author: Subham <[email protected]>
AuthorDate: Thu Mar 5 01:21:20 2026 +0530

    fix(ui): wire up logical date filter on /dagruns page (#62799) (#62848)
---
 .../src/airflow/ui/src/mocks/handlers/dag_runs.ts  | 87 ++++++++++++++++++++++
 .../src/airflow/ui/src/mocks/handlers/index.ts     |  9 ++-
 .../src/airflow/ui/src/pages/DagRuns.test.tsx      | 48 ++++++++++++
 airflow-core/src/airflow/ui/src/pages/DagRuns.tsx  |  8 ++
 4 files changed, 151 insertions(+), 1 deletion(-)

diff --git a/airflow-core/src/airflow/ui/src/mocks/handlers/dag_runs.ts 
b/airflow-core/src/airflow/ui/src/mocks/handlers/dag_runs.ts
new file mode 100644
index 00000000000..fe9083a3d76
--- /dev/null
+++ b/airflow-core/src/airflow/ui/src/mocks/handlers/dag_runs.ts
@@ -0,0 +1,87 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* eslint-disable unicorn/no-null */
+import { http, HttpResponse, type HttpHandler } from "msw";
+
+const dagRunBeforeFilter = {
+  conf: null,
+  dag_display_name: "test_dag",
+  dag_id: "test_dag",
+  dag_run_id: "run_before_filter",
+  dag_versions: [],
+  data_interval_end: null,
+  data_interval_start: null,
+  duration: 1.5,
+  end_date: "2024-12-31T00:00:01Z",
+  logical_date: "2024-12-31T00:00:00Z",
+  partition_key: null,
+  run_after: "2024-12-31T00:00:00Z",
+  run_type: "manual",
+  start_date: "2024-12-31T00:00:00Z",
+  state: "success",
+  triggering_user_name: "admin",
+};
+
+const dagRunInRange = {
+  conf: null,
+  dag_display_name: "test_dag",
+  dag_id: "test_dag",
+  dag_run_id: "run_in_range",
+  dag_versions: [],
+  data_interval_end: null,
+  data_interval_start: null,
+  duration: 2.0,
+  end_date: "2025-01-15T00:00:01Z",
+  logical_date: "2025-01-15T00:00:00Z",
+  partition_key: null,
+  run_after: "2025-01-15T00:00:00Z",
+  run_type: "manual",
+  start_date: "2025-01-15T00:00:00Z",
+  state: "success",
+  triggering_user_name: "admin",
+};
+
+export const handlers: Array<HttpHandler> = [
+  http.get("/api/v2/dags/:dagId/dagRuns", ({ request }) => {
+    const url = new URL(request.url);
+    const logicalDateGte = url.searchParams.get("logical_date_gte");
+    const logicalDateLte = url.searchParams.get("logical_date_lte");
+
+    const allRuns = [dagRunBeforeFilter, dagRunInRange];
+
+    const filtered = allRuns.filter((run) => {
+      const logicalDate = new Date(run.logical_date);
+
+      if (logicalDateGte !== null && logicalDate < new Date(logicalDateGte)) {
+        return false;
+      }
+      if (logicalDateLte !== null && logicalDate > new Date(logicalDateLte)) {
+        return false;
+      }
+
+      return true;
+    });
+
+    return HttpResponse.json({
+      dag_runs: filtered,
+      total_entries: filtered.length,
+    });
+  }),
+];
diff --git a/airflow-core/src/airflow/ui/src/mocks/handlers/index.ts 
b/airflow-core/src/airflow/ui/src/mocks/handlers/index.ts
index 0b6f1dd56c9..9f60605f2b6 100644
--- a/airflow-core/src/airflow/ui/src/mocks/handlers/index.ts
+++ b/airflow-core/src/airflow/ui/src/mocks/handlers/index.ts
@@ -18,7 +18,14 @@
  */
 import { handlers as configHandlers } from "./config";
 import { handlers as dagHandlers } from "./dag";
+import { handlers as dagRunsHandlers } from "./dag_runs";
 import { handlers as dagsHandlers } from "./dags";
 import { handlers as logHandlers } from "./log";
 
-export const handlers = [...configHandlers, ...dagHandlers, ...dagsHandlers, 
...logHandlers];
+export const handlers = [
+  ...configHandlers,
+  ...dagHandlers,
+  ...dagRunsHandlers,
+  ...dagsHandlers,
+  ...logHandlers,
+];
diff --git a/airflow-core/src/airflow/ui/src/pages/DagRuns.test.tsx 
b/airflow-core/src/airflow/ui/src/pages/DagRuns.test.tsx
new file mode 100644
index 00000000000..50d1d9d8e6a
--- /dev/null
+++ b/airflow-core/src/airflow/ui/src/pages/DagRuns.test.tsx
@@ -0,0 +1,48 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import "@testing-library/jest-dom";
+import { render, screen, waitFor } from "@testing-library/react";
+import { describe, expect, it } from "vitest";
+
+import { AppWrapper } from "src/utils/AppWrapper";
+
+// The dag_runs mock handler (see src/mocks/handlers/dag_runs.ts) returns:
+//   - run_before_filter (logical_date: 2024-12-31) — excluded when filtering 
Jan 2025
+//   - run_in_range      (logical_date: 2025-01-15) — included when filtering 
Jan 2025
+describe("DagRuns logical date filter", () => {
+  it("shows all runs when no logical date filter is applied", async () => {
+    render(<AppWrapper initialEntries={["/dag_runs"]} />);
+
+    await waitFor(() => 
expect(screen.getByText("run_in_range")).toBeInTheDocument());
+    expect(screen.getByText("run_before_filter")).toBeInTheDocument();
+  });
+
+  it("filters runs by logical_date_gte and logical_date_lte URL params", async 
() => {
+    render(
+      <AppWrapper
+        initialEntries={[
+          
"/dag_runs?logical_date_gte=2025-01-01T00%3A00%3A00Z&logical_date_lte=2025-01-31T23%3A59%3A59Z",
+        ]}
+      />,
+    );
+
+    await waitFor(() => 
expect(screen.getByText("run_in_range")).toBeInTheDocument());
+    expect(screen.queryByText("run_before_filter")).not.toBeInTheDocument();
+  });
+});
diff --git a/airflow-core/src/airflow/ui/src/pages/DagRuns.tsx 
b/airflow-core/src/airflow/ui/src/pages/DagRuns.tsx
index 99186d6102e..fba73b61f55 100644
--- a/airflow-core/src/airflow/ui/src/pages/DagRuns.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/DagRuns.tsx
@@ -16,6 +16,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
+/* eslint-disable max-lines */
 import { Flex, HStack, Link, Text } from "@chakra-ui/react";
 import type { ColumnDef } from "@tanstack/react-table";
 import type { TFunction } from "i18next";
@@ -50,6 +52,8 @@ const {
   DURATION_LTE: DURATION_LTE_PARAM,
   END_DATE_GTE: END_DATE_GTE_PARAM,
   END_DATE_LTE: END_DATE_LTE_PARAM,
+  LOGICAL_DATE_GTE: LOGICAL_DATE_GTE_PARAM,
+  LOGICAL_DATE_LTE: LOGICAL_DATE_LTE_PARAM,
   PARTITION_KEY_PATTERN: PARTITION_KEY_PATTERN_PARAM,
   RUN_AFTER_GTE: RUN_AFTER_GTE_PARAM,
   RUN_AFTER_LTE: RUN_AFTER_LTE_PARAM,
@@ -213,6 +217,8 @@ export const DagRuns = () => {
   const startDateLte = searchParams.get(START_DATE_LTE_PARAM);
   const endDateGte = searchParams.get(END_DATE_GTE_PARAM);
   const endDateLte = searchParams.get(END_DATE_LTE_PARAM);
+  const logicalDateGte = searchParams.get(LOGICAL_DATE_GTE_PARAM);
+  const logicalDateLte = searchParams.get(LOGICAL_DATE_LTE_PARAM);
   const runAfterGte = searchParams.get(RUN_AFTER_GTE_PARAM);
   const runAfterLte = searchParams.get(RUN_AFTER_LTE_PARAM);
   const durationGte = searchParams.get(DURATION_GTE_PARAM);
@@ -234,6 +240,8 @@ export const DagRuns = () => {
       endDateGte: endDateGte ?? undefined,
       endDateLte: endDateLte ?? undefined,
       limit: pageSize,
+      logicalDateGte: logicalDateGte ?? undefined,
+      logicalDateLte: logicalDateLte ?? undefined,
       offset: pageIndex * pageSize,
       orderBy,
       partitionKeyPattern: partitionKeyPattern ?? undefined,

Reply via email to