jason810496 commented on code in PR #72715:
URL: https://github.com/apache/airflow/pull/72715#discussion_r4367445232


##########
airflow-core/src/airflow/api_fastapi/core_api/routes/public/dag_bundles.py:
##########
@@ -435,3 +440,26 @@ def get_dag_bundle_files(
         dag_bundle_files=[DagBundleFileResponse(**row) for row in 
ordered[start:end]],
         total_entries=len(ordered),
     )
+
+
+@dag_bundles_router.post(
+    "/{bundle_name}/refresh",
+    status_code=status.HTTP_202_ACCEPTED,
+    responses=create_openapi_http_exception_doc([status.HTTP_404_NOT_FOUND]),
+    dependencies=[Depends(action_logging()), 
Depends(requires_access_dag_bundle(method="PUT"))],
+)
+def refresh_dag_bundle(bundle_name: str, session: SessionDep) -> 
DagBundleRefreshResponse:
+    """Request an asynchronous Dag bundle refresh."""
+    try:
+        with session.begin_nested():
+            session.add(
+                DagPriorityParsingRequest(
+                    bundle_name=bundle_name,
+                    relative_fileloc=None,
+                )
+            )
+            session.flush()
+    except IntegrityError:
+        # The deterministic request key means an equivalent refresh is already 
pending.
+        pass

Review Comment:
   It would be better to raise out the 409 status code and mention the reason 
out explicitly instead of hiding it silently. 



##########
airflow-core/src/airflow/api_fastapi/core_api/security.py:
##########
@@ -228,6 +228,30 @@ def inner(
     return inner
 
 
+def requires_access_dag_bundle(method: ResourceMethod) -> Callable[[str, 
BaseUser, Session], None]:
+    """Authorize a Dag bundle action against its owning team."""
+
+    def inner(bundle_name: str, user: GetUserDep, session: SessionDep) -> None:
+        bundle = session.scalar(
+            select(DagBundleModel).where(
+                DagBundleModel.name == bundle_name,
+                DagBundleModel.active.is_(True),
+            )
+        )
+        if bundle is None:
+            raise HTTPException(status.HTTP_404_NOT_FOUND, "Dag bundle not 
found")

Review Comment:
   ```suggestion
               raise HTTPException(status.HTTP_404_NOT_FOUND, "Dag bundle is 
not found or inactive")
   ```



##########
airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_dag_bundles.py:
##########
@@ -944,3 +949,112 @@ def 
test_404_for_a_bundle_whose_dags_are_not_readable(self, dag_scoped_client):
 
     def test_404_for_an_unknown_bundle(self, dag_scoped_client):
         assert 
dag_scoped_client.get("/dagBundles/no_such_bundle/files").status_code == 404
+
+
+class TestRefreshDagBundle:
+    def setup_method(self):
+        clear_db_dag_parsing_requests()
+        clear_db_dags()
+        clear_db_dag_bundles()
+        clear_db_logs()
+
+    def teardown_method(self):
+        clear_db_dag_parsing_requests()
+        clear_db_dags()
+        clear_db_dag_bundles()
+        clear_db_logs()
+
+    @pytest.fixture
+    def auth_manager(self, test_client):
+        auth_manager = get_auth_manager()
+        with mock.patch.object(auth_manager, "is_authorized_dag"):
+            yield auth_manager
+
+    def test_global_bundle_refresh_uses_dag_permission(self, auth_manager, 
session, test_client):
+        session.add(DagBundleModel(name="global-bundle"))
+        session.commit()
+        auth_manager.is_authorized_dag.return_value = True
+
+        response = test_client.post("/dagBundles/global-bundle/refresh")
+
+        assert response.status_code == 202
+        assert response.json() == {"bundle_name": "global-bundle"}
+        auth_manager.is_authorized_dag.assert_called_with(
+            method="PUT",
+            details=DagDetails(id=None, team_name=None),
+            user=mock.ANY,
+        )
+        parsing_requests = 
session.scalars(select(DagPriorityParsingRequest)).all()
+        assert len(parsing_requests) == 1
+        assert parsing_requests[0].bundle_name == "global-bundle"
+        assert parsing_requests[0].relative_fileloc is None
+        _check_last_log(session, dag_id=None, event="refresh_dag_bundle", 
logical_date=None)
+
+    def test_repeated_refresh_uses_existing_pending_request(self, 
auth_manager, session, test_client):
+        session.add(DagBundleModel(name="global-bundle"))
+        session.commit()
+        auth_manager.is_authorized_dag.return_value = True
+
+        first_response = test_client.post("/dagBundles/global-bundle/refresh")
+        second_response = test_client.post("/dagBundles/global-bundle/refresh")
+
+        assert first_response.status_code == second_response.status_code == 202

Review Comment:
   Then we will need to update the test case to expect 409 status code for the 
second request.



##########
airflow-core/src/airflow/api_fastapi/core_api/routes/public/dag_bundles.py:
##########
@@ -435,3 +440,26 @@ def get_dag_bundle_files(
         dag_bundle_files=[DagBundleFileResponse(**row) for row in 
ordered[start:end]],
         total_entries=len(ordered),
     )
+
+
+@dag_bundles_router.post(

Review Comment:
   It should be `put` instead of `post` btw.



##########
airflow-core/src/airflow/api_fastapi/core_api/security.py:
##########
@@ -228,6 +228,30 @@ def inner(
     return inner
 
 
+def requires_access_dag_bundle(method: ResourceMethod) -> Callable[[str, 
BaseUser, Session], None]:

Review Comment:
   Please reference the following authorization methods. 
   
   
https://github.com/apache/airflow/blob/e87c35e344ee4ba6ef41d879d401e2b77d4dcf86/airflow-core/src/airflow/api_fastapi/core_api/security.py#L231-L238
   
   
   
https://github.com/apache/airflow/blob/e87c35e344ee4ba6ef41d879d401e2b77d4dcf86/airflow-core/src/airflow/api_fastapi/core_api/security.py#L201-L228



##########
airflow-core/src/airflow/api_fastapi/core_api/routes/public/dag_bundles.py:
##########
@@ -435,3 +440,26 @@ def get_dag_bundle_files(
         dag_bundle_files=[DagBundleFileResponse(**row) for row in 
ordered[start:end]],
         total_entries=len(ordered),
     )
+
+
+@dag_bundles_router.post(
+    "/{bundle_name}/refresh",
+    status_code=status.HTTP_202_ACCEPTED,
+    responses=create_openapi_http_exception_doc([status.HTTP_404_NOT_FOUND]),
+    dependencies=[Depends(action_logging()), 
Depends(requires_access_dag_bundle(method="PUT"))],
+)
+def refresh_dag_bundle(bundle_name: str, session: SessionDep) -> 
DagBundleRefreshResponse:
+    """Request an asynchronous Dag bundle refresh."""
+    try:
+        with session.begin_nested():
+            session.add(
+                DagPriorityParsingRequest(
+                    bundle_name=bundle_name,
+                    relative_fileloc=None,
+                )
+            )
+            session.flush()
+    except IntegrityError:
+        # The deterministic request key means an equivalent refresh is already 
pending.
+        pass
+    return DagBundleRefreshResponse(bundle_name=bundle_name)

Review Comment:
   I think we should follow the convention to return empty string instead of 
bundle name.
   
   
https://github.com/apache/airflow/blob/c5faeb1555b4076739665b9434f616db37559727/airflow-core/src/airflow/api_fastapi/core_api/routes/public/dag_parsing.py#L32-L44
   
   There's no need to return the bundle_name back to user again since they pass 
bundle_name itself to API server so they should know it already.



-- 
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