From 7535661023eda803217ecfa8975230d8796f86a5 Mon Sep 17 00:00:00 2001 From: Alexander Hungenberg Date: Thu, 23 Nov 2023 17:01:12 +0100 Subject: [PATCH] adding support for foxops 2.3 endpoints (#36) adding support for new patch endpoint --- src/foxops_client/client_async.py | 45 ++++++++++++++++++++++++------- src/foxops_client/client_sync.py | 27 +++++++++++++++---- tests/conftest.py | 2 ++ tests/infrastructure/foxops.py | 13 ++++++--- tests/infrastructure/gitlab.py | 2 +- tests/test_client.py | 22 ++++++++++----- 6 files changed, 86 insertions(+), 25 deletions(-) diff --git a/src/foxops_client/client_async.py b/src/foxops_client/client_async.py index 6a284d8..837175a 100644 --- a/src/foxops_client/client_async.py +++ b/src/foxops_client/client_async.py @@ -98,20 +98,47 @@ async def delete_incarnation(self, incarnation_id: int): self._handle_unexpected_response(resp) raise ValueError("unexpected response") - async def update_incarnation( + async def patch_incarnation( self, incarnation_id: int, automerge: bool, - template_repository_version: str | None = None, - template_data: dict[str, str] | None = None, + requested_version: str | None = None, + requested_data: dict[str, Any] | None = None, + ): + data: dict[str, Any] = { + "automerge": automerge, + } + if requested_version is not None: + data["requested_version"] = requested_version + if requested_data is not None: + data["requested_data"] = requested_data + + resp = await self.retry_function(self.client.patch)(f"/api/incarnations/{incarnation_id}", json=data) + + match resp.status_code: + case httpx.codes.OK: + return IncarnationWithDetails.from_dict(resp.json()) + case httpx.codes.NOT_FOUND: + raise IncarnationDoesNotExistError(resp.json()["message"]) + case httpx.codes.BAD_REQUEST | httpx.codes.CONFLICT: + self.log.error(f"received error from FoxOps API: {resp.status_code} {resp.headers} {resp.text}") + raise FoxopsApiError(resp.json()["message"]) + + self._handle_unexpected_response(resp) + raise ValueError("unexpected response") + + async def put_incarnation( + self, + incarnation_id: int, + automerge: bool, + template_repository_version: str, + template_data: dict[str, Any], ) -> IncarnationWithDetails: data: dict[str, Any] = { "automerge": automerge, + "template_repository_version": template_repository_version, + "template_data": template_data, } - if template_repository_version is not None: - data["template_repository_version"] = template_repository_version - if template_data is not None: - data["template_data"] = template_data resp = await self.retry_function(self.client.put)(f"/api/incarnations/{incarnation_id}", json=data) @@ -132,7 +159,7 @@ async def create_incarnation( incarnation_repository: str, template_repository: str, template_repository_version: str, - template_data: dict[str, str], + template_data: dict[str, Any], target_directory: str | None = None, automerge: bool | None = None, ) -> IncarnationWithDetails: @@ -152,7 +179,7 @@ async def create_incarnation( match resp.status_code: case httpx.codes.CREATED: return IncarnationWithDetails.from_dict(resp.json()) - case httpx.codes.BAD_REQUEST: + case httpx.codes.BAD_REQUEST | httpx.codes.CONFLICT: self.log.error(f"received error from FoxOps API: {resp.status_code} {resp.headers} {resp.text}") raise FoxopsApiError(resp.json()["message"]) diff --git a/src/foxops_client/client_sync.py b/src/foxops_client/client_sync.py index 9a276e7..4a716eb 100644 --- a/src/foxops_client/client_sync.py +++ b/src/foxops_client/client_sync.py @@ -1,4 +1,5 @@ import asyncio +from typing import Any from foxops_client.client_async import AsyncFoxopsClient from foxops_client.types import Incarnation, IncarnationWithDetails @@ -35,15 +36,31 @@ def get_incarnation(self, incarnation_id: int) -> IncarnationWithDetails: def delete_incarnation(self, incarnation_id: int): return self.loop.run_until_complete(self.client.delete_incarnation(incarnation_id)) - def update_incarnation( + def patch_incarnation( self, incarnation_id: int, automerge: bool, - template_repository_version: str | None = None, - template_data: dict[str, str] | None = None, + requested_version: str | None = None, + requested_data: dict[str, Any] | None = None, + ): + return self.loop.run_until_complete( + self.client.patch_incarnation( + incarnation_id, + automerge, + requested_version=requested_version, + requested_data=requested_data, + ) + ) + + def put_incarnation( + self, + incarnation_id: int, + automerge: bool, + template_repository_version: str, + template_data: dict[str, Any], ) -> IncarnationWithDetails: return self.loop.run_until_complete( - self.client.update_incarnation( + self.client.put_incarnation( incarnation_id, automerge, template_repository_version=template_repository_version, @@ -56,7 +73,7 @@ def create_incarnation( incarnation_repository: str, template_repository: str, template_repository_version: str, - template_data: dict[str, str], + template_data: dict[str, Any], target_directory: str | None = None, automerge: bool | None = None, ) -> IncarnationWithDetails: diff --git a/tests/conftest.py b/tests/conftest.py index bdd9fbd..426e4ac 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,6 +3,7 @@ from infrastructure.foxops import ( FOXOPS_STATIC_TOKEN, foxops_container, + foxops_database_initialization, foxops_host_port, foxops_host_url, foxops_image, @@ -35,6 +36,7 @@ foxops_host_url, foxops_image, foxops_secrets_volume, + foxops_database_initialization, gitlab_access_token, gitlab_access_token_binary, gitlab_api_client, diff --git a/tests/infrastructure/foxops.py b/tests/infrastructure/foxops.py index c75b0f1..ad97a2b 100644 --- a/tests/infrastructure/foxops.py +++ b/tests/infrastructure/foxops.py @@ -7,13 +7,13 @@ FOXOPS_STATIC_TOKEN = "dummy" # Images -foxops_image = fetch(repository="ghcr.io/roche/foxops:e61d4c1e6d7e77bca5df14dfe803030fcb52a0be") +foxops_image = fetch(repository="ghcr.io/roche/foxops:v2.3.1") # Volumes foxops_secrets_volume = volume( name=f"{NAME_PREFIX}_foxops_secrets", initial_content={ - "foxops_gitlab_token": gitlab_access_token_binary, + "foxops_hoster_gitlab_token": gitlab_access_token_binary, }, scope="session", ) @@ -36,7 +36,8 @@ def ready(self): image="{foxops_image.id}", name=NAME_PREFIX + "_foxops", environment={ - "FOXOPS_GITLAB_ADDRESS": gitlab_docker_network_url, + "FOXOPS_HOSTER_TYPE": "gitlab", + "FOXOPS_HOSTER_GITLAB_ADDRESS": gitlab_docker_network_url, "FOXOPS_STATIC_TOKEN": FOXOPS_STATIC_TOKEN, }, volumes={ @@ -51,6 +52,12 @@ def ready(self): ) +@fixture(scope="session", autouse=True) +def foxops_database_initialization(foxops_container: FoxOpsContainer): + foxops_container.exec_run("rm /home/foxops/test.db") + foxops_container.exec_run("alembic upgrade head") + + @fixture(scope="session") def foxops_host_port(foxops_container: FoxOpsContainer): return foxops_container.ports[FOXOPS_LISTEN_PORT][0] diff --git a/tests/infrastructure/gitlab.py b/tests/infrastructure/gitlab.py index aa4f9cf..372f776 100644 --- a/tests/infrastructure/gitlab.py +++ b/tests/infrastructure/gitlab.py @@ -54,7 +54,7 @@ def ready(self): # we want gitlab to return a 200 several times in a row. # especially in the github runners it can act flaky otherwise. - for _ in range(3): + for _ in range(15): response = httpx.get(self.host_url, follow_redirects=True) if response.status_code != 200: return False diff --git a/tests/test_client.py b/tests/test_client.py index 002400e..dafe36d 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -52,7 +52,7 @@ def test_create_incarnation_with_conflicting_existing_incarnation(incarnation, f ) # THEN - assert e.value.message.find("already initialized") != -1 + assert e.value.message.find("is already a foxops incarnation") != -1 def test_delete_incarnation(incarnation, foxops_client: FoxopsClient): @@ -100,18 +100,26 @@ def test_list_incarnation_with_non_existing_incarnation(foxops_client): foxops_client.list_incarnations(incarnation_repository="nonexisting", target_directory=".") -def test_update_incarnation_with_template_data_change_and_no_automerge(incarnation, foxops_client): +def test_put_incarnation_returns_error_when_variables_are_missing(incarnation, foxops_client): # WHEN - response = foxops_client.update_incarnation( + with pytest.raises(FoxopsApiError): + foxops_client.put_incarnation( + incarnation_id=incarnation.id, + automerge=False, + template_repository_version="main", + template_data={}, + ) + + +def test_patch_incarnation_with_template_data_change_and_no_automerge(incarnation, foxops_client): + # WHEN + response = foxops_client.patch_incarnation( incarnation_id=incarnation.id, automerge=False, - template_data={"input_variable": "bar"}, + requested_data={"input_variable": "bar"}, ) # THEN - # because we didn't merge, the template data should be the same as in the original incarnation details - assert response.template_data == incarnation.template_data - assert response.commit_sha is not None assert response.commit_url is not None