From e34f1792b180fca20f3b07234270a0351d2d2b5e Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Thu, 12 Feb 2026 10:31:51 -0700 Subject: [PATCH 1/2] 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 --- EasyPost.Tests/ClientTest.cs | 18 +++++++ .../cassettes/net/client/make_api_call.json | 48 +++++++++++++++++++ EasyPost/_base/EasyPostClient.cs | 16 +++++++ 3 files changed, 82 insertions(+) create mode 100644 EasyPost.Tests/cassettes/net/client/make_api_call.json diff --git a/EasyPost.Tests/ClientTest.cs b/EasyPost.Tests/ClientTest.cs index 8b46946e6..6c05da776 100644 --- a/EasyPost.Tests/ClientTest.cs +++ b/EasyPost.Tests/ClientTest.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Threading; @@ -301,5 +302,22 @@ public async Task TestCancellationToken() // this will not record a cassette because the request should be cancelled before it is sent await Assert.ThrowsAsync(async () => await Client.Address.Create(new Parameters.Address.Create(), token)); } + + [Fact] + [Testing.Function] + public async Task TestClientMakeApiCall() + { + UseVCR("make_api_call"); + + Dictionary parameters = new() + { + { "page_size", 1 }, + }; + + Dictionary response = await Client.MakeApiCallAsync(Http.Method.Get, "/addresses", parameters); + + Assert.NotNull(response); + Assert.True(response.ContainsKey("addresses")); + } } } diff --git a/EasyPost.Tests/cassettes/net/client/make_api_call.json b/EasyPost.Tests/cassettes/net/client/make_api_call.json new file mode 100644 index 000000000..cbfb9ef0e --- /dev/null +++ b/EasyPost.Tests/cassettes/net/client/make_api_call.json @@ -0,0 +1,48 @@ +[ + { + "Duration": 378, + "RecordedAt": "2026-02-12T10:31:22.902545-07:00", + "Request": { + "Body": "", + "BodyContentType": "Text", + "ContentHeaders": {}, + "Method": "GET", + "RequestHeaders": { + "Authorization": "", + "User-Agent": "" + }, + "Uri": "https://api.easypost.com/v2//addresses?page_size=1" + }, + "Response": { + "Body": "{\"addresses\":[{\"id\":\"adr_f82e0f69015611f18f59ac1f6bc539ae\",\"object\":\"Address\",\"created_at\":\"2026-02-03T23:20:46Z\",\"updated_at\":\"2026-02-03T23:20:46Z\",\"name\":\"Jack Sparrow\",\"company\":null,\"street1\":\"388 Townsend St\",\"street2\":\"Apt 20\",\"city\":\"San Francisco\",\"state\":\"CA\",\"zip\":\"94107\",\"country\":\"US\",\"phone\":\"\",\"email\":\"\",\"mode\":\"test\",\"carrier_facility\":null,\"residential\":null,\"federal_tax_id\":null,\"state_tax_id\":null,\"verifications\":{}}],\"has_more\":true}", + "BodyContentType": "Json", + "ContentHeaders": { + "Expires": "0", + "Content-Type": "application/json; charset=utf-8", + "Content-Length": "483" + }, + "HttpVersion": "1.1", + "ResponseHeaders": { + "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": "f785caec698e0e6ae787d0db0122ce38", + "Cache-Control": "no-store, no-cache, private", + "Pragma": "no-cache", + "x-runtime": "0.044311", + "x-node": "bigweb42nuq", + "x-version-label": "easypost-202602121702-bfe72e7ac7-master", + "x-backend": "easypost", + "x-proxied": "intlb4nuq 0dcc3a6efb,extlb2nuq c01291cd8f", + "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload" + }, + "Status": { + "Code": 200, + "Message": "OK" + } + } + } +] diff --git a/EasyPost/_base/EasyPostClient.cs b/EasyPost/_base/EasyPostClient.cs index c95910572..71ca05315 100644 --- a/EasyPost/_base/EasyPostClient.cs +++ b/EasyPost/_base/EasyPostClient.cs @@ -181,6 +181,22 @@ public async Task RequestAsync(Method method, string endpoint, ApiVersion return errorRaised; } + /// + /// 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. + /// + /// HTTP to use for the request. + /// EasyPost API endpoint to use for the request. + /// Parameters to use for the request. + /// to use for the HTTP request. + /// A representing the response data. + public async Task> MakeApiCallAsync(Method method, string endpoint, Dictionary parameters, CancellationToken cancellationToken = default) + { + return await RequestAsync>(method, endpoint, ApiVersion.Current, cancellationToken, parameters); + } + /// /// Compare this to another object for equality. /// From ed4bde5e1f637c8a5c24e6ed3f1ab16721dc4b57 Mon Sep 17 00:00:00 2001 From: Justintime50 <39606064+Justintime50@users.noreply.github.com> Date: Thu, 12 Feb 2026 11:00:36 -0700 Subject: [PATCH 2/2] fix: update test to properly handle JSON deserialization with JArray/JObject --- EasyPost.Tests/ClientTest.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/EasyPost.Tests/ClientTest.cs b/EasyPost.Tests/ClientTest.cs index 6c05da776..c9559ebd3 100644 --- a/EasyPost.Tests/ClientTest.cs +++ b/EasyPost.Tests/ClientTest.cs @@ -8,8 +8,8 @@ using EasyPost.Exceptions.API; using EasyPost.Tests._Utilities; using EasyPost.Tests._Utilities.Attributes; +using Newtonsoft.Json.Linq; using Xunit; -using CustomAssertions = EasyPost.Tests._Utilities.Assertions.Assert; namespace EasyPost.Tests { @@ -316,8 +316,10 @@ public async Task TestClientMakeApiCall() Dictionary response = await Client.MakeApiCallAsync(Http.Method.Get, "/addresses", parameters); - Assert.NotNull(response); - Assert.True(response.ContainsKey("addresses")); + JArray addresses = response["addresses"] as JArray; + Assert.Single(addresses); + JObject firstAddress = addresses[0] as JObject; + Assert.Equal("Address", firstAddress["object"].ToString()); } } }