From d153aff8d90a81bdb477b885b70bc95fd0436217 Mon Sep 17 00:00:00 2001 From: Wenxi Zeng Date: Tue, 14 May 2024 11:22:00 -0500 Subject: [PATCH 1/3] prepare release 2.4.1 (#105) Co-authored-by: Wenxi Zeng --- Analytics-CSharp/Analytics-CSharp.csproj | 2 +- Analytics-CSharp/Segment/Analytics/Version.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Analytics-CSharp/Analytics-CSharp.csproj b/Analytics-CSharp/Analytics-CSharp.csproj index 0b02328..5efd3d2 100644 --- a/Analytics-CSharp/Analytics-CSharp.csproj +++ b/Analytics-CSharp/Analytics-CSharp.csproj @@ -10,7 +10,7 @@ Segment, Inc The hassle-free way to add analytics to your C# app. - 2.4.0 + 2.4.1 MIT https://github.com/segmentio/Analytics-CSharp git diff --git a/Analytics-CSharp/Segment/Analytics/Version.cs b/Analytics-CSharp/Segment/Analytics/Version.cs index aa1261f..a6ab55c 100644 --- a/Analytics-CSharp/Segment/Analytics/Version.cs +++ b/Analytics-CSharp/Segment/Analytics/Version.cs @@ -2,6 +2,6 @@ namespace Segment.Analytics { internal static class Version { - internal const string SegmentVersion = "2.4.0"; + internal const string SegmentVersion = "2.4.1"; } } From 4aa241b2402f67e670063bcb2a5e0d65b1040296 Mon Sep 17 00:00:00 2001 From: Wenxi Zeng Date: Tue, 11 Jun 2024 17:12:12 -0500 Subject: [PATCH 2/3] Fix serialization not accepting JsonObject/Dictionary with null values (#107) * add null checks to compat apis * update serialization.net version * add unit tests --------- Co-authored-by: Wenxi Zeng --- Analytics-CSharp/Analytics-CSharp.csproj | 2 +- .../Segment/Analytics/Compat/Migration.cs | 40 ++-- Tests/Compat/MigrationTest.cs | 182 ++++++++++++++++++ Tests/Utils/Stubs.cs | 5 + 4 files changed, 214 insertions(+), 15 deletions(-) create mode 100644 Tests/Compat/MigrationTest.cs diff --git a/Analytics-CSharp/Analytics-CSharp.csproj b/Analytics-CSharp/Analytics-CSharp.csproj index 5efd3d2..dea0150 100644 --- a/Analytics-CSharp/Analytics-CSharp.csproj +++ b/Analytics-CSharp/Analytics-CSharp.csproj @@ -39,7 +39,7 @@ - + diff --git a/Analytics-CSharp/Segment/Analytics/Compat/Migration.cs b/Analytics-CSharp/Segment/Analytics/Compat/Migration.cs index e629abf..b925764 100644 --- a/Analytics-CSharp/Segment/Analytics/Compat/Migration.cs +++ b/Analytics-CSharp/Segment/Analytics/Compat/Migration.cs @@ -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 @@ -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 properties) { + if (properties == null) + { + properties = new Dictionary(); + } properties.Add("userId", userId); analytics.Track(eventName, - JsonUtility.FromJson(JsonUtility.ToJson(properties))); + JsonUtility.FromJson(JsonUtility.ToJson(properties))); } [Obsolete("This should only be used if migrating from Analytics.NET or Analytics.Xamarin")] @@ -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 properties) { + if (properties == null) + { + properties = new Dictionary(); + } properties.Add("userId", userId); - analytics.Screen(eventName, - JsonUtility.FromJson(JsonUtility.ToJson(properties))); + analytics.Screen(title, + JsonUtility.FromJson(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 properties) { + if (properties == null) + { + properties = new Dictionary(); + } properties.Add("userId", userId); - analytics.Page(eventName, - JsonUtility.FromJson(JsonUtility.ToJson(properties))); + analytics.Page(title, + JsonUtility.FromJson(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 traits) { + if (traits == null) + { + traits = new Dictionary(); + } traits.Add("userId", userId); analytics.Group(groupId, - JsonUtility.FromJson(JsonUtility.ToJson(traits))); + JsonUtility.FromJson(JsonUtility.ToJson(traits))); } [Obsolete("This should only be used if migrating from Analytics.NET or Analytics.Xamarin")] diff --git a/Tests/Compat/MigrationTest.cs b/Tests/Compat/MigrationTest.cs new file mode 100644 index 0000000..b8891ba --- /dev/null +++ b/Tests/Compat/MigrationTest.cs @@ -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 _plugin; + + public MigrationTest() + { + _settings = JsonUtility.FromJson( + "{\"integrations\":{\"Segment.io\":{\"apiKey\":\"1vNgUqwJeCHmqgI9S1sOm9UHCyfYqbaQ\"}},\"plan\":{},\"edgeFunction\":{}}"); + + var mockHttpClient = new Mock(null, null, null); + mockHttpClient + .Setup(httpClient => httpClient.Settings()) + .ReturnsAsync(_settings); + + _plugin = new Mock + { + 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()); + } + + [Fact] + public void TestCompatTrackAcceptNullDict() + { + var actual = new List(); + _plugin.Setup(o => o.Track(Capture.In(actual))); + _analytics.Add(_plugin.Object); + _analytics.Track("user123", "foo", null); + + 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 + { + ["nullValue"] = null + }; + var actual = new List(); + _plugin.Setup(o => o.Track(Capture.In(actual))); + _analytics.Add(_plugin.Object); + _analytics.Track("user123", "foo", properties); + + 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(); + _plugin.Setup(o => o.Screen(Capture.In(actual))); + _analytics.Add(_plugin.Object); + _analytics.Screen("user123", "foo", null); + + 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 + { + ["nullValue"] = null + }; + var actual = new List(); + _plugin.Setup(o => o.Screen(Capture.In(actual))); + _analytics.Add(_plugin.Object); + _analytics.Screen("user123", "foo", properties); + + 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(); + _plugin.Setup(o => o.Page(Capture.In(actual))); + _analytics.Add(_plugin.Object); + _analytics.Page("user123", "foo", null); + + 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 + { + ["nullValue"] = null + }; + var actual = new List(); + _plugin.Setup(o => o.Page(Capture.In(actual))); + _analytics.Add(_plugin.Object); + _analytics.Page("user123", "foo", properties); + + 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(); + _plugin.Setup(o => o.Group(Capture.In(actual))); + _analytics.Add(_plugin.Object); + _analytics.Group("user123", "foo", null); + + 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 + { + ["nullValue"] = null + }; + var actual = new List(); + _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"]); + } + } +} diff --git a/Tests/Utils/Stubs.cs b/Tests/Utils/Stubs.cs index adfb003..ee32a6d 100644 --- a/Tests/Utils/Stubs.cs +++ b/Tests/Utils/Stubs.cs @@ -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; } From 1106e8879bf6bece5af277db4a87c90bcf8d02bc Mon Sep 17 00:00:00 2001 From: Wenxi Zeng Date: Wed, 12 Jun 2024 10:28:04 -0500 Subject: [PATCH 3/3] prepare release 2.4.2 --- Analytics-CSharp/Analytics-CSharp.csproj | 2 +- Analytics-CSharp/Segment/Analytics/Version.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Analytics-CSharp/Analytics-CSharp.csproj b/Analytics-CSharp/Analytics-CSharp.csproj index dea0150..fbfd69d 100644 --- a/Analytics-CSharp/Analytics-CSharp.csproj +++ b/Analytics-CSharp/Analytics-CSharp.csproj @@ -10,7 +10,7 @@ Segment, Inc The hassle-free way to add analytics to your C# app. - 2.4.1 + 2.4.2 MIT https://github.com/segmentio/Analytics-CSharp git diff --git a/Analytics-CSharp/Segment/Analytics/Version.cs b/Analytics-CSharp/Segment/Analytics/Version.cs index a6ab55c..228d1b5 100644 --- a/Analytics-CSharp/Segment/Analytics/Version.cs +++ b/Analytics-CSharp/Segment/Analytics/Version.cs @@ -2,6 +2,6 @@ namespace Segment.Analytics { internal static class Version { - internal const string SegmentVersion = "2.4.1"; + internal const string SegmentVersion = "2.4.2"; } }