mirror of
https://github.com/JKorf/CryptoExchange.Net.git
synced 2026-08-11 08:22:53 +00:00
e823114623
* Result types: * (Web)CallResult types are replaced by HttpResult, WebSocketResult and QueryResult with the same logic * Updated result types to record type * Result creation can be done with (Http/WebSocket/Query)Result.Ok(..) and .Fail(..) * Removed implicit result type conversion to bool, `if (result)` no longer works, instead use `if (result.Success)` * Replaced CallResult.SuccessResult with CallResult.Ok() * Fixed result object nullability hinting, for example Data might be null if Success isn't checked for true * Parameters & serialization: * Added support for `enabled` and `disabled` strings to bool converter * Removed ParameterCollection type, has been replaced by Parameters type * Removed ArraySerialization, OrderParameters and ParameterOrderComparer properties from RestApiClient, moved to ParameterSerializationsSettings * Updated RestRequestConfiguration in AuthenticationProvider.ProcessRequest to contain the full RequestDefinition instead of copied fields * Clients: * Updated Api client constructor logging parameter from ILogger to ILoggerFactory? * Added Api client constructor exchange name parameter * Added ToString overrides on base API types * Added Exchange property on BaseApiClient * Added ApiCredentials property on IRestApiClient and ISocketApiClient interfaces * Updated ILogger source from client name to topic specific client name * Removed logging from client creation * Fixed BaseRestClient SetApiCredentials not marked as virtual * Rest: * Added BaseAddress to RequestDefinition object * Updated RestApiClient AuthenticationProvider logic from private to protected and virtual * Removed RestApiClient.SendAsync baseAddress parameter removed * Removed RestApiClient.SendAsync without type parameter * WebSocket: * Updated MessageRouting definition into CreateForEvent for subscriptions and CreateForQuery for queries * Improved Query type safety with CeateForQuery which allows second parameter for specifying the result type * Renamed MessageRouter.CreateWithoutHandler to CreateVoid * Updated SocketApiClient.GetSocketConnection to check connection uri instead of Tag for finding compatible connections * Removed unused UnhandledMessageExpected property SocketApiClient * Fixed issue in SocketApiClient.GetSocketConnection causing requests to always wait the full max 10 seconds when there was a reconnecting socket * Shared APIs: * Updated Option definitions to always require the exchange name as first parameter * Added missing dedicated option types * Added Discover method on ISharedClient interface, returning info on supported capabilities and operations * Added SharedRequest GetParamValue helper method accepting multiple parameter names * Added ResetStaticExchangeParameters method on ExchangeParameters * Added Status property to SharedWithdrawal model * Added TradingModes property to SharedBalance model * Updated ExchangeSymbolCache to support multiple environments and additional key separation * Updated Shared ExchangeParameters parameter names to be case insensitive * Updated code comments * Replaced ExchangeResult with ExchangeCallResult type * Removed AsExchangeResult/ExchangeWebResult * Removed TradingMode from the response model, only maintained on models where it makes sense * Removed IListenKey support, listen keys now rely on internal management with TokenManager * Rate limiting: * Fixed websocket connection attempts counting towards rate limit even when server could not be reached * Removed host from rate limit methods, now part of the already provided RequestDefinition * Added amount parameter to RateLimit Reset method to allow partially resetting the limit * Added TokenManager implementation for automatic listenkey/token management * Added UserClientProvider base class * Added async streaming on UserDataTracker items with StreamUpdatesAsync * Added cancellation token support to UserDataTracker starting * Added Unit type for non-result types * Added ServerError constructor taking ErrorType and message to make it easier to create * Added SupportedEnvironments property to PlatformInfo * Updated SymbolOrderBook DoResyncAsync to return CallResult instead of CallResult<bool> which was redundant * Various small performance improvements
364 lines
12 KiB
C#
364 lines
12 KiB
C#
using CryptoExchange.Net.Objects.Errors;
|
|
using System;
|
|
|
|
namespace CryptoExchange.Net.Objects
|
|
{
|
|
/// <summary>
|
|
/// Base class for errors
|
|
/// </summary>
|
|
public abstract class Error
|
|
{
|
|
|
|
private int? _code;
|
|
/// <summary>
|
|
/// The int error code the server returned; or the http status code int value if there was no error code.<br />
|
|
/// <br />
|
|
/// <i>Note:</i><br />
|
|
/// The <see cref="ErrorCode"/> property should be used for more generic error checking; it might contain a string error code if the server does not return an int code.
|
|
/// </summary>
|
|
public int? Code
|
|
{
|
|
get
|
|
{
|
|
if (_code.HasValue)
|
|
return _code;
|
|
|
|
return int.TryParse(ErrorCode, out var r) ? r : null;
|
|
}
|
|
set
|
|
{
|
|
_code = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The error code returned by the server
|
|
/// </summary>
|
|
public string? ErrorCode { get; set; }
|
|
/// <summary>
|
|
/// The error description
|
|
/// </summary>
|
|
public string? ErrorDescription { get; set; }
|
|
|
|
/// <summary>
|
|
/// Error type
|
|
/// </summary>
|
|
public ErrorType ErrorType { get; set; }
|
|
|
|
/// <summary>
|
|
/// Whether the error is transient and can be retried
|
|
/// </summary>
|
|
public bool IsTransient { get; set; }
|
|
|
|
/// <summary>
|
|
/// The server message for the error that occurred
|
|
/// </summary>
|
|
public string? Message { get; set; }
|
|
|
|
/// <summary>
|
|
/// Underlying exception
|
|
/// </summary>
|
|
public Exception? Exception { get; set; }
|
|
|
|
/// <summary>
|
|
/// ctor
|
|
/// </summary>
|
|
protected Error(string? errorCode, ErrorInfo errorInfo, Exception? exception)
|
|
{
|
|
ErrorCode = errorCode;
|
|
ErrorType = errorInfo.ErrorType;
|
|
Message = errorInfo.Message;
|
|
ErrorDescription = errorInfo.ErrorDescription;
|
|
IsTransient = errorInfo.IsTransient;
|
|
Exception = exception;
|
|
}
|
|
|
|
/// <summary>
|
|
/// String representation
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public override string ToString()
|
|
{
|
|
return Code != null
|
|
? $"[{GetType().Name}.{ErrorType}] {Code}: {GetErrorDescription()}"
|
|
: $"[{GetType().Name}.{ErrorType}] {GetErrorDescription()}";
|
|
}
|
|
|
|
private string GetErrorDescription()
|
|
{
|
|
if (!string.IsNullOrEmpty(Message))
|
|
return Message!;
|
|
|
|
if (ErrorDescription != "Unknown error" || Exception == null)
|
|
return ErrorDescription!;
|
|
|
|
return Exception.Message;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Cant reach server error
|
|
/// </summary>
|
|
public class CantConnectError : Error
|
|
{
|
|
/// <summary>
|
|
/// Default error info
|
|
/// </summary>
|
|
protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.UnableToConnect, false, "Can't connect to the server");
|
|
|
|
/// <summary>
|
|
/// ctor
|
|
/// </summary>
|
|
public CantConnectError() : base(null, _errorInfo, null) { }
|
|
|
|
/// <summary>
|
|
/// ctor
|
|
/// </summary>
|
|
public CantConnectError(Exception? exception) : base(null, _errorInfo, exception) { }
|
|
|
|
/// <summary>
|
|
/// ctor
|
|
/// </summary>
|
|
protected CantConnectError(ErrorInfo info, Exception? exception) : base(null, info, exception) { }
|
|
}
|
|
|
|
/// <summary>
|
|
/// No api credentials provided while trying to access a private endpoint
|
|
/// </summary>
|
|
public class NoApiCredentialsError : Error
|
|
{
|
|
/// <summary>
|
|
/// Default error info
|
|
/// </summary>
|
|
protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.MissingCredentials, false,
|
|
"No credentials provided for private endpoint, set the `ApiCredentials` option in the client configuration");
|
|
|
|
/// <summary>
|
|
/// ctor
|
|
/// </summary>
|
|
public NoApiCredentialsError() : base(null, _errorInfo, null) { }
|
|
|
|
/// <summary>
|
|
/// ctor
|
|
/// </summary>
|
|
protected NoApiCredentialsError(ErrorInfo info, Exception? exception) : base(null, info, exception) { }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Error returned by the server
|
|
/// </summary>
|
|
public class ServerError : Error
|
|
{
|
|
/// <summary>
|
|
/// ctor
|
|
/// </summary>
|
|
public ServerError(ErrorType type, string message, Exception? exception = null)
|
|
: base(null, new ErrorInfo(type, message), exception)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// ctor
|
|
/// </summary>
|
|
public ServerError(ErrorInfo errorInfo, Exception? exception = null)
|
|
: base(null, errorInfo, exception) { }
|
|
|
|
/// <summary>
|
|
/// ctor
|
|
/// </summary>
|
|
public ServerError(int errorCode, ErrorInfo errorInfo, Exception? exception = null)
|
|
: this(errorCode.ToString(), errorInfo, exception) { }
|
|
|
|
/// <summary>
|
|
/// ctor
|
|
/// </summary>
|
|
public ServerError(string errorCode, ErrorInfo errorInfo, Exception? exception = null) : base(errorCode, errorInfo, exception) { }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Web error returned by the server
|
|
/// </summary>
|
|
public class WebError : Error
|
|
{
|
|
/// <summary>
|
|
/// Default error info
|
|
/// </summary>
|
|
protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.NetworkError, true, "Failed to complete the request to the server due to a network error");
|
|
|
|
/// <summary>
|
|
/// ctor
|
|
/// </summary>
|
|
public WebError(string? message = null, Exception? exception = null) : base(null, _errorInfo with { Message = (message?.Length > 0 ? _errorInfo.Message + ": " + message : _errorInfo.Message) }, exception) { }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Timeout error waiting for a response from the server
|
|
/// </summary>
|
|
public class TimeoutError : Error
|
|
{
|
|
/// <summary>
|
|
/// Default error info
|
|
/// </summary>
|
|
protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.Timeout, false, "Failed to receive a response from the server in time");
|
|
|
|
/// <summary>
|
|
/// ctor
|
|
/// </summary>
|
|
public TimeoutError(string? message = null, Exception? exception = null) : base(null, _errorInfo with { Message = (message?.Length > 0 ? _errorInfo.Message + ": " + message : _errorInfo.Message) }, exception) { }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Error while deserializing data
|
|
/// </summary>
|
|
public class DeserializeError : Error
|
|
{
|
|
/// <summary>
|
|
/// Default error info
|
|
/// </summary>
|
|
protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.DeserializationFailed, false, "Failed to deserialize data");
|
|
|
|
/// <summary>
|
|
/// ctor
|
|
/// </summary>
|
|
public DeserializeError(string? message = null, Exception? exception = null)
|
|
: base(null,
|
|
_errorInfo with
|
|
{
|
|
Message = message?.Length > 0
|
|
? message
|
|
: _errorInfo.Message
|
|
},
|
|
exception) { }
|
|
}
|
|
|
|
/// <summary>
|
|
/// An invalid parameter has been provided
|
|
/// </summary>
|
|
public class ArgumentError : Error
|
|
{
|
|
/// <summary>
|
|
/// Default error info for missing parameter
|
|
/// </summary>
|
|
protected static readonly ErrorInfo _missingInfo = new ErrorInfo(ErrorType.MissingParameter, false, "Missing parameter");
|
|
/// <summary>
|
|
/// Default error info for invalid parameter
|
|
/// </summary>
|
|
protected static readonly ErrorInfo _invalidInfo = new ErrorInfo(ErrorType.InvalidParameter, false, "Invalid parameter");
|
|
|
|
/// <summary>
|
|
/// ctor
|
|
/// </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);
|
|
|
|
/// <summary>
|
|
/// ctor
|
|
/// </summary>
|
|
public static ArgumentError Invalid(string parameterName, string message) => new ArgumentError(_invalidInfo with { Message = $"{_invalidInfo.Message} '{parameterName}': {message}" }, null);
|
|
|
|
/// <summary>
|
|
/// ctor
|
|
/// </summary>
|
|
protected ArgumentError(ErrorInfo info, Exception? exception) : base(null, info, exception) { }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rate limit exceeded (client side)
|
|
/// </summary>
|
|
public abstract class BaseRateLimitError : Error
|
|
{
|
|
/// <summary>
|
|
/// When the request can be retried
|
|
/// </summary>
|
|
public DateTime? RetryAfter { get; set; }
|
|
|
|
/// <summary>
|
|
/// ctor
|
|
/// </summary>
|
|
protected BaseRateLimitError(ErrorInfo errorInfo, Exception? exception) : base(null, errorInfo, exception) { }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rate limit exceeded (client side)
|
|
/// </summary>
|
|
public class ClientRateLimitError : BaseRateLimitError
|
|
{
|
|
/// <summary>
|
|
/// Default error info
|
|
/// </summary>
|
|
protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.RateLimitRequest, false, "Client rate limit exceeded");
|
|
|
|
/// <summary>
|
|
/// ctor
|
|
/// </summary>
|
|
public ClientRateLimitError(string? message = null, Exception? exception = null) : base(_errorInfo with { Message = (message?.Length > 0 ? _errorInfo.Message + ": " + message : _errorInfo.Message) }, exception) { }
|
|
|
|
/// <summary>
|
|
/// ctor
|
|
/// </summary>
|
|
protected ClientRateLimitError(ErrorInfo info, Exception? exception) : base(info, exception) { }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rate limit exceeded (server side)
|
|
/// </summary>
|
|
public class ServerRateLimitError : BaseRateLimitError
|
|
{
|
|
/// <summary>
|
|
/// Default error info
|
|
/// </summary>
|
|
protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.RateLimitRequest, false, "Server rate limit exceeded");
|
|
|
|
/// <summary>
|
|
/// ctor
|
|
/// </summary>
|
|
public ServerRateLimitError(string? message = null, Exception? exception = null) : base(_errorInfo with { Message = (message?.Length > 0 ? _errorInfo.Message + ": " + message : _errorInfo.Message) }, exception) { }
|
|
|
|
/// <summary>
|
|
/// ctor
|
|
/// </summary>
|
|
protected ServerRateLimitError(ErrorInfo info, Exception? exception) : base(info, exception) { }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Cancellation requested
|
|
/// </summary>
|
|
public class CancellationRequestedError : Error
|
|
{
|
|
/// <summary>
|
|
/// Default error info
|
|
/// </summary>
|
|
protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.CancellationRequested, false, "Cancellation requested");
|
|
|
|
/// <summary>
|
|
/// ctor
|
|
/// </summary>
|
|
public CancellationRequestedError(Exception? exception = null) : base(null, _errorInfo, null) { }
|
|
|
|
/// <summary>
|
|
/// ctor
|
|
/// </summary>
|
|
protected CancellationRequestedError(ErrorInfo info, Exception? exception) : base(null, info, exception) { }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Invalid operation requested
|
|
/// </summary>
|
|
public class InvalidOperationError : Error
|
|
{
|
|
/// <summary>
|
|
/// Default error info
|
|
/// </summary>
|
|
protected static readonly ErrorInfo _errorInfo = new ErrorInfo(ErrorType.InvalidOperation, false, "Operation invalid");
|
|
|
|
/// <summary>
|
|
/// ctor
|
|
/// </summary>
|
|
public InvalidOperationError(string message) : base(null, _errorInfo with { Message = message }, null) { }
|
|
|
|
/// <summary>
|
|
/// ctor
|
|
/// </summary>
|
|
protected InvalidOperationError(ErrorInfo info, Exception? exception) : base(null, info, exception) { }
|
|
}
|
|
}
|