1
0
mirror of https://github.com/JKorf/CryptoExchange.Net.git synced 2026-08-11 16:32:57 +00:00

CryptoExchange V12 (#281)

* 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
This commit is contained in:
Jan Korf
2026-06-29 10:38:09 +02:00
committed by GitHub
parent afb84a1bf0
commit e823114623
285 changed files with 7029 additions and 3658 deletions
-592
View File
@@ -1,592 +0,0 @@
using CryptoExchange.Net.SharedApis;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
namespace CryptoExchange.Net.Objects
{
/// <summary>
/// The result of an operation
/// </summary>
public class CallResult
{
/// <summary>
/// Static success result
/// </summary>
public static CallResult SuccessResult { get; } = new CallResult(null);
/// <summary>
/// An error if the call didn't succeed, will always be filled if Success = false
/// </summary>
public Error? Error { get; internal set; }
/// <summary>
/// Whether the call was successful
/// </summary>
public bool Success => Error == null;
/// <summary>
/// ctor
/// </summary>
/// <param name="error"></param>
public CallResult(Error? error)
{
Error = error;
}
/// <summary>
/// Overwrite bool check so we can use if(callResult) instead of if(callResult.Success)
/// </summary>
/// <param name="obj"></param>
public static implicit operator bool(CallResult obj)
{
return obj?.Success == true;
}
/// <inheritdoc />
public override string ToString()
{
return Success ? $"Success" : $"Error: {Error}";
}
}
/// <summary>
/// The result of an operation
/// </summary>
/// <typeparam name="T"></typeparam>
public class CallResult<T>: CallResult
{
/// <summary>
/// The data returned by the call, only available when Success = true
/// </summary>
public T Data { get; internal set; }
/// <summary>
/// The original data returned by the call, only available when `OutputOriginalData` is set to `true` in the client options
/// </summary>
public string? OriginalData { get; internal set; }
/// <summary>
/// ctor
/// </summary>
/// <param name="data"></param>
/// <param name="originalData"></param>
/// <param name="error"></param>
#pragma warning disable 8618
public CallResult([AllowNull]T data, string? originalData, Error? error): base(error)
#pragma warning restore 8618
{
OriginalData = originalData;
#pragma warning disable 8601
Data = data;
#pragma warning restore 8601
}
/// <summary>
/// Create a new data result
/// </summary>
/// <param name="data">The data to return</param>
public CallResult(T data) : this(data, null, null) { }
/// <summary>
/// Create a new error result
/// </summary>
/// <param name="error">The error to return</param>
public CallResult(Error error) : this(default, null, error) { }
/// <summary>
/// Create a new error result
/// </summary>
/// <param name="error">The error to return</param>
/// <param name="originalData">The original response data</param>
public CallResult(Error error, string? originalData) : this(default, originalData, error) { }
/// <summary>
/// Overwrite bool check so we can use if(callResult) instead of if(callResult.Success)
/// </summary>
/// <param name="obj"></param>
public static implicit operator bool(CallResult<T> obj)
{
return obj?.Success == true;
}
/// <summary>
/// Whether the call was successful or not. Useful for nullability checking.
/// </summary>
/// <param name="data">The data returned by the call.</param>
/// <param name="error"><see cref="Error"/> on failure.</param>
/// <returns><c>true</c> when <see cref="CallResult{T}"/> succeeded, <c>false</c> otherwise.</returns>
public bool GetResultOrError([MaybeNullWhen(false)] out T data, [NotNullWhen(false)] out Error? error)
{
if (Success)
{
data = Data!;
error = null;
return true;
}
else
{
data = default;
error = Error!;
return false;
}
}
/// <summary>
/// Copy the WebCallResult to a new data type
/// </summary>
/// <typeparam name="K">The new type</typeparam>
/// <param name="data">The data of the new type</param>
/// <returns></returns>
public CallResult<K> As<K>([AllowNull] K data)
{
return new CallResult<K>(data, OriginalData, Error);
}
/// <summary>
/// Copy as a dataless result
/// </summary>
/// <returns></returns>
public CallResult AsDataless()
{
if (Error != null )
return new CallResult(Error);
return SuccessResult;
}
/// <summary>
/// Copy as a dataless result
/// </summary>
/// <returns></returns>
public CallResult AsDatalessError(Error error)
{
return new CallResult(error);
}
/// <summary>
/// Copy the CallResult to a new data type
/// </summary>
/// <typeparam name="K">The new type</typeparam>
/// <param name="data">The data</param>
/// <param name="error">The error returned</param>
/// <returns></returns>
public CallResult<K> AsErrorWithData<K>(Error error, K data)
{
return new CallResult<K>(data, OriginalData, error);
}
/// <summary>
/// Copy the WebCallResult to a new data type
/// </summary>
/// <typeparam name="K">The new type</typeparam>
/// <param name="error">The error to return</param>
/// <returns></returns>
public CallResult<K> AsError<K>(Error error)
{
return new CallResult<K>(default, OriginalData, error);
}
/// <inheritdoc />
public override string ToString()
{
return Success ? $"Success" : $"Error: {Error}";
}
}
/// <summary>
/// The result of a request
/// </summary>
public class WebCallResult : CallResult
{
/// <summary>
/// The request http method
/// </summary>
public HttpMethod? RequestMethod { get; set; }
/// <summary>
/// HTTP protocol version
/// </summary>
public Version? HttpVersion { get; set; }
/// <summary>
/// The headers sent with the request
/// </summary>
public HttpRequestHeaders? RequestHeaders { get; set; }
/// <summary>
/// The request id
/// </summary>
public int? RequestId { get; set; }
/// <summary>
/// The url which was requested
/// </summary>
public string? RequestUrl { get; set; }
/// <summary>
/// The body of the request
/// </summary>
public string? RequestBody { get; set; }
/// <summary>
/// The original data returned by the call, only available when `OutputOriginalData` is set to `true` in the client options
/// </summary>
public string? OriginalData { get; internal set; }
/// <summary>
/// The status code of the response. Note that a OK status does not always indicate success, check the Success parameter for this.
/// </summary>
public HttpStatusCode? ResponseStatusCode { get; set; }
/// <summary>
/// The response headers
/// </summary>
public HttpResponseHeaders? ResponseHeaders { get; set; }
/// <summary>
/// The time between sending the request and receiving the response
/// </summary>
public TimeSpan? ResponseTime { get; set; }
/// <summary>
/// ctor
/// </summary>
public WebCallResult(
HttpStatusCode? code,
Version? httpVersion,
HttpResponseHeaders? responseHeaders,
TimeSpan? responseTime,
string? originalData,
int? requestId,
string? requestUrl,
string? requestBody,
HttpMethod? requestMethod,
HttpRequestHeaders? requestHeaders,
Error? error) : base(error)
{
ResponseStatusCode = code;
HttpVersion = httpVersion;
ResponseHeaders = responseHeaders;
ResponseTime = responseTime;
RequestId = requestId;
OriginalData = originalData;
RequestUrl = requestUrl;
RequestBody = requestBody;
RequestHeaders = requestHeaders;
RequestMethod = requestMethod;
}
/// <summary>
/// ctor
/// </summary>
/// <param name="error"></param>
public WebCallResult(Error error): base(error) { }
/// <summary>
/// Return the result as an error result
/// </summary>
/// <param name="error">The error returned</param>
/// <returns></returns>
public WebCallResult AsError(Error error)
{
return new WebCallResult(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, error);
}
/// <summary>
/// Copy the WebCallResult to a new data type
/// </summary>
/// <typeparam name="K">The new type</typeparam>
/// <param name="data">The data of the new type</param>
/// <returns></returns>
public WebCallResult<K> As<K>([AllowNull] K data)
{
return new WebCallResult<K>(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, 0, null, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, ResultDataSource.Server, data, Error);
}
/// <summary>
/// Copy the WebCallResult to an ExchangeWebResult of a new data type
/// </summary>
/// <typeparam name="K">The new type</typeparam>
/// <param name="exchange">The exchange</param>
/// <param name="tradeMode">Trade mode the result applies to</param>
/// <param name="data">The data</param>
/// <returns></returns>
public ExchangeWebResult<K> AsExchangeResult<K>(string exchange, TradingMode tradeMode, [AllowNull] K data)
{
return new ExchangeWebResult<K>(exchange, tradeMode, this.As<K>(data));
}
/// <summary>
/// Copy the WebCallResult to an ExchangeWebResult of a new data type
/// </summary>
/// <typeparam name="K">The new type</typeparam>
/// <param name="exchange">The exchange</param>
/// <param name="tradeModes">Trade modes the result applies to</param>
/// <param name="data">The data</param>
/// <returns></returns>
public ExchangeWebResult<K> AsExchangeResult<K>(string exchange, TradingMode[]? tradeModes, [AllowNull] K data)
{
return new ExchangeWebResult<K>(exchange, tradeModes, this.As<K>(data));
}
/// <summary>
/// Copy the WebCallResult to a new data type
/// </summary>
/// <typeparam name="K">The new type</typeparam>
/// <param name="error">The error returned</param>
/// <returns></returns>
public WebCallResult<K> AsError<K>(Error error)
{
return new WebCallResult<K>(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, 0, null, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, ResultDataSource.Server, default, error);
}
/// <inheritdoc />
public override string ToString()
{
return (Success ? $"Success" : $"Error: {Error}") + $" in {ResponseTime}";
}
}
/// <summary>
/// The result of a request
/// </summary>
/// <typeparam name="T"></typeparam>
public class WebCallResult<T>: CallResult<T>
{
/// <summary>
/// The request http method
/// </summary>
public HttpMethod? RequestMethod { get; set; }
/// <summary>
/// HTTP protocol version
/// </summary>
public Version? HttpVersion { get; set; }
/// <summary>
/// The headers sent with the request
/// </summary>
public HttpRequestHeaders? RequestHeaders { get; set; }
/// <summary>
/// The request id
/// </summary>
public int? RequestId { get; set; }
/// <summary>
/// The url which was requested
/// </summary>
public string? RequestUrl { get; set; }
/// <summary>
/// The body of the request
/// </summary>
public string? RequestBody { get; set; }
/// <summary>
/// The status code of the response. Note that a OK status does not always indicate success, check the Success parameter for this.
/// </summary>
public HttpStatusCode? ResponseStatusCode { get; set; }
/// <summary>
/// Length in bytes of the response
/// </summary>
public long? ResponseLength { get; set; }
/// <summary>
/// The response headers
/// </summary>
public HttpResponseHeaders? ResponseHeaders { get; set; }
/// <summary>
/// The time between sending the request and receiving the response
/// </summary>
public TimeSpan? ResponseTime { get; set; }
/// <summary>
/// The data source of this result
/// </summary>
public ResultDataSource DataSource { get; set; } = ResultDataSource.Server;
/// <summary>
/// Create a new result
/// </summary>
public WebCallResult(
HttpStatusCode? code,
Version? httpVersion,
HttpResponseHeaders? responseHeaders,
TimeSpan? responseTime,
long? responseLength,
string? originalData,
int? requestId,
string? requestUrl,
string? requestBody,
HttpMethod? requestMethod,
HttpRequestHeaders? requestHeaders,
ResultDataSource dataSource,
[AllowNull] T data,
Error? error) : base(data, originalData, error)
{
HttpVersion = httpVersion;
ResponseStatusCode = code;
ResponseHeaders = responseHeaders;
ResponseTime = responseTime;
ResponseLength = responseLength;
RequestId = requestId;
RequestUrl = requestUrl;
RequestBody = requestBody;
RequestHeaders = requestHeaders;
RequestMethod = requestMethod;
DataSource = dataSource;
}
/// <summary>
/// Copy as a dataless result
/// </summary>
/// <returns></returns>
public new WebCallResult AsDataless()
{
return new WebCallResult(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, Error);
}
/// <summary>
/// Copy as a dataless result
/// </summary>
/// <returns></returns>
public new WebCallResult AsDatalessError(Error error)
{
return new WebCallResult(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, error);
}
/// <summary>
/// Create a new error result
/// </summary>
/// <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) { }
/// <summary>
/// Copy the WebCallResult to a new data type
/// </summary>
/// <typeparam name="K">The new type</typeparam>
/// <param name="data">The data of the new type</param>
/// <returns></returns>
public new WebCallResult<K> As<K>([AllowNull] K data)
{
return new WebCallResult<K>(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, DataSource, data, Error);
}
/// <summary>
/// Copy the WebCallResult to a new data type
/// </summary>
/// <typeparam name="K">The new type</typeparam>
/// <param name="error">The error returned</param>
/// <returns></returns>
public new WebCallResult<K> AsError<K>(Error error)
{
return new WebCallResult<K>(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, DataSource, default, error);
}
/// <summary>
/// Copy the WebCallResult to a new data type
/// </summary>
/// <typeparam name="K">The new type</typeparam>
/// <param name="data">The data</param>
/// <param name="error">The error returned</param>
/// <returns></returns>
public new WebCallResult<K> AsErrorWithData<K>(Error error, K data)
{
return new WebCallResult<K>(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, DataSource, data, error);
}
/// <summary>
/// Copy the WebCallResult to an ExchangeWebResult of a new data type
/// </summary>
/// <param name="exchange">The exchange</param>
/// <param name="tradeMode">Trade mode the result applies to</param>
/// <returns></returns>
public ExchangeWebResult<T> AsExchangeResult(string exchange, TradingMode tradeMode)
{
return new ExchangeWebResult<T>(exchange, tradeMode, this);
}
/// <summary>
/// Copy the WebCallResult to an ExchangeWebResult of a new data type
/// </summary>
/// <param name="exchange">The exchange</param>
/// <param name="tradeModes">Trade modes the result applies to</param>
/// <returns></returns>
public ExchangeWebResult<T> AsExchangeResult(string exchange, TradingMode[] tradeModes)
{
return new ExchangeWebResult<T>(exchange, tradeModes, this);
}
/// <summary>
/// Copy the WebCallResult to an ExchangeWebResult of a new data type
/// </summary>
/// <typeparam name="K">The new type</typeparam>
/// <param name="exchange">The exchange</param>
/// <param name="tradeMode">Trade mode the result applies to</param>
/// <param name="data">Data</param>
/// <param name="nextPageRequest">Next page request</param>
/// <returns></returns>
public ExchangeWebResult<K> AsExchangeResult<K>(string exchange, TradingMode tradeMode, [AllowNull] K data, PageRequest? nextPageRequest = null)
{
return new ExchangeWebResult<K>(exchange, tradeMode, As<K>(data), nextPageRequest);
}
/// <summary>
/// Copy the WebCallResult to an ExchangeWebResult of a new data type
/// </summary>
/// <typeparam name="K">The new type</typeparam>
/// <param name="exchange">The exchange</param>
/// <param name="tradeModes">Trade modes the result applies to</param>
/// <param name="data">Data</param>
/// <param name="nextPageRequest">Next page token</param>
/// <returns></returns>
public ExchangeWebResult<K> AsExchangeResult<K>(string exchange, TradingMode[]? tradeModes, [AllowNull] K data, PageRequest? nextPageRequest = null)
{
return new ExchangeWebResult<K>(exchange, tradeModes, As<K>(data), nextPageRequest);
}
/// <summary>
/// Copy the WebCallResult to an ExchangeWebResult with a specific error
/// </summary>
/// <typeparam name="K">The new type</typeparam>
/// <param name="exchange">The exchange</param>
/// <param name="error">The error returned</param>
/// <returns></returns>
public ExchangeWebResult<K> AsExchangeError<K>(string exchange, Error error)
{
return new ExchangeWebResult<K>(exchange, null, AsError<K>(error));
}
/// <summary>
/// Return a copy of this result with data source set to cache
/// </summary>
/// <returns></returns>
internal WebCallResult<T> Cached()
{
return new WebCallResult<T>(ResponseStatusCode, HttpVersion, ResponseHeaders, ResponseTime, ResponseLength, OriginalData, RequestId, RequestUrl, RequestBody, RequestMethod, RequestHeaders, ResultDataSource.Cache, Data, Error);
}
/// <inheritdoc />
public override string ToString()
{
var sb = new StringBuilder();
sb.Append(Success ? $"Success response" : $"Error response: {Error}");
if (ResponseLength != null)
sb.Append($", {ResponseLength} bytes");
if (ResponseTime != null)
sb.Append($", received in {Math.Round(ResponseTime?.TotalMilliseconds ?? 0)}ms");
return sb.ToString();
}
}
}
+8
View File
@@ -149,6 +149,14 @@ namespace CryptoExchange.Net.Objects
/// </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>
@@ -44,7 +44,7 @@ namespace CryptoExchange.Net.Objects.Options
/// <inheritdoc />
public override string ToString()
{
return $"RequestTimeout: {RequestTimeout}, Proxy: {(Proxy == null ? "-" : "set")}";
return $"Proxy: {(Proxy == null ? "-" : "set")}";
}
}
}
@@ -105,6 +105,12 @@ namespace CryptoExchange.Net.Objects.Options
target.Environment = Environment;
return target;
}
/// <inheritdoc />
public override string ToString()
{
return $"{base.ToString()} | Environment: {Environment.Name}";
}
}
/// <inheritdoc />
@@ -131,7 +137,7 @@ namespace CryptoExchange.Net.Objects.Options
/// <inheritdoc />
public override string ToString()
{
return $"{base.ToString()}, ApiCredentials: {(ApiCredentials == null ? "-" : "set")}";
return $"{base.ToString()} | ApiCredentials: {(ApiCredentials == null ? "-" : "set")}";
}
}
@@ -132,6 +132,12 @@ namespace CryptoExchange.Net.Objects.Options
target.Environment = Environment;
return target;
}
/// <inheritdoc />
public override string ToString()
{
return $"{base.ToString()} | Environment: {Environment.Name}";
}
}
/// <summary>
@@ -159,7 +165,7 @@ namespace CryptoExchange.Net.Objects.Options
/// <inheritdoc />
public override string ToString()
{
return $"{base.ToString()}, ApiCredentials: {(ApiCredentials == null ? "-" : "set")}";
return $"{base.ToString()} | ApiCredentials: {(ApiCredentials == null ? "-" : "set")}";
}
}
}
@@ -1,343 +0,0 @@
using CryptoExchange.Net.Attributes;
using CryptoExchange.Net.Converters.SystemTextJson;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
namespace CryptoExchange.Net.Objects
{
/// <summary>
/// Parameters collection
/// </summary>
public class ParameterCollection : Dictionary<string, object>
{
/// <inheritdoc />
public new void Add(string key, object value)
{
if (value == null)
throw new ArgumentNullException(key);
base.Add(key, value);
}
/// <summary>
/// Add an optional parameter. Not added if value is null
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void AddOptional(string key, object? value)
{
if (value != null)
base.Add(key, value);
}
/// <summary>
/// Add a decimal value as string
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void AddString(string key, decimal value)
{
base.Add(key, value.ToString(CultureInfo.InvariantCulture));
}
/// <summary>
/// Add a decimal value as string. Not added if value is null
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void AddOptionalString(string key, decimal? value)
{
if (value != null)
base.Add(key, value.Value.ToString(CultureInfo.InvariantCulture));
}
/// <summary>
/// Add a int value as string
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void AddString(string key, int value)
{
base.Add(key, value.ToString(CultureInfo.InvariantCulture));
}
/// <summary>
/// Add a int value as string. Not added if value is null
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void AddOptionalString(string key, int? value)
{
if (value != null)
base.Add(key, value.Value.ToString(CultureInfo.InvariantCulture));
}
/// <summary>
/// Add a long value as string
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void AddString(string key, long value)
{
base.Add(key, value.ToString(CultureInfo.InvariantCulture));
}
/// <summary>
/// Add a long value as string. Not added if value is null
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void AddOptionalString(string key, long? value)
{
if (value != null)
base.Add(key, value.Value.ToString(CultureInfo.InvariantCulture));
}
/// <summary>
/// Add a DateTime value as string
/// </summary>
public void AddString(string key, DateTime value)
{
base.Add(key, value.ToString("yyyy-MM-ddTHH:mm:ssZ"));
}
/// <summary>
/// Add a DateTime value as string. Not added if value is null
/// </summary>
public void AddOptionalString(string key, DateTime? value)
{
if (value != null)
base.Add(key, value.Value.ToString("yyyy-MM-ddTHH:mm:ssZ"));
}
/// <summary>
/// Add a datetime value as milliseconds timestamp
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void AddMilliseconds(string key, DateTime value)
{
base.Add(key, DateTimeConverter.ConvertToMilliseconds(value));
}
/// <summary>
/// Add a datetime value as milliseconds timestamp. Not added if value is null
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void AddOptionalMilliseconds(string key, DateTime? value)
{
if (value != null)
base.Add(key, DateTimeConverter.ConvertToMilliseconds(value));
}
/// <summary>
/// Add a datetime value as milliseconds timestamp
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void AddMillisecondsString(string key, DateTime value)
{
base.Add(key, DateTimeConverter.ConvertToMilliseconds(value).Value.ToString(CultureInfo.InvariantCulture));
}
/// <summary>
/// Add a datetime value as milliseconds timestamp. Not added if value is null
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void AddOptionalMillisecondsString(string key, DateTime? value)
{
if (value != null)
base.Add(key, DateTimeConverter.ConvertToMilliseconds(value).Value.ToString(CultureInfo.InvariantCulture));
}
/// <summary>
/// Add a datetime value as seconds timestamp
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void AddSeconds(string key, DateTime value)
{
base.Add(key, DateTimeConverter.ConvertToSeconds(value));
}
/// <summary>
/// Add a datetime value as seconds timestamp. Not added if value is null
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void AddOptionalSeconds(string key, DateTime? value)
{
if (value != null)
base.Add(key, DateTimeConverter.ConvertToSeconds(value));
}
/// <summary>
/// Add a datetime value as string seconds timestamp
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void AddSecondsString(string key, DateTime value)
{
base.Add(key, DateTimeConverter.ConvertToSeconds(value).ToString()!);
}
/// <summary>
/// Add a datetime value as string seconds timestamp. Not added if value is null
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void AddOptionalSecondsString(string key, DateTime? value)
{
if (value != null)
base.Add(key, DateTimeConverter.ConvertToSeconds(value).ToString()!);
}
/// <summary>
/// Add an enum value as the string value as mapped using the <see cref="MapAttribute" />
/// </summary>
#if NET5_0_OR_GREATER
public void AddEnum<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields)] T>(string key, T value)
#else
public void AddEnum<T>(string key, T value)
#endif
where T : struct, Enum
{
base.Add(key, EnumConverter<T>.GetString(value)!);
}
/// <summary>
/// Add an enum value as the string value as mapped using the <see cref="MapAttribute" />
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
#if NET5_0_OR_GREATER
public void AddEnumAsInt<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields)] T>(string key, T value)
#else
public void AddEnumAsInt<T>(string key, T value)
#endif
where T : struct, Enum
{
var stringVal = EnumConverter<T>.GetString(value)!;
base.Add(key, int.Parse(stringVal)!);
}
/// <summary>
/// Add an enum value as the string value as mapped using the <see cref="MapAttribute" />. Not added if value is null
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
#if NET5_0_OR_GREATER
public void AddOptionalEnum<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields)] T>(string key, T? value)
#else
public void AddOptionalEnum<T>(string key, T? value)
#endif
where T : struct, Enum
{
if (value != null)
base.Add(key, EnumConverter<T>.GetString(value));
}
/// <summary>
/// Add an enum value as the string value as mapped using the <see cref="MapAttribute" />. Not added if value is null
/// </summary>
#if NET5_0_OR_GREATER
public void AddOptionalEnumAsInt<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields)] T>(string key, T? value)
#else
public void AddOptionalEnumAsInt<T>(string key, T? value)
#endif
where T : struct, Enum
{
if (value != null)
{
var stringVal = EnumConverter<T>.GetString(value);
base.Add(key, int.Parse(stringVal));
}
}
/// <summary>
/// Add key as comma separated values
/// </summary>
public void AddCommaSeparated(string key, IEnumerable<string> values)
{
base.Add(key, string.Join(",", values));
}
/// <summary>
/// Add key as comma separated values if there are values provided
/// </summary>
public void AddOptionalCommaSeparated(string key, IEnumerable<string>? values)
{
if (values == null || !values.Any())
return;
base.Add(key, string.Join(",", values));
}
/// <summary>
/// Add key as comma separated values
/// </summary>
#if NET5_0_OR_GREATER
public void AddCommaSeparated<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields)] T>(string key, IEnumerable<T> values)
#else
public void AddCommaSeparated<T>(string key, IEnumerable<T> values)
#endif
where T : struct, Enum
{
base.Add(key, string.Join(",", values.Select(x => EnumConverter.GetString(x))));
}
/// <summary>
/// Add key as comma separated values if there are values provided
/// </summary>
#if NET5_0_OR_GREATER
public void AddOptionalCommaSeparated<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields)] T>(string key, IEnumerable<T>? values)
#else
public void AddOptionalCommaSeparated<T>(string key, IEnumerable<T>? values)
#endif
where T : struct, Enum
{
if (values == null || !values.Any())
return;
base.Add(key, string.Join(",", values.Select(x => EnumConverter.GetString(x))));
}
/// <summary>
/// Add key as boolean lower case value
/// </summary>
public void AddBoolString(string key, bool value)
{
base.Add(key, value.ToString().ToLower());
}
/// <summary>
/// Add key as boolean lower case value if it's not null
/// </summary>
public void AddOptionalBoolString(string key, bool? value)
{
if (value == null)
return;
base.Add(key, value.ToString()!.ToLower());
}
/// <summary>
/// Set the request body. Can be used to specify a simple value or array as the body instead of an object
/// </summary>
/// <param name="body">Body to set</param>
/// <exception cref="InvalidOperationException"></exception>
public void SetBody(object body)
{
if (this.Any())
throw new InvalidOperationException("Can't set body when other parameters already specified");
base.Add(Constants.BodyPlaceHolderKey, body);
}
}
}
@@ -0,0 +1,146 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.Objects
{
/// <summary>
/// Settings for parameter serialization
/// </summary>
public class ParameterSerializationSettings
{
/// <summary>
/// Default serialization settings
/// </summary>
public static ParameterSerializationSettings Default { get; } = new ParameterSerializationSettings();
/// <summary>
/// Whether to sort the parameters
/// </summary>
public bool Sort { get; set; } = true;
/// <summary>
/// The parameter comparer when sorting
/// </summary>
public IComparer<string>? SortComparer { get; set; }
/// <summary>
/// Decimal serialization type
/// </summary>
public DecimalSerialization Decimal { get; set; } = DecimalSerialization.Number;
/// <summary>
/// DateTime serialization type
/// </summary>
public DateTimeSerialization DateTimes { get; set; } = DateTimeSerialization.MillisecondsNumber;
/// <summary>
/// Boolean serialization type
/// </summary>
public BoolSerialization Bool { get; set; } = BoolSerialization.Bool;
/// <summary>
/// Integer serialization type
/// </summary>
public IntegerSerialization Integer { get; set; } = IntegerSerialization.Number;
/// <summary>
/// Enum serialization type
/// </summary>
public EnumSerialization Enum { get; set; } = EnumSerialization.String;
/// <summary>
/// Array serialization type
/// </summary>
public ArrayParametersSerialization Array { get; set; } = ArrayParametersSerialization.Array;
}
/// <summary>
/// Type of decimal value serialization
/// </summary>
public enum DecimalSerialization
{
/// <summary>
/// Decimals should be serialized as numbers
/// </summary>
Number,
/// <summary>
/// Decimals should be strings
/// </summary>
String
}
/// <summary>
/// Type of DateTime value serialization
/// </summary>
public enum DateTimeSerialization
{
/// <summary>
/// DateTimes should be serialized as milliseconds number
/// </summary>
MillisecondsNumber,
/// <summary>
/// DateTimes should be serialized as milliseconds string
/// </summary>
MillisecondsString,
/// <summary>
/// DateTimes should be serialized as seconds number
/// </summary>
SecondsNumber,
/// <summary>
/// DateTimes should be serialized as seconds string
/// </summary>
SecondsString,
/// <summary>
/// DateTimes should be serialized as microseconds number
/// </summary>
MicrosecondsNumber,
/// <summary>
/// DateTimes should be serialized as microseconds string
/// </summary>
MicrosecondsString,
/// <summary>
/// DateTimes should be serialized as ISO 8601 string
/// </summary>
Rfc3339String
}
/// <summary>
/// Type of boolean value serialization
/// </summary>
public enum BoolSerialization
{
/// <summary>
/// Booleans should be serialized as bool values
/// </summary>
Bool,
/// <summary>
/// Booleans should be serialized as strings
/// </summary>
String
}
/// <summary>
/// Type of integer value serialization
/// </summary>
public enum IntegerSerialization
{
/// <summary>
/// Integers should be serialized as integer values
/// </summary>
Number,
/// <summary>
/// Integers should be serialized as strings
/// </summary>
String
}
/// <summary>
/// Type of enum value serialization
/// </summary>
public enum EnumSerialization
{
/// <summary>
/// Enums should be serialized as integer values
/// </summary>
Number,
/// <summary>
/// Enums should be serialized as strings
/// </summary>
String
}
}
+375
View File
@@ -0,0 +1,375 @@
using CryptoExchange.Net.Attributes;
using CryptoExchange.Net.Converters.SystemTextJson;
using CryptoExchange.Net.Interfaces;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
namespace CryptoExchange.Net.Objects
{
/// <summary>
/// Set of parameters
/// </summary>
public class Parameters : IDictionary<string, object>
{
private readonly ParameterSerializationSettings _serializationSettings;
private IDictionary<string, object> _parameters;
private object? _value;
/// <inheritdoc />
public object? BodyValue => _value;
/// <inheritdoc />
public ICollection<string> Keys => _parameters.Keys;
/// <inheritdoc />
public ICollection<object> Values => _parameters.Values;
/// <inheritdoc />
public int Count => _parameters.Count;
/// <inheritdoc />
public bool IsReadOnly => _parameters.IsReadOnly;
/// <summary>
/// Whether any parameters are defined
/// </summary>
public bool Empty => _parameters.Count == 0 && _value == null;
/// <inheritdoc />
public object this[string key] { get => _parameters[key]; set => _parameters[key] = value; }
/// <summary>
/// ctor
/// </summary>
/// <param name="serializationSettings">Serialization settings</param>
public Parameters(ParameterSerializationSettings serializationSettings)
{
_serializationSettings = serializationSettings;
if (_serializationSettings.Sort)
_parameters = new SortedDictionary<string, object>(_serializationSettings.SortComparer);
else
_parameters = new Dictionary<string, object>();
}
/// <summary>
/// ctor
/// </summary>
/// <param name="serializationSettings">Serialization settings</param>
/// <param name="value">Body value</param>
public Parameters(object value, ParameterSerializationSettings serializationSettings)
{
_parameters = new Dictionary<string, object>();
_serializationSettings = serializationSettings;
_value = value;
}
/// <summary>
/// Add a short value if it is not null
/// </summary>
public void Add(string key, short? value, IntegerSerialization? serialization = null)
{
if (value == null)
return;
Add(key, value.Value, serialization);
}
/// <summary>
/// Add a short value
/// </summary>
public void Add(string key, short value, IntegerSerialization? serialization = null)
{
var serializationToUse = serialization ?? _serializationSettings.Integer;
if (serializationToUse == IntegerSerialization.String)
_parameters.Add(key, value.ToString(CultureInfo.InvariantCulture));
else if (serializationToUse == IntegerSerialization.Number)
_parameters.Add(key, value);
else
throw new ArgumentException("Unknown Integer serialization setting");
}
/// <summary>
/// Add an int value if it is not null
/// </summary>
public void Add(string key, int? value, IntegerSerialization? serialization = null)
{
if (value == null)
return;
Add(key, value.Value, serialization);
}
/// <summary>
/// Add an int value
/// </summary>
public void Add(string key, int value, IntegerSerialization? serialization = null)
{
var serializationToUse = serialization ?? _serializationSettings.Integer;
if (serializationToUse == IntegerSerialization.String)
_parameters.Add(key, value.ToString(CultureInfo.InvariantCulture));
else if (serializationToUse == IntegerSerialization.Number)
_parameters.Add(key, value);
else
throw new ArgumentException("Unknown Integer serialization setting");
}
/// <summary>
/// Add a long value if it is not null
/// </summary>
public void Add(string key, long? value, IntegerSerialization? serialization = null)
{
if (value == null)
return;
Add(key, value.Value, serialization);
}
/// <summary>
/// Add a long value
/// </summary>
public void Add(string key, long value, IntegerSerialization? serialization = null)
{
var serializationToUse = serialization ?? _serializationSettings.Integer;
if (serializationToUse == IntegerSerialization.String)
_parameters.Add(key, value.ToString(CultureInfo.InvariantCulture));
else if (serializationToUse == IntegerSerialization.Number)
_parameters.Add(key, value);
else
throw new ArgumentException("Unknown Integer serialization setting");
}
/// <summary>
/// Add a decimal value if it is not null
/// </summary>
public void Add(string key, decimal? value, DecimalSerialization? serialization = null)
{
if (value == null)
return;
Add(key, value.Value, serialization);
}
/// <summary>
/// Add a decimal value
/// </summary>
public void Add(string key, decimal value, DecimalSerialization? serialization = null)
{
var serializationToUse = serialization ?? _serializationSettings.Decimal;
if (serializationToUse == DecimalSerialization.String)
_parameters.Add(key, value.ToString(CultureInfo.InvariantCulture));
else if (serializationToUse == DecimalSerialization.Number)
_parameters.Add(key, value);
else
throw new ArgumentException("Unknown Decimal serialization setting");
}
/// <summary>
/// Add a double value if it is not null
/// </summary>
public void Add(string key, double? value, DecimalSerialization? serialization = null)
{
if (value == null)
return;
Add(key, value.Value, serialization);
}
/// <summary>
/// Add a double value
/// </summary>
public void Add(string key, double value, DecimalSerialization? serialization = null)
{
var serializationToUse = serialization ?? _serializationSettings.Decimal;
if (serializationToUse == DecimalSerialization.String)
_parameters.Add(key, value.ToString(CultureInfo.InvariantCulture));
else if (serializationToUse == DecimalSerialization.Number)
_parameters.Add(key, value);
else
throw new ArgumentException("Unknown Decimal serialization setting");
}
/// <summary>
/// Add a bool value if it is not null
/// </summary>
public void Add(string key, bool? value, BoolSerialization? serialization = null)
{
if (value == null)
return;
Add(key, value.Value, serialization);
}
/// <summary>
/// Add a bool value
/// </summary>
public void Add(string key, bool value, BoolSerialization? serialization = null)
{
var serializationToUse = serialization ?? _serializationSettings.Bool;
if (serializationToUse == BoolSerialization.String)
_parameters.Add(key, value.ToString(CultureInfo.InvariantCulture).ToLowerInvariant());
else if (serializationToUse == BoolSerialization.Bool)
_parameters.Add(key, value);
else
throw new ArgumentException("Unknown Bool serialization setting");
}
/// <summary>
/// Add key as comma separated values if there are values provided
/// </summary>
public void AddCommaSeparated(string key, IEnumerable<string>? values)
{
if (values == null || !values.Any())
return;
_parameters.Add(key, string.Join(",", values));
}
/// <summary>
/// Add key as comma separated values
/// </summary>
#if NET5_0_OR_GREATER
public void AddCommaSeparated<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields)] T>(string key, IEnumerable<T> values)
#else
public void AddCommaSeparated<T>(string key, IEnumerable<T>? values)
#endif
where T : struct, Enum
{
if (values == null || !values.Any())
return;
_parameters.Add(key, string.Join(",", values.Select(x => EnumConverter.GetString(x))));
}
/// <summary>
/// Add an enum value if it is not null
/// </summary>
public void Add<
#if NET5_0_OR_GREATER
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields)]
# endif
T>(string key, T? value, EnumSerialization? serialization = null)
where T : struct, Enum
{
if (value == null)
return;
Add(key, value.Value, serialization);
}
/// <summary>
/// Add a enum value
/// </summary>
public void Add<
#if NET5_0_OR_GREATER
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicFields)]
#endif
T>(string key, T value, EnumSerialization? serialization = null)
where T : struct, Enum
{
var serializationToUse = serialization ?? _serializationSettings.Enum;
if (serializationToUse == EnumSerialization.String)
_parameters.Add(key, EnumConverter<T>.GetString(value));
else if (serializationToUse == EnumSerialization.Number)
_parameters.Add(key, int.Parse(EnumConverter<T>.GetString(value), CultureInfo.InvariantCulture));
else
throw new ArgumentException("Unknown Integer serialization setting");
}
/// <summary>
/// Add a DateTime value if it is not null
/// </summary>
public void Add(string key, DateTime? value, DateTimeSerialization? serialization = null)
{
if (value == null)
return;
Add(key, value.Value, serialization);
}
/// <summary>
/// Add a DateTime value
/// </summary>
public void Add(string key, DateTime value, DateTimeSerialization? serialization = null)
{
var serializationToUse = serialization ?? _serializationSettings.DateTimes;
if (serializationToUse == DateTimeSerialization.MillisecondsNumber)
_parameters.Add(key, DateTimeConverter.ConvertToMilliseconds(value));
else if (serializationToUse == DateTimeSerialization.MillisecondsString)
_parameters.Add(key, DateTimeConverter.ConvertToMilliseconds(value).Value.ToString(CultureInfo.InvariantCulture));
else if (serializationToUse == DateTimeSerialization.SecondsNumber)
_parameters.Add(key, DateTimeConverter.ConvertToSeconds(value));
else if (serializationToUse == DateTimeSerialization.SecondsString)
_parameters.Add(key, DateTimeConverter.ConvertToSeconds(value).Value.ToString(CultureInfo.InvariantCulture));
else if (serializationToUse == DateTimeSerialization.MicrosecondsNumber)
_parameters.Add(key, DateTimeConverter.ConvertToMicroseconds(value));
else if (serializationToUse == DateTimeSerialization.MicrosecondsString)
_parameters.Add(key, DateTimeConverter.ConvertToMicroseconds(value).Value.ToString(CultureInfo.InvariantCulture));
else if (serializationToUse == DateTimeSerialization.Rfc3339String)
_parameters.Add(key, value.ToRfc3339String());
else
throw new ArgumentException("Unknown DateTime serialization setting");
}
/// <summary>
/// Add a string value if it is not null
/// </summary>
public void Add(string key, string? value)
{
if (value == null)
return;
_parameters.Add(key, value);
}
/// <summary>
/// Add an array of values if there are values provided
/// </summary>
public void AddArray<T>(string key, IEnumerable<T>? values)
{
if (values == null || !values.Any())
return;
_parameters.Add(key, values is T[] arr ? arr : values.ToArray());
}
/// <summary>
/// Add a raw object value if it is not null
/// </summary>
public void AddRaw(string key, object? value)
{
if (value == null)
return;
_parameters.Add(key, value);
}
/// <inheritdoc />
public void Add(string key, object value) => _parameters.Add(key, value);
/// <inheritdoc />
public bool ContainsKey(string key) => _parameters.ContainsKey(key);
/// <inheritdoc />
public bool Remove(string key) => _parameters.Remove(key);
/// <inheritdoc />
public bool TryGetValue(string key, out object value) => _parameters.TryGetValue(key, out value!);
/// <inheritdoc />
public void Add(KeyValuePair<string, object> item) => _parameters.Add(item.Key, item.Value);
/// <inheritdoc />
public void Clear() => _parameters.Clear();
/// <inheritdoc />
public bool Contains(KeyValuePair<string, object> item) => _parameters.ContainsKey(item.Key) && _parameters[item.Key] == item.Value;
/// <inheritdoc />
public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex) => _parameters.CopyTo(array, arrayIndex);
/// <inheritdoc />
public bool Remove(KeyValuePair<string, object> item) => _parameters.Remove(item.Key);
/// <inheritdoc />
public IEnumerator<KeyValuePair<string, object>> GetEnumerator() => _parameters.GetEnumerator();
/// <inheritdoc />
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}
+14 -1
View File
@@ -33,11 +33,23 @@
/// Centralization type
/// </summary>
public CentralizationType CentralizationType { get; }
/// <summary>
/// Supported environments
/// </summary>
public string[] SupportedEnvironments { get; }
/// <summary>
/// ctor
/// </summary>
public PlatformInfo(string id, string displayName, string logo, string url, string[] apiDocsUrl, PlatformType platformType, CentralizationType centralizationType)
public PlatformInfo(
string id,
string displayName,
string logo,
string url,
string[] apiDocsUrl,
PlatformType platformType,
CentralizationType centralizationType,
string[] supportedEnvironments)
{
Id = id;
DisplayName = displayName;
@@ -46,6 +58,7 @@
ApiDocsUrl = apiDocsUrl;
PlatformType = platformType;
CentralizationType = centralizationType;
SupportedEnvironments = supportedEnvironments;
}
}
}
@@ -9,9 +9,14 @@ namespace CryptoExchange.Net.Objects
public class RequestDefinition
{
private string? _stringRep;
private string? _fullUrl;
// Basics
/// <summary>
/// Base address of the request
/// </summary>
public string BaseAddress { get; set; }
/// <summary>
/// Path of the request
/// </summary>
@@ -77,13 +82,31 @@ namespace CryptoExchange.Net.Objects
/// </summary>
public bool? ForcePathEndWithSlash { get; set; }
/// <summary>
/// Full url, host + path
/// </summary>
public string FullUrl
{
get
{
if (_fullUrl != null)
return _fullUrl;
var result = BaseAddress.AppendPath(Path);
if (ForcePathEndWithSlash == true && !result.EndsWith("/"))
result += "/";
_fullUrl = result;
return _fullUrl;
}
}
/// <summary>
/// ctor
/// </summary>
/// <param name="path"></param>
/// <param name="method"></param>
public RequestDefinition(string path, HttpMethod method)
public RequestDefinition(string baseAddress, string path, HttpMethod method)
{
BaseAddress = baseAddress;
Path = path;
Method = method;
@@ -1,5 +1,6 @@
using CryptoExchange.Net.RateLimiting.Interfaces;
using System.Collections.Concurrent;
using System.IO;
using System.Net.Http;
namespace CryptoExchange.Net.Objects
@@ -15,27 +16,30 @@ namespace CryptoExchange.Net.Objects
/// Get a definition if it is already in the cache or create a new definition and add it to the cache
/// </summary>
/// <param name="method">The HttpMethod</param>
/// <param name="baseAddress">The base address/host</param>
/// <param name="path">Endpoint path</param>
/// <param name="authenticated">Endpoint is authenticated</param>
/// <returns></returns>
public RequestDefinition GetOrCreate(HttpMethod method, string path, bool authenticated = false)
=> GetOrCreate(method, path, null, 0, authenticated, null, null, null, null, null);
public RequestDefinition GetOrCreate(HttpMethod method, string baseAddress, string path, bool authenticated = false)
=> GetOrCreate(method, baseAddress, path, null, 0, authenticated, null, null, null, null, null, null, null);
/// <summary>
/// Get a definition if it is already in the cache or create a new definition and add it to the cache
/// </summary>
/// <param name="method">The HttpMethod</param>
/// <param name="baseAddress">The base address/host</param>
/// <param name="path">Endpoint path</param>
/// <param name="rateLimitGate">The rate limit gate</param>
/// <param name="weight">Request weight</param>
/// <param name="authenticated">Endpoint is authenticated</param>
/// <returns></returns>
public RequestDefinition GetOrCreate(HttpMethod method, string path, IRateLimitGate rateLimitGate, int weight = 1, bool authenticated = false)
=> GetOrCreate(method, path, rateLimitGate, weight, authenticated, null, null, null, null, null);
public RequestDefinition GetOrCreate(HttpMethod method, string baseAddress, string path, IRateLimitGate rateLimitGate, int weight = 1, bool authenticated = false)
=> GetOrCreate(method, baseAddress, path, rateLimitGate, weight, authenticated, null, null, null, null, null, null, null);
/// <summary>
/// Get a definition if it is already in the cache or create a new definition and add it to the cache
/// </summary>
/// <param name="baseAddress">The base address/host</param>
/// <param name="method">The HttpMethod</param>
/// <param name="path">Endpoint path</param>
/// <param name="rateLimitGate">The rate limit gate</param>
@@ -48,9 +52,11 @@ namespace CryptoExchange.Net.Objects
/// <param name="preventCaching">Prevent request caching</param>
/// <param name="tryParseOnNonSuccess">Try parse the response even when status is not success</param>
/// <param name="forcePathEndWithSlash">Force trailing `/`</param>
/// <param name="identifier">Optional request identifier override</param>
/// <returns></returns>
public RequestDefinition GetOrCreate(
HttpMethod method,
string baseAddress,
string path,
IRateLimitGate? rateLimitGate,
int weight,
@@ -61,45 +67,13 @@ namespace CryptoExchange.Net.Objects
ArrayParametersSerialization? arraySerialization = null,
bool? preventCaching = null,
bool? tryParseOnNonSuccess = null,
bool? forcePathEndWithSlash = null)
=> GetOrCreate(method + path, method, path, rateLimitGate, weight, authenticated, limitGuard, requestBodyFormat, parameterPosition, arraySerialization, preventCaching, tryParseOnNonSuccess, forcePathEndWithSlash);
/// <summary>
/// Get a definition if it is already in the cache or create a new definition and add it to the cache
/// </summary>
/// <param name="identifier">Request identifier</param>
/// <param name="method">The HttpMethod</param>
/// <param name="path">Endpoint path</param>
/// <param name="rateLimitGate">The rate limit gate</param>
/// <param name="limitGuard">The rate limit guard for this specific endpoint</param>
/// <param name="weight">Request weight</param>
/// <param name="authenticated">Endpoint is authenticated</param>
/// <param name="requestBodyFormat">Request body format</param>
/// <param name="parameterPosition">Parameter position</param>
/// <param name="arraySerialization">Array serialization type</param>
/// <param name="preventCaching">Prevent request caching</param>
/// <param name="tryParseOnNonSuccess">Try parse the response even when status is not success</param>
/// <param name="forcePathEndWithSlash">Force trailing `/`</param>
/// <returns></returns>
public RequestDefinition GetOrCreate(
string identifier,
HttpMethod method,
string path,
IRateLimitGate? rateLimitGate,
int weight,
bool authenticated,
IRateLimitGuard? limitGuard = null,
RequestBodyFormat? requestBodyFormat = null,
HttpMethodParameterPosition? parameterPosition = null,
ArrayParametersSerialization? arraySerialization = null,
bool? preventCaching = null,
bool? tryParseOnNonSuccess = null,
bool? forcePathEndWithSlash = null)
bool? forcePathEndWithSlash = null,
string? identifier = null)
{
if (!_definitions.TryGetValue(identifier, out var def))
var identifierToUse = identifier ?? $"{path}{method.Method}{baseAddress}";
if (!_definitions.TryGetValue(identifierToUse, out var def))
{
def = new RequestDefinition(path, method)
def = new RequestDefinition(baseAddress, path, method)
{
Authenticated = authenticated,
LimitGuard = limitGuard,
@@ -110,9 +84,9 @@ namespace CryptoExchange.Net.Objects
ParameterPosition = parameterPosition,
PreventCaching = preventCaching ?? false,
TryParseOnNonSuccess = tryParseOnNonSuccess ?? false,
ForcePathEndWithSlash = forcePathEndWithSlash ?? false
ForcePathEndWithSlash = forcePathEndWithSlash ?? false,
};
_definitions.TryAdd(identifier, def);
_definitions.TryAdd(identifierToUse, def);
}
return def;
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using CryptoExchange.Net.Interfaces;
using System.Collections.Generic;
using System.Net.Http;
namespace CryptoExchange.Net.Objects
@@ -12,29 +13,17 @@ namespace CryptoExchange.Net.Objects
private string? _queryString;
/// <summary>
/// Http method
/// The request definition for the request
/// </summary>
public HttpMethod Method { get; set; }
/// <summary>
/// Whether the request needs authentication
/// </summary>
public bool Authenticated { get; set; }
/// <summary>
/// Base address for the request
/// </summary>
public string BaseAddress { get; set; }
/// <summary>
/// The request path
/// </summary>
public string Path { get; set; }
public RequestDefinition RequestDefinition { get; set; }
/// <summary>
/// Query parameters
/// </summary>
public IDictionary<string, object>? QueryParameters { get; set; }
public Parameters? QueryParameters { get; set; }
/// <summary>
/// Body parameters
/// </summary>
public IDictionary<string, object>? BodyParameters { get; set; }
public Parameters? BodyParameters { get; set; }
/// <summary>
/// Request headers
/// </summary>
@@ -57,22 +46,16 @@ namespace CryptoExchange.Net.Objects
/// </summary>
public RestRequestConfiguration(
RequestDefinition requestDefinition,
string baseAddress,
IDictionary<string, object>? queryParams,
IDictionary<string, object>? bodyParams,
Parameters? queryParams,
Parameters? bodyParams,
IDictionary<string, string>? headers,
ArrayParametersSerialization arraySerialization,
HttpMethodParameterPosition parametersPosition,
RequestBodyFormat bodyFormat)
{
Method = requestDefinition.Method;
Authenticated = requestDefinition.Authenticated;
Path = requestDefinition.Path;
BaseAddress = baseAddress;
RequestDefinition = requestDefinition;
QueryParameters = queryParams;
BodyParameters = bodyParams;
Headers = headers;
ArraySerialization = arraySerialization;
ParameterPosition = parametersPosition;
BodyFormat = bodyFormat;
}
@@ -80,15 +63,15 @@ namespace CryptoExchange.Net.Objects
/// <summary>
/// Get the parameter collection based on the ParameterPosition
/// </summary>
public IDictionary<string, object> GetPositionParameters()
public Parameters GetPositionParameters()
{
if (ParameterPosition == HttpMethodParameterPosition.InBody)
{
BodyParameters ??= new Dictionary<string, object>();
BodyParameters ??= new Parameters(ParameterSerializationSettings.Default);
return BodyParameters;
}
QueryParameters ??= new Dictionary<string, object>();
QueryParameters ??= new Parameters(ParameterSerializationSettings.Default);
return QueryParameters;
}
@@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Text;
namespace CryptoExchange.Net.Objects;
/// <summary>
/// Call result
/// </summary>
public record CallResult : ICallResult
{
private static CallResult _successResult = new CallResult();
/// <inheritdoc />
public Error? Error { get; init; }
/// <inheritdoc />
[MemberNotNullWhen(false, nameof(Error))]
public bool Success => Error == null;
/// <summary>
/// Create an error response
/// </summary>
/// <param name="error">The error</param>
public static CallResult Fail(Error error) => new CallResult { Error = error };
/// <summary>
/// Create a success result
/// </summary>
public static CallResult Ok() => _successResult;
/// <summary>
/// Create a success result
/// </summary>
/// <typeparam name="T">Result type</typeparam>
/// <param name="originalData">The original string data</param>
/// <param name="data">Data type</param>
public static CallResult<T> Ok<T>(T data, string? originalData = null) => new CallResult<T> { Data = data, OriginalData = originalData };
/// <summary>
/// Create an error response
/// </summary>
/// <typeparam name="T">Result type</typeparam>
/// <param name="originalData">The original string data</param>
/// <param name="error">The error</param>
public static CallResult<T> Fail<T>(Error error, string? originalData = null) => new CallResult<T> { Error = error, OriginalData = originalData };
/// <inheritdoc />
public override string ToString()
{
return Success ? $"Success" : $"Error: {Error}";
}
}
/// <inheritdoc />
public record CallResult<T> : CallResult, ICallResult<T>
{
/// <inheritdoc />
public new Error? Error
{
get => base.Error;
init => base.Error = value;
}
/// <inheritdoc />
[MemberNotNullWhen(false, nameof(Error))]
[MemberNotNullWhen(true, nameof(Data))]
public new bool Success => Error == null;
/// <summary>
/// The data returned by the call, only available when Success = true
/// </summary>
public T? Data { get; init; }
/// <summary>
/// The original data returned by the call, only available when `OutputOriginalData` is set to `true` in the client options
/// </summary>
public string? OriginalData { get; init; }
/// <summary>
/// Create an error response
/// </summary>
/// <param name="error">The error</param>
/// <param name="originalData">The original string data</param>
public static CallResult<T> Fail(Error error, string? originalData = null) => new CallResult<T> { Error = error, OriginalData = originalData };
/// <summary>
/// Create a success result
/// </summary>
/// <param name="data">The data</param>
/// <param name="originalData">The original string data</param>
/// <returns></returns>
public static CallResult<T> Ok(T data, string? originalData = null) => new CallResult<T> { Data = data, OriginalData = originalData };
}
/// <summary>
/// Call result for an exchange
/// </summary>
/// <typeparam name="T">Data type</typeparam>
public record ExchangeCallResult<T> : CallResult<T>
{
/// <summary>
/// Exchange name
/// </summary>
public string Exchange { get; set; } = string.Empty;
/// <summary>
/// Create an error response
/// </summary>
/// <param name="exchange">The exchange name</param>
/// <param name="error">The error</param>
/// <param name="originalData">The original string data</param>
public static ExchangeCallResult<T> Fail(string exchange, Error error, string? originalData = null) => new ExchangeCallResult<T> { Exchange = exchange, Error = error };
/// <summary>
/// Create a success result
/// </summary>
/// <param name="exchange">The exchange name</param>
/// <param name="data">The data</param>
/// <param name="originalData">The original string data</param>
/// <returns></returns>
public static ExchangeCallResult<T> Ok(string exchange, T data, string? originalData = null) => new ExchangeCallResult<T> { Exchange = exchange, Data = data };
}
@@ -0,0 +1,286 @@
using CryptoExchange.Net.SharedApis;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
namespace CryptoExchange.Net.Objects;
/// <summary>
/// HTTP call result
/// </summary>
public record HttpResult : IHttpResult
{
/// <summary>
/// Create a new success HTTP result
/// </summary>
public static HttpResult<T> Ok<T>(
string exchange,
HttpStatusCode code,
Version version,
HttpResponseHeaders responseHeaders,
TimeSpan elapsed,
long? contentLength,
string? originalData,
int requestId,
string uri,
string? content,
HttpMethod method,
HttpRequestHeaders requestHeaders,
ResultDataSource source,
T data) =>
new HttpResult<T>(exchange, data, null)
{
ResponseStatusCode = code,
HttpVersion = version,
ResponseHeaders = responseHeaders,
ResponseTime = elapsed,
ResponseLength = contentLength,
OriginalData = originalData,
RequestId = requestId,
RequestUrl = uri,
RequestBody = content,
RequestMethod = method,
RequestHeaders = requestHeaders,
DataSource = source,
};
/// <summary>
/// Create a new success HTTP result
/// </summary>
public static HttpResult<T> Ok<T>(IHttpResult result, T data, PageRequest? pageRequest = null) =>
new HttpResult<T>(result.Exchange, data, null)
{
ResponseStatusCode = result.ResponseStatusCode,
HttpVersion = result.HttpVersion,
ResponseHeaders = result.ResponseHeaders,
ResponseTime = result.ResponseTime,
ResponseLength = result.ResponseLength,
OriginalData = result.OriginalData,
RequestId = result.RequestId,
RequestUrl = result.RequestUrl,
RequestBody = result.RequestBody,
RequestMethod = result.RequestMethod,
RequestHeaders = result.RequestHeaders,
DataSource = result.DataSource,
Error = result.Error,
Data = data,
NextPageRequest = pageRequest
};
/// <summary>
/// Create a new error HTTP result
/// </summary>
public static HttpResult<T> Fail<T>(string exchange, Error error) => new HttpResult<T>(exchange, default, error);
/// <summary>
/// Create a new error HTTP result
/// </summary>
public static HttpResult<T> Fail<T>(IHttpResult result, Error? error = null, T? data = default)
=> new HttpResult<T>(result.Exchange, data, error ?? result.Error)
{
ResponseStatusCode = result.ResponseStatusCode,
HttpVersion = result.HttpVersion,
ResponseHeaders = result.ResponseHeaders,
ResponseTime = result.ResponseTime,
ResponseLength = result.ResponseLength,
OriginalData = result.OriginalData,
RequestId = result.RequestId,
RequestUrl = result.RequestUrl,
RequestBody = result.RequestBody,
RequestMethod = result.RequestMethod,
RequestHeaders = result.RequestHeaders,
DataSource = result.DataSource,
};
/// <summary>
/// Create a new error HTTP result
/// </summary>
public static HttpResult<T> Fail<T>(
string exchange,
HttpStatusCode? code,
Version? version,
HttpResponseHeaders? responseHeaders,
TimeSpan elapsed,
long? contentLength,
string? originalData,
int requestId,
string uri,
string? content,
HttpMethod method,
HttpRequestHeaders requestHeaders,
ResultDataSource source,
Error error,
T? result = default) =>
new HttpResult<T>(exchange, result, error)
{
ResponseStatusCode = code,
HttpVersion = version,
ResponseHeaders = responseHeaders,
ResponseTime = elapsed,
ResponseLength = contentLength,
OriginalData = originalData,
RequestId = requestId,
RequestUrl = uri,
RequestBody = content,
RequestMethod = method,
RequestHeaders = requestHeaders,
DataSource = source,
};
/// <summary>
/// Create a new error HTTP result
/// </summary>
public static HttpResult Fail(string exchange, Error error) => new HttpResult() { Exchange = exchange, Error = error };
/// <summary>
/// Create a new error HTTP result
/// </summary>
public static HttpResult Fail(IHttpResult result, Error? error = null)
=> new HttpResult()
{
ResponseStatusCode = result.ResponseStatusCode,
HttpVersion = result.HttpVersion,
ResponseHeaders = result.ResponseHeaders,
ResponseTime = result.ResponseTime,
ResponseLength = result.ResponseLength,
OriginalData = result.OriginalData,
RequestId = result.RequestId,
RequestUrl = result.RequestUrl,
RequestBody = result.RequestBody,
RequestMethod = result.RequestMethod,
RequestHeaders = result.RequestHeaders,
DataSource = result.DataSource,
Exchange = result.Exchange,
Error = error ?? result.Error
};
/// <summary>
/// Create a new success HTTP result
/// </summary>
public static HttpResult Ok(IHttpResult result)
=> new HttpResult()
{
ResponseStatusCode = result.ResponseStatusCode,
HttpVersion = result.HttpVersion,
ResponseHeaders = result.ResponseHeaders,
ResponseTime = result.ResponseTime,
ResponseLength = result.ResponseLength,
OriginalData = result.OriginalData,
RequestId = result.RequestId,
RequestUrl = result.RequestUrl,
RequestBody = result.RequestBody,
RequestMethod = result.RequestMethod,
RequestHeaders = result.RequestHeaders,
DataSource = result.DataSource,
Exchange = result.Exchange,
};
/// <summary>
/// Exchange name
/// </summary>
public string Exchange { get; init; } = string.Empty;
/// <inheritdoc />
public Error? Error { get; internal set; }
/// <inheritdoc />
[MemberNotNullWhen(false, nameof(Error))]
public bool Success => Error == null;
/// <summary>
/// The original data returned by the call, only available when `OutputOriginalData` is set to `true` in the client options
/// </summary>
public string? OriginalData { get; init; }
/// <summary>
/// The request http method
/// </summary>
public HttpMethod? RequestMethod { get; init; }
/// <summary>
/// HTTP protocol version
/// </summary>
public Version? HttpVersion { get; init; }
/// <summary>
/// The headers sent with the request
/// </summary>
public HttpRequestHeaders? RequestHeaders { get; init; }
/// <summary>
/// The request id
/// </summary>
public int? RequestId { get; init; }
/// <summary>
/// The url which was requested
/// </summary>
public string? RequestUrl { get; init; }
/// <summary>
/// The body of the request
/// </summary>
public string? RequestBody { get; init; }
/// <summary>
/// Length in bytes of the response
/// </summary>
public long? ResponseLength { get; init; }
/// <summary>
/// The status code of the response. Note that a OK status does not always indicate success, check the Success parameter for this.
/// </summary>
public HttpStatusCode? ResponseStatusCode { get; init; }
/// <summary>
/// The response headers
/// </summary>
public HttpResponseHeaders? ResponseHeaders { get; init; }
/// <summary>
/// The time between sending the request and receiving the response
/// </summary>
public TimeSpan? ResponseTime { get; init; }
/// <summary>
/// The data source of this result
/// </summary>
public ResultDataSource DataSource { get; init; } = ResultDataSource.Server;
}
/// <inheritdoc />
public record HttpResult<T> : HttpResult, IHttpResult<T>
{
/// <summary>
/// ctor
/// </summary>
public HttpResult(string exchange, T? value, Error? error)
{
Exchange = exchange;
Data = value;
Error = error;
}
/// <inheritdoc />
public new Error? Error
{
get => base.Error;
internal set => base.Error = value;
}
/// <inheritdoc />
[MemberNotNullWhen(false, nameof(Error))]
[MemberNotNullWhen(true, nameof(Data))]
public new bool Success => Error == null;
/// <summary>
/// The data returned by the call, only available when Success = true
/// </summary>
public T? Data { get; init; }
/// <summary>
/// Next page request, only potentially available when using Shared API's
/// </summary>
public PageRequest? NextPageRequest { get; init; }
}
@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Text;
namespace CryptoExchange.Net.Objects;
/// <summary>
/// Call result
/// </summary>
public interface ICallResult
{
/// <summary>
/// An error if the call didn't succeed, will always be filled if Success = false
/// </summary>
Error? Error { get; }
/// <summary>
/// Whether the call was successful
/// </summary>
[MemberNotNullWhen(false, nameof(Error))]
bool Success { get; }
}
/// <summary>
/// Call result
/// </summary>
/// <typeparam name="T">Result data type</typeparam>
public interface ICallResult<T> : ICallResult
{
/// <inheritdoc />
new Error? Error { get; }
/// <inheritdoc />
[MemberNotNullWhen(false, nameof(Error))]
[MemberNotNullWhen(true, nameof(Data))]
new bool Success { get; }
/// <summary>
/// The result data, only available when Success = true
/// </summary>
T? Data { get; }
}
@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
namespace CryptoExchange.Net.Objects
{
/// <summary>
/// HTTP call result
/// </summary>
public interface IHttpResult : ICallResult
{
/// <summary>
/// Exchange name
/// </summary>
string Exchange { get; init; }
/// <summary>
/// The original data returned by the call, only available when `OutputOriginalData` is set to `true` in the client options
/// </summary>
string? OriginalData { get; init; }
/// <summary>
/// The request http method
/// </summary>
HttpMethod? RequestMethod { get; init; }
/// <summary>
/// HTTP protocol version
/// </summary>
Version? HttpVersion { get; init; }
/// <summary>
/// The headers sent with the request
/// </summary>
HttpRequestHeaders? RequestHeaders { get; init; }
/// <summary>
/// The request id
/// </summary>
int? RequestId { get; init; }
/// <summary>
/// The url which was requested
/// </summary>
string? RequestUrl { get; init; }
/// <summary>
/// The body of the request
/// </summary>
string? RequestBody { get; init; }
/// <summary>
/// Length in bytes of the response
/// </summary>
long? ResponseLength { get; init; }
/// <summary>
/// The status code of the response. Note that a OK status does not always indicate success, check the Success parameter for this.
/// </summary>
HttpStatusCode? ResponseStatusCode { get; init; }
/// <summary>
/// The response headers
/// </summary>
HttpResponseHeaders? ResponseHeaders { get; init; }
/// <summary>
/// The time between sending the request and receiving the response
/// </summary>
TimeSpan? ResponseTime { get; init; }
/// <summary>
/// The data source of this result
/// </summary>
ResultDataSource DataSource { get; init; }
}
/// <summary>
/// HTTP call result
/// </summary>
/// <typeparam name="T">Result data type</typeparam>
public interface IHttpResult<T> : IHttpResult, ICallResult<T>
{
}
}
@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
namespace CryptoExchange.Net.Objects
{
/// <summary>
/// WebSocket call result
/// </summary>
public interface IWebSocketResult : ICallResult
{
/// <summary>
/// Exchange name
/// </summary>
string Exchange { get; init; }
/// <summary>
/// The request id
/// </summary>
public int? RequestId { get; init; }
/// <summary>
/// The url which was requested
/// </summary>
public int? ConnectionId { get; init; }
/// <summary>
/// The websocket url
/// </summary>
public string? Url { get; init; }
/// <summary>
/// The time between sending the request and receiving the response
/// </summary>
public TimeSpan? ResponseTime { get; init; }
}
/// <summary>
/// WebSocket call result
/// </summary>
/// <typeparam name="T">Data result type</typeparam>
public interface IWebSocketResult<T> : IWebSocketResult, ICallResult<T>
{
}
/// <summary>
/// Query result
/// </summary>
public interface IQueryResult : IWebSocketResult
{
/// <summary>
/// The original returned data, only available when OutputOriginalData is set to true in the client options
/// </summary>
public string? OriginalData { get; init; }
/// <summary>
/// The query request body
/// </summary>
public string? RequestBody { get; init; }
}
/// <summary>
/// Query result
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IQueryResult<T> : IQueryResult, IWebSocketResult<T>
{
}
}
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.Objects;
/// <summary>
/// Void result
/// </summary>
public readonly struct Unit
{
/// <summary>
/// Void value
/// </summary>
public static readonly Unit Value = default;
/// <summary>
/// Type
/// </summary>
public static Type Type { get; } = typeof(Unit);
}
@@ -0,0 +1,320 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Text;
namespace CryptoExchange.Net.Objects;
/// <summary>
/// WebSocket call result
/// </summary>
public record WebSocketResult : IWebSocketResult
{
/// <summary>
/// ctor
/// </summary>
public WebSocketResult(string exchange, Error? error)
{
Exchange = exchange;
Error = error;
}
/// <summary>
/// Create a new success WebSocket result
/// </summary>
public static WebSocketResult<T> Ok<T>(IWebSocketResult result, T data) =>
new WebSocketResult<T>(result.Exchange, data, null)
{
ConnectionId = result.ConnectionId,
Url = result.Url,
RequestId = result.RequestId,
ResponseTime = result.ResponseTime,
Error = result.Error,
Data = data
};
/// <summary>
/// Create a new success WebSocket result
/// </summary>
public static WebSocketResult<T> Ok<T>(
string exchange,
int connectionId,
TimeSpan elapsed,
int requestId,
string? url,
T data) =>
new WebSocketResult<T>(exchange, data, null)
{
ResponseTime = elapsed,
RequestId = requestId,
ConnectionId = connectionId,
Url = url
};
/// <summary>
/// Create a new success WebSocket result
/// </summary>
public static WebSocketResult Ok(
string exchange,
int connectionId,
TimeSpan elapsed,
int requestId,
string? url) =>
new WebSocketResult(exchange, null)
{
ResponseTime = elapsed,
RequestId = requestId,
ConnectionId = connectionId,
Url = url
};
/// <summary>
/// Create a new error WebSocket result
/// </summary>
public static WebSocketResult Fail(string exchange, Error error) => new WebSocketResult(exchange, error);
/// <summary>
/// Create a new error WebSocket result
/// </summary>
public static WebSocketResult Fail(
string exchange,
int connectionId,
TimeSpan elapsed,
int requestId,
string? url,
Error error) =>
new WebSocketResult(exchange, error)
{
ResponseTime = elapsed,
RequestId = requestId,
ConnectionId = connectionId,
Url = url
};
/// <summary>
/// Create a new error WebSocket result
/// </summary>
public static WebSocketResult<T> Fail<T>(IWebSocketResult result, Error? error = null, T? data = default)
=> new WebSocketResult<T>(result.Exchange, data, error ?? result.Error)
{
ConnectionId = result.ConnectionId,
Url = result.Url,
RequestId = result.RequestId,
ResponseTime = result.ResponseTime,
};
/// <summary>
/// Create a new error WebSocket result
/// </summary>
public static WebSocketResult<T> Fail<T>(string exchange, Error error) => new WebSocketResult<T>(exchange, default, error);
/// <summary>
/// Create a new error WebSocket result
/// </summary>
public static WebSocketResult<T> Fail<T>(
string exchange,
int connectionId,
TimeSpan elapsed,
int requestId,
string? url,
Error error) =>
new WebSocketResult<T>(exchange, default, error)
{
ResponseTime = elapsed,
RequestId = requestId,
ConnectionId = connectionId,
Url = url
};
/// <summary>
/// Exchange name
/// </summary>
public string Exchange { get; init; }
/// <inheritdoc />
public Error? Error { get; init; }
/// <inheritdoc />
[MemberNotNullWhen(false, nameof(Error))]
public bool Success => Error == null;
/// <summary>
/// The request id
/// </summary>
public int? RequestId { get; init; }
/// <summary>
/// The url which was requested
/// </summary>
public int? ConnectionId { get; init; }
/// <summary>
/// The websocket url
/// </summary>
public string? Url { get; init; }
/// <summary>
/// The time between sending the request and receiving the response
/// </summary>
public TimeSpan? ResponseTime { get; init; }
}
/// <inheritdoc />
public record WebSocketResult<T> : WebSocketResult, IWebSocketResult<T>
{
/// <summary>
/// ctor
/// </summary>
public WebSocketResult(string exchange, T? value, Error? error): base(exchange, error)
{
Data = value;
}
/// <inheritdoc />
public new Error? Error
{
get => base.Error;
init => base.Error = value;
}
/// <inheritdoc />
[MemberNotNullWhen(false, nameof(Error))]
[MemberNotNullWhen(true, nameof(Data))]
public new bool Success => Error == null;
/// <summary>
/// The data returned by the call, only available when Success = true
/// </summary>
public T? Data { get; init; }
}
/// <inheritdoc />
public record QueryResult : WebSocketResult
{
/// <summary>
/// ctor
/// </summary>
public QueryResult(string exchange, Error? error) : base(exchange, error)
{
}
/// <summary>
/// Create a new error Query result
/// </summary>
public new static QueryResult Fail(string exchange, Error error) => new QueryResult(exchange, error);
/// <summary>
/// Create a new error WebSocket result
/// </summary>
public static QueryResult Fail(IQueryResult result, Error? error = null)
=> new QueryResult(result.Exchange, error ?? result.Error)
{
ConnectionId = result.ConnectionId,
Url = result.Url,
RequestId = result.RequestId,
ResponseTime = result.ResponseTime,
};
/// <summary>
/// Create a new success query result
/// </summary>
public static QueryResult<T> Ok<T>(
string exchange,
int connectionId,
TimeSpan elapsed,
int requestId,
string? requestBody,
string? url,
string? originalData,
T data) =>
new QueryResult<T>(exchange, data, null)
{
ResponseTime = elapsed,
RequestId = requestId,
RequestBody = requestBody,
ConnectionId = connectionId,
Url = url,
OriginalData = originalData,
};
/// <summary>
/// Create a new success WebSocket result
/// </summary>
public static QueryResult<T> Ok<T>(IQueryResult result, T data) =>
new QueryResult<T>(result.Exchange, data, null)
{
ConnectionId = result.ConnectionId,
Url = result.Url,
RequestId = result.RequestId,
RequestBody = result.RequestBody,
ResponseTime = result.ResponseTime,
Error = result.Error,
OriginalData = result.OriginalData,
Data = data
};
/// <summary>
/// Create a new error WebSocket result
/// </summary>
public static QueryResult<T> Fail<T>(
string exchange,
int connectionId,
TimeSpan elapsed,
int requestId,
string? requestBody,
string? url,
string? originalData,
Error error) =>
new QueryResult<T>(exchange, default, error)
{
ResponseTime = elapsed,
RequestId = requestId,
RequestBody = requestBody,
ConnectionId = connectionId,
OriginalData = originalData,
Url = url
};
/// <summary>
/// Create a new error WebSocket result
/// </summary>
public static QueryResult<T> Fail<T>(IQueryResult result, Error? error = null, T? data = default)
=> new QueryResult<T>(result.Exchange, data, error ?? result.Error)
{
ConnectionId = result.ConnectionId,
Url = result.Url,
RequestId = result.RequestId,
RequestBody = result.RequestBody,
OriginalData = result.OriginalData,
ResponseTime = result.ResponseTime,
};
/// <summary>
/// Create a new error WebSocket result
/// </summary>
public new static QueryResult<T> Fail<T>(string exchange, Error error) => new QueryResult<T>(exchange, default, error);
/// <inheritdoc />
public string? RequestBody { get; init; }
}
/// <inheritdoc />
public record QueryResult<T> : QueryResult, IQueryResult<T>
{
/// <summary>
/// ctor
/// </summary>
public QueryResult(string exchange, T? value, Error? error) : base(exchange, error)
{
Data = value;
}
/// <inheritdoc />
public new Error? Error
{
get => base.Error;
init => base.Error = value;
}
/// <inheritdoc />
[MemberNotNullWhen(false, nameof(Error))]
[MemberNotNullWhen(true, nameof(Data))]
public new bool Success => Error == null;
/// <inheritdoc />
public T? Data { get; set; }
/// <inheritdoc />
public string? OriginalData { get; init; }
}