Skip to content

Commit

Permalink
Fix serialization not accepting JsonObject/Dictionary with null values (
Browse files Browse the repository at this point in the history
#107)

* add null checks to compat apis

* update serialization.net version

* add unit tests

---------

Co-authored-by: Wenxi Zeng <[email protected]>
  • Loading branch information
wenxi-zeng and Wenxi Zeng authored Jun 11, 2024
1 parent d153aff commit 4aa241b
Show file tree
Hide file tree
Showing 4 changed files with 214 additions and 15 deletions.
2 changes: 1 addition & 1 deletion Analytics-CSharp/Analytics-CSharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Coroutine.NET" Version="1.4.0" />
<PackageReference Include="Serialization.NET" Version="1.4.0" />
<PackageReference Include="Serialization.NET" Version="1.4.1" />
<PackageReference Include="Sovran.NET" Version="1.4.0" />
</ItemGroup>
<ItemGroup>
Expand Down
40 changes: 26 additions & 14 deletions Analytics-CSharp/Segment/Analytics/Compat/Migration.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Reflection;
using Segment.Analytics;
using Segment.Serialization;

namespace Segment.Analytics.Compat
Expand Down Expand Up @@ -31,9 +27,13 @@ public static void Track(this Analytics analytics, string userId, string eventNa
public static void Track(this Analytics analytics, string userId, string eventName,
Dictionary<string, object> properties)
{
if (properties == null)
{
properties = new Dictionary<string, object>();
}
properties.Add("userId", userId);
analytics.Track(eventName,
JsonUtility.FromJson<Segment.Serialization.JsonObject>(JsonUtility.ToJson(properties)));
JsonUtility.FromJson<JsonObject>(JsonUtility.ToJson(properties)));
}

[Obsolete("This should only be used if migrating from Analytics.NET or Analytics.Xamarin")]
Expand All @@ -43,36 +43,48 @@ public static void Screen(this Analytics analytics, string userId, string eventN
}

[Obsolete("This should only be used if migrating from Analytics.NET or Analytics.Xamarin")]
public static void Screen(this Analytics analytics, string userId, string eventName,
public static void Screen(this Analytics analytics, string userId, string title,
Dictionary<string, object> properties)
{
if (properties == null)
{
properties = new Dictionary<string, object>();
}
properties.Add("userId", userId);
analytics.Screen(eventName,
JsonUtility.FromJson<Segment.Serialization.JsonObject>(JsonUtility.ToJson(properties)));
analytics.Screen(title,
JsonUtility.FromJson<JsonObject>(JsonUtility.ToJson(properties)));
}

[Obsolete("This should only be used if migrating from Analytics.NET or Analytics.Xamarin")]
public static void Page(this Analytics analytics, string userId, string eventName)
public static void Page(this Analytics analytics, string userId, string title)
{
analytics.Page(eventName, new JsonObject() {{"userId", userId}});
analytics.Page(title, new JsonObject() {{"userId", userId}});
}

[Obsolete("This should only be used if migrating from Analytics.NET or Analytics.Xamarin")]
public static void Page(this Analytics analytics, string userId, string eventName,
public static void Page(this Analytics analytics, string userId, string title,
Dictionary<string, object> properties)
{
if (properties == null)
{
properties = new Dictionary<string, object>();
}
properties.Add("userId", userId);
analytics.Page(eventName,
JsonUtility.FromJson<Segment.Serialization.JsonObject>(JsonUtility.ToJson(properties)));
analytics.Page(title,
JsonUtility.FromJson<JsonObject>(JsonUtility.ToJson(properties)));
}

[Obsolete("This should only be used if migrating from Analytics.NET or Analytics.Xamarin")]
public static void Group(this Analytics analytics, string userId, string groupId,
Dictionary<string, object> traits)
{
if (traits == null)
{
traits = new Dictionary<string, object>();
}
traits.Add("userId", userId);
analytics.Group(groupId,
JsonUtility.FromJson<Segment.Serialization.JsonObject>(JsonUtility.ToJson(traits)));
JsonUtility.FromJson<JsonObject>(JsonUtility.ToJson(traits)));
}

[Obsolete("This should only be used if migrating from Analytics.NET or Analytics.Xamarin")]
Expand Down
182 changes: 182 additions & 0 deletions Tests/Compat/MigrationTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
using System.Collections.Generic;
using Moq;
using Segment.Analytics;
using Segment.Analytics.Utilities;
using Segment.Analytics.Compat;
using Segment.Serialization;
using Tests.Utils;
using Xunit;

namespace Tests.Compat
{
public class MigrationTest
{
private readonly Analytics _analytics;

private Settings? _settings;

private readonly Mock<StubAfterEventPlugin> _plugin;

public MigrationTest()
{
_settings = JsonUtility.FromJson<Settings?>(
"{\"integrations\":{\"Segment.io\":{\"apiKey\":\"1vNgUqwJeCHmqgI9S1sOm9UHCyfYqbaQ\"}},\"plan\":{},\"edgeFunction\":{}}");

var mockHttpClient = new Mock<HTTPClient>(null, null, null);
mockHttpClient
.Setup(httpClient => httpClient.Settings())
.ReturnsAsync(_settings);

_plugin = new Mock<StubAfterEventPlugin>
{
CallBase = true
};

var config = new Configuration(
writeKey: "123",
storageProvider: new DefaultStorageProvider("tests"),
autoAddSegmentDestination: false,
useSynchronizeDispatcher: true,
httpClientProvider: new MockHttpClientProvider(mockHttpClient)
);
_analytics = new Analytics(config);
_analytics.Add(new UserIdPlugin());

Check warning on line 43 in Tests/Compat/MigrationTest.cs

View workflow job for this annotation

GitHub Actions / build

'UserIdPlugin' is obsolete: 'This should only be used if migrating from Analytics.NET or Analytics.Xamarin'
}

[Fact]
public void TestCompatTrackAcceptNullDict()
{
var actual = new List<TrackEvent>();
_plugin.Setup(o => o.Track(Capture.In(actual)));
_analytics.Add(_plugin.Object);
_analytics.Track("user123", "foo", null);

Check warning on line 52 in Tests/Compat/MigrationTest.cs

View workflow job for this annotation

GitHub Actions / build

'AnalyticsExtensions.Track(Analytics, string, string, Dictionary<string, object>)' is obsolete: 'This should only be used if migrating from Analytics.NET or Analytics.Xamarin'

Assert.NotEmpty(actual);
Assert.False(actual[0].Properties.ContainsKey("userId"));
Assert.Equal("user123", actual[0].UserId);
Assert.Equal("foo", actual[0].Event);
}

[Fact]
public void TestCompatTrackAcceptDictWithNulls()
{
var properties = new Dictionary<string, object>
{
["nullValue"] = null
};
var actual = new List<TrackEvent>();
_plugin.Setup(o => o.Track(Capture.In(actual)));
_analytics.Add(_plugin.Object);
_analytics.Track("user123", "foo", properties);

Check warning on line 70 in Tests/Compat/MigrationTest.cs

View workflow job for this annotation

GitHub Actions / build

'AnalyticsExtensions.Track(Analytics, string, string, Dictionary<string, object>)' is obsolete: 'This should only be used if migrating from Analytics.NET or Analytics.Xamarin'

Assert.NotEmpty(actual);
Assert.False(actual[0].Properties.ContainsKey("userId"));
Assert.Equal("user123", actual[0].UserId);
Assert.Equal("foo", actual[0].Event);
Assert.True(actual[0].Properties.ContainsKey("nullValue"));
Assert.Equal(JsonNull.Instance, actual[0].Properties["nullValue"]);
}

[Fact]
public void TestCompatScreenAcceptNullDict()
{
var actual = new List<ScreenEvent>();
_plugin.Setup(o => o.Screen(Capture.In(actual)));
_analytics.Add(_plugin.Object);
_analytics.Screen("user123", "foo", null);

Check warning on line 86 in Tests/Compat/MigrationTest.cs

View workflow job for this annotation

GitHub Actions / build

'AnalyticsExtensions.Screen(Analytics, string, string, Dictionary<string, object>)' is obsolete: 'This should only be used if migrating from Analytics.NET or Analytics.Xamarin'

Assert.NotEmpty(actual);
Assert.False(actual[0].Properties.ContainsKey("userId"));
Assert.Equal("user123", actual[0].UserId);
Assert.Equal("foo", actual[0].Name);
}

[Fact]
public void TestCompatScreenAcceptDictWithNulls()
{
var properties = new Dictionary<string, object>
{
["nullValue"] = null
};
var actual = new List<ScreenEvent>();
_plugin.Setup(o => o.Screen(Capture.In(actual)));
_analytics.Add(_plugin.Object);
_analytics.Screen("user123", "foo", properties);

Check warning on line 104 in Tests/Compat/MigrationTest.cs

View workflow job for this annotation

GitHub Actions / build

'AnalyticsExtensions.Screen(Analytics, string, string, Dictionary<string, object>)' is obsolete: 'This should only be used if migrating from Analytics.NET or Analytics.Xamarin'

Assert.NotEmpty(actual);
Assert.False(actual[0].Properties.ContainsKey("userId"));
Assert.Equal("user123", actual[0].UserId);
Assert.Equal("foo", actual[0].Name);
Assert.True(actual[0].Properties.ContainsKey("nullValue"));
Assert.Equal(JsonNull.Instance, actual[0].Properties["nullValue"]);
}

[Fact]
public void TestCompatPageAcceptNullDict()
{
var actual = new List<PageEvent>();
_plugin.Setup(o => o.Page(Capture.In(actual)));
_analytics.Add(_plugin.Object);
_analytics.Page("user123", "foo", null);

Check warning on line 120 in Tests/Compat/MigrationTest.cs

View workflow job for this annotation

GitHub Actions / build

'AnalyticsExtensions.Page(Analytics, string, string, Dictionary<string, object>)' is obsolete: 'This should only be used if migrating from Analytics.NET or Analytics.Xamarin'

Assert.NotEmpty(actual);
Assert.False(actual[0].Properties.ContainsKey("userId"));
Assert.Equal("user123", actual[0].UserId);
Assert.Equal("foo", actual[0].Name);
}

[Fact]
public void TestCompatPageAcceptDictWithNulls()
{
var properties = new Dictionary<string, object>
{
["nullValue"] = null
};
var actual = new List<PageEvent>();
_plugin.Setup(o => o.Page(Capture.In(actual)));
_analytics.Add(_plugin.Object);
_analytics.Page("user123", "foo", properties);

Check warning on line 138 in Tests/Compat/MigrationTest.cs

View workflow job for this annotation

GitHub Actions / build

'AnalyticsExtensions.Page(Analytics, string, string, Dictionary<string, object>)' is obsolete: 'This should only be used if migrating from Analytics.NET or Analytics.Xamarin'

Assert.NotEmpty(actual);
Assert.False(actual[0].Properties.ContainsKey("userId"));
Assert.Equal("user123", actual[0].UserId);
Assert.Equal("foo", actual[0].Name);
Assert.True(actual[0].Properties.ContainsKey("nullValue"));
Assert.Equal(JsonNull.Instance, actual[0].Properties["nullValue"]);
}

[Fact]
public void TestCompatGroupAcceptNullDict()
{
var actual = new List<GroupEvent>();
_plugin.Setup(o => o.Group(Capture.In(actual)));
_analytics.Add(_plugin.Object);
_analytics.Group("user123", "foo", null);

Check warning on line 154 in Tests/Compat/MigrationTest.cs

View workflow job for this annotation

GitHub Actions / build

'AnalyticsExtensions.Group(Analytics, string, string, Dictionary<string, object>)' is obsolete: 'This should only be used if migrating from Analytics.NET or Analytics.Xamarin'

Assert.NotEmpty(actual);
Assert.False(actual[0].Traits.ContainsKey("userId"));
Assert.Equal("user123", actual[0].UserId);
Assert.Equal("foo", actual[0].GroupId);
}

[Fact]
public void TestCompatGroupAcceptDictWithNulls()
{
var properties = new Dictionary<string, object>
{
["nullValue"] = null
};
var actual = new List<GroupEvent>();
_plugin.Setup(o => o.Group(Capture.In(actual)));
_analytics.Add(_plugin.Object);
_analytics.Group("user123", "foo", properties);

Assert.NotEmpty(actual);
Assert.False(actual[0].Traits.ContainsKey("userId"));
Assert.Equal("user123", actual[0].UserId);
Assert.Equal("foo", actual[0].GroupId);
Assert.True(actual[0].Traits.ContainsKey("nullValue"));
Assert.Equal(JsonNull.Instance, actual[0].Traits["nullValue"]);
}
}
}
5 changes: 5 additions & 0 deletions Tests/Utils/Stubs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ public class StubEventPlugin : EventPlugin
public override PluginType Type => PluginType.Before;
}

public class StubAfterEventPlugin : EventPlugin
{
public override PluginType Type => PluginType.After;
}

public class StubDestinationPlugin : DestinationPlugin
{
public override string Key { get; }
Expand Down

0 comments on commit 4aa241b

Please sign in to comment.