Skip to content

Commit 0a135ef

Browse files
authored
Functionality to get the next page of a paginated collection (#261)
1 parent 275dad4 commit 0a135ef

35 files changed

+1620
-53
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# CHANGELOG
22

3+
## Next release
4+
5+
- Add `get_next_page` function that retrieves the next page of results for a paginated collection
6+
37
## v7.10.0 (2023-02-17)
48

59
- Adds beta `retrieve_stateless_rates` function

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ clean:
2727

2828
## coverage - Test the project and generate an HTML coverage report
2929
coverage:
30-
$(VIRTUAL_BIN)/pytest --cov=$(PROJECT_NAME) --cov-branch --cov-report=html --cov-report=term-missing --cov-fail-under=88
30+
$(VIRTUAL_BIN)/pytest --cov=$(PROJECT_NAME) --cov-branch --cov-report=html --cov-report=term-missing --cov-fail-under=87
3131

3232
## docs - Generates docs for the library
3333
docs:

easypost/address.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@
1010
RequestMethod,
1111
Requestor,
1212
)
13-
from easypost.resource import AllResource
13+
from easypost.resource import (
14+
AllResource,
15+
NextPageResource,
16+
)
1417

1518

16-
class Address(AllResource):
19+
class Address(AllResource, NextPageResource):
1720
@classmethod
1821
def create(
1922
cls,

easypost/event.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@
1010
RequestMethod,
1111
Requestor,
1212
)
13-
from easypost.resource import AllResource
13+
from easypost.resource import (
14+
AllResource,
15+
NextPageResource,
16+
)
1417

1518

16-
class Event(AllResource):
19+
class Event(AllResource, NextPageResource):
1720
@classmethod
1821
def receive(cls, values: str) -> "Event":
1922
return convert_to_easypost_object(response=json.loads(s=values), api_key=easypost.api_key)

easypost/insurance.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from easypost.resource import (
22
AllResource,
33
CreateResource,
4+
NextPageResource,
45
)
56

67

7-
class Insurance(AllResource, CreateResource):
8+
class Insurance(AllResource, CreateResource, NextPageResource):
89
pass

easypost/pickup.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@
77
from easypost.resource import (
88
AllResource,
99
CreateResource,
10+
NextPageResource,
1011
)
1112
from easypost.util import get_lowest_object_rate
1213

1314

14-
class Pickup(CreateResource, AllResource):
15+
class Pickup(CreateResource, AllResource, NextPageResource):
1516
def buy(self, **params) -> "Pickup":
1617
"""Buy a pickup."""
1718
requestor = Requestor(local_api_key=self._api_key)

easypost/referral.py

+26
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,32 @@ def all(
7878
)
7979
return convert_to_easypost_object(response=response, api_key=api_key)
8080

81+
@classmethod
82+
def get_next_page(
83+
cls,
84+
referrals: Dict[str, Any],
85+
page_size: int,
86+
api_key: Optional[str] = None,
87+
) -> List["Referral"]:
88+
"""Retrieve next page of a referral customers."""
89+
requestor = Requestor(local_api_key=api_key)
90+
url = "/referral_customers"
91+
referral_array = referrals["referral_customers"]
92+
93+
if referral_array is None or len(referral_array) == 0 or not referrals.get("has_more"):
94+
raise Error(message="There are no more pages to retrieve.")
95+
96+
params = {
97+
"before_id": referral_array[-1].id,
98+
"page_size": page_size,
99+
}
100+
101+
response, api_key = requestor.request(method=RequestMethod.GET, url=url, params=params)
102+
if response is None or len(response["referral_customers"]) == 0 or not response["has_more"]:
103+
raise Error(message="There are no more pages to retrieve.")
104+
105+
return convert_to_easypost_object(response=response, api_key=api_key)
106+
81107
@staticmethod
82108
def add_credit_card(
83109
referral_api_key: str,

easypost/refund.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from easypost.resource import (
22
AllResource,
33
CreateResource,
4+
NextPageResource,
45
)
56

67

7-
class Refund(CreateResource, AllResource):
8+
class Refund(CreateResource, AllResource, NextPageResource):
89
pass

easypost/report.py

+29-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
from typing import Optional
1+
from typing import (
2+
Any,
3+
Dict,
4+
List,
5+
Optional,
6+
)
27

38
from easypost.easypost_object import convert_to_easypost_object
49
from easypost.requestor import (
@@ -10,17 +15,37 @@
1015

1116
class Report(Resource):
1217
@classmethod
13-
def create(cls, api_key: Optional[str] = None, **params):
18+
def create(cls, api_key: Optional[str] = None, **params) -> "Report":
1419
"""Create a report."""
1520
requestor = Requestor(local_api_key=api_key)
1621
url = f"{cls.class_url()}/{params.get('type')}"
1722
response, api_key = requestor.request(method=RequestMethod.POST, url=url, params=params)
1823
return convert_to_easypost_object(response=response, api_key=api_key)
1924

2025
@classmethod
21-
def all(cls, api_key: Optional[str] = None, **params):
26+
def all(cls, api_key: Optional[str] = None, **params) -> List["Report"]:
2227
"""Retrieve all reports."""
2328
requestor = Requestor(local_api_key=api_key)
24-
url = f"{cls.class_url()}/{params.get('type')}"
29+
type = params.pop("type")
30+
url = f"{cls.class_url()}/{type}"
31+
response, api_key = requestor.request(method=RequestMethod.GET, url=url, params=params)
32+
response["type"] = type
33+
return convert_to_easypost_object(response=response, api_key=api_key)
34+
35+
@classmethod
36+
def get_next_page(
37+
cls,
38+
reports: Dict[str, Any],
39+
page_size: int = None,
40+
api_key: Optional[str] = None,
41+
) -> List["Report"]:
42+
"""Get next page of Report collection"""
43+
requestor = Requestor(local_api_key=api_key)
44+
type = reports.get("type")
45+
url = f"{cls.class_url()}/{type}"
46+
params = {
47+
"before_id": reports["reports"][-1].id,
48+
"page_size": page_size,
49+
}
2550
response, api_key = requestor.request(method=RequestMethod.GET, url=url, params=params)
2651
return convert_to_easypost_object(response=response, api_key=api_key)

easypost/requestor.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def request(
8484
params: Optional[Dict[str, Any]] = None,
8585
api_key_required: bool = True,
8686
beta: bool = False,
87-
) -> Tuple[dict, Optional[str]]:
87+
) -> Tuple[Dict[str, Any], Optional[str]]:
8888
"""Make a request to the EasyPost API."""
8989
if params is None:
9090
params = {}

easypost/resource.py

+34
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import re
22
from typing import (
33
Any,
4+
Dict,
45
List,
56
Optional,
67
)
@@ -65,6 +66,39 @@ def all(cls, api_key: Optional[str] = None, **params) -> List[Any]:
6566
return convert_to_easypost_object(response=response, api_key=api_key)
6667

6768

69+
class NextPageResource(Resource):
70+
@classmethod
71+
def get_next_page(
72+
cls,
73+
collection: Dict[str, Any],
74+
page_size: int,
75+
api_key: Optional[str] = None,
76+
optional_params: Optional[Dict[str, Any]] = None,
77+
) -> List[Any]:
78+
"""Retrieve next page of a specific collection."""
79+
requestor = Requestor(local_api_key=api_key)
80+
url = cls.class_url()
81+
collection_array = collection.get(url[1:])
82+
83+
if collection_array is None or len(collection_array) == 0 or not collection.get("has_more"):
84+
raise Error(message="There are no more pages to retrieve.")
85+
86+
params = {
87+
"before_id": collection_array[-1].id,
88+
"page_size": page_size,
89+
}
90+
91+
if optional_params:
92+
params.update(optional_params)
93+
94+
response, api_key = requestor.request(method=RequestMethod.GET, url=url, params=params)
95+
response_array: List[Any] = response.get(url[1:]) # type: ignore
96+
if response is None or len(response_array) == 0 or not response.get("has_more"):
97+
raise Error(message="There are no more pages to retrieve.")
98+
99+
return convert_to_easypost_object(response=response, api_key=api_key)
100+
101+
68102
class CreateResource(Resource):
69103
@classmethod
70104
def create(cls, api_key: Optional[str] = None, **params) -> Any:

easypost/scanform.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
RequestMethod,
66
Requestor,
77
)
8-
from easypost.resource import AllResource
8+
from easypost.resource import (
9+
AllResource,
10+
NextPageResource,
11+
)
912

1013

11-
class ScanForm(AllResource):
14+
class ScanForm(AllResource, NextPageResource):
1215
@classmethod
13-
def create(cls, api_key: Optional[str] = None, **params):
16+
def create(cls, api_key: Optional[str] = None, **params) -> "ScanForm":
1417
"""Create a scanform."""
1518
requestor = Requestor(local_api_key=api_key)
1619
url = cls.class_url()

easypost/shipment.py

+26-2
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,35 @@
1212
RequestMethod,
1313
Requestor,
1414
)
15-
from easypost.resource import AllResource
15+
from easypost.resource import NextPageResource
1616
from easypost.util import get_lowest_object_rate
1717

1818

19-
class Shipment(AllResource):
19+
class Shipment(NextPageResource):
20+
@classmethod
21+
def all(cls, api_key: Optional[str] = None, **params) -> List["Shipment"]:
22+
"""Retrieve a list of Shipments."""
23+
requestor = Requestor(local_api_key=api_key)
24+
response, api_key = requestor.request(method=RequestMethod.GET, url="/shipments", params=params)
25+
response["include_children"] = params.get("include_children")
26+
response["purchased"] = params.get("purchased")
27+
return convert_to_easypost_object(response=response, api_key=api_key)
28+
29+
@classmethod
30+
def get_next_page(
31+
cls,
32+
shipments: Dict[str, Any],
33+
page_size: int,
34+
api_key: Optional[str] = None,
35+
optional_params: Optional[Dict[str, Any]] = None,
36+
) -> List["Shipment"]:
37+
"""Get next page of shipment collection."""
38+
optional_params = {
39+
"include_children": shipments.get("include_children"),
40+
"purchased": shipments.get("purchased"),
41+
}
42+
return super().get_next_page(shipments, page_size, api_key, optional_params)
43+
2044
@classmethod
2145
def create(cls, api_key: Optional[str] = None, with_carbon_offset: Optional[bool] = False, **params) -> "Shipment":
2246
"""Create an Shipment object."""

easypost/tracker.py

+28-1
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,19 @@
55
Optional,
66
)
77

8+
from easypost.easypost_object import convert_to_easypost_object
89
from easypost.requestor import (
910
RequestMethod,
1011
Requestor,
1112
)
1213
from easypost.resource import (
1314
AllResource,
1415
CreateResource,
16+
NextPageResource,
1517
)
1618

1719

18-
class Tracker(AllResource, CreateResource):
20+
class Tracker(AllResource, CreateResource, NextPageResource):
1921
@classmethod
2022
def create_list(cls, trackers: List[Dict[str, Any]], api_key: Optional[str] = None) -> bool:
2123
"""Create a list of trackers."""
@@ -24,3 +26,28 @@ def create_list(cls, trackers: List[Dict[str, Any]], api_key: Optional[str] = No
2426
new_params = {"trackers": trackers}
2527
_, _ = requestor.request(method=RequestMethod.POST, url=url, params=new_params)
2628
return True
29+
30+
@classmethod
31+
def all(cls, api_key: Optional[str] = None, **params) -> List["Tracker"]:
32+
"""Retrieve a list of Trackers."""
33+
requestor = Requestor(local_api_key=api_key)
34+
url = cls.class_url()
35+
response, api_key = requestor.request(method=RequestMethod.GET, url=url, params=params)
36+
response["tracking_code"] = params.get("tracking_code")
37+
response["carrier"] = params.get("carrier")
38+
return convert_to_easypost_object(response=response, api_key=api_key)
39+
40+
@classmethod
41+
def get_next_page(
42+
cls,
43+
trackers: Dict[str, Any],
44+
page_size: int,
45+
api_key: Optional[str] = None,
46+
optional_params: Optional[Dict[str, Any]] = None,
47+
) -> List["Tracker"]:
48+
"""Get next page of Tracker collection."""
49+
optional_params = {
50+
"tracking_code": trackers.get("tracking_code"),
51+
"carrier": trackers.get("carrier"),
52+
}
53+
return super().get_next_page(trackers, page_size, api_key, optional_params)

0 commit comments

Comments
 (0)