From 55403e45ab94325fc5e233f5c9816b619a59bcb2 Mon Sep 17 00:00:00 2001 From: Joe Pill Date: Mon, 10 Jun 2024 08:33:36 -0500 Subject: [PATCH] feat: add method to initialize SDK with an httpClient --- ShipEngine/ShipEngine.cs | 45 ++++++++++++++++++++++++++++----- ShipEngine/ShipEngine.csproj | 8 ++++-- ShipEngine/ShipEngineClient.cs | 46 +++++++++++++++++++++++----------- 3 files changed, 76 insertions(+), 23 deletions(-) diff --git a/ShipEngine/ShipEngine.cs b/ShipEngine/ShipEngine.cs index 0d3fa593..dbb9df0c 100644 --- a/ShipEngine/ShipEngine.cs +++ b/ShipEngine/ShipEngine.cs @@ -2,15 +2,33 @@ using System.Collections.Generic; using System.Net.Http; using System.Text.Json; -using System.Text.Json.Serialization; using System.Threading.Tasks; +using System; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; namespace ShipEngineSDK { + public static class ShipEngineExtensions + { + public static IHostApplicationBuilder AddShipEngine(this IHostApplicationBuilder builder, Action? clientOverride = null) + { + builder.Services.AddHttpClient(c => + { + var baseUri = builder.Configuration["ShipEngine:BaseUrl"] ?? "https://api.shipengine.com"; + var apiKey = builder.Configuration["ShipEngine:ApiKey"]; + ShipEngineClient.ConfigureHttpClient(c, apiKey, new Uri(baseUri)); + clientOverride?.Invoke(c); + }); + + return builder; + } + } + /// /// Contains methods for interacting with the ShipEngine API. /// - public class ShipEngine : ShipEngineClient + public class ShipEngine : ShipEngineClient, IDisposable { /// /// Global HttpClient for ShipEngine instance. @@ -28,9 +46,8 @@ public class ShipEngine : ShipEngineClient /// Api Key associated with the ShipEngine account you want to use public ShipEngine(string apiKey) : base() { - var client = new HttpClient(); _config = new Config(apiKey); - _client = ConfigureHttpClient(_config, client); + _client = ConfigureHttpClient(_config, new HttpClient()); } /// @@ -39,9 +56,25 @@ public ShipEngine(string apiKey) : base() /// Config object containing custom configurations public ShipEngine(Config config) : base() { - var client = new HttpClient(); this._config = config; - _client = ConfigureHttpClient(config, client); + _client = ConfigureHttpClient(config, new HttpClient()); + } + + /// + /// Initialize the ShipEngine SDK with an httpClient object + /// + /// HttpClient object to be used for ShipEngine API calls. We expect the httpClient has already been configured with ConfigureHttpClient + public ShipEngine(HttpClient httpClient) : base() + { + _client = httpClient; + } + + // + // Dispose of the ShipEngine client + // + public void Dispose() + { + _client.Dispose(); } /// diff --git a/ShipEngine/ShipEngine.csproj b/ShipEngine/ShipEngine.csproj index 68c44998..4e70de0c 100644 --- a/ShipEngine/ShipEngine.csproj +++ b/ShipEngine/ShipEngine.csproj @@ -1,4 +1,4 @@ - + ShipEngine @@ -28,6 +28,10 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + + + + @@ -38,4 +42,4 @@ - + \ No newline at end of file diff --git a/ShipEngine/ShipEngineClient.cs b/ShipEngine/ShipEngineClient.cs index b32400ef..8c094dbb 100644 --- a/ShipEngine/ShipEngineClient.cs +++ b/ShipEngine/ShipEngineClient.cs @@ -21,22 +21,14 @@ public class ShipEngineClient /// Options for serializing the method call params to JSON. /// A separate inline setting is used for deserializing the response /// - protected readonly JsonSerializerOptions JsonSerializerOptions; - - /// - /// Constructor for ShipEngineClient - /// - 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"; @@ -71,6 +63,30 @@ public static HttpClient ConfigureHttpClient(Config config, HttpClient client) return client; } + public static HttpClient ConfigureHttpClient(HttpClient client, string apiKey, Uri? baseUri, TimeSpan? timeout = null) + { + client.DefaultRequestHeaders.Accept.Clear(); + + var osPlatform = Environment.OSVersion.Platform.ToString(); + var osVersion = Environment.OSVersion.Version.ToString(); + var clrVersion = Environment.Version.ToString(); + + var sdkVersionObject = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; + var sdkVersion = $"{sdkVersionObject.Major}.{sdkVersionObject.Minor}.{sdkVersionObject.Build}"; + + var userAgentString = $"shipengine-dotnet/{sdkVersion} {osPlatform}/{osVersion} clr/{clrVersion}"; + + client.DefaultRequestHeaders.Add("User-Agent", userAgentString); + client.DefaultRequestHeaders.Add("Api-Key", apiKey); + client.DefaultRequestHeaders.Add("Accept", JsonMediaType); + + client.BaseAddress = baseUri ?? new Uri("https://api.shipengine.com"); + + client.Timeout = timeout ?? TimeSpan.FromSeconds(60); + + return client; + } + private async Task DeserializedResultOrThrow(HttpResponseMessage response) {