Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ network on genuine misses.
## Network lookups

Without the extra (or when you don't request `local`), cepx queries the live
providers (Correios, ViaCEP, WideNet, BrasilAPI, and OpenCEP) **concurrently**, and the
providers (Correios, ViaCEP, WideNet, BrasilAPI, OpenCEP, and AwesomeAPI) **concurrently**, and the
**first successful** response wins. The lookup only fails once **every** provider
fails, aggregating each error.

Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ keywords = [
"widenet",
"apicep",
"opencep",
"awesomeapi",
]
classifiers = [
"Development Status :: 4 - Beta",
Expand Down
4 changes: 4 additions & 0 deletions src/cepx/providers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

from cepx.providers.awesomeapi import AwesomeAPI
from cepx.providers.base import DEFAULT_TIMEOUT, HttpProvider, Provider
from cepx.providers.brasilapi import BrasilAPI
from cepx.providers.correios import Correios
Expand All @@ -19,6 +20,7 @@
"widenet",
"brasilapi",
"opencep",
"awesomeapi",
)


Expand All @@ -30,13 +32,15 @@ def get_available_providers() -> dict[str, Provider]:
"widenet": WideNet(),
"brasilapi": BrasilAPI(),
"opencep": OpenCep(),
"awesomeapi": AwesomeAPI(),
"local": LocalProvider(),
}


__all__ = (
"DEFAULT_PROVIDER_NAMES",
"DEFAULT_TIMEOUT",
"AwesomeAPI",
"BrasilAPI",
"Correios",
"CorreiosAlt",
Expand Down
43 changes: 43 additions & 0 deletions src/cepx/providers/awesomeapi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from __future__ import annotations

import json

from cepx._types import Address, RequestSpec
from cepx.errors import ProviderError
from cepx.providers.base import HttpProvider


class AwesomeAPI(HttpProvider):
name = "awesomeapi"
connection_error_message = "Failed to connect to the AwesomeAPI provider."

def build_request(self, cep: str) -> RequestSpec:
headers = {
"content-type": "application/json;charset=utf-8",
}

return RequestSpec(
method="GET",
url=f"https://cep.awesomeapi.com.br/json/{cep}",
headers=headers,
)

def parse(self, status_code: int, body: str) -> Address:
if status_code == 404:
raise ProviderError(
"CEP not found in the AwesomeAPI database.",
self.name,
)

if not 200 <= status_code < 300:
raise ProviderError(self.connection_error_message, self.name)

data = json.loads(body)

return self.build_address(
cep=data.get("cep"),
state=data.get("state"),
city=data.get("city"),
neighborhood=data.get("district"),
street=data.get("address"),
)
19 changes: 19 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
VIACEP_URL = "https://viacep.com.br/ws/05010000/json/"
WIDENET_URL = "https://cdn.apicep.com/file/apicep/05010-000.json"
OPENCEP_URL = "https://opencep.com/v1/05010000"
AWESOMEAPI_URL = "https://cep.awesomeapi.com.br/json/05010000"

CORREIOS_URL = (
"https://apps.correios.com.br"
Expand Down Expand Up @@ -41,6 +42,7 @@
"widenet",
"brasilapi",
"opencep",
"awesomeapi",
]


Expand Down Expand Up @@ -87,6 +89,18 @@ def stub_found(router, name):
"uf": "SP",
}
)
elif name == "awesomeapi":
router.get(AWESOMEAPI_URL).respond(
json={
"cep": "05010000",
"address_type": "Rua",
"address_name": "Caiubi",
"address": "Rua Caiubi",
"state": "SP",
"district": "Perdizes",
"city": "São Paulo",
}
)
elif name == "correios":
router.post(CORREIOS_URL).respond(text=CORREIOS_XML_FOUND)
elif name == "correios-alt":
Expand Down Expand Up @@ -118,6 +132,10 @@ def stub_failed(router, name):
)
elif name == "opencep":
router.get(OPENCEP_URL).respond(status_code=404, json={})
elif name == "awesomeapi":
router.get(AWESOMEAPI_URL).respond(
status_code=404, json={"code": "not_found"}
)
elif name == "correios":
router.post(CORREIOS_URL).respond(
status_code=500, text=CORREIOS_XML_FAULT
Expand Down Expand Up @@ -165,6 +183,7 @@ def _clear_bundled_db_cache():


__all__ = (
"AWESOMEAPI_URL",
"BRASILAPI_URL",
"CORREIOS_ALT_URL",
"CORREIOS_URL",
Expand Down
5 changes: 4 additions & 1 deletion tests/unit/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def test_first_success_wins_sync(mock_all_found, expected_address):
"widenet",
"brasilapi",
"opencep",
"awesomeapi",
}


Expand Down Expand Up @@ -86,14 +87,15 @@ def test_all_failed_sync(mock_all_failed):
"widenet",
"brasilapi",
"opencep",
"awesomeapi",
}


async def test_all_failed_async(mock_all_failed):
with pytest.raises(cepx.CepxError) as info:
await cepx.acep("05010000")
assert info.value.type == "provider_error"
assert len(info.value.errors) == 6
assert len(info.value.errors) == 7


def test_aggregated_errors_preserve_provider_order(mock_all_failed):
Expand All @@ -106,6 +108,7 @@ def test_aggregated_errors_preserve_provider_order(mock_all_failed):
"widenet",
"brasilapi",
"opencep",
"awesomeapi",
]


Expand Down
28 changes: 28 additions & 0 deletions tests/unit/test_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import cepx
from tests.conftest import (
AWESOMEAPI_URL,
BRASILAPI_URL,
CORREIOS_ALT_URL,
CORREIOS_URL,
Expand Down Expand Up @@ -43,6 +44,11 @@ def test_opencep(mock_all_found, expected_address):
_assert_matches(address, "opencep", expected_address)


def test_awesomeapi(mock_all_found, expected_address):
address = cepx.cep("05010000", providers=["awesomeapi"])
_assert_matches(address, "awesomeapi", expected_address)


def test_correios(mock_all_found, expected_address):
address = cepx.cep("05010000", providers=["correios"])
_assert_matches(address, "correios", expected_address)
Expand Down Expand Up @@ -103,6 +109,28 @@ def test_opencep_non_2xx_is_connection_error():
)


@respx.mock
def test_awesomeapi_not_found():
respx.get(AWESOMEAPI_URL).respond(
status_code=404, json={"code": "not_found"}
)
with pytest.raises(cepx.CepxError) as info:
cepx.cep("05010000", providers=["awesomeapi"])
assert info.value.errors[0].message == (
"CEP not found in the AwesomeAPI database."
)


@respx.mock
def test_awesomeapi_non_2xx_is_connection_error():
respx.get(AWESOMEAPI_URL).respond(status_code=500)
with pytest.raises(cepx.CepxError) as info:
cepx.cep("05010000", providers=["awesomeapi"])
assert info.value.errors[0].message == (
"Failed to connect to the AwesomeAPI provider."
)


@respx.mock
def test_correios_fault_message():
respx.post(CORREIOS_URL).respond(status_code=500, text=CORREIOS_XML_FAULT)
Expand Down
3 changes: 2 additions & 1 deletion tests/unit/test_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ def test_none_providers_uses_all():
"widenet",
"brasilapi",
"opencep",
"awesomeapi",
]


def test_empty_providers_uses_all():
_, providers = prepare("05010000", [])
assert len(providers) == 6
assert len(providers) == 7


def test_selected_providers_preserve_order():
Expand Down
Loading