diff --git a/CHANGELOG.md b/CHANGELOG.md index 739ac96..12f534c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Next Release - Adds `UspsShipAccount` support to the create carrier method +- Adds `tracker.retrieve_batch` function ## v10.1.0 (2025-06-18) diff --git a/easypost/services/tracker_service.py b/easypost/services/tracker_service.py index 5a35bdd..734f835 100644 --- a/easypost/services/tracker_service.py +++ b/easypost/services/tracker_service.py @@ -4,7 +4,12 @@ ) from easypost.constant import _FILTERS_KEY +from easypost.easypost_object import convert_to_easypost_object from easypost.models import Tracker +from easypost.requestor import ( + RequestMethod, + Requestor, +) from easypost.services.base_service import BaseService @@ -31,6 +36,14 @@ def all(self, **params) -> dict[str, Any]: return self._all_resources(self._model_class, filters, **params) + def retrieve_batch(self, **params) -> Tracker: + """Retrieve a batch of Trackers.""" + url = "/trackers/batch" + + response = Requestor(self._client).request(method=RequestMethod.POST, url=url, params=params) + + return convert_to_easypost_object(response=response) + def retrieve(self, id: str) -> Tracker: """Retrieve a Tracker.""" return self._retrieve_resource(self._model_class, id) diff --git a/tests/cassettes/test_tracker_retrieve_batch.yaml b/tests/cassettes/test_tracker_retrieve_batch.yaml new file mode 100644 index 0000000..5631687 --- /dev/null +++ b/tests/cassettes/test_tracker_retrieve_batch.yaml @@ -0,0 +1,180 @@ +interactions: +- request: + body: '{"tracker": {"tracking_code": "EZ1000000001"}}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '46' + Content-Type: + - application/json + authorization: + - + user-agent: + - + method: POST + uri: https://api.easypost.com/v2/trackers + response: + body: + string: '{"id": "trk_52d78046d6a14e8b92af8b8546080a05", "object": "Tracker", + "mode": "test", "tracking_code": "EZ1000000001", "status": "pre_transit", + "status_detail": "status_update", "created_at": "2025-11-05T21:59:07Z", "updated_at": + "2025-11-05T21:59:07Z", "signed_by": null, "weight": null, "est_delivery_date": + "2025-11-05T21:59:07Z", "shipment_id": null, "carrier": "USPS", "tracking_details": + [{"object": "TrackingDetail", "message": "Pre-Shipment Info Sent to USPS", + "description": null, "status": "pre_transit", "status_detail": "status_update", + "datetime": "2025-10-05T21:59:07Z", "source": "USPS", "carrier_code": null, + "tracking_location": {"object": "TrackingLocation", "city": null, "state": + null, "country": null, "zip": null}, "est_delivery_date": null}, {"object": + "TrackingDetail", "message": "Shipping Label Created", "description": null, + "status": "pre_transit", "status_detail": "status_update", "datetime": "2025-10-06T10:36:07Z", + "source": "USPS", "carrier_code": null, "tracking_location": {"object": "TrackingLocation", + "city": "HOUSTON", "state": "TX", "country": null, "zip": "77063"}, "est_delivery_date": + null}], "carrier_detail": {"object": "CarrierDetail", "service": "First-Class + Package Service", "container_type": null, "est_delivery_date_local": null, + "est_delivery_time_local": null, "origin_location": "HOUSTON TX, 77001", "origin_tracking_location": + {"object": "TrackingLocation", "city": "HOUSTON", "state": "TX", "country": + null, "zip": "77063"}, "destination_location": "CHARLESTON SC, 29401", "destination_tracking_location": + null, "guaranteed_delivery_date": null, "alternate_identifier": null, "initial_delivery_attempt": + null}, "delivery_evidence": [], "finalized": true, "is_return": false, "public_url": + "https://track.easypost.com/djE6dHJrXzUyZDc4MDQ2ZDZhMTRlOGI5MmFmOGI4NTQ2MDgwYTA1", + "fees": [{"object": "Fee", "type": "TrackerFee", "amount": "0.02000", "charged": + false, "refunded": false}]}' + headers: + cache-control: + - private, no-cache, no-store + content-length: + - '1797' + content-type: + - application/json; charset=utf-8 + expires: + - '0' + location: + - /api/v2/trackers/trk_52d78046d6a14e8b92af8b8546080a05 + 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: + - 38fab072690bc8aae7994c3e024d2342 + x-frame-options: + - SAMEORIGIN + x-node: + - bigweb64nuq + x-permitted-cross-domain-policies: + - none + x-proxied: + - intlb4nuq c0061e0a2e + - extlb2nuq cbbd141214 + x-runtime: + - '0.122872' + x-version-label: + - easypost-202511052124-83d556159a-master + x-xss-protection: + - 1; mode=block + status: + code: 201 + message: Created +- request: + body: '{"tracking_codes": ["EZ1000000001"]}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + Content-Length: + - '36' + Content-Type: + - application/json + authorization: + - + user-agent: + - + method: POST + uri: https://api.easypost.com/v2/trackers/batch + response: + body: + string: '{"trackers": [{"id": "trk_52d78046d6a14e8b92af8b8546080a05", "object": + "Tracker", "mode": "test", "tracking_code": "EZ1000000001", "status": "pre_transit", + "status_detail": "status_update", "created_at": "2025-11-05T21:59:07Z", "updated_at": + "2025-11-05T21:59:07Z", "signed_by": null, "weight": null, "est_delivery_date": + "2025-11-05T21:59:07Z", "shipment_id": null, "carrier": "USPS", "tracking_details": + [{"object": "TrackingDetail", "message": "Pre-Shipment Info Sent to USPS", + "description": "", "status": "pre_transit", "status_detail": "status_update", + "datetime": "2025-10-05T21:59:07Z", "source": "USPS", "carrier_code": "", + "tracking_location": {"object": "TrackingLocation", "city": null, "state": + null, "country": null, "zip": null}, "est_delivery_date": null}, {"object": + "TrackingDetail", "message": "Shipping Label Created", "description": "", + "status": "pre_transit", "status_detail": "status_update", "datetime": "2025-10-06T10:36:07Z", + "source": "USPS", "carrier_code": "", "tracking_location": {"object": "TrackingLocation", + "city": "HOUSTON", "state": "TX", "country": null, "zip": "77063"}, "est_delivery_date": + null}], "fees": [{"object": "Fee", "type": "TrackerFee", "amount": "0.02000", + "charged": true, "refunded": false}], "carrier_detail": {"object": "CarrierDetail", + "service": "First-Class Package Service", "container_type": null, "est_delivery_date_local": + null, "est_delivery_time_local": null, "origin_location": "HOUSTON TX, 77001", + "origin_tracking_location": {"object": "TrackingLocation", "city": "HOUSTON", + "state": "TX", "country": null, "zip": "77063"}, "destination_location": "CHARLESTON + SC, 29401", "destination_tracking_location": null, "guaranteed_delivery_date": + null, "alternate_identifier": null, "initial_delivery_attempt": null}, "delivery_evidence": + [], "public_url": "https://track.easypost.com/djE6dHJrXzUyZDc4MDQ2ZDZhMTRlOGI5MmFmOGI4NTQ2MDgwYTA1"}], + "has_more": false}' + headers: + cache-control: + - private, no-cache, no-store + content-length: + - '1785' + 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: + - 38fab075690bc8abe7994c40024d239e + x-frame-options: + - SAMEORIGIN + x-node: + - bigweb55nuq + x-permitted-cross-domain-policies: + - none + x-proxied: + - intlb4nuq c0061e0a2e + - extlb2nuq cbbd141214 + x-runtime: + - '0.055965' + x-version-label: + - easypost-202511052124-83d556159a-master + x-xss-protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/tests/test_tracker.py b/tests/test_tracker.py index 557d5c0..afc71c0 100644 --- a/tests/test_tracker.py +++ b/tests/test_tracker.py @@ -38,6 +38,17 @@ def test_tracker_all(page_size, test_client): assert all(isinstance(tracker, Tracker) for tracker in trackers_array) +@pytest.mark.vcr() +def test_tracker_retrieve_batch(page_size, test_client): + tracker = test_client.tracker.create(tracking_code="EZ1000000001") + + trackers = test_client.tracker.retrieve_batch(tracking_codes=[tracker.tracking_code]) + + trackers_array = trackers["trackers"] + + assert all(isinstance(tracker, Tracker) for tracker in trackers_array) + + @pytest.mark.vcr() def test_tracker_get_next_page(page_size, test_client): try: