Skip to content

Commit

Permalink
re #18251 Issue_with_logging_of_IDs_as_integers_in_loggly (#9)
Browse files Browse the repository at this point in the history
* re #18251_Issue_with_logging_of_IDs_as_integers_in_loggly

- Have added an overridable SerializerSettings property to the LogglyOptions class with a default value
- Have added a JsonConverter that would convert integer Id properties to string and included it in the default SerializerSettings
- Changed the serializer settings to include Default Value properties
- Have written tests for the FormattedIdJsonConverter

* re #18251_Issue_with_logging_of_IDs_as_integers_in_loggly

- Fixed the CookieContainer Autofixture issue

* re #18251_Issue_with_logging_of_IDs_as_integers_in_loggly

- Fixed the CookieContainer Autofixture failing the integration tests

* re #18251_Issue_with_logging_of_IDs_as_integers_in_loggly

- Deleted a commentary
  • Loading branch information
Dorin-Ciobanu committed Jan 10, 2023
1 parent 00d2285 commit 0386160
Show file tree
Hide file tree
Showing 7 changed files with 267 additions and 17 deletions.
30 changes: 30 additions & 0 deletions src/Logging/Loggly/Loggly/FormattedIdConverter .cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;

public class FormattedIdConverter : JsonConverter
{
private readonly HashSet<Type> IdNumericTypes = new HashSet<Type>
{
typeof(byte), typeof(short), typeof(int), typeof(long),
typeof(sbyte), typeof(ushort), typeof(uint), typeof(ulong),
};

public override bool CanConvert(Type objectType)
{
return IdNumericTypes.Contains(objectType);
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (writer.Path.EndsWith("Id", StringComparison.OrdinalIgnoreCase))
writer.WriteValue(Convert.ToString(value));
else
writer.WriteValue(value);
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
19 changes: 19 additions & 0 deletions src/Logging/Loggly/Loggly/JsonSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Newtonsoft.Json;

namespace EMG.Extensions.Logging.Loggly
{
public static class JsonSettings
{
public static readonly JsonSerializerSettings SerializerSettings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.None,
NullValueHandling = NullValueHandling.Ignore,
Formatting = Formatting.None,
DefaultValueHandling = DefaultValueHandling.Include,
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
DateFormatHandling = DateFormatHandling.IsoDateFormat,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
Converters = { new FormattedIdConverter() }
};
}
}
15 changes: 2 additions & 13 deletions src/Logging/Loggly/Loggly/LogglyHttpClient.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
Expand Down Expand Up @@ -32,7 +31,7 @@ public async Task PublishAsync(LogglyMessage message)
{
using (var request = new HttpRequestMessage(HttpMethod.Post, $"/inputs/{_options.ApiKey}/tag/{_options.Environment}"))
{
var content = JsonConvert.SerializeObject(FixData(message), SerializerSettings);
var content = JsonConvert.SerializeObject(FixData(message), _options.SerializerSettings);
request.Content = new StringContent(content, _options.ContentEncoding, "application/json");

using (var response = await _http.SendAsync(request).ConfigureAwait(false))
Expand Down Expand Up @@ -66,7 +65,7 @@ public async Task PublishManyAsync(IEnumerable<LogglyMessage> messages)
tags.Add(_options.Environment);
request.Headers.Add("X-LOGGLY-TAG", tags);

var fixedMessages = string.Join("\n", messages.Select(FixData).Select(s => JsonConvert.SerializeObject(s, SerializerSettings)));
var fixedMessages = string.Join("\n", messages.Select(FixData).Select(s => JsonConvert.SerializeObject(s, _options.SerializerSettings)));
request.Content = new StringContent(fixedMessages, _options.ContentEncoding, "application/json");

using (var response = await _http.SendAsync(request).ConfigureAwait(false))
Expand Down Expand Up @@ -109,15 +108,5 @@ private static LogglyMessage CloneLogglyEvent(LogglyMessage logglyMessage, objec
Message = logglyMessage.Message
};
}

private static readonly JsonSerializerSettings SerializerSettings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.None,
NullValueHandling = NullValueHandling.Ignore,
Formatting = Formatting.None,
DefaultValueHandling = DefaultValueHandling.Ignore,
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
DateFormatHandling = DateFormatHandling.IsoDateFormat,
};
}
}
3 changes: 3 additions & 0 deletions src/Logging/Loggly/Loggly/LogglyOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Text;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

namespace EMG.Extensions.Logging.Loggly
{
Expand All @@ -28,6 +29,8 @@ public class LogglyOptions
public Encoding ContentEncoding { get; set; } = Encoding.UTF8;

public TimeSpan Buffer { get; set; } = TimeSpan.FromMilliseconds(50);

public JsonSerializerSettings SerializerSettings { get; set; } = JsonSettings.SerializerSettings;
}

public delegate bool FilterDelegate(string categoryName, EventId eventId, LogLevel logLevel);
Expand Down
2 changes: 2 additions & 0 deletions tests/Logging/Tests.Loggly/AutoMoqData.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using AutoFixture;
Expand Down Expand Up @@ -31,6 +32,7 @@ public AutoMoqDataAttribute() : base(() =>
fixture.Customize<LogglyOptions>(o => o
.Without(p => p.LogglyHost)
.Without(p => p.LogglyScheme)
.Without(p => p.SerializerSettings)
.With(p => p.SuppressExceptions, false)
.With(p => p.ContentEncoding, Encoding.UTF8));
Expand Down
196 changes: 196 additions & 0 deletions tests/Logging/Tests.Loggly/FormattedIdConverterTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Newtonsoft.Json;
using Moq;

namespace Tests
{
[TestFixture]
public class FormattedIdConverterTest
{

[Test, AutoMoqData]
public void CanConvert_returns_true_for_integer_types(FormattedIdConverter sut)
{
// Assert
foreach (var intType in integerTypesList)
{
Assert.That(sut.CanConvert(intType), Is.EqualTo(true));
}
}

[Test, AutoMoqData]
public void CanConvert_returns_false_for_non_integer_primitive_types(FormattedIdConverter sut)
{
// Arrange
var frameworkTypes = typeof(Type).Assembly.GetTypes()
.Where(x => x.IsPrimitive).ToList();

var nonIntegerTypesList = frameworkTypes.Except(integerTypesList);

// Assert
foreach (var intType in nonIntegerTypesList)
{
Assert.That(sut.CanConvert(intType), Is.EqualTo(false));
}
}

readonly List<Type> integerTypesList = new List<Type>
{
typeof(byte), typeof(short), typeof(int), typeof(long),
typeof(sbyte), typeof(ushort), typeof(uint), typeof(ulong),
};


[Test, AutoMoqData]
public void WriteJson_calls_WriteValue_method_of_JsonWriter_for_single_value_once_only(FormattedIdConverter sut
, JsonWriter writer
, JsonSerializer serializer
, byte value)
{

// Act
sut.WriteJson(writer, value, serializer);

// Assert
Mock.Get(writer).Verify(i => i.WriteValue(It.IsAny<object>()), Times.Once);
}

[Test, AutoMoqData]
public void WriteJson_calls_WriteValue_method_of_JsonWriter_for_single_value_with_the_right_value(FormattedIdConverter sut
, JsonWriter writer
, JsonSerializer serializer
, byte value)
{

// Act
sut.WriteJson(writer, value, serializer);

// Assert
Mock.Get(writer).Verify(i => i.WriteValue(value), Times.Once);
}

[Test, AutoMoqData]
public void WriteJson_calls_WriteValue_method_of_JsonWriter_for_non_Id_property_once_only(FormattedIdConverter sut
, JsonWriter writer
, JsonSerializer serializer
, string propertyName
, byte value)
{
Assume.That(propertyName, Does.Not.EndWith("Id"));

writer.WriteStartObject();
writer.WritePropertyName(propertyName);

// Act
sut.WriteJson(writer, value, serializer);

// Assert
Mock.Get(writer).Verify(i => i.WriteValue(It.IsAny<object>()), Times.Once);
}

[Test, AutoMoqData]
public void WriteJson_calls_WriteValue_method_of_JsonWriter_for_non_Id_property_with_the_right_value(FormattedIdConverter sut
, JsonWriter writer
, JsonSerializer serializer
, string propertyName
, byte value)
{
Assume.That(propertyName, Does.Not.EndWith("Id"));

writer.WriteStartObject();
writer.WritePropertyName(propertyName);

// Act
sut.WriteJson(writer, value, serializer);

// Assert
Mock.Get(writer).Verify(i => i.WriteValue(value), Times.Once);
}

[Test, AutoMoqData]
public void WriteJson_calls_WriteValue_of_JsonWriter_for_Id_Property_once(FormattedIdConverter sut
, JsonWriter writer
, JsonSerializer serializer
, string propertyName
, byte value)
{
// Arrange
writer.WriteStartObject();
writer.WritePropertyName($"{propertyName}Id");

// Act
sut.WriteJson(writer, value, serializer);

// Assert
Mock.Get(writer).Verify(i => i.WriteValue(Convert.ToString(value)), Times.Once);
}

[Test, AutoMoqData]
public void WriteJson_calls_WriteValue_of_JsonWriter_for_Id_Property_with_string_value(FormattedIdConverter sut
, JsonWriter writer
, JsonSerializer serializer
, string propertyName
, byte value)
{
// Arrange
writer.WriteStartObject();
writer.WritePropertyName($"{propertyName}Id");

// Act
sut.WriteJson(writer, value, serializer);

// Assert
Mock.Get(writer).Verify(i => i.WriteValue(Convert.ToString(value)), Times.Once);
}

[Test, AutoMoqData]
public void WriteJson_checks_PropertyName_CaseInsensitive_Lower(FormattedIdConverter sut
, JsonWriter writer
, JsonSerializer serializer
, string propertyName
, byte value)
{
// Arrange
writer.WriteStartObject();
writer.WritePropertyName($"{propertyName}Id".ToLower());

// Act
sut.WriteJson(writer, value, serializer);

// Assert
Mock.Get(writer).Verify(i => i.WriteValue(Convert.ToString(value)), Times.Once);
}

[Test, AutoMoqData]
public void WriteJson_checks_PropertyName_CaseInsensitive_Upper(FormattedIdConverter sut
, JsonWriter writer
, JsonSerializer serializer
, string propertyName
, byte value)
{
// Arrange
writer.WriteStartObject();
writer.WritePropertyName($"{propertyName}Id".ToUpper());

// Act
sut.WriteJson(writer, value, serializer);

// Assert
Mock.Get(writer).Verify(i => i.WriteValue(Convert.ToString(value)), Times.Once);
}

[Test, AutoMoqData]
public void ReadJson_throws_NotImplementedException(FormattedIdConverter sut
, JsonReader reader
, Type objectType
, object existingValue
, JsonSerializer serializer)
{
// Assert
Assert.Throws<NotImplementedException>(() => sut.ReadJson(reader, objectType, existingValue, serializer));
}
}
}
19 changes: 15 additions & 4 deletions tests/Logging/Tests.Loggly/Loggly/LogglyHttpClientTests.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using AutoFixture;
using AutoFixture.Idioms;
using AutoFixture.NUnit3;
using EMG.Extensions.Logging.Loggly;
using Moq;
using NUnit.Framework;
using WorldDomination.Net.Http;

Expand All @@ -16,9 +14,22 @@ namespace Tests.Loggly
[TestFixture]
public class LogglyHttpClientTests
{
[Test, AutoData]
public void Constructor_is_guarded(GuardClauseAssertion assertion)
[Test, AutoMoqData]
public void Constructor_is_guarded(GuardClauseAssertion assertion, IFixture fixture)
{
var registration = new HttpMessageOptions
{
HttpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK),
HttpMethod = HttpMethod.Post
};

var handler = new FakeHttpMessageHandler(registration);

fixture.Register((LogglyOptions o) => new HttpClient(handler)
{
BaseAddress = new Uri($"{o.LogglyScheme}://{o.LogglyHost}")
});

assertion.Verify(typeof(LogglyHttpClient).GetConstructors());
}

Expand Down

0 comments on commit 0386160

Please sign in to comment.