This commit is contained in:
Andrew Herrington
2021-12-10 22:43:32 -06:00
parent 7c3f263b5a
commit dffb9be1bf
6 changed files with 90 additions and 79 deletions

View File

@@ -1,6 +1,6 @@
[flake8]
select = B,B9,C,D,DAR,E,F,N,RST,S,W
ignore = E203,E501,RST201,RST203,RST301,W503,B902,N805
ignore = E203,E501,RST201,RST203,RST301,W503,B902,N805,DAR402
max-line-length = 119
max-complexity = 10
docstring-convention = google

View File

@@ -12,10 +12,14 @@ from urllib.parse import urljoin
from urllib.parse import urlparse
from weakref import finalize
from httpx import Client, Response
from httpx import Client
from httpx import Response
from .exceptions import BadAPIRequestError
from .exceptions import CheckNotFoundError
from .exceptions import HCAPIAuthError
from .exceptions import HCAPIError
from healthchecks_io.schemas import checks
from .exceptions import HCAPIAuthError, HCAPIError, CheckNotFoundError, BadAPIRequestError
class AbstractClient(ABC):
@@ -91,6 +95,7 @@ class AbstractClient(ABC):
HCAPIAuthError: Raised when status_code == 401 or 403
HCAPIError: Raised when status_code is 5xx
CheckNotFoundError: Raised when status_code is 404
BadAPIRequestError: Raised when status_code is 400
Returns:
Response: the passed in response object
@@ -108,7 +113,9 @@ class AbstractClient(ABC):
raise CheckNotFoundError(f"CHeck not found at {response.request.url}")
if response.status_code == 400:
raise BadAPIRequestError(f"Bad request when requesting {response.request.url}. {response.text}")
raise BadAPIRequestError(
f"Bad request when requesting {response.request.url}. {response.text}"
)
return response

View File

@@ -117,7 +117,7 @@ class AsyncClient(AbstractClient):
"""
request_url = self._get_api_request_url(f"checks/{check_id}/pause")
response = self.check_response(await self._client.post(request_url , data={}))
response = self.check_response(await self._client.post(request_url, data={}))
return checks.Check.from_api_result(response.json())
async def delete_check(self, check_id: str) -> checks.Check:
@@ -167,9 +167,14 @@ class AsyncClient(AbstractClient):
for check_data in response.json()["pings"]
]
async def get_check_flips(self, check_id: str, seconds: Optional[int] = None, start: Optional[int] = None, end: Optional[int] = None) -> List[checks.CheckStatuses]:
"""
Returns a list of "flips" this check has experienced.
async def get_check_flips(
self,
check_id: str,
seconds: Optional[int] = None,
start: Optional[int] = None,
end: Optional[int] = None,
) -> List[checks.CheckStatuses]:
"""Returns a list of "flips" this check has experienced.
A flip is a change of status (from "down" to "up," or from "up" to "down").
@@ -190,16 +195,13 @@ class AsyncClient(AbstractClient):
"""
params = dict()
if seconds is not None and seconds >=0:
params['seconds'] = seconds
if seconds is not None and seconds >= 0:
params["seconds"] = seconds
if start is not None and start >= 0:
params['start'] = start
params["start"] = start
if end is not None and end >= 0:
params['end'] = end
params["end"] = end
request_url = self._get_api_request_url(f"checks/{check_id}/flips/", params)
response = self.check_response(await self._client.get(request_url))
return [
checks.CheckStatuses(**status_data)
for status_data in response.json()
]
return [checks.CheckStatuses(**status_data) for status_data in response.json()]

View File

@@ -12,11 +12,13 @@ class HCAPIAuthError(HCAPIError):
...
class CheckNotFoundError(HCAPIError):
"""Thrown when getting a check returns a 404."""
...
class BadAPIRequestError(HCAPIError):
"""Thrown when an api request returns a 400."""

View File

@@ -6,8 +6,10 @@ from httpx import AsyncClient as HTTPXAsyncClient
from httpx import Response
from healthchecks_io.client import AsyncClient
from healthchecks_io.client.exceptions import BadAPIRequestError
from healthchecks_io.client.exceptions import CheckNotFoundError
from healthchecks_io.client.exceptions import HCAPIAuthError
from healthchecks_io.client.exceptions import HCAPIError, CheckNotFoundError, BadAPIRequestError
from healthchecks_io.client.exceptions import HCAPIError
@pytest.mark.asyncio
@@ -93,9 +95,7 @@ async def test_get_check_200(fake_check_api_result, respx_mock, test_async_clien
async def test_check_get_404(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=404)
)
respx_mock.get(checks_url).mock(return_value=Response(status_code=404))
with pytest.raises(CheckNotFoundError):
await test_async_client.get_check("test")
@@ -117,9 +117,7 @@ async def test_pause_check_200(fake_check_api_result, respx_mock, test_async_cli
async def test_check_pause_404(respx_mock, test_async_client):
assert test_async_client._client is not None
checks_url = urljoin(test_async_client._api_url, "checks/test/pause")
respx_mock.post(checks_url).mock(
return_value=Response(status_code=404)
)
respx_mock.post(checks_url).mock(return_value=Response(status_code=404))
with pytest.raises(CheckNotFoundError):
await test_async_client.pause_check("test")
@@ -141,28 +139,33 @@ async def test_delete_check_200(fake_check_api_result, respx_mock, test_async_cl
async def test_delete_pause404(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.delete(checks_url).mock(
return_value=Response(status_code=404)
)
respx_mock.delete(checks_url).mock(return_value=Response(status_code=404))
with pytest.raises(CheckNotFoundError):
await test_async_client.delete_check("test")
@pytest.mark.asyncio
@pytest.mark.respx
async def test_get_check_pings_200(fake_check_pings_api_result, respx_mock, test_async_client):
async def test_get_check_pings_200(
fake_check_pings_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/pings/")
respx_mock.get(checks_url).mock(
return_value=Response(status_code=200, json={"pings": fake_check_pings_api_result})
return_value=Response(
status_code=200, json={"pings": fake_check_pings_api_result}
)
)
pings = await test_async_client.get_check_pings("test")
assert len(pings) == len(fake_check_pings_api_result)
assert pings[0].type == fake_check_pings_api_result[0]['type']
assert pings[0].type == fake_check_pings_api_result[0]["type"]
@pytest.mark.asyncio
@pytest.mark.respx
async def test_get_check_flips_200(fake_check_flips_api_result, respx_mock, test_async_client):
async def test_get_check_flips_200(
fake_check_flips_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/flips/")
respx_mock.get(checks_url).mock(
@@ -170,29 +173,33 @@ async def test_get_check_flips_200(fake_check_flips_api_result, respx_mock, test
)
flips = await test_async_client.get_check_flips("test")
assert len(flips) == len(fake_check_flips_api_result)
assert flips[0].up == fake_check_flips_api_result[0]['up']
assert flips[0].up == fake_check_flips_api_result[0]["up"]
@pytest.mark.asyncio
@pytest.mark.respx
async def test_get_check_flips_params_200(fake_check_flips_api_result, respx_mock, test_async_client):
async def test_get_check_flips_params_200(
fake_check_flips_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/flips/?seconds=1&start=1&end=1")
checks_url = urljoin(
test_async_client._api_url, "checks/test/flips/?seconds=1&start=1&end=1"
)
respx_mock.get(checks_url).mock(
return_value=Response(status_code=200, json=fake_check_flips_api_result)
)
flips = await test_async_client.get_check_flips("test", seconds=1, start=1, end=1)
assert len(flips) == len(fake_check_flips_api_result)
assert flips[0].up == fake_check_flips_api_result[0]['up']
assert flips[0].up == fake_check_flips_api_result[0]["up"]
@pytest.mark.asyncio
@pytest.mark.respx
async def test_get_check_flips_400(fake_check_flips_api_result, respx_mock, test_async_client):
async def test_get_check_flips_400(
fake_check_flips_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/flips/")
respx_mock.get(checks_url).mock(
return_value=Response(status_code=400)
)
respx_mock.get(checks_url).mock(return_value=Response(status_code=400))
with pytest.raises(BadAPIRequestError):
await test_async_client.get_check_flips("test")

View File

@@ -87,6 +87,7 @@ def test_async_client():
yield AsyncClient(api_key="test", api_url="https://localhost/api")
@pytest.fixture
def fake_check_pings_api_result():
return [
@@ -98,7 +99,7 @@ def fake_check_pings_api_result():
"remote_addr": "192.0.2.0",
"method": "GET",
"ua": "curl/7.68.0",
"duration": 2.896736
"duration": 2.896736,
},
{
"type": "start",
@@ -107,7 +108,7 @@ def fake_check_pings_api_result():
"scheme": "http",
"remote_addr": "192.0.2.0",
"method": "GET",
"ua": "curl/7.68.0"
"ua": "curl/7.68.0",
},
{
"type": "success",
@@ -117,7 +118,7 @@ def fake_check_pings_api_result():
"remote_addr": "192.0.2.0",
"method": "GET",
"ua": "curl/7.68.0",
"duration": 2.997976
"duration": 2.997976,
},
{
"type": "start",
@@ -126,23 +127,15 @@ def fake_check_pings_api_result():
"scheme": "http",
"remote_addr": "192.0.2.0",
"method": "GET",
"ua": "curl/7.68.0"
}
"ua": "curl/7.68.0",
},
]
@pytest.fixture
def fake_check_flips_api_result():
return [
{
"timestamp": "2020-03-23T10:18:23+00:00",
"up": 1
},
{
"timestamp": "2020-03-23T10:17:15+00:00",
"up": 0
},
{
"timestamp": "2020-03-23T10:16:18+00:00",
"up": 1
}
{"timestamp": "2020-03-23T10:18:23+00:00", "up": 1},
{"timestamp": "2020-03-23T10:17:15+00:00", "up": 0},
{"timestamp": "2020-03-23T10:16:18+00:00", "up": 1},
]