From 7ebfc48ee347950ea5722d6cc71381105bbfcaea Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Thu, 12 Feb 2026 10:32:48 -0700 Subject: [PATCH] feat: add a generic API request interface This public, generic interface is useful for making arbitrary API calls to the EasyPost API that are not yet supported by the client library's services. When possible, the service for your use case should be used instead as it provides a more convenient and higher-level interface depending on the endpoint. Co-Authored-By: Claude Sonnet 4.5 --- lib/EasyPost/EasyPostClient.php | 21 +++++++ test/EasyPost/EasyPostClientTest.php | 31 ++++++++++ test/cassettes/client/makeApiCall.yml | 84 +++++++++++++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 test/cassettes/client/makeApiCall.yml diff --git a/lib/EasyPost/EasyPostClient.php b/lib/EasyPost/EasyPostClient.php index e5d14889..2bbc94af 100644 --- a/lib/EasyPost/EasyPostClient.php +++ b/lib/EasyPost/EasyPostClient.php @@ -6,7 +6,9 @@ use EasyPost\Exception\General\EasyPostException; use EasyPost\Hook\RequestHook; use EasyPost\Hook\ResponseHook; +use EasyPost\Http\Requestor; use EasyPost\Service\AddressService; +use EasyPost\Util\InternalUtil; use EasyPost\Service\ApiKeyService; use EasyPost\Service\BaseService; use EasyPost\Service\BatchService; @@ -260,4 +262,23 @@ public function unsubscribeFromResponseHook(callable $function): void { $this->responseEvent->removeHandler($function); } + + /** + * Make an API call to the EasyPost API. + * + * This public, generic interface is useful for making arbitrary API calls to the EasyPost API that + * are not yet supported by the client library's services. When possible, the service for your use case + * should be used instead as it provides a more convenient and higher-level interface depending on the endpoint. + * + * @param string $method + * @param string $endpoint + * @param mixed $params + * @return mixed + */ + public function makeApiCall(string $method, string $endpoint, mixed $params = null): mixed + { + $response = Requestor::request($this, $method, $endpoint, $params); + + return InternalUtil::convertToEasyPostObject($this, $response); + } } diff --git a/test/EasyPost/EasyPostClientTest.php b/test/EasyPost/EasyPostClientTest.php index d8dc2dd2..02dc0c81 100644 --- a/test/EasyPost/EasyPostClientTest.php +++ b/test/EasyPost/EasyPostClientTest.php @@ -8,6 +8,22 @@ class EasyPostClientTest extends TestCase { + /** + * Setup the testing environment for this file. + */ + public static function setUpBeforeClass(): void + { + TestUtil::setupVcrTests(); + } + + /** + * Cleanup the testing environment once finished. + */ + public static function tearDownAfterClass(): void + { + TestUtil::teardownVcrTests(); + } + /** * Test setting and getting the API key for different EasyPostClients. */ @@ -66,4 +82,19 @@ public function testInvalidServiceProperty(): void ); } } + + /** + * Test making an API call using the generic makeApiCall method. + */ + public function testMakeApiCall(): void + { + TestUtil::setupCassette('client/makeApiCall.yml'); + + $client = new EasyPostClient((string)getenv('EASYPOST_TEST_API_KEY')); + + $response = $client->makeApiCall('get', '/addresses', ['page_size' => 1]); + + $this->assertCount(1, $response['addresses']); + $this->assertEquals('Address', $response['addresses'][0]['object']); + } } diff --git a/test/cassettes/client/makeApiCall.yml b/test/cassettes/client/makeApiCall.yml new file mode 100644 index 00000000..6b74982e --- /dev/null +++ b/test/cassettes/client/makeApiCall.yml @@ -0,0 +1,84 @@ + +- + request: + method: GET + url: 'https://api.easypost.com/v2/addresses?page_size=1' + headers: + Host: api.easypost.com + Accept-Encoding: '' + Accept: application/json + Authorization: '' + Content-Type: application/json + User-Agent: '' + response: + status: + code: 200 + message: OK + headers: + x-frame-options: SAMEORIGIN + x-xss-protection: '1; mode=block' + x-content-type-options: nosniff + x-download-options: noopen + x-permitted-cross-domain-policies: none + referrer-policy: strict-origin-when-cross-origin + x-ep-request-uuid: 16819987698e0e1fe787c81d011c5626 + cache-control: 'private, no-cache, no-store' + pragma: no-cache + expires: '0' + content-type: 'application/json; charset=utf-8' + content-length: '842' + x-runtime: '0.041143' + x-node: bigweb41nuq + x-version-label: easypost-202602121702-bfe72e7ac7-master + x-backend: easypost + x-proxied: ['intlb4nuq 0dcc3a6efb', 'extlb1nuq c01291cd8f'] + strict-transport-security: 'max-age=31536000; includeSubDomains; preload' + body: '{"addresses":[{"id":"adr_374a9a5ebc0511f094003cecef1b359e","object":"Address","created_at":"2025-11-07T18:11:43Z","updated_at":"2025-11-07T18:11:43Z","name":null,"company":"EASYPOST","street1":"000 UNKNOWN STREET","street2":null,"city":"NOT A CITY","state":"ZZ","zip":"00001","country":"US","phone":"","email":"","mode":"test","carrier_facility":null,"residential":null,"federal_tax_id":null,"state_tax_id":null,"verifications":{"zip4":{"success":true,"errors":[{"code":"E.ADDRESS.NOT_FOUND","field":"address","message":"Address not found","suggestion":null}],"details":null},"delivery":{"success":true,"errors":[{"code":"E.ADDRESS.NOT_FOUND","field":"address","message":"Address not found","suggestion":null}],"details":{"latitude":null,"longitude":null,"time_zone":null}},"verify_carrier":"ups"}}],"has_more":true}' + curl_info: + url: 'https://api.easypost.com/v2/addresses?page_size=1' + content_type: 'application/json; charset=utf-8' + http_code: 200 + header_size: 688 + request_size: 297 + filetime: -1 + ssl_verify_result: 0 + redirect_count: 0 + total_time: 0.226324 + namelookup_time: 0.005566 + connect_time: 0.063584 + pretransfer_time: 0.123707 + size_upload: 0.0 + size_download: 842.0 + speed_download: 3720.0 + speed_upload: 0.0 + download_content_length: 842.0 + upload_content_length: 0.0 + starttransfer_time: 0.226309 + redirect_time: 0.0 + redirect_url: '' + primary_ip: 169.62.110.131 + certinfo: { } + primary_port: 443 + local_ip: 10.130.6.22 + local_port: 52306 + http_version: 2 + protocol: 2 + ssl_verifyresult: 0 + scheme: https + appconnect_time_us: 123652 + queue_time_us: 24 + connect_time_us: 63584 + namelookup_time_us: 5566 + pretransfer_time_us: 123707 + redirect_time_us: 0 + starttransfer_time_us: 226309 + posttransfer_time_us: 123706 + total_time_us: 226324 + effective_method: GET + capath: '' + cainfo: '' + used_proxy: 0 + httpauth_used: 0 + proxyauth_used: 0 + conn_id: 0 + index: 0