This is an automated email from the ASF dual-hosted git repository.
rahulvats pushed a commit to branch v3-1-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/v3-1-test by this push:
new c4290ab3a83 [v3-1-test] Return empty list for queued asset events
instead of 404 (#62934) (#62976)
c4290ab3a83 is described below
commit c4290ab3a83204d720934111a55c3e61e0422584
Author: Wei Lee <[email protected]>
AuthorDate: Fri Mar 6 14:50:42 2026 +0800
[v3-1-test] Return empty list for queued asset events instead of 404
(#62934) (#62976)
* [v3-1-test] Return empty list for queued asset events instead of 404
(#62934)
(cherry picked from commit f08f5975e7d93844ff8f226fb32ed27b243255fa)
Co-authored-by: Guan-Ming (Wesley) Chiu
<[email protected]>
* fixup! [v3-1-test] Return empty list for queued asset events instead of
404 (#62934) (cherry picked from commit
f08f5975e7d93844ff8f226fb32ed27b243255fa)
* fixup! fixup! [v3-1-test] Return empty list for queued asset events
instead of 404 (#62934) (cherry picked from commit
f08f5975e7d93844ff8f226fb32ed27b243255fa)
---------
Co-authored-by: Guan-Ming (Wesley) Chiu
<[email protected]>
---
.../core_api/openapi/v2-rest-api-generated.yaml | 12 -------
.../api_fastapi/core_api/routes/public/assets.py | 10 ------
.../config_templates/airflow_local_settings.py | 4 +--
.../ui/openapi-gen/requests/services.gen.ts | 2 --
.../airflow/ui/openapi-gen/requests/types.gen.ts | 8 -----
.../src/airflow/ui/src/pages/Dag/Header.tsx | 1 -
.../ui/src/pages/DagsList/AssetSchedule.tsx | 39 ++++++----------------
.../src/airflow/ui/src/pages/DagsList/DagCard.tsx | 1 -
.../src/airflow/ui/src/pages/DagsList/DagsList.tsx | 21 +++++-------
.../src/airflow/ui/src/pages/DagsList/Schedule.tsx | 16 ++-------
.../core_api/routes/public/test_assets.py | 12 +++----
11 files changed, 29 insertions(+), 97 deletions(-)
diff --git
a/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
b/airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml
index 0f4fa84182c..7e8afefeb71 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
@@ -545,12 +545,6 @@ paths:
schema:
$ref: '#/components/schemas/HTTPExceptionResponse'
description: Forbidden
- '404':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/HTTPExceptionResponse'
- description: Not Found
'422':
description: Validation Error
content:
@@ -700,12 +694,6 @@ paths:
schema:
$ref: '#/components/schemas/HTTPExceptionResponse'
description: Forbidden
- '404':
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/HTTPExceptionResponse'
- description: Not Found
'422':
description: Validation Error
content:
diff --git
a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/assets.py
b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/assets.py
index a00739564f8..b1bafae7766 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/assets.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/assets.py
@@ -390,7 +390,6 @@ def materialize_asset(
@assets_router.get(
"/assets/{asset_id}/queuedEvents",
- responses=create_openapi_http_exception_doc([status.HTTP_404_NOT_FOUND]),
dependencies=[Depends(requires_access_asset(method="GET"))],
)
def get_asset_queued_events(
@@ -408,12 +407,6 @@ def get_asset_queued_events(
dag_asset_queued_events_select, total_entries =
paginated_select(statement=query)
adrqs = session.scalars(dag_asset_queued_events_select).all()
- if not adrqs:
- raise HTTPException(
- status.HTTP_404_NOT_FOUND,
- f"Queue event with asset_id: `{asset_id}` was not found",
- )
-
queued_events = [
QueuedEventResponse(
created_at=adrq.created_at,
@@ -486,7 +479,6 @@ def get_asset(
@assets_router.get(
"/dags/{dag_id}/assets/queuedEvents",
- responses=create_openapi_http_exception_doc([status.HTTP_404_NOT_FOUND]),
dependencies=[Depends(requires_access_asset(method="GET")),
Depends(requires_access_dag(method="GET"))],
)
def get_dag_asset_queued_events(
@@ -503,8 +495,6 @@ def get_dag_asset_queued_events(
dag_asset_queued_events_select, total_entries =
paginated_select(statement=query)
adrqs = session.scalars(dag_asset_queued_events_select).all()
- if not adrqs:
- raise HTTPException(status.HTTP_404_NOT_FOUND, f"Queue event with
dag_id: `{dag_id}` was not found")
queued_events = [
QueuedEventResponse(
diff --git
a/airflow-core/src/airflow/config_templates/airflow_local_settings.py
b/airflow-core/src/airflow/config_templates/airflow_local_settings.py
index 3a547aa58ee..51331d209ec 100644
--- a/airflow-core/src/airflow/config_templates/airflow_local_settings.py
+++ b/airflow-core/src/airflow/config_templates/airflow_local_settings.py
@@ -282,8 +282,8 @@ if REMOTE_LOGGING:
)
remote_task_handler_kwargs = {}
elif ELASTICSEARCH_HOST:
- from airflow.providers.elasticsearch.log.es_task_handler import (
- ElasticsearchRemoteLogIO, # type: ignore[attr-defined]
+ from airflow.providers.elasticsearch.log.es_task_handler import ( #
type: ignore[attr-defined]
+ ElasticsearchRemoteLogIO,
)
ELASTICSEARCH_WRITE_STDOUT: bool = conf.getboolean("elasticsearch",
"WRITE_STDOUT")
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 153da21a141..645e62d5fa2 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
@@ -212,7 +212,6 @@ export class AssetService {
errors: {
401: 'Unauthorized',
403: 'Forbidden',
- 404: 'Not Found',
422: 'Validation Error'
}
});
@@ -292,7 +291,6 @@ export class AssetService {
errors: {
401: 'Unauthorized',
403: 'Forbidden',
- 404: 'Not Found',
422: 'Validation Error'
}
});
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 29e7b3f6031..efb5de0f958 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
@@ -3493,10 +3493,6 @@ export type $OpenApiTs = {
* Forbidden
*/
403: HTTPExceptionResponse;
- /**
- * Not Found
- */
- 404: HTTPExceptionResponse;
/**
* Validation Error
*/
@@ -3572,10 +3568,6 @@ export type $OpenApiTs = {
* Forbidden
*/
403: HTTPExceptionResponse;
- /**
- * Not Found
- */
- 404: HTTPExceptionResponse;
/**
* Validation Error
*/
diff --git a/airflow-core/src/airflow/ui/src/pages/Dag/Header.tsx
b/airflow-core/src/airflow/ui/src/pages/Dag/Header.tsx
index 84913f45fd7..bee669e6943 100644
--- a/airflow-core/src/airflow/ui/src/pages/Dag/Header.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Dag/Header.tsx
@@ -67,7 +67,6 @@ export const Header = ({
<Schedule
assetExpression={dag.asset_expression}
dagId={dag.dag_id}
- latestRunAfter={latestRunInfo?.run_after}
timetableDescription={dag.timetable_description}
timetableSummary={dag.timetable_summary}
/>
diff --git a/airflow-core/src/airflow/ui/src/pages/DagsList/AssetSchedule.tsx
b/airflow-core/src/airflow/ui/src/pages/DagsList/AssetSchedule.tsx
index fe1962e891a..7118d601172 100644
--- a/airflow-core/src/airflow/ui/src/pages/DagsList/AssetSchedule.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/DagsList/AssetSchedule.tsx
@@ -31,48 +31,31 @@ import { Button, Popover } from "src/components/ui";
type Props = {
readonly assetExpression?: ExpressionType | null;
readonly dagId: string;
- readonly latestRunAfter?: string;
readonly timetableSummary: string | null;
};
-export const AssetSchedule = ({ assetExpression, dagId, latestRunAfter,
timetableSummary }: Props) => {
+export const AssetSchedule = ({ assetExpression, dagId, timetableSummary }:
Props) => {
const { t: translate } = useTranslation(["dags", "common"]);
const { data: nextRun, isLoading: isNextRunLoading } =
useAssetServiceNextRunAssets({ dagId });
- const {
- data: queuedEventsData,
- error: queuedEventsError,
- isLoading: isQueuedEventsLoading,
- } = useAssetServiceGetDagAssetQueuedEvents({ dagId }, undefined);
+ const { data: queuedEventsData, isLoading: isQueuedEventsLoading } =
useAssetServiceGetDagAssetQueuedEvents(
+ { dagId },
+ undefined,
+ { enabled: true },
+ );
const nextRunEvents = (nextRun?.events ?? []) as Array<NextRunEvent>;
- const queuedEventsErrorStatus =
- typeof queuedEventsError === "object" && queuedEventsError !== null &&
"status" in queuedEventsError
- ? (queuedEventsError as { status?: number }).status
- : undefined;
- const hasQueuedEventsError = Boolean(queuedEventsError) &&
queuedEventsErrorStatus !== 404;
const queuedAssetEvents = new Map<number, string>();
- if (!hasQueuedEventsError) {
- for (const event of queuedEventsData?.queued_events ?? []) {
- // Keep a single event timestamp per asset, using the latest one when
duplicates exist.
- const existingEventDate = queuedAssetEvents.get(event.asset_id);
+ for (const event of queuedEventsData?.queued_events ?? []) {
+ // Keep a single event timestamp per asset, using the latest one when
duplicates exist.
+ const existingEventDate = queuedAssetEvents.get(event.asset_id);
- if (existingEventDate === undefined ||
dayjs(event.created_at).isAfter(existingEventDate)) {
- queuedAssetEvents.set(event.asset_id, event.created_at);
- }
+ if (existingEventDate === undefined ||
dayjs(event.created_at).isAfter(existingEventDate)) {
+ queuedAssetEvents.set(event.asset_id, event.created_at);
}
}
-
const pendingEvents = nextRunEvents.flatMap((event) => {
- if (hasQueuedEventsError) {
- if (event.lastUpdate === null) {
- return [];
- }
-
- return latestRunAfter !== undefined &&
dayjs(event.lastUpdate).isAfter(latestRunAfter) ? [event] : [];
- }
-
const queuedAt = queuedAssetEvents.get(event.id);
return queuedAt === undefined ? [] : [{ ...event, lastUpdate:
event.lastUpdate ?? queuedAt }];
diff --git a/airflow-core/src/airflow/ui/src/pages/DagsList/DagCard.tsx
b/airflow-core/src/airflow/ui/src/pages/DagsList/DagCard.tsx
index a43233feb9b..de2487175d3 100644
--- a/airflow-core/src/airflow/ui/src/pages/DagsList/DagCard.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/DagsList/DagCard.tsx
@@ -81,7 +81,6 @@ export const DagCard = ({ dag }: Props) => {
<Schedule
assetExpression={dag.asset_expression}
dagId={dag.dag_id}
- latestRunAfter={latestRun?.run_after}
timetableDescription={dag.timetable_description}
timetableSummary={dag.timetable_summary}
/>
diff --git a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx
b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx
index 0c32a2568e8..b3f62bfd9d2 100644
--- a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx
@@ -86,19 +86,14 @@ const createColumns = (
},
{
accessorKey: "timetable_description",
- cell: ({ row: { original } }) => {
- const [latestRun] = original.latest_dag_runs;
-
- return (
- <Schedule
- assetExpression={original.asset_expression}
- dagId={original.dag_id}
- latestRunAfter={latestRun?.run_after}
- timetableDescription={original.timetable_description}
- timetableSummary={original.timetable_summary}
- />
- );
- },
+ cell: ({ row: { original } }) => (
+ <Schedule
+ assetExpression={original.asset_expression}
+ dagId={original.dag_id}
+ timetableDescription={original.timetable_description}
+ timetableSummary={original.timetable_summary}
+ />
+ ),
enableSorting: false,
header: () => translate("dagDetails.schedule"),
},
diff --git a/airflow-core/src/airflow/ui/src/pages/DagsList/Schedule.tsx
b/airflow-core/src/airflow/ui/src/pages/DagsList/Schedule.tsx
index ca8f39215c2..c02eac648a7 100644
--- a/airflow-core/src/airflow/ui/src/pages/DagsList/Schedule.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/DagsList/Schedule.tsx
@@ -28,28 +28,16 @@ import { AssetSchedule } from "./AssetSchedule";
type Props = {
readonly assetExpression: ExpressionType | null | undefined;
readonly dagId: string;
- readonly latestRunAfter?: string;
readonly timetableDescription?: string | null;
readonly timetableSummary: string | null;
};
-export const Schedule = ({
- assetExpression,
- dagId,
- latestRunAfter,
- timetableDescription,
- timetableSummary,
-}: Props) => {
+export const Schedule = ({ assetExpression, dagId, timetableDescription,
timetableSummary }: Props) => {
const { t: translate } = useTranslation("dags");
return Boolean(timetableSummary) ? (
Boolean(assetExpression) || timetableSummary ===
translate("schedule.asset") ? (
- <AssetSchedule
- assetExpression={assetExpression}
- dagId={dagId}
- latestRunAfter={latestRunAfter}
- timetableSummary={timetableSummary}
- />
+ <AssetSchedule assetExpression={assetExpression} dagId={dagId}
timetableSummary={timetableSummary} />
) : (
<Tooltip content={timetableDescription}>
<Text fontSize="sm">
diff --git
a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_assets.py
b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_assets.py
index ddb7d8e7a94..625b5abc9d7 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_assets.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_assets.py
@@ -1035,15 +1035,15 @@ class
TestGetDagAssetQueuedEvents(TestQueuedEventEndpoint):
response =
unauthorized_test_client.get("/dags/random/assets/queuedEvents")
assert response.status_code == 403
- def test_should_respond_404(self, test_client):
+ def test_should_respond_200_empty(self, test_client):
dag_id = "not_exists"
response = test_client.get(
f"/dags/{dag_id}/assets/queuedEvents",
)
- assert response.status_code == 404
- assert response.json()["detail"] == "Queue event with dag_id:
`not_exists` was not found"
+ assert response.status_code == 200
+ assert response.json() == {"queued_events": [], "total_entries": 0}
class TestDeleteDagDatasetQueuedEvents(TestQueuedEventEndpoint):
@@ -1294,10 +1294,10 @@ class TestGetAssetQueuedEvents(TestQueuedEventEndpoint):
response = unauthorized_test_client.get("/assets/1/queuedEvents")
assert response.status_code == 403
- def test_should_respond_404(self, test_client):
+ def test_should_respond_200_empty(self, test_client):
response = test_client.get("/assets/1/queuedEvents")
- assert response.status_code == 404
- assert response.json()["detail"] == "Queue event with asset_id: `1`
was not found"
+ assert response.status_code == 200
+ assert response.json() == {"queued_events": [], "total_entries": 0}
class TestDeleteAssetQueuedEvents(TestQueuedEventEndpoint):