From 48ca717e3a38d23feea3d5d9cf9651ef6bb77330 Mon Sep 17 00:00:00 2001 From: Kaio Oliveira Date: Mon, 2 Jun 2025 14:36:50 -0300 Subject: [PATCH] feat: [AAP-46254] trigger project resync after project update This commit causes auto sync/import of a project when one of its url or scm_branch/tag/commit is updated, without the user having to manually trigger the sync via the UI/API. --- src/aap_eda/api/views/project.py | 31 +++++++++++++++++- tests/integration/api/test_project.py | 45 +++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/src/aap_eda/api/views/project.py b/src/aap_eda/api/views/project.py index 1943bd116..d424b9630 100644 --- a/src/aap_eda/api/views/project.py +++ b/src/aap_eda/api/views/project.py @@ -199,7 +199,8 @@ def retrieve(self, request, pk): None, description="Update failed with integrity checking.", ), - }, + } + | RedisDependencyMixin.redis_unavailable_response(), ) def partial_update(self, request, pk): project = self.get_object() @@ -234,6 +235,34 @@ def partial_update(self, request, pk): ) ) + if {"scm_branch", "scm_refspec", "url"}.intersection( + update_fields + ) and ( + project.import_state + not in [ + models.Project.ImportState.PENDING, + models.Project.ImportState.RUNNING, + ] + ): + # check if redis is available + self.redis_is_available() + + try: + job_id = tasks.sync_project(project.id) + except redis.ConnectionError: + return RedisDependencyMixin.redis_unavailable_response() + + project.import_state = models.Project.ImportState.PENDING + if job_id: + project.import_task_id = job_id + + logger.info( + f"Triggered import/sync task {job_id}" + f" for project {project.id}" + ) + + project.save() + return Response(serializers.ProjectSerializer(project).data) @extend_schema( diff --git a/tests/integration/api/test_project.py b/tests/integration/api/test_project.py index f4b0431db..68feb854f 100644 --- a/tests/integration/api/test_project.py +++ b/tests/integration/api/test_project.py @@ -839,12 +839,57 @@ def test_partial_update_project_url( assert response.status_code == status.HTTP_200_OK assert response.json()["url"] == new_url + assert response.json()["import_state"] == "pending" new_project.refresh_from_db() assert new_project.url == new_url assert new_project.url != original_url +@pytest.mark.django_db +def test_partial_update_project_scm_branch( + new_project: models.Project, + admin_client: APIClient, +): + original_scm_branch = new_project.scm_branch + new_scm_branch = "dev" + + response = admin_client.patch( + f"{api_url_v1}/projects/{new_project.id}/", + data={"scm_branch": new_scm_branch}, + ) + + assert response.status_code == status.HTTP_200_OK + assert response.json()["scm_branch"] == new_scm_branch + assert response.json()["import_state"] == "pending" + + new_project.refresh_from_db() + assert new_project.scm_branch == new_scm_branch + assert new_project.scm_branch != original_scm_branch + + +@pytest.mark.django_db +def test_partial_update_project_scm_refspec( + new_project: models.Project, + admin_client: APIClient, +): + original_scm_refspec = new_project.scm_refspec + new_scm_refspec = "path/to/testref" + + response = admin_client.patch( + f"{api_url_v1}/projects/{new_project.id}/", + data={"scm_refspec": new_scm_refspec}, + ) + + assert response.status_code == status.HTTP_200_OK + assert response.json()["scm_refspec"] == new_scm_refspec + assert response.json()["import_state"] == "pending" + + new_project.refresh_from_db() + assert new_project.scm_refspec == new_scm_refspec + assert new_project.scm_refspec != original_scm_refspec + + @pytest.mark.django_db def test_delete_project( new_project: models.Project,