From 06c6b44429a615d492c7150af5d2b5a6bd99a1ed Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Thu, 3 Nov 2022 16:00:21 -0600 Subject: [PATCH] feat: adds register function to CarrierAccount for custom registration carriers --- CHANGELOG.md | 4 + easypost/carrier_account.py | 9 +++ easypost/error.py | 2 + .../test_carrier_account_register.yaml | 78 +++++++++++++++++++ tests/test_carrier_account.py | 19 +++++ 5 files changed, 112 insertions(+) create mode 100644 tests/cassettes/test_carrier_account_register.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index bfb3093d..eadc7a4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## NEXT RELEASE + +- Adds `register` function to CarrierAccount for carriers with custom registration such as FedEx or UPS + ## v7.6.1 (2022-10-24) - Concatenates `error.message` if it incorrectly comes back from the API as a list diff --git a/easypost/carrier_account.py b/easypost/carrier_account.py index ac5c78c2..aa4dbbe0 100644 --- a/easypost/carrier_account.py +++ b/easypost/carrier_account.py @@ -23,3 +23,12 @@ def types(cls, api_key: Optional[str] = None) -> List[str]: requestor = Requestor(local_api_key=api_key) response, api_key = requestor.request(method=RequestMethod.GET, url="/carrier_types") return convert_to_easypost_object(response=response, api_key=api_key) + + @classmethod + def register(cls, api_key: Optional[str] = None, **params) -> "CarrierAccount": + """Creates a Carrier Account that requires custom registration (eg: FedEx & UPS).""" + requestor = Requestor(local_api_key=api_key) + url = f"{cls.class_url()}/register" + wrapped_params = {cls.snakecase_name(): params} + response, api_key = requestor.request(method=RequestMethod.POST, url=url, params=wrapped_params) + return convert_to_easypost_object(response=response, api_key=api_key) diff --git a/easypost/error.py b/easypost/error.py index ff2d8ed1..8729dda1 100644 --- a/easypost/error.py +++ b/easypost/error.py @@ -20,6 +20,8 @@ def __init__( self.http_status = http_status self.http_body = http_body self.original_exception = original_exception + # TODO: add missing `errors` param among others in thread-safe rewrite and update tests + if http_body: try: self.json_body = json.loads(http_body) diff --git a/tests/cassettes/test_carrier_account_register.yaml b/tests/cassettes/test_carrier_account_register.yaml new file mode 100644 index 00000000..3a6ca579 --- /dev/null +++ b/tests/cassettes/test_carrier_account_register.yaml @@ -0,0 +1,78 @@ +interactions: +- request: + body: '{"carrier_account": {"type": "UpsAccount", "registration_data": {}}}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '68' + Content-Type: + - application/json + authorization: + - + user-agent: + - + method: POST + uri: https://api.easypost.com/v2/carrier_accounts/register + response: + body: + string: '{"error": {"code": "UNPROCESSABLE_ENTITY", "message": "The request + was understood, but cannot be processed.", "errors": [{"field": "account_number", + "message": "must be present and a string"}, {"field": "city", "message": "must + be present and a string"}, {"field": "company", "message": "must be present + and a string"}, {"field": "country", "message": "must be present and a string"}, + {"field": "email", "message": "must be present and a string"}, {"field": "name", + "message": "must be present and a string"}, {"field": "phone", "message": + "must be present and a string"}, {"field": "postal_code", "message": "must + be present and a string"}, {"field": "state", "message": "must be present + and a string"}, {"field": "street1", "message": "must be present and a string"}, + {"field": "title", "message": "must be present and a string"}, {"field": "website", + "message": "must be present and a string"}]}}' + headers: + cache-control: + - private, no-cache, no-store + content-length: + - '846' + content-type: + - application/json; charset=utf-8 + expires: + - '0' + pragma: + - no-cache + referrer-policy: + - strict-origin-when-cross-origin + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + x-backend: + - easypost + x-content-type-options: + - nosniff + x-download-options: + - noopen + x-ep-request-uuid: + - 513791bf636437fee789f242001139c3 + x-frame-options: + - SAMEORIGIN + x-node: + - bigweb5nuq + x-permitted-cross-domain-policies: + - none + x-proxied: + - intlb1nuq 29913d444b + - extlb1nuq 29913d444b + x-runtime: + - '0.038035' + x-version-label: + - easypost-202211032002-79b51b1468-master + x-xss-protection: + - 1; mode=block + status: + code: 422 + message: Unprocessable Entity +version: 1 diff --git a/tests/test_carrier_account.py b/tests/test_carrier_account.py index 15f2ab51..84af650e 100644 --- a/tests/test_carrier_account.py +++ b/tests/test_carrier_account.py @@ -63,3 +63,22 @@ def test_carrier_account_type(prod_api_key): types = easypost.CarrierAccount.types() assert isinstance(types, list) + + +@pytest.mark.vcr() +def test_carrier_account_register(prod_api_key): + """Test register a Carrier Account with custom registration such as FedEx or UPS. + + We purposefully don't pass data here because real data is required for this endpoint + which we don't have in a test context, simply assert the error matches when no data is passed. + """ + carrier_account = { + "type": "UpsAccount", + "registration_data": {}, + } + + try: + easypost.CarrierAccount.register(**carrier_account) + except easypost.Error as error: + # TODO: Assert against error.errors when that property gets added + assert '{"field": "account_number", "message": "must be present and a string"}' in error.http_body