Skip to content

Commit

Permalink
feat(csharp): add performances playground (#2644)
Browse files Browse the repository at this point in the history
  • Loading branch information
morganleroi authored Feb 7, 2024
1 parent ec644ff commit 832e86a
Show file tree
Hide file tree
Showing 21 changed files with 649 additions and 247 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,65 +1,30 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

namespace Algolia.Search.Models.Common;

/// <summary>
/// Abstract base class for oneOf, anyOf schemas in the API specification
/// </summary>
public abstract class AbstractSchema
namespace Algolia.Search.Models.Common
{
/// <summary>
/// Custom JSON serializer
/// Abstract base class for oneOf, anyOf schemas in the OpenAPI specification
/// </summary>
public static readonly JsonSerializerSettings SerializerSettings = new()
public abstract partial class AbstractSchema
{
// OpenAPI generated types generally hide default constructors.
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
MissingMemberHandling = MissingMemberHandling.Error,
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy
{
OverrideSpecifiedNames = false
}
}
};

/// <summary>
/// Custom JSON serializer for objects with additional properties
/// </summary>
public static readonly JsonSerializerSettings AdditionalPropertiesSerializerSettings = new JsonSerializerSettings
{
// OpenAPI generated types generally hide default constructors.
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
MissingMemberHandling = MissingMemberHandling.Ignore,
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy
{
OverrideSpecifiedNames = false
}
}
};

/// <summary>
/// Gets or Sets the actual instance
/// </summary>
public abstract object ActualInstance { get; set; }
/// <summary>
/// Gets or Sets the actual instance
/// </summary>
public abstract Object ActualInstance { get; set; }

/// <summary>
/// Gets or Sets IsNullable to indicate whether the instance is nullable
/// </summary>
public bool IsNullable { get; protected set; }
/// <summary>
/// Gets or Sets IsNullable to indicate whether the instance is nullable
/// </summary>
public bool IsNullable { get; protected set; }

/// <summary>
/// Gets or Sets the schema type, which can be either `oneOf` or `anyOf`
/// </summary>
public string SchemaType { get; protected set; }
/// <summary>
/// Gets or Sets the schema type, which can be either `oneOf` or `anyOf`
/// </summary>
public string SchemaType { get; protected set; }

/// <summary>
/// Converts the instance into JSON string.
/// </summary>
public abstract string ToJson();
/// <summary>
/// Converts the instance into JSON string.
/// </summary>
public abstract string ToJson();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Algolia.Search.Exceptions;
using Algolia.Search.Models.Common;
Expand All @@ -11,29 +10,28 @@ namespace Algolia.Search.Serializer;

internal class DefaultJsonSerializer : ISerializer
{
private readonly JsonSerializerSettings _serializerSettings;
private readonly ILogger<DefaultJsonSerializer> _logger;

public DefaultJsonSerializer(JsonSerializerSettings serializerSettings, ILoggerFactory logger)
public DefaultJsonSerializer(ILoggerFactory logger)
{
_serializerSettings = serializerSettings;
_logger = logger.CreateLogger<DefaultJsonSerializer>();
}

/// <summary>
/// Serialize the object into a JSON string.
/// </summary>
/// <param name="obj">Object to be serialized.</param>
/// <param name="data">Object to be serialized.</param>
/// <returns>A JSON string.</returns>
public string Serialize(object obj)
public string Serialize(object data)
{
if (obj is AbstractSchema schema)
if (data is AbstractSchema schema)
{
// the object to be serialized is an oneOf/anyOf schema
return schema.ToJson();
var serialize = schema.ToJson();
return serialize;
}

return JsonConvert.SerializeObject(obj, _serializerSettings);
return JsonConvert.SerializeObject(data, JsonConfig.AlgoliaJsonSerializerSettings);
}

public async Task<T> Deserialize<T>(Stream response)
Expand All @@ -50,37 +48,12 @@ public async Task<T> Deserialize<T>(Stream response)
/// <returns>Object representation of the JSON string.</returns>
private async Task<object> Deserialize(Stream response, Type type)
{
if (type == typeof(byte[])) // return a byte array
{
using var reader = new StreamReader(response);
return Encoding.UTF8.GetBytes(await reader.ReadToEndAsync().ConfigureAwait(false));
}

if (type == typeof(Stream))
{
return response;
}

if (type.Name.StartsWith("System.Nullable`1[[System.DateTime")) // return a datetime object
{
using var reader = new StreamReader(response);
var text = await reader.ReadToEndAsync().ConfigureAwait(false);
return DateTime.Parse(text, null, System.Globalization.DateTimeStyles.RoundtripKind);
}

if (type == typeof(string) || type.Name.StartsWith("System.Nullable")) // return primitive type
{
using var reader = new StreamReader(response);
var text = await reader.ReadToEndAsync().ConfigureAwait(false);
return Convert.ChangeType(text, type);
}

// Json Model
try
{
using var reader = new StreamReader(response);
var text = await reader.ReadToEndAsync().ConfigureAwait(false);
return JsonConvert.DeserializeObject(text, type, _serializerSettings);
var readToEndAsync = await reader.ReadToEndAsync().ConfigureAwait(false);
return JsonConvert.DeserializeObject(readToEndAsync, type,
JsonConfig.AlgoliaJsonSerializerSettings);
}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,29 @@ internal static class JsonConfig
{
public const string JsonContentType = "application/json";

public static JsonSerializerSettings AlgoliaJsonSerializerSettings => new()
private static readonly DefaultContractResolver Resolver = new() { NamingStrategy = new CamelCaseNamingStrategy() };

public static readonly JsonSerializerSettings AlgoliaJsonSerializerSettings = new()
{
Formatting = Formatting.None,
NullValueHandling = NullValueHandling.Ignore,
ContractResolver = new DefaultContractResolver { NamingStrategy = new CamelCaseNamingStrategy() },
ContractResolver = Resolver,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
DateParseHandling = DateParseHandling.DateTime,
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
MissingMemberHandling = MissingMemberHandling.Ignore,
};

// When DeserializeOneOfSettings is used, we set MissingMemberHandling to Error to throw an exception if a property is missing
public static readonly JsonSerializerSettings DeserializeOneOfSettings = new()
{
Formatting = Formatting.None,
NullValueHandling = NullValueHandling.Ignore,
ContractResolver = Resolver,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
DateParseHandling = DateParseHandling.DateTime,
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
MissingMemberHandling = MissingMemberHandling.Error,
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;

namespace Algolia.Search.Transport;

/// <summary>
/// https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/enumeration-types
/// Binary enums beware when adding new values
/// </summary>
[Flags]
public enum CallType
{
/// <summary>
/// Read Call
/// </summary>
Read = 1,

/// <summary>
/// Write Call
/// </summary>
Write = 2
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace Algolia.Search.Transport;

/// <summary>
/// Http Scheme
/// </summary>
public enum HttpScheme
{
/// <summary>
/// Http
/// </summary>
Http,
/// <summary>
/// Https
/// </summary>
Https
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public HttpTransport(AlgoliaConfig config, IHttpRequester httpClient, ILoggerFac
_algoliaConfig = config ?? throw new ArgumentNullException(nameof(config));
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
_retryStrategy = new RetryStrategy(config);
_serializer = new DefaultJsonSerializer(JsonConfig.AlgoliaJsonSerializerSettings, loggerFactory);
_serializer = new DefaultJsonSerializer(loggerFactory);
_logger = loggerFactory.CreateLogger<HttpTransport>();
}

Expand Down Expand Up @@ -116,7 +116,7 @@ private async Task<TResult> ExecuteRequestAsync<TResult, TData>(HttpMethod metho
foreach (var host in _retryStrategy.GetTryableHost(callType))
{
request.Body = CreateRequestContent(requestOptions?.Data, request.CanCompress, _logger);
request.Uri = BuildUri(host.Url, uri, requestOptions?.CustomPathParameters, requestOptions?.PathParameters,
request.Uri = BuildUri(host, uri, requestOptions?.CustomPathParameters, requestOptions?.PathParameters,
requestOptions?.QueryParameters);
var requestTimeout =
TimeSpan.FromTicks((requestOptions?.Timeout ?? GetTimeOut(callType)).Ticks * (host.RetryCount + 1));
Expand Down Expand Up @@ -228,13 +228,14 @@ private IDictionary<string, string> GenerateHeaders(IDictionary<string, string>
/// <summary>
/// Build uri depending on the method
/// </summary>
/// <param name="url"></param>
/// <param name="host"></param>
/// <param name="baseUri"></param>
/// <param name="customPathParameters"></param>
/// <param name="pathParameters"></param>
/// <param name="optionalQueryParameters"></param>
/// <returns></returns>
private static Uri BuildUri(string url, string baseUri, IDictionary<string, string> customPathParameters = null,
private static Uri BuildUri(StatefulHost host, string baseUri,
IDictionary<string, string> customPathParameters = null,
IDictionary<string, string> pathParameters = null,
IDictionary<string, string> optionalQueryParameters = null)
{
Expand All @@ -255,13 +256,19 @@ private static Uri BuildUri(string url, string baseUri, IDictionary<string, stri
}
}

if (optionalQueryParameters != null)
var builder = new UriBuilder { Scheme = host.Scheme.ToString(), Host = host.Url, Path = path };

if (optionalQueryParameters != null && optionalQueryParameters.Any())
{
builder.Query = optionalQueryParameters.ToQueryString();
}

if (host.Port.HasValue)
{
var queryParams = optionalQueryParameters.ToQueryString();
return new UriBuilder { Scheme = "https", Host = url, Path = path, Query = queryParams }.Uri;
builder.Port = host.Port.Value;
}

return new UriBuilder { Scheme = "https", Host = url, Path = path }.Uri;
return builder.Uri;
}

/// <summary>
Expand Down
Loading

0 comments on commit 832e86a

Please sign in to comment.