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 c34b73a46eb Remove `/webapp` prefix from new UI (#47041)
c34b73a46eb is described below

commit c34b73a46ebd438d8c13c3e9066b3d11c21fc2a2
Author: Jed Cunningham <[email protected]>
AuthorDate: Tue Feb 25 09:27:17 2025 -0700

    Remove `/webapp` prefix from new UI (#47041)
---
 airflow/api_fastapi/app.py                                   | 12 ++++++------
 airflow/api_fastapi/core_api/app.py                          |  4 ++--
 airflow/auth/managers/simple/ui/src/login/Login.tsx          |  2 +-
 airflow/ui/index.html                                        |  2 +-
 airflow/ui/src/router.tsx                                    |  5 ++---
 .../providers/amazon/aws/auth_manager/router/login.py        |  3 ++-
 .../tests/system/amazon/aws/tests/test_aws_auth_manager.py   |  2 +-
 .../tests/unit/amazon/aws/auth_manager/router/test_login.py  |  2 +-
 providers/fab/src/airflow/providers/fab/www/views.py         |  2 +-
 .../tests/unit/fab/www/views/test_views_custom_user_views.py |  2 +-
 tests/api_fastapi/core_api/routes/public/test_assets.py      |  4 ++--
 tests/api_fastapi/test_app.py                                | 11 +++++++++++
 12 files changed, 31 insertions(+), 20 deletions(-)

diff --git a/airflow/api_fastapi/app.py b/airflow/api_fastapi/app.py
index 3c812cde056..e2a883f0a6b 100644
--- a/airflow/api_fastapi/app.py
+++ b/airflow/api_fastapi/app.py
@@ -78,20 +78,20 @@ def create_app(apps: str = "all") -> FastAPI:
         root_path=root_path,
     )
 
+    if "execution" in apps_list or "all" in apps_list:
+        task_exec_api_app = create_task_execution_api_app()
+        init_error_handlers(task_exec_api_app)
+        app.mount("/execution", task_exec_api_app)
+
     if "core" in apps_list or "all" in apps_list:
         init_dag_bag(app)
-        init_views(app)
         init_plugins(app)
         init_auth_manager(app)
         init_flask_plugins(app)
+        init_views(app)  # Core views need to be the last routes added - it 
has a catch all route
         init_error_handlers(app)
         init_middlewares(app)
 
-    if "execution" in apps_list or "all" in apps_list:
-        task_exec_api_app = create_task_execution_api_app()
-        init_error_handlers(task_exec_api_app)
-        app.mount("/execution", task_exec_api_app)
-
     init_config(app)
 
     return app
diff --git a/airflow/api_fastapi/core_api/app.py 
b/airflow/api_fastapi/core_api/app.py
index 10f1cef175c..278209cf239 100644
--- a/airflow/api_fastapi/core_api/app.py
+++ b/airflow/api_fastapi/core_api/app.py
@@ -67,7 +67,7 @@ def init_views(app: FastAPI) -> None:
     templates = Jinja2Templates(directory=directory)
 
     app.mount(
-        "/webapp/static",
+        "/static",
         StaticFiles(
             directory=directory,
             html=True,
@@ -75,7 +75,7 @@ def init_views(app: FastAPI) -> None:
         name="webapp_static_folder",
     )
 
-    @app.get("/webapp/{rest_of_path:path}", response_class=HTMLResponse, 
include_in_schema=False)
+    @app.get("/{rest_of_path:path}", response_class=HTMLResponse, 
include_in_schema=False)
     def webapp(request: Request, rest_of_path: str):
         return templates.TemplateResponse(
             "/index.html",
diff --git a/airflow/auth/managers/simple/ui/src/login/Login.tsx 
b/airflow/auth/managers/simple/ui/src/login/Login.tsx
index e34fb76bd23..95718047b3b 100644
--- a/airflow/auth/managers/simple/ui/src/login/Login.tsx
+++ b/airflow/auth/managers/simple/ui/src/login/Login.tsx
@@ -36,7 +36,7 @@ type ExpandedApiError = {
 export const Login = () => {
     const onSuccess = (data: LoginResponse) => {
         // Redirect to index page with the token
-        globalThis.location.replace(`/webapp/?token=${data.jwt_token}`);
+        globalThis.location.replace(`/?token=${data.jwt_token}`);
     }
     const {createToken, error: err, isPending, setError} = 
useCreateToken({onSuccess});
     const error = err as ExpandedApiError;
diff --git a/airflow/ui/index.html b/airflow/ui/index.html
index b7fad8bc005..44a3b462f28 100644
--- a/airflow/ui/index.html
+++ b/airflow/ui/index.html
@@ -2,7 +2,7 @@
 <html lang="en" style="height: 100%">
   <head>
     <meta charset="UTF-8" />
-    <base href="{{ backend_server_base_url }}/webapp/" />
+    <base href="{{ backend_server_base_url }}/" />
     <link rel="icon" type="image/png" href="/static/pin_32.png" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <meta name="backend-server-base-url" content="{{ backend_server_base_url 
}}" />
diff --git a/airflow/ui/src/router.tsx b/airflow/ui/src/router.tsx
index 6efd6224756..c33feb6bbef 100644
--- a/airflow/ui/src/router.tsx
+++ b/airflow/ui/src/router.tsx
@@ -168,8 +168,7 @@ export const routerConfig = [
   },
 ];
 
-const locationPath = globalThis.window.location.pathname;
-const indexOf = locationPath.indexOf("webapp/");
-const basename = locationPath.slice(0, indexOf + 7);
+const baseUrl = document.querySelector("base")?.href ?? 
"http://localhost:9091/";;
+const basename = new URL(baseUrl).pathname;
 
 export const router = createBrowserRouter(routerConfig, { basename });
diff --git 
a/providers/amazon/src/airflow/providers/amazon/aws/auth_manager/router/login.py
 
b/providers/amazon/src/airflow/providers/amazon/aws/auth_manager/router/login.py
index aca770dca29..cac448555ad 100644
--- 
a/providers/amazon/src/airflow/providers/amazon/aws/auth_manager/router/login.py
+++ 
b/providers/amazon/src/airflow/providers/amazon/aws/auth_manager/router/login.py
@@ -79,7 +79,8 @@ def login_callback(request: Request):
         username=saml_auth.get_nameid(),
         email=attributes["email"][0] if "email" in attributes else None,
     )
-    return 
RedirectResponse(url=f"/webapp?token={get_auth_manager().get_jwt_token(user)}", 
status_code=303)
+    url = f"{conf.get('fastapi', 
'base_url')}/?token={get_auth_manager().get_jwt_token(user)}"
+    return RedirectResponse(url=url, status_code=303)
 
 
 def _init_saml_auth(request: Request) -> OneLogin_Saml2_Auth:
diff --git 
a/providers/amazon/tests/system/amazon/aws/tests/test_aws_auth_manager.py 
b/providers/amazon/tests/system/amazon/aws/tests/test_aws_auth_manager.py
index 163b4b52b4f..6e9c6bf3f55 100644
--- a/providers/amazon/tests/system/amazon/aws/tests/test_aws_auth_manager.py
+++ b/providers/amazon/tests/system/amazon/aws/tests/test_aws_auth_manager.py
@@ -194,4 +194,4 @@ class TestAwsAuthManager:
         response = client_admin_permissions.post("/auth/login_callback", 
follow_redirects=False)
         assert response.status_code == 303
         assert "location" in response.headers
-        assert "webapp?token=" in response.headers["location"]
+        assert "/?token=" in response.headers["location"]
diff --git 
a/providers/amazon/tests/unit/amazon/aws/auth_manager/router/test_login.py 
b/providers/amazon/tests/unit/amazon/aws/auth_manager/router/test_login.py
index f68eb7b7fe2..62731654b6b 100644
--- a/providers/amazon/tests/unit/amazon/aws/auth_manager/router/test_login.py
+++ b/providers/amazon/tests/unit/amazon/aws/auth_manager/router/test_login.py
@@ -117,7 +117,7 @@ class TestLoginRouter:
                 response = client.post("/auth/login_callback", 
follow_redirects=False)
                 assert response.status_code == 303
                 assert "location" in response.headers
-                assert 
response.headers["location"].startswith("/webapp?token=")
+                assert 
response.headers["location"].startswith("http://localhost:9091/?token=";)
 
     def test_login_callback_unsuccessful(self):
         with conf_vars(
diff --git a/providers/fab/src/airflow/providers/fab/www/views.py 
b/providers/fab/src/airflow/providers/fab/www/views.py
index 1f1c4e70add..9f2a73db632 100644
--- a/providers/fab/src/airflow/providers/fab/www/views.py
+++ b/providers/fab/src/airflow/providers/fab/www/views.py
@@ -70,7 +70,7 @@ class FabIndexView(IndexView):
     def index(self):
         if g.user is not None and g.user.is_authenticated:
             token = get_auth_manager().get_jwt_token(g.user)
-            return redirect(f"/webapp?token={token}", code=302)
+            return redirect(f"{conf.get('fastapi', 
'base_url')}/?token={token}", code=302)
         else:
             return super().index()
 
diff --git 
a/providers/fab/tests/unit/fab/www/views/test_views_custom_user_views.py 
b/providers/fab/tests/unit/fab/www/views/test_views_custom_user_views.py
index 2d363527646..a75079d8939 100644
--- a/providers/fab/tests/unit/fab/www/views/test_views_custom_user_views.py
+++ b/providers/fab/tests/unit/fab/www/views/test_views_custom_user_views.py
@@ -173,7 +173,7 @@ class TestSecurity:
             password="has_access",
         )
 
-        client.post(f"/users/delete/{user_to_delete.id}", 
follow_redirects=True)
+        client.post(f"/users/delete/{user_to_delete.id}", 
follow_redirects=False)
         assert bool(self.security_manager.get_user_by_id(user_to_delete.id)) 
is False
 
 
diff --git a/tests/api_fastapi/core_api/routes/public/test_assets.py 
b/tests/api_fastapi/core_api/routes/public/test_assets.py
index 743e01425ad..68c0026f8ca 100644
--- a/tests/api_fastapi/core_api/routes/public/test_assets.py
+++ b/tests/api_fastapi/core_api/routes/public/test_assets.py
@@ -1034,7 +1034,7 @@ class TestGetAssetQueuedEvents(TestQueuedEventEndpoint):
         asset_id = 1
         self._create_asset_dag_run_queues(dag_id, asset_id, session)
 
-        response = test_client.get(f"/public/assets/{asset_id}/queuedEvents/")
+        response = test_client.get(f"/public/assets/{asset_id}/queuedEvents")
         assert response.status_code == 200
         assert response.json() == {
             "queued_events": [
@@ -1099,7 +1099,7 @@ class 
TestDeleteDagAssetQueuedEvent(TestQueuedEventEndpoint):
         asset_id = 1
 
         response = test_client.delete(
-            f"/public/dags/{dag_id}/assets/{asset_id}/queuedEvents/",
+            f"/public/dags/{dag_id}/assets/{asset_id}/queuedEvents",
         )
 
         assert response.status_code == 404
diff --git a/tests/api_fastapi/test_app.py b/tests/api_fastapi/test_app.py
index e2a15988d4b..34ccfdfdd7b 100644
--- a/tests/api_fastapi/test_app.py
+++ b/tests/api_fastapi/test_app.py
@@ -92,3 +92,14 @@ def test_all_apps(mock_create_task_exec_api, 
mock_init_plugins, mock_init_views,
 
     # Assert that execution-related functions were also called
     mock_create_task_exec_api.assert_called_once_with()
+
+
+def test_catch_all_route_last(client):
+    """
+    Ensure the catch all route that returns the initial html is the last route 
in the fastapi app.
+
+    If it's not, it results in any routes/apps added afterwards to not be 
reachable, as the catch all
+    route responds instead.
+    """
+    test_app = client(apps="all").app
+    assert test_app.routes[-1].path == "/{rest_of_path:path}"

Reply via email to