diff --git a/easypost/constant.py b/easypost/constant.py index b90ae70..3d153e6 100644 --- a/easypost/constant.py +++ b/easypost/constant.py @@ -36,9 +36,10 @@ "FedexAccount", "FedexSmartpostAccount", ] -_UPS_OATH_CARRIER_ACCOUNT_TYPES = [ +_UPS_OAUTH_CARRIER_ACCOUNT_TYPES = [ "UpsAccount", "UpsMailInnovationsAccount", "UpsSurepostAccount", ] _FILTERS_KEY = "filters" +_EXCLUDED_CLASS_NAMES = ["ups_oauth_registrations"] diff --git a/easypost/services/base_service.py b/easypost/services/base_service.py index 2e6c5c5..203aa5f 100644 --- a/easypost/services/base_service.py +++ b/easypost/services/base_service.py @@ -6,6 +6,7 @@ ) from easypost.constant import ( + _EXCLUDED_CLASS_NAMES, _FILTERS_KEY, NO_MORE_PAGES_ERROR, ) @@ -30,7 +31,9 @@ def _snakecase_name(self, class_name: str) -> str: def _class_url(self, class_name: str) -> str: """Generate a URL based on class name.""" transformed_class_name = self._snakecase_name(class_name) - if transformed_class_name[-1:] in ("s", "h"): + if transformed_class_name in _EXCLUDED_CLASS_NAMES: + return f"/{transformed_class_name}" + elif transformed_class_name[-1:] in ("s", "h"): return f"/{transformed_class_name}es" else: return f"/{transformed_class_name}s" diff --git a/easypost/services/carrier_account_service.py b/easypost/services/carrier_account_service.py index 0864c03..53d3cea 100644 --- a/easypost/services/carrier_account_service.py +++ b/easypost/services/carrier_account_service.py @@ -7,7 +7,7 @@ from easypost.constant import ( _CARRIER_ACCOUNT_TYPES_WITH_CUSTOM_WORKFLOWS, - _UPS_OATH_CARRIER_ACCOUNT_TYPES, + _UPS_OAUTH_CARRIER_ACCOUNT_TYPES, MISSING_PARAMETER_ERROR, ) from easypost.easypost_object import convert_to_easypost_object @@ -33,7 +33,7 @@ def create(self, **params) -> CarrierAccount: raise MissingParameterError(MISSING_PARAMETER_ERROR.format("type")) url = self._select_carrier_account_creation_endpoint(carrier_account_type=carrier_account_type) - if carrier_account_type in _UPS_OATH_CARRIER_ACCOUNT_TYPES: + if carrier_account_type in _UPS_OAUTH_CARRIER_ACCOUNT_TYPES: wrapped_params = {"ups_oauth_registrations": params} else: wrapped_params = {self._snakecase_name(self._model_class): params} @@ -54,7 +54,7 @@ def update(self, id: str, **params) -> CarrierAccount: """Update a CarrierAccount.""" carrier_account = self.retrieve(id) - if carrier_account.get("type") in _UPS_OATH_CARRIER_ACCOUNT_TYPES: + if carrier_account.get("type") in _UPS_OAUTH_CARRIER_ACCOUNT_TYPES: class_name = "UpsOauthRegistrations" else: class_name = self._model_class @@ -75,7 +75,7 @@ def _select_carrier_account_creation_endpoint(self, carrier_account_type: Option """Determines which API endpoint to use for the creation call.""" if carrier_account_type in _CARRIER_ACCOUNT_TYPES_WITH_CUSTOM_WORKFLOWS: return "/carrier_accounts/register" - elif carrier_account_type in _UPS_OATH_CARRIER_ACCOUNT_TYPES: + elif carrier_account_type in _UPS_OAUTH_CARRIER_ACCOUNT_TYPES: return "/ups_oauth_registrations" return "/carrier_accounts" diff --git a/tests/cassettes/test_carrier_account_create_ups.yaml b/tests/cassettes/test_carrier_account_create_ups.yaml index 07ff059..36a389f 100644 --- a/tests/cassettes/test_carrier_account_create_ups.yaml +++ b/tests/cassettes/test_carrier_account_create_ups.yaml @@ -1,6 +1,6 @@ interactions: - request: - body: '{"ups_oauth_registrations": {"type": "UpsAccount", "account_number": 123}}' + body: '{"ups_oauth_registrations": {"type": "UpsAccount", "account_number": "123456789"}}' headers: Accept: - '*/*' @@ -9,7 +9,7 @@ interactions: Connection: - keep-alive Content-Length: - - '74' + - '82' Content-Type: - application/json authorization: @@ -20,14 +20,16 @@ interactions: uri: https://api.easypost.com/v2/ups_oauth_registrations response: body: - string: '{"error": {"code": "PARAMETER.INVALID_TYPE", "message": "Wrong parameter - type.", "errors": [{"field": "ups_oauth_registrations.account_number", "message": - "must be a string"}]}}' + string: '{"id": "ca_032bd1041a5d4418b8378bcc2a738a45", "object": "CarrierAccount", + "type": "UpsAccount", "clone": false, "created_at": "2024-07-09T17:02:56Z", + "updated_at": "2024-07-09T17:02:56Z", "description": null, "reference": null, + "billing_type": "carrier", "readable": "UPS", "logo": null, "fields": [], + "credentials": {}, "test_credentials": {}}' headers: cache-control: - private, no-cache, no-store content-length: - - '168' + - '1392' content-type: - application/json; charset=utf-8 expires: @@ -47,23 +49,88 @@ interactions: x-download-options: - noopen x-ep-request-uuid: - - b98ca9f3668c49a7e2cb1d1f001f7231 + - 52ecff22668d6d40e2b7ddea00434ef1 x-frame-options: - SAMEORIGIN x-node: - - bigweb36nuq + - bigweb34nuq x-permitted-cross-domain-policies: - none x-proxied: - intlb4nuq fa152d4755 - - extlb2nuq fa152d4755 + - extlb1nuq fa152d4755 x-runtime: - - '0.053482' + - '0.087747' x-version-label: - - easypost-202407081949-3228d5845b-master + - easypost-202407091438-d7c6f02f06-master x-xss-protection: - 1; mode=block status: - code: 422 - message: Unprocessable Entity + code: 201 + message: Created +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '0' + authorization: + - + user-agent: + - + method: DELETE + uri: https://api.easypost.com/v2/carrier_accounts/ca_032bd1041a5d4418b8378bcc2a738a45 + response: + body: + string: '{}' + headers: + cache-control: + - private, no-cache, no-store + content-length: + - '2' + 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-canary: + - direct + x-content-type-options: + - nosniff + x-download-options: + - noopen + x-ep-request-uuid: + - 52ecff22668d6d40e2b7ddea00434f1f + x-frame-options: + - SAMEORIGIN + x-node: + - bigweb43nuq + x-permitted-cross-domain-policies: + - none + x-proxied: + - intlb3nuq fa152d4755 + - extlb1nuq fa152d4755 + x-runtime: + - '0.208397' + x-version-label: + - easypost-202407091438-d7c6f02f06-master + x-xss-protection: + - 1; mode=block + status: + code: 200 + message: OK version: 1 diff --git a/tests/cassettes/test_carrier_account_update_ups.yaml b/tests/cassettes/test_carrier_account_update_ups.yaml index e1f605e..005b542 100644 --- a/tests/cassettes/test_carrier_account_update_ups.yaml +++ b/tests/cassettes/test_carrier_account_update_ups.yaml @@ -1,6 +1,6 @@ interactions: - request: - body: '{"ups_oauth_registrations": {"type": "UpsAccount", "account_number": 123}}' + body: '{"ups_oauth_registrations": {"type": "UpsAccount", "account_number": "123456789"}}' headers: Accept: - '*/*' @@ -9,24 +9,92 @@ interactions: Connection: - keep-alive Content-Length: - - '74' + - '82' Content-Type: - application/json authorization: - user-agent: - - method: PATCH - uri: https://api.easypost.com/v2/ups_oauth_registrationses/ca_123 + method: POST + uri: https://api.easypost.com/v2/ups_oauth_registrations + response: + body: + string: '{"id": "ca_e5527ea2bd0f44c59f85fd198428da2b", "object": "CarrierAccount", + "type": "UpsAccount", "clone": false, "created_at": "2024-07-09T17:22:32Z", + "updated_at": "2024-07-09T17:22:32Z", "description": null, "reference": null, + "billing_type": "carrier", "readable": "UPS", "logo": null, "fields": [], + "credentials": {}, "test_credentials": {}}' + headers: + cache-control: + - private, no-cache, no-store + content-length: + - '1388' + 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: + - 52ecff1e668d71d8e2b7eef000485d09 + x-frame-options: + - SAMEORIGIN + x-node: + - bigweb53nuq + x-permitted-cross-domain-policies: + - none + x-proxied: + - intlb4nuq fa152d4755 + - extlb1nuq fa152d4755 + x-runtime: + - '0.090996' + x-version-label: + - easypost-202407091438-d7c6f02f06-master + x-xss-protection: + - 1; mode=block + status: + code: 201 + message: Created +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + authorization: + - + user-agent: + - + method: GET + uri: https://api.easypost.com/v2/carrier_accounts/ca_e5527ea2bd0f44c59f85fd198428da2b response: body: - string: '{"error": {"code": "NOT_FOUND", "message": "The requested resource - could not be found.", "errors": []}}' + string: '{"id": "ca_e5527ea2bd0f44c59f85fd198428da2b", "object": "CarrierAccount", + "type": "UpsAccount", "clone": false, "created_at": "2024-07-09T17:22:32Z", + "updated_at": "2024-07-09T17:22:32Z", "description": null, "reference": null, + "billing_type": "carrier", "readable": "UPS", "logo": null, "fields": [], + "credentials": {}, "test_credentials": {}}' headers: cache-control: - private, no-cache, no-store content-length: - - '97' + - '1388' content-type: - application/json; charset=utf-8 expires: @@ -39,8 +107,6 @@ interactions: - max-age=31536000; includeSubDomains; preload transfer-encoding: - chunked - vary: - - Accept x-backend: - easypost x-content-type-options: @@ -48,23 +114,155 @@ interactions: x-download-options: - noopen x-ep-request-uuid: - - b98ca9f4668c59ebe2cc2cd8002eee16 + - 52ecff1e668d71d8e2b7eef000485d33 x-frame-options: - SAMEORIGIN x-node: - - bigweb42nuq + - bigweb36nuq x-permitted-cross-domain-policies: - none x-proxied: - intlb4nuq fa152d4755 - - extlb2nuq fa152d4755 + - extlb1nuq fa152d4755 + x-runtime: + - '0.040106' + x-version-label: + - easypost-202407091438-d7c6f02f06-master + x-xss-protection: + - 1; mode=block + status: + code: 200 + message: OK +- request: + body: '{"ups_oauth_registrations": {"account_number": "987654321"}}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '60' + Content-Type: + - application/json + authorization: + - + user-agent: + - + method: PATCH + uri: https://api.easypost.com/v2/ups_oauth_registrations/ca_e5527ea2bd0f44c59f85fd198428da2b + response: + body: + string: '{"id": "ca_e5527ea2bd0f44c59f85fd198428da2b", "object": "CarrierAccount", + "type": "UpsAccount", "clone": false, "created_at": "2024-07-09T17:22:32Z", + "updated_at": "2024-07-09T17:22:32Z", "description": null, "reference": null, + "billing_type": "carrier", "readable": "UPS", "logo": null, "fields": [], + "credentials": {}, "test_credentials": {}}' + headers: + cache-control: + - private, no-cache, no-store + content-length: + - '1416' + 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: + - 52ecff1e668d71d8e2b7eef000485d4a + x-frame-options: + - SAMEORIGIN + x-node: + - bigweb39nuq + x-permitted-cross-domain-policies: + - none + x-proxied: + - intlb3nuq fa152d4755 + - extlb1nuq fa152d4755 + x-runtime: + - '0.079076' + x-version-label: + - easypost-202407091438-d7c6f02f06-master + x-xss-protection: + - 1; mode=block + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '0' + authorization: + - + user-agent: + - + method: DELETE + uri: https://api.easypost.com/v2/carrier_accounts/ca_e5527ea2bd0f44c59f85fd198428da2b + response: + body: + string: '{}' + headers: + cache-control: + - private, no-cache, no-store + content-length: + - '2' + 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: + - 52ecff1e668d71d9e2b7eef000485d62 + x-frame-options: + - SAMEORIGIN + x-node: + - bigweb36nuq + x-permitted-cross-domain-policies: + - none + x-proxied: + - intlb3nuq fa152d4755 + - extlb1nuq fa152d4755 x-runtime: - - '0.024976' + - '0.078431' x-version-label: - - easypost-202407081949-3228d5845b-master + - easypost-202407091438-d7c6f02f06-master x-xss-protection: - 1; mode=block status: - code: 404 - message: Not Found + code: 200 + message: OK version: 1 diff --git a/tests/test_carrier_account.py b/tests/test_carrier_account.py index 573b1b2..37d6fe8 100644 --- a/tests/test_carrier_account.py +++ b/tests/test_carrier_account.py @@ -99,45 +99,35 @@ def test_carrier_account_create_with_custom_workflow(prod_client): @pytest.mark.vcr() def test_carrier_account_create_ups(prod_client): - """Test registering a UPS Carrier Account. - - 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 = { + """Test registering a UPS Carrier Account which uses a different URL and schema.""" + params = { "type": "UpsAccount", - "account_number": 123, + "account_number": "123456789", } - try: - prod_client.carrier_account.create(**carrier_account) - except ApiError as error: - assert error.http_status == 422 - assert any( - [error["field"] == "account_number" and error["message"] == "must be present and a string"] - for error in error.errors - ) + carrier_account = prod_client.carrier_account.create(**params) + assert isinstance(carrier_account, CarrierAccount) + assert str.startswith(carrier_account.id, "ca_") + assert carrier_account.type == "UpsAccount" -@pytest.mark.vcr() -@patch( - "easypost.services.carrier_account_service.CarrierAccountService.retrieve", - return_value={"type": "UpsAccount"}, -) -def test_carrier_account_update_ups(mock_retrieve, prod_client): - """Test updating a UPS Carrier Account. + prod_client.carrier_account.delete(carrier_account.id) # Delete the carrier account once it's done being tested. - 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 that we sent the request correctly. - This test will require someone to ensure the cassette looks the way we want (URL) - """ - carrier_account = { +@pytest.mark.vcr() +def test_carrier_account_update_ups(prod_client): + """Test updating a UPS Carrier Account which uses a different URL and schema.""" + params = { "type": "UpsAccount", - "account_number": 123, + "account_number": "123456789", } - try: - prod_client.carrier_account.update("ca_123", **carrier_account) - except ApiError as error: - assert error.http_status == 404 + carrier_account = prod_client.carrier_account.create(**params) + + updated_carrier_account = prod_client.carrier_account.update(carrier_account.id, account_number="987654321") + + assert isinstance(carrier_account, CarrierAccount) + assert str.startswith(carrier_account.id, "ca_") + assert carrier_account.type == "UpsAccount" + + prod_client.carrier_account.delete(carrier_account.id) # Delete the carrier account once it's done being tested.