diff --git a/src/Packages/Audience/Runtime/Events/MessageBuilder.cs b/src/Packages/Audience/Runtime/Events/MessageBuilder.cs new file mode 100644 index 000000000..efc3ec396 --- /dev/null +++ b/src/Packages/Audience/Runtime/Events/MessageBuilder.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; + +namespace Immutable.Audience +{ + internal static class MessageBuilder + { + internal static Dictionary Track( + string eventName, + string anonymousId, + string userId, + string packageVersion, + Dictionary properties = null) + { + var msg = BuildBase("track", packageVersion); + msg["eventName"] = Truncate(eventName, 256); + + if (!string.IsNullOrEmpty(anonymousId)) + msg["anonymousId"] = Truncate(anonymousId, 256); + + if (!string.IsNullOrEmpty(userId)) + msg["userId"] = Truncate(userId, 256); + + if (properties != null && properties.Count > 0) + msg["properties"] = properties; + + return msg; + } + + internal static Dictionary Identify( + string anonymousId, + string userId, + string identityType, + string packageVersion, + Dictionary traits = null) + { + var msg = BuildBase("identify", packageVersion); + + if (!string.IsNullOrEmpty(anonymousId)) + msg["anonymousId"] = Truncate(anonymousId, 256); + + if (!string.IsNullOrEmpty(userId)) + msg["userId"] = Truncate(userId, 256); + + if (!string.IsNullOrEmpty(identityType)) + msg["identityType"] = Truncate(identityType, 256); + + if (traits != null && traits.Count > 0) + msg["traits"] = traits; + + return msg; + } + + internal static Dictionary Alias( + string fromId, + string fromType, + string toId, + string toType, + string packageVersion) + { + var msg = BuildBase("alias", packageVersion); + msg["fromId"] = Truncate(fromId, 256); + msg["fromType"] = Truncate(fromType, 256); + msg["toId"] = Truncate(toId, 256); + msg["toType"] = Truncate(toType, 256); + return msg; + } + + private static Dictionary BuildBase(string type, string packageVersion) + { + return new Dictionary + { + ["type"] = type, + ["messageId"] = Guid.NewGuid().ToString(), + ["eventTimestamp"] = DateTime.UtcNow.ToString("o"), + ["context"] = new Dictionary + { + ["library"] = Constants.LibraryName, + ["libraryVersion"] = Truncate(packageVersion, 256) + }, + ["surface"] = Constants.Surface + }; + } + + private static string Truncate(string s, int maxLen) + { + if (s == null || s.Length <= maxLen) + return s; + return s.Substring(0, maxLen); + } + } +} diff --git a/src/Packages/Audience/Tests/Runtime/MessageBuilderTests.cs b/src/Packages/Audience/Tests/Runtime/MessageBuilderTests.cs new file mode 100644 index 000000000..bd3aad3cc --- /dev/null +++ b/src/Packages/Audience/Tests/Runtime/MessageBuilderTests.cs @@ -0,0 +1,101 @@ +using System.Collections.Generic; +using NUnit.Framework; + +namespace Immutable.Audience.Tests +{ + [TestFixture] + public class MessageBuilderTests + { + private const string PackageVersion = "1.2.3"; + + [Test] + public void Track_RequiredFieldsPresent() + { + var result = MessageBuilder.Track("level_complete", "anon-1", null, PackageVersion); + + Assert.AreEqual("track", result["type"]); + Assert.IsTrue(result.ContainsKey("messageId")); + Assert.IsTrue(result.ContainsKey("eventTimestamp")); + Assert.IsTrue(result.ContainsKey("context")); + Assert.IsTrue(result.ContainsKey("surface")); + Assert.AreEqual("level_complete", result["eventName"]); + } + + [Test] + public void Track_EventNameLongerThan256Chars_TruncatedTo256() + { + var longName = new string('x', 300); + + var result = MessageBuilder.Track(longName, null, null, PackageVersion); + + Assert.AreEqual(256, ((string)result["eventName"]).Length); + } + + [Test] + public void Track_NullUserId_NotPresentInDict() + { + var result = MessageBuilder.Track("evt", "anon-1", null, PackageVersion); + + Assert.IsFalse(result.ContainsKey("userId")); + } + + [Test] + public void Track_NonNullUserId_PresentInDict() + { + var result = MessageBuilder.Track("evt", "anon-1", "user-99", PackageVersion); + + Assert.IsTrue(result.ContainsKey("userId")); + Assert.AreEqual("user-99", result["userId"]); + } + + [Test] + public void Identify_TypeAndIdentityFieldsPresent() + { + var result = MessageBuilder.Identify("anon-42", "user-42", "steam", PackageVersion); + + Assert.AreEqual("identify", result["type"]); + Assert.AreEqual("anon-42", result["anonymousId"]); + Assert.AreEqual("user-42", result["userId"]); + Assert.AreEqual("steam", result["identityType"]); + } + + [Test] + public void Alias_AllFourFieldsPresent() + { + var result = MessageBuilder.Alias("from-id", "email", "to-id", "steam", PackageVersion); + + Assert.AreEqual("alias", result["type"]); + Assert.AreEqual("from-id", result["fromId"]); + Assert.AreEqual("email", result["fromType"]); + Assert.AreEqual("to-id", result["toId"]); + Assert.AreEqual("steam", result["toType"]); + } + + [Test] + public void AllMessages_ContextContainsLibraryAndLibraryVersion() + { + var track = MessageBuilder.Track("evt", null, null, PackageVersion); + var identify = MessageBuilder.Identify(null, "u1", null, PackageVersion); + var alias = MessageBuilder.Alias("f", "t1", "t", "t2", PackageVersion); + + foreach (var msg in new[] { track, identify, alias }) + { + var ctx = (Dictionary)msg["context"]; + Assert.AreEqual(Constants.LibraryName, ctx["library"]); + Assert.AreEqual(PackageVersion, ctx["libraryVersion"]); + } + } + + [Test] + public void AllMessages_SurfaceIsUnity() + { + var track = MessageBuilder.Track("evt", null, null, PackageVersion); + var identify = MessageBuilder.Identify(null, "u1", null, PackageVersion); + var alias = MessageBuilder.Alias("f", "t1", "t", "t2", PackageVersion); + + Assert.AreEqual("unity", track["surface"]); + Assert.AreEqual("unity", identify["surface"]); + Assert.AreEqual("unity", alias["surface"]); + } + } +}