From 9a6b196d8e32b4acc2b24023a6bdf6e91d7867ca Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Wed, 31 May 2023 10:04:13 -0600 Subject: [PATCH 01/10] fix: payload inherits resource --- CHANGELOG.md | 4 ++++ easypost/payload.py | 5 ++++- setup.py | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3c2dc99..1414ece7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## v7.13.1 (2023-05-31) + +- Fixes a bug where `Payload` didn't inherit `EasyPostObject` which could throw errors when retrieving event payloads (closes #284) + ## v7.13.0 (2023-05-02) - Adds `retrieve_estimated_delivery_date` function to the Shipment class diff --git a/easypost/payload.py b/easypost/payload.py index 075e7263..1f5ab5ec 100644 --- a/easypost/payload.py +++ b/easypost/payload.py @@ -1,2 +1,5 @@ -class Payload: +from easypost.resource import Resource + + +class Payload(Resource): pass diff --git a/setup.py b/setup.py index b31c8f4a..4e054541 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ setup( name="easypost", - version="7.13.0", + version="7.13.1", description="EasyPost Shipping API Client Library for Python", author="EasyPost", author_email="support@easypost.com", From 812498b0874d566b04ac1e44a55011328985d374 Mon Sep 17 00:00:00 2001 From: Nate Harris Date: Mon, 23 Oct 2023 13:42:59 -0600 Subject: [PATCH 02/10] [feat] Add beta feature to retrieve paginated child user list (#300) --- easypost/beta/__init__.py | 1 + easypost/beta/user.py | 51 ++++++++++++++ .../test_beta_user_all_children.yaml | 67 +++++++++++++++++++ .../test_beta_user_get_next_page.yaml | 67 +++++++++++++++++++ tests/test_beta_user.py | 30 +++++++++ 5 files changed, 216 insertions(+) create mode 100644 easypost/beta/user.py create mode 100644 tests/cassettes/test_beta_user_all_children.yaml create mode 100644 tests/cassettes/test_beta_user_get_next_page.yaml create mode 100644 tests/test_beta_user.py diff --git a/easypost/beta/__init__.py b/easypost/beta/__init__.py index af8ec3a2..dedb4ec3 100644 --- a/easypost/beta/__init__.py +++ b/easypost/beta/__init__.py @@ -2,3 +2,4 @@ from easypost.beta.carrier_metadata import CarrierMetadata from easypost.beta.rate import Rate from easypost.beta.referral import Referral +from easypost.beta.user import User diff --git a/easypost/beta/user.py b/easypost/beta/user.py new file mode 100644 index 00000000..ea093dad --- /dev/null +++ b/easypost/beta/user.py @@ -0,0 +1,51 @@ +from typing import ( + Any, + Dict, + List, + Optional, +) + +from easypost.easypost_object import convert_to_easypost_object +from easypost.error import Error +from easypost.requestor import ( + RequestMethod, + Requestor, +) +from easypost.resource import Resource + + +class User(Resource): + @classmethod + def all_children(cls, api_key: Optional[str] = None, **params) -> Dict[str, Any]: + """Retrieve a paginated list of children from the API.""" + requestor = Requestor(local_api_key=api_key) + url = "/users/children" + response, api_key = requestor.request(method=RequestMethod.GET, url=url, params=params, beta=True) + return convert_to_easypost_object(response=response, api_key=api_key) + + @classmethod + def get_next_page_of_children( + cls, + children: Dict[str, Any], + page_size: int, + api_key: Optional[str] = None, + ) -> List["User"]: + """Get next page of children.""" + requestor = Requestor(local_api_key=api_key) + url = "/users/children" + children_array = children.get("children", []) + + if len(children_array) == 0 or not children.get("has_more", False): + raise Error(message="There are no more pages to retrieve.") + + params = { + "after_id": children_array[-1].id, + "page_size": page_size, + } + + data, api_key = requestor.request(method=RequestMethod.GET, url=url, params=params, beta=True) + next_children_array: List[Any] = data.get("children", []) + if len(next_children_array) == 0 or not data.get("has_more", False): + raise Error(message="There are no more pages to retrieve.") + + return convert_to_easypost_object(response=data, api_key=api_key) diff --git a/tests/cassettes/test_beta_user_all_children.yaml b/tests/cassettes/test_beta_user_all_children.yaml new file mode 100644 index 00000000..14ad3068 --- /dev/null +++ b/tests/cassettes/test_beta_user_all_children.yaml @@ -0,0 +1,67 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + authorization: + - + user-agent: + - + method: GET + uri: https://api.easypost.com/beta/users/children?page_size=5 + response: + body: + string: '{"children": [], "has_more": false}' + headers: + cache-control: + - private, no-cache, no-store + content-length: + - '32' + content-type: + - application/json; charset=utf-8 + etag: + - W/"3e2ec6a518ba813e2b978a9e1e34e8f6" + expires: + - '0' + pragma: + - no-cache + referrer-policy: + - strict-origin-when-cross-origin + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + vary: + - Origin + x-backend: + - easypost + x-content-type-options: + - nosniff + x-download-options: + - noopen + x-ep-request-uuid: + - 9c098db26532e374e786ab5e0050ccad + x-frame-options: + - SAMEORIGIN + x-node: + - bigweb38nuq + x-permitted-cross-domain-policies: + - none + x-proxied: + - intlb2nuq 5a08ab70b2 + - extlb2nuq 003ad9bca0 + x-runtime: + - '0.037394' + x-version-label: + - easypost-202310201725-2922e4b728-master + x-xss-protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_beta_user_get_next_page.yaml b/tests/cassettes/test_beta_user_get_next_page.yaml new file mode 100644 index 00000000..23fce0e8 --- /dev/null +++ b/tests/cassettes/test_beta_user_get_next_page.yaml @@ -0,0 +1,67 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + authorization: + - + user-agent: + - + method: GET + uri: https://api.easypost.com/beta/users/children?page_size=5 + response: + body: + string: '{"children": [], "has_more": false}' + headers: + cache-control: + - private, no-cache, no-store + content-length: + - '32' + content-type: + - application/json; charset=utf-8 + etag: + - W/"3e2ec6a518ba813e2b978a9e1e34e8f6" + expires: + - '0' + pragma: + - no-cache + referrer-policy: + - strict-origin-when-cross-origin + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + vary: + - Origin + x-backend: + - easypost + x-content-type-options: + - nosniff + x-download-options: + - noopen + x-ep-request-uuid: + - 9c098db16532e6e0e786b4a20051d0f3 + x-frame-options: + - SAMEORIGIN + x-node: + - bigweb39nuq + x-permitted-cross-domain-policies: + - none + x-proxied: + - intlb1nuq 5a08ab70b2 + - extlb2nuq 003ad9bca0 + x-runtime: + - '0.034338' + x-version-label: + - easypost-202310201725-2922e4b728-master + x-xss-protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/tests/test_beta_user.py b/tests/test_beta_user.py new file mode 100644 index 00000000..e056d8d6 --- /dev/null +++ b/tests/test_beta_user.py @@ -0,0 +1,30 @@ +import pytest + +import easypost + + +@pytest.mark.vcr() +def test_beta_user_all_children(prod_api_key, page_size): + children_data = easypost.beta.User.all_children(page_size=page_size) + + children_array = children_data["children"] + assert len(children_array) <= page_size + assert all(isinstance(child, easypost.User) for child in children_array) + + has_more = children_data["has_more"] + assert isinstance(has_more, bool) + + +@pytest.mark.vcr() +def test_beta_user_get_next_page(prod_api_key, page_size): + try: + children = easypost.beta.User.all_children(page_size=page_size) + next_page = easypost.beta.User.get_next_page_of_children(children=children, page_size=page_size) + + first_id_of_first_page = children["children"][0].id + first_id_of_second_page = next_page["children"][0].id + + assert first_id_of_first_page != first_id_of_second_page + except easypost.Error as e: + if e.message != "There are no more pages to retrieve.": + raise easypost.Error(message="Test failed intentionally.") From 6d6065cea5b73550eebe0c69be22dd8d75b256c8 Mon Sep 17 00:00:00 2001 From: Nate Harris Date: Mon, 23 Oct 2023 15:04:08 -0600 Subject: [PATCH 03/10] - Update changelog, version number (#301) --- CHANGELOG.md | 4 ++++ setup.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1414ece7..80b0d25b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## v7.14.0 (2023-10-23) + +- Adds beta `all_children` function to the User class for retrieving paginated lists of children users + ## v7.13.1 (2023-05-31) - Fixes a bug where `Payload` didn't inherit `EasyPostObject` which could throw errors when retrieving event payloads (closes #284) diff --git a/setup.py b/setup.py index 4e054541..b84eb667 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ setup( name="easypost", - version="7.13.1", + version="7.14.0", description="EasyPost Shipping API Client Library for Python", author="EasyPost", author_email="support@easypost.com", From e6ee44fe292784a9990d7e24ebed2e5151be9fc0 Mon Sep 17 00:00:00 2001 From: Nate Harris Date: Mon, 30 Oct 2023 10:06:17 -0600 Subject: [PATCH 04/10] [fix] Remove bad post-request guard clause in pagination functions (#303) - Remove bad post-request guard clause causing pagination functions to never return the final page of results - Add unit test to beta user children pagination tests to verify pagination function works as expected (allows iteration over all available pages) --- easypost/beta/user.py | 2 +- easypost/resource.py | 2 +- ...t_beta_user_get_next_page_collect_all.yaml | 67 +++++++++++++++++ tests/test_beta_user.py | 72 +++++++++++++++++++ 4 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 tests/cassettes/test_beta_user_get_next_page_collect_all.yaml diff --git a/easypost/beta/user.py b/easypost/beta/user.py index ea093dad..8e3feb80 100644 --- a/easypost/beta/user.py +++ b/easypost/beta/user.py @@ -45,7 +45,7 @@ def get_next_page_of_children( data, api_key = requestor.request(method=RequestMethod.GET, url=url, params=params, beta=True) next_children_array: List[Any] = data.get("children", []) - if len(next_children_array) == 0 or not data.get("has_more", False): + if len(next_children_array) == 0: raise Error(message="There are no more pages to retrieve.") return convert_to_easypost_object(response=data, api_key=api_key) diff --git a/easypost/resource.py b/easypost/resource.py index e6050d17..dbd93f73 100644 --- a/easypost/resource.py +++ b/easypost/resource.py @@ -93,7 +93,7 @@ def get_next_page( response, api_key = requestor.request(method=RequestMethod.GET, url=url, params=params) response_array: List[Any] = response.get(url[1:]) # type: ignore - if response is None or len(response_array) == 0 or not response.get("has_more"): + if response is None or len(response_array) == 0: raise Error(message="There are no more pages to retrieve.") return convert_to_easypost_object(response=response, api_key=api_key) diff --git a/tests/cassettes/test_beta_user_get_next_page_collect_all.yaml b/tests/cassettes/test_beta_user_get_next_page_collect_all.yaml new file mode 100644 index 00000000..4482191c --- /dev/null +++ b/tests/cassettes/test_beta_user_get_next_page_collect_all.yaml @@ -0,0 +1,67 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + authorization: + - + user-agent: + - + method: GET + uri: https://api.easypost.com/beta/users/children?page_size=1 + response: + body: + string: '{"children": [], "has_more": false}' + headers: + cache-control: + - private, no-cache, no-store + content-length: + - '32' + content-type: + - application/json; charset=utf-8 + etag: + - W/"3e2ec6a518ba813e2b978a9e1e34e8f6" + expires: + - '0' + pragma: + - no-cache + referrer-policy: + - strict-origin-when-cross-origin + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + vary: + - Origin + x-backend: + - easypost + x-content-type-options: + - nosniff + x-download-options: + - noopen + x-ep-request-uuid: + - e7025b87653f1d16f42cf30400b96021 + x-frame-options: + - SAMEORIGIN + x-node: + - bigweb35nuq + x-permitted-cross-domain-policies: + - none + x-proxied: + - intlb2nuq b3de2c47ef + - extlb2nuq 003ad9bca0 + x-runtime: + - '0.036347' + x-version-label: + - easypost-202310271808-bc7d2a481d-master + x-xss-protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/tests/test_beta_user.py b/tests/test_beta_user.py index e056d8d6..da72261c 100644 --- a/tests/test_beta_user.py +++ b/tests/test_beta_user.py @@ -1,3 +1,5 @@ +from unittest.mock import patch + import pytest import easypost @@ -28,3 +30,73 @@ def test_beta_user_get_next_page(prod_api_key, page_size): except easypost.Error as e: if e.message != "There are no more pages to retrieve.": raise easypost.Error(message="Test failed intentionally.") + + +@pytest.mark.vcr() # Cassette not needed due to mocking, but used to avoid making real bogus API calls +def test_beta_user_get_next_page_collect_all(prod_api_key): + page_size = 1 # Doesn't matter what this is, we're mocking the response + all_children = [] + + first_page_response = { + "children": [ + { + "id": "user_123", + } + ], + "has_more": True, + } + + # Mock the initial "get all children" call + return_value = (first_page_response, prod_api_key) + with patch("easypost.requestor.Requestor.request", return_value=return_value): + first_page = easypost.beta.User.all_children(page_size=page_size) + all_children += first_page["children"] + previous_page = first_page + + second_page_response = { + "children": [ + { + "id": "user_456", + } + ], + "has_more": True, + } + + # Mock the first "get next page" call with more to collect after + # (current page "has_more" = True, next page "has_more" = True) + return_value = (second_page_response, prod_api_key) + with patch("easypost.requestor.Requestor.request", return_value=return_value): + next_page = easypost.beta.User.get_next_page_of_children( + children=previous_page, page_size=page_size # type: ignore + ) + all_children += next_page["children"] + previous_page = next_page + + third_page_response = { + "children": [ + { + "id": "user_789", + } + ], + "has_more": False, + } + + # Mock the second "get next page" call with no more to collect + # (current page "has_more" = True, next page "has_more" = False) + return_value = (third_page_response, prod_api_key) + with patch("easypost.requestor.Requestor.request", return_value=return_value): + next_page = easypost.beta.User.get_next_page_of_children( + children=previous_page, page_size=page_size # type: ignore + ) + all_children += next_page["children"] + previous_page = next_page + + # Verify we have all children (from both the "get all children" and "get next page" calls) + # Ensures that no guard clauses inside the "get next page" method are preventing us from collecting all children + assert len(all_children) == 3 + + # Now that the previous page has "has_more" = False, it should throw an error before even making the API call + with pytest.raises(easypost.Error) as error: + _ = easypost.beta.User.get_next_page_of_children(children=previous_page, page_size=page_size) # type: ignore + + assert str(error.value) == "There are no more pages to retrieve." From daa9ec4ad6bc7bd9c8116f6667587f5c6406fd68 Mon Sep 17 00:00:00 2001 From: Nate Harris Date: Mon, 30 Oct 2023 11:50:09 -0600 Subject: [PATCH 05/10] [chore] Prep for v7.14.1 release (#304) * - Prep for v7.14.1 release --- CHANGELOG.md | 4 ++++ easypost/version.py | 2 +- setup.py | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80b0d25b..b2d3d7c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## v7.14.1 (2023-10-30) + +- Fixes a bug where `get_next_page` functions threw an error, preventing users from retrieving the final page of results + ## v7.14.0 (2023-10-23) - Adds beta `all_children` function to the User class for retrieving paginated lists of children users diff --git a/easypost/version.py b/easypost/version.py index 505fb15f..df251363 100644 --- a/easypost/version.py +++ b/easypost/version.py @@ -1,4 +1,4 @@ -VERSION = "7.13.0" +VERSION = "7.14.1" numbers = [str(number) for number in VERSION.split(".")] VERSION_INFO = numbers diff --git a/setup.py b/setup.py index b84eb667..4461826f 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ setup( name="easypost", - version="7.14.0", + version="7.14.1", description="EasyPost Shipping API Client Library for Python", author="EasyPost", author_email="support@easypost.com", From bd0dc6333e48c97e2cd572b2fb9bdfaa6c205be4 Mon Sep 17 00:00:00 2001 From: "Junjie(Jack) Chen" Date: Thu, 4 Jan 2024 15:42:36 -0500 Subject: [PATCH 06/10] Move all_children functions to GA (#320) --- CHANGELOG.md | 5 ++ easypost/beta/user.py | 12 ++++ easypost/requestor.py | 2 +- easypost/user.py | 36 ++++++++++ tests/cassettes/test_user_all_children.yaml | 71 +++++++++++++++++++ tests/cassettes/test_user_get_next_page.yaml | 73 ++++++++++++++++++++ tests/test_user.py | 27 ++++++++ 7 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 tests/cassettes/test_user_all_children.yaml create mode 100644 tests/cassettes/test_user_get_next_page.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index b2d3d7c8..f6305b98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # CHANGELOG +## v7.15.0 Next release + +- Adds `all_children` function to the User class for retrieving paginated lists of children users, and deprecate the beta function +- Adds `get_next_page_of_children` function to the User class for retrieving next paginated lists of children users, and deprecate the beta function + ## v7.14.1 (2023-10-30) - Fixes a bug where `get_next_page` functions threw an error, preventing users from retrieving the final page of results diff --git a/easypost/beta/user.py b/easypost/beta/user.py index 8e3feb80..181c02dd 100644 --- a/easypost/beta/user.py +++ b/easypost/beta/user.py @@ -4,6 +4,7 @@ List, Optional, ) +from warnings import warn from easypost.easypost_object import convert_to_easypost_object from easypost.error import Error @@ -17,6 +18,11 @@ class User(Resource): @classmethod def all_children(cls, api_key: Optional[str] = None, **params) -> Dict[str, Any]: + warn( + "This function is deprecated, please use `all_children` function in `user` class for GA accessibility", + DeprecationWarning, + stacklevel=2, + ) """Retrieve a paginated list of children from the API.""" requestor = Requestor(local_api_key=api_key) url = "/users/children" @@ -30,6 +36,12 @@ def get_next_page_of_children( page_size: int, api_key: Optional[str] = None, ) -> List["User"]: + warn( + "This function is deprecated, please use `get_next_page_of_children` function in" + "`user` class for GA accessibility", + DeprecationWarning, + stacklevel=2, + ) """Get next page of children.""" requestor = Requestor(local_api_key=api_key) url = "/users/children" diff --git a/easypost/requestor.py b/easypost/requestor.py index f230e4cd..2373a8bd 100644 --- a/easypost/requestor.py +++ b/easypost/requestor.py @@ -42,7 +42,7 @@ def _objects_to_ids(cls, param: Dict[str, Any]) -> Dict[str, Any]: return {"id": param.id} elif isinstance(param, dict): data = {} - for (k, v) in param.items(): + for k, v in param.items(): if isinstance(v, list): data[k] = [cls._objects_to_ids(item) for item in v] # type: ignore else: diff --git a/easypost/user.py b/easypost/user.py index 0810f1bf..e721cadb 100644 --- a/easypost/user.py +++ b/easypost/user.py @@ -7,6 +7,7 @@ from easypost.api_key import ApiKey from easypost.easypost_object import convert_to_easypost_object +from easypost.error import Error from easypost.requestor import ( RequestMethod, Requestor, @@ -83,3 +84,38 @@ def update_brand(self, api_key: Optional[str] = None, **params) -> "User": method=RequestMethod.PATCH, url=self.instance_url() + "/brand", params=params ) return convert_to_easypost_object(response=response, api_key=api_key) + + @classmethod + def all_children(cls, api_key: Optional[str] = None, **params) -> Dict[str, Any]: + """Retrieve a paginated list of children from the API.""" + requestor = Requestor(local_api_key=api_key) + url = "/users/children" + response, api_key = requestor.request(method=RequestMethod.GET, url=url, params=params) + return convert_to_easypost_object(response=response, api_key=api_key) + + @classmethod + def get_next_page_of_children( + cls, + children: Dict[str, Any], + page_size: int, + api_key: Optional[str] = None, + ) -> List["User"]: + """Get next page of children.""" + requestor = Requestor(local_api_key=api_key) + url = "/users/children" + children_array = children.get("children", []) + + if len(children_array) == 0 or not children.get("has_more", False): + raise Error(message="There are no more pages to retrieve.") + + params = { + "after_id": children_array[-1].id, + "page_size": page_size, + } + + data, api_key = requestor.request(method=RequestMethod.GET, url=url, params=params) + next_children_array: List[Any] = data.get("children", []) + if len(next_children_array) == 0: + raise Error(message="There are no more pages to retrieve.") + + return convert_to_easypost_object(response=data, api_key=api_key) diff --git a/tests/cassettes/test_user_all_children.yaml b/tests/cassettes/test_user_all_children.yaml new file mode 100644 index 00000000..c4c73c2d --- /dev/null +++ b/tests/cassettes/test_user_all_children.yaml @@ -0,0 +1,71 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + authorization: + - + user-agent: + - + method: GET + uri: https://api.easypost.com/v2/users/children?page_size=5 + response: + body: + string: '{"children": [{"id": "user_484dd58db70a4f31b4bb862998cf0e04", "object": + "User", "parent_id": "user_0f6b83e3530b401cb1e8aeaa6a250d4d", "name": "Test + User", "phone_number": "", "verified": true, "created_at": "2023-05-16T22:01:20Z"}, + {"id": "user_14e894c0d541459395f4456e7cf4f175", "object": "User", "parent_id": + "user_0f6b83e3530b401cb1e8aeaa6a250d4d", "name": "Test User", "phone_number": + "", "verified": true, "created_at": "2023-09-27T22:05:26Z"}, {"id": + "user_f04df3dad13848339a7975d295d6629f", "object": "User", "parent_id": "user_0f6b83e3530b401cb1e8aeaa6a250d4d", + "name": "Test User", "phone_number": "", "verified": true, "created_at": + "2023-11-30T19:23:22Z"}], "has_more": false}' + headers: + cache-control: + - private, no-cache, no-store + content-length: + - '673' + 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: + - ec7851e965970bd2e79a7e400013d8db + x-frame-options: + - SAMEORIGIN + x-node: + - bigweb39nuq + x-permitted-cross-domain-policies: + - none + x-proxied: + - intlb1nuq 2c48984abf + - extlb1nuq 003ad9bca0 + x-runtime: + - '0.039816' + x-version-label: + - easypost-202401041812-437974c716-master + x-xss-protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/tests/cassettes/test_user_get_next_page.yaml b/tests/cassettes/test_user_get_next_page.yaml new file mode 100644 index 00000000..111918a3 --- /dev/null +++ b/tests/cassettes/test_user_get_next_page.yaml @@ -0,0 +1,73 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + authorization: + - + user-agent: + - + method: GET + uri: https://api.easypost.com/v2/users/children?page_size=5 + response: + body: + string: '{"children": [{"id": "user_484dd58db70a4f31b4bb862998cf0e04", "object": + "User", "parent_id": "user_0f6b83e3530b401cb1e8aeaa6a250d4d", "name": "Test + User", "phone_number": "", "verified": true, "created_at": "2023-05-16T22:01:20Z"}, + {"id": "user_14e894c0d541459395f4456e7cf4f175", "object": "User", "parent_id": + "user_0f6b83e3530b401cb1e8aeaa6a250d4d", "name": "Test User", "phone_number": + "", "verified": true, "created_at": "2023-09-27T22:05:26Z"}, {"id": + "user_f04df3dad13848339a7975d295d6629f", "object": "User", "parent_id": "user_0f6b83e3530b401cb1e8aeaa6a250d4d", + "name": "Test User", "phone_number": "", "verified": true, "created_at": + "2023-11-30T19:23:22Z"}], "has_more": false}' + headers: + cache-control: + - private, no-cache, no-store + content-length: + - '673' + 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: + - ec7851e765970bd2e79a7e410013d944 + x-frame-options: + - SAMEORIGIN + x-node: + - bigweb32nuq + x-permitted-cross-domain-policies: + - none + x-proxied: + - intlb1nuq 2c48984abf + - extlb1nuq 003ad9bca0 + x-runtime: + - '0.177179' + x-version-label: + - easypost-202401041812-437974c716-master + x-xss-protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/tests/test_user.py b/tests/test_user.py index db9bf428..c35556af 100644 --- a/tests/test_user.py +++ b/tests/test_user.py @@ -109,3 +109,30 @@ def test_user_update_brand(prod_api_key): assert isinstance(brand, easypost.Brand) assert str.startswith(brand.id, "brd_") assert brand.color == color + + +@pytest.mark.vcr() +def test_user_all_children(prod_api_key, page_size): + children_data = easypost.User.all_children(page_size=page_size) + + children_array = children_data["children"] + assert len(children_array) <= page_size + assert all(isinstance(child, easypost.User) for child in children_array) + + has_more = children_data["has_more"] + assert isinstance(has_more, bool) + + +@pytest.mark.vcr() +def test_user_get_next_page(prod_api_key, page_size): + try: + children = easypost.User.all_children(page_size=page_size) + next_page = easypost.User.get_next_page_of_children(children=children, page_size=page_size) + + first_id_of_first_page = children["children"][0].id + first_id_of_second_page = next_page["children"][0].id + + assert first_id_of_first_page != first_id_of_second_page + except easypost.Error as e: + if e.message != "There are no more pages to retrieve.": + raise easypost.Error(message="Test failed intentionally.") From 2b52eba562610452f5d96637386dffe74428727e Mon Sep 17 00:00:00 2001 From: "Junjie(Jack) Chen" Date: Mon, 8 Jan 2024 12:16:16 -0500 Subject: [PATCH 07/10] Remove all_children beta functions (#321) --- CHANGELOG.md | 4 +- easypost/beta/__init__.py | 1 - easypost/beta/user.py | 63 ----------------------- tests/test_beta_user.py | 102 -------------------------------------- 4 files changed, 2 insertions(+), 168 deletions(-) delete mode 100644 easypost/beta/user.py delete mode 100644 tests/test_beta_user.py diff --git a/CHANGELOG.md b/CHANGELOG.md index f6305b98..e3d19c97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,8 @@ ## v7.15.0 Next release -- Adds `all_children` function to the User class for retrieving paginated lists of children users, and deprecate the beta function -- Adds `get_next_page_of_children` function to the User class for retrieving next paginated lists of children users, and deprecate the beta function +- Adds `all_children` function to the User class for retrieving paginated lists of children users, and removes the beta function +- Adds `get_next_page_of_children` function to the User class for retrieving next paginated lists of children users, and removes the beta function ## v7.14.1 (2023-10-30) diff --git a/easypost/beta/__init__.py b/easypost/beta/__init__.py index dedb4ec3..af8ec3a2 100644 --- a/easypost/beta/__init__.py +++ b/easypost/beta/__init__.py @@ -2,4 +2,3 @@ from easypost.beta.carrier_metadata import CarrierMetadata from easypost.beta.rate import Rate from easypost.beta.referral import Referral -from easypost.beta.user import User diff --git a/easypost/beta/user.py b/easypost/beta/user.py deleted file mode 100644 index 181c02dd..00000000 --- a/easypost/beta/user.py +++ /dev/null @@ -1,63 +0,0 @@ -from typing import ( - Any, - Dict, - List, - Optional, -) -from warnings import warn - -from easypost.easypost_object import convert_to_easypost_object -from easypost.error import Error -from easypost.requestor import ( - RequestMethod, - Requestor, -) -from easypost.resource import Resource - - -class User(Resource): - @classmethod - def all_children(cls, api_key: Optional[str] = None, **params) -> Dict[str, Any]: - warn( - "This function is deprecated, please use `all_children` function in `user` class for GA accessibility", - DeprecationWarning, - stacklevel=2, - ) - """Retrieve a paginated list of children from the API.""" - requestor = Requestor(local_api_key=api_key) - url = "/users/children" - response, api_key = requestor.request(method=RequestMethod.GET, url=url, params=params, beta=True) - return convert_to_easypost_object(response=response, api_key=api_key) - - @classmethod - def get_next_page_of_children( - cls, - children: Dict[str, Any], - page_size: int, - api_key: Optional[str] = None, - ) -> List["User"]: - warn( - "This function is deprecated, please use `get_next_page_of_children` function in" - "`user` class for GA accessibility", - DeprecationWarning, - stacklevel=2, - ) - """Get next page of children.""" - requestor = Requestor(local_api_key=api_key) - url = "/users/children" - children_array = children.get("children", []) - - if len(children_array) == 0 or not children.get("has_more", False): - raise Error(message="There are no more pages to retrieve.") - - params = { - "after_id": children_array[-1].id, - "page_size": page_size, - } - - data, api_key = requestor.request(method=RequestMethod.GET, url=url, params=params, beta=True) - next_children_array: List[Any] = data.get("children", []) - if len(next_children_array) == 0: - raise Error(message="There are no more pages to retrieve.") - - return convert_to_easypost_object(response=data, api_key=api_key) diff --git a/tests/test_beta_user.py b/tests/test_beta_user.py deleted file mode 100644 index da72261c..00000000 --- a/tests/test_beta_user.py +++ /dev/null @@ -1,102 +0,0 @@ -from unittest.mock import patch - -import pytest - -import easypost - - -@pytest.mark.vcr() -def test_beta_user_all_children(prod_api_key, page_size): - children_data = easypost.beta.User.all_children(page_size=page_size) - - children_array = children_data["children"] - assert len(children_array) <= page_size - assert all(isinstance(child, easypost.User) for child in children_array) - - has_more = children_data["has_more"] - assert isinstance(has_more, bool) - - -@pytest.mark.vcr() -def test_beta_user_get_next_page(prod_api_key, page_size): - try: - children = easypost.beta.User.all_children(page_size=page_size) - next_page = easypost.beta.User.get_next_page_of_children(children=children, page_size=page_size) - - first_id_of_first_page = children["children"][0].id - first_id_of_second_page = next_page["children"][0].id - - assert first_id_of_first_page != first_id_of_second_page - except easypost.Error as e: - if e.message != "There are no more pages to retrieve.": - raise easypost.Error(message="Test failed intentionally.") - - -@pytest.mark.vcr() # Cassette not needed due to mocking, but used to avoid making real bogus API calls -def test_beta_user_get_next_page_collect_all(prod_api_key): - page_size = 1 # Doesn't matter what this is, we're mocking the response - all_children = [] - - first_page_response = { - "children": [ - { - "id": "user_123", - } - ], - "has_more": True, - } - - # Mock the initial "get all children" call - return_value = (first_page_response, prod_api_key) - with patch("easypost.requestor.Requestor.request", return_value=return_value): - first_page = easypost.beta.User.all_children(page_size=page_size) - all_children += first_page["children"] - previous_page = first_page - - second_page_response = { - "children": [ - { - "id": "user_456", - } - ], - "has_more": True, - } - - # Mock the first "get next page" call with more to collect after - # (current page "has_more" = True, next page "has_more" = True) - return_value = (second_page_response, prod_api_key) - with patch("easypost.requestor.Requestor.request", return_value=return_value): - next_page = easypost.beta.User.get_next_page_of_children( - children=previous_page, page_size=page_size # type: ignore - ) - all_children += next_page["children"] - previous_page = next_page - - third_page_response = { - "children": [ - { - "id": "user_789", - } - ], - "has_more": False, - } - - # Mock the second "get next page" call with no more to collect - # (current page "has_more" = True, next page "has_more" = False) - return_value = (third_page_response, prod_api_key) - with patch("easypost.requestor.Requestor.request", return_value=return_value): - next_page = easypost.beta.User.get_next_page_of_children( - children=previous_page, page_size=page_size # type: ignore - ) - all_children += next_page["children"] - previous_page = next_page - - # Verify we have all children (from both the "get all children" and "get next page" calls) - # Ensures that no guard clauses inside the "get next page" method are preventing us from collecting all children - assert len(all_children) == 3 - - # Now that the previous page has "has_more" = False, it should throw an error before even making the API call - with pytest.raises(easypost.Error) as error: - _ = easypost.beta.User.get_next_page_of_children(children=previous_page, page_size=page_size) # type: ignore - - assert str(error.value) == "There are no more pages to retrieve." From 72abe911557f3681db8a05662d1c681cf7a6b7ce Mon Sep 17 00:00:00 2001 From: "Junjie(Jack) Chen" Date: Mon, 8 Jan 2024 16:46:21 -0500 Subject: [PATCH 08/10] prep release for v7.15.0 (#324) --- CHANGELOG.md | 2 +- easypost/version.py | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3d19c97..06fd311b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # CHANGELOG -## v7.15.0 Next release +## v7.15.0 (2024-01-08) - Adds `all_children` function to the User class for retrieving paginated lists of children users, and removes the beta function - Adds `get_next_page_of_children` function to the User class for retrieving next paginated lists of children users, and removes the beta function diff --git a/easypost/version.py b/easypost/version.py index df251363..f5852f2c 100644 --- a/easypost/version.py +++ b/easypost/version.py @@ -1,4 +1,4 @@ -VERSION = "7.14.1" +VERSION = "7.15.0" numbers = [str(number) for number in VERSION.split(".")] VERSION_INFO = numbers diff --git a/setup.py b/setup.py index 4461826f..b4436760 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ setup( name="easypost", - version="7.14.1", + version="7.15.0", description="EasyPost Shipping API Client Library for Python", author="EasyPost", author_email="support@easypost.com", From 72a0f0742e3fec1a0efca3fb5c94d558c2765c09 Mon Sep 17 00:00:00 2001 From: Nate Harris Date: Tue, 9 Jan 2024 11:31:45 -0700 Subject: [PATCH 09/10] - Prep for v7.15.1 release (#326) --- CHANGELOG.md | 7 ++++++- easypost/version.py | 2 +- setup.py | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06fd311b..16448a4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,15 @@ # CHANGELOG -## v7.15.0 (2024-01-08) + +## v7.15.1 (2024-01-09) - Adds `all_children` function to the User class for retrieving paginated lists of children users, and removes the beta function - Adds `get_next_page_of_children` function to the User class for retrieving next paginated lists of children users, and removes the beta function +## v7.15.0 (2024-01-08) + +**This release erroneously included code from v8.x.x and v9.x.x. Use v7.15.1 instead.** + ## v7.14.1 (2023-10-30) - Fixes a bug where `get_next_page` functions threw an error, preventing users from retrieving the final page of results diff --git a/easypost/version.py b/easypost/version.py index f5852f2c..d63dd58e 100644 --- a/easypost/version.py +++ b/easypost/version.py @@ -1,4 +1,4 @@ -VERSION = "7.15.0" +VERSION = "7.15.1" numbers = [str(number) for number in VERSION.split(".")] VERSION_INFO = numbers diff --git a/setup.py b/setup.py index b4436760..449fc0d3 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ setup( name="easypost", - version="7.15.0", + version="7.15.1", description="EasyPost Shipping API Client Library for Python", author="EasyPost", author_email="support@easypost.com", From 94ffd957f5735530efeaf1221764a612009ea50e Mon Sep 17 00:00:00 2001 From: Justin Hammond <39606064+Justintime50@users.noreply.github.com> Date: Tue, 9 Jan 2024 12:21:13 -0700 Subject: [PATCH 10/10] Revert "- Prep for v7.15.1 release (#326)" (#327) This reverts commit 72a0f0742e3fec1a0efca3fb5c94d558c2765c09. --- CHANGELOG.md | 7 +------ easypost/version.py | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16448a4d..06fd311b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,10 @@ # CHANGELOG - -## v7.15.1 (2024-01-09) +## v7.15.0 (2024-01-08) - Adds `all_children` function to the User class for retrieving paginated lists of children users, and removes the beta function - Adds `get_next_page_of_children` function to the User class for retrieving next paginated lists of children users, and removes the beta function -## v7.15.0 (2024-01-08) - -**This release erroneously included code from v8.x.x and v9.x.x. Use v7.15.1 instead.** - ## v7.14.1 (2023-10-30) - Fixes a bug where `get_next_page` functions threw an error, preventing users from retrieving the final page of results diff --git a/easypost/version.py b/easypost/version.py index d63dd58e..f5852f2c 100644 --- a/easypost/version.py +++ b/easypost/version.py @@ -1,4 +1,4 @@ -VERSION = "7.15.1" +VERSION = "7.15.0" numbers = [str(number) for number in VERSION.split(".")] VERSION_INFO = numbers diff --git a/setup.py b/setup.py index 449fc0d3..b4436760 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ setup( name="easypost", - version="7.15.1", + version="7.15.0", description="EasyPost Shipping API Client Library for Python", author="EasyPost", author_email="support@easypost.com",