Skip to content

Commit

Permalink
feat: add method to initialize SDK with an httpClient
Browse files Browse the repository at this point in the history
  • Loading branch information
jpill committed Jun 27, 2024
1 parent 17196b2 commit e3b660f
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 30 deletions.
9 changes: 8 additions & 1 deletion ShipEngine/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ override ShipEngineSDK.Common.MonetaryValueConverter.Write(System.Text.Json.Utf8
readonly ShipEngineSDK.Config.ApiKey -> string!
readonly ShipEngineSDK.Config.Retries -> int
readonly ShipEngineSDK.Config.Timeout -> System.TimeSpan
readonly ShipEngineSDK.ShipEngineClient.JsonSerializerOptions -> System.Text.Json.JsonSerializerOptions!
ShipEngineSDK.Common.Address
ShipEngineSDK.Common.Address.Address() -> void
ShipEngineSDK.Common.Address.AddressLine1.get -> string?
Expand Down Expand Up @@ -1171,12 +1170,14 @@ ShipEngineSDK.ShipEngine.CreateLabelFromShipmentDetails(ShipEngineSDK.CreateLabe
ShipEngineSDK.ShipEngine.CreateLabelFromShipmentDetails(ShipEngineSDK.CreateLabelFromShipmentDetails.Params! labelParams, ShipEngineSDK.Config! methodConfig) -> System.Threading.Tasks.Task<ShipEngineSDK.CreateLabelFromShipmentDetails.Result!>!
ShipEngineSDK.ShipEngine.CreateManifest(ShipEngineSDK.Config! methodConfig, ShipEngineSDK.Manifests.Params! manifestParams) -> System.Threading.Tasks.Task<ShipEngineSDK.Manifests.Result!>!
ShipEngineSDK.ShipEngine.CreateManifest(ShipEngineSDK.Manifests.Params! manifestParams) -> System.Threading.Tasks.Task<ShipEngineSDK.Manifests.Result!>!
ShipEngineSDK.ShipEngine.Dispose() -> void
ShipEngineSDK.ShipEngine.GetRatesWithShipmentDetails(ShipEngineSDK.GetRatesWithShipmentDetails.Params! rateParams) -> System.Threading.Tasks.Task<ShipEngineSDK.GetRatesWithShipmentDetails.Result!>!
ShipEngineSDK.ShipEngine.GetRatesWithShipmentDetails(ShipEngineSDK.GetRatesWithShipmentDetails.Params! rateParams, ShipEngineSDK.Config! methodConfig) -> System.Threading.Tasks.Task<ShipEngineSDK.GetRatesWithShipmentDetails.Result!>!
ShipEngineSDK.ShipEngine.ListCarriers() -> System.Threading.Tasks.Task<ShipEngineSDK.ListCarriers.Result!>!
ShipEngineSDK.ShipEngine.ListCarriers(ShipEngineSDK.Config! methodConfig) -> System.Threading.Tasks.Task<ShipEngineSDK.ListCarriers.Result!>!
ShipEngineSDK.ShipEngine.ShipEngine(ShipEngineSDK.Config! config) -> void
ShipEngineSDK.ShipEngine.ShipEngine(string! apiKey) -> void
ShipEngineSDK.ShipEngine.ShipEngine(System.Net.Http.HttpClient! httpClient) -> void
ShipEngineSDK.ShipEngine.TrackUsingCarrierCodeAndTrackingNumber(string! trackingNumber, string! carrierCode) -> System.Threading.Tasks.Task<ShipEngineSDK.TrackUsingCarrierCodeAndTrackingNumber.Result!>!
ShipEngineSDK.ShipEngine.TrackUsingCarrierCodeAndTrackingNumber(string! trackingNumber, string! carrierCode, ShipEngineSDK.Config! methodConfig) -> System.Threading.Tasks.Task<ShipEngineSDK.TrackUsingCarrierCodeAndTrackingNumber.Result!>!
ShipEngineSDK.ShipEngine.TrackUsingLabelId(string! labelId) -> System.Threading.Tasks.Task<ShipEngineSDK.TrackUsingLabelId.Result!>!
Expand All @@ -1189,6 +1190,8 @@ ShipEngineSDK.ShipEngine._client -> System.Net.Http.HttpClient!
ShipEngineSDK.ShipEngine._config -> ShipEngineSDK.Config!
ShipEngineSDK.ShipEngineClient
ShipEngineSDK.ShipEngineClient.ShipEngineClient() -> void
ShipEngineSDK.ShipEngineClient.CancellationToken.get -> System.Threading.CancellationToken
ShipEngineSDK.ShipEngineClient.CancellationToken.set -> void
ShipEngineSDK.ShipEngineException
ShipEngineSDK.ShipEngineException.ErrorCode.get -> ShipEngineSDK.ErrorCode
ShipEngineSDK.ShipEngineException.ErrorCode.set -> void
Expand All @@ -1200,6 +1203,7 @@ ShipEngineSDK.ShipEngineException.RequestId.get -> string?
ShipEngineSDK.ShipEngineException.ResponseMessage.get -> System.Net.Http.HttpResponseMessage?
ShipEngineSDK.ShipEngineException.ResponseMessage.set -> void
ShipEngineSDK.ShipEngineException.ShipEngineException(string! message, ShipEngineSDK.ErrorSource errorSource = ShipEngineSDK.ErrorSource.Shipengine, ShipEngineSDK.ErrorType errorType = ShipEngineSDK.ErrorType.System, ShipEngineSDK.ErrorCode errorCode = ShipEngineSDK.ErrorCode.Unspecified, System.Net.Http.HttpResponseMessage? responseMessage = null, string? requestID = null) -> void
ShipEngineSDK.ShipEngineExtensions
ShipEngineSDK.ShipEngineMock
ShipEngineSDK.ShipEngineMock.ShipEngineMock() -> void
ShipEngineSDK.TrackUsingCarrierCodeAndTrackingNumber.Result
Expand Down Expand Up @@ -1310,7 +1314,10 @@ ShipEngineSDK.VoidLabelWithLabelId.Result.Approved.set -> void
ShipEngineSDK.VoidLabelWithLabelId.Result.Message.get -> string?
ShipEngineSDK.VoidLabelWithLabelId.Result.Message.set -> void
ShipEngineSDK.VoidLabelWithLabelId.Result.Result() -> void
static readonly ShipEngineSDK.ShipEngineClient.JsonSerializerOptions -> System.Text.Json.JsonSerializerOptions!
static ShipEngineSDK.ShipEngineClient.ConfigureHttpClient(ShipEngineSDK.Config! config, System.Net.Http.HttpClient! client) -> System.Net.Http.HttpClient!
static ShipEngineSDK.ShipEngineClient.ConfigureHttpClient(System.Net.Http.HttpClient! client, string! apiKey, System.Uri? baseUri, System.TimeSpan? timeout = null) -> System.Net.Http.HttpClient!
static ShipEngineSDK.ShipEngineExtensions.AddShipEngine(this Microsoft.Extensions.Hosting.IHostApplicationBuilder! builder, System.Action<System.Net.Http.HttpClient!>? configureClient = null) -> Microsoft.Extensions.Hosting.IHostApplicationBuilder!
virtual ShipEngineSDK.ShipEngineClient.SendHttpRequestAsync<T>(System.Net.Http.HttpMethod! method, string! path, string? jsonContent, System.Net.Http.HttpClient! client, ShipEngineSDK.Config! config) -> System.Threading.Tasks.Task<T>!
virtual ShipEngineSDK.ShipEngineMock.CreateLabelFromRate(ShipEngineSDK.CreateLabelFromRate.Params! createLabelFromRateParams) -> System.Threading.Tasks.Task<ShipEngineSDK.CreateLabelFromRate.Result!>!
virtual ShipEngineSDK.ShipEngineMock.CreateLabelFromRate(ShipEngineSDK.CreateLabelFromRate.Params! createLabelFromRateParams, ShipEngineSDK.Config! methodConfig) -> System.Threading.Tasks.Task<ShipEngineSDK.CreateLabelFromRate.Result!>!
Expand Down
58 changes: 50 additions & 8 deletions ShipEngine/ShipEngine.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using ShipEngineSDK.Common;
using ShipEngineSDK.Manifests;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Result = ShipEngineSDK.ValidateAddresses.Result;
using ShipEngineSDK.Common;
using ShipEngineSDK.Manifests;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;

namespace ShipEngineSDK
Expand Down Expand Up @@ -345,10 +347,35 @@ public virtual Task<List<Result>> ValidateAddresses(List<Address> addresses, Con
}
}

/// <summary>
/// Extension method to allow customized client configuration
/// </summary>
public static class ShipEngineExtensions
{
/// <summary>
/// Adds ShipEngine to the host builder and configures the client.
/// </summary>
/// <param name="builder"></param>
/// <param name="configureClient"></param>
/// <returns></returns>
public static IHostApplicationBuilder AddShipEngine(this IHostApplicationBuilder builder, Action<HttpClient>? configureClient = null)
{
builder.Services.AddHttpClient<ShipEngine>(c =>
{
var baseUri = builder.Configuration["ShipEngine:BaseUrl"] ?? "https://api.shipengine.com";
var apiKey = builder.Configuration["ShipEngine:ApiKey"] ?? "";
ShipEngineClient.ConfigureHttpClient(c, apiKey, new Uri(baseUri));
configureClient?.Invoke(c);
});

return builder;
}
}

/// <summary>
/// Contains methods for interacting with the ShipEngine API.
/// </summary>
public class ShipEngine : ShipEngineClient, IShipEngine
public class ShipEngine : ShipEngineClient, IDisposable, IShipEngine
{
/// <summary>
/// Global HttpClient for ShipEngine instance.
Expand All @@ -366,9 +393,8 @@ public class ShipEngine : ShipEngineClient, IShipEngine
/// <param name="apiKey">Api Key associated with the ShipEngine account you want to use</param>
public ShipEngine(string apiKey) : base()
{
var client = new HttpClient();
_config = new Config(apiKey);
_client = ConfigureHttpClient(_config, client);
_client = ConfigureHttpClient(_config, new HttpClient());
}

/// <summary>
Expand All @@ -377,9 +403,25 @@ public ShipEngine(string apiKey) : base()
/// <param name="config">Config object containing custom configurations</param>
public ShipEngine(Config config) : base()
{
var client = new HttpClient();
this._config = config;
_client = ConfigureHttpClient(config, client);
_client = ConfigureHttpClient(config, new HttpClient());
}

/// <summary>
/// Initialize the ShipEngine SDK with an httpClient object
/// </summary>
/// <param name="httpClient">HttpClient object to be used for ShipEngine API calls. We expect the httpClient has already been configured with ConfigureHttpClient</param>
public ShipEngine(HttpClient httpClient) : base()
{
_client = httpClient;
}

/// <summary>
/// Dispose of the ShipEngine client
/// </summary>
public void Dispose()
{
_client.Dispose();
}

/// <summary>
Expand Down
5 changes: 4 additions & 1 deletion ShipEngine/ShipEngine.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="System.Text.Json" Version="8.0.3" />
<None Include="..\README.md" Pack="true" PackagePath="\"/>
<None Include="..\README.md" Pack="true" PackagePath="\" />
</ItemGroup>

<ItemGroup>
Expand Down
53 changes: 33 additions & 20 deletions ShipEngine/ShipEngineClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Net.Http.Headers;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;

namespace ShipEngineSDK
Expand All @@ -21,25 +22,22 @@ public class ShipEngineClient
/// Options for serializing the method call params to JSON.
/// A separate inline setting is used for deserializing the response
/// </summary>
protected readonly JsonSerializerOptions JsonSerializerOptions;

/// <summary>
/// Constructor for ShipEngineClient
/// </summary>
public ShipEngineClient()
protected static readonly JsonSerializerOptions JsonSerializerOptions = new JsonSerializerOptions
{
JsonSerializerOptions = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
PropertyNameCaseInsensitive = true,
WriteIndented = true,
Converters = { new JsonStringEnumMemberConverter() }
};
}
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
PropertyNameCaseInsensitive = true,
WriteIndented = true,
Converters = { new JsonStringEnumMemberConverter() }
};

private const string JsonMediaType = "application/json";

/// <summary>
/// Token to cancel the request
/// </summary>
public CancellationToken CancellationToken { get; set; }

/// <summary>
/// Sets the HttpClient User agent, the json media type, and the API key to be used
/// for all ShipEngine API calls unless overrwritten at the method level.
Expand Down Expand Up @@ -71,9 +69,24 @@ public static HttpClient ConfigureHttpClient(Config config, HttpClient client)
return client;
}

private async Task<T> DeserializedResultOrThrow<T>(HttpResponseMessage response)
/// <summary>
/// Sets the HttpClient User agent, the json media type, and the API key to be used
/// for all ShipEngine API calls unless overwritten at the method level.
/// </summary>
/// <param name="client">The HttpClient to be configured</param>
/// <param name="apiKey">The API key to be used for all ShipEngine API calls</param>
/// <param name="baseUri">The base URI for the ShipEngine API</param>
/// <param name="timeout">The timeout for the ShipEngine API Calls</param>
/// <returns></returns>
public static HttpClient ConfigureHttpClient(HttpClient client, string apiKey, Uri? baseUri, TimeSpan? timeout = null)
{
var config = new Config(apiKey, timeout);
client.BaseAddress = baseUri ?? new Uri("https://api.shipengine.com");
return ConfigureHttpClient(config, client);
}

private async Task<T> DeserializedResultOrThrow<T>(HttpResponseMessage response)
{
var contentString = await response.Content.ReadAsStringAsync();

if (!response.IsSuccessStatusCode)
Expand Down Expand Up @@ -135,7 +148,7 @@ public virtual async Task<T> SendHttpRequestAsync<T>(HttpMethod method, string p
try
{
var request = BuildRequest(method, path, jsonContent);
var streamTask = client.SendAsync(request);
var streamTask = client.SendAsync(request, CancellationToken);
response = await streamTask;

var deserializedResult = await DeserializedResultOrThrow<T>(response);
Expand Down Expand Up @@ -202,10 +215,10 @@ private async Task WaitAndRetry(HttpResponseMessage? response, Config config, Sh
);
}

await Task.Delay((int)retryAfter * 1000).ConfigureAwait(false);
await Task.Delay((int)retryAfter * 1000, CancellationToken).ConfigureAwait(false);
}

private HttpRequestMessage BuildRequest(HttpMethod method, string path, string? jsonContent)
private static HttpRequestMessage BuildRequest(HttpMethod method, string path, string? jsonContent)
{
var request = new HttpRequestMessage(method, path);

Expand All @@ -217,7 +230,7 @@ private HttpRequestMessage BuildRequest(HttpMethod method, string path, string?
return request;
}

private bool ShouldRetry(
private static bool ShouldRetry(
int numRetries,
HttpStatusCode? statusCode,
HttpHeaders? headers,
Expand Down

0 comments on commit e3b660f

Please sign in to comment.