using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
///
/// Options for an exchange endpoint
///
public abstract class EndpointOptions
{
///
/// Required exchange-specific parameters. These can be provided via the `exchangeParameters` property of the request object.
///
public List RequiredExchangeParameters { get; set; } = new List();
///
/// Optional exchange-specific parameters. These can be provided via the `exchangeParameters` property of the request object.
///
public List OptionalExchangeParameters { get; set; } = new List();
///
/// Exchange
///
public string Exchange { get; set; }
///
/// Endpoint name
///
public string EndpointName { get; set; }
///
/// Exchange specific additional info
///
public string? RequestNotes { get; set; }
///
/// Whether the call requires authentication
///
public bool NeedsAuthentication { get; set; }
///
/// Whether the call is supported. If false the exchange API does not support this operation.
///
public bool Supported { get; set; } = true;
///
/// ctor
///
public EndpointOptions(string exchange, string endpointName, bool needAuthentication)
{
Exchange = exchange;
EndpointName = endpointName;
NeedsAuthentication = needAuthentication;
}
///
/// Validate a request
///
/// Provided exchange parameters
/// Request trading mode
/// Supported trading modes
///
public virtual Error? ValidateRequest(ExchangeParameters? exchangeParameters, TradingMode? tradingMode, TradingMode[] supportedTradingModes)
{
if (!Supported)
return ArgumentError.Invalid("Endpoint", $"Endpoint {Exchange} {EndpointName} is not supported by the API");
if (tradingMode != null && !supportedTradingModes.Contains(tradingMode.Value))
return ArgumentError.Invalid("TradingMode", $"TradingMode.{tradingMode} is not supported, supported types: {string.Join(", ", supportedTradingModes)}");
foreach (var param in RequiredExchangeParameters)
{
if (param.Names!.All(x => ExchangeParameters.HasValue(exchangeParameters, Exchange, x, param.ValueType) != true))
return ArgumentError.Invalid(string.Join("/", param.Names!), $"One of exchange parameters `{string.Join(", ", param.Names!)}` for exchange `{Exchange}` should be provided. Example: {param.ExampleValue}");
}
return null;
}
}
///
/// Options for an exchange endpoint
///
/// Type of data
#if NET5_0_OR_GREATER
public class EndpointOptions<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] TRequest> : EndpointOptions
where TRequest : SharedRequest
#else
public abstract class EndpointOptions : EndpointOptions
where TRequest : SharedRequest
#endif
{
///
/// ctor
///
public EndpointOptions(string exchange, bool needsAuthentication, string requestName) : base(exchange, requestName, needsAuthentication)
{
}
}
///
/// Options for an exchange endpoint
///
/// Type of data
/// Type of the client
#if NET5_0_OR_GREATER
public abstract class EndpointOptions<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] TRequest, TClient> : EndpointOptions
where TRequest : SharedRequest
where TClient : ISharedClient
#else
public abstract class EndpointOptions : EndpointOptions
where TRequest : SharedRequest
where TClient : ISharedClient
#endif
{
private static PropertyInfo[] _requestProperties = typeof(TRequest).GetProperties();
///
/// Required optional parameters in the request. These can be provided via the `exchangeParameters` property of the request object.
///
public List RequiredOptionalParameters { get; set; } = new List();
///
/// Whether this accepts multiple symbols (Only applicable to request requiring symbol parameters)
///
public bool SupportsMultipleSymbols { get; set; } = false;
///
/// The max number of symbols which can be passed in a call (Only applicable to request requiring symbol parameters)
///
public int? MaxSymbolCount { get; set; }
///
/// ctor
///
public EndpointOptions(string exchange, bool needsAuthentication, string requestName) : base(exchange, needsAuthentication, requestName)
{
}
///
/// Validate a request
///
/// The request
/// Containing client
///
public virtual Error? ValidateRequest(TRequest request, TClient client)
{
if (NeedsAuthentication && !client.Authenticated)
return new NoApiCredentialsError();
foreach (var param in RequiredOptionalParameters)
{
if (param.Names!.All(x => _requestProperties.Single(p => p.Name == x).GetValue(request, null) == null))
return ArgumentError.Invalid(string.Join("/", param.Names!), $"One of optional parameters `{string.Join(", ", param.Names!)}` for exchange `{Exchange}` should be provided. Example: {param.ExampleValue}");
}
if (request is SharedSymbolRequest symbolsRequest)
{
if (symbolsRequest.Symbols != null)
{
if (!SupportsMultipleSymbols)
return ArgumentError.Invalid(nameof(SharedSymbolRequest.Symbols), $"Only a single symbol parameter is allowed, multiple symbols are not supported");
if (symbolsRequest.Symbols.Length > MaxSymbolCount)
return ArgumentError.Invalid(nameof(SharedSymbolRequest.Symbols), $"Max number of symbols is {MaxSymbolCount} but {symbolsRequest.Symbols.Length} were passed");
}
}
return ValidateRequest(request.ExchangeParameters, request.TradingMode, client.SupportedTradingModes);
}
///
public override string ToString()
{
if (!Supported)
return $"{Exchange} {EndpointName} NOT SUPPORTED";
var sb = new StringBuilder();
sb.AppendLine($"{Exchange} {EndpointName}");
sb.AppendLine($"Needs authentication: {NeedsAuthentication}");
if (!string.IsNullOrEmpty(RequestNotes))
sb.AppendLine(RequestNotes);
if (RequiredOptionalParameters.Any())
{
sb.AppendLine($"Required optional parameters:");
foreach(var param in RequiredOptionalParameters)
sb.AppendLine($" {param}");
}
if (RequiredExchangeParameters.Any())
{
sb.AppendLine($"Required exchange specific parameters:");
foreach (var param in RequiredExchangeParameters)
sb.AppendLine($" {param}");
}
if (OptionalExchangeParameters.Any())
{
sb.AppendLine($"Optional exchange specific parameters:");
foreach (var param in OptionalExchangeParameters)
sb.AppendLine($" {param}");
}
return sb.ToString();
}
}
}