1
0
mirror of https://github.com/JKorf/CryptoExchange.Net.git synced 2026-08-14 01:42:55 +00:00

Compare commits

..

3 Commits

Author SHA1 Message Date
JKorf 73764970b0 . 2025-08-25 19:11:50 +02:00
JKorf 9ba29035b2 Merge branch 'master' into feature/code-analyzis 2025-08-25 17:37:46 +02:00
JKorf 4c953e2c87 wip 2025-08-24 21:58:52 +02:00
370 changed files with 23484 additions and 24320 deletions
@@ -6,9 +6,9 @@
<PackageId>CryptoExchange.Net.Protobuf</PackageId> <PackageId>CryptoExchange.Net.Protobuf</PackageId>
<Authors>JKorf</Authors> <Authors>JKorf</Authors>
<Description>Protobuf support for CryptoExchange.Net</Description> <Description>Protobuf support for CryptoExchange.Net</Description>
<PackageVersion>9.9.0</PackageVersion> <PackageVersion>9.6.0</PackageVersion>
<AssemblyVersion>9.9.0</AssemblyVersion> <AssemblyVersion>9.6.0</AssemblyVersion>
<FileVersion>9.9.0</FileVersion> <FileVersion>9.6.0</FileVersion>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance> <PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<PackageTags>CryptoExchange;CryptoExchange.Net</PackageTags> <PackageTags>CryptoExchange;CryptoExchange.Net</PackageTags>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
@@ -41,7 +41,7 @@
<DocumentationFile>CryptoExchange.Net.Protobuf.xml</DocumentationFile> <DocumentationFile>CryptoExchange.Net.Protobuf.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CryptoExchange.Net" Version="9.9.0" /> <PackageReference Include="CryptoExchange.Net" Version="9.6.0" />
<PackageReference Include="protobuf-net" Version="3.2.56" /> <PackageReference Include="protobuf-net" Version="3.2.56" />
</ItemGroup> </ItemGroup>
</Project> </Project>
-9
View File
@@ -5,15 +5,6 @@
Protobuf support for CryptoExchange.Net. Protobuf support for CryptoExchange.Net.
## Release notes ## Release notes
* Version 9.9.0 - 06 Oct 2025
* Updated CryptoExchange.Net version to 9.9.0, see https://github.com/JKorf/CryptoExchange.Net/releases/
* Version 9.8.0 - 30 Sep 2025
* Updated CryptoExchange.Net version to 9.8.0, see https://github.com/JKorf/CryptoExchange.Net/releases/
* Version 9.7.0 - 01 Sep 2025
* Updated CryptoExchange.Net version to 9.7.0, see https://github.com/JKorf/CryptoExchange.Net/releases/
* Version 9.6.0 - 25 Aug 2025 * Version 9.6.0 - 25 Aug 2025
* Updated CryptoExchange.Net version to 9.6.0 * Updated CryptoExchange.Net version to 9.6.0
@@ -5,7 +5,6 @@ using NUnit.Framework.Legacy;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -114,7 +113,6 @@ namespace CryptoExchange.Net.UnitTests
{ {
var result = new WebCallResult<TestObjectResult>( var result = new WebCallResult<TestObjectResult>(
System.Net.HttpStatusCode.OK, System.Net.HttpStatusCode.OK,
HttpVersion.Version11,
new KeyValuePair<string, string[]>[0], new KeyValuePair<string, string[]>[0],
TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1),
null, null,
@@ -145,7 +143,6 @@ namespace CryptoExchange.Net.UnitTests
{ {
var result = new WebCallResult<TestObjectResult>( var result = new WebCallResult<TestObjectResult>(
System.Net.HttpStatusCode.OK, System.Net.HttpStatusCode.OK,
HttpVersion.Version11,
new KeyValuePair<string, string[]>[0], new KeyValuePair<string, string[]>[0],
TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1),
null, null,
@@ -5,6 +5,10 @@
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<None Include="..\CryptoExchange.Net\.editorconfig" Link=".editorconfig" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1"></PackageReference> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1"></PackageReference>
<PackageReference Include="Moq" Version="4.20.72" /> <PackageReference Include="Moq" Version="4.20.72" />
@@ -32,7 +32,6 @@ namespace CryptoExchange.Net.UnitTests
[TestCase(0.1, 1, 0.0001, RoundingType.Closest, 0.532, 0.532)] [TestCase(0.1, 1, 0.0001, RoundingType.Closest, 0.532, 0.532)]
[TestCase(0.1, 1, 0.0001, RoundingType.Down, 0.5516592, 0.5516)] [TestCase(0.1, 1, 0.0001, RoundingType.Down, 0.5516592, 0.5516)]
[TestCase(0.1, 1, 0.0001, RoundingType.Closest, 0.5516592, 0.5517)] [TestCase(0.1, 1, 0.0001, RoundingType.Closest, 0.5516592, 0.5517)]
[TestCase(0, 1, 0.000000001, RoundingType.Closest, 0.0000097232, 0.000009723)]
public void AdjustValueStepTests(decimal min, decimal max, decimal? step, RoundingType roundingType, decimal input, decimal expected) public void AdjustValueStepTests(decimal min, decimal max, decimal? step, RoundingType roundingType, decimal input, decimal expected)
{ {
var result = ExchangeHelpers.AdjustValueStep(min, max, step, roundingType, input); var result = ExchangeHelpers.AdjustValueStep(min, max, step, roundingType, input);
@@ -202,7 +202,7 @@ namespace CryptoExchange.Net.UnitTests
await sub; await sub;
// assert // assert
ClassicAssert.IsTrue(client.SubClient.TestSubscription.Status != SubscriptionStatus.Subscribed); ClassicAssert.IsFalse(client.SubClient.TestSubscription.Confirmed);
} }
[TestCase()] [TestCase()]
@@ -225,7 +225,7 @@ namespace CryptoExchange.Net.UnitTests
await sub; await sub;
// assert // assert
Assert.That(client.SubClient.TestSubscription.Status == SubscriptionStatus.Subscribed); Assert.That(client.SubClient.TestSubscription.Confirmed);
} }
} }
} }
@@ -60,8 +60,8 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations
request.Setup(c => c.GetHeaders()).Returns(() => headers.ToArray()); request.Setup(c => c.GetHeaders()).Returns(() => headers.ToArray());
var factory = Mock.Get(Api1.RequestFactory); var factory = Mock.Get(Api1.RequestFactory);
factory.Setup(c => c.Create(It.IsAny<Version>(), It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>())) factory.Setup(c => c.Create(It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
.Callback<Version, HttpMethod, Uri, int>((version, method, uri, id) => .Callback<HttpMethod, Uri, int>((method, uri, id) =>
{ {
request.Setup(a => a.Uri).Returns(uri); request.Setup(a => a.Uri).Returns(uri);
request.Setup(a => a.Method).Returns(method); request.Setup(a => a.Method).Returns(method);
@@ -69,8 +69,8 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations
.Returns(request.Object); .Returns(request.Object);
factory = Mock.Get(Api2.RequestFactory); factory = Mock.Get(Api2.RequestFactory);
factory.Setup(c => c.Create(It.IsAny<Version>(), It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>())) factory.Setup(c => c.Create(It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
.Callback<Version, HttpMethod, Uri, int>((version, method, uri, id) => .Callback<HttpMethod, Uri, int>((method, uri, id) =>
{ {
request.Setup(a => a.Uri).Returns(uri); request.Setup(a => a.Uri).Returns(uri);
request.Setup(a => a.Method).Returns(method); request.Setup(a => a.Method).Returns(method);
@@ -90,12 +90,12 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations
request.Setup(c => c.GetResponseAsync(It.IsAny<CancellationToken>())).Throws(we); request.Setup(c => c.GetResponseAsync(It.IsAny<CancellationToken>())).Throws(we);
var factory = Mock.Get(Api1.RequestFactory); var factory = Mock.Get(Api1.RequestFactory);
factory.Setup(c => c.Create(It.IsAny<Version>(), It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>())) factory.Setup(c => c.Create(It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
.Returns(request.Object); .Returns(request.Object);
factory = Mock.Get(Api2.RequestFactory); factory = Mock.Get(Api2.RequestFactory);
factory.Setup(c => c.Create(It.IsAny<Version>(), It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>())) factory.Setup(c => c.Create(It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
.Returns(request.Object); .Returns(request.Object);
} }
@@ -118,13 +118,13 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations
request.Setup(c => c.GetHeaders()).Returns(headers.ToArray()); request.Setup(c => c.GetHeaders()).Returns(headers.ToArray());
var factory = Mock.Get(Api1.RequestFactory); var factory = Mock.Get(Api1.RequestFactory);
factory.Setup(c => c.Create(It.IsAny<Version>(), It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>())) factory.Setup(c => c.Create(It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
.Callback<Version, HttpMethod, Uri, int>((version, method, uri, id) => request.Setup(a => a.Uri).Returns(uri)) .Callback<HttpMethod, Uri, int>((method, uri, id) => request.Setup(a => a.Uri).Returns(uri))
.Returns(request.Object); .Returns(request.Object);
factory = Mock.Get(Api2.RequestFactory); factory = Mock.Get(Api2.RequestFactory);
factory.Setup(c => c.Create(It.IsAny<Version>(), It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>())) factory.Setup(c => c.Create(It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
.Callback<Version, HttpMethod, Uri, int>((version, method, uri, id) => request.Setup(a => a.Uri).Returns(uri)) .Callback<HttpMethod, Uri, int>((method, uri, id) => request.Setup(a => a.Uri).Returns(uri))
.Returns(request.Object); .Returns(request.Object);
} }
} }
@@ -1,132 +0,0 @@
//using System;
//using System.IO;
//using System.Net.WebSockets;
//using System.Security.Authentication;
//using System.Text;
//using System.Threading.Tasks;
//using CryptoExchange.Net.Interfaces;
//using CryptoExchange.Net.Objects;
//namespace CryptoExchange.Net.UnitTests.TestImplementations
//{
// public class TestSocket: IWebsocket
// {
// public bool CanConnect { get; set; }
// public bool Connected { get; set; }
// public event Func<Task> OnClose;
//#pragma warning disable 0067
// public event Func<Task> OnReconnected;
// public event Func<Task> OnReconnecting;
// public event Func<int, Task> OnRequestRateLimited;
//#pragma warning restore 0067
// public event Func<int, Task> OnRequestSent;
// public event Func<WebSocketMessageType, ReadOnlyMemory<byte>, Task> OnStreamMessage;
// public event Func<Exception, Task> OnError;
// public event Func<Task> OnOpen;
// public Func<Task<Uri>> GetReconnectionUrl { get; set; }
// public int Id { get; }
// public bool ShouldReconnect { get; set; }
// public TimeSpan Timeout { get; set; }
// public Func<string, string> DataInterpreterString { get; set; }
// public Func<byte[], string> DataInterpreterBytes { get; set; }
// public DateTime? DisconnectTime { get; set; }
// public string Url { get; }
// public bool IsClosed => !Connected;
// public bool IsOpen => Connected;
// public bool PingConnection { get; set; }
// public TimeSpan PingInterval { get; set; }
// public SslProtocols SSLProtocols { get; set; }
// public Encoding Encoding { get; set; }
// public int ConnectCalls { get; private set; }
// public bool Reconnecting { get; set; }
// public string Origin { get; set; }
// public int? RatelimitPerSecond { get; set; }
// public double IncomingKbps => throw new NotImplementedException();
// public Uri Uri => new Uri("");
// public TimeSpan KeepAliveInterval { get; set; }
// public static int lastId = 0;
// public static object lastIdLock = new object();
// public TestSocket()
// {
// lock (lastIdLock)
// {
// Id = lastId + 1;
// lastId++;
// }
// }
// public Task<CallResult> ConnectAsync()
// {
// Connected = CanConnect;
// ConnectCalls++;
// if (CanConnect)
// InvokeOpen();
// return Task.FromResult(CanConnect ? new CallResult(null) : new CallResult(new CantConnectError()));
// }
// public bool Send(int requestId, string data, int weight)
// {
// if(!Connected)
// throw new Exception("Socket not connected");
// OnRequestSent?.Invoke(requestId);
// return true;
// }
// public void Reset()
// {
// }
// public Task CloseAsync()
// {
// Connected = false;
// DisconnectTime = DateTime.UtcNow;
// OnClose?.Invoke();
// return Task.FromResult(0);
// }
// public void SetProxy(string host, int port)
// {
// throw new NotImplementedException();
// }
// public void Dispose()
// {
// }
// public void InvokeClose()
// {
// Connected = false;
// DisconnectTime = DateTime.UtcNow;
// Reconnecting = true;
// OnClose?.Invoke();
// }
// public void InvokeOpen()
// {
// OnOpen?.Invoke();
// }
// public void InvokeMessage(string data)
// {
// OnStreamMessage?.Invoke(WebSocketMessageType.Text, new ReadOnlyMemory<byte>(Encoding.UTF8.GetBytes(data))).Wait();
// }
// public void SetProxy(ApiProxy proxy)
// {
// throw new NotImplementedException();
// }
// public void InvokeError(Exception error)
// {
// OnError?.Invoke(error);
// }
// public Task ReconnectAsync() => Task.CompletedTask;
// }
//}
+183
View File
@@ -0,0 +1,183 @@
root = true
[*]
# Indentation and spacing
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
charset = utf-8
max_line_length = 140
insert_final_newline = true
# ReSharper code style properties
resharper_csharp_keep_existing_embedded_arrangement = false
resharper_csharp_place_accessorholder_attribute_on_same_line = false
resharper_csharp_wrap_after_declaration_lpar = true
resharper_csharp_wrap_parameters_style = chop_if_long
resharper_csharp_blank_lines_around_single_line_auto_property = 1
resharper_csharp_keep_blank_lines_in_declarations = 1
resharper_trailing_comma_in_multiline_lists = true
[*.cs]
indent_size = 4
# Code style conventions
dotnet_style_predefined_type_for_member_access = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_object_initializer = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
csharp_style_expression_bodied_methods = true:suggestion
csharp_style_namespace_declarations = file_scoped:warning
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
csharp_prefer_braces = when_multiline:warning
# Analyzer preferences
dotnet_diagnostic.CA2007.severity = warning # Call ConfigureAwait on the awaited Task.
dotnet_code_quality.CA2007.exclude_async_void_methods = true
dotnet_code_quality.CA2007.output_kind = DynamicallyLinkedLibrary
dotnet_diagnostic.CA1000.severity = none # Do not declare static members on generic types
dotnet_diagnostic.CA1051.severity = none # Do not declare visible instance fields
dotnet_diagnostic.CA1510.severity = none # Use ArgumentNullException throw helper
dotnet_diagnostic.CA1720.severity = none # Identifiers should not contain type names
dotnet_diagnostic.CA1716.severity = none # Identifiers should not match keywords
dotnet_diagnostic.CA1835.severity = none # Use ArgumentNullException throw helper
dotnet_diagnostic.CA1846.severity = none # Prefer AsSpan over Substring
dotnet_diagnostic.CA1848.severity = none # Use the LoggerMessage delegates
dotnet_diagnostic.CA1850.severity = none # Prefer static HashData method over ComputeHash
dotnet_diagnostic.CA1866.severity = none # Use 'string.Method(char)' instead of 'string.Method(string)' for string with single char
dotnet_diagnostic.CA2201.severity = none # Do not raise reserved exception types
dotnet_diagnostic.CA2208.severity = none # Do not raise reserved exception types
dotnet_diagnostic.IDE0005.severity = warning # Using directive is unnecessary
[*.xml]
ij_xml_space_inside_empty_tag = true
[*.cs]
#### Naming styles ####
# Naming rules
dotnet_naming_rule.interface_should_be_begins_with_i.severity = warning
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = warning
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.private_or_internal_field_should_be_fields_start_with__.severity = warning
dotnet_naming_rule.private_or_internal_field_should_be_fields_start_with__.symbols = private_or_internal_field
dotnet_naming_rule.private_or_internal_field_should_be_fields_start_with__.style = fields_start_with__
# Symbol specifications
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field
dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected
dotnet_naming_symbols.private_or_internal_field.required_modifiers =
# Naming styles
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.fields_start_with__.required_prefix = _
dotnet_naming_style.fields_start_with__.required_suffix =
dotnet_naming_style.fields_start_with__.word_separator =
dotnet_naming_style.fields_start_with__.capitalization = camel_case
csharp_indent_labels = one_less_than_current
csharp_using_directive_placement = outside_namespace:suggestion
csharp_prefer_simple_using_statement = true:suggestion
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_top_level_statements = true:silent
csharp_style_prefer_primary_constructors = true:suggestion
csharp_prefer_system_threading_lock = true:suggestion
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:suggestion
csharp_style_expression_bodied_indexers = true:suggestion
csharp_style_expression_bodied_accessors = true:suggestion
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = true:silent
[*.vb]
#### Naming styles ####
# Naming rules
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
# Symbol specifications
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
# Naming styles
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
[*.{cs,vb}]
#### Naming styles ####
# Naming rules
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
# Symbol specifications
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =
# Naming styles
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_style_operator_placement_when_wrapping = beginning_of_line
tab_width = 4
end_of_line = crlf
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
+4 -5
View File
@@ -1,6 +1,5 @@
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("CryptoExchange.Net.UnitTests")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("CryptoExchange.Net.UnitTests")]
namespace System.Runtime.CompilerServices namespace System.Runtime.CompilerServices;
{
internal static class IsExternalInit { } internal static class IsExternalInit { }
}
@@ -1,12 +1,11 @@
using System; using System;
namespace CryptoExchange.Net.Attributes namespace CryptoExchange.Net.Attributes;
/// <summary>
/// Used for conversion in ArrayConverter
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class JsonConversionAttribute: Attribute
{ {
/// <summary>
/// Used for conversion in ArrayConverter
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class JsonConversionAttribute: Attribute
{
}
} }
@@ -1,13 +1,13 @@
using System; using System;
namespace CryptoExchange.Net.Attributes namespace CryptoExchange.Net.Attributes;
/// <summary>
/// Map a enum entry to string values
/// </summary>
[AttributeUsage(AttributeTargets.Field)]
public class MapAttribute : Attribute
{ {
/// <summary>
/// Map a enum entry to string values
/// </summary>
[AttributeUsage(AttributeTargets.Field)]
public class MapAttribute : Attribute
{
/// <summary> /// <summary>
/// Values mapping to the enum entry /// Values mapping to the enum entry
/// </summary> /// </summary>
@@ -21,5 +21,4 @@ namespace CryptoExchange.Net.Attributes
{ {
Values = maps; Values = maps;
} }
}
} }
@@ -1,15 +1,12 @@
using System; using System;
using System.IO;
using CryptoExchange.Net.Converters.SystemTextJson;
using CryptoExchange.Net.Converters.MessageParsing;
namespace CryptoExchange.Net.Authentication namespace CryptoExchange.Net.Authentication;
/// <summary>
/// Api credentials, used to sign requests accessing private endpoints
/// </summary>
public class ApiCredentials
{ {
/// <summary>
/// Api credentials, used to sign requests accessing private endpoints
/// </summary>
public class ApiCredentials
{
/// <summary> /// <summary>
/// The api key / label to authenticate requests /// The api key / label to authenticate requests
/// </summary> /// </summary>
@@ -56,5 +53,4 @@ namespace CryptoExchange.Net.Authentication
{ {
return new ApiCredentials(Key, Secret, Pass, CredentialType); return new ApiCredentials(Key, Secret, Pass, CredentialType);
} }
}
} }
@@ -1,10 +1,10 @@
namespace CryptoExchange.Net.Authentication namespace CryptoExchange.Net.Authentication;
/// <summary>
/// Credentials type
/// </summary>
public enum ApiCredentialsType
{ {
/// <summary>
/// Credentials type
/// </summary>
public enum ApiCredentialsType
{
/// <summary> /// <summary>
/// Hmac keys credentials /// Hmac keys credentials
/// </summary> /// </summary>
@@ -17,5 +17,4 @@
/// Rsa keys credentials in pem/base64 format. Only available for .NetStandard 2.1 and up, use xml format for lower. /// Rsa keys credentials in pem/base64 format. Only available for .NetStandard 2.1 and up, use xml format for lower.
/// </summary> /// </summary>
RsaPem RsaPem
}
} }
@@ -1,21 +1,20 @@
using CryptoExchange.Net.Clients; using CryptoExchange.Net.Clients;
using CryptoExchange.Net.Converters.SystemTextJson; using CryptoExchange.Net.Converters.SystemTextJson;
using CryptoExchange.Net.Interfaces; using CryptoExchange.Net.Interfaces;
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Net.Http;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
namespace CryptoExchange.Net.Authentication namespace CryptoExchange.Net.Authentication;
/// <summary>
/// Base class for authentication providers
/// </summary>
public abstract class AuthenticationProvider
{ {
/// <summary>
/// Base class for authentication providers
/// </summary>
public abstract class AuthenticationProvider
{
internal IAuthTimeProvider TimeProvider { get; set; } = new AuthTimeProvider(); internal IAuthTimeProvider TimeProvider { get; set; } = new AuthTimeProvider();
/// <summary> /// <summary>
@@ -209,7 +208,9 @@ namespace CryptoExchange.Net.Authentication
/// <returns></returns> /// <returns></returns>
protected static string SignMD5(string data, SignOutputType? outputType = null) protected static string SignMD5(string data, SignOutputType? outputType = null)
{ {
#pragma warning disable CA5351
using var encryptor = MD5.Create(); using var encryptor = MD5.Create();
#pragma warning restore CA5351
var resultBytes = encryptor.ComputeHash(Encoding.UTF8.GetBytes(data)); var resultBytes = encryptor.ComputeHash(Encoding.UTF8.GetBytes(data));
return outputType == SignOutputType.Base64 ? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes); return outputType == SignOutputType.Base64 ? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes);
} }
@@ -222,7 +223,9 @@ namespace CryptoExchange.Net.Authentication
/// <returns></returns> /// <returns></returns>
protected static string SignMD5(byte[] data, SignOutputType? outputType = null) protected static string SignMD5(byte[] data, SignOutputType? outputType = null)
{ {
#pragma warning disable CA5351
using var encryptor = MD5.Create(); using var encryptor = MD5.Create();
#pragma warning restore CA5351
var resultBytes = encryptor.ComputeHash(data); var resultBytes = encryptor.ComputeHash(data);
return outputType == SignOutputType.Base64 ? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes); return outputType == SignOutputType.Base64 ? BytesToBase64String(resultBytes) : BytesToHexString(resultBytes);
} }
@@ -234,7 +237,9 @@ namespace CryptoExchange.Net.Authentication
/// <returns></returns> /// <returns></returns>
protected static byte[] SignMD5Bytes(string data) protected static byte[] SignMD5Bytes(string data)
{ {
#pragma warning disable CA5351
using var encryptor = MD5.Create(); using var encryptor = MD5.Create();
#pragma warning restore CA5351
return encryptor.ComputeHash(Encoding.UTF8.GetBytes(data)); return encryptor.ComputeHash(Encoding.UTF8.GetBytes(data));
} }
@@ -454,13 +459,17 @@ namespace CryptoExchange.Net.Authentication
else else
return stringSerializer.Serialize(parameters); return stringSerializer.Serialize(parameters);
} }
} }
/// <inheritdoc />
public abstract class AuthenticationProvider<TApiCredentials> : AuthenticationProvider where TApiCredentials : ApiCredentials
{
/// <inheritdoc /> /// <inheritdoc />
public abstract class AuthenticationProvider<TApiCredentials> : AuthenticationProvider where TApiCredentials : ApiCredentials #pragma warning disable IDE1006 // Naming Styles
{ #pragma warning disable CA1707 // Naming Styles
/// <inheritdoc />
protected new TApiCredentials _credentials => (TApiCredentials)base._credentials; protected new TApiCredentials _credentials => (TApiCredentials)base._credentials;
#pragma warning restore IDE1006 // Naming Styles
#pragma warning restore CA1707 // Naming Styles
/// <summary> /// <summary>
/// ctor /// ctor
@@ -469,5 +478,4 @@ namespace CryptoExchange.Net.Authentication
protected AuthenticationProvider(TApiCredentials credentials) : base(credentials) protected AuthenticationProvider(TApiCredentials credentials) : base(credentials)
{ {
} }
}
} }
@@ -1,10 +1,10 @@
namespace CryptoExchange.Net.Authentication namespace CryptoExchange.Net.Authentication;
/// <summary>
/// Output string type
/// </summary>
public enum SignOutputType
{ {
/// <summary>
/// Output string type
/// </summary>
public enum SignOutputType
{
/// <summary> /// <summary>
/// Hex string /// Hex string
/// </summary> /// </summary>
@@ -13,5 +13,4 @@
/// Base64 string /// Base64 string
/// </summary> /// </summary>
Base64 Base64
}
} }
+4 -5
View File
@@ -1,11 +1,11 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Linq; using System.Linq;
namespace CryptoExchange.Net.Caching namespace CryptoExchange.Net.Caching;
internal class MemoryCache
{ {
internal class MemoryCache
{
private readonly ConcurrentDictionary<string, CacheItem> _cache = new ConcurrentDictionary<string, CacheItem>(); private readonly ConcurrentDictionary<string, CacheItem> _cache = new ConcurrentDictionary<string, CacheItem>();
private readonly object _lock = new object(); private readonly object _lock = new object();
@@ -49,5 +49,4 @@ namespace CryptoExchange.Net.Caching
Value = value; Value = value;
} }
} }
}
} }
+16 -10
View File
@@ -1,20 +1,18 @@
using System; using System;
using System.Collections.Generic;
using CryptoExchange.Net.Authentication; using CryptoExchange.Net.Authentication;
using CryptoExchange.Net.Interfaces; using CryptoExchange.Net.Interfaces;
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Errors; using CryptoExchange.Net.Objects.Errors;
using CryptoExchange.Net.Objects.Options; using CryptoExchange.Net.Objects.Options;
using CryptoExchange.Net.SharedApis; using CryptoExchange.Net.SharedApis;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace CryptoExchange.Net.Clients namespace CryptoExchange.Net.Clients;
/// <summary>
/// Base API for all API clients
/// </summary>
public abstract class BaseApiClient : IDisposable, IBaseApiClient
{ {
/// <summary>
/// Base API for all API clients
/// </summary>
public abstract class BaseApiClient : IDisposable, IBaseApiClient
{
/// <summary> /// <summary>
/// Logger /// Logger
/// </summary> /// </summary>
@@ -126,9 +124,17 @@ namespace CryptoExchange.Net.Clients
/// <summary> /// <summary>
/// Dispose /// Dispose
/// </summary> /// </summary>
public virtual void Dispose() public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Dispose
/// </summary>
public virtual void Dispose(bool disposing)
{ {
_disposing = true; _disposing = true;
} }
}
} }
+23 -11
View File
@@ -1,17 +1,16 @@
using CryptoExchange.Net.Authentication; using CryptoExchange.Net.Authentication;
using CryptoExchange.Net.Objects.Options; using CryptoExchange.Net.Objects.Options;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace CryptoExchange.Net.Clients namespace CryptoExchange.Net.Clients;
/// <summary>
/// The base for all clients, websocket client and rest client
/// </summary>
public abstract class BaseClient : IDisposable
{ {
/// <summary>
/// The base for all clients, websocket client and rest client
/// </summary>
public abstract class BaseClient : IDisposable
{
/// <summary> /// <summary>
/// Version of the CryptoExchange.Net base library /// Version of the CryptoExchange.Net base library
/// </summary> /// </summary>
@@ -80,7 +79,7 @@ namespace CryptoExchange.Net.Clients
throw new ArgumentNullException(nameof(options)); throw new ArgumentNullException(nameof(options));
ClientOptions = options; ClientOptions = options;
_logger.Log(LogLevel.Trace, $"Client configuration: {options}, CryptoExchange.Net: v{CryptoExchangeLibVersion}, {Exchange}.Net: v{ExchangeLibVersion}"); _logger.Log(LogLevel.Trace, "Client configuration: {Options}, CryptoExchange.Net: v{CryptoExchangeVersion}, {Exchange}.Net: v{ExchangeVersion}", options, CryptoExchangeLibVersion, Exchange, ExchangeLibVersion);
} }
/// <summary> /// <summary>
@@ -102,7 +101,7 @@ namespace CryptoExchange.Net.Clients
if (ClientOptions == null) if (ClientOptions == null)
throw new InvalidOperationException("Client should have called Initialize before adding API clients"); throw new InvalidOperationException("Client should have called Initialize before adding API clients");
_logger.Log(LogLevel.Trace, $" {apiClient.GetType().Name}, base address: {apiClient.BaseAddress}"); _logger.Log(LogLevel.Trace, " {ApiClient}, base address: {BaseAddress}", apiClient.GetType().Name, apiClient.BaseAddress);
ApiClients.Add(apiClient); ApiClients.Add(apiClient);
return apiClient; return apiClient;
} }
@@ -120,11 +119,24 @@ namespace CryptoExchange.Net.Clients
/// <summary> /// <summary>
/// Dispose /// Dispose
/// </summary> /// </summary>
public virtual void Dispose() public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Dispose
/// </summary>
public virtual void Dispose(bool disposing)
{
if (disposing)
{ {
_logger.Log(LogLevel.Debug, "Disposing client"); _logger.Log(LogLevel.Debug, "Disposing client");
foreach (var client in ApiClients) foreach (var client in ApiClients)
client.Dispose(); client.Dispose();
} }
} }
} }
+6 -7
View File
@@ -3,13 +3,13 @@ using CryptoExchange.Net.Interfaces;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
namespace CryptoExchange.Net.Clients namespace CryptoExchange.Net.Clients;
/// <summary>
/// Base rest client
/// </summary>
public abstract class BaseRestClient : BaseClient, IRestClient
{ {
/// <summary>
/// Base rest client
/// </summary>
public abstract class BaseRestClient : BaseClient, IRestClient
{
/// <inheritdoc /> /// <inheritdoc />
public int TotalRequestsMade => ApiClients.OfType<RestApiClient>().Sum(s => s.TotalRequestsMade); public int TotalRequestsMade => ApiClients.OfType<RestApiClient>().Sum(s => s.TotalRequestsMade);
@@ -22,5 +22,4 @@ namespace CryptoExchange.Net.Clients
{ {
_logger = loggerFactory?.CreateLogger(name + ".RestClient") ?? NullLoggerFactory.Instance.CreateLogger(name); _logger = loggerFactory?.CreateLogger(name + ".RestClient") ?? NullLoggerFactory.Instance.CreateLogger(name);
} }
}
} }
@@ -1,22 +1,21 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml.Linq;
using CryptoExchange.Net.Interfaces; using CryptoExchange.Net.Interfaces;
using CryptoExchange.Net.Logging.Extensions; using CryptoExchange.Net.Logging.Extensions;
using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.Objects.Sockets;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
namespace CryptoExchange.Net.Clients namespace CryptoExchange.Net.Clients;
/// <summary>
/// Base for socket client implementations
/// </summary>
public abstract class BaseSocketClient : BaseClient, ISocketClient
{ {
/// <summary>
/// Base for socket client implementations
/// </summary>
public abstract class BaseSocketClient : BaseClient, ISocketClient
{
#region fields #region fields
/// <summary> /// <summary>
@@ -128,5 +127,4 @@ namespace CryptoExchange.Net.Clients
return result; return result;
} }
}
} }
+19 -8
View File
@@ -1,14 +1,14 @@
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace CryptoExchange.Net.Clients namespace CryptoExchange.Net.Clients;
/// <summary>
/// Base crypto client
/// </summary>
public class CryptoBaseClient : IDisposable
{ {
/// <summary>
/// Base crypto client
/// </summary>
public class CryptoBaseClient : IDisposable
{
private readonly Dictionary<Type, object> _serviceCache = new Dictionary<Type, object>(); private readonly Dictionary<Type, object> _serviceCache = new Dictionary<Type, object>();
/// <summary> /// <summary>
@@ -59,9 +59,20 @@ namespace CryptoExchange.Net.Clients
/// <summary> /// <summary>
/// Dispose /// Dispose
/// </summary> /// </summary>
public void Dispose() public void Dispose(bool disposing)
{
if (disposing)
{ {
_serviceCache.Clear(); _serviceCache.Clear();
} }
} }
/// <summary>
/// Dispose
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
} }
@@ -1,14 +1,11 @@
using CryptoExchange.Net.Interfaces; using CryptoExchange.Net.Interfaces;
using Microsoft.Extensions.DependencyInjection;
using System; using System;
using System.Collections.Generic;
using System.Linq;
namespace CryptoExchange.Net.Clients namespace CryptoExchange.Net.Clients;
/// <inheritdoc />
public class CryptoRestClient : CryptoBaseClient, ICryptoRestClient
{ {
/// <inheritdoc />
public class CryptoRestClient : CryptoBaseClient, ICryptoRestClient
{
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
@@ -23,5 +20,4 @@ namespace CryptoExchange.Net.Clients
public CryptoRestClient(IServiceProvider serviceProvider) : base(serviceProvider) public CryptoRestClient(IServiceProvider serviceProvider) : base(serviceProvider)
{ {
} }
}
} }
@@ -1,11 +1,11 @@
using CryptoExchange.Net.Interfaces; using CryptoExchange.Net.Interfaces;
using System; using System;
namespace CryptoExchange.Net.Clients namespace CryptoExchange.Net.Clients;
/// <inheritdoc />
public class CryptoSocketClient : CryptoBaseClient, ICryptoSocketClient
{ {
/// <inheritdoc />
public class CryptoSocketClient : CryptoBaseClient, ICryptoSocketClient
{
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
@@ -20,5 +20,4 @@ namespace CryptoExchange.Net.Clients
public CryptoSocketClient(IServiceProvider serviceProvider) : base(serviceProvider) public CryptoSocketClient(IServiceProvider serviceProvider) : base(serviceProvider)
{ {
} }
}
} }
+24 -44
View File
@@ -18,13 +18,13 @@ using CryptoExchange.Net.RateLimiting.Interfaces;
using CryptoExchange.Net.Requests; using CryptoExchange.Net.Requests;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace CryptoExchange.Net.Clients namespace CryptoExchange.Net.Clients;
/// <summary>
/// Base rest API client for interacting with a REST API
/// </summary>
public abstract class RestApiClient : BaseApiClient, IRestApiClient
{ {
/// <summary>
/// Base rest API client for interacting with a REST API
/// </summary>
public abstract class RestApiClient : BaseApiClient, IRestApiClient
{
/// <inheritdoc /> /// <inheritdoc />
public IRequestFactory RequestFactory { get; set; } = new RequestFactory(); public IRequestFactory RequestFactory { get; set; } = new RequestFactory();
@@ -106,7 +106,7 @@ namespace CryptoExchange.Net.Clients
options, options,
apiOptions) apiOptions)
{ {
RequestFactory.Configure(options, httpClient); RequestFactory.Configure(options.Proxy, options.RequestTimeout, httpClient);
} }
/// <summary> /// <summary>
@@ -388,7 +388,7 @@ namespace CryptoExchange.Net.Clients
queryString = $"?{queryString}"; queryString = $"?{queryString}";
var uri = new Uri(baseAddress.AppendPath(definition.Path) + queryString); var uri = new Uri(baseAddress.AppendPath(definition.Path) + queryString);
var request = RequestFactory.Create(ClientOptions.HttpVersion, definition.Method, uri, requestId); var request = RequestFactory.Create(definition.Method, uri, requestId);
request.Accept = Constants.JsonContentHeader; request.Accept = Constants.JsonContentHeader;
foreach (var header in requestConfiguration.Headers) foreach (var header in requestConfiguration.Headers)
@@ -443,6 +443,9 @@ namespace CryptoExchange.Net.Clients
{ {
response = await request.GetResponseAsync(cancellationToken).ConfigureAwait(false); response = await request.GetResponseAsync(cancellationToken).ConfigureAwait(false);
sw.Stop(); sw.Stop();
var statusCode = response.StatusCode;
var headers = response.ResponseHeaders;
var responseLength = response.ContentLength;
responseStream = await response.GetResponseStreamAsync().ConfigureAwait(false); responseStream = await response.GetResponseStreamAsync().ConfigureAwait(false);
var outputOriginalData = ApiOptions.OutputOriginalData ?? ClientOptions.OutputOriginalData; var outputOriginalData = ApiOptions.OutputOriginalData ?? ClientOptions.OutputOriginalData;
@@ -472,18 +475,18 @@ namespace CryptoExchange.Net.Clients
if (error.Code == null || error.Code == 0) if (error.Code == null || error.Code == 0)
error.Code = (int)response.StatusCode; error.Code = (int)response.StatusCode;
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, response.ContentLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, error!); return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, error!);
} }
var valid = await accessor.Read(responseStream, outputOriginalData).ConfigureAwait(false); var valid = await accessor.Read(responseStream, outputOriginalData).ConfigureAwait(false);
if (typeof(T) == typeof(object)) if (typeof(T) == typeof(object))
// Success status code and expected empty response, assume it's correct // Success status code and expected empty response, assume it's correct
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, 0, accessor.OriginalDataAvailable ? accessor.GetOriginalString() : "[Data only available when OutputOriginal = true in client options]", request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, null); return new WebCallResult<T>(statusCode, headers, sw.Elapsed, 0, accessor.OriginalDataAvailable ? accessor.GetOriginalString() : "[Data only available when OutputOriginal = true in client options]", request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, null);
if (!valid) if (!valid)
{ {
// Invalid json // Invalid json
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, response.ContentLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, valid.Error); return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, valid.Error);
} }
// Json response received // Json response received
@@ -500,55 +503,33 @@ namespace CryptoExchange.Net.Clients
} }
// Success status code, but TryParseError determined it was an error response // Success status code, but TryParseError determined it was an error response
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, response.ContentLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, parsedError); return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, parsedError);
} }
var deserializeResult = accessor.Deserialize<T>(); var deserializeResult = accessor.Deserialize<T>();
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, response.ContentLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, deserializeResult.Data, deserializeResult.Error); return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, responseLength, OutputOriginalData ? accessor.GetOriginalString() : null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, deserializeResult.Data, deserializeResult.Error);
} }
catch (HttpRequestException requestException) catch (HttpRequestException requestException)
{ {
// Request exception, can't reach server for instance // Request exception, can't reach server for instance
var error = new WebError(requestException.Message, requestException); var error = new WebError(requestException.Message, requestException);
return new WebCallResult<T>(null, null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, error); return new WebCallResult<T>(null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, error);
} }
catch (OperationCanceledException canceledException) catch (OperationCanceledException canceledException)
{ {
if (cancellationToken != default && canceledException.CancellationToken == cancellationToken) if (cancellationToken != default && canceledException.CancellationToken == cancellationToken)
{ {
// Cancellation token canceled by caller // Cancellation token canceled by caller
return new WebCallResult<T>(null, null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, new CancellationRequestedError(canceledException)); return new WebCallResult<T>(null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, new CancellationRequestedError(canceledException));
} }
else else
{ {
// Request timed out // Request timed out
var error = new WebError($"Request timed out", exception: canceledException); var error = new WebError($"Request timed out", exception: canceledException);
error.ErrorType = ErrorType.Timeout; error.ErrorType = ErrorType.Timeout;
return new WebCallResult<T>(null, null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, error); return new WebCallResult<T>(null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, error);
} }
} }
catch (ArgumentException argumentException)
{
if (argumentException.Message.StartsWith("Only HTTP/"))
{
// Unsupported HTTP version error .net framework
var error = ArgumentError.Invalid(nameof(RestExchangeOptions.HttpVersion), $"Invalid HTTP version {request.HttpVersion}: " + argumentException.Message);
return new WebCallResult<T>(null, null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, error);
}
throw;
}
catch (NotSupportedException notSupportedException)
{
if (notSupportedException.Message.StartsWith("Request version value must be one of"))
{
// Unsupported HTTP version error dotnet code
var error = ArgumentError.Invalid(nameof(RestExchangeOptions.HttpVersion), $"Invalid HTTP version {request.HttpVersion}: " + notSupportedException.Message);
return new WebCallResult<T>(null, null, null, sw.Elapsed, null, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, error);
}
throw;
}
finally finally
{ {
accessor?.Clear(); accessor?.Clear();
@@ -656,7 +637,7 @@ namespace CryptoExchange.Net.Clients
{ {
// Handle retry after header // Handle retry after header
var retryAfterHeader = responseHeaders.SingleOrDefault(r => r.Key.Equals("Retry-After", StringComparison.InvariantCultureIgnoreCase)); var retryAfterHeader = responseHeaders.SingleOrDefault(r => r.Key.Equals("Retry-After", StringComparison.InvariantCultureIgnoreCase));
if (retryAfterHeader.Value?.Any() != true) if (!(retryAfterHeader.Value.Length > 0))
return new ServerRateLimitError(); return new ServerRateLimitError();
var value = retryAfterHeader.Value.First(); var value = retryAfterHeader.Value.First();
@@ -693,21 +674,21 @@ namespace CryptoExchange.Net.Clients
{ {
base.SetOptions(options); base.SetOptions(options);
RequestFactory.UpdateSettings(options.Proxy, options.RequestTimeout ?? ClientOptions.RequestTimeout, ClientOptions.HttpKeepAliveInterval); RequestFactory.UpdateSettings(options.Proxy, options.RequestTimeout ?? ClientOptions.RequestTimeout);
} }
internal async Task<WebCallResult<bool>> SyncTimeAsync() internal async Task<WebCallResult<bool>> SyncTimeAsync()
{ {
var timeSyncParams = GetTimeSyncInfo(); var timeSyncParams = GetTimeSyncInfo();
if (timeSyncParams == null) if (timeSyncParams == null)
return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, null, null, ResultDataSource.Server, true, null); return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, null, ResultDataSource.Server, true, null);
if (await timeSyncParams.TimeSyncState.Semaphore.WaitAsync(0).ConfigureAwait(false)) if (await timeSyncParams.TimeSyncState.Semaphore.WaitAsync(0).ConfigureAwait(false))
{ {
if (!timeSyncParams.SyncTime || DateTime.UtcNow - timeSyncParams.TimeSyncState.LastSyncTime < timeSyncParams.RecalculationInterval) if (!timeSyncParams.SyncTime || DateTime.UtcNow - timeSyncParams.TimeSyncState.LastSyncTime < timeSyncParams.RecalculationInterval)
{ {
timeSyncParams.TimeSyncState.Semaphore.Release(); timeSyncParams.TimeSyncState.Semaphore.Release();
return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, null, null, ResultDataSource.Server, true, null); return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, null, ResultDataSource.Server, true, null);
} }
var localTime = DateTime.UtcNow; var localTime = DateTime.UtcNow;
@@ -736,12 +717,11 @@ namespace CryptoExchange.Net.Clients
timeSyncParams.TimeSyncState.Semaphore.Release(); timeSyncParams.TimeSyncState.Semaphore.Release();
} }
return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, null, null, ResultDataSource.Server, true, null); return new WebCallResult<bool>(null, null, null, null, null, null, null, null, null, null, ResultDataSource.Server, true, null);
} }
private bool ShouldCache(RequestDefinition definition) private bool ShouldCache(RequestDefinition definition)
=> ClientOptions.CachingEnabled => ClientOptions.CachingEnabled
&& definition.Method == HttpMethod.Get && definition.Method == HttpMethod.Get
&& !definition.PreventCaching; && !definition.PreventCaching;
}
} }
+42 -64
View File
@@ -17,13 +17,13 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CryptoExchange.Net.Clients namespace CryptoExchange.Net.Clients;
/// <summary>
/// Base socket API client for interaction with a websocket API
/// </summary>
public abstract class SocketApiClient : BaseApiClient, ISocketApiClient
{ {
/// <summary>
/// Base socket API client for interaction with a websocket API
/// </summary>
public abstract class SocketApiClient : BaseApiClient, ISocketApiClient
{
#region Fields #region Fields
/// <inheritdoc/> /// <inheritdoc/>
public IWebsocketFactory SocketFactory { get; set; } = new WebsocketFactory(); public IWebsocketFactory SocketFactory { get; set; } = new WebsocketFactory();
@@ -86,7 +86,7 @@ namespace CryptoExchange.Net.Clients
/// <summary> /// <summary>
/// Whether to continue processing and forward unparsable messages to handlers /// Whether to continue processing and forward unparsable messages to handlers
/// </summary> /// </summary>
protected internal bool ProcessUnparsableMessages { get; set; } = false; protected internal bool ProcessUnparsableMessages { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public double IncomingKbps public double IncomingKbps
@@ -227,7 +227,7 @@ namespace CryptoExchange.Net.Clients
while (true) while (true)
{ {
// Get a new or existing socket connection // Get a new or existing socket connection
var socketResult = await GetSocketConnection(url, subscription.Authenticated, false, ct, subscription.Topic).ConfigureAwait(false); var socketResult = await GetSocketConnection(url, subscription.Authenticated, false, subscription.Topic).ConfigureAwait(false);
if (!socketResult) if (!socketResult)
return socketResult.As<UpdateSubscription>(null); return socketResult.As<UpdateSubscription>(null);
@@ -269,7 +269,6 @@ namespace CryptoExchange.Net.Clients
return new CallResult<UpdateSubscription>(new ServerError(new ErrorInfo(ErrorType.WebsocketPaused, "Socket is paused"))); return new CallResult<UpdateSubscription>(new ServerError(new ErrorInfo(ErrorType.WebsocketPaused, "Socket is paused")));
} }
subscription.Status = SubscriptionStatus.Subscribing;
var waitEvent = new AsyncResetEvent(false); var waitEvent = new AsyncResetEvent(false);
var subQuery = subscription.CreateSubscriptionQuery(socketConnection); var subQuery = subscription.CreateSubscriptionQuery(socketConnection);
if (subQuery != null) if (subQuery != null)
@@ -280,7 +279,7 @@ namespace CryptoExchange.Net.Clients
{ {
waitEvent?.Set(); waitEvent?.Set();
var isTimeout = subResult.Error is CancellationRequestedError; var isTimeout = subResult.Error is CancellationRequestedError;
if (isTimeout && subscription.Status == SubscriptionStatus.Subscribed) if (isTimeout && subscription.Confirmed)
{ {
// No response received, but the subscription did receive updates. We'll assume success // No response received, but the subscription did receive updates. We'll assume success
} }
@@ -288,7 +287,6 @@ namespace CryptoExchange.Net.Clients
{ {
_logger.FailedToSubscribe(socketConnection.SocketId, subResult.Error?.ToString()); _logger.FailedToSubscribe(socketConnection.SocketId, subResult.Error?.ToString());
// If this was a timeout we still need to send an unsubscribe to prevent messages coming in later // If this was a timeout we still need to send an unsubscribe to prevent messages coming in later
subscription.Status = SubscriptionStatus.Pending;
await socketConnection.CloseAsync(subscription).ConfigureAwait(false); await socketConnection.CloseAsync(subscription).ConfigureAwait(false);
return new CallResult<UpdateSubscription>(subResult.Error!); return new CallResult<UpdateSubscription>(subResult.Error!);
} }
@@ -297,7 +295,7 @@ namespace CryptoExchange.Net.Clients
subscription.HandleSubQueryResponse(subQuery.Response!); subscription.HandleSubQueryResponse(subQuery.Response!);
} }
subscription.Status = SubscriptionStatus.Subscribed; subscription.Confirmed = true;
if (ct != default) if (ct != default)
{ {
subscription.CancellationTokenRegistration = ct.Register(async () => subscription.CancellationTokenRegistration = ct.Register(async () =>
@@ -342,10 +340,16 @@ namespace CryptoExchange.Net.Clients
SocketConnection socketConnection; SocketConnection socketConnection;
var released = false; var released = false;
await semaphoreSlim.WaitAsync().ConfigureAwait(false);
try try
{ {
var socketResult = await GetSocketConnection(url, query.Authenticated, true, ct).ConfigureAwait(false); await semaphoreSlim.WaitAsync(ct).ConfigureAwait(false);
}
catch (OperationCanceledException) { }
try
{
var socketResult = await GetSocketConnection(url, query.Authenticated, true).ConfigureAwait(false);
if (!socketResult) if (!socketResult)
return socketResult.As<THandlerResponse>(default); return socketResult.As<THandlerResponse>(default);
@@ -397,7 +401,13 @@ namespace CryptoExchange.Net.Clients
return connectResult; return connectResult;
if (ClientOptions.DelayAfterConnect != TimeSpan.Zero) if (ClientOptions.DelayAfterConnect != TimeSpan.Zero)
await Task.Delay(ClientOptions.DelayAfterConnect).ConfigureAwait(false); {
try
{
await Task.Delay(ClientOptions.DelayAfterConnect, ct).ConfigureAwait(false);
}
catch (OperationCanceledException) { }
}
if (!authenticated || socket.Authenticated) if (!authenticated || socket.Authenticated)
return CallResult.SuccessResult; return CallResult.SuccessResult;
@@ -496,56 +506,25 @@ namespace CryptoExchange.Net.Clients
/// <param name="address">The address the socket is for</param> /// <param name="address">The address the socket is for</param>
/// <param name="authenticated">Whether the socket should be authenticated</param> /// <param name="authenticated">Whether the socket should be authenticated</param>
/// <param name="dedicatedRequestConnection">Whether a dedicated request connection should be returned</param> /// <param name="dedicatedRequestConnection">Whether a dedicated request connection should be returned</param>
/// <param name="ct">Cancellation token</param>
/// <param name="topic">The subscription topic, can be provided when multiple of the same topics are not allowed on a connection</param> /// <param name="topic">The subscription topic, can be provided when multiple of the same topics are not allowed on a connection</param>
/// <returns></returns> /// <returns></returns>
protected virtual async Task<CallResult<SocketConnection>> GetSocketConnection(string address, bool authenticated, bool dedicatedRequestConnection, CancellationToken ct, string? topic = null) protected virtual async Task<CallResult<SocketConnection>> GetSocketConnection(string address, bool authenticated, bool dedicatedRequestConnection, string? topic = null)
{ {
var socketQuery = socketConnections.Where(s => s.Value.Tag.TrimEnd('/') == address.TrimEnd('/') var socketQuery = socketConnections.Where(s => (s.Value.Status == SocketConnection.SocketStatus.None || s.Value.Status == SocketConnection.SocketStatus.Connected)
&& s.Value.Tag.TrimEnd('/') == address.TrimEnd('/')
&& s.Value.ApiClient.GetType() == GetType() && s.Value.ApiClient.GetType() == GetType()
&& (AllowTopicsOnTheSameConnection || !s.Value.Topics.Contains(topic))) && (s.Value.Authenticated == authenticated || !authenticated)
.Select(x => x.Value) && (AllowTopicsOnTheSameConnection || !s.Value.Topics.Contains(topic))
.ToList(); && s.Value.Connected);
// If all current socket connections are reconnecting or resubscribing wait for that to finish as we can probably use the existing connection SocketConnection connection;
var delayStart = DateTime.UtcNow;
var delayed = false;
while (socketQuery.Count >= 1 && socketQuery.All(x => x.Status == SocketConnection.SocketStatus.Reconnecting || x.Status == SocketConnection.SocketStatus.Resubscribing))
{
if (DateTime.UtcNow - delayStart > TimeSpan.FromSeconds(10))
{
if (socketQuery.Count >= 1 && socketQuery.All(x => x.Status == SocketConnection.SocketStatus.Reconnecting || x.Status == SocketConnection.SocketStatus.Resubscribing))
{
// If after this time we still trying to reconnect/reprocess there is some issue in the connection
_logger.TimeoutWaitingForReconnectingSocket();
return new CallResult<SocketConnection>(new CantConnectError());
}
break;
}
delayed = true;
try { await Task.Delay(50, ct).ConfigureAwait(false); } catch (Exception) { }
if (ct.IsCancellationRequested)
return new CallResult<SocketConnection>(new CancellationRequestedError());
}
if (delayed)
_logger.WaitedForReconnectingSocket((long)(DateTime.UtcNow - delayStart).TotalMilliseconds);
socketQuery = socketQuery.Where(s => (s.Status == SocketConnection.SocketStatus.None || s.Status == SocketConnection.SocketStatus.Connected)
&& (s.Authenticated == authenticated || !authenticated)
&& s.Connected).ToList();
SocketConnection? connection;
if (!dedicatedRequestConnection) if (!dedicatedRequestConnection)
{ {
connection = socketQuery.Where(s => !s.DedicatedRequestConnection.IsDedicatedRequestConnection).OrderBy(s => s.UserSubscriptionCount).FirstOrDefault(); connection = socketQuery.Where(s => !s.Value.DedicatedRequestConnection.IsDedicatedRequestConnection).OrderBy(s => s.Value.UserSubscriptionCount).FirstOrDefault().Value;
} }
else else
{ {
connection = socketQuery.Where(s => s.DedicatedRequestConnection.IsDedicatedRequestConnection).FirstOrDefault(); connection = socketQuery.Where(s => s.Value.DedicatedRequestConnection.IsDedicatedRequestConnection).FirstOrDefault().Value;
if (connection != null && !connection.DedicatedRequestConnection.Authenticated) if (connection != null && !connection.DedicatedRequestConnection.Authenticated)
// Mark dedicated request connection as authenticated if the request is authenticated // Mark dedicated request connection as authenticated if the request is authenticated
connection.DedicatedRequestConnection.Authenticated = authenticated; connection.DedicatedRequestConnection.Authenticated = authenticated;
@@ -553,13 +532,10 @@ namespace CryptoExchange.Net.Clients
if (connection != null) if (connection != null)
{ {
if (connection.UserSubscriptionCount < ClientOptions.SocketSubscriptionsCombineTarget if (connection.UserSubscriptionCount < ClientOptions.SocketSubscriptionsCombineTarget || (socketConnections.Count >= (ApiOptions.MaxSocketConnections ?? ClientOptions.MaxSocketConnections) && socketConnections.All(s => s.Value.UserSubscriptionCount >= ClientOptions.SocketSubscriptionsCombineTarget)))
|| (socketConnections.Count >= (ApiOptions.MaxSocketConnections ?? ClientOptions.MaxSocketConnections) && socketConnections.All(s => s.Value.UserSubscriptionCount >= ClientOptions.SocketSubscriptionsCombineTarget)))
{
// Use existing socket if it has less than target connections OR it has the least connections and we can't make new // Use existing socket if it has less than target connections OR it has the least connections and we can't make new
return new CallResult<SocketConnection>(connection); return new CallResult<SocketConnection>(connection);
} }
}
var connectionAddress = await GetConnectionUrlAsync(address, authenticated).ConfigureAwait(false); var connectionAddress = await GetConnectionUrlAsync(address, authenticated).ConfigureAwait(false);
if (!connectionAddress) if (!connectionAddress)
@@ -752,7 +728,7 @@ namespace CryptoExchange.Net.Clients
{ {
foreach (var item in DedicatedConnectionConfigs) foreach (var item in DedicatedConnectionConfigs)
{ {
var socketResult = await GetSocketConnection(item.SocketAddress, item.Authenticated, true, CancellationToken.None).ConfigureAwait(false); var socketResult = await GetSocketConnection(item.SocketAddress, item.Authenticated, true).ConfigureAwait(false);
if (!socketResult) if (!socketResult)
return socketResult.AsDataless(); return socketResult.AsDataless();
@@ -849,7 +825,7 @@ namespace CryptoExchange.Net.Clients
cs.SubscriptionStates.ForEach(subState => cs.SubscriptionStates.ForEach(subState =>
{ {
sb.AppendLine($"\t\t\tId: {subState.Id}"); sb.AppendLine($"\t\t\tId: {subState.Id}");
sb.AppendLine($"\t\t\tStatus: {subState.Status}"); sb.AppendLine($"\t\t\tConfirmed: {subState.Confirmed}");
sb.AppendLine($"\t\t\tInvocations: {subState.Invocations}"); sb.AppendLine($"\t\t\tInvocations: {subState.Invocations}");
sb.AppendLine($"\t\t\tIdentifiers: [{subState.ListenMatcher.ToString()}]"); sb.AppendLine($"\t\t\tIdentifiers: [{subState.ListenMatcher.ToString()}]");
}); });
@@ -863,8 +839,11 @@ namespace CryptoExchange.Net.Clients
/// <summary> /// <summary>
/// Dispose the client /// Dispose the client
/// </summary> /// </summary>
public override void Dispose() public override void Dispose(bool disposing)
{ {
if (disposing)
return;
_disposing = true; _disposing = true;
var tasks = new List<Task>(); var tasks = new List<Task>();
{ {
@@ -879,7 +858,7 @@ namespace CryptoExchange.Net.Clients
} }
semaphoreSlim?.Dispose(); semaphoreSlim?.Dispose();
base.Dispose(); base.Dispose(disposing);
} }
/// <summary> /// <summary>
@@ -897,5 +876,4 @@ namespace CryptoExchange.Net.Clients
/// <param name="data"></param> /// <param name="data"></param>
/// <returns></returns> /// <returns></returns>
public virtual ReadOnlyMemory<byte> PreprocessStreamMessage(SocketConnection connection, WebSocketMessageType type, ReadOnlyMemory<byte> data) => data; public virtual ReadOnlyMemory<byte> PreprocessStreamMessage(SocketConnection connection, WebSocketMessageType type, ReadOnlyMemory<byte> data) => data;
}
} }
@@ -1,13 +1,13 @@
using System; using System;
namespace CryptoExchange.Net.Converters namespace CryptoExchange.Net.Converters;
/// <summary>
/// Mark property as an index in the array
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class ArrayPropertyAttribute : Attribute
{ {
/// <summary>
/// Mark property as an index in the array
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class ArrayPropertyAttribute : Attribute
{
/// <summary> /// <summary>
/// The index in the array /// The index in the array
/// </summary> /// </summary>
@@ -21,5 +21,4 @@ namespace CryptoExchange.Net.Converters
{ {
Index = index; Index = index;
} }
}
} }
@@ -1,16 +1,14 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters namespace CryptoExchange.Net.Converters;
/// <summary>
/// Caching for JsonSerializerContext instances
/// </summary>
public static class JsonSerializerContextCache
{ {
/// <summary>
/// Caching for JsonSerializerContext instances
/// </summary>
public static class JsonSerializerContextCache
{
private static ConcurrentDictionary<Type, JsonSerializerContext> _cache = new ConcurrentDictionary<Type, JsonSerializerContext>(); private static ConcurrentDictionary<Type, JsonSerializerContext> _cache = new ConcurrentDictionary<Type, JsonSerializerContext>();
/// <summary> /// <summary>
@@ -27,5 +25,4 @@ namespace CryptoExchange.Net.Converters
_cache[contextType] = instance; _cache[contextType] = instance;
return instance; return instance;
} }
}
} }
@@ -1,10 +1,10 @@
namespace CryptoExchange.Net.Converters.MessageParsing namespace CryptoExchange.Net.Converters.MessageParsing;
/// <summary>
/// Node accessor
/// </summary>
public readonly struct NodeAccessor
{ {
/// <summary>
/// Node accessor
/// </summary>
public readonly struct NodeAccessor
{
/// <summary> /// <summary>
/// Index /// Index
/// </summary> /// </summary>
@@ -45,5 +45,4 @@
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public static NodeAccessor PropertyName() { return new NodeAccessor(null, null, 2); } public static NodeAccessor PropertyName() { return new NodeAccessor(null, null, 2); }
}
} }
@@ -1,13 +1,13 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
namespace CryptoExchange.Net.Converters.MessageParsing namespace CryptoExchange.Net.Converters.MessageParsing;
/// <summary>
/// Message access definition
/// </summary>
public readonly struct MessagePath : IEnumerable<NodeAccessor>
{ {
/// <summary>
/// Message access definition
/// </summary>
public readonly struct MessagePath : IEnumerable<NodeAccessor>
{
private readonly List<NodeAccessor> _path; private readonly List<NodeAccessor> _path;
internal void Add(NodeAccessor node) internal void Add(NodeAccessor node)
@@ -46,5 +46,4 @@ namespace CryptoExchange.Net.Converters.MessageParsing
{ {
return GetEnumerator(); return GetEnumerator();
} }
}
} }
@@ -1,10 +1,10 @@
namespace CryptoExchange.Net.Converters.MessageParsing namespace CryptoExchange.Net.Converters.MessageParsing;
/// <summary>
/// Message path extension methods
/// </summary>
public static class MessagePathExtension
{ {
/// <summary>
/// Message path extension methods
/// </summary>
public static class MessagePathExtension
{
/// <summary> /// <summary>
/// Add a string node accessor /// Add a string node accessor
/// </summary> /// </summary>
@@ -39,5 +39,4 @@
path.Add(NodeAccessor.Int(index)); path.Add(NodeAccessor.Int(index));
return path; return path;
} }
}
} }
@@ -1,10 +1,10 @@
namespace CryptoExchange.Net.Converters.MessageParsing namespace CryptoExchange.Net.Converters.MessageParsing;
/// <summary>
/// Message node type
/// </summary>
public enum NodeType
{ {
/// <summary>
/// Message node type
/// </summary>
public enum NodeType
{
/// <summary> /// <summary>
/// Array node /// Array node
/// </summary> /// </summary>
@@ -17,5 +17,4 @@
/// Value node /// Value node
/// </summary> /// </summary>
Value Value
}
} }
@@ -1,28 +1,27 @@
using System; using System;
using System.Collections.Concurrent;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using System.Text.Json; using System.Text.Json;
using CryptoExchange.Net.Attributes;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Diagnostics;
namespace CryptoExchange.Net.Converters.SystemTextJson
{
/// <summary>
/// Converter for arrays to objects. Can deserialize data like [0.1, 0.2, "test"] to an object. Mapping is done by marking the class with [JsonConverter(typeof(ArrayConverter))] and the properties
/// with [ArrayProperty(x)] where x is the index of the property in the array
/// </summary>
#if NET5_0_OR_GREATER #if NET5_0_OR_GREATER
public class ArrayConverter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T> : JsonConverter<T> where T : new() using System.Diagnostics.CodeAnalysis;
#else
public class ArrayConverter<T> : JsonConverter<T> where T : new()
#endif #endif
{ using System.Threading;
namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Converter for arrays to objects. Can deserialize data like [0.1, 0.2, "test"] to an object. Mapping is done by marking the class with [JsonConverter(typeof(ArrayConverter))] and the properties
/// with [ArrayProperty(x)] where x is the index of the property in the array
/// </summary>
#if NET5_0_OR_GREATER
public class ArrayConverter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T> : JsonConverter<T> where T : new()
#else
public class ArrayConverter<T> : JsonConverter<T> where T : new()
#endif
{
private static readonly Lazy<List<ArrayPropertyInfo>> _typePropertyInfo = new Lazy<List<ArrayPropertyInfo>>(CacheTypeAttributes, LazyThreadSafetyMode.PublicationOnly); private static readonly Lazy<List<ArrayPropertyInfo>> _typePropertyInfo = new Lazy<List<ArrayPropertyInfo>>(CacheTypeAttributes, LazyThreadSafetyMode.PublicationOnly);
/// <inheritdoc /> /// <inheritdoc />
@@ -228,7 +227,6 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
public JsonConverter? JsonConverter { get; set; } public JsonConverter? JsonConverter { get; set; }
public bool DefaultDeserialization { get; set; } public bool DefaultDeserialization { get; set; }
public Type TargetType { get; set; } = null!; public Type TargetType { get; set; } = null!;
public JsonSerializerOptions? JsonSerializerOptions { get; set; } = null; public JsonSerializerOptions? JsonSerializerOptions { get; set; }
}
} }
} }
@@ -1,15 +1,15 @@
using System; using System;
using System.Globalization; using System.Globalization;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Decimal converter that handles overflowing decimal values (by setting it to decimal.MaxValue)
/// </summary>
public class BigDecimalConverter : JsonConverter<decimal>
{ {
/// <summary>
/// Decimal converter that handles overflowing decimal values (by setting it to decimal.MaxValue)
/// </summary>
public class BigDecimalConverter : JsonConverter<decimal>
{
/// <inheritdoc /> /// <inheritdoc />
public override decimal Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override decimal Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
@@ -42,5 +42,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
{ {
writer.WriteNumberValue(value); writer.WriteNumberValue(value);
} }
}
} }
@@ -1,16 +1,16 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Bool converter
/// </summary>
public class BoolConverter : JsonConverterFactory
{ {
/// <summary>
/// Bool converter
/// </summary>
public class BoolConverter : JsonConverterFactory
{
/// <inheritdoc /> /// <inheritdoc />
public override bool CanConvert(Type typeToConvert) public override bool CanConvert(Type typeToConvert)
{ {
@@ -28,7 +28,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> (T)((object?)ReadBool(ref reader, typeToConvert, options) ?? default(T))!; => (T)((object?)ReadBool(ref reader, typeToConvert, options) ?? default(T))!;
public bool? ReadBool(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public static bool? ReadBool(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
if (reader.TokenType == JsonTokenType.True) if (reader.TokenType == JsonTokenType.True)
return true; return true;
@@ -80,5 +80,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
} }
} }
}
} }
@@ -1,23 +1,23 @@
using System; using System;
using System.Collections.Generic; #if NET5_0_OR_GREATER
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
#endif
using System.Linq; using System.Linq;
using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
{
/// <summary> /// <summary>
/// Converter for comma separated enum values /// Converter for comma separated enum values
/// </summary> /// </summary>
#if NET5_0_OR_GREATER #if NET5_0_OR_GREATER
public class CommaSplitEnumConverter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields)] T> : JsonConverter<T[]> where T : struct, Enum public class CommaSplitEnumConverter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields)] T> : JsonConverter<T[]> where T : struct, Enum
#else #else
public class CommaSplitEnumConverter<T> : JsonConverter<T[]> where T : struct, Enum public class CommaSplitEnumConverter<T> : JsonConverter<T[]> where T : struct, Enum
#endif #endif
{ {
/// <inheritdoc /> /// <inheritdoc />
public override T[]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override T[]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
@@ -33,5 +33,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
{ {
writer.WriteStringValue(string.Join(",", value.Select(x => EnumConverter.GetString(x)))); writer.WriteStringValue(string.Join(",", value.Select(x => EnumConverter.GetString(x))));
} }
}
} }
@@ -1,17 +1,17 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Globalization; using System.Globalization;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Date time converter
/// </summary>
public class DateTimeConverter : JsonConverterFactory
{ {
/// <summary>
/// Date time converter
/// </summary>
public class DateTimeConverter : JsonConverterFactory
{
private static readonly DateTime _epoch = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); private static readonly DateTime _epoch = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
private const long _ticksPerSecond = TimeSpan.TicksPerMillisecond * 1000; private const long _ticksPerSecond = TimeSpan.TicksPerMillisecond * 1000;
private const double _ticksPerMicrosecond = TimeSpan.TicksPerMillisecond / 1000d; private const double _ticksPerMicrosecond = TimeSpan.TicksPerMillisecond / 1000d;
@@ -34,7 +34,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> (T)((object?)ReadDateTime(ref reader, typeToConvert, options) ?? default(T))!; => (T)((object?)ReadDateTime(ref reader, typeToConvert, options) ?? default(T))!;
private DateTime? ReadDateTime(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) private static DateTime? ReadDateTime(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
if (reader.TokenType == JsonTokenType.Null) if (reader.TokenType == JsonTokenType.Null)
{ {
@@ -238,5 +238,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
/// <returns></returns> /// <returns></returns>
[return: NotNullIfNotNull("time")] [return: NotNullIfNotNull("time")]
public static long? ConvertToNanoseconds(DateTime? time) => time == null ? null : (long)Math.Round((time.Value - _epoch).Ticks / _ticksPerNanosecond); public static long? ConvertToNanoseconds(DateTime? time) => time == null ? null : (long)Math.Round((time.Value - _epoch).Ticks / _ticksPerNanosecond);
}
} }
@@ -1,15 +1,14 @@
using System; using System;
using System.Globalization;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Decimal converter
/// </summary>
public class DecimalConverter : JsonConverter<decimal?>
{ {
/// <summary>
/// Decimal converter
/// </summary>
public class DecimalConverter : JsonConverter<decimal?>
{
/// <inheritdoc /> /// <inheritdoc />
public override decimal? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override decimal? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
@@ -41,5 +40,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
else else
writer.WriteNumberValue(value.Value); writer.WriteNumberValue(value.Value);
} }
}
} }
@@ -1,15 +1,15 @@
using System; using System;
using System.Globalization; using System.Globalization;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Converter for serializing decimal values as string
/// </summary>
public class DecimalStringWriterConverter : JsonConverter<decimal>
{ {
/// <summary>
/// Converter for serializing decimal values as string
/// </summary>
public class DecimalStringWriterConverter : JsonConverter<decimal>
{
/// <inheritdoc /> /// <inheritdoc />
public override decimal Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override decimal Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
@@ -19,5 +19,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
/// <inheritdoc /> /// <inheritdoc />
public override void Write(Utf8JsonWriter writer, decimal value, JsonSerializerOptions options) public override void Write(Utf8JsonWriter writer, decimal value, JsonSerializerOptions options)
=> writer.WriteStringValue(value.ToString(CultureInfo.InvariantCulture) ?? null); => writer.WriteStringValue(value.ToString(CultureInfo.InvariantCulture) ?? null);
}
} }
@@ -1,4 +1,4 @@
using CryptoExchange.Net.Attributes; using CryptoExchange.Net.Attributes;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
@@ -9,13 +9,13 @@ using System.Reflection;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Static EnumConverter methods
/// </summary>
public static class EnumConverter
{ {
/// <summary>
/// Static EnumConverter methods
/// </summary>
public static class EnumConverter
{
/// <summary> /// <summary>
/// Get the enum value from a string /// Get the enum value from a string
/// </summary> /// </summary>
@@ -52,20 +52,20 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
public static string? GetString<T>(T? enumValue) where T : struct, Enum public static string? GetString<T>(T? enumValue) where T : struct, Enum
#endif #endif
=> EnumConverter<T>.GetString(enumValue); => EnumConverter<T>.GetString(enumValue);
} }
/// <summary> /// <summary>
/// Converter for enum values. Enums entries should be noted with a MapAttribute to map the enum value to a string value /// Converter for enum values. Enums entries should be noted with a MapAttribute to map the enum value to a string value
/// </summary> /// </summary>
#if NET5_0_OR_GREATER #if NET5_0_OR_GREATER
public class EnumConverter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields)] T> public class EnumConverter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields)] T>
#else #else
public class EnumConverter<T> public class EnumConverter<T>
#endif #endif
: JsonConverter<T>, INullableConverterFactory where T : struct, Enum : JsonConverter<T>, INullableConverterFactory where T : struct, Enum
{ {
private static List<KeyValuePair<T, string>>? _mapping = null; private static List<KeyValuePair<T, string>>? _mapping;
private NullableEnumConverter? _nullableEnumConverter = null; private NullableEnumConverter? _nullableEnumConverter;
private static ConcurrentBag<string> _unknownValuesWarned = new ConcurrentBag<string>(); private static ConcurrentBag<string> _unknownValuesWarned = new ConcurrentBag<string>();
@@ -79,7 +79,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
} }
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
return _enumConverter.ReadNullable(ref reader, typeToConvert, options, out var isEmptyString, out var warn); return EnumConverter<T>.ReadNullable(ref reader, typeToConvert, options, out var isEmptyString, out var warn);
} }
public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options) public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options)
@@ -122,7 +122,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
} }
} }
private T? ReadNullable(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, out bool isEmptyString, out bool warn) private static T? ReadNullable(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, out bool isEmptyString, out bool warn)
{ {
isEmptyString = false; isEmptyString = false;
warn = false; warn = false;
@@ -140,10 +140,10 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
_ => throw new Exception("Invalid token type for enum deserialization: " + reader.TokenType) _ => throw new Exception("Invalid token type for enum deserialization: " + reader.TokenType)
}; };
if (stringValue is null) if (string.IsNullOrEmpty(stringValue))
return null; return null;
if (!GetValue(enumType, stringValue, out var result)) if (!GetValue(enumType, stringValue!, out var result))
{ {
if (string.IsNullOrWhiteSpace(stringValue)) if (string.IsNullOrWhiteSpace(stringValue))
{ {
@@ -204,13 +204,6 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
return false; return false;
} }
if (String.IsNullOrEmpty(value))
{
// An empty/null value will always fail when parsing, so just return here
result = default;
return false;
}
try try
{ {
// If no explicit mapping is found try to parse string // If no explicit mapping is found try to parse string
@@ -292,5 +285,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
_nullableEnumConverter ??= new NullableEnumConverter(this); _nullableEnumConverter ??= new NullableEnumConverter(this);
return _nullableEnumConverter; return _nullableEnumConverter;
} }
}
} }
@@ -1,15 +1,14 @@
using System; using System;
using System.Globalization;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Converter for serializing enum values as int
/// </summary>
public class EnumIntWriterConverter<T> : JsonConverter<T> where T: struct, Enum
{ {
/// <summary>
/// Converter for serializing enum values as int
/// </summary>
public class EnumIntWriterConverter<T> : JsonConverter<T> where T: struct, Enum
{
/// <inheritdoc /> /// <inheritdoc />
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
@@ -19,5 +18,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
/// <inheritdoc /> /// <inheritdoc />
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
=> writer.WriteNumberValue((int)(object)value); => writer.WriteNumberValue((int)(object)value);
}
} }
@@ -1,9 +1,8 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
internal interface INullableConverterFactory
{ {
internal interface INullableConverterFactory
{
JsonConverter CreateNullableConverter(); JsonConverter CreateNullableConverter();
}
} }
@@ -1,15 +1,15 @@
using System; using System;
using System.Globalization; using System.Globalization;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Int converter
/// </summary>
public class IntConverter : JsonConverter<int?>
{ {
/// <summary>
/// Int converter
/// </summary>
public class IntConverter : JsonConverter<int?>
{
/// <inheritdoc /> /// <inheritdoc />
public override int? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override int? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
@@ -36,5 +36,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
else else
writer.WriteNumberValue(value.Value); writer.WriteNumberValue(value.Value);
} }
}
} }
@@ -1,15 +1,15 @@
using System; using System;
using System.Globalization; using System.Globalization;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Int converter
/// </summary>
public class LongConverter : JsonConverter<long?>
{ {
/// <summary>
/// Int converter
/// </summary>
public class LongConverter : JsonConverter<long?>
{
/// <inheritdoc /> /// <inheritdoc />
public override long? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override long? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
@@ -36,5 +36,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
else else
writer.WriteNumberValue(value.Value); writer.WriteNumberValue(value.Value);
} }
}
} }
@@ -1,14 +1,12 @@
using System; using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json.Serialization.Metadata; using System.Text.Json.Serialization.Metadata;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
internal class NullableEnumConverterFactory : JsonConverterFactory
{ {
internal class NullableEnumConverterFactory : JsonConverterFactory
{
private readonly IJsonTypeInfoResolver _jsonTypeInfoResolver; private readonly IJsonTypeInfoResolver _jsonTypeInfoResolver;
private static readonly JsonSerializerOptions _options = new JsonSerializerOptions(); private static readonly JsonSerializerOptions _options = new JsonSerializerOptions();
@@ -39,5 +37,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
return nullConverterFactory.CreateNullableConverter(); return nullConverterFactory.CreateNullableConverter();
} }
}
} }
@@ -1,14 +1,14 @@
using System; using System;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Read string or number as string
/// </summary>
public class NumberStringConverter : JsonConverter<string?>
{ {
/// <summary>
/// Read string or number as string
/// </summary>
public class NumberStringConverter : JsonConverter<string?>
{
/// <inheritdoc /> /// <inheritdoc />
public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
@@ -38,5 +38,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
{ {
writer.WriteStringValue(value); writer.WriteStringValue(value);
} }
}
} }
@@ -1,15 +1,17 @@
using System; using System;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using System.Text.Json; using System.Text.Json;
#if NET5_0_OR_GREATER
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
#endif
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Converter for values which contain a nested json value
/// </summary>
public class ObjectStringConverter<T> : JsonConverter<T>
{ {
/// <summary>
/// Converter for values which contain a nested json value
/// </summary>
public class ObjectStringConverter<T> : JsonConverter<T>
{
/// <inheritdoc /> /// <inheritdoc />
#if NET5_0_OR_GREATER #if NET5_0_OR_GREATER
[UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL3050:RequiresUnreferencedCode", Justification = "JsonSerializerOptions provided here has TypeInfoResolver set")] [UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL3050:RequiresUnreferencedCode", Justification = "JsonSerializerOptions provided here has TypeInfoResolver set")]
@@ -24,7 +26,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
if (string.IsNullOrEmpty(value)) if (string.IsNullOrEmpty(value))
return default; return default;
return (T?)JsonDocument.Parse(value!).Deserialize(typeof(T), options); return JsonDocument.Parse(value!).Deserialize<T>(options);
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -39,5 +41,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
writer.WriteStringValue(JsonSerializer.Serialize(value, options)); writer.WriteStringValue(JsonSerializer.Serialize(value, options));
} }
}
} }
@@ -1,15 +1,15 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Replace a value on a string property
/// </summary>
public abstract class ReplaceConverter : JsonConverter<string>
{ {
/// <summary>
/// Replace a value on a string property
/// </summary>
public abstract class ReplaceConverter : JsonConverter<string>
{
private readonly (string ValueToReplace, string ValueToReplaceWith)[] _replacementSets; private readonly (string ValueToReplace, string ValueToReplaceWith)[] _replacementSets;
/// <summary> /// <summary>
@@ -19,7 +19,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
{ {
_replacementSets = replaceSets.Select(x => _replacementSets = replaceSets.Select(x =>
{ {
var split = x.Split(new string[] { "->" }, StringSplitOptions.None); var split = x.Split(["->"], StringSplitOptions.None);
if (split.Length != 2) if (split.Length != 2)
throw new ArgumentException("Invalid replacement config"); throw new ArgumentException("Invalid replacement config");
return (split[0], split[1]); return (split[0], split[1]);
@@ -37,5 +37,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
/// <inheritdoc /> /// <inheritdoc />
public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) => writer.WriteStringValue(value); public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) => writer.WriteStringValue(value);
}
} }
@@ -1,15 +1,13 @@
using System; using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Attribute to mark a model as json serializable. Used for AOT compilation.
/// </summary>
[AttributeUsage(System.AttributeTargets.Class | AttributeTargets.Enum | System.AttributeTargets.Interface)]
public class SerializationModelAttribute : Attribute
{ {
/// <summary>
/// Attribute to mark a model as json serializable. Used for AOT compilation.
/// </summary>
[AttributeUsage(System.AttributeTargets.Class | AttributeTargets.Enum | System.AttributeTargets.Interface)]
public class SerializationModelAttribute : Attribute
{
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
@@ -19,5 +17,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
/// </summary> /// </summary>
/// <param name="type"></param> /// <param name="type"></param>
public SerializationModelAttribute(Type type) { } public SerializationModelAttribute(Type type) { }
}
} }
@@ -1,14 +1,14 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// Serializer options
/// </summary>
public static class SerializerOptions
{ {
/// <summary>
/// Serializer options
/// </summary>
public static class SerializerOptions
{
private static readonly ConcurrentDictionary<JsonSerializerContext, JsonSerializerOptions> _cache = new ConcurrentDictionary<JsonSerializerContext, JsonSerializerOptions>(); private static readonly ConcurrentDictionary<JsonSerializerContext, JsonSerializerOptions> _cache = new ConcurrentDictionary<JsonSerializerContext, JsonSerializerOptions>();
/// <summary> /// <summary>
@@ -43,5 +43,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
return options; return options;
} }
}
} }
@@ -1,17 +1,15 @@
using CryptoExchange.Net.SharedApis; using CryptoExchange.Net.SharedApis;
using System; using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
{
internal class SharedQuantityConverter : SharedQuantityReferenceConverter<SharedQuantity> { }
internal class SharedOrderQuantityConverter : SharedQuantityReferenceConverter<SharedOrderQuantity> { }
internal class SharedQuantityReferenceConverter<T> : JsonConverter<T> where T: SharedQuantityReference, new() internal class SharedQuantityConverter : SharedQuantityReferenceConverter<SharedQuantity> { }
{ internal class SharedOrderQuantityConverter : SharedQuantityReferenceConverter<SharedOrderQuantity> { }
internal class SharedQuantityReferenceConverter<T> : JsonConverter<T> where T: SharedQuantityReference, new()
{
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
if (reader.TokenType != JsonTokenType.StartArray) if (reader.TokenType != JsonTokenType.StartArray)
@@ -56,5 +54,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
writer.WriteNumberValue(value.QuantityInContracts.Value); writer.WriteNumberValue(value.QuantityInContracts.Value);
writer.WriteEndArray(); writer.WriteEndArray();
} }
}
} }
@@ -1,14 +1,12 @@
using CryptoExchange.Net.SharedApis; using CryptoExchange.Net.SharedApis;
using System; using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
internal class SharedSymbolConverter : JsonConverter<SharedSymbol>
{ {
internal class SharedSymbolConverter : JsonConverter<SharedSymbol>
{
public override SharedSymbol? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override SharedSymbol? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
if (reader.TokenType != JsonTokenType.StartArray) if (reader.TokenType != JsonTokenType.StartArray)
@@ -42,5 +40,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
writer.WriteStringValue(value.DeliverTime?.ToString()); writer.WriteStringValue(value.DeliverTime?.ToString());
writer.WriteEndArray(); writer.WriteEndArray();
} }
}
} }
@@ -1,21 +1,22 @@
using CryptoExchange.Net.Converters.MessageParsing; using CryptoExchange.Net.Converters.MessageParsing;
using CryptoExchange.Net.Interfaces; using CryptoExchange.Net.Interfaces;
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
using System; using System;
using System.Collections.Generic; #if NET5_0_OR_GREATER
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
#endif
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <summary>
/// System.Text.Json message accessor
/// </summary>
public abstract class SystemTextJsonMessageAccessor : IMessageAccessor
{ {
/// <summary>
/// System.Text.Json message accessor
/// </summary>
public abstract class SystemTextJsonMessageAccessor : IMessageAccessor
{
/// <summary> /// <summary>
/// The JsonDocument loaded /// The JsonDocument loaded
/// </summary> /// </summary>
@@ -235,13 +236,15 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
/// <inheritdoc /> /// <inheritdoc />
public abstract void Clear(); public abstract void Clear();
} }
/// <summary> /// <summary>
/// System.Text.Json stream message accessor /// System.Text.Json stream message accessor
/// </summary> /// </summary>
public class SystemTextJsonStreamMessageAccessor : SystemTextJsonMessageAccessor, IStreamMessageAccessor #pragma warning disable CA1001 // Types that own disposable fields should be disposable
{ public class SystemTextJsonStreamMessageAccessor : SystemTextJsonMessageAccessor, IStreamMessageAccessor
#pragma warning restore CA1001 // Types that own disposable fields should be disposable
{
private Stream? _stream; private Stream? _stream;
/// <inheritdoc /> /// <inheritdoc />
@@ -308,13 +311,13 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
_document = null; _document = null;
} }
} }
/// <summary> /// <summary>
/// System.Text.Json byte message accessor /// System.Text.Json byte message accessor
/// </summary> /// </summary>
public class SystemTextJsonByteMessageAccessor : SystemTextJsonMessageAccessor, IByteMessageAccessor public class SystemTextJsonByteMessageAccessor : SystemTextJsonMessageAccessor, IByteMessageAccessor
{ {
private ReadOnlyMemory<byte> _bytes; private ReadOnlyMemory<byte> _bytes;
/// <summary> /// <summary>
@@ -370,5 +373,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
_document?.Dispose(); _document?.Dispose();
_document = null; _document = null;
} }
}
} }
@@ -1,14 +1,14 @@
using CryptoExchange.Net.Interfaces; using CryptoExchange.Net.Interfaces;
#if NET5_0_OR_GREATER
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
#endif
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
namespace CryptoExchange.Net.Converters.SystemTextJson namespace CryptoExchange.Net.Converters.SystemTextJson;
/// <inheritdoc />
public class SystemTextJsonMessageSerializer : IStringMessageSerializer
{ {
/// <inheritdoc />
public class SystemTextJsonMessageSerializer : IStringMessageSerializer
{
private readonly JsonSerializerOptions _options; private readonly JsonSerializerOptions _options;
/// <summary> /// <summary>
@@ -25,5 +25,4 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
[UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL3050:RequiresUnreferencedCode", Justification = "Everything referenced in the loaded assembly is manually preserved, so it's safe")] [UnconditionalSuppressMessage("AssemblyLoadTrimming", "IL3050:RequiresUnreferencedCode", Justification = "Everything referenced in the loaded assembly is manually preserved, so it's safe")]
#endif #endif
public string Serialize<T>(T message) => JsonSerializer.Serialize(message, _options); public string Serialize<T>(T message) => JsonSerializer.Serialize(message, _options);
}
} }
+13 -3
View File
@@ -6,9 +6,9 @@
<PackageId>CryptoExchange.Net</PackageId> <PackageId>CryptoExchange.Net</PackageId>
<Authors>JKorf</Authors> <Authors>JKorf</Authors>
<Description>CryptoExchange.Net is a base library which is used to implement different cryptocurrency (exchange) API's. It provides a standardized way of implementing different API's, which results in a very similar experience for users of the API implementations.</Description> <Description>CryptoExchange.Net is a base library which is used to implement different cryptocurrency (exchange) API's. It provides a standardized way of implementing different API's, which results in a very similar experience for users of the API implementations.</Description>
<PackageVersion>9.9.0</PackageVersion> <PackageVersion>9.6.0</PackageVersion>
<AssemblyVersion>9.9.0</AssemblyVersion> <AssemblyVersion>9.6.0</AssemblyVersion>
<FileVersion>9.9.0</FileVersion> <FileVersion>9.6.0</FileVersion>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance> <PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<PackageTags>OKX;OKX.Net;Mexc;Mexc.Net;Kucoin;Kucoin.Net;Kraken;Kraken.Net;Huobi;Huobi.Net;CoinEx;CoinEx.Net;Bybit;Bybit.Net;Bitget;Bitget.Net;Bitfinex;Bitfinex.Net;Binance;Binance.Net;CryptoCurrency;CryptoCurrency Exchange;CryptoExchange.Net</PackageTags> <PackageTags>OKX;OKX.Net;Mexc;Mexc.Net;Kucoin;Kucoin.Net;Kraken;Kraken.Net;Huobi;Huobi.Net;CoinEx;CoinEx.Net;Bybit;Bybit.Net;Bitget;Bitget.Net;Bitfinex;Bitfinex.Net;Binance;Binance.Net;CryptoCurrency;CryptoCurrency Exchange;CryptoExchange.Net</PackageTags>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
@@ -24,6 +24,7 @@
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="C:\Projects\CryptoExchange.Net\CryptoExchange.Net\.editorconfig" />
<None Include="Icon\icon.png" Pack="true" PackagePath="\" /> <None Include="Icon\icon.png" Pack="true" PackagePath="\" />
<None Include="..\README.md" Pack="true" PackagePath="\" /> <None Include="..\README.md" Pack="true" PackagePath="\" />
</ItemGroup> </ItemGroup>
@@ -40,6 +41,12 @@
<PropertyGroup> <PropertyGroup>
<DocumentationFile>CryptoExchange.Net.xml</DocumentationFile> <DocumentationFile>CryptoExchange.Net.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<AnalysisMode>Recommended</AnalysisMode>
<AnalysisModeGlobalization>None</AnalysisModeGlobalization>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ConfigureAwaitChecker.Analyzer" Version="5.0.0.1"> <PackageReference Include="ConfigureAwaitChecker.Analyzer" Version="5.0.0.1">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
@@ -58,4 +65,7 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.6" /> <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.6" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.6" /> <PackageReference Include="Microsoft.Extensions.Http" Version="9.0.6" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<EditorConfigFiles Remove="C:\Projects\CryptoExchange.Net\CryptoExchange.Net\.editorconfig" />
</ItemGroup>
</Project> </Project>
+16 -13
View File
@@ -1,20 +1,22 @@
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
using CryptoExchange.Net.SharedApis; using CryptoExchange.Net.SharedApis;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
#if NETSTANDARD2_1_OR_GREATER || NET9_0_OR_GREATER
using System.Security.Cryptography; using System.Security.Cryptography;
#endif
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CryptoExchange.Net namespace CryptoExchange.Net;
/// <summary>
/// General helpers functions
/// </summary>
public static class ExchangeHelpers
{ {
/// <summary>
/// General helpers functions
/// </summary>
public static class ExchangeHelpers
{
private const string _allowedRandomChars = "ABCDEFGHIJKLMONOPQRSTUVWXYZabcdefghijklmonopqrstuvwxyz0123456789"; private const string _allowedRandomChars = "ABCDEFGHIJKLMONOPQRSTUVWXYZabcdefghijklmonopqrstuvwxyz0123456789";
private const string _allowedRandomHexChars = "0123456789ABCDEF"; private const string _allowedRandomHexChars = "0123456789ABCDEF";
@@ -89,6 +91,8 @@ namespace CryptoExchange.Net
else value += (step.Value - offset); else value += (step.Value - offset);
} }
value = RoundDown(value, 8);
return value.Normalize(); return value.Normalize();
} }
@@ -285,16 +289,16 @@ namespace CryptoExchange.Net
/// <summary> /// <summary>
/// Execute multiple requests to retrieve multiple pages of the result set /// Execute multiple requests to retrieve multiple pages of the result set
/// </summary> /// </summary>
/// <typeparam name="T">Type of the client</typeparam> /// <typeparam name="TResult">Type of the client</typeparam>
/// <typeparam name="U">Type of the request</typeparam> /// <typeparam name="TRequest">Type of the request</typeparam>
/// <param name="paginatedFunc">The func to execute with each request</param> /// <param name="paginatedFunc">The func to execute with each request</param>
/// <param name="request">The request parameters</param> /// <param name="request">The request parameters</param>
/// <param name="ct">Cancellation token</param> /// <param name="ct">Cancellation token</param>
/// <returns></returns> /// <returns></returns>
public static async IAsyncEnumerable<ExchangeWebResult<T[]>> ExecutePages<T, U>(Func<U, INextPageToken?, CancellationToken, Task<ExchangeWebResult<T[]>>> paginatedFunc, U request, [EnumeratorCancellation]CancellationToken ct = default) public static async IAsyncEnumerable<ExchangeWebResult<TResult[]>> ExecutePages<TResult, TRequest>(Func<TRequest, INextPageToken?, CancellationToken, Task<ExchangeWebResult<TResult[]>>> paginatedFunc, TRequest request, [EnumeratorCancellation]CancellationToken ct = default)
{ {
var result = new List<T>(); var result = new List<TResult>();
ExchangeWebResult<T[]> batch; ExchangeWebResult<TResult[]> batch;
INextPageToken? nextPageToken = null; INextPageToken? nextPageToken = null;
while (true) while (true)
{ {
@@ -383,5 +387,4 @@ namespace CryptoExchange.Net
// Unknown decimal format, return null // Unknown decimal format, return null
return null; return null;
} }
}
} }
+7 -9
View File
@@ -1,17 +1,16 @@
using CryptoExchange.Net.SharedApis; using CryptoExchange.Net.SharedApis;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
namespace CryptoExchange.Net namespace CryptoExchange.Net;
/// <summary>
/// Cache for symbol parsing
/// </summary>
public static class ExchangeSymbolCache
{ {
/// <summary>
/// Cache for symbol parsing
/// </summary>
public static class ExchangeSymbolCache
{
private static ConcurrentDictionary<string, ExchangeInfo> _symbolInfos = new ConcurrentDictionary<string, ExchangeInfo>(); private static ConcurrentDictionary<string, ExchangeInfo> _symbolInfos = new ConcurrentDictionary<string, ExchangeInfo>();
/// <summary> /// <summary>
@@ -66,5 +65,4 @@ namespace CryptoExchange.Net
Symbols = symbols; Symbols = symbols;
} }
} }
}
} }
+7 -11
View File
@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO.Compression; using System.IO.Compression;
using System.IO; using System.IO;
@@ -10,17 +10,14 @@ using CryptoExchange.Net.Objects;
using System.Globalization; using System.Globalization;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using CryptoExchange.Net.SharedApis; using CryptoExchange.Net.SharedApis;
using System.Text.Json.Serialization.Metadata;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace CryptoExchange.Net namespace CryptoExchange.Net;
/// <summary>
/// Helper methods
/// </summary>
public static class ExtensionMethods
{ {
/// <summary>
/// Helper methods
/// </summary>
public static class ExtensionMethods
{
/// <summary> /// <summary>
/// Add a parameter /// Add a parameter
/// </summary> /// </summary>
@@ -519,6 +516,5 @@ namespace CryptoExchange.Net
return services; return services;
} }
}
} }
@@ -1,16 +1,15 @@
using System; using System;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Time provider
/// </summary>
internal interface IAuthTimeProvider
{ {
/// <summary>
/// Time provider
/// </summary>
internal interface IAuthTimeProvider
{
/// <summary> /// <summary>
/// Get current time /// Get current time
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
DateTime GetTime(); DateTime GetTime();
}
} }
@@ -1,16 +1,15 @@
using CryptoExchange.Net.Authentication; using CryptoExchange.Net.Authentication;
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Options; using CryptoExchange.Net.Objects.Options;
using CryptoExchange.Net.SharedApis; using CryptoExchange.Net.SharedApis;
using System; using System;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Base api client
/// </summary>
public interface IBaseApiClient
{ {
/// <summary>
/// Base api client
/// </summary>
public interface IBaseApiClient
{
/// <summary> /// <summary>
/// Base address /// Base address
/// </summary> /// </summary>
@@ -44,5 +43,4 @@ namespace CryptoExchange.Net.Interfaces
/// <typeparam name="T">Api credentials type</typeparam> /// <typeparam name="T">Api credentials type</typeparam>
/// <param name="options">Options to set</param> /// <param name="options">Options to set</param>
void SetOptions<T>(UpdateOptions<T> options) where T : ApiCredentials; void SetOptions<T>(UpdateOptions<T> options) where T : ApiCredentials;
}
} }
@@ -1,17 +1,16 @@
using System; using System;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Client for accessing REST API's for different exchanges
/// </summary>
public interface ICryptoRestClient
{ {
/// <summary>
/// Client for accessing REST API's for different exchanges
/// </summary>
public interface ICryptoRestClient
{
/// <summary> /// <summary>
/// Try get /// Try get
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <returns></returns> /// <returns></returns>
T TryGet<T>(Func<T> createFunc); T TryGet<T>(Func<T> createFunc);
}
} }
@@ -1,17 +1,16 @@
using System; using System;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Client for accessing Websocket API's for different exchanges
/// </summary>
public interface ICryptoSocketClient
{ {
/// <summary>
/// Client for accessing Websocket API's for different exchanges
/// </summary>
public interface ICryptoSocketClient
{
/// <summary> /// <summary>
/// Try get a client by type for the service collection /// Try get a client by type for the service collection
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <returns></returns> /// <returns></returns>
T TryGet<T>(Func<T> createFunc); T TryGet<T>(Func<T> createFunc);
}
} }
@@ -1,18 +1,19 @@
using CryptoExchange.Net.Converters.MessageParsing; using CryptoExchange.Net.Converters.MessageParsing;
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
using System; using System;
using System.Collections.Generic; #if NET5_0_OR_GREATER
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
#endif
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Message accessor
/// </summary>
public interface IMessageAccessor
{ {
/// <summary>
/// Message accessor
/// </summary>
public interface IMessageAccessor
{
/// <summary> /// <summary>
/// Is this a valid message /// Is this a valid message
/// </summary> /// </summary>
@@ -81,30 +82,29 @@ namespace CryptoExchange.Net.Interfaces
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
string GetOriginalString(); string GetOriginalString();
} }
/// <summary> /// <summary>
/// Stream message accessor /// Stream message accessor
/// </summary> /// </summary>
public interface IStreamMessageAccessor : IMessageAccessor public interface IStreamMessageAccessor : IMessageAccessor
{ {
/// <summary> /// <summary>
/// Load a stream message /// Load a stream message
/// </summary> /// </summary>
/// <param name="stream"></param> /// <param name="stream"></param>
/// <param name="bufferStream"></param> /// <param name="bufferStream"></param>
Task<CallResult> Read(Stream stream, bool bufferStream); Task<CallResult> Read(Stream stream, bool bufferStream);
} }
/// <summary> /// <summary>
/// Byte message accessor /// Byte message accessor
/// </summary> /// </summary>
public interface IByteMessageAccessor : IMessageAccessor public interface IByteMessageAccessor : IMessageAccessor
{ {
/// <summary> /// <summary>
/// Load a data message /// Load a data message
/// </summary> /// </summary>
/// <param name="data"></param> /// <param name="data"></param>
CallResult Read(ReadOnlyMemory<byte> data); CallResult Read(ReadOnlyMemory<byte> data);
}
} }
@@ -1,17 +1,16 @@
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.Objects.Sockets;
using CryptoExchange.Net.Sockets; using CryptoExchange.Net.Sockets;
using System; using System;
using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Message processor
/// </summary>
public interface IMessageProcessor
{ {
/// <summary>
/// Message processor
/// </summary>
public interface IMessageProcessor
{
/// <summary> /// <summary>
/// Id of the processor /// Id of the processor
/// </summary> /// </summary>
@@ -31,5 +30,4 @@ namespace CryptoExchange.Net.Interfaces
/// <param name="type"></param> /// <param name="type"></param>
/// <returns></returns> /// <returns></returns>
CallResult<object> Deserialize(IMessageAccessor accessor, Type type); CallResult<object> Deserialize(IMessageAccessor accessor, Type type);
}
} }
@@ -1,37 +1,34 @@
using System.Diagnostics.CodeAnalysis; namespace CryptoExchange.Net.Interfaces;
namespace CryptoExchange.Net.Interfaces /// <summary>
/// Serializer interface
/// </summary>
public interface IMessageSerializer
{ {
/// <summary> }
/// Serializer interface
/// </summary>
public interface IMessageSerializer
{
}
/// <summary> /// <summary>
/// Serialize to byte array /// Serialize to byte array
/// </summary> /// </summary>
public interface IByteMessageSerializer: IMessageSerializer public interface IByteMessageSerializer: IMessageSerializer
{ {
/// <summary> /// <summary>
/// Serialize an object to a string /// Serialize an object to a string
/// </summary> /// </summary>
/// <param name="message"></param> /// <param name="message"></param>
/// <returns></returns> /// <returns></returns>
byte[] Serialize<T>(T message); byte[] Serialize<T>(T message);
} }
/// <summary> /// <summary>
/// Serialize to string /// Serialize to string
/// </summary> /// </summary>
public interface IStringMessageSerializer: IMessageSerializer public interface IStringMessageSerializer: IMessageSerializer
{ {
/// <summary> /// <summary>
/// Serialize an object to a string /// Serialize an object to a string
/// </summary> /// </summary>
/// <param name="message"></param> /// <param name="message"></param>
/// <returns></returns> /// <returns></returns>
string Serialize<T>(T message); string Serialize<T>(T message);
}
} }
@@ -1,14 +1,13 @@
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// A provider for a nonce value used when signing requests
/// </summary>
public interface INonceProvider
{ {
/// <summary>
/// A provider for a nonce value used when signing requests
/// </summary>
public interface INonceProvider
{
/// <summary> /// <summary>
/// Get nonce value. Nonce value should be unique and incremental for each call /// Get nonce value. Nonce value should be unique and incremental for each call
/// </summary> /// </summary>
/// <returns>Nonce value</returns> /// <returns>Nonce value</returns>
long GetNonce(); long GetNonce();
}
} }
@@ -1,14 +1,14 @@
using CryptoExchange.Net.Objects.Options; using CryptoExchange.Net.Objects.Options;
using CryptoExchange.Net.SharedApis; using CryptoExchange.Net.SharedApis;
using System; using System;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Factory for ISymbolOrderBook instances
/// </summary>
public interface IOrderBookFactory<TOptions> where TOptions : OrderBookOptions
{ {
/// <summary>
/// Factory for ISymbolOrderBook instances
/// </summary>
public interface IOrderBookFactory<TOptions> where TOptions : OrderBookOptions
{
/// <summary> /// <summary>
/// Create a new order book by symbol name /// Create a new order book by symbol name
/// </summary> /// </summary>
@@ -31,5 +31,4 @@ namespace CryptoExchange.Net.Interfaces
/// <param name="options">Options for the order book</param> /// <param name="options">Options for the order book</param>
/// <returns></returns> /// <returns></returns>
public ISymbolOrderBook Create(SharedSymbol symbol, Action<TOptions>? options = null); public ISymbolOrderBook Create(SharedSymbol symbol, Action<TOptions>? options = null);
}
} }
@@ -4,13 +4,13 @@ using System.Net.Http;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Rate limiter interface
/// </summary>
public interface IRateLimiter
{ {
/// <summary>
/// Rate limiter interface
/// </summary>
public interface IRateLimiter
{
/// <summary> /// <summary>
/// Limit a request based on previous requests made /// Limit a request based on previous requests made
/// </summary> /// </summary>
@@ -24,5 +24,4 @@ namespace CryptoExchange.Net.Interfaces
/// <param name="ct">Cancellation token to cancel waiting</param> /// <param name="ct">Cancellation token to cancel waiting</param>
/// <returns>The time in milliseconds spend waiting</returns> /// <returns>The time in milliseconds spend waiting</returns>
Task<CallResult<int>> LimitRequestAsync(ILogger log, string endpoint, HttpMethod method, bool signed, string? apiKey, RateLimitingBehaviour limitBehaviour, int requestWeight, CancellationToken ct); Task<CallResult<int>> LimitRequestAsync(ILogger log, string endpoint, HttpMethod method, bool signed, string? apiKey, RateLimitingBehaviour limitBehaviour, int requestWeight, CancellationToken ct);
}
} }
+7 -12
View File
@@ -1,16 +1,16 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Request interface
/// </summary>
public interface IRequest
{ {
/// <summary>
/// Request interface
/// </summary>
public interface IRequest
{
/// <summary> /// <summary>
/// Accept header /// Accept header
/// </summary> /// </summary>
@@ -28,10 +28,6 @@ namespace CryptoExchange.Net.Interfaces
/// </summary> /// </summary>
Uri Uri { get; } Uri Uri { get; }
/// <summary> /// <summary>
/// HTTP protocol version
/// </summary>
Version HttpVersion { get; }
/// <summary>
/// internal request id for tracing /// internal request id for tracing
/// </summary> /// </summary>
int RequestId { get; } int RequestId { get; }
@@ -66,5 +62,4 @@ namespace CryptoExchange.Net.Interfaces
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
Task<IResponse> GetResponseAsync(CancellationToken cancellationToken); Task<IResponse> GetResponseAsync(CancellationToken cancellationToken);
}
} }
@@ -1,33 +1,35 @@
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Options;
using System; using System;
using System.Net.Http; using System.Net.Http;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Request factory interface
/// </summary>
public interface IRequestFactory
{ {
/// <summary>
/// Request factory interface
/// </summary>
public interface IRequestFactory
{
/// <summary> /// <summary>
/// Create a request for an uri /// Create a request for an uri
/// </summary> /// </summary>
IRequest Create(Version httpRequestVersion, HttpMethod method, Uri uri, int requestId); /// <param name="method"></param>
/// <param name="uri"></param>
/// <param name="requestId"></param>
/// <returns></returns>
IRequest Create(HttpMethod method, Uri uri, int requestId);
/// <summary> /// <summary>
/// Configure the requests created by this factory /// Configure the requests created by this factory
/// </summary> /// </summary>
/// <param name="options">Rest client options</param> /// <param name="requestTimeout">Request timeout to use</param>
/// <param name="httpClient">Optional shared http client instance</param> /// <param name="httpClient">Optional shared http client instance</param>
void Configure(RestExchangeOptions options, HttpClient? httpClient = null); /// <param name="proxy">Optional proxy to use when no http client is provided</param>
void Configure(ApiProxy? proxy, TimeSpan requestTimeout, HttpClient? httpClient = null);
/// <summary> /// <summary>
/// Update settings /// Update settings
/// </summary> /// </summary>
/// <param name="proxy">Proxy to use</param> /// <param name="proxy">Proxy to use</param>
/// <param name="requestTimeout">Request timeout to use</param> /// <param name="requestTimeout">Request timeout to use</param>
/// <param name="httpKeepAliveInterval">Http client keep alive interval</param> void UpdateSettings(ApiProxy? proxy, TimeSpan requestTimeout);
void UpdateSettings(ApiProxy? proxy, TimeSpan requestTimeout, TimeSpan? httpKeepAliveInterval);
}
} }
+6 -13
View File
@@ -1,26 +1,20 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Response object interface
/// </summary>
public interface IResponse
{ {
/// <summary>
/// Response object interface
/// </summary>
public interface IResponse
{
/// <summary> /// <summary>
/// The response status code /// The response status code
/// </summary> /// </summary>
HttpStatusCode StatusCode { get; } HttpStatusCode StatusCode { get; }
/// <summary>
/// Http protocol version
/// </summary>
Version HttpVersion { get; }
/// <summary> /// <summary>
/// Whether the status code indicates a success status /// Whether the status code indicates a success status
/// </summary> /// </summary>
@@ -46,5 +40,4 @@ namespace CryptoExchange.Net.Interfaces
/// Close the response /// Close the response
/// </summary> /// </summary>
void Close(); void Close();
}
} }
@@ -1,10 +1,10 @@
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Base rest API client
/// </summary>
public interface IRestApiClient : IBaseApiClient
{ {
/// <summary>
/// Base rest API client
/// </summary>
public interface IRestApiClient : IBaseApiClient
{
/// <summary> /// <summary>
/// The factory for creating requests. Used for unit testing /// The factory for creating requests. Used for unit testing
/// </summary> /// </summary>
@@ -14,5 +14,4 @@
/// Total amount of requests made with this API client /// Total amount of requests made with this API client
/// </summary> /// </summary>
int TotalRequestsMade { get; set; } int TotalRequestsMade { get; set; }
}
} }
+7 -8
View File
@@ -1,13 +1,13 @@
using System; using System;
using CryptoExchange.Net.Objects.Options; using CryptoExchange.Net.Objects.Options;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Base class for rest API implementations
/// </summary>
public interface IRestClient: IDisposable
{ {
/// <summary>
/// Base class for rest API implementations
/// </summary>
public interface IRestClient: IDisposable
{
/// <summary> /// <summary>
/// The options provided for this client /// The options provided for this client
/// </summary> /// </summary>
@@ -22,5 +22,4 @@ namespace CryptoExchange.Net.Interfaces
/// The exchange name /// The exchange name
/// </summary> /// </summary>
string Exchange { get; } string Exchange { get; }
}
} }
@@ -1,15 +1,15 @@
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Options; using CryptoExchange.Net.Objects.Options;
using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.Objects.Sockets;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Socket API client
/// </summary>
public interface ISocketApiClient: IBaseApiClient
{ {
/// <summary>
/// Socket API client
/// </summary>
public interface ISocketApiClient: IBaseApiClient
{
/// <summary> /// <summary>
/// The current amount of socket connections on the API client /// The current amount of socket connections on the API client
/// </summary> /// </summary>
@@ -66,5 +66,4 @@ namespace CryptoExchange.Net.Interfaces
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
Task<CallResult> PrepareConnectionsAsync(); Task<CallResult> PrepareConnectionsAsync();
}
} }
@@ -1,15 +1,15 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using CryptoExchange.Net.Objects.Options; using CryptoExchange.Net.Objects.Options;
using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.Objects.Sockets;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Base class for socket API implementations
/// </summary>
public interface ISocketClient: IDisposable
{ {
/// <summary>
/// Base class for socket API implementations
/// </summary>
public interface ISocketClient: IDisposable
{
/// <summary> /// <summary>
/// The exchange name /// The exchange name
/// </summary> /// </summary>
@@ -54,5 +54,4 @@ namespace CryptoExchange.Net.Interfaces
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
Task UnsubscribeAllAsync(); Task UnsubscribeAllAsync();
}
} }
@@ -1,16 +1,15 @@
using System; using System;
using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Interface for order book
/// </summary>
public interface ISymbolOrderBook
{ {
/// <summary>
/// Interface for order book
/// </summary>
public interface ISymbolOrderBook
{
/// <summary> /// <summary>
/// The exchange the book is for /// The exchange the book is for
/// </summary> /// </summary>
@@ -127,5 +126,4 @@ namespace CryptoExchange.Net.Interfaces
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
string ToString(int rows); string ToString(int rows);
}
} }
@@ -1,10 +1,10 @@
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Interface for order book entries
/// </summary>
public interface ISymbolOrderBookEntry
{ {
/// <summary>
/// Interface for order book entries
/// </summary>
public interface ISymbolOrderBookEntry
{
/// <summary> /// <summary>
/// The quantity of the entry /// The quantity of the entry
/// </summary> /// </summary>
@@ -13,16 +13,15 @@
/// The price of the entry /// The price of the entry
/// </summary> /// </summary>
decimal Price { get; set; } decimal Price { get; set; }
} }
/// <summary> /// <summary>
/// Interface for order book entries /// Interface for order book entries
/// </summary> /// </summary>
public interface ISymbolOrderSequencedBookEntry: ISymbolOrderBookEntry public interface ISymbolOrderSequencedBookEntry: ISymbolOrderBookEntry
{ {
/// <summary> /// <summary>
/// Sequence of the update /// Sequence of the update
/// </summary> /// </summary>
long Sequence { get; set; } long Sequence { get; set; }
}
} }
@@ -1,45 +0,0 @@
using CryptoExchange.Net.SharedApis;
using CryptoExchange.Net.Trackers.Klines;
using CryptoExchange.Net.Trackers.Trades;
using System;
namespace CryptoExchange.Net.Interfaces
{
/// <summary>
/// Tracker factory
/// </summary>
public interface ITrackerFactory
{
/// <summary>
/// Whether the factory supports creating a KlineTracker instance for this symbol and interval
/// </summary>
/// <param name="symbol">The symbol</param>
/// <param name="interval">The kline interval</param>
bool CanCreateKlineTracker(SharedSymbol symbol, SharedKlineInterval interval);
/// <summary>
/// Create a new kline tracker
/// </summary>
/// <param name="symbol">The symbol</param>
/// <param name="interval">Kline interval</param>
/// <param name="limit">The max amount of klines to retain</param>
/// <param name="period">The max period the data should be retained</param>
/// <returns></returns>
IKlineTracker CreateKlineTracker(SharedSymbol symbol, SharedKlineInterval interval, int? limit = null, TimeSpan? period = null);
/// <summary>
/// Whether the factory supports creating a TradeTracker instance for this symbol
/// </summary>
/// <param name="symbol">The symbol</param>
bool CanCreateTradeTracker(SharedSymbol symbol);
/// <summary>
/// Create a new trade tracker for a symbol
/// </summary>
/// <param name="symbol">The symbol</param>
/// <param name="limit">The max amount of trades to retain</param>
/// <param name="period">The max period the data should be retained</param>
/// <returns></returns>
ITradeTracker CreateTradeTracker(SharedSymbol symbol, int? limit = null, TimeSpan? period = null);
}
}
+7 -8
View File
@@ -1,16 +1,16 @@
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
using System; using System;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Websocket connection interface
/// </summary>
public interface IWebsocket: IDisposable
{ {
/// <summary>
/// Websocket connection interface
/// </summary>
public interface IWebsocket: IDisposable
{
/// <summary> /// <summary>
/// Websocket closed event /// Websocket closed event
/// </summary> /// </summary>
@@ -106,5 +106,4 @@ namespace CryptoExchange.Net.Interfaces
/// Update proxy setting /// Update proxy setting
/// </summary> /// </summary>
void UpdateProxy(ApiProxy? proxy); void UpdateProxy(ApiProxy? proxy);
}
} }
@@ -1,13 +1,13 @@
using CryptoExchange.Net.Objects.Sockets; using CryptoExchange.Net.Objects.Sockets;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace CryptoExchange.Net.Interfaces namespace CryptoExchange.Net.Interfaces;
/// <summary>
/// Websocket factory interface
/// </summary>
public interface IWebsocketFactory
{ {
/// <summary>
/// Websocket factory interface
/// </summary>
public interface IWebsocketFactory
{
/// <summary> /// <summary>
/// Create a websocket for an url /// Create a websocket for an url
/// </summary> /// </summary>
@@ -15,5 +15,4 @@ namespace CryptoExchange.Net.Interfaces
/// <param name="parameters">The parameters to use for the connection</param> /// <param name="parameters">The parameters to use for the connection</param>
/// <returns></returns> /// <returns></returns>
IWebsocket CreateWebsocket(ILogger logger, WebSocketParameters parameters); IWebsocket CreateWebsocket(ILogger logger, WebSocketParameters parameters);
}
} }
+5 -66
View File
@@ -1,17 +1,10 @@
using CryptoExchange.Net.Objects; namespace CryptoExchange.Net;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text;
namespace CryptoExchange.Net /// <summary>
/// Helpers for client libraries
/// </summary>
public static class LibraryHelpers
{ {
/// <summary>
/// Helpers for client libraries
/// </summary>
public static class LibraryHelpers
{
/// <summary> /// <summary>
/// Client order id separator /// Client order id separator
/// </summary> /// </summary>
@@ -46,58 +39,4 @@ namespace CryptoExchange.Net
return clientOrderId; return clientOrderId;
} }
/// <summary>
/// Create a new HttpMessageHandler instance
/// </summary>
public static HttpMessageHandler CreateHttpClientMessageHandler(ApiProxy? proxy, TimeSpan? keepAliveInterval)
{
#if NET5_0_OR_GREATER
var socketHandler = new SocketsHttpHandler();
try
{
if (keepAliveInterval != null && keepAliveInterval != TimeSpan.Zero)
{
socketHandler.KeepAlivePingPolicy = HttpKeepAlivePingPolicy.Always;
socketHandler.KeepAlivePingDelay = keepAliveInterval.Value;
socketHandler.KeepAlivePingTimeout = TimeSpan.FromSeconds(10);
}
socketHandler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
socketHandler.DefaultProxyCredentials = CredentialCache.DefaultCredentials;
}
catch (PlatformNotSupportedException) { }
catch (NotImplementedException) { } // Mono runtime throws NotImplementedException
if (proxy != null)
{
socketHandler.Proxy = new WebProxy
{
Address = new Uri($"{proxy.Host}:{proxy.Port}"),
Credentials = proxy.Password == null ? null : new NetworkCredential(proxy.Login, proxy.Password)
};
}
return socketHandler;
#else
var httpHandler = new HttpClientHandler();
try
{
httpHandler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
httpHandler.DefaultProxyCredentials = CredentialCache.DefaultCredentials;
}
catch (PlatformNotSupportedException) { }
catch (NotImplementedException) { } // Mono runtime throws NotImplementedException
if (proxy != null)
{
httpHandler.Proxy = new WebProxy
{
Address = new Uri($"{proxy.Host}:{proxy.Port}"),
Credentials = proxy.Password == null ? null : new NetworkCredential(proxy.Login, proxy.Password)
};
}
return httpHandler;
#endif
}
}
} }
@@ -1,11 +1,11 @@
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System; using System;
namespace CryptoExchange.Net.Logging.Extensions namespace CryptoExchange.Net.Logging.Extensions;
{
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public static class CryptoExchangeWebSocketClientLoggingExtension public static class CryptoExchangeWebSocketClientLoggingExtension
{ {
private static readonly Action<ILogger, int, Exception?> _connecting; private static readonly Action<ILogger, int, Exception?> _connecting;
private static readonly Action<ILogger, int, string, Exception?> _connectionFailed; private static readonly Action<ILogger, int, string, Exception?> _connectionFailed;
private static readonly Action<ILogger, int, Exception?> _connectingCanceled; private static readonly Action<ILogger, int, Exception?> _connectingCanceled;
@@ -383,5 +383,4 @@ namespace CryptoExchange.Net.Logging.Extensions
{ {
_connectingCanceled(logger, socketId, null); _connectingCanceled(logger, socketId, null);
} }
}
} }
@@ -1,11 +1,11 @@
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System; using System;
namespace CryptoExchange.Net.Logging.Extensions namespace CryptoExchange.Net.Logging.Extensions;
{
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public static class RateLimitGateLoggingExtensions public static class RateLimitGateLoggingExtensions
{ {
private static readonly Action<ILogger, int, string, string, string, Exception?> _rateLimitRequestFailed; private static readonly Action<ILogger, int, string, string, string, Exception?> _rateLimitRequestFailed;
private static readonly Action<ILogger, int, string, string, Exception?> _rateLimitConnectionFailed; private static readonly Action<ILogger, int, string, string, Exception?> _rateLimitConnectionFailed;
private static readonly Action<ILogger, int, string, TimeSpan, string, string, Exception?> _rateLimitDelayingRequest; private static readonly Action<ILogger, int, string, TimeSpan, string, string, Exception?> _rateLimitDelayingRequest;
@@ -75,5 +75,4 @@ namespace CryptoExchange.Net.Logging.Extensions
{ {
_rateLimitAppliedRequest(logger, requestIdId, path, guard, limit, current, null); _rateLimitAppliedRequest(logger, requestIdId, path, guard, limit, current, null);
} }
}
} }
@@ -1,14 +1,14 @@
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System; using System;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
namespace CryptoExchange.Net.Logging.Extensions namespace CryptoExchange.Net.Logging.Extensions;
{
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public static class RestApiClientLoggingExtensions public static class RestApiClientLoggingExtensions
{ {
private static readonly Action<ILogger, int?, int?, long, string?, string?, Exception?> _restApiErrorReceived; private static readonly Action<ILogger, int?, int?, long, string?, string?, Exception?> _restApiErrorReceived;
private static readonly Action<ILogger, int?, int?, long, string?, Exception?> _restApiResponseReceived; private static readonly Action<ILogger, int?, int?, long, string?, Exception?> _restApiResponseReceived;
private static readonly Action<ILogger, int, string, Exception?> _restApiFailedToSyncTime; private static readonly Action<ILogger, int, string, Exception?> _restApiFailedToSyncTime;
@@ -155,5 +155,4 @@ namespace CryptoExchange.Net.Logging.Extensions
{ {
_restApiCancellationRequested(logger, requestId, null); _restApiCancellationRequested(logger, requestId, null);
} }
}
} }
@@ -1,11 +1,11 @@
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System; using System;
namespace CryptoExchange.Net.Logging.Extensions namespace CryptoExchange.Net.Logging.Extensions;
{
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public static class SocketApiClientLoggingExtension public static class SocketApiClientLoggingExtension
{ {
private static readonly Action<ILogger, int, Exception?> _failedToAddSubscriptionRetryOnDifferentConnection; private static readonly Action<ILogger, int, Exception?> _failedToAddSubscriptionRetryOnDifferentConnection;
private static readonly Action<ILogger, int, Exception?> _hasBeenPausedCantSubscribeAtThisMoment; private static readonly Action<ILogger, int, Exception?> _hasBeenPausedCantSubscribeAtThisMoment;
private static readonly Action<ILogger, int, string?, Exception?> _failedToSubscribe; private static readonly Action<ILogger, int, string?, Exception?> _failedToSubscribe;
@@ -23,8 +23,6 @@ namespace CryptoExchange.Net.Logging.Extensions
private static readonly Action<ILogger, int, int, Exception?> _unsubscribingSubscription; private static readonly Action<ILogger, int, int, Exception?> _unsubscribingSubscription;
private static readonly Action<ILogger, int, Exception?> _reconnectingAllConnections; private static readonly Action<ILogger, int, Exception?> _reconnectingAllConnections;
private static readonly Action<ILogger, DateTime, Exception?> _addingRetryAfterGuard; private static readonly Action<ILogger, DateTime, Exception?> _addingRetryAfterGuard;
private static readonly Action<ILogger, Exception?> _timeoutWaitingForReconnectingSocket;
private static readonly Action<ILogger, long, Exception?> _waitedForReconnectingSocket;
static SocketApiClientLoggingExtension() static SocketApiClientLoggingExtension()
{ {
@@ -112,16 +110,6 @@ namespace CryptoExchange.Net.Logging.Extensions
LogLevel.Warning, LogLevel.Warning,
new EventId(3018, "AddRetryAfterGuard"), new EventId(3018, "AddRetryAfterGuard"),
"Adding RetryAfterGuard ({RetryAfter}) because the connection attempt was rate limited"); "Adding RetryAfterGuard ({RetryAfter}) because the connection attempt was rate limited");
_timeoutWaitingForReconnectingSocket = LoggerMessage.Define(
LogLevel.Debug,
new EventId(3019, "TimeoutWaitingForReconnectingSocket"),
"Timeout while waiting for existing socket reconnection, failing request");
_waitedForReconnectingSocket = LoggerMessage.Define<long>(
LogLevel.Trace,
new EventId(3020, "WaitedForReconnectingSocket"),
"Waited for reconnecting socket for {Timespan}ms");
} }
public static void FailedToAddSubscriptionRetryOnDifferentConnection(this ILogger logger, int socketId) public static void FailedToAddSubscriptionRetryOnDifferentConnection(this ILogger logger, int socketId)
@@ -208,14 +196,4 @@ namespace CryptoExchange.Net.Logging.Extensions
{ {
_addingRetryAfterGuard(logger, retryAfter, null); _addingRetryAfterGuard(logger, retryAfter, null);
} }
public static void TimeoutWaitingForReconnectingSocket(this ILogger logger)
{
_timeoutWaitingForReconnectingSocket(logger, null);
}
public static void WaitedForReconnectingSocket(this ILogger logger, long milliseconds)
{
_waitedForReconnectingSocket(logger, milliseconds, null);
}
}
} }
@@ -1,12 +1,12 @@
using System; using System;
using System.Net.WebSockets; using System.Net.WebSockets;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace CryptoExchange.Net.Logging.Extensions namespace CryptoExchange.Net.Logging.Extensions;
{
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public static class SocketConnectionLoggingExtension public static class SocketConnectionLoggingExtension
{ {
private static readonly Action<ILogger, int, bool, Exception?> _activityPaused; private static readonly Action<ILogger, int, bool, Exception?> _activityPaused;
private static readonly Action<ILogger, int, Sockets.SocketConnection.SocketStatus, Sockets.SocketConnection.SocketStatus, Exception?> _socketStatusChanged; private static readonly Action<ILogger, int, Sockets.SocketConnection.SocketStatus, Sockets.SocketConnection.SocketStatus, Exception?> _socketStatusChanged;
private static readonly Action<ILogger, int, string?, Exception?> _failedReconnectProcessing; private static readonly Action<ILogger, int, string?, Exception?> _failedReconnectProcessing;
@@ -345,5 +345,4 @@ namespace CryptoExchange.Net.Logging.Extensions
{ {
_sendingByteData(logger, socketId, requestId, length, null); _sendingByteData(logger, socketId, requestId, length, null);
} }
}
} }
@@ -1,13 +1,13 @@
using System; using System;
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace CryptoExchange.Net.Logging.Extensions namespace CryptoExchange.Net.Logging.Extensions;
{
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public static class SymbolOrderBookLoggingExtensions public static class SymbolOrderBookLoggingExtensions
{ {
private static readonly Action<ILogger, string, string, OrderBookStatus, OrderBookStatus, Exception?> _orderBookStatusChanged; private static readonly Action<ILogger, string, string, OrderBookStatus, OrderBookStatus, Exception?> _orderBookStatusChanged;
private static readonly Action<ILogger, string, string, Exception?> _orderBookStarting; private static readonly Action<ILogger, string, string, Exception?> _orderBookStarting;
private static readonly Action<ILogger, string, string, Exception?> _orderBookStoppedStarting; private static readonly Action<ILogger, string, string, Exception?> _orderBookStoppedStarting;
@@ -233,5 +233,4 @@ namespace CryptoExchange.Net.Logging.Extensions
{ {
_orderBookOutOfSyncChecksum(logger, api, symbol, null); _orderBookOutOfSyncChecksum(logger, api, symbol, null);
} }
}
} }
@@ -1,13 +1,13 @@
using System; using System;
using CryptoExchange.Net.Objects; using CryptoExchange.Net.Objects;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace CryptoExchange.Net.Logging.Extensions namespace CryptoExchange.Net.Logging.Extensions;
{
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public static class TrackerLoggingExtensions public static class TrackerLoggingExtensions
{ {
private static readonly Action<ILogger, string, SyncStatus, SyncStatus, Exception?> _klineTrackerStatusChanged; private static readonly Action<ILogger, string, SyncStatus, SyncStatus, Exception?> _klineTrackerStatusChanged;
private static readonly Action<ILogger, string, Exception?> _klineTrackerStarting; private static readonly Action<ILogger, string, Exception?> _klineTrackerStarting;
private static readonly Action<ILogger, string, string, Exception?> _klineTrackerStartFailed; private static readonly Action<ILogger, string, string, Exception?> _klineTrackerStartFailed;
@@ -287,5 +287,4 @@ namespace CryptoExchange.Net.Logging.Extensions
{ {
_tradeTrackerConnectionRestored(logger, symbol, null); _tradeTrackerConnectionRestored(logger, symbol, null);
} }
}
} }
+6 -7
View File
@@ -1,10 +1,10 @@
namespace CryptoExchange.Net.Objects namespace CryptoExchange.Net.Objects;
/// <summary>
/// Proxy info
/// </summary>
public class ApiProxy
{ {
/// <summary>
/// Proxy info
/// </summary>
public class ApiProxy
{
/// <summary> /// <summary>
/// The host address of the proxy /// The host address of the proxy
/// </summary> /// </summary>
@@ -38,5 +38,4 @@
Login = login; Login = login;
Password = password; Password = password;
} }
}
} }
+5 -10
View File
@@ -1,14 +1,10 @@
using System; namespace CryptoExchange.Net.Objects;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.Objects /// <summary>
/// An alias used by the exchange for an asset commonly known by another name
/// </summary>
public class AssetAlias
{ {
/// <summary>
/// An alias used by the exchange for an asset commonly known by another name
/// </summary>
public class AssetAlias
{
/// <summary> /// <summary>
/// The name of the asset on the exchange /// The name of the asset on the exchange
/// </summary> /// </summary>
@@ -26,5 +22,4 @@ namespace CryptoExchange.Net.Objects
ExchangeAssetName = exchangeName; ExchangeAssetName = exchangeName;
CommonAssetName = commonName; CommonAssetName = commonName;
} }
}
} }
@@ -1,15 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
namespace CryptoExchange.Net.Objects namespace CryptoExchange.Net.Objects;
/// <summary>
/// Exchange configuration for asset aliases
/// </summary>
public class AssetAliasConfiguration
{ {
/// <summary>
/// Exchange configuration for asset aliases
/// </summary>
public class AssetAliasConfiguration
{
/// <summary> /// <summary>
/// Defined aliases /// Defined aliases
/// </summary> /// </summary>
@@ -30,5 +27,4 @@ namespace CryptoExchange.Net.Objects
/// </summary> /// </summary>
public string ExchangeToCommonName(string exchangeName) => !AutoConvertEnabled ? exchangeName : Aliases.SingleOrDefault(x => x.ExchangeAssetName == exchangeName)?.CommonAssetName ?? exchangeName; public string ExchangeToCommonName(string exchangeName) => !AutoConvertEnabled ? exchangeName : Aliases.SingleOrDefault(x => x.ExchangeAssetName == exchangeName)?.CommonAssetName ?? exchangeName;
}
} }
@@ -1,17 +1,17 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CryptoExchange.Net.Objects namespace CryptoExchange.Net.Objects;
/// <summary>
/// Async auto reset based on Stephen Toub`s implementation
/// https://devblogs.microsoft.com/pfxteam/building-async-coordination-primitives-part-2-asyncautoresetevent/
/// </summary>
public class AsyncResetEvent : IDisposable
{ {
/// <summary>
/// Async auto reset based on Stephen Toub`s implementation
/// https://devblogs.microsoft.com/pfxteam/building-async-coordination-primitives-part-2-asyncautoresetevent/
/// </summary>
public class AsyncResetEvent : IDisposable
{
private static readonly Task<bool> _completed = Task.FromResult(true); private static readonly Task<bool> _completed = Task.FromResult(true);
private Queue<TaskCompletionSource<bool>> _waits = new Queue<TaskCompletionSource<bool>>(); private Queue<TaskCompletionSource<bool>> _waits = new Queue<TaskCompletionSource<bool>>();
private bool _signaled; private bool _signaled;
@@ -106,15 +106,28 @@ namespace CryptoExchange.Net.Objects
toRelease.TrySetResult(true); toRelease.TrySetResult(true);
} }
else if (!_signaled) else if (!_signaled)
{
_signaled = true; _signaled = true;
} }
} }
} }
}
/// <summary> /// <summary>
/// Dispose /// Dispose
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Dispose
/// </summary>
public void Dispose(bool disposing)
{
if (disposing)
{ {
_waits.Clear(); _waits.Clear();
} }
@@ -1,10 +1,9 @@
using CryptoExchange.Net.Interfaces; using CryptoExchange.Net.Interfaces;
using System; using System;
namespace CryptoExchange.Net.Objects namespace CryptoExchange.Net.Objects;
internal class AuthTimeProvider : IAuthTimeProvider
{ {
internal class AuthTimeProvider : IAuthTimeProvider
{
public DateTime GetTime() => DateTime.UtcNow; public DateTime GetTime() => DateTime.UtcNow;
}
} }
@@ -1,13 +1,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace CryptoExchange.Net.Objects namespace CryptoExchange.Net.Objects;
/// <summary>
/// Comparer for byte order
/// </summary>
public class ByteOrderComparer : IComparer<byte[]>
{ {
/// <summary>
/// Comparer for byte order
/// </summary>
public class ByteOrderComparer : IComparer<byte[]>
{
/// <summary> /// <summary>
/// Compare function /// Compare function
/// </summary> /// </summary>
@@ -54,5 +54,4 @@ namespace CryptoExchange.Net.Objects
// Compare lengths. // Compare lengths.
return x.Length < y.Length ? -1 : 1; return x.Length < y.Length ? -1 : 1;
} }
}
} }
+84 -86
View File
@@ -1,4 +1,4 @@
using CryptoExchange.Net.SharedApis; using CryptoExchange.Net.SharedApis;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
@@ -6,13 +6,13 @@ using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Text; using System.Text;
namespace CryptoExchange.Net.Objects namespace CryptoExchange.Net.Objects;
/// <summary>
/// The result of an operation
/// </summary>
public class CallResult
{ {
/// <summary>
/// The result of an operation
/// </summary>
public class CallResult
{
/// <summary> /// <summary>
/// Static success result /// Static success result
/// </summary> /// </summary>
@@ -51,14 +51,14 @@ namespace CryptoExchange.Net.Objects
{ {
return Success ? $"Success" : $"Error: {Error}"; return Success ? $"Success" : $"Error: {Error}";
} }
} }
/// <summary> /// <summary>
/// The result of an operation /// The result of an operation
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
public class CallResult<T>: CallResult public class CallResult<T>: CallResult
{ {
/// <summary> /// <summary>
/// The data returned by the call, only available when Success = true /// The data returned by the call, only available when Success = true
/// </summary> /// </summary>
@@ -140,12 +140,12 @@ namespace CryptoExchange.Net.Objects
/// <summary> /// <summary>
/// Copy the WebCallResult to a new data type /// Copy the WebCallResult to a new data type
/// </summary> /// </summary>
/// <typeparam name="K">The new type</typeparam> /// <typeparam name="TNew">The new type</typeparam>
/// <param name="data">The data of the new type</param> /// <param name="data">The data of the new type</param>
/// <returns></returns> /// <returns></returns>
public CallResult<K> As<K>([AllowNull] K data) public CallResult<TNew> As<TNew>([AllowNull] TNew data)
{ {
return new CallResult<K>(data, OriginalData, Error); return new CallResult<TNew>(data, OriginalData, Error);
} }
/// <summary> /// <summary>
@@ -169,24 +169,24 @@ namespace CryptoExchange.Net.Objects
/// <summary> /// <summary>
/// Copy the CallResult to a new data type /// Copy the CallResult to a new data type
/// </summary> /// </summary>
/// <typeparam name="K">The new type</typeparam> /// <typeparam name="TNew">The new type</typeparam>
/// <param name="data">The data</param> /// <param name="data">The data</param>
/// <param name="error">The error returned</param> /// <param name="error">The error returned</param>
/// <returns></returns> /// <returns></returns>
public CallResult<K> AsErrorWithData<K>(Error error, K data) public CallResult<TNew> AsErrorWithData<TNew>(Error error, TNew data)
{ {
return new CallResult<K>(data, OriginalData, error); return new CallResult<TNew>(data, OriginalData, error);
} }
/// <summary> /// <summary>
/// Copy the WebCallResult to a new data type /// Copy the WebCallResult to a new data type
/// </summary> /// </summary>
/// <typeparam name="K">The new type</typeparam> /// <typeparam name="TNew">The new type</typeparam>
/// <param name="error">The error to return</param> /// <param name="error">The error to return</param>
/// <returns></returns> /// <returns></returns>
public CallResult<K> AsError<K>(Error error) public CallResult<TNew> AsError<TNew>(Error error)
{ {
return new CallResult<K>(default, OriginalData, error); return new CallResult<TNew>(default, OriginalData, error);
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -194,23 +194,18 @@ namespace CryptoExchange.Net.Objects
{ {
return Success ? $"Success" : $"Error: {Error}"; return Success ? $"Success" : $"Error: {Error}";
} }
} }
/// <summary> /// <summary>
/// The result of a request /// The result of a request
/// </summary> /// </summary>
public class WebCallResult : CallResult public class WebCallResult : CallResult
{ {
/// <summary> /// <summary>
/// The request http method /// The request http method
/// </summary> /// </summary>
public HttpMethod? RequestMethod { get; set; } public HttpMethod? RequestMethod { get; set; }
/// <summary>
/// HTTP protocol version
/// </summary>
public Version? HttpVersion { get; set; }
/// <summary> /// <summary>
/// The headers sent with the request /// The headers sent with the request
/// </summary> /// </summary>
@@ -256,7 +251,6 @@ namespace CryptoExchange.Net.Objects
/// </summary> /// </summary>
public WebCallResult( public WebCallResult(
HttpStatusCode? code, HttpStatusCode? code,
Version? httpVersion,
KeyValuePair<string, string[]>[]? responseHeaders, KeyValuePair<string, string[]>[]? responseHeaders,
TimeSpan? responseTime, TimeSpan? responseTime,
string? originalData, string? originalData,
@@ -268,7 +262,6 @@ namespace CryptoExchange.Net.Objects
Error? error) : base(error) Error? error) : base(error)
{ {
ResponseStatusCode = code; ResponseStatusCode = code;
HttpVersion = httpVersion;
ResponseHeaders = responseHeaders; ResponseHeaders = responseHeaders;
ResponseTime = responseTime; ResponseTime = responseTime;
RequestId = requestId; RequestId = requestId;
@@ -293,55 +286,55 @@ namespace CryptoExchange.Net.Objects
/// <returns></returns> /// <returns></returns>
public WebCallResult AsError(Error error) public WebCallResult AsError(Error error)
{ {
return new WebCallResult(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, error); return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, error);
} }
/// <summary> /// <summary>
/// Copy the WebCallResult to a new data type /// Copy the WebCallResult to a new data type
/// </summary> /// </summary>
/// <typeparam name="K">The new type</typeparam> /// <typeparam name="TNew">The new type</typeparam>
/// <param name="data">The data of the new type</param> /// <param name="data">The data of the new type</param>
/// <returns></returns> /// <returns></returns>
public WebCallResult<K> As<K>([AllowNull] K data) public WebCallResult<TNew> As<TNew>([AllowNull] TNew data)
{ {
return new WebCallResult<K>(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, 0, null, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, ResultDataSource.Server, data, Error); return new WebCallResult<TNew>(ResponseStatusCode, ResponseHeaders, ResponseTime, 0, null, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, ResultDataSource.Server, data, Error);
} }
/// <summary> /// <summary>
/// Copy the WebCallResult to an ExchangeWebResult of a new data type /// Copy the WebCallResult to an ExchangeWebResult of a new data type
/// </summary> /// </summary>
/// <typeparam name="K">The new type</typeparam> /// <typeparam name="TNew">The new type</typeparam>
/// <param name="exchange">The exchange</param> /// <param name="exchange">The exchange</param>
/// <param name="tradeMode">Trade mode the result applies to</param> /// <param name="tradeMode">Trade mode the result applies to</param>
/// <param name="data">The data</param> /// <param name="data">The data</param>
/// <returns></returns> /// <returns></returns>
public ExchangeWebResult<K> AsExchangeResult<K>(string exchange, TradingMode tradeMode, [AllowNull] K data) public ExchangeWebResult<TNew> AsExchangeResult<TNew>(string exchange, TradingMode tradeMode, [AllowNull] TNew data)
{ {
return new ExchangeWebResult<K>(exchange, tradeMode, this.As<K>(data)); return new ExchangeWebResult<TNew>(exchange, tradeMode, this.As<TNew>(data));
} }
/// <summary> /// <summary>
/// Copy the WebCallResult to an ExchangeWebResult of a new data type /// Copy the WebCallResult to an ExchangeWebResult of a new data type
/// </summary> /// </summary>
/// <typeparam name="K">The new type</typeparam> /// <typeparam name="TNew">The new type</typeparam>
/// <param name="exchange">The exchange</param> /// <param name="exchange">The exchange</param>
/// <param name="tradeModes">Trade modes the result applies to</param> /// <param name="tradeModes">Trade modes the result applies to</param>
/// <param name="data">The data</param> /// <param name="data">The data</param>
/// <returns></returns> /// <returns></returns>
public ExchangeWebResult<K> AsExchangeResult<K>(string exchange, TradingMode[]? tradeModes, [AllowNull] K data) public ExchangeWebResult<TNew> AsExchangeResult<TNew>(string exchange, TradingMode[]? tradeModes, [AllowNull] TNew data)
{ {
return new ExchangeWebResult<K>(exchange, tradeModes, this.As<K>(data)); return new ExchangeWebResult<TNew>(exchange, tradeModes, this.As<TNew>(data));
} }
/// <summary> /// <summary>
/// Copy the WebCallResult to a new data type /// Copy the WebCallResult to a new data type
/// </summary> /// </summary>
/// <typeparam name="K">The new type</typeparam> /// <typeparam name="TNew">The new type</typeparam>
/// <param name="error">The error returned</param> /// <param name="error">The error returned</param>
/// <returns></returns> /// <returns></returns>
public WebCallResult<K> AsError<K>(Error error) public WebCallResult<TNew> AsError<TNew>(Error error)
{ {
return new WebCallResult<K>(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, 0, null, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, ResultDataSource.Server, default, error); return new WebCallResult<TNew>(ResponseStatusCode, ResponseHeaders, ResponseTime, 0, null, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, ResultDataSource.Server, default, error);
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -349,24 +342,19 @@ namespace CryptoExchange.Net.Objects
{ {
return (Success ? $"Success" : $"Error: {Error}") + $" in {ResponseTime}"; return (Success ? $"Success" : $"Error: {Error}") + $" in {ResponseTime}";
} }
} }
/// <summary> /// <summary>
/// The result of a request /// The result of a request
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
public class WebCallResult<T>: CallResult<T> public class WebCallResult<T>: CallResult<T>
{ {
/// <summary> /// <summary>
/// The request http method /// The request http method
/// </summary> /// </summary>
public HttpMethod? RequestMethod { get; set; } public HttpMethod? RequestMethod { get; set; }
/// <summary>
/// HTTP protocol version
/// </summary>
public Version? HttpVersion { get; set; }
/// <summary> /// <summary>
/// The headers sent with the request /// The headers sent with the request
/// </summary> /// </summary>
@@ -415,9 +403,21 @@ namespace CryptoExchange.Net.Objects
/// <summary> /// <summary>
/// Create a new result /// Create a new result
/// </summary> /// </summary>
/// <param name="code"></param>
/// <param name="responseHeaders"></param>
/// <param name="responseTime"></param>
/// <param name="responseLength"></param>
/// <param name="originalData"></param>
/// <param name="requestId"></param>
/// <param name="requestUrl"></param>
/// <param name="requestBody"></param>
/// <param name="requestMethod"></param>
/// <param name="requestHeaders"></param>
/// <param name="dataSource"></param>
/// <param name="data"></param>
/// <param name="error"></param>
public WebCallResult( public WebCallResult(
HttpStatusCode? code, HttpStatusCode? code,
Version? httpVersion,
KeyValuePair<string, string[]>[]? responseHeaders, KeyValuePair<string, string[]>[]? responseHeaders,
TimeSpan? responseTime, TimeSpan? responseTime,
long? responseLength, long? responseLength,
@@ -431,7 +431,6 @@ namespace CryptoExchange.Net.Objects
[AllowNull] T data, [AllowNull] T data,
Error? error) : base(data, originalData, error) Error? error) : base(data, originalData, error)
{ {
HttpVersion = httpVersion;
ResponseStatusCode = code; ResponseStatusCode = code;
ResponseHeaders = responseHeaders; ResponseHeaders = responseHeaders;
ResponseTime = responseTime; ResponseTime = responseTime;
@@ -451,7 +450,7 @@ namespace CryptoExchange.Net.Objects
/// <returns></returns> /// <returns></returns>
public new WebCallResult AsDataless() public new WebCallResult AsDataless()
{ {
return new WebCallResult(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, Error); return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, Error);
} }
/// <summary> /// <summary>
/// Copy as a dataless result /// Copy as a dataless result
@@ -459,47 +458,47 @@ namespace CryptoExchange.Net.Objects
/// <returns></returns> /// <returns></returns>
public new WebCallResult AsDatalessError(Error error) public new WebCallResult AsDatalessError(Error error)
{ {
return new WebCallResult(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, error); return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, error);
} }
/// <summary> /// <summary>
/// Create a new error result /// Create a new error result
/// </summary> /// </summary>
/// <param name="error">The error</param> /// <param name="error">The error</param>
public WebCallResult(Error? error) : this(null, null, null, null, null, null, null, null, null, null, null, ResultDataSource.Server, default, error) { } public WebCallResult(Error? error) : this(null, null, null, null, null, null, null, null, null, null, ResultDataSource.Server, default, error) { }
/// <summary> /// <summary>
/// Copy the WebCallResult to a new data type /// Copy the WebCallResult to a new data type
/// </summary> /// </summary>
/// <typeparam name="K">The new type</typeparam> /// <typeparam name="TNew">The new type</typeparam>
/// <param name="data">The data of the new type</param> /// <param name="data">The data of the new type</param>
/// <returns></returns> /// <returns></returns>
public new WebCallResult<K> As<K>([AllowNull] K data) public new WebCallResult<TNew> As<TNew>([AllowNull] TNew data)
{ {
return new WebCallResult<K>(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, DataSource, data, Error); return new WebCallResult<TNew>(ResponseStatusCode, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, DataSource, data, Error);
} }
/// <summary> /// <summary>
/// Copy the WebCallResult to a new data type /// Copy the WebCallResult to a new data type
/// </summary> /// </summary>
/// <typeparam name="K">The new type</typeparam> /// <typeparam name="TNew">The new type</typeparam>
/// <param name="error">The error returned</param> /// <param name="error">The error returned</param>
/// <returns></returns> /// <returns></returns>
public new WebCallResult<K> AsError<K>(Error error) public new WebCallResult<TNew> AsError<TNew>(Error error)
{ {
return new WebCallResult<K>(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, DataSource, default, error); return new WebCallResult<TNew>(ResponseStatusCode, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, DataSource, default, error);
} }
/// <summary> /// <summary>
/// Copy the WebCallResult to a new data type /// Copy the WebCallResult to a new data type
/// </summary> /// </summary>
/// <typeparam name="K">The new type</typeparam> /// <typeparam name="TNew">The new type</typeparam>
/// <param name="data">The data</param> /// <param name="data">The data</param>
/// <param name="error">The error returned</param> /// <param name="error">The error returned</param>
/// <returns></returns> /// <returns></returns>
public new WebCallResult<K> AsErrorWithData<K>(Error error, K data) public new WebCallResult<TNew> AsErrorWithData<TNew>(Error error, TNew data)
{ {
return new WebCallResult<K>(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, DataSource, data, error); return new WebCallResult<TNew>(ResponseStatusCode, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, DataSource, data, error);
} }
/// <summary> /// <summary>
@@ -527,41 +526,41 @@ namespace CryptoExchange.Net.Objects
/// <summary> /// <summary>
/// Copy the WebCallResult to an ExchangeWebResult of a new data type /// Copy the WebCallResult to an ExchangeWebResult of a new data type
/// </summary> /// </summary>
/// <typeparam name="K">The new type</typeparam> /// <typeparam name="TNew">The new type</typeparam>
/// <param name="exchange">The exchange</param> /// <param name="exchange">The exchange</param>
/// <param name="tradeMode">Trade mode the result applies to</param> /// <param name="tradeMode">Trade mode the result applies to</param>
/// <param name="data">Data</param> /// <param name="data">Data</param>
/// <param name="nextPageToken">Next page token</param> /// <param name="nextPageToken">Next page token</param>
/// <returns></returns> /// <returns></returns>
public ExchangeWebResult<K> AsExchangeResult<K>(string exchange, TradingMode tradeMode, [AllowNull] K data, INextPageToken? nextPageToken = null) public ExchangeWebResult<TNew> AsExchangeResult<TNew>(string exchange, TradingMode tradeMode, [AllowNull] TNew data, INextPageToken? nextPageToken = null)
{ {
return new ExchangeWebResult<K>(exchange, tradeMode, As<K>(data), nextPageToken); return new ExchangeWebResult<TNew>(exchange, tradeMode, As<TNew>(data), nextPageToken);
} }
/// <summary> /// <summary>
/// Copy the WebCallResult to an ExchangeWebResult of a new data type /// Copy the WebCallResult to an ExchangeWebResult of a new data type
/// </summary> /// </summary>
/// <typeparam name="K">The new type</typeparam> /// <typeparam name="TNew">The new type</typeparam>
/// <param name="exchange">The exchange</param> /// <param name="exchange">The exchange</param>
/// <param name="tradeModes">Trade modes the result applies to</param> /// <param name="tradeModes">Trade modes the result applies to</param>
/// <param name="data">Data</param> /// <param name="data">Data</param>
/// <param name="nextPageToken">Next page token</param> /// <param name="nextPageToken">Next page token</param>
/// <returns></returns> /// <returns></returns>
public ExchangeWebResult<K> AsExchangeResult<K>(string exchange, TradingMode[]? tradeModes, [AllowNull] K data, INextPageToken? nextPageToken = null) public ExchangeWebResult<TNew> AsExchangeResult<TNew>(string exchange, TradingMode[]? tradeModes, [AllowNull] TNew data, INextPageToken? nextPageToken = null)
{ {
return new ExchangeWebResult<K>(exchange, tradeModes, As<K>(data), nextPageToken); return new ExchangeWebResult<TNew>(exchange, tradeModes, As<TNew>(data), nextPageToken);
} }
/// <summary> /// <summary>
/// Copy the WebCallResult to an ExchangeWebResult with a specific error /// Copy the WebCallResult to an ExchangeWebResult with a specific error
/// </summary> /// </summary>
/// <typeparam name="K">The new type</typeparam> /// <typeparam name="TNew">The new type</typeparam>
/// <param name="exchange">The exchange</param> /// <param name="exchange">The exchange</param>
/// <param name="error">The error returned</param> /// <param name="error">The error returned</param>
/// <returns></returns> /// <returns></returns>
public ExchangeWebResult<K> AsExchangeError<K>(string exchange, Error error) public ExchangeWebResult<TNew> AsExchangeError<TNew>(string exchange, Error error)
{ {
return new ExchangeWebResult<K>(exchange, null, AsError<K>(error)); return new ExchangeWebResult<TNew>(exchange, null, AsError<TNew>(error));
} }
/// <summary> /// <summary>
@@ -570,7 +569,7 @@ namespace CryptoExchange.Net.Objects
/// <returns></returns> /// <returns></returns>
internal WebCallResult<T> Cached() internal WebCallResult<T> Cached()
{ {
return new WebCallResult<T>(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, ResultDataSource.Cache, Data, Error); return new WebCallResult<T>(ResponseStatusCode, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, ResultDataSource.Cache, Data, Error);
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -585,5 +584,4 @@ namespace CryptoExchange.Net.Objects
return sb.ToString(); return sb.ToString();
} }
}
} }
+6 -7
View File
@@ -1,10 +1,10 @@
namespace CryptoExchange.Net.Objects namespace CryptoExchange.Net.Objects;
/// <summary>
/// Constants
/// </summary>
public class Constants
{ {
/// <summary>
/// Constants
/// </summary>
public class Constants
{
/// <summary> /// <summary>
/// Json content type header /// Json content type header
/// </summary> /// </summary>
@@ -17,5 +17,4 @@
/// Placeholder key for when request body should be set to the value of this KVP /// Placeholder key for when request body should be set to the value of this KVP
/// </summary> /// </summary>
public const string BodyPlaceHolderKey = "_BODY_"; public const string BodyPlaceHolderKey = "_BODY_";
}
} }
+83 -114
View File
@@ -1,12 +1,10 @@
using CryptoExchange.Net.Attributes; namespace CryptoExchange.Net.Objects;
namespace CryptoExchange.Net.Objects /// <summary>
/// What to do when a request would exceed the rate limit
/// </summary>
public enum RateLimitingBehaviour
{ {
/// <summary>
/// What to do when a request would exceed the rate limit
/// </summary>
public enum RateLimitingBehaviour
{
/// <summary> /// <summary>
/// Fail the request /// Fail the request
/// </summary> /// </summary>
@@ -15,13 +13,13 @@ namespace CryptoExchange.Net.Objects
/// Wait till the request can be send /// Wait till the request can be send
/// </summary> /// </summary>
Wait Wait
} }
/// <summary> /// <summary>
/// What to do when a request would exceed the rate limit /// What to do when a request would exceed the rate limit
/// </summary> /// </summary>
public enum RateLimitWindowType public enum RateLimitWindowType
{ {
/// <summary> /// <summary>
/// A sliding window /// A sliding window
/// </summary> /// </summary>
@@ -38,13 +36,13 @@ namespace CryptoExchange.Net.Objects
/// Decaying window /// Decaying window
/// </summary> /// </summary>
Decay Decay
} }
/// <summary> /// <summary>
/// Where the parameters for a HttpMethod should be added in a request /// Where the parameters for a HttpMethod should be added in a request
/// </summary> /// </summary>
public enum HttpMethodParameterPosition public enum HttpMethodParameterPosition
{ {
/// <summary> /// <summary>
/// Parameters in body /// Parameters in body
/// </summary> /// </summary>
@@ -53,13 +51,13 @@ namespace CryptoExchange.Net.Objects
/// Parameters in url /// Parameters in url
/// </summary> /// </summary>
InUri InUri
} }
/// <summary> /// <summary>
/// The format of the request body /// The format of the request body
/// </summary> /// </summary>
public enum RequestBodyFormat public enum RequestBodyFormat
{ {
/// <summary> /// <summary>
/// Form data /// Form data
/// </summary> /// </summary>
@@ -68,13 +66,13 @@ namespace CryptoExchange.Net.Objects
/// Json /// Json
/// </summary> /// </summary>
Json Json
} }
/// <summary> /// <summary>
/// Tracker sync status /// Tracker sync status
/// </summary> /// </summary>
public enum SyncStatus public enum SyncStatus
{ {
/// <summary> /// <summary>
/// Not connected /// Not connected
/// </summary> /// </summary>
@@ -95,13 +93,13 @@ namespace CryptoExchange.Net.Objects
/// Disposed /// Disposed
/// </summary> /// </summary>
Disposed Disposed
} }
/// <summary> /// <summary>
/// Status of the order book /// Status of the order book
/// </summary> /// </summary>
public enum OrderBookStatus public enum OrderBookStatus
{ {
/// <summary> /// <summary>
/// Not connected /// Not connected
/// </summary> /// </summary>
@@ -130,13 +128,13 @@ namespace CryptoExchange.Net.Objects
/// Disposed /// Disposed
/// </summary> /// </summary>
Disposed Disposed
} }
/// <summary> /// <summary>
/// Order book entry type /// Order book entry type
/// </summary> /// </summary>
public enum OrderBookEntryType public enum OrderBookEntryType
{ {
/// <summary> /// <summary>
/// Ask /// Ask
/// </summary> /// </summary>
@@ -145,14 +143,14 @@ namespace CryptoExchange.Net.Objects
/// Bid /// Bid
/// </summary> /// </summary>
Bid Bid
} }
/// <summary> /// <summary>
/// Define how array parameters should be send /// Define how array parameters should be send
/// </summary> /// </summary>
public enum ArrayParametersSerialization public enum ArrayParametersSerialization
#pragma warning disable CS1570 // XML comment has badly formed XML #pragma warning disable CS1570 // XML comment has badly formed XML
{ {
/// <summary> /// <summary>
/// Send as key=value1&key=value2 /// Send as key=value1&key=value2
/// </summary> /// </summary>
@@ -167,13 +165,13 @@ namespace CryptoExchange.Net.Objects
/// </summary> /// </summary>
JsonArray JsonArray
#pragma warning restore CS1570 // XML comment has badly formed XML #pragma warning restore CS1570 // XML comment has badly formed XML
} }
/// <summary> /// <summary>
/// How to round /// How to round
/// </summary> /// </summary>
public enum RoundingType public enum RoundingType
{ {
/// <summary> /// <summary>
/// Round down (flooring) /// Round down (flooring)
/// </summary> /// </summary>
@@ -186,13 +184,13 @@ namespace CryptoExchange.Net.Objects
/// Round up (ceil) /// Round up (ceil)
/// </summary> /// </summary>
Up Up
} }
/// <summary> /// <summary>
/// Type of the update /// Type of the update
/// </summary> /// </summary>
public enum SocketUpdateType public enum SocketUpdateType
{ {
/// <summary> /// <summary>
/// A update /// A update
/// </summary> /// </summary>
@@ -201,13 +199,13 @@ namespace CryptoExchange.Net.Objects
/// A snapshot, generally send at the start of the connection /// A snapshot, generally send at the start of the connection
/// </summary> /// </summary>
Snapshot Snapshot
} }
/// <summary> /// <summary>
/// Reconnect policy /// Reconnect policy
/// </summary> /// </summary>
public enum ReconnectPolicy public enum ReconnectPolicy
{ {
/// <summary> /// <summary>
/// Reconnect is disabled /// Reconnect is disabled
/// </summary> /// </summary>
@@ -220,13 +218,13 @@ namespace CryptoExchange.Net.Objects
/// Backoff policy of 2^`reconnectAttempt`, where `reconnectAttempt` has a max value of 5 /// Backoff policy of 2^`reconnectAttempt`, where `reconnectAttempt` has a max value of 5
/// </summary> /// </summary>
ExponentialBackoff ExponentialBackoff
} }
/// <summary> /// <summary>
/// The data source of the result /// The data source of the result
/// </summary> /// </summary>
public enum ResultDataSource public enum ResultDataSource
{ {
/// <summary> /// <summary>
/// From server /// From server
/// </summary> /// </summary>
@@ -235,13 +233,13 @@ namespace CryptoExchange.Net.Objects
/// From cache /// From cache
/// </summary> /// </summary>
Cache Cache
} }
/// <summary> /// <summary>
/// Type of exchange /// Type of exchange
/// </summary> /// </summary>
public enum ExchangeType public enum ExchangeType
{ {
/// <summary> /// <summary>
/// Centralized /// Centralized
/// </summary> /// </summary>
@@ -250,13 +248,13 @@ namespace CryptoExchange.Net.Objects
/// Decentralized /// Decentralized
/// </summary> /// </summary>
DEX DEX
} }
/// <summary> /// <summary>
/// Timeout behavior for queries /// Timeout behavior for queries
/// </summary> /// </summary>
public enum TimeoutBehavior public enum TimeoutBehavior
{ {
/// <summary> /// <summary>
/// Fail the request /// Fail the request
/// </summary> /// </summary>
@@ -265,33 +263,4 @@ namespace CryptoExchange.Net.Objects
/// Mark the query as successful /// Mark the query as successful
/// </summary> /// </summary>
Succeed Succeed
}
/// <summary>
/// Subscription status
/// </summary>
public enum SubscriptionStatus
{
/// <summary>
/// Pending, waiting before (re)subscription can be started
/// </summary>
Pending,
/// <summary>
/// Currently (re)subscribing, will start producing updates soon if subscription is successful
/// </summary>
Subscribing,
/// <summary>
/// Subscribed and listening to updates
/// </summary>
Subscribed,
/// <summary>
/// Subscription is being closed and will stop producing updates
/// </summary>
Closing,
/// <summary>
/// Subscription is closed and will no long produce updates
/// </summary>
Closed
}
} }
+102 -104
View File
@@ -1,14 +1,13 @@
using CryptoExchange.Net.Objects.Errors; using CryptoExchange.Net.Objects.Errors;
using System; using System;
namespace CryptoExchange.Net.Objects namespace CryptoExchange.Net.Objects;
{
/// <summary>
/// Base class for errors
/// </summary>
public abstract class Error
{
/// <summary>
/// Base class for errors
/// </summary>
public abstract class Error
{
private int? _code; private int? _code;
/// <summary> /// <summary>
/// The int error code the server returned; or the http status code int value if there was no error code.<br /> /// The int error code the server returned; or the http status code int value if there was no error code.<br />
@@ -81,60 +80,60 @@ namespace CryptoExchange.Net.Objects
{ {
return ErrorCode != null ? $"[{GetType().Name}.{ErrorType}] {ErrorCode}: {Message ?? ErrorDescription}" : $"[{GetType().Name}.{ErrorType}] {Message ?? ErrorDescription}"; return ErrorCode != null ? $"[{GetType().Name}.{ErrorType}] {ErrorCode}: {Message ?? ErrorDescription}" : $"[{GetType().Name}.{ErrorType}] {Message ?? ErrorDescription}";
} }
} }
/// <summary> /// <summary>
/// Cant reach server error /// Cant reach server error
/// </summary> /// </summary>
public class CantConnectError : Error public class CantConnectError : Error
{ {
/// <summary> /// <summary>
/// Default error info /// Default error info
/// </summary> /// </summary>
protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.UnableToConnect, false, "Can't connect to the server"); protected static readonly ErrorInfo errorInfo = new ErrorInfo(ErrorType.UnableToConnect, false, "Can't connect to the server");
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
public CantConnectError() : base(null, _errorInfo, null) { } public CantConnectError() : base(null, errorInfo, null) { }
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
public CantConnectError(Exception? exception) : base(null, _errorInfo, exception) { } public CantConnectError(Exception? exception) : base(null, errorInfo, exception) { }
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
protected CantConnectError(ErrorInfo info, Exception? exception) : base(null, info, exception) { } protected CantConnectError(ErrorInfo info, Exception? exception) : base(null, info, exception) { }
} }
/// <summary> /// <summary>
/// No api credentials provided while trying to access a private endpoint /// No api credentials provided while trying to access a private endpoint
/// </summary> /// </summary>
public class NoApiCredentialsError : Error public class NoApiCredentialsError : Error
{ {
/// <summary> /// <summary>
/// Default error info /// Default error info
/// </summary> /// </summary>
protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.MissingCredentials, false, "No credentials provided for private endpoint"); protected static readonly ErrorInfo errorInfo = new ErrorInfo(ErrorType.MissingCredentials, false, "No credentials provided for private endpoint");
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
public NoApiCredentialsError() : base(null, _errorInfo, null) { } public NoApiCredentialsError() : base(null, errorInfo, null) { }
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
protected NoApiCredentialsError(ErrorInfo info, Exception? exception) : base(null, info, exception) { } protected NoApiCredentialsError(ErrorInfo info, Exception? exception) : base(null, info, exception) { }
} }
/// <summary> /// <summary>
/// Error returned by the server /// Error returned by the server
/// </summary> /// </summary>
public class ServerError : Error public class ServerError : Error
{ {
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
@@ -151,91 +150,91 @@ namespace CryptoExchange.Net.Objects
/// ctor /// ctor
/// </summary> /// </summary>
public ServerError(string errorCode, ErrorInfo errorInfo, Exception? exception = null) : base(errorCode, errorInfo, exception) { } public ServerError(string errorCode, ErrorInfo errorInfo, Exception? exception = null) : base(errorCode, errorInfo, exception) { }
} }
/// <summary> /// <summary>
/// Web error returned by the server /// Web error returned by the server
/// </summary> /// </summary>
public class WebError : Error public class WebError : Error
{ {
/// <summary> /// <summary>
/// Default error info /// Default error info
/// </summary> /// </summary>
protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.NetworkError, true, "Failed to complete the request to the server due to a network error"); protected static readonly ErrorInfo errorInfo = new ErrorInfo(ErrorType.NetworkError, true, "Failed to complete the request to the server due to a network error");
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
public WebError(string? message = null, Exception? exception = null) : base(null, _errorInfo with { Message = (message?.Length > 0 ? _errorInfo.Message + ": " + message : _errorInfo.Message) }, exception) { } public WebError(string? message = null, Exception? exception = null) : base(null, errorInfo with { Message = (message?.Length > 0 ? errorInfo.Message + ": " + message : errorInfo.Message) }, exception) { }
} }
/// <summary> /// <summary>
/// Timeout error waiting for a response from the server /// Timeout error waiting for a response from the server
/// </summary> /// </summary>
public class TimeoutError : Error public class TimeoutError : Error
{ {
/// <summary> /// <summary>
/// Default error info /// Default error info
/// </summary> /// </summary>
protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.Timeout, false, "Failed to receive a response from the server in time"); protected static readonly ErrorInfo errorInfo = new ErrorInfo(ErrorType.Timeout, false, "Failed to receive a response from the server in time");
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
public TimeoutError(string? message = null, Exception? exception = null) : base(null, _errorInfo with { Message = (message?.Length > 0 ? _errorInfo.Message + ": " + message : _errorInfo.Message) }, exception) { } public TimeoutError(string? message = null, Exception? exception = null) : base(null, errorInfo with { Message = (message?.Length > 0 ? errorInfo.Message + ": " + message : errorInfo.Message) }, exception) { }
} }
/// <summary> /// <summary>
/// Error while deserializing data /// Error while deserializing data
/// </summary> /// </summary>
public class DeserializeError : Error public class DeserializeError : Error
{ {
/// <summary> /// <summary>
/// Default error info /// Default error info
/// </summary> /// </summary>
protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.DeserializationFailed, false, "Failed to deserialize data"); protected static readonly ErrorInfo errorInfo = new ErrorInfo(ErrorType.DeserializationFailed, false, "Failed to deserialize data");
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
public DeserializeError(string? message = null, Exception? exception = null) : base(null, _errorInfo with { Message = (message?.Length > 0 ? _errorInfo.Message + ": " + message : _errorInfo.Message) }, exception) { } public DeserializeError(string? message = null, Exception? exception = null) : base(null, errorInfo with { Message = (message?.Length > 0 ? errorInfo.Message + ": " + message : errorInfo.Message) }, exception) { }
} }
/// <summary> /// <summary>
/// An invalid parameter has been provided /// An invalid parameter has been provided
/// </summary> /// </summary>
public class ArgumentError : Error public class ArgumentError : Error
{ {
/// <summary> /// <summary>
/// Default error info for missing parameter /// Default error info for missing parameter
/// </summary> /// </summary>
protected static readonly ErrorInfo _missingInfo = new ErrorInfo(ErrorType.MissingParameter, false, "Missing parameter"); protected static readonly ErrorInfo missingInfo = new ErrorInfo(ErrorType.MissingParameter, false, "Missing parameter");
/// <summary> /// <summary>
/// Default error info for invalid parameter /// Default error info for invalid parameter
/// </summary> /// </summary>
protected static readonly ErrorInfo _invalidInfo = new ErrorInfo(ErrorType.InvalidParameter, false, "Invalid parameter"); protected static readonly ErrorInfo invalidInfo = new ErrorInfo(ErrorType.InvalidParameter, false, "Invalid parameter");
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
public static ArgumentError Missing(string parameterName, string? message = null) => new ArgumentError(_missingInfo with { Message = message == null ? $"{_missingInfo.Message} '{parameterName}'" : $"{_missingInfo.Message} '{parameterName}': {message}" }, null); public static ArgumentError Missing(string parameterName, string? message = null) => new ArgumentError(missingInfo with { Message = message == null ? $"{missingInfo.Message} '{parameterName}'" : $"{missingInfo.Message} '{parameterName}': {message}" }, null);
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
public static ArgumentError Invalid(string parameterName, string message) => new ArgumentError(_invalidInfo with { Message = $"{_invalidInfo.Message} '{parameterName}': {message}" }, null); public static ArgumentError Invalid(string parameterName, string message) => new ArgumentError(invalidInfo with { Message = $"{invalidInfo.Message} '{parameterName}': {message}" }, null);
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
protected ArgumentError(ErrorInfo info, Exception? exception) : base(null, info, exception) { } protected ArgumentError(ErrorInfo info, Exception? exception) : base(null, info, exception) { }
} }
/// <summary> /// <summary>
/// Rate limit exceeded (client side) /// Rate limit exceeded (client side)
/// </summary> /// </summary>
public abstract class BaseRateLimitError : Error public abstract class BaseRateLimitError : Error
{ {
/// <summary> /// <summary>
/// When the request can be retried /// When the request can be retried
/// </summary> /// </summary>
@@ -245,89 +244,88 @@ namespace CryptoExchange.Net.Objects
/// ctor /// ctor
/// </summary> /// </summary>
protected BaseRateLimitError(ErrorInfo errorInfo, Exception? exception) : base(null, errorInfo, exception) { } protected BaseRateLimitError(ErrorInfo errorInfo, Exception? exception) : base(null, errorInfo, exception) { }
} }
/// <summary> /// <summary>
/// Rate limit exceeded (client side) /// Rate limit exceeded (client side)
/// </summary> /// </summary>
public class ClientRateLimitError : BaseRateLimitError public class ClientRateLimitError : BaseRateLimitError
{ {
/// <summary> /// <summary>
/// Default error info /// Default error info
/// </summary> /// </summary>
protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.RateLimitRequest, false, "Client rate limit exceeded"); protected static readonly ErrorInfo errorInfo = new ErrorInfo(ErrorType.RateLimitRequest, false, "Client rate limit exceeded");
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
public ClientRateLimitError(string? message = null, Exception? exception = null) : base(_errorInfo with { Message = (message?.Length > 0 ? _errorInfo.Message + ": " + message : _errorInfo.Message) }, exception) { } public ClientRateLimitError(string? message = null, Exception? exception = null) : base(errorInfo with { Message = (message?.Length > 0 ? errorInfo.Message + ": " + message : errorInfo.Message) }, exception) { }
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
protected ClientRateLimitError(ErrorInfo info, Exception? exception) : base(info, exception) { } protected ClientRateLimitError(ErrorInfo info, Exception? exception) : base(info, exception) { }
} }
/// <summary> /// <summary>
/// Rate limit exceeded (server side) /// Rate limit exceeded (server side)
/// </summary> /// </summary>
public class ServerRateLimitError : BaseRateLimitError public class ServerRateLimitError : BaseRateLimitError
{ {
/// <summary> /// <summary>
/// Default error info /// Default error info
/// </summary> /// </summary>
protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.RateLimitRequest, false, "Server rate limit exceeded"); protected static readonly ErrorInfo errorInfo = new ErrorInfo(ErrorType.RateLimitRequest, false, "Server rate limit exceeded");
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
public ServerRateLimitError(string? message = null, Exception? exception = null) : base(_errorInfo with { Message = (message?.Length > 0 ? _errorInfo.Message + ": " + message : _errorInfo.Message) }, exception) { } public ServerRateLimitError(string? message = null, Exception? exception = null) : base(errorInfo with { Message = (message?.Length > 0 ? errorInfo.Message + ": " + message : errorInfo.Message) }, exception) { }
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
protected ServerRateLimitError(ErrorInfo info, Exception? exception) : base(info, exception) { } protected ServerRateLimitError(ErrorInfo info, Exception? exception) : base(info, exception) { }
} }
/// <summary> /// <summary>
/// Cancellation requested /// Cancellation requested
/// </summary> /// </summary>
public class CancellationRequestedError : Error public class CancellationRequestedError : Error
{ {
/// <summary> /// <summary>
/// Default error info /// Default error info
/// </summary> /// </summary>
protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.CancellationRequested, false, "Cancellation requested"); protected static readonly ErrorInfo errorInfo = new ErrorInfo(ErrorType.CancellationRequested, false, "Cancellation requested");
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
public CancellationRequestedError(Exception? exception = null) : base(null, _errorInfo, null) { } public CancellationRequestedError(Exception? exception = null) : base(null, errorInfo, null) { }
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
protected CancellationRequestedError(ErrorInfo info, Exception? exception) : base(null, info, exception) { } protected CancellationRequestedError(ErrorInfo info, Exception? exception) : base(null, info, exception) { }
} }
/// <summary> /// <summary>
/// Invalid operation requested /// Invalid operation requested
/// </summary> /// </summary>
public class InvalidOperationError : Error public class InvalidOperationError : Error
{ {
/// <summary> /// <summary>
/// Default error info /// Default error info
/// </summary> /// </summary>
protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.InvalidOperation, false, "Operation invalid"); protected static readonly ErrorInfo errorInfo = new ErrorInfo(ErrorType.InvalidOperation, false, "Operation invalid");
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
public InvalidOperationError(string message) : base(null, _errorInfo with { Message = message }, null) { } public InvalidOperationError(string message) : base(null, errorInfo with { Message = message }, null) { }
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
protected InvalidOperationError(ErrorInfo info, Exception? exception) : base(null, info, exception) { } protected InvalidOperationError(ErrorInfo info, Exception? exception) : base(null, info, exception) { }
}
} }
@@ -1,14 +1,12 @@
using System; using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.Objects.Errors namespace CryptoExchange.Net.Objects.Errors;
/// <summary>
/// Error evaluator
/// </summary>
public class ErrorEvaluator
{ {
/// <summary>
/// Error evaluator
/// </summary>
public class ErrorEvaluator
{
/// <summary> /// <summary>
/// Error code /// Error code
/// </summary> /// </summary>
@@ -36,5 +34,4 @@ namespace CryptoExchange.Net.Objects.Errors
ErrorCodes = errorCodes; ErrorCodes = errorCodes;
ErrorTypeEvaluator = errorTypeEvaluator; ErrorTypeEvaluator = errorTypeEvaluator;
} }
}
} }
@@ -1,12 +1,10 @@
using System; namespace CryptoExchange.Net.Objects.Errors;
namespace CryptoExchange.Net.Objects.Errors /// <summary>
/// Error info
/// </summary>
public record ErrorInfo
{ {
/// <summary>
/// Error info
/// </summary>
public record ErrorInfo
{
/// <summary> /// <summary>
/// Unknown error info /// Unknown error info
/// </summary> /// </summary>
@@ -54,5 +52,4 @@ namespace CryptoExchange.Net.Objects.Errors
IsTransient = isTransient; IsTransient = isTransient;
ErrorDescription = description; ErrorDescription = description;
} }
}
} }

Some files were not shown because too many files have changed in this diff Show More