pierrejeambrun commented on code in PR #52408:
URL: https://github.com/apache/airflow/pull/52408#discussion_r2176905905


##########
airflow-core/src/airflow/ui/src/layouts/Nav/PluginMenus.tsx:
##########
@@ -30,32 +30,27 @@ import { PluginMenuItem } from "./PluginMenuItem";
 
 export const PluginMenus = () => {
   const { t: translate } = useTranslation("common");
-  const { data } = usePluginServiceGetPlugins();
+  const menuPlugins = (useConfig("plugins_extra_menu_items") as 
Array<ExternalViewResponse>) ?? [];
 

Review Comment:
   We shouldn't need a hard casting like that. "as"
   



##########
airflow-core/src/airflow/api_fastapi/core_api/routes/ui/config.py:
##########
@@ -46,17 +48,30 @@
     responses=create_openapi_http_exception_doc([status.HTTP_404_NOT_FOUND]),
     dependencies=[Depends(requires_authenticated())],
 )
-def get_configs() -> ConfigResponse:
+async def get_configs(user: GetUserDep) -> ConfigResponse:
     """Get configs for UI."""
     config = {key: conf.get("api", key) for key in API_CONFIG_KEYS}
 
     task_log_reader = TaskLogReader()
+    plugins_manager.initialize_ui_plugins()

Review Comment:
   You do not need to do that, this is done at the init of the FastAPI app, can 
you remove it?



##########
airflow-core/src/airflow/api_fastapi/core_api/routes/ui/config.py:
##########
@@ -46,17 +48,29 @@
     responses=create_openapi_http_exception_doc([status.HTTP_404_NOT_FOUND]),
     dependencies=[Depends(requires_authenticated())],
 )
-def get_configs() -> ConfigResponse:
+async def get_configs(user: GetUserDep) -> ConfigResponse:

Review Comment:
   > get_config depends on GetUserDep which is async and as per FastAPI rule: 
any route function that calls an async dependency must also be async.
   
   Where do you get this information from ? I thend to think that it does not 
matter and this is confirmed by FastAPI documentation:
   https://fastapi.tiangolo.com/tutorial/dependencies/#to-async-or-not-to-async
   



##########
airflow-core/src/airflow/api_fastapi/core_api/routes/ui/config.py:
##########
@@ -46,17 +48,29 @@
     responses=create_openapi_http_exception_doc([status.HTTP_404_NOT_FOUND]),
     dependencies=[Depends(requires_authenticated())],
 )
-def get_configs() -> ConfigResponse:
+async def get_configs(user: GetUserDep) -> ConfigResponse:
     """Get configs for UI."""
     config = {key: conf.get("api", key) for key in API_CONFIG_KEYS}
 
     task_log_reader = TaskLogReader()
+    plugins_manager.initialize_ui_plugins()
+
+    auth_manager = get_auth_manager()
+    authorized_plugins = []
+    if plugins_manager.external_views:
+        for view in plugins_manager.external_views:
+            if auth_manager.is_authorized_custom_view(method="GET", 
resource_name=view["name"], user=user):
+                authorized_plugins.append(view)

Review Comment:
   I think we are mixing things up. Why would plugin permissions be in the 
`auth manger custom views permissions`?  Those are not related, permissions to 
access plugins is `AccessView.PLUGINS`. 
   
   Also we assume that this subpart of the plugins (external views in the nav) 
are public, so everybody can load the UI or access it.



##########
airflow-core/src/airflow/api_fastapi/core_api/routes/ui/config.py:
##########
@@ -46,17 +48,30 @@
     responses=create_openapi_http_exception_doc([status.HTTP_404_NOT_FOUND]),
     dependencies=[Depends(requires_authenticated())],
 )
-def get_configs() -> ConfigResponse:
+async def get_configs(user: GetUserDep) -> ConfigResponse:
     """Get configs for UI."""
     config = {key: conf.get("api", key) for key in API_CONFIG_KEYS}
 
     task_log_reader = TaskLogReader()
+    plugins_manager.initialize_ui_plugins()
+
+    # First, we check which of the available external plugin views the current 
user is authorized to see.
+    auth_manager = get_auth_manager()
+    authorized_plugins = []

Review Comment:
   I think we should rename this, those are external_views, not plugins.



##########
airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_config.py:
##########
@@ -56,7 +59,8 @@ def mock_config_data():
         yield
 
 
-class TestGetConfig:
[email protected]_plugin_manager(plugins=[])
+class TestGetConfigWithNoPlugins:

Review Comment:
   1 class per endpoint. Multiple method for different test cases. We shouldn't 
 have two classes there.



##########
airflow-core/src/airflow/ui/src/layouts/Nav/PluginMenus.tsx:
##########
@@ -30,32 +30,27 @@ import { PluginMenuItem } from "./PluginMenuItem";
 
 export const PluginMenus = () => {
   const { t: translate } = useTranslation("common");
-  const { data } = usePluginServiceGetPlugins();
+  const menuPlugins = (useConfig("plugins_extra_menu_items") as 
Array<ExternalViewResponse>) ?? [];
 
-  const menuPlugins =
-    data?.plugins.flatMap((plugin) => plugin.external_views).filter((view) => 
view.destination === "nav") ??
-    [];
+  const directExternalViews = menuPlugins.filter((p: ExternalViewResponse) => 
!p.is_menu_item);
+  const menuExternalViews = menuPlugins.filter((p: ExternalViewResponse) => 
p.is_menu_item);

Review Comment:
   I don't understand what's going on here. What is `is_menu_item` attribute, 
that does not exist.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to