From 850fd6bcc6680ea343ecd0b5be8120871c81366b Mon Sep 17 00:00:00 2001 From: Jan Korf Date: Tue, 19 Nov 2024 13:46:42 +0100 Subject: [PATCH] Client Configuration (#14) Updated CryptoExchange.Net to version 8.3.0 Added support for loading client settings from IConfiguration Updated client constructors to accept IOptions from DI --- CoinGecko.Net/Clients/CoinGeckoRestClient.cs | 19 +++-- CoinGecko.Net/CoinGecko.Net.csproj | 4 +- CoinGecko.Net/CoinGecko.Net.xml | 33 +++++++-- CoinGecko.Net/CoinGeckoEnvironment.cs | 20 ++++++ .../ServiceCollectionExtensions.cs | 69 ++++++++++++++----- .../Objects/CoinGeckoApiCredentials.cs | 4 +- .../Objects/Options/CoinGeckoRestOptions.cs | 18 +++-- docs/index.html | 11 ++- 8 files changed, 134 insertions(+), 44 deletions(-) diff --git a/CoinGecko.Net/Clients/CoinGeckoRestClient.cs b/CoinGecko.Net/Clients/CoinGeckoRestClient.cs index 14b0bca..c1c878a 100644 --- a/CoinGecko.Net/Clients/CoinGeckoRestClient.cs +++ b/CoinGecko.Net/Clients/CoinGeckoRestClient.cs @@ -5,6 +5,7 @@ using System; using CoinGecko.Net.Objects.Options; using CryptoExchange.Net.Clients; +using Microsoft.Extensions.Options; namespace CoinGecko.Net.Clients { @@ -19,25 +20,23 @@ public class CoinGeckoRestClient: BaseRestClient, ICoinGeckoRestClient /// Create a new instance of the CoinGeckoClient using provided options /// /// Option configuration delegate - public CoinGeckoRestClient(Action? optionsDelegate = null) : this(null, null, optionsDelegate) + public CoinGeckoRestClient(Action? optionsDelegate = null) + : this(null, null, Options.Create(ApplyOptionsDelegate(optionsDelegate))) { } /// /// Create a new instance of the CoinGeckoClient /// - /// Option configuration delegate + /// Option configuration /// The logger factory /// Http client for this client - public CoinGeckoRestClient(HttpClient? httpClient, ILoggerFactory? loggerFactory, Action? optionsDelegate = null) + public CoinGeckoRestClient(HttpClient? httpClient, ILoggerFactory? loggerFactory, IOptions options) : base(loggerFactory, "CoinGecko") { - var options = CoinGeckoRestOptions.Default.Copy(); - if (optionsDelegate != null) - optionsDelegate(options); - Initialize(options); + Initialize(options.Value); - Api = AddApiClient(new CoinGeckoRestClientApi(_logger, httpClient, options)); + Api = AddApiClient(new CoinGeckoRestClientApi(_logger, httpClient, options.Value)); } #endregion @@ -47,9 +46,7 @@ public CoinGeckoRestClient(HttpClient? httpClient, ILoggerFactory? loggerFactory /// Option configuration delegate public static void SetDefaultOptions(Action optionsDelegate) { - var options = CoinGeckoRestOptions.Default.Copy(); - optionsDelegate(options); - CoinGeckoRestOptions.Default = options; + CoinGeckoRestOptions.Default = ApplyOptionsDelegate(optionsDelegate); } } } diff --git a/CoinGecko.Net/CoinGecko.Net.csproj b/CoinGecko.Net/CoinGecko.Net.csproj index 6bbece8..f2eae57 100644 --- a/CoinGecko.Net/CoinGecko.Net.csproj +++ b/CoinGecko.Net/CoinGecko.Net.csproj @@ -1,4 +1,4 @@ - + netstandard2.0;netstandard2.1 enable @@ -48,7 +48,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/CoinGecko.Net/CoinGecko.Net.xml b/CoinGecko.Net/CoinGecko.Net.xml index af23cde..cbc2a3e 100644 --- a/CoinGecko.Net/CoinGecko.Net.xml +++ b/CoinGecko.Net/CoinGecko.Net.xml @@ -16,11 +16,11 @@ Option configuration delegate - + Create a new instance of the CoinGeckoClient - Option configuration delegate + Option configuration The logger factory Http client for this client @@ -212,6 +212,16 @@ Rest client address pro API + + + ctor for DI, use for creating a custom environment + + + + + Get the CoinGecko environment by name + + Live environment @@ -660,7 +670,7 @@ ctor - The API key + The API key Whether or not this is a demo key @@ -3170,6 +3180,11 @@ Default options for the CoinGecko client + + + ctor + + Api options @@ -3204,12 +3219,20 @@ Extensions for DI + + + Add services such as the ICoinGeckoRestClient. Configures the services based on the provided configuration. + + The service collection + The configuration(section) containing the options + + - Add the ICoinGeckoClient to the sevice collection so they can be injected + Add services such as the ICoinGeckoRestClient. Services will be configured based on the provided options. The service collection - Set default options for the rest client + Set options for the CoinGecko services diff --git a/CoinGecko.Net/CoinGeckoEnvironment.cs b/CoinGecko.Net/CoinGeckoEnvironment.cs index e2e3517..8ebd20d 100644 --- a/CoinGecko.Net/CoinGeckoEnvironment.cs +++ b/CoinGecko.Net/CoinGeckoEnvironment.cs @@ -25,6 +25,26 @@ internal CoinGeckoEnvironment(string name, RestApiAddressPro = restBaseAddressPro; } + /// + /// ctor for DI, use for creating a custom environment + /// +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. + public CoinGeckoEnvironment() : base(TradeEnvironmentNames.Live) +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. + { } + + /// + /// Get the CoinGecko environment by name + /// + public static CoinGeckoEnvironment? GetEnvironmentByName(string? name) + => name switch + { + TradeEnvironmentNames.Live => Live, + "" => Live, + null => Live, + _ => default + }; + /// /// Live environment /// diff --git a/CoinGecko.Net/ExtensionMethods/ServiceCollectionExtensions.cs b/CoinGecko.Net/ExtensionMethods/ServiceCollectionExtensions.cs index 2bfffe8..96aa758 100644 --- a/CoinGecko.Net/ExtensionMethods/ServiceCollectionExtensions.cs +++ b/CoinGecko.Net/ExtensionMethods/ServiceCollectionExtensions.cs @@ -1,8 +1,12 @@ -using CoinGecko.Net.Clients; +using CoinGecko.Net; +using CoinGecko.Net.Clients; using CoinGecko.Net.Interfaces; using CoinGecko.Net.Objects.Options; using CryptoExchange.Net.Clients; using CryptoExchange.Net.Interfaces; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using System; using System.Net; using System.Net.Http; @@ -15,35 +19,66 @@ namespace Microsoft.Extensions.DependencyInjection public static class ServiceCollectionExtensions { /// - /// Add the ICoinGeckoClient to the sevice collection so they can be injected + /// Add services such as the ICoinGeckoRestClient. Configures the services based on the provided configuration. /// /// The service collection - /// Set default options for the rest client + /// The configuration(section) containing the options /// public static IServiceCollection AddCoinGecko( this IServiceCollection services, - Action? defaultRestOptionsDelegate = null) + IConfiguration configuration) { - var restOptions = CoinGeckoRestOptions.Default.Copy(); + var options = new CoinGeckoRestOptions(); + configuration.Bind(options); - if (defaultRestOptionsDelegate != null) - { - defaultRestOptionsDelegate(restOptions); - CoinGeckoRestClient.SetDefaultOptions(defaultRestOptionsDelegate); - } + var restEnvName = options.Environment?.Name ?? options.Environment?.Name ?? CoinGeckoEnvironment.Live.Name; + options.Environment = CoinGeckoEnvironment.GetEnvironmentByName(restEnvName) ?? options.Environment!; + options.ApiCredentials = options.ApiCredentials ?? options.ApiCredentials; - services.AddHttpClient(options => - { - options.Timeout = restOptions.RequestTimeout; - }).ConfigurePrimaryHttpMessageHandler(() => + services.AddSingleton(x => Options.Options.Create(options)); + + return AddCoinGeckoCore(services); + } + + /// + /// Add services such as the ICoinGeckoRestClient. Services will be configured based on the provided options. + /// + /// The service collection + /// Set options for the CoinGecko services + /// + public static IServiceCollection AddCoinGecko( + this IServiceCollection services, + Action? optionsDelegate = null) + { + services.Configure((x) => { optionsDelegate?.Invoke(x); }); + + return AddCoinGeckoCore(services); + } + + private static IServiceCollection AddCoinGeckoCore( + this IServiceCollection services) + { + services.AddHttpClient((client, serviceProvider) => { + var options = serviceProvider.GetRequiredService>().Value; + client.Timeout = options.RequestTimeout; + return new CoinGeckoRestClient(client, serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService>()); + }).ConfigurePrimaryHttpMessageHandler((serviceProvider) => { var handler = new HttpClientHandler(); - if (restOptions.Proxy != null) + try + { + handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; + } + catch (PlatformNotSupportedException) + { } + + var options = serviceProvider.GetRequiredService>().Value; + if (options.Proxy != null) { handler.Proxy = new WebProxy { - Address = new Uri($"{restOptions.Proxy.Host}:{restOptions.Proxy.Port}"), - Credentials = restOptions.Proxy.Password == null ? null : new NetworkCredential(restOptions.Proxy.Login, restOptions.Proxy.Password) + Address = new Uri($"{options.Proxy.Host}:{options.Proxy.Port}"), + Credentials = options.Proxy.Password == null ? null : new NetworkCredential(options.Proxy.Login, options.Proxy.Password) }; } return handler; diff --git a/CoinGecko.Net/Objects/CoinGeckoApiCredentials.cs b/CoinGecko.Net/Objects/CoinGeckoApiCredentials.cs index 44f929d..97162f1 100644 --- a/CoinGecko.Net/Objects/CoinGeckoApiCredentials.cs +++ b/CoinGecko.Net/Objects/CoinGeckoApiCredentials.cs @@ -15,9 +15,9 @@ public class CoinGeckoApiCredentials : ApiCredentials /// /// ctor /// - /// The API key + /// The API key /// Whether or not this is a demo key - public CoinGeckoApiCredentials(string apiKey, bool demoKey = false) : base(apiKey, "-") + public CoinGeckoApiCredentials(string key, bool demoKey = false) : base(key, "-") { DemoKey = demoKey; } diff --git a/CoinGecko.Net/Objects/Options/CoinGeckoRestOptions.cs b/CoinGecko.Net/Objects/Options/CoinGeckoRestOptions.cs index fc0c11b..4eefb7b 100644 --- a/CoinGecko.Net/Objects/Options/CoinGeckoRestOptions.cs +++ b/CoinGecko.Net/Objects/Options/CoinGeckoRestOptions.cs @@ -10,21 +10,29 @@ public class CoinGeckoRestOptions : RestExchangeOptions /// Default options for the CoinGecko client /// - public static CoinGeckoRestOptions Default { get; set; } = new CoinGeckoRestOptions() + internal static CoinGeckoRestOptions Default { get; set; } = new CoinGeckoRestOptions() { Environment = CoinGeckoEnvironment.Live }; + /// + /// ctor + /// + public CoinGeckoRestOptions() + { + Default?.Set(this); + } + /// /// Api options /// public RestApiOptions ApiOptions { get; private set; } = new RestApiOptions(); - internal CoinGeckoRestOptions Copy() + internal CoinGeckoRestOptions Set(CoinGeckoRestOptions targetOptions) { - var options = Copy(); - options.ApiOptions = ApiOptions.Copy(); - return options; + targetOptions = base.Set(targetOptions); + targetOptions.ApiOptions = ApiOptions.Set(targetOptions.ApiOptions); + return targetOptions; } } } diff --git a/docs/index.html b/docs/index.html index efd1e99..78652c8 100644 --- a/docs/index.html +++ b/docs/index.html @@ -178,8 +178,15 @@

API Access

CoinGecko.Net can be configured using Dotnet dependency injection, after which the clients can be injected into your services. It also correctly configures logging and HttpClient usage.

-
builder.Services.AddCoinGecko(options => {
-  // Options can be configured here
+		  
// Configure options from config file
+// see https://github.com/JKorf/CryptoExchange.Net/tree/master/Examples/example-config.json for an example
+builder.Services.AddCoinGecko(builder.Configuration.GetSection("CoinGecko"));
+		  
+// OR
+		  
+ builder.Services.AddCoinGecko(options => {
+  // Configure options in code
+  options.RequestTimeout = TimeSpan.FromSeconds(10);
 });

The ICoinGeckoRestClient can then be injected.