Skip to content

Commit

Permalink
Client Configuration (#235)
Browse files Browse the repository at this point in the history
Updated CryptoExchange.Net to version 8.3.0
Added support for loading client settings from IConfiguration
Added DI registration method for configuring Rest and Socket options at the same time
Added DisplayName and ImageUrl properties to KucoinExchange class
Updated client constructors to accept IOptions from DI
Removed redundant KucoinSocketClient constructor
  • Loading branch information
JKorf authored Nov 19, 2024
1 parent 5b65e93 commit be0502b
Show file tree
Hide file tree
Showing 17 changed files with 433 additions and 111 deletions.
105 changes: 105 additions & 0 deletions Kucoin.Net.UnitTests/KucoinClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
using System.Net.Http;
using CryptoExchange.Net.Clients;
using CryptoExchange.Net.Converters.JsonNet;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Kucoin.Net.Interfaces.Clients;
using CryptoExchange.Net.Objects;

namespace Kucoin.Net.UnitTests
{
Expand Down Expand Up @@ -110,5 +114,106 @@ public void CheckInterfaces()
CryptoExchange.Net.Testing.TestHelpers.CheckForMissingRestInterfaces<KucoinRestClient>();
CryptoExchange.Net.Testing.TestHelpers.CheckForMissingSocketInterfaces<KucoinSocketClient>();
}

[Test]
[TestCase(TradeEnvironmentNames.Live, "https://api.kucoin.com/")]
[TestCase(TradeEnvironmentNames.Testnet, "https://openapi-sandbox.kucoin.com/")]
[TestCase("", "https://api.kucoin.com/")]
public void TestConstructorEnvironments(string environmentName, string expected)
{
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string>
{
{ "Kucoin:Environment:Name", environmentName },
}).Build();

var collection = new ServiceCollection();
collection.AddKucoin(configuration.GetSection("Kucoin"));
var provider = collection.BuildServiceProvider();

var client = provider.GetRequiredService<IKucoinRestClient>();

var address = client.SpotApi.BaseAddress;

Assert.That(address, Is.EqualTo(expected));
}

[Test]
public void TestConstructorNullEnvironment()
{
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string>
{
{ "Kucoin", null },
}).Build();

var collection = new ServiceCollection();
collection.AddKucoin(configuration.GetSection("Kucoin"));
var provider = collection.BuildServiceProvider();

var client = provider.GetRequiredService<IKucoinRestClient>();

var address = client.SpotApi.BaseAddress;

Assert.That(address, Is.EqualTo("https://api.kucoin.com/"));
}

[Test]
public void TestConstructorApiOverwriteEnvironment()
{
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string>
{
{ "Kucoin:Environment:Name", "test" },
{ "Kucoin:Rest:Environment:Name", "live" },
}).Build();

var collection = new ServiceCollection();
collection.AddKucoin(configuration.GetSection("Kucoin"));
var provider = collection.BuildServiceProvider();

var client = provider.GetRequiredService<IKucoinRestClient>();

var address = client.SpotApi.BaseAddress;

Assert.That(address, Is.EqualTo("https://api.kucoin.com/"));
}

[Test]
public void TestConstructorConfiguration()
{
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string>
{
{ "ApiCredentials:Key", "123" },
{ "ApiCredentials:Secret", "456" },
{ "ApiCredentials:PassPhrase", "222" },
{ "Socket:ApiCredentials:Key", "456" },
{ "Socket:ApiCredentials:Secret", "789" },
{ "Socket:ApiCredentials:PassPhrase", "111" },
{ "Rest:OutputOriginalData", "true" },
{ "Socket:OutputOriginalData", "false" },
{ "Rest:Proxy:Host", "host" },
{ "Rest:Proxy:Port", "80" },
{ "Socket:Proxy:Host", "host2" },
{ "Socket:Proxy:Port", "81" },
}).Build();

var collection = new ServiceCollection();
collection.AddKucoin(configuration);
var provider = collection.BuildServiceProvider();

var restClient = provider.GetRequiredService<IKucoinRestClient>();
var socketClient = provider.GetRequiredService<IKucoinSocketClient>();

Assert.That(((BaseApiClient)restClient.SpotApi).OutputOriginalData, Is.True);
Assert.That(((BaseApiClient)socketClient.SpotApi).OutputOriginalData, Is.False);
Assert.That(((BaseApiClient)restClient.SpotApi).AuthenticationProvider.ApiKey, Is.EqualTo("123"));
Assert.That(((BaseApiClient)socketClient.SpotApi).AuthenticationProvider.ApiKey, Is.EqualTo("456"));
Assert.That(((BaseApiClient)restClient.SpotApi).ClientOptions.Proxy.Host, Is.EqualTo("host"));
Assert.That(((BaseApiClient)restClient.SpotApi).ClientOptions.Proxy.Port, Is.EqualTo(80));
Assert.That(((BaseApiClient)socketClient.SpotApi).ClientOptions.Proxy.Host, Is.EqualTo("host2"));
Assert.That(((BaseApiClient)socketClient.SpotApi).ClientOptions.Proxy.Port, Is.EqualTo(81));
}
}
}
10 changes: 6 additions & 4 deletions Kucoin.Net.UnitTests/KucoinRestIntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Kucoin.Net.Objects.Options;

namespace Kucoin.Net.UnitTests
{
Expand All @@ -28,11 +30,11 @@ public override KucoinRestClient GetClient(ILoggerFactory loggerFactory)
var pass = Environment.GetEnvironmentVariable("APIPASS");

Authenticated = key != null && sec != null;
return new KucoinRestClient(null, loggerFactory, opts =>
return new KucoinRestClient(null, loggerFactory, Options.Create(new KucoinRestOptions
{
opts.OutputOriginalData = true;
opts.ApiCredentials = Authenticated ? new KucoinApiCredentials(key, sec, pass) : null;
});
OutputOriginalData = true,
ApiCredentials = Authenticated ? new KucoinApiCredentials(key, sec, pass) : null
}));
}

[Test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ public async Task<CallResult<UpdateSubscription>> SubscribeToCrossMarginLeverage
/// <inheritdoc />
protected override async Task<CallResult<string?>> GetConnectionUrlAsync(string address, bool authenticated)
{
if (ClientOptions.Environment.EnvironmentName == "UnitTesting")
if (ClientOptions.Environment.Name == "UnitTesting")
return new CallResult<string?>("wss://ws-api-spot.kucoin.com");

var apiCredentials = (KucoinApiCredentials?)(ApiOptions.ApiCredentials ?? _baseClient.ClientOptions.ApiCredentials ?? KucoinSocketOptions.Default.ApiCredentials ?? KucoinRestOptions.Default.ApiCredentials);
Expand Down
21 changes: 9 additions & 12 deletions Kucoin.Net/Clients/KucoinRestClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Kucoin.Net.Objects.Options;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Net.Http;

Expand All @@ -26,25 +27,23 @@ public class KucoinRestClient : BaseRestClient, IKucoinRestClient
/// Create a new instance of KucoinClient
/// </summary>
/// <param name="optionsDelegate">Option configuration delegate</param>
public KucoinRestClient(Action<KucoinRestOptions>? optionsDelegate = null) : this(null, null, optionsDelegate)
public KucoinRestClient(Action<KucoinRestOptions>? optionsDelegate = null)
: this(null, null, Options.Create(ApplyOptionsDelegate(optionsDelegate)))
{
}

/// <summary>
/// Create a new instance of KucoinClient
/// </summary>
/// <param name="optionsDelegate">Option configuration delegate</param>
/// <param name="options">Option configuration</param>
/// <param name="loggerFactory">The logger factory</param>
/// <param name="httpClient">Http client for this client</param>
public KucoinRestClient(HttpClient? httpClient, ILoggerFactory? loggerFactory, Action<KucoinRestOptions>? optionsDelegate = null) : base(loggerFactory, "Kucoin")
public KucoinRestClient(HttpClient? httpClient, ILoggerFactory? loggerFactory, IOptions<KucoinRestOptions> options) : base(loggerFactory, "Kucoin")
{
var options = KucoinRestOptions.Default.Copy();
if (optionsDelegate != null)
optionsDelegate(options);
Initialize(options);
Initialize(options.Value);

SpotApi = AddApiClient(new KucoinRestClientSpotApi(_logger, httpClient, this, options));
FuturesApi = AddApiClient(new KucoinRestClientFuturesApi(_logger, httpClient, this, options));
SpotApi = AddApiClient(new KucoinRestClientSpotApi(_logger, httpClient, this, options.Value));
FuturesApi = AddApiClient(new KucoinRestClientFuturesApi(_logger, httpClient, this, options.Value));
}

/// <inheritdoc />
Expand All @@ -60,9 +59,7 @@ public void SetApiCredentials(KucoinApiCredentials credentials)
/// <param name="optionsFunc">Option configuration delegate</param>
public static void SetDefaultOptions(Action<KucoinRestOptions> optionsFunc)
{
var options = KucoinRestOptions.Default.Copy();
optionsFunc(options);
KucoinRestOptions.Default = options;
KucoinRestOptions.Default = ApplyOptionsDelegate(optionsFunc);
}
}
}
28 changes: 9 additions & 19 deletions Kucoin.Net/Clients/KucoinSocketClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Kucoin.Net.Objects.Options;
using Microsoft.Extensions.DependencyInjection;
using CryptoExchange.Net.Clients;
using Microsoft.Extensions.Options;

namespace Kucoin.Net.Clients
{
Expand All @@ -23,37 +24,28 @@ public class KucoinSocketClient : BaseSocketClient, IKucoinSocketClient
public IKucoinSocketClientFuturesApi FuturesApi { get; }

#endregion
/// <summary>
/// Create a new instance of the OKXSocketClient
/// </summary>
/// <param name="loggerFactory">The logger</param>
public KucoinSocketClient(ILoggerFactory? loggerFactory = null) : this((x) => { }, loggerFactory)
{
}

/// <summary>
/// Create a new instance of KucoinSocketClient
/// </summary>
/// <param name="optionsDelegate">Option configuration delegate</param>
public KucoinSocketClient(Action<KucoinSocketOptions> optionsDelegate) : this(optionsDelegate, null)
public KucoinSocketClient(Action<KucoinSocketOptions>? optionsDelegate = null)
: this(Options.Create(ApplyOptionsDelegate(optionsDelegate)), null)
{
}

/// <summary>
/// Create a new instance of KucoinSocketClient
/// </summary>
/// <param name="optionsDelegate">Option configuration delegate</param>
/// <param name="options">Option configuration</param>
/// <param name="loggerFactory">The logger factory</param>
[ActivatorUtilitiesConstructor]
public KucoinSocketClient(Action<KucoinSocketOptions>? optionsDelegate, ILoggerFactory? loggerFactory = null) : base(loggerFactory, "Kucoin")
public KucoinSocketClient(IOptions<KucoinSocketOptions> options, ILoggerFactory? loggerFactory = null) : base(loggerFactory, "Kucoin")
{
var options = KucoinSocketOptions.Default.Copy();
if (optionsDelegate != null)
optionsDelegate(options);
Initialize(options);
Initialize(options.Value);

SpotApi = AddApiClient(new KucoinSocketClientSpotApi(_logger, this, options));
FuturesApi = AddApiClient(new KucoinSocketClientFuturesApi(_logger, this, options));
SpotApi = AddApiClient(new KucoinSocketClientSpotApi(_logger, this, options.Value));
FuturesApi = AddApiClient(new KucoinSocketClientFuturesApi(_logger, this, options.Value));
}

/// <summary>
Expand All @@ -62,9 +54,7 @@ public KucoinSocketClient(Action<KucoinSocketOptions>? optionsDelegate, ILoggerF
/// <param name="optionsDelegate">Option configuration delegate</param>
public static void SetDefaultOptions(Action<KucoinSocketOptions> optionsDelegate)
{
var options = KucoinSocketOptions.Default.Copy();
optionsDelegate(options);
KucoinSocketOptions.Default = options;
KucoinSocketOptions.Default = ApplyOptionsDelegate(optionsDelegate);
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion Kucoin.Net/Clients/SpotApi/KucoinSocketClientSpotApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ public async Task<CallResult<UpdateSubscription>> SubscribeToMarginOrderUpdatesA
/// <inheritdoc />
protected override async Task<CallResult<string?>> GetConnectionUrlAsync(string address, bool authenticated)
{
if (ClientOptions.Environment.EnvironmentName == "UnitTesting")
if (ClientOptions.Environment.Name == "UnitTesting")
return new CallResult<string?>("wss://ws-api-spot.kucoin.com");

var apiCredentials = (KucoinApiCredentials?)(ApiOptions.ApiCredentials ?? _baseClient.ClientOptions.ApiCredentials);
Expand Down
Loading

0 comments on commit be0502b

Please sign in to comment.