This is an automated email from the ASF dual-hosted git repository.
jedcunningham 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 d862ad92cea add 404 response for invalid routes (#48545)
d862ad92cea is described below
commit d862ad92ceaa9a96309ad4ee7882d2a6bffe5f1c
Author: Kalyan R <[email protected]>
AuthorDate: Sun Mar 30 22:39:48 2025 +0530
add 404 response for invalid routes (#48545)
---
.../api_fastapi/core_api/openapi/v1-generated.yaml | 24 +++++++++++++++++++
.../api_fastapi/core_api/routes/public/__init__.py | 10 +++++++-
.../src/airflow/ui/openapi-gen/queries/common.ts | 17 ++++++++++++++
.../ui/openapi-gen/queries/ensureQueryData.ts | 21 +++++++++++++++++
.../src/airflow/ui/openapi-gen/queries/prefetch.ts | 21 +++++++++++++++++
.../src/airflow/ui/openapi-gen/queries/queries.ts | 27 ++++++++++++++++++++++
.../src/airflow/ui/openapi-gen/queries/suspense.ts | 27 ++++++++++++++++++++++
.../ui/openapi-gen/requests/services.gen.ts | 25 ++++++++++++++++++++
.../airflow/ui/openapi-gen/requests/types.gen.ts | 21 +++++++++++++++++
.../api_fastapi/core_api/routes/test_routes.py | 8 +++++++
10 files changed, 200 insertions(+), 1 deletion(-)
diff --git
a/airflow-core/src/airflow/api_fastapi/core_api/openapi/v1-generated.yaml
b/airflow-core/src/airflow/api_fastapi/core_api/openapi/v1-generated.yaml
index c7c8ce31833..d82252dc6bf 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/openapi/v1-generated.yaml
+++ b/airflow-core/src/airflow/api_fastapi/core_api/openapi/v1-generated.yaml
@@ -7304,6 +7304,30 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
+ /api/v2/{rest_of_path}:
+ get:
+ summary: Not Found Handler
+ description: Catch all route to handle invalid endpoints.
+ operationId: not_found_handler
+ parameters:
+ - name: rest_of_path
+ in: path
+ required: true
+ schema:
+ type: string
+ title: Rest Of Path
+ responses:
+ '200':
+ description: Successful Response
+ content:
+ application/json:
+ schema: {}
+ '422':
+ description: Validation Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/HTTPValidationError'
components:
schemas:
AppBuilderMenuItemResponse:
diff --git
a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/__init__.py
b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/__init__.py
index 77fe7a33a18..dc14f724a30 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/__init__.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/__init__.py
@@ -17,7 +17,8 @@
from __future__ import annotations
-from fastapi import status
+from fastapi import Request, status
+from starlette.responses import JSONResponse
from airflow.api_fastapi.common.router import AirflowRouter
from airflow.api_fastapi.core_api.openapi.exceptions import
create_openapi_http_exception_doc
@@ -57,6 +58,7 @@ authenticated_router = AirflowRouter(
responses=create_openapi_http_exception_doc([status.HTTP_401_UNAUTHORIZED,
status.HTTP_403_FORBIDDEN]),
)
+
authenticated_router.include_router(assets_router)
authenticated_router.include_router(backfills_router)
authenticated_router.include_router(connections_router)
@@ -91,3 +93,9 @@ public_router.include_router(authenticated_router)
public_router.include_router(monitor_router)
public_router.include_router(version_router)
public_router.include_router(auth_router)
+
+
+@public_router.get("/{rest_of_path:path}", include_in_schema=False)
+def not_found_handler(request: Request, rest_of_path: str):
+ """Catch all route to handle invalid endpoints."""
+ return JSONResponse(status_code=404, content={"error": "invalid route"})
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 135bc03a36d..b3308f30f9e 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/common.ts
@@ -17,6 +17,7 @@ import {
DagWarningService,
DagsService,
DashboardService,
+ DefaultService,
DependenciesService,
EventLogService,
ExtraLinksService,
@@ -1794,6 +1795,22 @@ export const UseLoginServiceLogoutKeyFn = (
} = {},
queryKey?: Array<unknown>,
) => [useLoginServiceLogoutKey, ...(queryKey ?? [{ next }])];
+export type DefaultServiceNotFoundHandlerDefaultResponse = Awaited<
+ ReturnType<typeof DefaultService.notFoundHandler>
+>;
+export type DefaultServiceNotFoundHandlerQueryResult<
+ TData = DefaultServiceNotFoundHandlerDefaultResponse,
+ TError = unknown,
+> = UseQueryResult<TData, TError>;
+export const useDefaultServiceNotFoundHandlerKey =
"DefaultServiceNotFoundHandler";
+export const UseDefaultServiceNotFoundHandlerKeyFn = (
+ {
+ restOfPath,
+ }: {
+ restOfPath: string;
+ },
+ queryKey?: Array<unknown>,
+) => [useDefaultServiceNotFoundHandlerKey, ...(queryKey ?? [{ restOfPath }])];
export type AssetServiceCreateAssetEventMutationResult = Awaited<
ReturnType<typeof AssetService.createAssetEvent>
>;
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 846819e8c13..db69e50d1c8 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/ensureQueryData.ts
@@ -16,6 +16,7 @@ import {
DagWarningService,
DagsService,
DashboardService,
+ DefaultService,
DependenciesService,
EventLogService,
ExtraLinksService,
@@ -2511,3 +2512,23 @@ export const ensureUseLoginServiceLogoutData = (
queryKey: Common.UseLoginServiceLogoutKeyFn({ next }),
queryFn: () => LoginService.logout({ next }),
});
+/**
+ * Not Found Handler
+ * Catch all route to handle invalid endpoints.
+ * @param data The data for the request.
+ * @param data.restOfPath
+ * @returns unknown Successful Response
+ * @throws ApiError
+ */
+export const ensureUseDefaultServiceNotFoundHandlerData = (
+ queryClient: QueryClient,
+ {
+ restOfPath,
+ }: {
+ restOfPath: string;
+ },
+) =>
+ queryClient.ensureQueryData({
+ queryKey: Common.UseDefaultServiceNotFoundHandlerKeyFn({ restOfPath }),
+ queryFn: () => DefaultService.notFoundHandler({ restOfPath }),
+ });
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 f49e671be05..99d44a857e1 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/prefetch.ts
@@ -16,6 +16,7 @@ import {
DagWarningService,
DagsService,
DashboardService,
+ DefaultService,
DependenciesService,
EventLogService,
ExtraLinksService,
@@ -2511,3 +2512,23 @@ export const prefetchUseLoginServiceLogout = (
queryKey: Common.UseLoginServiceLogoutKeyFn({ next }),
queryFn: () => LoginService.logout({ next }),
});
+/**
+ * Not Found Handler
+ * Catch all route to handle invalid endpoints.
+ * @param data The data for the request.
+ * @param data.restOfPath
+ * @returns unknown Successful Response
+ * @throws ApiError
+ */
+export const prefetchUseDefaultServiceNotFoundHandler = (
+ queryClient: QueryClient,
+ {
+ restOfPath,
+ }: {
+ restOfPath: string;
+ },
+) =>
+ queryClient.prefetchQuery({
+ queryKey: Common.UseDefaultServiceNotFoundHandlerKeyFn({ restOfPath }),
+ queryFn: () => DefaultService.notFoundHandler({ restOfPath }),
+ });
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 5baaf1218cd..9429a0b14bc 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts
@@ -17,6 +17,7 @@ import {
DagWarningService,
DagsService,
DashboardService,
+ DefaultService,
DependenciesService,
EventLogService,
ExtraLinksService,
@@ -2990,6 +2991,32 @@ export const useLoginServiceLogout = <
queryFn: () => LoginService.logout({ next }) as TData,
...options,
});
+/**
+ * Not Found Handler
+ * Catch all route to handle invalid endpoints.
+ * @param data The data for the request.
+ * @param data.restOfPath
+ * @returns unknown Successful Response
+ * @throws ApiError
+ */
+export const useDefaultServiceNotFoundHandler = <
+ TData = Common.DefaultServiceNotFoundHandlerDefaultResponse,
+ TError = unknown,
+ TQueryKey extends Array<unknown> = unknown[],
+>(
+ {
+ restOfPath,
+ }: {
+ restOfPath: string;
+ },
+ queryKey?: TQueryKey,
+ options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">,
+) =>
+ useQuery<TData, TError>({
+ queryKey: Common.UseDefaultServiceNotFoundHandlerKeyFn({ restOfPath },
queryKey),
+ queryFn: () => DefaultService.notFoundHandler({ restOfPath }) as TData,
+ ...options,
+ });
/**
* Create Asset Event
* Create asset events.
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 4d9fa9e218c..732c28f8e37 100644
--- a/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts
+++ b/airflow-core/src/airflow/ui/openapi-gen/queries/suspense.ts
@@ -16,6 +16,7 @@ import {
DagWarningService,
DagsService,
DashboardService,
+ DefaultService,
DependenciesService,
EventLogService,
ExtraLinksService,
@@ -2967,3 +2968,29 @@ export const useLoginServiceLogoutSuspense = <
queryFn: () => LoginService.logout({ next }) as TData,
...options,
});
+/**
+ * Not Found Handler
+ * Catch all route to handle invalid endpoints.
+ * @param data The data for the request.
+ * @param data.restOfPath
+ * @returns unknown Successful Response
+ * @throws ApiError
+ */
+export const useDefaultServiceNotFoundHandlerSuspense = <
+ TData = Common.DefaultServiceNotFoundHandlerDefaultResponse,
+ TError = unknown,
+ TQueryKey extends Array<unknown> = unknown[],
+>(
+ {
+ restOfPath,
+ }: {
+ restOfPath: string;
+ },
+ queryKey?: TQueryKey,
+ options?: Omit<UseQueryOptions<TData, TError>, "queryKey" | "queryFn">,
+) =>
+ useSuspenseQuery<TData, TError>({
+ queryKey: Common.UseDefaultServiceNotFoundHandlerKeyFn({ restOfPath },
queryKey),
+ queryFn: () => DefaultService.notFoundHandler({ restOfPath }) as TData,
+ ...options,
+ });
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 64289b24c18..208adb5e49e 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
@@ -215,6 +215,8 @@ import type {
LoginResponse,
LogoutData,
LogoutResponse,
+ NotFoundHandlerData,
+ NotFoundHandlerResponse,
} from "./types.gen";
export class AuthLinksService {
@@ -3583,3 +3585,26 @@ export class LoginService {
});
}
}
+
+export class DefaultService {
+ /**
+ * Not Found Handler
+ * Catch all route to handle invalid endpoints.
+ * @param data The data for the request.
+ * @param data.restOfPath
+ * @returns unknown Successful Response
+ * @throws ApiError
+ */
+ public static notFoundHandler(data: NotFoundHandlerData):
CancelablePromise<NotFoundHandlerResponse> {
+ return __request(OpenAPI, {
+ method: "GET",
+ url: "/api/v2/{rest_of_path}",
+ path: {
+ rest_of_path: data.restOfPath,
+ },
+ errors: {
+ 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 08183a2bad9..0b17592b022 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
@@ -2603,6 +2603,12 @@ export type LogoutData = {
export type LogoutResponse = unknown;
+export type NotFoundHandlerData = {
+ restOfPath: string;
+};
+
+export type NotFoundHandlerResponse = unknown;
+
export type $OpenApiTs = {
"/ui/auth/links": {
get: {
@@ -5429,4 +5435,19 @@ export type $OpenApiTs = {
};
};
};
+ "/api/v2/{rest_of_path}": {
+ get: {
+ req: NotFoundHandlerData;
+ res: {
+ /**
+ * Successful Response
+ */
+ 200: unknown;
+ /**
+ * Validation Error
+ */
+ 422: HTTPValidationError;
+ };
+ };
+ };
};
diff --git a/airflow-core/tests/unit/api_fastapi/core_api/routes/test_routes.py
b/airflow-core/tests/unit/api_fastapi/core_api/routes/test_routes.py
index 9ec7b384864..78f1fc80ae7 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/routes/test_routes.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/test_routes.py
@@ -24,6 +24,7 @@ NO_AUTH_PATHS = {
"/api/v2/auth/logout",
"/api/v2/version",
"/api/v2/monitor/health",
+ "/api/v2/{rest_of_path:path}",
}
@@ -50,3 +51,10 @@ def test_routes_with_responses():
# All other routes should have 401 and 403 responses indicating
they require auth
assert 401 in route.responses, f"Route {route.path} is missing 401
response"
assert 403 in route.responses, f"Route {route.path} is missing 403
response"
+
+
+def test_invalid_routes_return_404(test_client):
+ """Invalid routes should return a 404."""
+ response = test_client.get("/api/v2/nonexistent")
+ assert response.status_code == 404
+ assert response.json() == {"error": "invalid route"}