Kontrola vsech 180 operaci proti falesnemu iDoklad API a round trip 331 modelu SDK v obou smerech. - SdkNullablePropertyConverter doplnuje cteni NullableProperty<T>. Konvertor SDK umi jen zapis, takze PATCH s takovou polozkou koncil prazdnou 500 uz pri cteni tela. Tykalo se 29 modelu. Zapis zustava na konvertoru SDK, odchozi payload se nemeni. - Datum uvnitr NullableProperty se normalizuje na UTC stejne jako zbytek serializace. - ListModifiers parsuje datum ve filtru s AdjustToUniversal a AssumeUniversal. Filtr se zonou se drive posouval o offset. - POST /attachments kontroluje FileName a FileBytes, SDK na ne sahalo bez kontroly na null a vracelo neosetrenou 500. - ExceptionHandlingMiddleware ma posledni zachyt, zadny request uz nekonci prazdnou 500. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
72 lines
2.7 KiB
C#
72 lines
2.7 KiB
C#
using System.Reflection;
|
|
using Newtonsoft.Json;
|
|
using Newtonsoft.Json.Serialization;
|
|
|
|
namespace Idoklad.Infrastructure;
|
|
|
|
/// <summary>
|
|
/// Makes iDoklad SDK models usable in both directions. The SDK only ever serializes what it sends
|
|
/// and deserializes what it receives, so several of its converters, attached through
|
|
/// <see cref="JsonConverterAttribute"/>, implement one direction and throw
|
|
/// <see cref="NotImplementedException"/> in the other. This service needs both directions, so this
|
|
/// resolver swaps those converters for wrappers that fill in the missing half. Everything else is
|
|
/// left to <see cref="DefaultContractResolver"/>.
|
|
/// </summary>
|
|
public sealed class SdkContractResolver : DefaultContractResolver
|
|
{
|
|
/// <summary>
|
|
/// SDK converters that cannot write, so responses carrying such a model would fail.
|
|
/// </summary>
|
|
private static readonly HashSet<string> WriteUnsupportedConverterTypeNames = new(StringComparer.Ordinal)
|
|
{
|
|
// Attached to DateTime members of the invoice, bank account, cash register and similar models.
|
|
"IdokladSdk.Serialization.DateTimeConverter",
|
|
|
|
// Attached to the NotificationListGetModel type itself.
|
|
"IdokladSdk.Serialization.NotificationJsonConverter",
|
|
};
|
|
|
|
/// <summary>
|
|
/// SDK converters that cannot read, so a request body carrying such a member would fail.
|
|
/// </summary>
|
|
private const string NullablePropertyConverterTypeName = "IdokladSdk.Clients.NullablePropertyJsonConverter";
|
|
|
|
protected override JsonContract CreateContract(Type objectType)
|
|
{
|
|
var contract = base.CreateContract(objectType);
|
|
contract.Converter = Replace(contract.Converter);
|
|
return contract;
|
|
}
|
|
|
|
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
|
|
{
|
|
var property = base.CreateProperty(member, memberSerialization);
|
|
property.Converter = Replace(property.Converter);
|
|
|
|
// Collections carry the member converter as the item converter instead.
|
|
property.ItemConverter = Replace(property.ItemConverter);
|
|
|
|
return property;
|
|
}
|
|
|
|
private static JsonConverter? Replace(JsonConverter? converter)
|
|
{
|
|
var typeName = converter?.GetType().FullName;
|
|
|
|
if (typeName is null)
|
|
{
|
|
return converter;
|
|
}
|
|
|
|
if (WriteUnsupportedConverterTypeNames.Contains(typeName))
|
|
{
|
|
return new SdkWriteBypassJsonConverter(converter!);
|
|
}
|
|
|
|
// Attached to NullableProperty<T> members of Patch models; writing works, reading throws.
|
|
return typeName == NullablePropertyConverterTypeName
|
|
? new SdkNullablePropertyConverter(converter!)
|
|
: converter;
|
|
}
|
|
}
|