mirror of
https://github.com/JKorf/CryptoExchange.Net
synced 2025-12-16 02:41:03 +00:00
wip
This commit is contained in:
parent
07fdee1740
commit
839ebc4ca9
@ -17,6 +17,7 @@ using System.Linq;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -98,8 +99,6 @@ namespace CryptoExchange.Net.Clients
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected abstract IRestMessageHandler MessageHandler { get; }
|
protected abstract IRestMessageHandler MessageHandler { get; }
|
||||||
|
|
||||||
private static MediaTypeWithQualityHeaderValue AcceptJsonContent = new MediaTypeWithQualityHeaderValue(Constants.JsonContentHeader);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ctor
|
/// ctor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -327,6 +326,9 @@ namespace CryptoExchange.Net.Clients
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check rate limits for the request
|
||||||
|
/// </summary>
|
||||||
protected virtual async Task<Error?> RateLimitAsync(
|
protected virtual async Task<Error?> RateLimitAsync(
|
||||||
string host,
|
string host,
|
||||||
int requestId,
|
int requestId,
|
||||||
@ -412,8 +414,7 @@ namespace CryptoExchange.Net.Clients
|
|||||||
|
|
||||||
var uri = new Uri(baseAddress.AppendPath(definition.Path) + queryString);
|
var uri = new Uri(baseAddress.AppendPath(definition.Path) + queryString);
|
||||||
var request = RequestFactory.Create(ClientOptions.HttpVersion, definition.Method, uri, requestId);
|
var request = RequestFactory.Create(ClientOptions.HttpVersion, definition.Method, uri, requestId);
|
||||||
#warning Should be configurable
|
request.Accept = MessageHandler.AcceptHeader;
|
||||||
request.Accept = AcceptJsonContent;
|
|
||||||
|
|
||||||
if (requestConfiguration.Headers != null)
|
if (requestConfiguration.Headers != null)
|
||||||
{
|
{
|
||||||
@ -473,7 +474,23 @@ namespace CryptoExchange.Net.Clients
|
|||||||
response = await request.GetResponseAsync(cancellationToken).ConfigureAwait(false);
|
response = await request.GetResponseAsync(cancellationToken).ConfigureAwait(false);
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
responseStream = await response.GetResponseStreamAsync().ConfigureAwait(false);
|
responseStream = await response.GetResponseStreamAsync().ConfigureAwait(false);
|
||||||
|
string? originalData = null;
|
||||||
var outputOriginalData = ApiOptions.OutputOriginalData ?? ClientOptions.OutputOriginalData;
|
var outputOriginalData = ApiOptions.OutputOriginalData ?? ClientOptions.OutputOriginalData;
|
||||||
|
if (outputOriginalData)
|
||||||
|
{
|
||||||
|
// If we want to return the original string data from the stream, but still want to process it
|
||||||
|
// we'll need to copy it as the stream isn't seekable, and thus we can only read it once
|
||||||
|
var memoryStream = new MemoryStream();
|
||||||
|
await responseStream.CopyToAsync(memoryStream).ConfigureAwait(false);
|
||||||
|
using var reader = new StreamReader(memoryStream, Encoding.UTF8,false, 4096, true);
|
||||||
|
memoryStream.Position = 0;
|
||||||
|
originalData = await reader.ReadToEndAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
// Continue processing from the memory stream since the response stream is already read and we can't seek it
|
||||||
|
responseStream.Close();
|
||||||
|
memoryStream.Position = 0;
|
||||||
|
responseStream = memoryStream;
|
||||||
|
}
|
||||||
|
|
||||||
if (!response.IsSuccessStatusCode && !requestDefinition.TryParseOnNonSuccess)
|
if (!response.IsSuccessStatusCode && !requestDefinition.TryParseOnNonSuccess)
|
||||||
{
|
{
|
||||||
@ -502,12 +519,12 @@ namespace CryptoExchange.Net.Clients
|
|||||||
responseStream).ConfigureAwait(false);
|
responseStream).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, response.ContentLength, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, error);
|
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, response.ContentLength, originalData, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof(T) == typeof(object))
|
if (typeof(T) == typeof(object))
|
||||||
// Success status code and expected empty response, assume it's correct
|
// Success status code and expected empty response, assume it's correct
|
||||||
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, 0, null/*accessor.OriginalDataAvailable ? accessor.GetOriginalString() : "[Data only available when OutputOriginal = true in client options]"*/, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, null);
|
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, 0, originalData, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, null);
|
||||||
|
|
||||||
// Data response received
|
// Data response received
|
||||||
var parsedError = await MessageHandler.CheckForErrorResponse(
|
var parsedError = await MessageHandler.CheckForErrorResponse(
|
||||||
@ -527,11 +544,11 @@ namespace CryptoExchange.Net.Clients
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Success status code, but TryParseError determined it was an error response
|
// Success status code, but TryParseError determined it was an error response
|
||||||
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, response.ContentLength, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, parsedError);
|
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, response.ContentLength, originalData, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, default, parsedError);
|
||||||
}
|
}
|
||||||
|
|
||||||
var (deserializeResult, deserializeError) = await MessageHandler.TryDeserializeAsync<T>(responseStream, state, cancellationToken).ConfigureAwait(false);
|
var (deserializeResult, deserializeError) = await MessageHandler.TryDeserializeAsync<T>(responseStream, state, cancellationToken).ConfigureAwait(false);
|
||||||
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, response.ContentLength, null, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, deserializeResult, deserializeError);
|
return new WebCallResult<T>(response.StatusCode, response.HttpVersion, response.ResponseHeaders, sw.Elapsed, response.ContentLength, originalData, request.RequestId, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), ResultDataSource.Server, deserializeResult, deserializeError);
|
||||||
}
|
}
|
||||||
catch (HttpRequestException requestException)
|
catch (HttpRequestException requestException)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -14,6 +14,15 @@ namespace CryptoExchange.Net.Converters.MessageParsing.DynamicConverters
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IRestMessageHandler
|
public interface IRestMessageHandler
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The `accept` HTTP response header for the request
|
||||||
|
/// </summary>
|
||||||
|
MediaTypeWithQualityHeaderValue AcceptHeader { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create an object to keep state for a request
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
object? CreateState();
|
object? CreateState();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -4,26 +4,41 @@ using System.Text;
|
|||||||
|
|
||||||
namespace CryptoExchange.Net.Converters.MessageParsing.DynamicConverters
|
namespace CryptoExchange.Net.Converters.MessageParsing.DynamicConverters
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Message evaluator
|
||||||
|
/// </summary>
|
||||||
public class MessageEvaluator
|
public class MessageEvaluator
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The priority of this evaluator, higher prio evaluators (with lower Priority number) will be checked for matches before lower ones
|
||||||
|
/// </summary>
|
||||||
public int Priority { get; set; }
|
public int Priority { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to immediately match the evaluator when it is matched. Can only be used when the evaluator has a single unique field to look for
|
||||||
|
/// </summary>
|
||||||
public bool ForceIfFound { get; set; }
|
public bool ForceIfFound { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The fields this evaluator has to look for
|
||||||
|
/// </summary>
|
||||||
public MessageFieldReference[] Fields { get; set; }
|
public MessageFieldReference[] Fields { get; set; }
|
||||||
|
/// <summary>
|
||||||
public Func<SearchResult, string> IdentifyMessageCallback { get; set; }
|
/// The callback for getting the identifier string
|
||||||
|
/// </summary>
|
||||||
|
public Func<SearchResult, string>? IdentifyMessageCallback { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The static identifier string to return when this evaluator is matched
|
||||||
|
/// </summary>
|
||||||
public string? StaticIdentifier { get; set; }
|
public string? StaticIdentifier { get; set; }
|
||||||
|
|
||||||
public string? IdentifyMessage(SearchResult result)
|
internal string? IdentifyMessage(SearchResult result)
|
||||||
{
|
{
|
||||||
if (StaticIdentifier != null)
|
if (StaticIdentifier != null)
|
||||||
return StaticIdentifier;
|
return StaticIdentifier;
|
||||||
|
|
||||||
return IdentifyMessageCallback(result);
|
return IdentifyMessageCallback!(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Statisfied(SearchResult result)
|
internal bool Statisfied(SearchResult result)
|
||||||
{
|
{
|
||||||
foreach(var field in Fields)
|
foreach(var field in Fields)
|
||||||
{
|
{
|
||||||
@ -34,90 +49,4 @@ namespace CryptoExchange.Net.Converters.MessageParsing.DynamicConverters
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class MessageFieldReference
|
|
||||||
{
|
|
||||||
public string SearchName { get; set; }
|
|
||||||
public int Depth { get; set; } = 1;
|
|
||||||
|
|
||||||
public Func<string?, bool>? Constraint { get; set; }
|
|
||||||
|
|
||||||
public MessageFieldReference(string searchName)
|
|
||||||
{
|
|
||||||
SearchName = searchName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class PropertyFieldReference : MessageFieldReference
|
|
||||||
{
|
|
||||||
public byte[] PropertyName { get; set; }
|
|
||||||
public bool ArrayValues { get; set; }
|
|
||||||
|
|
||||||
public PropertyFieldReference(string propertyName) : base(propertyName)
|
|
||||||
{
|
|
||||||
PropertyName = Encoding.UTF8.GetBytes(propertyName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ArrayFieldReference : MessageFieldReference
|
|
||||||
{
|
|
||||||
public int ArrayIndex { get; set; }
|
|
||||||
|
|
||||||
public ArrayFieldReference(string searchName, int depth, int index) : base(searchName)
|
|
||||||
{
|
|
||||||
Depth = depth;
|
|
||||||
ArrayIndex = index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MessageEvalutorFieldReference
|
|
||||||
{
|
|
||||||
public bool SkipReading { get; set; }
|
|
||||||
public bool OverlappingField { get; set; }
|
|
||||||
public MessageFieldReference Field { get; set; }
|
|
||||||
public MessageEvaluator? ForceEvaluator { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SearchResult
|
|
||||||
{
|
|
||||||
private List<SearchResultItem> _items = new List<SearchResultItem>();
|
|
||||||
|
|
||||||
public string FieldValue(string searchName)
|
|
||||||
{
|
|
||||||
foreach(var item in _items)
|
|
||||||
{
|
|
||||||
if (item.Field.SearchName.Equals(searchName, StringComparison.Ordinal))
|
|
||||||
return item.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Exception(""); // TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Count => _items.Count;
|
|
||||||
|
|
||||||
public void Clear() => _items.Clear();
|
|
||||||
|
|
||||||
public bool Contains(MessageFieldReference field)
|
|
||||||
{
|
|
||||||
foreach(var item in _items)
|
|
||||||
{
|
|
||||||
if (item.Field == field)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Write(MessageFieldReference field, string? value) => _items.Add(new SearchResultItem
|
|
||||||
{
|
|
||||||
Field = field,
|
|
||||||
Value = value
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct SearchResultItem
|
|
||||||
{
|
|
||||||
public MessageFieldReference Field { get; set; }
|
|
||||||
public string? Value { get; set; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace CryptoExchange.Net.Converters.MessageParsing.DynamicConverters
|
||||||
|
{
|
||||||
|
internal class MessageEvalutorFieldReference
|
||||||
|
{
|
||||||
|
public bool SkipReading { get; set; }
|
||||||
|
public bool OverlappingField { get; set; }
|
||||||
|
public MessageFieldReference Field { get; set; }
|
||||||
|
public MessageEvaluator? ForceEvaluator { get; set; }
|
||||||
|
|
||||||
|
public MessageEvalutorFieldReference(MessageFieldReference field)
|
||||||
|
{
|
||||||
|
Field = field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,77 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace CryptoExchange.Net.Converters.MessageParsing.DynamicConverters
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Reference to a message field
|
||||||
|
/// </summary>
|
||||||
|
public abstract class MessageFieldReference
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The name for this search field
|
||||||
|
/// </summary>
|
||||||
|
public string SearchName { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The depth at which to look for this field
|
||||||
|
/// </summary>
|
||||||
|
public int Depth { get; set; } = 1;
|
||||||
|
/// <summary>
|
||||||
|
/// Callback to check if the field value matches an expected constraint
|
||||||
|
/// </summary>
|
||||||
|
public Func<string?, bool>? Constraint { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
public MessageFieldReference(string searchName)
|
||||||
|
{
|
||||||
|
SearchName = searchName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reference to a property message field
|
||||||
|
/// </summary>
|
||||||
|
public class PropertyFieldReference : MessageFieldReference
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The property name in the JSON
|
||||||
|
/// </summary>
|
||||||
|
public byte[] PropertyName { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the property value is array values
|
||||||
|
/// </summary>
|
||||||
|
public bool ArrayValues { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
public PropertyFieldReference(string propertyName) : base(propertyName)
|
||||||
|
{
|
||||||
|
PropertyName = Encoding.UTF8.GetBytes(propertyName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reference to an array message field
|
||||||
|
/// </summary>
|
||||||
|
public class ArrayFieldReference : MessageFieldReference
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The index in the array
|
||||||
|
/// </summary>
|
||||||
|
public int ArrayIndex { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ctor
|
||||||
|
/// </summary>
|
||||||
|
public ArrayFieldReference(string searchName, int depth, int index) : base(searchName)
|
||||||
|
{
|
||||||
|
Depth = depth;
|
||||||
|
ArrayIndex = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace CryptoExchange.Net.Converters.MessageParsing.DynamicConverters
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The results of a search for fields in a JSON message
|
||||||
|
/// </summary>
|
||||||
|
public class SearchResult
|
||||||
|
{
|
||||||
|
private List<SearchResultItem> _items = new List<SearchResultItem>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the value of a field
|
||||||
|
/// </summary>
|
||||||
|
public string? FieldValue(string searchName)
|
||||||
|
{
|
||||||
|
foreach (var item in _items)
|
||||||
|
{
|
||||||
|
if (item.Field.SearchName.Equals(searchName, StringComparison.Ordinal))
|
||||||
|
return item.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception(""); // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The number of found search field values
|
||||||
|
/// </summary>
|
||||||
|
public int Count => _items.Count;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clear the search result
|
||||||
|
/// </summary>
|
||||||
|
public void Clear() => _items.Clear();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the value for a specific field was found
|
||||||
|
/// </summary>
|
||||||
|
public bool Contains(MessageFieldReference field)
|
||||||
|
{
|
||||||
|
foreach (var item in _items)
|
||||||
|
{
|
||||||
|
if (item.Field == field)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Write a value to the result
|
||||||
|
/// </summary>
|
||||||
|
public void Write(MessageFieldReference field, string? value) => _items.Add(new SearchResultItem
|
||||||
|
{
|
||||||
|
Field = field,
|
||||||
|
Value = value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace CryptoExchange.Net.Converters.MessageParsing.DynamicConverters
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Search result value
|
||||||
|
/// </summary>
|
||||||
|
public struct SearchResultItem
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The field the values is for
|
||||||
|
/// </summary>
|
||||||
|
public MessageFieldReference Field { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The value of the field
|
||||||
|
/// </summary>
|
||||||
|
public string? Value { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -27,6 +27,8 @@ namespace CryptoExchange.Net.Converters.SystemTextJson.MessageConverters
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class JsonRestMessageHandler : IRestMessageHandler
|
public abstract class JsonRestMessageHandler : IRestMessageHandler
|
||||||
{
|
{
|
||||||
|
private static MediaTypeWithQualityHeaderValue _acceptJsonContent = new MediaTypeWithQualityHeaderValue(Constants.JsonContentHeader);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Empty rate limit error
|
/// Empty rate limit error
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -37,6 +39,10 @@ namespace CryptoExchange.Net.Converters.SystemTextJson.MessageConverters
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract JsonSerializerOptions Options { get; }
|
public abstract JsonSerializerOptions Options { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public MediaTypeWithQualityHeaderValue AcceptHeader => _acceptJsonContent;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public virtual object CreateState() => new JsonDocState();
|
public virtual object CreateState() => new JsonDocState();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -75,6 +81,9 @@ namespace CryptoExchange.Net.Converters.SystemTextJson.MessageConverters
|
|||||||
HttpResponseHeaders responseHeaders,
|
HttpResponseHeaders responseHeaders,
|
||||||
Stream responseStream) => new ValueTask<Error?>((Error?)null);
|
Stream responseStream) => new ValueTask<Error?>((Error?)null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Read the response into a JsonDocument object
|
||||||
|
/// </summary>
|
||||||
protected virtual async ValueTask<(Error?, JsonDocument?)> GetJsonDocument(Stream stream, object? state)
|
protected virtual async ValueTask<(Error?, JsonDocument?)> GetJsonDocument(Stream stream, object? state)
|
||||||
{
|
{
|
||||||
if (state is JsonDocState documentState && documentState.Document != null)
|
if (state is JsonDocState documentState && documentState.Document != null)
|
||||||
@ -106,7 +115,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson.MessageConverters
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result = await JsonSerializer.DeserializeAsync<T>(responseStream, Options)!.ConfigureAwait(false);
|
result = await JsonSerializer.DeserializeAsync<T>(responseStream, Options)!.ConfigureAwait(false)!;
|
||||||
}
|
}
|
||||||
return (result, null);
|
return (result, null);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -101,12 +101,11 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_searchFields.Add(new MessageEvalutorFieldReference
|
_searchFields.Add(new MessageEvalutorFieldReference(field)
|
||||||
{
|
{
|
||||||
SkipReading = evaluator.IdentifyMessageCallback == null && field.Constraint == null,
|
SkipReading = evaluator.IdentifyMessageCallback == null && field.Constraint == null,
|
||||||
ForceEvaluator = !existingSameSearchField.Any() ? (evaluator.ForceIfFound ? evaluator : null) : null,
|
ForceEvaluator = !existingSameSearchField.Any() ? (evaluator.ForceIfFound ? evaluator : null) : null,
|
||||||
OverlappingField = overlapping.Any(),
|
OverlappingField = overlapping.Any()
|
||||||
Field = field
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (field.Depth > _maxSearchDepth)
|
if (field.Depth > _maxSearchDepth)
|
||||||
|
|||||||
@ -21,10 +21,6 @@ namespace CryptoExchange.Net.Interfaces
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public MessageMatcher MessageMatcher { get; }
|
public MessageMatcher MessageMatcher { get; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The types the message processor deserializes to
|
|
||||||
/// </summary>
|
|
||||||
public HashSet<Type> DeserializationTypes { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Handle a message
|
/// Handle a message
|
||||||
/// </summary>
|
/// </summary>
|
||||||
CallResult Handle(SocketConnection connection, DateTime receiveTime, string? originalData, object result, MessageHandlerLink matchedHandler);
|
CallResult Handle(SocketConnection connection, DateTime receiveTime, string? originalData, object result, MessageHandlerLink matchedHandler);
|
||||||
|
|||||||
@ -8,7 +8,8 @@ namespace CryptoExchange.Net.Objects.Options
|
|||||||
public class ApiOptions
|
public class ApiOptions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If true, the CallResult and DataEvent objects will also include the originally received data in the OriginalData property
|
/// If true, the CallResult and DataEvent objects will also include the originally received string data in the OriginalData property.
|
||||||
|
/// Note that this comes at a performance cost
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool? OutputOriginalData { get; set; }
|
public bool? OutputOriginalData { get; set; }
|
||||||
|
|
||||||
|
|||||||
@ -14,7 +14,8 @@ namespace CryptoExchange.Net.Objects.Options
|
|||||||
public ApiProxy? Proxy { get; set; }
|
public ApiProxy? Proxy { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If true, the CallResult and DataEvent objects will also include the originally received data in the OriginalData property
|
/// If true, the CallResult and DataEvent objects will also include the originally received string data in the OriginalData property.
|
||||||
|
/// Note that this comes at a performance cost
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool OutputOriginalData { get; set; } = false;
|
public bool OutputOriginalData { get; set; } = false;
|
||||||
|
|
||||||
|
|||||||
@ -56,9 +56,9 @@ namespace CryptoExchange.Net.Objects.Sockets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public class DataEvent<T> : DataEvent
|
public class DataEvent<T> : DataEvent
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The received data deserialized into an object
|
/// The received data deserialized into an object
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -120,8 +120,6 @@ namespace CryptoExchange.Net.Objects.Sockets
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Copy the DataEvent to a new data type
|
/// Copy the DataEvent to a new data type
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="exchange">The exchange the result is for</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public ExchangeEvent<K> AsExchangeEvent<K>(string exchange, K data)
|
public ExchangeEvent<K> AsExchangeEvent<K>(string exchange, K data)
|
||||||
{
|
{
|
||||||
return new ExchangeEvent<K>(exchange, this, data)
|
return new ExchangeEvent<K>(exchange, this, data)
|
||||||
|
|||||||
@ -41,7 +41,6 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
|
|
||||||
private ClientWebSocket _socket;
|
private ClientWebSocket _socket;
|
||||||
private CancellationTokenSource _ctsSource;
|
private CancellationTokenSource _ctsSource;
|
||||||
private DateTime _lastReceivedMessagesUpdate;
|
|
||||||
private Task? _processTask;
|
private Task? _processTask;
|
||||||
private Task? _closeTask;
|
private Task? _closeTask;
|
||||||
private bool _stopRequested;
|
private bool _stopRequested;
|
||||||
|
|||||||
@ -59,22 +59,11 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// Response
|
/// Response
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public object? Response { get; set; }
|
public object? Response { get; set; }
|
||||||
#warning check if there is a better solution for this in combination with the MessageMatcher
|
|
||||||
public HashSet<Type> DeserializationTypes { get; set; }
|
|
||||||
|
|
||||||
private MessageMatcher _matcher;
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Matcher for this subscription
|
/// Matcher for this subscription
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MessageMatcher MessageMatcher
|
public MessageMatcher MessageMatcher { get; set; }
|
||||||
{
|
|
||||||
get => _matcher;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_matcher = value;
|
|
||||||
DeserializationTypes = new HashSet<Type>(MessageMatcher.HandlerLinks.Select(x => x.DeserializationType));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The query request object
|
/// The query request object
|
||||||
|
|||||||
@ -35,8 +35,6 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool UserSubscription { get; set; }
|
public bool UserSubscription { get; set; }
|
||||||
|
|
||||||
public HashSet<Type> DeserializationTypes { get; set; }
|
|
||||||
|
|
||||||
private SubscriptionStatus _status;
|
private SubscriptionStatus _status;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Current subscription status
|
/// Current subscription status
|
||||||
@ -74,20 +72,10 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Authenticated { get; }
|
public bool Authenticated { get; }
|
||||||
|
|
||||||
|
|
||||||
private MessageMatcher _matcher;
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Matcher for this subscription
|
/// Matcher for this subscription
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MessageMatcher MessageMatcher
|
public MessageMatcher MessageMatcher { get; set; }
|
||||||
{
|
|
||||||
get => _matcher;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_matcher = value;
|
|
||||||
DeserializationTypes = new HashSet<Type>(MessageMatcher.HandlerLinks.Select(x => x.DeserializationType));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Cancellation token registration
|
/// Cancellation token registration
|
||||||
|
|||||||
@ -17,8 +17,6 @@ namespace CryptoExchange.Net.Testing
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get a client instance
|
/// Get a client instance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="loggerFactory"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public abstract TClient GetClient(ILoggerFactory loggerFactory, bool newDeserialization);
|
public abstract TClient GetClient(ILoggerFactory loggerFactory, bool newDeserialization);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -59,14 +57,15 @@ namespace CryptoExchange.Net.Testing
|
|||||||
/// Execute a REST endpoint call and check for any errors or warnings.
|
/// Execute a REST endpoint call and check for any errors or warnings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">Type of response</typeparam>
|
/// <typeparam name="T">Type of response</typeparam>
|
||||||
|
/// <param name="useNewDeserialization">Whether to use the new deserialization method</param>
|
||||||
/// <param name="expression">The call expression</param>
|
/// <param name="expression">The call expression</param>
|
||||||
/// <param name="authRequest">Whether this is an authenticated request</param>
|
/// <param name="authRequest">Whether this is an authenticated request</param>
|
||||||
public async Task RunAndCheckResult<T>(bool newDeserialization, Expression<Func<TClient, Task<WebCallResult<T>>>> expression, bool authRequest)
|
public async Task RunAndCheckResult<T>(bool useNewDeserialization, Expression<Func<TClient, Task<WebCallResult<T>>>> expression, bool authRequest)
|
||||||
{
|
{
|
||||||
if (!ShouldRun())
|
if (!ShouldRun())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var client = CreateClient(newDeserialization);
|
var client = CreateClient(useNewDeserialization);
|
||||||
|
|
||||||
var expressionBody = (MethodCallExpression)expression.Body;
|
var expressionBody = (MethodCallExpression)expression.Body;
|
||||||
if (authRequest && !Authenticated)
|
if (authRequest && !Authenticated)
|
||||||
|
|||||||
@ -19,8 +19,6 @@ namespace CryptoExchange.Net.Testing
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get a client instance
|
/// Get a client instance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="loggerFactory"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public abstract TClient GetClient(ILoggerFactory loggerFactory, bool newDeserialization);
|
public abstract TClient GetClient(ILoggerFactory loggerFactory, bool newDeserialization);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -61,6 +59,7 @@ namespace CryptoExchange.Net.Testing
|
|||||||
/// Execute a REST endpoint call and check for any errors or warnings.
|
/// Execute a REST endpoint call and check for any errors or warnings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">Type of the update</typeparam>
|
/// <typeparam name="T">Type of the update</typeparam>
|
||||||
|
/// <param name="useNewDeserialization">Whether to use the new deserialization method</param>
|
||||||
/// <param name="expression">The call expression</param>
|
/// <param name="expression">The call expression</param>
|
||||||
/// <param name="expectUpdate">Whether an update is expected</param>
|
/// <param name="expectUpdate">Whether an update is expected</param>
|
||||||
/// <param name="authRequest">Whether this is an authenticated request</param>
|
/// <param name="authRequest">Whether this is an authenticated request</param>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user