From 709397fdaf21065c0abfc4d6dd95f574849bada7 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Sat, 18 Jan 2025 19:27:29 +0100 Subject: [PATCH] Improve header value. --- Directory.Build.props | 2 +- .../HeaderValueSerializer.cs | 30 ++++++++----- .../EnvelopeHeadersTests.cs | 31 ++++++++++++- events/Squidex.Events.Tests/TestHelpers.cs | 9 +++- events/Squidex.Events/EnvelopeExtensions.cs | 12 ++--- events/Squidex.Events/EventHeaderValue.cs | 45 +++++++++---------- .../Utils/HeaderValueConverter.cs | 31 +++++++------ 7 files changed, 101 insertions(+), 59 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index deb5481..c09ef66 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -11,7 +11,7 @@ https://github.com/squidex/squidex true snupkg - 6.27.0 + 6.28.0 diff --git a/events/Squidex.Events.Mongo/HeaderValueSerializer.cs b/events/Squidex.Events.Mongo/HeaderValueSerializer.cs index e0d62c8..f6b551c 100644 --- a/events/Squidex.Events.Mongo/HeaderValueSerializer.cs +++ b/events/Squidex.Events.Mongo/HeaderValueSerializer.cs @@ -19,13 +19,18 @@ public override HeaderValue Deserialize(BsonDeserializationContext context, Bson switch (reader.CurrentBsonType) { case BsonType.String: - return new HeaderStringValue(reader.ReadString()); + return reader.ReadString(); case BsonType.Int32: - return new HeaderNumberValue(reader.ReadInt32()); + return reader.ReadInt32(); case BsonType.Int64: - return new HeaderNumberValue(reader.ReadInt64()); + return reader.ReadInt64(); + case BsonType.Double: + return reader.ReadDouble(); case BsonType.Boolean: - return new HeaderBooleanValue(reader.ReadBoolean()); + return reader.ReadBoolean(); + case BsonType.Null: + reader.ReadNull(); + return default; default: throw new BsonSerializationException($"Unsupported token '{reader.CurrentBsonType}'."); } @@ -34,16 +39,19 @@ public override HeaderValue Deserialize(BsonDeserializationContext context, Bson public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, HeaderValue value) { var writer = context.Writer; - switch (value) + switch (value.Value) { - case HeaderStringValue s: - writer.WriteString(s.Value); + case string s: + writer.WriteString(s); break; - case HeaderNumberValue n: - writer.WriteInt64(n.Value); + case double n: + writer.WriteDouble(n); break; - case HeaderBooleanValue b: - writer.WriteBoolean(b.Value); + case bool b: + writer.WriteBoolean(b); + break; + case null: + writer.WriteNull(); break; default: throw new BsonSerializationException($"Unsupported value type '{value.GetType()}'."); diff --git a/events/Squidex.Events.Tests/EnvelopeHeadersTests.cs b/events/Squidex.Events.Tests/EnvelopeHeadersTests.cs index 5fe4c81..dc25a17 100644 --- a/events/Squidex.Events.Tests/EnvelopeHeadersTests.cs +++ b/events/Squidex.Events.Tests/EnvelopeHeadersTests.cs @@ -6,6 +6,7 @@ // ========================================================================== using FakeItEasy; +using MongoDB.Bson; using MongoDB.Bson.Serialization; using Squidex.Events.Mongo; using Xunit; @@ -211,7 +212,8 @@ public void Should_serialize_and_deserialize_to_json_bytes() ["key1"] = 13, ["key2"] = "Hello World", ["key3"] = true, - ["key4"] = false + ["key4"] = false, + ["key5"] = default }; var json = source.SerializeToJsonBytes(); @@ -229,7 +231,8 @@ public void Should_serialize_and_deserialize_bson() ["key1"] = 13, ["key2"] = "Hello World", ["key3"] = true, - ["key4"] = false + ["key4"] = false, + ["key5"] = default }; var deserialized = source.SerializeAndDeserializeBson(); @@ -237,6 +240,30 @@ public void Should_serialize_and_deserialize_bson() CompareHeaders(deserialized, source); } + [Fact] + public void Should_serialize_and_deserialize_bson_numbers() + { + var source = new BsonDocument + { + ["number1"] = 100, + ["number2"] = 200L, + ["number3"] = 300.5f, + ["number4"] = 400.5d + }; + + var expected = new EnvelopeHeaders + { + ["number1"] = 100, + ["number2"] = 200, + ["number3"] = 300.5, + ["number4"] = 400.5 + }; + + var deserialized = source.SerializeAndDeserializeBson(); + + CompareHeaders(deserialized, expected); + } + private static void CompareHeaders(EnvelopeHeaders lhs, EnvelopeHeaders rhs) { foreach (var key in lhs.Keys.Concat(rhs.Keys).Distinct()) diff --git a/events/Squidex.Events.Tests/TestHelpers.cs b/events/Squidex.Events.Tests/TestHelpers.cs index 323e540..1a9bdff 100644 --- a/events/Squidex.Events.Tests/TestHelpers.cs +++ b/events/Squidex.Events.Tests/TestHelpers.cs @@ -35,19 +35,24 @@ public sealed class ObjectHolder } public static T SerializeAndDeserializeBson(this T value) + { + return SerializeAndDeserializeBson(value); + } + + public static TOut SerializeAndDeserializeBson(this TIn value) { using var stream = new MemoryStream(); using (var writer = new BsonBinaryWriter(stream)) { - BsonSerializer.Serialize(writer, new ObjectHolder { Value = value }); + BsonSerializer.Serialize(writer, new ObjectHolder { Value = value }); } stream.Position = 0; using (var reader = new BsonBinaryReader(stream)) { - return BsonSerializer.Deserialize>(reader).Value; + return BsonSerializer.Deserialize>(reader).Value; } } } diff --git a/events/Squidex.Events/EnvelopeExtensions.cs b/events/Squidex.Events/EnvelopeExtensions.cs index 3ee9e2d..48df101 100644 --- a/events/Squidex.Events/EnvelopeExtensions.cs +++ b/events/Squidex.Events/EnvelopeExtensions.cs @@ -20,7 +20,7 @@ public static DateTime GetDateTime(this EnvelopeHeaders obj, string key) { if (obj.TryGetValue(key, out var found)) { - if (found is HeaderStringValue s && DateTime.TryParse(s.Value, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var dateTime)) + if (found.Value is string s && DateTime.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var dateTime)) { return dateTime; } @@ -33,12 +33,12 @@ public static long GetLong(this EnvelopeHeaders obj, string key) { if (obj.TryGetValue(key, out var found)) { - if (found is HeaderNumberValue n) + if (found.Value is double n) { - return n.Value; + return (long)n; } - if (found is HeaderStringValue s && double.TryParse(s.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result)) + if (found.Value is string s && double.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out var result)) { return (long)result; } @@ -59,9 +59,9 @@ public static string GetString(this EnvelopeHeaders obj, string key) public static bool GetBoolean(this EnvelopeHeaders obj, string key) { - if (obj.TryGetValue(key, out var found) && found is HeaderBooleanValue b) + if (obj.TryGetValue(key, out var found) && found.Value is bool b) { - return b.Value; + return b; } return false; diff --git a/events/Squidex.Events/EventHeaderValue.cs b/events/Squidex.Events/EventHeaderValue.cs index 8fd4753..efb7a09 100644 --- a/events/Squidex.Events/EventHeaderValue.cs +++ b/events/Squidex.Events/EventHeaderValue.cs @@ -6,48 +6,45 @@ // ========================================================================== #pragma warning disable MA0048 // File name must match type name -#pragma warning disable SA1313 // Parameter names should begin with lower-case letter namespace Squidex.Events; -public abstract record HeaderValue +public readonly record struct HeaderValue { - public static implicit operator HeaderValue(string source) - { - return new HeaderStringValue(source); - } + public object? Value { get; } - public static implicit operator HeaderValue(long source) + private HeaderValue(object? value) { - return new HeaderNumberValue(source); + Value = value; } - public static implicit operator HeaderValue(bool source) + public static implicit operator HeaderValue(string source) { - return new HeaderBooleanValue(source); + return new HeaderValue(source); } -} -public record HeaderBooleanValue(bool Value) : HeaderValue -{ - public override string ToString() + public static implicit operator HeaderValue(double source) { - return Value ? "true" : "false"; + return new HeaderValue(source); } -} -public record HeaderNumberValue(long Value) : HeaderValue -{ - public override string ToString() + public static implicit operator HeaderValue(bool source) { - return Value.ToString(); + return new HeaderValue(source); } -} -public record HeaderStringValue(string Value) : HeaderValue -{ public override string ToString() { - return Value; + switch (Value) + { + case null: + return "null"; + case true: + return "true"; + case false: + return "false"; + default: + return Value.ToString()!; + } } } diff --git a/events/Squidex.Events/Utils/HeaderValueConverter.cs b/events/Squidex.Events/Utils/HeaderValueConverter.cs index 04a7609..e5b8840 100644 --- a/events/Squidex.Events/Utils/HeaderValueConverter.cs +++ b/events/Squidex.Events/Utils/HeaderValueConverter.cs @@ -12,18 +12,20 @@ namespace Squidex.Events.Utils; public sealed class HeaderValueConverter : JsonConverter { - public override HeaderValue? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override HeaderValue Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { switch (reader.TokenType) { case JsonTokenType.String: - return new HeaderStringValue(reader.GetString()!); + return reader.GetString()!; case JsonTokenType.Number: - return new HeaderNumberValue(reader.GetInt64()); + return reader.GetDouble(); + case JsonTokenType.Null: + return default; case JsonTokenType.True: - return new HeaderBooleanValue(true); + return true; case JsonTokenType.False: - return new HeaderBooleanValue(false); + return false; default: throw new JsonException($"Unsupported token '{reader.TokenType}'."); } @@ -31,19 +33,22 @@ public sealed class HeaderValueConverter : JsonConverter public override void Write(Utf8JsonWriter writer, HeaderValue value, JsonSerializerOptions options) { - switch (value) + switch (value.Value) { - case HeaderStringValue s: - writer.WriteStringValue(s.Value); + case string s: + writer.WriteStringValue(s); break; - case HeaderNumberValue n: - writer.WriteNumberValue(n.Value); + case double n: + writer.WriteNumberValue(n); break; - case HeaderBooleanValue b: - writer.WriteBooleanValue(b.Value); + case bool b: + writer.WriteBooleanValue(b); + break; + case null: + writer.WriteNullValue(); break; default: - throw new JsonException($"Unsupported value type '{value.GetType()}'."); + throw new JsonException($"Unsupported value type '{value.Value.GetType()}'."); } } }