diff --git a/EasyPost.Tests/ClientTest.cs b/EasyPost.Tests/ClientTest.cs index 8b46946e6..c9559ebd3 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; @@ -7,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 { @@ -301,5 +302,24 @@ 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); + + JArray addresses = response["addresses"] as JArray; + Assert.Single(addresses); + JObject firstAddress = addresses[0] as JObject; + Assert.Equal("Address", firstAddress["object"].ToString()); + } } } 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. ///