Skip to content

Commit

Permalink
Merge pull request #433 from ably/feature/422-refactor-message-extras
Browse files Browse the repository at this point in the history
Refactor MessageExtras
  • Loading branch information
Quintin Willison committed Sep 7, 2020
2 parents 344d1d2 + 28e8fe4 commit 16df9a1
Show file tree
Hide file tree
Showing 6 changed files with 309 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var extrasToken = JToken.Load(reader);
return new MessageExtras(extrasToken);
return MessageExtras.From(extrasToken);
}

public override bool CanConvert(Type objectType)
Expand Down
34 changes: 21 additions & 13 deletions src/IO.Ably.Shared/Types/MessageExtras.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using IO.Ably.CustomSerialisers;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

Expand All @@ -6,33 +7,51 @@ namespace IO.Ably.Types
/// <summary>
/// Extra properties on the Message.
/// </summary>
[JsonConverter(typeof(MessageExtrasConverter))]
public class MessageExtras
{
private const string DeltaProperty = "delta";

/// <summary>
/// Data holds actual extra information associated with message.
/// </summary>
[JsonIgnore]
private JToken Data { get; }

/// <summary>
/// Delta extras is part of the Ably delta's functionality.
/// </summary>
[JsonIgnore]
public DeltaExtras Delta { get; }

/// <summary>
/// Messages extras is a flexible object that may other properties that are not exposed by the strongly typed implementation.
/// </summary>
/// <param name="data">the json object passed to Message extras.</param>
public MessageExtras(JToken data = null)
: this(data, null)
{
}

private MessageExtras(JToken data, DeltaExtras delta)
{
Data = data;
Delta = delta;
}

internal static MessageExtras From(JToken data = null)
{
DeltaExtras delta = null;
if (data != null && data is JObject dataObject)
{
var deltaProp = dataObject[DeltaProperty];
if (deltaProp != null && deltaProp is JObject deltaObject)
{
Delta = deltaObject.ToObject<DeltaExtras>();
delta = deltaObject.ToObject<DeltaExtras>();
}
}

return new MessageExtras(data, delta);
}

/// <summary>
Expand All @@ -41,18 +60,7 @@ public MessageExtras(JToken data = null)
/// <returns>returns the inner json.</returns>
public JToken ToJson()
{
if (Data == null && Delta == null)
{
return null;
}

var result = Data?.DeepClone() ?? new JObject();
if (Delta != null)
{
result[DeltaProperty] = JObject.FromObject(Delta);
}

return result;
return Data?.DeepClone();
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,48 +1,144 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using IO.Ably.CustomSerialisers;
using IO.Ably.Tests.Shared.Helpers;
using IO.Ably.Types;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using Xunit;
using Xunit.Abstractions;

namespace IO.Ably.Tests.DotNetCore20.CustomSerializers
{
public class MessageExtrasConverterTests
{
public JsonSerializerSettings JsonSettings
private readonly ITestOutputHelper _testOutputHelper;
public JsonSerializerSettings JsonSettings = JsonHelper.Settings;

public MessageExtrasConverterTests(ITestOutputHelper testOutputHelper)
{
get
{
JsonSerializerSettings res = new JsonSerializerSettings();
res.Converters = new List<JsonConverter>()
{
new MessageExtrasConverter(),
};
res.DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind;
res.NullValueHandling = NullValueHandling.Ignore;
res.ContractResolver = new CamelCasePropertyNamesContractResolver();
return res;
}
_testOutputHelper = testOutputHelper;
}

[Fact]
[Trait("spec ", "tm2i")]
public void Should_parse_MessageExtras_json_correctly()
public void ShouldParse_MessageExtrasJson()
{
var json = "{ \"random\":\"boo\", \"delta\":{ \"From\": \"1\", \"Format\":\"best\" } }";
var originalJObject = JObject.Parse(json);
var json = @"
{
'random':'boo',
'delta': {
'From': '1',
'Format':'best'
}
}";
var originalJToken = JToken.Parse(json);
var messageExtras = JsonConvert.DeserializeObject<MessageExtras>(json, JsonSettings);

messageExtras.Delta.Should().NotBeNull();
messageExtras.Delta.From.Should().Be("1");
messageExtras.Delta.Format.Should().Be("best");

((string)messageExtras.ToJson()["random"]).Should().Be("boo");

var serialized = JsonConvert.SerializeObject(messageExtras, JsonSettings);
var serializedJToken = JToken.Parse(serialized);

JAssert.DeepEquals(serializedJToken, originalJToken, _testOutputHelper).Should().Be(true);
}

[Fact]
[Trait("spec ", "tm2i")]
public void ShouldParse_MessageExtrasJson_WithEmptyDelta()
{
var json = @"{
'random':'boo',
'foo':'fooValue',
'bar':'barValue',
'object' : {
'key1': 'value1',
'key2': 'value2'
}
}";
var originalJToken = JToken.Parse(json);
var messageExtras = JsonConvert.DeserializeObject<MessageExtras>(json, JsonSettings);
((string)messageExtras.ToJson()["random"]).Should().Be("boo");
((string)messageExtras.ToJson()["foo"]).Should().Be("fooValue");
((string)messageExtras.ToJson()["bar"]).Should().Be("barValue");
((string)messageExtras.ToJson()["object"]["key1"]).Should().Be("value1");
((string)messageExtras.ToJson()["object"]["key2"]).Should().Be("value2");

var serialized = JsonConvert.SerializeObject(messageExtras, JsonSettings);
var serializedJToken = JToken.Parse(serialized);
JAssert.DeepEquals(serializedJToken, originalJToken, _testOutputHelper).Should().Be(true);
}

[Fact]
[Trait("spec ", "tm2i")]
public void ShouldParse_MessageExtrasJson_WithDelta()
{
var json = @"{
'delta': {
'From': '1',
'Format':'best'
}
}";
var originalJToken = JToken.Parse(json);
var messageExtras = JsonConvert.DeserializeObject<MessageExtras>(json, JsonSettings);

messageExtras.Delta.Should().NotBeNull();
messageExtras.Delta.From.Should().Be("1");
messageExtras.Delta.Format.Should().Be("best");

var serialized = JsonConvert.SerializeObject(messageExtras, JsonSettings);
var serializedJObject = JObject.Parse(serialized);
JToken.DeepEquals(serializedJObject, originalJObject).Should().BeTrue();
var serializedJToken = JToken.Parse(serialized);
JAssert.DeepEquals(serializedJToken, originalJToken, _testOutputHelper).Should().Be(true);
}

[Fact]
[Trait("spec ", "tm2i")]
public void ShouldParse_Message_WithNullMessageExtras()
{
var json = @"{
'id':'UniqueId',
'clientId':'clientId',
'connectionId':'connectionId',
'name':'connectionName',
'data':'data',
'encoding':'encoding',
'extras': null
}";

var messageObject = JsonConvert.DeserializeObject<Message>(json, JsonSettings);
messageObject.Extras.Should().BeNull();
var serialized = JsonConvert.SerializeObject(messageObject, JsonSettings);
var serializedJToken = JToken.Parse(serialized);
serializedJToken.Contains("extras").Should().Be(false);
}

[Fact]
[Trait("spec ", "tm2i")]
public void ShouldParse_Message_WithArbitraryMessageExtras()
{
var json = @"{
'id':'UniqueId',
'clientId':'clientId',
'connectionId':'connectionId',
'name':'connectionName',
'data':'data',
'extras': 'extraData',
'encoding':'encoding'
}";

var messageObject = JsonConvert.DeserializeObject<Message>(json, JsonSettings);
messageObject.Extras.Delta.Should().BeNull();
messageObject.Extras.ToJson().ToString().Should().Be("extraData");
var serialized = JsonConvert.SerializeObject(messageObject, JsonSettings);
var serializedJToken = JToken.Parse(serialized);
serializedJToken.Contains("extras").Should().Be(false);
}
}
}
24 changes: 24 additions & 0 deletions src/IO.Ably.Tests.Shared/Helpers/JAssert.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;
using Newtonsoft.Json.Linq;
using Xunit.Abstractions;

namespace IO.Ably.Tests.Shared.Helpers
{
internal class JAssert
{
// todo: upgrade testing library - https://github.com/fluentassertions/fluentassertions.json/issues/7
// https://stackoverflow.com/questions/52645603/how-to-compare-two-json-objects-using-c-sharp

public static bool DeepEquals(JToken token1, JToken token2, ITestOutputHelper testOutputHelper)
{
var areEqual = JToken.DeepEquals(token1, token2);
if (!areEqual)
{
var diff = JDiff.Differentiate(token1, token2);
testOutputHelper.WriteLine($"Json Difference {diff}");
}

return areEqual;
}
}
}
Loading

0 comments on commit 16df9a1

Please sign in to comment.