Skip to content
Open
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
15 changes: 15 additions & 0 deletions easypost/easypost_client.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
from typing import Any

from easypost.constant import (
API_BASE,
API_VERSION,
INVALID_REQUESTS_VERSION_ERROR,
SUPPORT_EMAIL,
TIMEOUT,
)
from easypost.easypost_object import convert_to_easypost_object
from easypost.hooks import RequestHook, ResponseHook
from easypost.requestor import RequestMethod, Requestor
from easypost.services import (
AddressService,
ApiKeyService,
Expand Down Expand Up @@ -138,3 +142,14 @@ def subscribe_to_response_hook(self, function):
def unsubscribe_from_response_hook(self, function):
"""Unsubscribe functions from running when a response occurs."""
self._response_hook -= function

def make_api_call(self, method: RequestMethod, endpoint: str, params: dict[str, Any]) -> dict[str, Any]:
"""Make an API call to the EasyPost API.

This public, generic interface is useful for making arbitrary API calls to the EasyPost API that
are not yet supported by the client library's services. When possible, the service for your use case
should be used instead as it provides a more convenient and higher-level interface depending on the endpoint.
"""
response = Requestor(self).request(method=method, url=endpoint, params=params)

return convert_to_easypost_object(response=response)
74 changes: 74 additions & 0 deletions tests/cassettes/test_client_make_api_call.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 17 additions & 5 deletions tests/test_easypost_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@

from easypost.easypost_client import EasyPostClient
from easypost.errors import TimeoutError
from easypost.requestor import RequestMethod


def test_api_key():
def test_easypost_client_api_key():
"""Tests setting and getting API keys from different client objects."""
client1 = EasyPostClient(api_key="123")
assert client1.api_key == "123"
Expand All @@ -17,23 +18,23 @@ def test_api_key():
assert client2.api_key == "456"


def test_no_api_key():
def test_easypost_client_no_api_key():
"""Tests that we raise an error when no API key is passed to the client."""
with pytest.raises(TypeError) as error:
EasyPostClient()

assert "missing 1 required positional argument: 'api_key'" in str(error.value)


def test_invalid_client_property():
def test_easypost_client_invalid_client_property():
"""Tests that we throw an error when attempting to use an invalid property of a client."""
with pytest.raises(AttributeError) as error:
EasyPostClient("123").invalid_property()

assert str(error.value) == "'EasyPostClient' object has no attribute 'invalid_property'"


def test_api_base():
def test_easypost_client_api_base():
"""Tests that we can override the API base of the client object."""
client1 = EasyPostClient(api_key="123")
assert client1.api_base == "https://api.easypost.com/v2"
Expand All @@ -43,7 +44,7 @@ def test_api_base():


@patch("requests.Session")
def test_client_timeout(mock_session, basic_shipment):
def test_easypost_client_timeout(mock_session, basic_shipment):
"""Tests that the timeout gets used properly in requests when set."""
mock_session().request.side_effect = requests.exceptions.Timeout()
client = EasyPostClient(api_key=os.getenv("EASYPOST_TEST_API_KEY"), timeout=0.1)
Expand All @@ -53,3 +54,14 @@ def test_client_timeout(mock_session, basic_shipment):
assert False
except TimeoutError as error:
assert error.message == "Request timed out."


@pytest.mark.vcr()
def test_client_make_api_call():
"""Tests that we can make an API call using the generic make_api_call method."""
client = EasyPostClient(api_key=os.getenv("EASYPOST_TEST_API_KEY"))

response = client.make_api_call(method=RequestMethod.GET, endpoint="/addresses", params={"page_size": 1})

assert len(response["addresses"]) == 1
assert response["addresses"][0]["object"] == "Address"