From 557f4e4477349f67fb52c55536c6d2e5a1fce4f8 Mon Sep 17 00:00:00 2001 From: Andrew Herrington Date: Fri, 10 Dec 2021 19:48:28 -0600 Subject: [PATCH] generic response checking method and get_check --- src/healthchecks_io/client/_abstract.py | 28 +++++++++++++++++- src/healthchecks_io/client/asyncclient.py | 35 ++++++++++++++++------- src/healthchecks_io/client/exceptions.py | 5 ++++ tests/client/test_async.py | 12 ++++++++ 4 files changed, 68 insertions(+), 12 deletions(-) diff --git a/src/healthchecks_io/client/_abstract.py b/src/healthchecks_io/client/_abstract.py index f253c0a..334ca19 100644 --- a/src/healthchecks_io/client/_abstract.py +++ b/src/healthchecks_io/client/_abstract.py @@ -12,9 +12,10 @@ from urllib.parse import urljoin from urllib.parse import urlparse from weakref import finalize -from httpx import Client +from httpx import Client, Response from healthchecks_io.schemas import checks +from .exceptions import HCAPIAuthError, HCAPIError, CheckNotFoundError class AbstractClient(ABC): @@ -79,6 +80,31 @@ class AbstractClient(ABC): """ return self._client.is_closed + @staticmethod + def check_response(response: Response) -> Response: + """Checks a healthchecks.io response. + + Args: + response (Response): a response from the healthchecks.io api + + Raises: + HCAPIAuthError: Raised when status_code == 401 or 403 + HCAPIError: Raised when status_code is 5xx + + Returns: + Response: the passed in response object + """ + if response.status_code == 401 or response.status_code == 403: + raise HCAPIAuthError("Auth failure when getting checks") + + if str(response.status_code).startswith("5"): + raise HCAPIError( + f"Error when reaching out to HC API at {response.request.url}. " + f"Status Code {response.status_code}. Response {response.text}" + ) + + return response + @staticmethod def _add_url_params(url: str, params: Dict[str, str], replace: bool = True): """Add GET params to provided URL being aware of existing. diff --git a/src/healthchecks_io/client/asyncclient.py b/src/healthchecks_io/client/asyncclient.py index e845602..88f640d 100644 --- a/src/healthchecks_io/client/asyncclient.py +++ b/src/healthchecks_io/client/asyncclient.py @@ -6,7 +6,7 @@ from typing import Optional from httpx import AsyncClient as HTTPXAsyncClient from ._abstract import AbstractClient -from .exceptions import HCAPIAuthError +from .exceptions import HCAPIAuthError, CheckNotFoundError from .exceptions import HCAPIError from healthchecks_io import VERSION from healthchecks_io.schemas import checks @@ -69,18 +69,31 @@ class AsyncClient(AbstractClient): request_url, {"tag": tag}, replace=False ) - response = await self._client.get(request_url) - - if response.status_code == 401: - raise HCAPIAuthError("Auth failure when getting checks") - - if response.status_code != 200: - raise HCAPIError( - f"Error when reaching out to HC API at {request_url}. " - f"Status Code {response.status_code}. Response {response.text}" - ) + response = self.check_response(await self._client.get(request_url)) return [ checks.Check.from_api_result(check_data) for check_data in response.json()["checks"] ] + + async def get_check(self, check_id: str) -> checks.Check: + """Get a single check by id. + + check_id can either be a check uuid if using a read/write api key + or a unique key if using a read only api key. + + Args: + check_id (str): check's uuid or unique id + + Returns: + checks.Check: the check + + Raises: + CheckNotFoundError: when no check with check_id is found + """ + request_url = self._get_api_request_url(f"checks/{check_id}") + response = self.check_response(await self._client.get(request_url)) + if response.status_code == 404: + raise CheckNotFoundError(f"{check_id} not found at {request_url}") + return checks.Check.from_api_result(response.json()) + diff --git a/src/healthchecks_io/client/exceptions.py b/src/healthchecks_io/client/exceptions.py index 4356660..751b5b2 100644 --- a/src/healthchecks_io/client/exceptions.py +++ b/src/healthchecks_io/client/exceptions.py @@ -11,3 +11,8 @@ class HCAPIAuthError(HCAPIError): """Thrown when we fail to auth to the Healthchecks api.""" ... + +class CheckNotFoundError(HCAPIError): + """Thrown when getting a check returns a 404.""" + + ... \ No newline at end of file diff --git a/tests/client/test_async.py b/tests/client/test_async.py index 6333df5..91fca06 100644 --- a/tests/client/test_async.py +++ b/tests/client/test_async.py @@ -74,3 +74,15 @@ def test_finalizer_closes(test_async_client): assert not test_async_client.is_closed test_async_client._finalizer_method() assert test_async_client.is_closed + + +@pytest.mark.asyncio +@pytest.mark.respx +async def test_get_check_200(fake_check_api_result, respx_mock, test_async_client): + assert test_async_client._client is not None + checks_url = urljoin(test_async_client._api_url, "checks/test") + respx_mock.get(checks_url).mock( + return_value=Response(status_code=200, json=fake_check_api_result) + ) + check = await test_async_client.get_check(check_id="test") + assert check.name == fake_check_api_result["name"]