From aed6b276944fa3e050f2a8737133f54b690f4a44 Mon Sep 17 00:00:00 2001 From: William Roush Date: Sun, 12 Jun 2016 19:44:58 -0400 Subject: [PATCH 01/10] Really basic ability to send stuff to segment. --- .gitignore | 7 + Readme.md | 12 +- SegmentDotNet.sln | 44 ++++++ global.json | 6 + src/SegmentDotNet/Abstractions/IDateTime.cs | 9 ++ .../Abstractions/SystemDateTime.cs | 9 ++ .../Client/Request/Abstract/AnonymousBase.cs | 17 +++ .../Client/Request/Abstract/Base.cs | 17 +++ .../Client/Request/Abstract/TraitsBase.cs | 18 +++ .../Request/Abstract/UserTimestampBase.cs | 26 ++++ src/SegmentDotNet/Client/Request/Alias.cs | 25 ++++ src/SegmentDotNet/Client/Request/Batch.cs | 20 +++ src/SegmentDotNet/Client/Request/Group.cs | 25 ++++ src/SegmentDotNet/Client/Request/Identify.cs | 20 +++ .../Request/Interfaces/ISegmentRequest.cs | 7 + src/SegmentDotNet/Client/Request/Page.cs | 23 +++ src/SegmentDotNet/Client/Request/Screen.cs | 23 +++ src/SegmentDotNet/Client/Request/Track.cs | 27 ++++ src/SegmentDotNet/Client/SegmentClient.cs | 86 ++++++++++++ .../Configuration/SegmentConfiguration.cs | 7 + src/SegmentDotNet/Properties/AssemblyInfo.cs | 19 +++ src/SegmentDotNet/SegmentDotNet.xproj | 21 +++ src/SegmentDotNet/project.json | 15 ++ .../Client/SegmentClientTests.cs | 131 ++++++++++++++++++ .../Properties/AssemblyInfo.cs | 19 +++ .../SegmentDotNet.Tests.xproj | 22 +++ test/SegmentDotNet.Tests/project.json | 33 +++++ test/SegmentDotNet.Tests/sample.env | 1 + 28 files changed, 688 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 SegmentDotNet.sln create mode 100644 global.json create mode 100644 src/SegmentDotNet/Abstractions/IDateTime.cs create mode 100644 src/SegmentDotNet/Abstractions/SystemDateTime.cs create mode 100644 src/SegmentDotNet/Client/Request/Abstract/AnonymousBase.cs create mode 100644 src/SegmentDotNet/Client/Request/Abstract/Base.cs create mode 100644 src/SegmentDotNet/Client/Request/Abstract/TraitsBase.cs create mode 100644 src/SegmentDotNet/Client/Request/Abstract/UserTimestampBase.cs create mode 100644 src/SegmentDotNet/Client/Request/Alias.cs create mode 100644 src/SegmentDotNet/Client/Request/Batch.cs create mode 100644 src/SegmentDotNet/Client/Request/Group.cs create mode 100644 src/SegmentDotNet/Client/Request/Identify.cs create mode 100644 src/SegmentDotNet/Client/Request/Interfaces/ISegmentRequest.cs create mode 100644 src/SegmentDotNet/Client/Request/Page.cs create mode 100644 src/SegmentDotNet/Client/Request/Screen.cs create mode 100644 src/SegmentDotNet/Client/Request/Track.cs create mode 100644 src/SegmentDotNet/Client/SegmentClient.cs create mode 100644 src/SegmentDotNet/Configuration/SegmentConfiguration.cs create mode 100644 src/SegmentDotNet/Properties/AssemblyInfo.cs create mode 100644 src/SegmentDotNet/SegmentDotNet.xproj create mode 100644 src/SegmentDotNet/project.json create mode 100644 test/SegmentDotNet.Tests/Client/SegmentClientTests.cs create mode 100644 test/SegmentDotNet.Tests/Properties/AssemblyInfo.cs create mode 100644 test/SegmentDotNet.Tests/SegmentDotNet.Tests.xproj create mode 100644 test/SegmentDotNet.Tests/project.json create mode 100644 test/SegmentDotNet.Tests/sample.env diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7d988da --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +project.lock.json +test/SegmentDotNet.Tests/bin/ +test/SegmentDotNet.Tests/obj/ +src/SegmentDotNet/obj/ +src/SegmentDotNet/bin/Debug/ +test/SegmentDotNet.Tests/.env +.vs/ diff --git a/Readme.md b/Readme.md index ae3eb9e..e345393 100644 --- a/Readme.md +++ b/Readme.md @@ -1,3 +1,13 @@ # SegmentDotNet -Segment integration for your .NET Core projects, relies on dependency injection. \ No newline at end of file +Segment integration for your .NET Core projects, relies on dependency injection. + +# Tests + +Copy test/SegmentDotNet.Tests/sample.env to test/SegmentDotNet.Tests/.env and fill out the `SEGMENT_WRITE_KEY` variable with your Segment write key. + +To run tests run `dotnet test` from the command line using test/SegmentDotNet.tests as your working directory. + +There would be more tests but [Segment returns 200 response codes](https://segment.com/docs/libraries/http/#selecting-integrations) regardless of invalid input, auth keys, or any other bad information other than malformed/too large JSON. I'll add more once Segment fixes this. + +Running tests will attempt to push events using the key defined as the environment variable `SEGMENT_WRITE_KEY`, you can check for results in the debugger on Segment for now. \ No newline at end of file diff --git a/SegmentDotNet.sln b/SegmentDotNet.sln new file mode 100644 index 0000000..1ce3e8b --- /dev/null +++ b/SegmentDotNet.sln @@ -0,0 +1,44 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25123.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5E6DE7BC-0749-4C63-8441-95A5756E5167}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{EDA04816-70FA-4427-AC2D-D5FBA1B12951}" + ProjectSection(SolutionItems) = preProject + CHANGELOG.md = CHANGELOG.md + global.json = global.json + LICENSE.txt = LICENSE.txt + Readme.md = Readme.md + EndProjectSection +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SegmentDotNet", "src\SegmentDotNet\SegmentDotNet.xproj", "{AE25F674-BAAB-41B7-8FF5-36EAF7341ECC}" +EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SegmentDotNet.Tests", "test\SegmentDotNet.Tests\SegmentDotNet.Tests.xproj", "{D5950492-E758-4EB5-AE16-F5D4BCF1EC85}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{5BF05EAE-3AD2-40BF-9EE1-5E92C1F5A90B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AE25F674-BAAB-41B7-8FF5-36EAF7341ECC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AE25F674-BAAB-41B7-8FF5-36EAF7341ECC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AE25F674-BAAB-41B7-8FF5-36EAF7341ECC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AE25F674-BAAB-41B7-8FF5-36EAF7341ECC}.Release|Any CPU.Build.0 = Release|Any CPU + {D5950492-E758-4EB5-AE16-F5D4BCF1EC85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D5950492-E758-4EB5-AE16-F5D4BCF1EC85}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D5950492-E758-4EB5-AE16-F5D4BCF1EC85}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D5950492-E758-4EB5-AE16-F5D4BCF1EC85}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {AE25F674-BAAB-41B7-8FF5-36EAF7341ECC} = {5E6DE7BC-0749-4C63-8441-95A5756E5167} + {D5950492-E758-4EB5-AE16-F5D4BCF1EC85} = {5BF05EAE-3AD2-40BF-9EE1-5E92C1F5A90B} + EndGlobalSection +EndGlobal diff --git a/global.json b/global.json new file mode 100644 index 0000000..b51e28b --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "projects": [ "src", "test" ], + "sdk": { + "version": "1.0.0-preview1-002702" + } +} diff --git a/src/SegmentDotNet/Abstractions/IDateTime.cs b/src/SegmentDotNet/Abstractions/IDateTime.cs new file mode 100644 index 0000000..635766c --- /dev/null +++ b/src/SegmentDotNet/Abstractions/IDateTime.cs @@ -0,0 +1,9 @@ +namespace SegmentDotNet.Abstractions +{ + using System; + + public interface IDateTime + { + DateTime UtcNow { get; } + } +} diff --git a/src/SegmentDotNet/Abstractions/SystemDateTime.cs b/src/SegmentDotNet/Abstractions/SystemDateTime.cs new file mode 100644 index 0000000..35429a2 --- /dev/null +++ b/src/SegmentDotNet/Abstractions/SystemDateTime.cs @@ -0,0 +1,9 @@ +namespace SegmentDotNet.Abstractions +{ + using System; + + public class SystemDateTime : IDateTime + { + public DateTime UtcNow { get { return DateTime.UtcNow; } } + } +} diff --git a/src/SegmentDotNet/Client/Request/Abstract/AnonymousBase.cs b/src/SegmentDotNet/Client/Request/Abstract/AnonymousBase.cs new file mode 100644 index 0000000..6b8bb74 --- /dev/null +++ b/src/SegmentDotNet/Client/Request/Abstract/AnonymousBase.cs @@ -0,0 +1,17 @@ +namespace SegmentDotNet.Client.Request.Abstract +{ + using Abstractions; + using Newtonsoft.Json; + + public abstract class AnonymousBase : UserTimestampBase + { + public AnonymousBase(IDateTime datetime) + : base(datetime) + { + + } + + [JsonProperty("anonymousId")] + public string AnonymousId { get; set; } + } +} diff --git a/src/SegmentDotNet/Client/Request/Abstract/Base.cs b/src/SegmentDotNet/Client/Request/Abstract/Base.cs new file mode 100644 index 0000000..7f9a4c4 --- /dev/null +++ b/src/SegmentDotNet/Client/Request/Abstract/Base.cs @@ -0,0 +1,17 @@ +namespace SegmentDotNet.Client.Request.Abstract +{ + using Interfaces; + using Newtonsoft.Json; + using System.Collections.Generic; + + public abstract class Base : ISegmentRequest + { + [JsonProperty("context")] + public Dictionary Context { get; set; } + + [JsonProperty("integrations")] + public Dictionary Integrations { get; set; } + + public abstract string Endpoint { get; } + } +} \ No newline at end of file diff --git a/src/SegmentDotNet/Client/Request/Abstract/TraitsBase.cs b/src/SegmentDotNet/Client/Request/Abstract/TraitsBase.cs new file mode 100644 index 0000000..afa9ee2 --- /dev/null +++ b/src/SegmentDotNet/Client/Request/Abstract/TraitsBase.cs @@ -0,0 +1,18 @@ +namespace SegmentDotNet.Client.Request.Abstract +{ + using Abstractions; + using Newtonsoft.Json; + using System.Collections.Generic; + + public abstract class TraitsBase : AnonymousBase + { + public TraitsBase(IDateTime datetime) + : base(datetime) + { + + } + + [JsonProperty("traits")] + public Dictionary Traits { get; set; } + } +} diff --git a/src/SegmentDotNet/Client/Request/Abstract/UserTimestampBase.cs b/src/SegmentDotNet/Client/Request/Abstract/UserTimestampBase.cs new file mode 100644 index 0000000..7a20e6f --- /dev/null +++ b/src/SegmentDotNet/Client/Request/Abstract/UserTimestampBase.cs @@ -0,0 +1,26 @@ +namespace SegmentDotNet.Client.Request.Abstract +{ + using Abstractions; + using Newtonsoft.Json; + using System; + + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public abstract class UserTimestampBase : Base + { + public UserTimestampBase(IDateTime datetime) + { + this.DateTime = datetime; + } + + protected IDateTime DateTime { get; set; } + + [JsonProperty("type")] + public abstract string Type { get; } + + [JsonProperty("userId")] + public string UserId { get; set; } + + [JsonProperty("timestamp")] + public DateTime Timestamp { get { return this.DateTime.UtcNow; } } + } +} diff --git a/src/SegmentDotNet/Client/Request/Alias.cs b/src/SegmentDotNet/Client/Request/Alias.cs new file mode 100644 index 0000000..4f8a9b7 --- /dev/null +++ b/src/SegmentDotNet/Client/Request/Alias.cs @@ -0,0 +1,25 @@ + +namespace SegmentDotNet.Client.Request +{ + using System; + using Abstract; + using Abstractions; + using Newtonsoft.Json; + + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public class Alias : UserTimestampBase + { + public Alias(IDateTime datetime) + : base(datetime) + { + + } + + public override string Type { get { return "alias"; } } + + public override string Endpoint { get { return "alias"; } } + + [JsonProperty("previousId")] + public string PreviousId { get; set; } + } +} diff --git a/src/SegmentDotNet/Client/Request/Batch.cs b/src/SegmentDotNet/Client/Request/Batch.cs new file mode 100644 index 0000000..8e80a75 --- /dev/null +++ b/src/SegmentDotNet/Client/Request/Batch.cs @@ -0,0 +1,20 @@ +namespace SegmentDotNet.Client.Request +{ + using Abstract; + using Newtonsoft.Json; + using System.Collections.Generic; + + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public class Batch : Base + { + public Batch() + { + this.Items = new List(); + } + + public override string Endpoint { get { return "batch"; } } + + [JsonProperty("batch")] + public List Items { get; set; } + } +} diff --git a/src/SegmentDotNet/Client/Request/Group.cs b/src/SegmentDotNet/Client/Request/Group.cs new file mode 100644 index 0000000..8cd88f5 --- /dev/null +++ b/src/SegmentDotNet/Client/Request/Group.cs @@ -0,0 +1,25 @@ +using System; + +namespace SegmentDotNet.Client.Request +{ + using Abstract; + using Abstractions; + using Newtonsoft.Json; + + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public class Group : TraitsBase + { + public Group(IDateTime datetime) + : base(datetime) + { + + } + + public override string Type { get { return "group"; } } + + public override string Endpoint { get { return "group"; } } + + [JsonProperty("groupId")] + public string GroupId { get; set; } + } +} diff --git a/src/SegmentDotNet/Client/Request/Identify.cs b/src/SegmentDotNet/Client/Request/Identify.cs new file mode 100644 index 0000000..fd4b807 --- /dev/null +++ b/src/SegmentDotNet/Client/Request/Identify.cs @@ -0,0 +1,20 @@ +namespace SegmentDotNet.Client.Request +{ + using Abstract; + using Abstractions; + using Newtonsoft.Json; + + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public class Identify : TraitsBase + { + public Identify(IDateTime datetime) + : base(datetime) + { + + } + + public override string Type { get { return "identify"; } } + + public override string Endpoint { get { return "identify"; } } + } +} diff --git a/src/SegmentDotNet/Client/Request/Interfaces/ISegmentRequest.cs b/src/SegmentDotNet/Client/Request/Interfaces/ISegmentRequest.cs new file mode 100644 index 0000000..b1e3d3e --- /dev/null +++ b/src/SegmentDotNet/Client/Request/Interfaces/ISegmentRequest.cs @@ -0,0 +1,7 @@ +namespace SegmentDotNet.Client.Request.Interfaces +{ + public interface ISegmentRequest + { + string Endpoint { get; } + } +} diff --git a/src/SegmentDotNet/Client/Request/Page.cs b/src/SegmentDotNet/Client/Request/Page.cs new file mode 100644 index 0000000..c811834 --- /dev/null +++ b/src/SegmentDotNet/Client/Request/Page.cs @@ -0,0 +1,23 @@ +namespace SegmentDotNet.Client.Request +{ + using Abstract; + using Abstractions; + using Newtonsoft.Json; + + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public class Page : AnonymousBase + { + public Page(IDateTime datetime) + : base(datetime) + { + + } + + public override string Type { get { return "page"; } } + + public override string Endpoint { get { return "page"; } } + + [JsonProperty("name")] + public string Name { get; set; } + } +} diff --git a/src/SegmentDotNet/Client/Request/Screen.cs b/src/SegmentDotNet/Client/Request/Screen.cs new file mode 100644 index 0000000..d4eea1a --- /dev/null +++ b/src/SegmentDotNet/Client/Request/Screen.cs @@ -0,0 +1,23 @@ +namespace SegmentDotNet.Client.Request +{ + using Abstract; + using Abstractions; + using Newtonsoft.Json; + + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public class Screen : AnonymousBase + { + public Screen(IDateTime datetime) + : base(datetime) + { + + } + + public override string Type { get { return "screen"; } } + + public override string Endpoint { get { return "screen"; } } + + [JsonProperty("name")] + public string Name { get; set; } + } +} diff --git a/src/SegmentDotNet/Client/Request/Track.cs b/src/SegmentDotNet/Client/Request/Track.cs new file mode 100644 index 0000000..5019d6b --- /dev/null +++ b/src/SegmentDotNet/Client/Request/Track.cs @@ -0,0 +1,27 @@ +namespace SegmentDotNet.Client.Request +{ + using Abstract; + using Abstractions; + using Newtonsoft.Json; + using System.Collections.Generic; + + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public class Track : AnonymousBase + { + public Track(IDateTime datetime) + : base(datetime) + { + + } + + public override string Type { get { return "track"; } } + + public override string Endpoint { get { return "track"; } } + + [JsonProperty("event")] + public string Event { get; set; } + + [JsonProperty("properties")] + public Dictionary Properties { get; set; } + } +} diff --git a/src/SegmentDotNet/Client/SegmentClient.cs b/src/SegmentDotNet/Client/SegmentClient.cs new file mode 100644 index 0000000..0e316a1 --- /dev/null +++ b/src/SegmentDotNet/Client/SegmentClient.cs @@ -0,0 +1,86 @@ +namespace SegmentDotNet.Client +{ + using Configuration; + using Microsoft.Extensions.Options; + using Newtonsoft.Json; + using Request; + using Request.Abstract; + using System; + using System.Net.Http; + using System.Net.Http.Headers; + using System.Text; + using System.Threading.Tasks; + + public class SegmentClient + { + public SegmentClient(IOptions configuration) + { + this.Configuration = configuration.Value; + } + + protected const string URL = "https://api.segment.io/v1/"; + + protected SegmentConfiguration Configuration { get; set; } + + public async Task Alias(Alias alias) + { + await this.Post(alias); + } + + public async Task Batch(Batch batch) + { + await this.Post(batch); + } + + public async Task Identify(Identify identify) + { + await this.Post(identify); + } + + public async Task Group(Group group) + { + await this.Post(group); + } + + public async Task Screen(Screen screen) + { + await this.Post(screen); + } + + public async Task Page(Page page) + { + await this.Post(page); + } + + public async Task Track(Track track) + { + await this.Post(track); + } + + public async Task Post(Base baseClass) + { + var json = this.Serialize(baseClass); + using (var client = new HttpClient()) + { + client.DefaultRequestHeaders.Clear(); + client.DefaultRequestHeaders.Add("Authorization", $"Basic {this.GetWriteKey()}"); + client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + var response = await client.PostAsync($"{URL}{baseClass.Endpoint}", new StringContent(json, Encoding.UTF8, "application/json")); + if (!response.IsSuccessStatusCode) + { + throw new Exception(response.ReasonPhrase); + } + } + } + + public string Serialize(Base baseClass) + { + return JsonConvert.SerializeObject(baseClass); + } + + public string GetWriteKey() + { + return Convert.ToBase64String(Encoding.UTF8.GetBytes(this.Configuration.WriteKey + ":")); + } + } +} diff --git a/src/SegmentDotNet/Configuration/SegmentConfiguration.cs b/src/SegmentDotNet/Configuration/SegmentConfiguration.cs new file mode 100644 index 0000000..b54fb6a --- /dev/null +++ b/src/SegmentDotNet/Configuration/SegmentConfiguration.cs @@ -0,0 +1,7 @@ +namespace SegmentDotNet.Configuration +{ + public class SegmentConfiguration + { + public virtual string WriteKey { get; set; } + } +} diff --git a/src/SegmentDotNet/Properties/AssemblyInfo.cs b/src/SegmentDotNet/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..b76ca2b --- /dev/null +++ b/src/SegmentDotNet/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SegmentDotNet")] +[assembly: AssemblyTrademark("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("ae25f674-baab-41b7-8ff5-36eaf7341ecc")] diff --git a/src/SegmentDotNet/SegmentDotNet.xproj b/src/SegmentDotNet/SegmentDotNet.xproj new file mode 100644 index 0000000..2bf53ce --- /dev/null +++ b/src/SegmentDotNet/SegmentDotNet.xproj @@ -0,0 +1,21 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + ae25f674-baab-41b7-8ff5-36eaf7341ecc + SegmentDotNet + .\obj + .\bin\ + v4.6 + + + + 2.0 + + + diff --git a/src/SegmentDotNet/project.json b/src/SegmentDotNet/project.json new file mode 100644 index 0000000..d4fd5aa --- /dev/null +++ b/src/SegmentDotNet/project.json @@ -0,0 +1,15 @@ +{ + "version": "0.1.0-*", + + "dependencies": { + "Microsoft.Extensions.Options": "1.0.0-rc2-final", + "NETStandard.Library": "1.5.0-rc2-24027", + "Newtonsoft.Json": "8.0.3" + }, + + "frameworks": { + "netstandard1.5": { + "imports": "dnxcore50" + } + } +} diff --git a/test/SegmentDotNet.Tests/Client/SegmentClientTests.cs b/test/SegmentDotNet.Tests/Client/SegmentClientTests.cs new file mode 100644 index 0000000..1e658fd --- /dev/null +++ b/test/SegmentDotNet.Tests/Client/SegmentClientTests.cs @@ -0,0 +1,131 @@ +namespace SegmentDotNet.Tests.Client +{ + using Abstractions; + using Configuration; + using Microsoft.Extensions.Options; + using Moq; + using Newtonsoft.Json; + using SegmentDotNet.Client; + using SegmentDotNet.Client.Request; + using SegmentDotNet.Client.Request.Abstract; + using System; + using System.IO; + using System.Threading.Tasks; + using Xunit; + + public class SegmentClientTests : IDisposable + { + public SegmentClientTests() + { + // Tooling for this in the future would be amazing. + var env = File.ReadAllText(".env"); + var lines = env.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None); + foreach(var line in lines) + { + var segments = line.Split('='); + if(segments[0] == "SEGMENT_WRITE_KEY") + { + Environment.SetEnvironmentVariable("SEGMENT_WRITE_KEY", segments[1]); + } + } + } + + [Fact] + public void Should_Serialize_Timestamp() + { + var json = this.SetupClient().Serialize(this.SetupRequest()); + var jsonObject = JsonConvert.DeserializeAnonymousType(json, new { timestamp = "" }); + Assert.Equal("2016-06-12T05:20:50.523Z", jsonObject.timestamp); + } + + [Fact] + public async Task Can_Identify() + { + var identify = this.SetupRequest(); + identify.UserId = "1234"; + await this.SetupClient().Identify(identify); + } + + [Fact] + public async Task Can_Group() + { + var group = this.SetupRequest(); + group.GroupId = "G1234"; + group.UserId = "1234"; + await this.SetupClient().Group(group); + } + + [Fact] + public async Task Can_Track() + { + var track = this.SetupRequest(); + track.UserId = "1234"; + track.Event = "Can_Track Test"; + await this.SetupClient().Track(track); + } + + [Fact] + public async Task Can_Page() + { + var page = this.SetupRequest(); + page.UserId = "1234"; + page.Name = "Can_Page Test"; + await this.SetupClient().Page(page); + } + + [Fact] + public async Task Can_Screen() + { + var screen = this.SetupRequest(); + screen.UserId = "1234"; + screen.Name = "Can_Screen Test"; + await this.SetupClient().Screen(screen); + } + + [Fact] + public async Task Can_Alias() + { + var alias = this.SetupRequest(); + alias.PreviousId = "P1234"; + alias.UserId = "1234"; + await this.SetupClient().Alias(alias); + } + + [Fact] + public async Task Can_Batch() + { + var identify = this.SetupRequest(); + identify.UserId = "B1234"; + var batch = new Batch(); + batch.Items.Add(identify); + await this.SetupClient().Batch(batch); + } + + protected SegmentClient SetupClient(string writeKey = null) + { + if(writeKey == null) + { + writeKey = Environment.GetEnvironmentVariable("SEGMENT_WRITE_KEY"); + } + + var optionsMock = new Mock>(); + var configurationMock = new Mock(); + configurationMock.Setup(c => c.WriteKey).Returns(writeKey); + optionsMock.Setup(o => o.Value).Returns(configurationMock.Object); + var segmentClient = new SegmentClient(optionsMock.Object); + return segmentClient; + } + + protected T SetupRequest() + where T : UserTimestampBase + { + var datetimeMock = new Mock(); + datetimeMock.Setup(d => d.UtcNow).Returns(new DateTime(2016, 6, 12, 5, 20, 50, 523, DateTimeKind.Utc)); + return Activator.CreateInstance(typeof(T), datetimeMock.Object) as T; + } + + public void Dispose() + { + } + } +} diff --git a/test/SegmentDotNet.Tests/Properties/AssemblyInfo.cs b/test/SegmentDotNet.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..73a4864 --- /dev/null +++ b/test/SegmentDotNet.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SegmentDotNet.Tests")] +[assembly: AssemblyTrademark("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("d5950492-e758-4eb5-ae16-f5d4bcf1ec85")] diff --git a/test/SegmentDotNet.Tests/SegmentDotNet.Tests.xproj b/test/SegmentDotNet.Tests/SegmentDotNet.Tests.xproj new file mode 100644 index 0000000..a88a96d --- /dev/null +++ b/test/SegmentDotNet.Tests/SegmentDotNet.Tests.xproj @@ -0,0 +1,22 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + d5950492-e758-4eb5-ae16-f5d4bcf1ec85 + SegmentDotNet.Tests + .\obj + .\bin\ + v4.6 + + + 2.0 + + + + + + \ No newline at end of file diff --git a/test/SegmentDotNet.Tests/project.json b/test/SegmentDotNet.Tests/project.json new file mode 100644 index 0000000..3acc829 --- /dev/null +++ b/test/SegmentDotNet.Tests/project.json @@ -0,0 +1,33 @@ +{ + "version": "0.1.0-*", + + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-24027", + "dotnet-test-xunit": "1.0.0-rc2-build10025", + "xunit": "2.1.0", + "Moq": "4.6.25-alpha", + "SegmentDotNet": "0.1.0-*" + }, + "testRunner": "xunit", + "frameworks": { + "netcoreapp1.0": { + "imports": [ + "dnxcore50", + "portable-net45+win81" + ], + "dependencies": { + "Microsoft.NETCore.App": { + "version": "1.0.0-rc2-3002702", + "type": "platform" + }, + "System.Diagnostics.TraceSource": "4.0.0-rc2-24027" + } + } + }, + "tools": { + "Microsoft.DotNet.Watcher.Tools": { + "version": "1.0.0-*", + "imports": "portable-net451+win8" + } + } +} diff --git a/test/SegmentDotNet.Tests/sample.env b/test/SegmentDotNet.Tests/sample.env new file mode 100644 index 0000000..c644574 --- /dev/null +++ b/test/SegmentDotNet.Tests/sample.env @@ -0,0 +1 @@ +SEGMENT_WRITE_KEY= \ No newline at end of file From c502c3eeb2affeb0d819aebf8427575b7f7131d0 Mon Sep 17 00:00:00 2001 From: William Roush Date: Sun, 12 Jun 2016 19:54:23 -0400 Subject: [PATCH 02/10] Add support for environment variables on build server. --- .../Client/SegmentClientTests.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/test/SegmentDotNet.Tests/Client/SegmentClientTests.cs b/test/SegmentDotNet.Tests/Client/SegmentClientTests.cs index 1e658fd..48bd490 100644 --- a/test/SegmentDotNet.Tests/Client/SegmentClientTests.cs +++ b/test/SegmentDotNet.Tests/Client/SegmentClientTests.cs @@ -18,14 +18,17 @@ public class SegmentClientTests : IDisposable public SegmentClientTests() { // Tooling for this in the future would be amazing. - var env = File.ReadAllText(".env"); - var lines = env.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None); - foreach(var line in lines) + if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("SEGMENT_WRITE_KEY"))) { - var segments = line.Split('='); - if(segments[0] == "SEGMENT_WRITE_KEY") + var env = File.ReadAllText(".env"); + var lines = env.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None); + foreach (var line in lines) { - Environment.SetEnvironmentVariable("SEGMENT_WRITE_KEY", segments[1]); + var segments = line.Split('='); + if (segments[0] == "SEGMENT_WRITE_KEY") + { + Environment.SetEnvironmentVariable("SEGMENT_WRITE_KEY", segments[1]); + } } } } From 3b102a2607029671c299293fb2e770542f318148 Mon Sep 17 00:00:00 2001 From: William Roush Date: Sun, 12 Jun 2016 20:58:05 -0400 Subject: [PATCH 03/10] Add basic serialization support on contexts. --- .../Client/Request/Abstract/Base.cs | 8 +++- .../Contexts/ContextCollection.cs | 23 ++++++++++ .../Contexts/GoogleAnalyitics.cs | 29 ++++++++++++ .../Contexts/Interfaces/IContext.cs | 10 +++++ .../ContextCollectionSerializer.cs | 31 +++++++++++++ src/SegmentDotNet/project.json | 3 +- .../Client/SegmentClientTests.cs | 31 +------------ .../Contexts/ContextCollectionTests.cs | 45 +++++++++++++++++++ .../Contexts/GoogleAnalyiticsTests.cs | 36 +++++++++++++++ test/SegmentDotNet.Tests/TestBase.cs | 35 +++++++++++++++ 10 files changed, 218 insertions(+), 33 deletions(-) create mode 100644 src/SegmentDotNet/Contexts/ContextCollection.cs create mode 100644 src/SegmentDotNet/Contexts/GoogleAnalyitics.cs create mode 100644 src/SegmentDotNet/Contexts/Interfaces/IContext.cs create mode 100644 src/SegmentDotNet/Contexts/Serializers/ContextCollectionSerializer.cs create mode 100644 test/SegmentDotNet.Tests/Contexts/ContextCollectionTests.cs create mode 100644 test/SegmentDotNet.Tests/Contexts/GoogleAnalyiticsTests.cs create mode 100644 test/SegmentDotNet.Tests/TestBase.cs diff --git a/src/SegmentDotNet/Client/Request/Abstract/Base.cs b/src/SegmentDotNet/Client/Request/Abstract/Base.cs index 7f9a4c4..9abf296 100644 --- a/src/SegmentDotNet/Client/Request/Abstract/Base.cs +++ b/src/SegmentDotNet/Client/Request/Abstract/Base.cs @@ -1,13 +1,19 @@ namespace SegmentDotNet.Client.Request.Abstract { + using Contexts; using Interfaces; using Newtonsoft.Json; using System.Collections.Generic; public abstract class Base : ISegmentRequest { + public Base() + { + this.Context = new ContextCollection(); + } + [JsonProperty("context")] - public Dictionary Context { get; set; } + public ContextCollection Context { get; set; } [JsonProperty("integrations")] public Dictionary Integrations { get; set; } diff --git a/src/SegmentDotNet/Contexts/ContextCollection.cs b/src/SegmentDotNet/Contexts/ContextCollection.cs new file mode 100644 index 0000000..27cf333 --- /dev/null +++ b/src/SegmentDotNet/Contexts/ContextCollection.cs @@ -0,0 +1,23 @@ +namespace SegmentDotNet.Contexts +{ + using Interfaces; + using Newtonsoft.Json; + using Serializers; + using System.Collections.Generic; + + [JsonConverter(typeof(ContextCollectionSerializer))] + public class ContextCollection + { + public ContextCollection() + { + this.Contexts = new List(); + } + + public List Contexts { get; set; } + + public void Add(IContext context) + { + this.Contexts.Add(context); + } + } +} diff --git a/src/SegmentDotNet/Contexts/GoogleAnalyitics.cs b/src/SegmentDotNet/Contexts/GoogleAnalyitics.cs new file mode 100644 index 0000000..29883c4 --- /dev/null +++ b/src/SegmentDotNet/Contexts/GoogleAnalyitics.cs @@ -0,0 +1,29 @@ +namespace SegmentDotNet.Contexts +{ + using Interfaces; + using Microsoft.AspNetCore.Http; + using Newtonsoft.Json; + using System.Linq; + + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public class GoogleAnalyitics : IContext + { + public GoogleAnalyitics(IHttpContextAccessor httpContextAccessor) + { + this.HttpContextAccessor = httpContextAccessor; + } + + protected IHttpContextAccessor HttpContextAccessor { get; set; } + + public string Key { get { return "Google Analyitics"; } } + + [JsonProperty("clientId")] + public string ClientId { get { return this.GetGoogleAnalyticsCookie(); } } + + private string GetGoogleAnalyticsCookie() + { + var cookie = this.HttpContextAccessor.HttpContext.Request.Cookies["_ga"]; + return cookie == null ? null : string.Join(".", cookie.Split('.').Reverse().Take(2).Reverse()); + } + } +} diff --git a/src/SegmentDotNet/Contexts/Interfaces/IContext.cs b/src/SegmentDotNet/Contexts/Interfaces/IContext.cs new file mode 100644 index 0000000..5ebb089 --- /dev/null +++ b/src/SegmentDotNet/Contexts/Interfaces/IContext.cs @@ -0,0 +1,10 @@ +namespace SegmentDotNet.Contexts.Interfaces +{ + using Newtonsoft.Json; + + public interface IContext + { + [JsonIgnore] + string Key { get; } + } +} diff --git a/src/SegmentDotNet/Contexts/Serializers/ContextCollectionSerializer.cs b/src/SegmentDotNet/Contexts/Serializers/ContextCollectionSerializer.cs new file mode 100644 index 0000000..452030e --- /dev/null +++ b/src/SegmentDotNet/Contexts/Serializers/ContextCollectionSerializer.cs @@ -0,0 +1,31 @@ +namespace SegmentDotNet.Contexts.Serializers +{ + using System; + using Newtonsoft.Json; + + public class ContextCollectionSerializer : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return typeof(ContextCollection) == objectType; + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var contextCollection = value as ContextCollection; + writer.WriteStartObject(); + foreach (var context in contextCollection.Contexts) + { + writer.WritePropertyName(context.Key); + serializer.Serialize(writer, context); + } + + writer.WriteEndObject(); + } + } +} diff --git a/src/SegmentDotNet/project.json b/src/SegmentDotNet/project.json index d4fd5aa..ec3cbf4 100644 --- a/src/SegmentDotNet/project.json +++ b/src/SegmentDotNet/project.json @@ -1,12 +1,11 @@ { "version": "0.1.0-*", - "dependencies": { + "Microsoft.AspNetCore.Http.Abstractions": "1.0.0-rc2-final", "Microsoft.Extensions.Options": "1.0.0-rc2-final", "NETStandard.Library": "1.5.0-rc2-24027", "Newtonsoft.Json": "8.0.3" }, - "frameworks": { "netstandard1.5": { "imports": "dnxcore50" diff --git a/test/SegmentDotNet.Tests/Client/SegmentClientTests.cs b/test/SegmentDotNet.Tests/Client/SegmentClientTests.cs index 48bd490..b7d3be4 100644 --- a/test/SegmentDotNet.Tests/Client/SegmentClientTests.cs +++ b/test/SegmentDotNet.Tests/Client/SegmentClientTests.cs @@ -1,19 +1,13 @@ namespace SegmentDotNet.Tests.Client { - using Abstractions; - using Configuration; - using Microsoft.Extensions.Options; - using Moq; using Newtonsoft.Json; - using SegmentDotNet.Client; using SegmentDotNet.Client.Request; - using SegmentDotNet.Client.Request.Abstract; using System; using System.IO; using System.Threading.Tasks; using Xunit; - public class SegmentClientTests : IDisposable + public class SegmentClientTests : TestBase, IDisposable { public SegmentClientTests() { @@ -104,29 +98,6 @@ public async Task Can_Batch() await this.SetupClient().Batch(batch); } - protected SegmentClient SetupClient(string writeKey = null) - { - if(writeKey == null) - { - writeKey = Environment.GetEnvironmentVariable("SEGMENT_WRITE_KEY"); - } - - var optionsMock = new Mock>(); - var configurationMock = new Mock(); - configurationMock.Setup(c => c.WriteKey).Returns(writeKey); - optionsMock.Setup(o => o.Value).Returns(configurationMock.Object); - var segmentClient = new SegmentClient(optionsMock.Object); - return segmentClient; - } - - protected T SetupRequest() - where T : UserTimestampBase - { - var datetimeMock = new Mock(); - datetimeMock.Setup(d => d.UtcNow).Returns(new DateTime(2016, 6, 12, 5, 20, 50, 523, DateTimeKind.Utc)); - return Activator.CreateInstance(typeof(T), datetimeMock.Object) as T; - } - public void Dispose() { } diff --git a/test/SegmentDotNet.Tests/Contexts/ContextCollectionTests.cs b/test/SegmentDotNet.Tests/Contexts/ContextCollectionTests.cs new file mode 100644 index 0000000..9b10637 --- /dev/null +++ b/test/SegmentDotNet.Tests/Contexts/ContextCollectionTests.cs @@ -0,0 +1,45 @@ +namespace SegmentDotNet.Tests.Contexts +{ + using Newtonsoft.Json; + using SegmentDotNet.Contexts; + using SegmentDotNet.Contexts.Interfaces; + using Xunit; + + public class ContextCollectionTests + { + protected class ContextMock : IContext + { + public string Key { get { return "Context Mock"; } } + + [JsonProperty("test")] + public string Test { get { return "value"; } } + } + + protected class ContextMock2 : IContext + { + public string Key { get { return "Context Mock2"; } } + + [JsonProperty("test2")] + public string Test { get { return "value2"; } } + } + + [Fact] + public void Serialization_Works() + { + var contextCollection = new ContextCollection(); + contextCollection.Add(new ContextMock()); + var json = JsonConvert.SerializeObject(contextCollection); + Assert.Equal("{\"Context Mock\":{\"test\":\"value\"}}", json); + } + + [Fact] + public void Serialization_Multiple_Contexts_Works() + { + var contextCollection = new ContextCollection(); + contextCollection.Add(new ContextMock()); + contextCollection.Add(new ContextMock2()); + var json = JsonConvert.SerializeObject(contextCollection); + Assert.Equal("{\"Context Mock\":{\"test\":\"value\"},\"Context Mock2\":{\"test2\":\"value2\"}}", json); + } + } +} diff --git a/test/SegmentDotNet.Tests/Contexts/GoogleAnalyiticsTests.cs b/test/SegmentDotNet.Tests/Contexts/GoogleAnalyiticsTests.cs new file mode 100644 index 0000000..5f5503b --- /dev/null +++ b/test/SegmentDotNet.Tests/Contexts/GoogleAnalyiticsTests.cs @@ -0,0 +1,36 @@ +namespace SegmentDotNet.Tests.Contexts +{ + using Microsoft.AspNetCore.Http; + using Moq; + using Newtonsoft.Json; + using SegmentDotNet.Client.Request; + using SegmentDotNet.Contexts; + using Xunit; + + public class GoogleAnalyiticsTest : TestBase + { + [Fact] + public void Parses_Google_Analyitics_Cookie() + { + var googleAnalyitics = this.SetupGoogleAnalyitics(); + Assert.Equal("1033501218.1368477899", googleAnalyitics.ClientId); + } + + [Fact] + public void Serialize_With_Extensions() + { + var track = this.SetupRequest(); + track.Context.Add(this.SetupGoogleAnalyitics()); + var request = JsonConvert.DeserializeAnonymousType(this.SetupClient().Serialize(track), new { context = new { Google_Analyitics = new { clientId = "" } } }); + Assert.Equal("1033501218.1368477899", request.context.Google_Analyitics.clientId); + } + + protected GoogleAnalyitics SetupGoogleAnalyitics() + { + var gaCookie = "GA1.2.1033501218.1368477899"; + var httpContextAccessorMock = new Mock(); + httpContextAccessorMock.Setup(h => h.HttpContext.Request.Cookies["_ga"]).Returns(gaCookie); + return new GoogleAnalyitics(httpContextAccessorMock.Object); + } + } +} diff --git a/test/SegmentDotNet.Tests/TestBase.cs b/test/SegmentDotNet.Tests/TestBase.cs new file mode 100644 index 0000000..b8fd5a4 --- /dev/null +++ b/test/SegmentDotNet.Tests/TestBase.cs @@ -0,0 +1,35 @@ +namespace SegmentDotNet.Tests +{ + using Moq; + using Abstractions; + using SegmentDotNet.Client.Request.Abstract; + using System; + using SegmentDotNet.Client; + using Configuration; + using Microsoft.Extensions.Options; + public abstract class TestBase + { + protected SegmentClient SetupClient(string writeKey = null) + { + if (writeKey == null) + { + writeKey = Environment.GetEnvironmentVariable("SEGMENT_WRITE_KEY"); + } + + var optionsMock = new Mock>(); + var configurationMock = new Mock(); + configurationMock.Setup(c => c.WriteKey).Returns(writeKey); + optionsMock.Setup(o => o.Value).Returns(configurationMock.Object); + var segmentClient = new SegmentClient(optionsMock.Object); + return segmentClient; + } + + protected T SetupRequest() + where T : UserTimestampBase + { + var datetimeMock = new Mock(); + datetimeMock.Setup(d => d.UtcNow).Returns(new DateTime(2016, 6, 12, 5, 20, 50, 523, DateTimeKind.Utc)); + return Activator.CreateInstance(typeof(T), datetimeMock.Object) as T; + } + } +} From c21d30c9d429d2b0985ce3b6ad0e2272e522b50a Mon Sep 17 00:00:00 2001 From: William Roush Date: Mon, 13 Jun 2016 03:38:26 -0400 Subject: [PATCH 04/10] Adds basic serializer support, still needs work. --- .../Contexts/Abstract/ContextBase.cs | 23 +++++++++ .../Contexts/GoogleAnalyitics.cs | 6 +-- .../Serializers/ContextBaseSerializer.cs | 50 +++++++++++++++++++ src/SegmentDotNet/project.json | 3 +- .../Contexts/ContextCollectionTests.cs | 21 ++++++-- .../Contexts/GoogleAnalyiticsTests.cs | 5 +- 6 files changed, 97 insertions(+), 11 deletions(-) create mode 100644 src/SegmentDotNet/Contexts/Abstract/ContextBase.cs create mode 100644 src/SegmentDotNet/Contexts/Serializers/ContextBaseSerializer.cs diff --git a/src/SegmentDotNet/Contexts/Abstract/ContextBase.cs b/src/SegmentDotNet/Contexts/Abstract/ContextBase.cs new file mode 100644 index 0000000..da97b15 --- /dev/null +++ b/src/SegmentDotNet/Contexts/Abstract/ContextBase.cs @@ -0,0 +1,23 @@ +namespace SegmentDotNet.Contexts.Abstract +{ + using Interfaces; + using Newtonsoft.Json; + using Serializers; + + [JsonConverter(typeof(ContextBaseSerializer))] + public abstract class ContextBase : IContext + { + public ContextBase() + { + this.IsNested = true; + } + + public abstract string Key { get; } + + [JsonIgnore] + public object Properties { get; set; } + + [JsonIgnore] + public bool IsNested { get; set; } + } +} diff --git a/src/SegmentDotNet/Contexts/GoogleAnalyitics.cs b/src/SegmentDotNet/Contexts/GoogleAnalyitics.cs index 29883c4..7f7875d 100644 --- a/src/SegmentDotNet/Contexts/GoogleAnalyitics.cs +++ b/src/SegmentDotNet/Contexts/GoogleAnalyitics.cs @@ -1,12 +1,12 @@ namespace SegmentDotNet.Contexts { - using Interfaces; + using Abstract; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; using System.Linq; [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class GoogleAnalyitics : IContext + public class GoogleAnalyitics : ContextBase { public GoogleAnalyitics(IHttpContextAccessor httpContextAccessor) { @@ -15,7 +15,7 @@ public GoogleAnalyitics(IHttpContextAccessor httpContextAccessor) protected IHttpContextAccessor HttpContextAccessor { get; set; } - public string Key { get { return "Google Analyitics"; } } + public override string Key { get { return "Google Analytics"; } } [JsonProperty("clientId")] public string ClientId { get { return this.GetGoogleAnalyticsCookie(); } } diff --git a/src/SegmentDotNet/Contexts/Serializers/ContextBaseSerializer.cs b/src/SegmentDotNet/Contexts/Serializers/ContextBaseSerializer.cs new file mode 100644 index 0000000..7588b49 --- /dev/null +++ b/src/SegmentDotNet/Contexts/Serializers/ContextBaseSerializer.cs @@ -0,0 +1,50 @@ +namespace SegmentDotNet.Contexts.Serializers +{ + using Abstract; + using Newtonsoft.Json; + using Newtonsoft.Json.Linq; + using System; + using System.Reflection; + + public class ContextBaseSerializer : JsonConverter + { + protected object NestedLock = new object(); + + protected bool Nested { get; set; } + + public override bool CanWrite { get { lock (this.NestedLock) { return true && !this.Nested; } } } + + public override bool CanConvert(Type objectType) + { + return typeof(ContextBase).GetTypeInfo().IsAssignableFrom(objectType) && !this.Nested; + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var contextBase = value as ContextBase; + JToken valueToken; + lock (this.NestedLock) + { + this.Nested = true; + valueToken = JToken.FromObject(value); + this.Nested = false; + } + + if (contextBase.Properties != null) + { + var propertiesToken = JToken.FromObject(contextBase.Properties); + foreach (var property in propertiesToken.Children()) + { + valueToken[property.Name] = property.Value; + } + } + + valueToken.WriteTo(writer); + } + } +} diff --git a/src/SegmentDotNet/project.json b/src/SegmentDotNet/project.json index ec3cbf4..845a96e 100644 --- a/src/SegmentDotNet/project.json +++ b/src/SegmentDotNet/project.json @@ -4,7 +4,8 @@ "Microsoft.AspNetCore.Http.Abstractions": "1.0.0-rc2-final", "Microsoft.Extensions.Options": "1.0.0-rc2-final", "NETStandard.Library": "1.5.0-rc2-24027", - "Newtonsoft.Json": "8.0.3" + "Newtonsoft.Json": "8.0.3", + "System.Dynamic.Runtime": "4.0.11-rc2-24027" }, "frameworks": { "netstandard1.5": { diff --git a/test/SegmentDotNet.Tests/Contexts/ContextCollectionTests.cs b/test/SegmentDotNet.Tests/Contexts/ContextCollectionTests.cs index 9b10637..b9e1d71 100644 --- a/test/SegmentDotNet.Tests/Contexts/ContextCollectionTests.cs +++ b/test/SegmentDotNet.Tests/Contexts/ContextCollectionTests.cs @@ -2,22 +2,22 @@ { using Newtonsoft.Json; using SegmentDotNet.Contexts; - using SegmentDotNet.Contexts.Interfaces; + using SegmentDotNet.Contexts.Abstract; using Xunit; public class ContextCollectionTests { - protected class ContextMock : IContext + protected class ContextMock : ContextBase { - public string Key { get { return "Context Mock"; } } + public override string Key { get { return "Context Mock"; } } [JsonProperty("test")] public string Test { get { return "value"; } } } - protected class ContextMock2 : IContext + protected class ContextMock2 : ContextBase { - public string Key { get { return "Context Mock2"; } } + public override string Key { get { return "Context Mock2"; } } [JsonProperty("test2")] public string Test { get { return "value2"; } } @@ -41,5 +41,16 @@ public void Serialization_Multiple_Contexts_Works() var json = JsonConvert.SerializeObject(contextCollection); Assert.Equal("{\"Context Mock\":{\"test\":\"value\"},\"Context Mock2\":{\"test2\":\"value2\"}}", json); } + + [Fact] + public void Serialization_With_Custom_Params() + { + var contextCollection = new ContextCollection(); + var contextMock = new ContextMock(); + contextMock.Properties = new { custom1 = 1, custom2 = "custom222" }; + contextCollection.Add(contextMock); + var json = JsonConvert.SerializeObject(contextCollection); + Assert.Equal("{\"Context Mock\":{\"test\":\"value\",\"custom1\":1,\"custom2\":\"custom222\"}}", json); + } } } diff --git a/test/SegmentDotNet.Tests/Contexts/GoogleAnalyiticsTests.cs b/test/SegmentDotNet.Tests/Contexts/GoogleAnalyiticsTests.cs index 5f5503b..9543dfa 100644 --- a/test/SegmentDotNet.Tests/Contexts/GoogleAnalyiticsTests.cs +++ b/test/SegmentDotNet.Tests/Contexts/GoogleAnalyiticsTests.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Http; using Moq; using Newtonsoft.Json; + using Newtonsoft.Json.Linq; using SegmentDotNet.Client.Request; using SegmentDotNet.Contexts; using Xunit; @@ -21,8 +22,8 @@ public void Serialize_With_Extensions() { var track = this.SetupRequest(); track.Context.Add(this.SetupGoogleAnalyitics()); - var request = JsonConvert.DeserializeAnonymousType(this.SetupClient().Serialize(track), new { context = new { Google_Analyitics = new { clientId = "" } } }); - Assert.Equal("1033501218.1368477899", request.context.Google_Analyitics.clientId); + var context = JObject.Parse(this.SetupClient().Serialize(track))["context"]; + Assert.Equal("{\"Google Analytics\":{\"clientId\":\"1033501218.1368477899\"}}", context.ToString(Formatting.None)); } protected GoogleAnalyitics SetupGoogleAnalyitics() From ac514d0c270a048c243d3ea913f7c065d42d0b10 Mon Sep 17 00:00:00 2001 From: William Roush Date: Sat, 18 Jun 2016 01:57:57 -0400 Subject: [PATCH 05/10] Cleans up serialization a lot. --- .../Serializers/ContextBaseSerializer.cs | 19 ++++--------------- .../Json/ForcedObjectResolver.cs | 16 ++++++++++++++++ .../Json/ForcedObjectSerializer.cs | 13 +++++++++++++ .../Contexts/ContextCollectionTests.cs | 15 +++++++++++++++ 4 files changed, 48 insertions(+), 15 deletions(-) create mode 100644 src/SegmentDotNet/Json/ForcedObjectResolver.cs create mode 100644 src/SegmentDotNet/Json/ForcedObjectSerializer.cs diff --git a/src/SegmentDotNet/Contexts/Serializers/ContextBaseSerializer.cs b/src/SegmentDotNet/Contexts/Serializers/ContextBaseSerializer.cs index 7588b49..434d5f2 100644 --- a/src/SegmentDotNet/Contexts/Serializers/ContextBaseSerializer.cs +++ b/src/SegmentDotNet/Contexts/Serializers/ContextBaseSerializer.cs @@ -1,22 +1,17 @@ namespace SegmentDotNet.Contexts.Serializers { using Abstract; + using Json; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Reflection; - + public class ContextBaseSerializer : JsonConverter { - protected object NestedLock = new object(); - - protected bool Nested { get; set; } - - public override bool CanWrite { get { lock (this.NestedLock) { return true && !this.Nested; } } } - public override bool CanConvert(Type objectType) { - return typeof(ContextBase).GetTypeInfo().IsAssignableFrom(objectType) && !this.Nested; + return typeof(ContextBase).GetTypeInfo().IsAssignableFrom(objectType); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) @@ -27,13 +22,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var contextBase = value as ContextBase; - JToken valueToken; - lock (this.NestedLock) - { - this.Nested = true; - valueToken = JToken.FromObject(value); - this.Nested = false; - } + var valueToken = JToken.FromObject(value, new ForcedObjectSerializer()); if (contextBase.Properties != null) { diff --git a/src/SegmentDotNet/Json/ForcedObjectResolver.cs b/src/SegmentDotNet/Json/ForcedObjectResolver.cs new file mode 100644 index 0000000..b75eeae --- /dev/null +++ b/src/SegmentDotNet/Json/ForcedObjectResolver.cs @@ -0,0 +1,16 @@ +namespace SegmentDotNet.Json +{ + using System; + using Newtonsoft.Json.Serialization; + + internal class ForcedObjectResolver : DefaultContractResolver + { + public override JsonContract ResolveContract(Type type) + { + // We're going to null the converter to force it to serialize this as a plain object. + var contract = base.ResolveContract(type); + contract.Converter = null; + return contract; + } + } +} diff --git a/src/SegmentDotNet/Json/ForcedObjectSerializer.cs b/src/SegmentDotNet/Json/ForcedObjectSerializer.cs new file mode 100644 index 0000000..6ad518b --- /dev/null +++ b/src/SegmentDotNet/Json/ForcedObjectSerializer.cs @@ -0,0 +1,13 @@ +namespace SegmentDotNet.Json +{ + using Newtonsoft.Json; + + internal class ForcedObjectSerializer : JsonSerializer + { + public ForcedObjectSerializer() + : base() + { + this.ContractResolver = new ForcedObjectResolver(); + } + } +} diff --git a/test/SegmentDotNet.Tests/Contexts/ContextCollectionTests.cs b/test/SegmentDotNet.Tests/Contexts/ContextCollectionTests.cs index b9e1d71..6a3f91e 100644 --- a/test/SegmentDotNet.Tests/Contexts/ContextCollectionTests.cs +++ b/test/SegmentDotNet.Tests/Contexts/ContextCollectionTests.cs @@ -52,5 +52,20 @@ public void Serialization_With_Custom_Params() var json = JsonConvert.SerializeObject(contextCollection); Assert.Equal("{\"Context Mock\":{\"test\":\"value\",\"custom1\":1,\"custom2\":\"custom222\"}}", json); } + + [Fact] + public void Serialization_With_Nested_Custom_Params() + { + var nestedContextCollection = new ContextCollection(); + var contextMock2 = new ContextMock(); + contextMock2.Properties = new { custom1 = "nested test" }; + nestedContextCollection.Add(contextMock2); + var contextCollection = new ContextCollection(); + var contextMock = new ContextMock(); + contextMock.Properties = new { custom1 = nestedContextCollection }; + contextCollection.Add(contextMock); + var json = JsonConvert.SerializeObject(contextCollection); + Assert.Equal("{\"Context Mock\":{\"test\":\"value\",\"custom1\":{\"Context Mock\":{\"test\":\"value\",\"custom1\":\"nested test\"}}}}", json); + } } } From a31daad050949741904746747a2cbfff91e65c8d Mon Sep 17 00:00:00 2001 From: William Roush Date: Sat, 18 Jun 2016 02:25:12 -0400 Subject: [PATCH 06/10] Code cleanup, adding more tests. --- .../Contexts/Abstract/ContextBase.cs | 8 ------ .../Client/SegmentClientTests.cs | 11 +++++++- .../Serializers/ContextBaseSerializerTests.cs | 28 +++++++++++++++++++ .../ContextCollectionSerializerTests.cs | 28 +++++++++++++++++++ 4 files changed, 66 insertions(+), 9 deletions(-) create mode 100644 test/SegmentDotNet.Tests/Contexts/Serializers/ContextBaseSerializerTests.cs create mode 100644 test/SegmentDotNet.Tests/Contexts/Serializers/ContextCollectionSerializerTests.cs diff --git a/src/SegmentDotNet/Contexts/Abstract/ContextBase.cs b/src/SegmentDotNet/Contexts/Abstract/ContextBase.cs index da97b15..9e2c5cd 100644 --- a/src/SegmentDotNet/Contexts/Abstract/ContextBase.cs +++ b/src/SegmentDotNet/Contexts/Abstract/ContextBase.cs @@ -7,17 +7,9 @@ [JsonConverter(typeof(ContextBaseSerializer))] public abstract class ContextBase : IContext { - public ContextBase() - { - this.IsNested = true; - } - public abstract string Key { get; } [JsonIgnore] public object Properties { get; set; } - - [JsonIgnore] - public bool IsNested { get; set; } } } diff --git a/test/SegmentDotNet.Tests/Client/SegmentClientTests.cs b/test/SegmentDotNet.Tests/Client/SegmentClientTests.cs index b7d3be4..6e83621 100644 --- a/test/SegmentDotNet.Tests/Client/SegmentClientTests.cs +++ b/test/SegmentDotNet.Tests/Client/SegmentClientTests.cs @@ -26,7 +26,7 @@ public SegmentClientTests() } } } - + [Fact] public void Should_Serialize_Timestamp() { @@ -98,6 +98,15 @@ public async Task Can_Batch() await this.SetupClient().Batch(batch); } + [Fact] + public async Task Should_Error_On_Large_Json() + { + var page = this.SetupRequest(); + page.UserId = "1234"; + page.Name = new string('a', 102400); + await Assert.ThrowsAsync(async () => await this.SetupClient().Page(page)); + } + public void Dispose() { } diff --git a/test/SegmentDotNet.Tests/Contexts/Serializers/ContextBaseSerializerTests.cs b/test/SegmentDotNet.Tests/Contexts/Serializers/ContextBaseSerializerTests.cs new file mode 100644 index 0000000..1317f34 --- /dev/null +++ b/test/SegmentDotNet.Tests/Contexts/Serializers/ContextBaseSerializerTests.cs @@ -0,0 +1,28 @@ +namespace SegmentDotNet.Tests.Contexts.Serializers +{ + using SegmentDotNet.Contexts; + using SegmentDotNet.Contexts.Serializers; + using System; + using Xunit; + + public class ContextBaseSerializerTests + { + [Fact] + public void Cannot_ReadJson() + { + Assert.Throws(() => new ContextBaseSerializer().ReadJson(null, null, null, null)); + } + + [Fact] + public void Valid_CanConvert() + { + Assert.True(new ContextBaseSerializer().CanConvert(typeof(GoogleAnalyitics))); + } + + [Fact] + public void Inalid_CanConvert() + { + Assert.False(new ContextBaseSerializer().CanConvert(typeof(string))); + } + } +} diff --git a/test/SegmentDotNet.Tests/Contexts/Serializers/ContextCollectionSerializerTests.cs b/test/SegmentDotNet.Tests/Contexts/Serializers/ContextCollectionSerializerTests.cs new file mode 100644 index 0000000..c3803c5 --- /dev/null +++ b/test/SegmentDotNet.Tests/Contexts/Serializers/ContextCollectionSerializerTests.cs @@ -0,0 +1,28 @@ +namespace SegmentDotNet.Tests.Contexts.Serializers +{ + using SegmentDotNet.Contexts; + using SegmentDotNet.Contexts.Serializers; + using System; + using Xunit; + + public class ContextCollectionSerializerTests + { + [Fact] + public void Cannot_ReadJson() + { + Assert.Throws(() => new ContextCollectionSerializer().ReadJson(null, null, null, null)); + } + + [Fact] + public void Valid_CanConvert() + { + Assert.True(new ContextCollectionSerializer().CanConvert(typeof(ContextCollection))); + } + + [Fact] + public void Inalid_CanConvert() + { + Assert.False(new ContextCollectionSerializer().CanConvert(typeof(string))); + } + } +} From 6c5ba8c5bc053bb12e143254519301b3706f6e47 Mon Sep 17 00:00:00 2001 From: William Roush Date: Sat, 18 Jun 2016 22:54:46 -0400 Subject: [PATCH 07/10] New class layout to support all new features. --- .../Client/Request/Abstract/AnonymousBase.cs | 6 +- .../Client/Request/Abstract/Base.cs | 15 ++-- .../Client/Request/Abstract/TraitsBase.cs | 11 +-- .../Request/Abstract/UserTimestampBase.cs | 8 ++- src/SegmentDotNet/Client/Request/Alias.cs | 6 +- src/SegmentDotNet/Client/Request/Batch.cs | 4 +- src/SegmentDotNet/Client/Request/Group.cs | 7 +- src/SegmentDotNet/Client/Request/Identify.cs | 7 +- src/SegmentDotNet/Client/Request/Page.cs | 6 +- src/SegmentDotNet/Client/Request/Screen.cs | 6 +- src/SegmentDotNet/Client/Request/Track.cs | 6 +- src/SegmentDotNet/Client/SegmentClient.cs | 2 +- .../Contexts/Abstract/ContextBase.cs | 15 ---- .../Contexts/ContextCollection.cs | 23 ------ .../Contexts/GoogleAnalyitics.cs | 29 -------- .../Contexts/Interfaces/IContext.cs | 10 --- .../ContextCollectionSerializer.cs | 31 -------- .../Populators/Contexts/Context.cs | 20 ++++++ .../Populators/Contexts/GoogleAnalyitics.cs | 46 ++++++++++++ .../Populators/Contexts/IContext.cs | 9 +++ .../Populators/Integrations/Integrations.cs | 6 ++ src/SegmentDotNet/Populators/Populator.cs | 9 +++ .../Populators/Properties/Properties.cs | 6 ++ src/SegmentDotNet/Populators/Traits/Traits.cs | 6 ++ .../Abstract/PropertyContainerBase.cs | 18 +++++ .../Interfaces/IPropertyContainer.cs | 11 +++ .../PropertyContainerBaseSerializer.cs} | 15 ++-- .../Client/SegmentClientTests.cs | 20 +++--- .../Contexts/ContextCollectionTests.cs | 71 ------------------- .../Serializers/ContextBaseSerializerTests.cs | 28 -------- .../ContextCollectionSerializerTests.cs | 28 -------- .../Contexts/GoogleAnalyiticsTests.cs | 16 ++--- .../PropertyContainerBaseSerializerTests.cs | 30 ++++++++ test/SegmentDotNet.Tests/TestBase.cs | 7 ++ 34 files changed, 242 insertions(+), 296 deletions(-) delete mode 100644 src/SegmentDotNet/Contexts/Abstract/ContextBase.cs delete mode 100644 src/SegmentDotNet/Contexts/ContextCollection.cs delete mode 100644 src/SegmentDotNet/Contexts/GoogleAnalyitics.cs delete mode 100644 src/SegmentDotNet/Contexts/Interfaces/IContext.cs delete mode 100644 src/SegmentDotNet/Contexts/Serializers/ContextCollectionSerializer.cs create mode 100644 src/SegmentDotNet/Populators/Contexts/Context.cs create mode 100644 src/SegmentDotNet/Populators/Contexts/GoogleAnalyitics.cs create mode 100644 src/SegmentDotNet/Populators/Contexts/IContext.cs create mode 100644 src/SegmentDotNet/Populators/Integrations/Integrations.cs create mode 100644 src/SegmentDotNet/Populators/Populator.cs create mode 100644 src/SegmentDotNet/Populators/Properties/Properties.cs create mode 100644 src/SegmentDotNet/Populators/Traits/Traits.cs create mode 100644 src/SegmentDotNet/PropertyContainers/Abstract/PropertyContainerBase.cs create mode 100644 src/SegmentDotNet/PropertyContainers/Interfaces/IPropertyContainer.cs rename src/SegmentDotNet/{Contexts/Serializers/ContextBaseSerializer.cs => PropertyContainers/Serializers/PropertyContainerBaseSerializer.cs} (66%) delete mode 100644 test/SegmentDotNet.Tests/Contexts/ContextCollectionTests.cs delete mode 100644 test/SegmentDotNet.Tests/Contexts/Serializers/ContextBaseSerializerTests.cs delete mode 100644 test/SegmentDotNet.Tests/Contexts/Serializers/ContextCollectionSerializerTests.cs rename test/SegmentDotNet.Tests/{ => Populators}/Contexts/GoogleAnalyiticsTests.cs (64%) create mode 100644 test/SegmentDotNet.Tests/PropertyContainers/Serializers/PropertyContainerBaseSerializerTests.cs diff --git a/src/SegmentDotNet/Client/Request/Abstract/AnonymousBase.cs b/src/SegmentDotNet/Client/Request/Abstract/AnonymousBase.cs index 6b8bb74..633b67c 100644 --- a/src/SegmentDotNet/Client/Request/Abstract/AnonymousBase.cs +++ b/src/SegmentDotNet/Client/Request/Abstract/AnonymousBase.cs @@ -2,13 +2,13 @@ { using Abstractions; using Newtonsoft.Json; + using Populators.Contexts; + using Populators.Integrations; public abstract class AnonymousBase : UserTimestampBase { - public AnonymousBase(IDateTime datetime) - : base(datetime) + public AnonymousBase(Context context, IDateTime datetime, Integrations integrations) : base(context, datetime, integrations) { - } [JsonProperty("anonymousId")] diff --git a/src/SegmentDotNet/Client/Request/Abstract/Base.cs b/src/SegmentDotNet/Client/Request/Abstract/Base.cs index 9abf296..eb72bf9 100644 --- a/src/SegmentDotNet/Client/Request/Abstract/Base.cs +++ b/src/SegmentDotNet/Client/Request/Abstract/Base.cs @@ -1,22 +1,25 @@ namespace SegmentDotNet.Client.Request.Abstract { - using Contexts; using Interfaces; using Newtonsoft.Json; - using System.Collections.Generic; + using Populators.Contexts; + using Populators.Integrations; public abstract class Base : ISegmentRequest { - public Base() + public Base( + Context context, + Integrations integrations) { - this.Context = new ContextCollection(); + this.Context = context; + this.Integrations = integrations; } [JsonProperty("context")] - public ContextCollection Context { get; set; } + public Context Context { get; set; } [JsonProperty("integrations")] - public Dictionary Integrations { get; set; } + public Integrations Integrations { get; set; } public abstract string Endpoint { get; } } diff --git a/src/SegmentDotNet/Client/Request/Abstract/TraitsBase.cs b/src/SegmentDotNet/Client/Request/Abstract/TraitsBase.cs index afa9ee2..52bcc1b 100644 --- a/src/SegmentDotNet/Client/Request/Abstract/TraitsBase.cs +++ b/src/SegmentDotNet/Client/Request/Abstract/TraitsBase.cs @@ -2,17 +2,18 @@ { using Abstractions; using Newtonsoft.Json; - using System.Collections.Generic; + using Populators.Traits; + using Populators.Contexts; + using Populators.Integrations; public abstract class TraitsBase : AnonymousBase { - public TraitsBase(IDateTime datetime) - : base(datetime) + public TraitsBase(Context context, IDateTime datetime, Integrations integrations, Traits traits) : base(context, datetime, integrations) { - + this.Traits = traits; } [JsonProperty("traits")] - public Dictionary Traits { get; set; } + public Traits Traits { get; set; } } } diff --git a/src/SegmentDotNet/Client/Request/Abstract/UserTimestampBase.cs b/src/SegmentDotNet/Client/Request/Abstract/UserTimestampBase.cs index 7a20e6f..a4fff93 100644 --- a/src/SegmentDotNet/Client/Request/Abstract/UserTimestampBase.cs +++ b/src/SegmentDotNet/Client/Request/Abstract/UserTimestampBase.cs @@ -3,11 +3,17 @@ using Abstractions; using Newtonsoft.Json; using System; + using Populators.Contexts; + using Populators.Integrations; [JsonObject(MemberSerialization = MemberSerialization.OptIn)] public abstract class UserTimestampBase : Base { - public UserTimestampBase(IDateTime datetime) + public UserTimestampBase( + Context context, + IDateTime datetime, + Integrations integrations) + : base(context, integrations) { this.DateTime = datetime; } diff --git a/src/SegmentDotNet/Client/Request/Alias.cs b/src/SegmentDotNet/Client/Request/Alias.cs index 4f8a9b7..03fc3d0 100644 --- a/src/SegmentDotNet/Client/Request/Alias.cs +++ b/src/SegmentDotNet/Client/Request/Alias.cs @@ -5,14 +5,14 @@ namespace SegmentDotNet.Client.Request using Abstract; using Abstractions; using Newtonsoft.Json; + using Populators.Contexts; + using Populators.Integrations; [JsonObject(MemberSerialization = MemberSerialization.OptIn)] public class Alias : UserTimestampBase { - public Alias(IDateTime datetime) - : base(datetime) + public Alias(Context context, IDateTime datetime, Integrations integrations) : base(context, datetime, integrations) { - } public override string Type { get { return "alias"; } } diff --git a/src/SegmentDotNet/Client/Request/Batch.cs b/src/SegmentDotNet/Client/Request/Batch.cs index 8e80a75..f213462 100644 --- a/src/SegmentDotNet/Client/Request/Batch.cs +++ b/src/SegmentDotNet/Client/Request/Batch.cs @@ -3,11 +3,13 @@ using Abstract; using Newtonsoft.Json; using System.Collections.Generic; + using Populators.Contexts; + using Populators.Integrations; [JsonObject(MemberSerialization = MemberSerialization.OptIn)] public class Batch : Base { - public Batch() + public Batch(Context context, Integrations integrations) : base(context, integrations) { this.Items = new List(); } diff --git a/src/SegmentDotNet/Client/Request/Group.cs b/src/SegmentDotNet/Client/Request/Group.cs index 8cd88f5..6ec399b 100644 --- a/src/SegmentDotNet/Client/Request/Group.cs +++ b/src/SegmentDotNet/Client/Request/Group.cs @@ -5,14 +5,15 @@ namespace SegmentDotNet.Client.Request using Abstract; using Abstractions; using Newtonsoft.Json; + using Populators.Traits; + using Populators.Contexts; + using Populators.Integrations; [JsonObject(MemberSerialization = MemberSerialization.OptIn)] public class Group : TraitsBase { - public Group(IDateTime datetime) - : base(datetime) + public Group(Context context, IDateTime datetime, Integrations integrations, Traits traits) : base(context, datetime, integrations, traits) { - } public override string Type { get { return "group"; } } diff --git a/src/SegmentDotNet/Client/Request/Identify.cs b/src/SegmentDotNet/Client/Request/Identify.cs index fd4b807..58bcc98 100644 --- a/src/SegmentDotNet/Client/Request/Identify.cs +++ b/src/SegmentDotNet/Client/Request/Identify.cs @@ -3,14 +3,15 @@ using Abstract; using Abstractions; using Newtonsoft.Json; + using Populators.Traits; + using Populators.Contexts; + using Populators.Integrations; [JsonObject(MemberSerialization = MemberSerialization.OptIn)] public class Identify : TraitsBase { - public Identify(IDateTime datetime) - : base(datetime) + public Identify(Context context, IDateTime datetime, Integrations integrations, Traits traits) : base(context, datetime, integrations, traits) { - } public override string Type { get { return "identify"; } } diff --git a/src/SegmentDotNet/Client/Request/Page.cs b/src/SegmentDotNet/Client/Request/Page.cs index c811834..0f14f31 100644 --- a/src/SegmentDotNet/Client/Request/Page.cs +++ b/src/SegmentDotNet/Client/Request/Page.cs @@ -3,14 +3,14 @@ using Abstract; using Abstractions; using Newtonsoft.Json; + using Populators.Contexts; + using Populators.Integrations; [JsonObject(MemberSerialization = MemberSerialization.OptIn)] public class Page : AnonymousBase { - public Page(IDateTime datetime) - : base(datetime) + public Page(Context context, IDateTime datetime, Integrations integrations) : base(context, datetime, integrations) { - } public override string Type { get { return "page"; } } diff --git a/src/SegmentDotNet/Client/Request/Screen.cs b/src/SegmentDotNet/Client/Request/Screen.cs index d4eea1a..0df7f65 100644 --- a/src/SegmentDotNet/Client/Request/Screen.cs +++ b/src/SegmentDotNet/Client/Request/Screen.cs @@ -3,14 +3,14 @@ using Abstract; using Abstractions; using Newtonsoft.Json; + using Populators.Contexts; + using Populators.Integrations; [JsonObject(MemberSerialization = MemberSerialization.OptIn)] public class Screen : AnonymousBase { - public Screen(IDateTime datetime) - : base(datetime) + public Screen(Context context, IDateTime datetime, Integrations integrations) : base(context, datetime, integrations) { - } public override string Type { get { return "screen"; } } diff --git a/src/SegmentDotNet/Client/Request/Track.cs b/src/SegmentDotNet/Client/Request/Track.cs index 5019d6b..e6147c4 100644 --- a/src/SegmentDotNet/Client/Request/Track.cs +++ b/src/SegmentDotNet/Client/Request/Track.cs @@ -4,14 +4,14 @@ using Abstractions; using Newtonsoft.Json; using System.Collections.Generic; + using Populators.Contexts; + using Populators.Integrations; [JsonObject(MemberSerialization = MemberSerialization.OptIn)] public class Track : AnonymousBase { - public Track(IDateTime datetime) - : base(datetime) + public Track(Context context, IDateTime datetime, Integrations integrations) : base(context, datetime, integrations) { - } public override string Type { get { return "track"; } } diff --git a/src/SegmentDotNet/Client/SegmentClient.cs b/src/SegmentDotNet/Client/SegmentClient.cs index 0e316a1..ef6f0ad 100644 --- a/src/SegmentDotNet/Client/SegmentClient.cs +++ b/src/SegmentDotNet/Client/SegmentClient.cs @@ -73,7 +73,7 @@ public async Task Post(Base baseClass) } } - public string Serialize(Base baseClass) + public string Serialize(object baseClass) { return JsonConvert.SerializeObject(baseClass); } diff --git a/src/SegmentDotNet/Contexts/Abstract/ContextBase.cs b/src/SegmentDotNet/Contexts/Abstract/ContextBase.cs deleted file mode 100644 index 9e2c5cd..0000000 --- a/src/SegmentDotNet/Contexts/Abstract/ContextBase.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace SegmentDotNet.Contexts.Abstract -{ - using Interfaces; - using Newtonsoft.Json; - using Serializers; - - [JsonConverter(typeof(ContextBaseSerializer))] - public abstract class ContextBase : IContext - { - public abstract string Key { get; } - - [JsonIgnore] - public object Properties { get; set; } - } -} diff --git a/src/SegmentDotNet/Contexts/ContextCollection.cs b/src/SegmentDotNet/Contexts/ContextCollection.cs deleted file mode 100644 index 27cf333..0000000 --- a/src/SegmentDotNet/Contexts/ContextCollection.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace SegmentDotNet.Contexts -{ - using Interfaces; - using Newtonsoft.Json; - using Serializers; - using System.Collections.Generic; - - [JsonConverter(typeof(ContextCollectionSerializer))] - public class ContextCollection - { - public ContextCollection() - { - this.Contexts = new List(); - } - - public List Contexts { get; set; } - - public void Add(IContext context) - { - this.Contexts.Add(context); - } - } -} diff --git a/src/SegmentDotNet/Contexts/GoogleAnalyitics.cs b/src/SegmentDotNet/Contexts/GoogleAnalyitics.cs deleted file mode 100644 index 7f7875d..0000000 --- a/src/SegmentDotNet/Contexts/GoogleAnalyitics.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace SegmentDotNet.Contexts -{ - using Abstract; - using Microsoft.AspNetCore.Http; - using Newtonsoft.Json; - using System.Linq; - - [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class GoogleAnalyitics : ContextBase - { - public GoogleAnalyitics(IHttpContextAccessor httpContextAccessor) - { - this.HttpContextAccessor = httpContextAccessor; - } - - protected IHttpContextAccessor HttpContextAccessor { get; set; } - - public override string Key { get { return "Google Analytics"; } } - - [JsonProperty("clientId")] - public string ClientId { get { return this.GetGoogleAnalyticsCookie(); } } - - private string GetGoogleAnalyticsCookie() - { - var cookie = this.HttpContextAccessor.HttpContext.Request.Cookies["_ga"]; - return cookie == null ? null : string.Join(".", cookie.Split('.').Reverse().Take(2).Reverse()); - } - } -} diff --git a/src/SegmentDotNet/Contexts/Interfaces/IContext.cs b/src/SegmentDotNet/Contexts/Interfaces/IContext.cs deleted file mode 100644 index 5ebb089..0000000 --- a/src/SegmentDotNet/Contexts/Interfaces/IContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace SegmentDotNet.Contexts.Interfaces -{ - using Newtonsoft.Json; - - public interface IContext - { - [JsonIgnore] - string Key { get; } - } -} diff --git a/src/SegmentDotNet/Contexts/Serializers/ContextCollectionSerializer.cs b/src/SegmentDotNet/Contexts/Serializers/ContextCollectionSerializer.cs deleted file mode 100644 index 452030e..0000000 --- a/src/SegmentDotNet/Contexts/Serializers/ContextCollectionSerializer.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace SegmentDotNet.Contexts.Serializers -{ - using System; - using Newtonsoft.Json; - - public class ContextCollectionSerializer : JsonConverter - { - public override bool CanConvert(Type objectType) - { - return typeof(ContextCollection) == objectType; - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - throw new NotImplementedException(); - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - var contextCollection = value as ContextCollection; - writer.WriteStartObject(); - foreach (var context in contextCollection.Contexts) - { - writer.WritePropertyName(context.Key); - serializer.Serialize(writer, context); - } - - writer.WriteEndObject(); - } - } -} diff --git a/src/SegmentDotNet/Populators/Contexts/Context.cs b/src/SegmentDotNet/Populators/Contexts/Context.cs new file mode 100644 index 0000000..7a6897c --- /dev/null +++ b/src/SegmentDotNet/Populators/Contexts/Context.cs @@ -0,0 +1,20 @@ +namespace SegmentDotNet.Populators.Contexts +{ + using System; + using System.Collections.Generic; + + public class Context : Populator + { + public Context(IEnumerable contexts) + { + this.Contexts = new List(contexts); + } + + protected List Contexts { get; set; } + + public override void Prepare() + { + this.Contexts.ForEach(c => c.UpdateContext(this.Properties)); + } + } +} diff --git a/src/SegmentDotNet/Populators/Contexts/GoogleAnalyitics.cs b/src/SegmentDotNet/Populators/Contexts/GoogleAnalyitics.cs new file mode 100644 index 0000000..44479c9 --- /dev/null +++ b/src/SegmentDotNet/Populators/Contexts/GoogleAnalyitics.cs @@ -0,0 +1,46 @@ +namespace SegmentDotNet.Populators.Contexts +{ + using Microsoft.AspNetCore.Http; + using Newtonsoft.Json; + using System.Linq; + using System.Collections.Generic; + + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public class GoogleAnalyitics : IContext + { + public class GoogleAnalyiticsPayload + { + [JsonProperty("clientId")] + public string ClientId { get; set; } + } + + public GoogleAnalyitics(IHttpContextAccessor httpContextAccessor) + { + this.HttpContextAccessor = httpContextAccessor; + this.Payload = this.GeneratePayload(); + } + + private GoogleAnalyiticsPayload GeneratePayload() + { + return new GoogleAnalyiticsPayload + { + ClientId = this.GetGoogleAnalyticsCookie() + }; + } + + public GoogleAnalyiticsPayload Payload { get; set; } + + protected IHttpContextAccessor HttpContextAccessor { get; set; } + + private string GetGoogleAnalyticsCookie() + { + var cookie = this.HttpContextAccessor.HttpContext.Request.Cookies["_ga"]; + return cookie == null ? null : string.Join(".", cookie.Split('.').Reverse().Take(2).Reverse()); + } + + public void UpdateContext(Dictionary context) + { + context.Add("Google Analyitics", this.Payload); + } + } +} diff --git a/src/SegmentDotNet/Populators/Contexts/IContext.cs b/src/SegmentDotNet/Populators/Contexts/IContext.cs new file mode 100644 index 0000000..bf830a2 --- /dev/null +++ b/src/SegmentDotNet/Populators/Contexts/IContext.cs @@ -0,0 +1,9 @@ +namespace SegmentDotNet.Populators.Contexts +{ + using System.Collections.Generic; + + public interface IContext + { + void UpdateContext(Dictionary context); + } +} diff --git a/src/SegmentDotNet/Populators/Integrations/Integrations.cs b/src/SegmentDotNet/Populators/Integrations/Integrations.cs new file mode 100644 index 0000000..2d9a5bf --- /dev/null +++ b/src/SegmentDotNet/Populators/Integrations/Integrations.cs @@ -0,0 +1,6 @@ +namespace SegmentDotNet.Populators.Integrations +{ + public class Integrations + { + } +} diff --git a/src/SegmentDotNet/Populators/Populator.cs b/src/SegmentDotNet/Populators/Populator.cs new file mode 100644 index 0000000..1e68b87 --- /dev/null +++ b/src/SegmentDotNet/Populators/Populator.cs @@ -0,0 +1,9 @@ +namespace SegmentDotNet.Populators +{ + using PropertyContainers.Abstract; + + public abstract class Populator : PropertyContainerBase + { + public abstract void Prepare(); + } +} diff --git a/src/SegmentDotNet/Populators/Properties/Properties.cs b/src/SegmentDotNet/Populators/Properties/Properties.cs new file mode 100644 index 0000000..2ccd074 --- /dev/null +++ b/src/SegmentDotNet/Populators/Properties/Properties.cs @@ -0,0 +1,6 @@ +namespace SegmentDotNet.Populators.Properties +{ + public class Properties + { + } +} diff --git a/src/SegmentDotNet/Populators/Traits/Traits.cs b/src/SegmentDotNet/Populators/Traits/Traits.cs new file mode 100644 index 0000000..8d63672 --- /dev/null +++ b/src/SegmentDotNet/Populators/Traits/Traits.cs @@ -0,0 +1,6 @@ +namespace SegmentDotNet.Populators.Traits +{ + public class Traits + { + } +} diff --git a/src/SegmentDotNet/PropertyContainers/Abstract/PropertyContainerBase.cs b/src/SegmentDotNet/PropertyContainers/Abstract/PropertyContainerBase.cs new file mode 100644 index 0000000..7cb8558 --- /dev/null +++ b/src/SegmentDotNet/PropertyContainers/Abstract/PropertyContainerBase.cs @@ -0,0 +1,18 @@ +namespace SegmentDotNet.PropertyContainers.Abstract +{ + using Interfaces; + using Newtonsoft.Json; + using Serializers; + using System.Collections.Generic; + + [JsonConverter(typeof(PropertyContainerBaseSerializer))] + public abstract class PropertyContainerBase : IPropertyContainer + { + public PropertyContainerBase() + { + this.Properties = new Dictionary(); + } + + public Dictionary Properties { get; set; } + } +} diff --git a/src/SegmentDotNet/PropertyContainers/Interfaces/IPropertyContainer.cs b/src/SegmentDotNet/PropertyContainers/Interfaces/IPropertyContainer.cs new file mode 100644 index 0000000..b4f638a --- /dev/null +++ b/src/SegmentDotNet/PropertyContainers/Interfaces/IPropertyContainer.cs @@ -0,0 +1,11 @@ +namespace SegmentDotNet.PropertyContainers.Interfaces +{ + using Newtonsoft.Json; + using System.Collections.Generic; + + public interface IPropertyContainer + { + [JsonIgnore] + Dictionary Properties { get; set; } + } +} diff --git a/src/SegmentDotNet/Contexts/Serializers/ContextBaseSerializer.cs b/src/SegmentDotNet/PropertyContainers/Serializers/PropertyContainerBaseSerializer.cs similarity index 66% rename from src/SegmentDotNet/Contexts/Serializers/ContextBaseSerializer.cs rename to src/SegmentDotNet/PropertyContainers/Serializers/PropertyContainerBaseSerializer.cs index 434d5f2..c225835 100644 --- a/src/SegmentDotNet/Contexts/Serializers/ContextBaseSerializer.cs +++ b/src/SegmentDotNet/PropertyContainers/Serializers/PropertyContainerBaseSerializer.cs @@ -1,17 +1,17 @@ -namespace SegmentDotNet.Contexts.Serializers +namespace SegmentDotNet.PropertyContainers.Serializers { - using Abstract; + using Interfaces; using Json; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Reflection; - public class ContextBaseSerializer : JsonConverter + public class PropertyContainerBaseSerializer : JsonConverter { public override bool CanConvert(Type objectType) { - return typeof(ContextBase).GetTypeInfo().IsAssignableFrom(objectType); + return typeof(IPropertyContainer).GetTypeInfo().IsAssignableFrom(objectType); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) @@ -21,12 +21,11 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { - var contextBase = value as ContextBase; + var propertyContainer = value as IPropertyContainer; var valueToken = JToken.FromObject(value, new ForcedObjectSerializer()); - - if (contextBase.Properties != null) + if (propertyContainer.Properties != null) { - var propertiesToken = JToken.FromObject(contextBase.Properties); + var propertiesToken = JToken.FromObject(propertyContainer.Properties); foreach (var property in propertiesToken.Children()) { valueToken[property.Name] = property.Value; diff --git a/test/SegmentDotNet.Tests/Client/SegmentClientTests.cs b/test/SegmentDotNet.Tests/Client/SegmentClientTests.cs index 6e83621..0f0c83e 100644 --- a/test/SegmentDotNet.Tests/Client/SegmentClientTests.cs +++ b/test/SegmentDotNet.Tests/Client/SegmentClientTests.cs @@ -30,7 +30,7 @@ public SegmentClientTests() [Fact] public void Should_Serialize_Timestamp() { - var json = this.SetupClient().Serialize(this.SetupRequest()); + var json = this.SetupClient().Serialize(new Alias(null, this.GetDateTimeMock(), null)); var jsonObject = JsonConvert.DeserializeAnonymousType(json, new { timestamp = "" }); Assert.Equal("2016-06-12T05:20:50.523Z", jsonObject.timestamp); } @@ -38,7 +38,7 @@ public void Should_Serialize_Timestamp() [Fact] public async Task Can_Identify() { - var identify = this.SetupRequest(); + var identify = new Identify(null, this.GetDateTimeMock(), null, null); identify.UserId = "1234"; await this.SetupClient().Identify(identify); } @@ -46,7 +46,7 @@ public async Task Can_Identify() [Fact] public async Task Can_Group() { - var group = this.SetupRequest(); + var group = new Group(null, this.GetDateTimeMock(), null, null); group.GroupId = "G1234"; group.UserId = "1234"; await this.SetupClient().Group(group); @@ -55,7 +55,7 @@ public async Task Can_Group() [Fact] public async Task Can_Track() { - var track = this.SetupRequest(); + var track = new Track(null, this.GetDateTimeMock(), null); track.UserId = "1234"; track.Event = "Can_Track Test"; await this.SetupClient().Track(track); @@ -64,7 +64,7 @@ public async Task Can_Track() [Fact] public async Task Can_Page() { - var page = this.SetupRequest(); + var page = new Page(null, this.GetDateTimeMock(), null); page.UserId = "1234"; page.Name = "Can_Page Test"; await this.SetupClient().Page(page); @@ -73,7 +73,7 @@ public async Task Can_Page() [Fact] public async Task Can_Screen() { - var screen = this.SetupRequest(); + var screen = new Screen(null, this.GetDateTimeMock(), null); screen.UserId = "1234"; screen.Name = "Can_Screen Test"; await this.SetupClient().Screen(screen); @@ -82,7 +82,7 @@ public async Task Can_Screen() [Fact] public async Task Can_Alias() { - var alias = this.SetupRequest(); + var alias = new Alias(null, this.GetDateTimeMock(), null); alias.PreviousId = "P1234"; alias.UserId = "1234"; await this.SetupClient().Alias(alias); @@ -91,9 +91,9 @@ public async Task Can_Alias() [Fact] public async Task Can_Batch() { - var identify = this.SetupRequest(); + var identify = new Identify(null, this.GetDateTimeMock(), null, null); identify.UserId = "B1234"; - var batch = new Batch(); + var batch = new Batch(null, null); batch.Items.Add(identify); await this.SetupClient().Batch(batch); } @@ -101,7 +101,7 @@ public async Task Can_Batch() [Fact] public async Task Should_Error_On_Large_Json() { - var page = this.SetupRequest(); + var page = new Page(null, this.GetDateTimeMock(), null); page.UserId = "1234"; page.Name = new string('a', 102400); await Assert.ThrowsAsync(async () => await this.SetupClient().Page(page)); diff --git a/test/SegmentDotNet.Tests/Contexts/ContextCollectionTests.cs b/test/SegmentDotNet.Tests/Contexts/ContextCollectionTests.cs deleted file mode 100644 index 6a3f91e..0000000 --- a/test/SegmentDotNet.Tests/Contexts/ContextCollectionTests.cs +++ /dev/null @@ -1,71 +0,0 @@ -namespace SegmentDotNet.Tests.Contexts -{ - using Newtonsoft.Json; - using SegmentDotNet.Contexts; - using SegmentDotNet.Contexts.Abstract; - using Xunit; - - public class ContextCollectionTests - { - protected class ContextMock : ContextBase - { - public override string Key { get { return "Context Mock"; } } - - [JsonProperty("test")] - public string Test { get { return "value"; } } - } - - protected class ContextMock2 : ContextBase - { - public override string Key { get { return "Context Mock2"; } } - - [JsonProperty("test2")] - public string Test { get { return "value2"; } } - } - - [Fact] - public void Serialization_Works() - { - var contextCollection = new ContextCollection(); - contextCollection.Add(new ContextMock()); - var json = JsonConvert.SerializeObject(contextCollection); - Assert.Equal("{\"Context Mock\":{\"test\":\"value\"}}", json); - } - - [Fact] - public void Serialization_Multiple_Contexts_Works() - { - var contextCollection = new ContextCollection(); - contextCollection.Add(new ContextMock()); - contextCollection.Add(new ContextMock2()); - var json = JsonConvert.SerializeObject(contextCollection); - Assert.Equal("{\"Context Mock\":{\"test\":\"value\"},\"Context Mock2\":{\"test2\":\"value2\"}}", json); - } - - [Fact] - public void Serialization_With_Custom_Params() - { - var contextCollection = new ContextCollection(); - var contextMock = new ContextMock(); - contextMock.Properties = new { custom1 = 1, custom2 = "custom222" }; - contextCollection.Add(contextMock); - var json = JsonConvert.SerializeObject(contextCollection); - Assert.Equal("{\"Context Mock\":{\"test\":\"value\",\"custom1\":1,\"custom2\":\"custom222\"}}", json); - } - - [Fact] - public void Serialization_With_Nested_Custom_Params() - { - var nestedContextCollection = new ContextCollection(); - var contextMock2 = new ContextMock(); - contextMock2.Properties = new { custom1 = "nested test" }; - nestedContextCollection.Add(contextMock2); - var contextCollection = new ContextCollection(); - var contextMock = new ContextMock(); - contextMock.Properties = new { custom1 = nestedContextCollection }; - contextCollection.Add(contextMock); - var json = JsonConvert.SerializeObject(contextCollection); - Assert.Equal("{\"Context Mock\":{\"test\":\"value\",\"custom1\":{\"Context Mock\":{\"test\":\"value\",\"custom1\":\"nested test\"}}}}", json); - } - } -} diff --git a/test/SegmentDotNet.Tests/Contexts/Serializers/ContextBaseSerializerTests.cs b/test/SegmentDotNet.Tests/Contexts/Serializers/ContextBaseSerializerTests.cs deleted file mode 100644 index 1317f34..0000000 --- a/test/SegmentDotNet.Tests/Contexts/Serializers/ContextBaseSerializerTests.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace SegmentDotNet.Tests.Contexts.Serializers -{ - using SegmentDotNet.Contexts; - using SegmentDotNet.Contexts.Serializers; - using System; - using Xunit; - - public class ContextBaseSerializerTests - { - [Fact] - public void Cannot_ReadJson() - { - Assert.Throws(() => new ContextBaseSerializer().ReadJson(null, null, null, null)); - } - - [Fact] - public void Valid_CanConvert() - { - Assert.True(new ContextBaseSerializer().CanConvert(typeof(GoogleAnalyitics))); - } - - [Fact] - public void Inalid_CanConvert() - { - Assert.False(new ContextBaseSerializer().CanConvert(typeof(string))); - } - } -} diff --git a/test/SegmentDotNet.Tests/Contexts/Serializers/ContextCollectionSerializerTests.cs b/test/SegmentDotNet.Tests/Contexts/Serializers/ContextCollectionSerializerTests.cs deleted file mode 100644 index c3803c5..0000000 --- a/test/SegmentDotNet.Tests/Contexts/Serializers/ContextCollectionSerializerTests.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace SegmentDotNet.Tests.Contexts.Serializers -{ - using SegmentDotNet.Contexts; - using SegmentDotNet.Contexts.Serializers; - using System; - using Xunit; - - public class ContextCollectionSerializerTests - { - [Fact] - public void Cannot_ReadJson() - { - Assert.Throws(() => new ContextCollectionSerializer().ReadJson(null, null, null, null)); - } - - [Fact] - public void Valid_CanConvert() - { - Assert.True(new ContextCollectionSerializer().CanConvert(typeof(ContextCollection))); - } - - [Fact] - public void Inalid_CanConvert() - { - Assert.False(new ContextCollectionSerializer().CanConvert(typeof(string))); - } - } -} diff --git a/test/SegmentDotNet.Tests/Contexts/GoogleAnalyiticsTests.cs b/test/SegmentDotNet.Tests/Populators/Contexts/GoogleAnalyiticsTests.cs similarity index 64% rename from test/SegmentDotNet.Tests/Contexts/GoogleAnalyiticsTests.cs rename to test/SegmentDotNet.Tests/Populators/Contexts/GoogleAnalyiticsTests.cs index 9543dfa..bafac42 100644 --- a/test/SegmentDotNet.Tests/Contexts/GoogleAnalyiticsTests.cs +++ b/test/SegmentDotNet.Tests/Populators/Contexts/GoogleAnalyiticsTests.cs @@ -1,11 +1,11 @@ -namespace SegmentDotNet.Tests.Contexts +namespace SegmentDotNet.Tests.Populators.Contexts { using Microsoft.AspNetCore.Http; using Moq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; - using SegmentDotNet.Client.Request; - using SegmentDotNet.Contexts; + using SegmentDotNet.Populators.Contexts; + using System.Collections.Generic; using Xunit; public class GoogleAnalyiticsTest : TestBase @@ -14,16 +14,16 @@ public class GoogleAnalyiticsTest : TestBase public void Parses_Google_Analyitics_Cookie() { var googleAnalyitics = this.SetupGoogleAnalyitics(); - Assert.Equal("1033501218.1368477899", googleAnalyitics.ClientId); + Assert.Equal("1033501218.1368477899", googleAnalyitics.Payload.ClientId); } [Fact] public void Serialize_With_Extensions() { - var track = this.SetupRequest(); - track.Context.Add(this.SetupGoogleAnalyitics()); - var context = JObject.Parse(this.SetupClient().Serialize(track))["context"]; - Assert.Equal("{\"Google Analytics\":{\"clientId\":\"1033501218.1368477899\"}}", context.ToString(Formatting.None)); + var context = new Context(new List() { this.SetupGoogleAnalyitics() }); + context.Prepare(); + var json = JObject.Parse(this.SetupClient().Serialize(context)); + Assert.Equal("{\"Google Analyitics\":{\"clientId\":\"1033501218.1368477899\"}}", json.ToString(Formatting.None)); } protected GoogleAnalyitics SetupGoogleAnalyitics() diff --git a/test/SegmentDotNet.Tests/PropertyContainers/Serializers/PropertyContainerBaseSerializerTests.cs b/test/SegmentDotNet.Tests/PropertyContainers/Serializers/PropertyContainerBaseSerializerTests.cs new file mode 100644 index 0000000..08c912f --- /dev/null +++ b/test/SegmentDotNet.Tests/PropertyContainers/Serializers/PropertyContainerBaseSerializerTests.cs @@ -0,0 +1,30 @@ +namespace SegmentDotNet.Tests.PropertyContainers.Serializers +{ + using Moq; + using SegmentDotNet.PropertyContainers.Interfaces; + using SegmentDotNet.PropertyContainers.Serializers; + using System; + using Xunit; + + public class PropertyContainerBaseSerializerTests + { + [Fact] + public void Cannot_ReadJson() + { + Assert.Throws(() => new PropertyContainerBaseSerializer().ReadJson(null, null, null, null)); + } + + [Fact] + public void Valid_CanConvert() + { + var propertyContainer = new Mock(); + Assert.True(new PropertyContainerBaseSerializer().CanConvert(propertyContainer.Object.GetType())); + } + + [Fact] + public void Inalid_CanConvert() + { + Assert.False(new PropertyContainerBaseSerializer().CanConvert(typeof(string))); + } + } +} diff --git a/test/SegmentDotNet.Tests/TestBase.cs b/test/SegmentDotNet.Tests/TestBase.cs index b8fd5a4..6eee0ad 100644 --- a/test/SegmentDotNet.Tests/TestBase.cs +++ b/test/SegmentDotNet.Tests/TestBase.cs @@ -31,5 +31,12 @@ protected T SetupRequest() datetimeMock.Setup(d => d.UtcNow).Returns(new DateTime(2016, 6, 12, 5, 20, 50, 523, DateTimeKind.Utc)); return Activator.CreateInstance(typeof(T), datetimeMock.Object) as T; } + + protected IDateTime GetDateTimeMock() + { + var datetimeMock = new Mock(); + datetimeMock.Setup(d => d.UtcNow).Returns(new DateTime(2016, 6, 12, 5, 20, 50, 523, DateTimeKind.Utc)); + return datetimeMock.Object; + } } } From 9b38cff1ad116730ee4aae6d8eca26dea2eb8576 Mon Sep 17 00:00:00 2001 From: William Roush Date: Sun, 19 Jun 2016 05:55:53 -0400 Subject: [PATCH 08/10] Add request and library support to context, some code cleanup. --- src/SegmentDotNet/Client/Request/Track.cs | 8 +-- .../Populators/Contexts/Context.cs | 8 +-- .../Populators/Contexts/GoogleAnalyitics.cs | 12 ++-- .../Populators/Contexts/IContext.cs | 9 --- .../Populators/Contexts/IContextUpdater.cs | 6 ++ .../Populators/Contexts/Library.cs | 15 +++++ .../Populators/Contexts/Request.cs | 34 ++++++++++ .../Populators/IPopulatorUpdater.cs | 9 +++ .../Populators/Integrations/Integrations.cs | 9 ++- .../Populators/Properties/Properties.cs | 9 ++- src/SegmentDotNet/Populators/Traits/Traits.cs | 9 ++- src/SegmentDotNet/project.json | 1 + .../Client/SegmentClientTests.cs | 2 +- .../Contexts/GoogleAnalyiticsTests.cs | 14 ++++- .../Populators/Contexts/LibraryTests.cs | 22 +++++++ .../Populators/Contexts/RequestTests.cs | 62 +++++++++++++++++++ test/SegmentDotNet.Tests/project.json | 3 +- 17 files changed, 201 insertions(+), 31 deletions(-) delete mode 100644 src/SegmentDotNet/Populators/Contexts/IContext.cs create mode 100644 src/SegmentDotNet/Populators/Contexts/IContextUpdater.cs create mode 100644 src/SegmentDotNet/Populators/Contexts/Library.cs create mode 100644 src/SegmentDotNet/Populators/Contexts/Request.cs create mode 100644 src/SegmentDotNet/Populators/IPopulatorUpdater.cs create mode 100644 test/SegmentDotNet.Tests/Populators/Contexts/LibraryTests.cs create mode 100644 test/SegmentDotNet.Tests/Populators/Contexts/RequestTests.cs diff --git a/src/SegmentDotNet/Client/Request/Track.cs b/src/SegmentDotNet/Client/Request/Track.cs index e6147c4..de51d55 100644 --- a/src/SegmentDotNet/Client/Request/Track.cs +++ b/src/SegmentDotNet/Client/Request/Track.cs @@ -3,15 +3,15 @@ using Abstract; using Abstractions; using Newtonsoft.Json; - using System.Collections.Generic; using Populators.Contexts; using Populators.Integrations; - + using Populators.Properties; [JsonObject(MemberSerialization = MemberSerialization.OptIn)] public class Track : AnonymousBase { - public Track(Context context, IDateTime datetime, Integrations integrations) : base(context, datetime, integrations) + public Track(Context context, IDateTime datetime, Integrations integrations, Properties properties) : base(context, datetime, integrations) { + this.Properties = properties; } public override string Type { get { return "track"; } } @@ -22,6 +22,6 @@ public Track(Context context, IDateTime datetime, Integrations integrations) : b public string Event { get; set; } [JsonProperty("properties")] - public Dictionary Properties { get; set; } + public Properties Properties { get; set; } } } diff --git a/src/SegmentDotNet/Populators/Contexts/Context.cs b/src/SegmentDotNet/Populators/Contexts/Context.cs index 7a6897c..14231e5 100644 --- a/src/SegmentDotNet/Populators/Contexts/Context.cs +++ b/src/SegmentDotNet/Populators/Contexts/Context.cs @@ -5,16 +5,16 @@ public class Context : Populator { - public Context(IEnumerable contexts) + public Context(IEnumerable contexts) { - this.Contexts = new List(contexts); + this.Contexts = new List(contexts); } - protected List Contexts { get; set; } + protected List Contexts { get; set; } public override void Prepare() { - this.Contexts.ForEach(c => c.UpdateContext(this.Properties)); + this.Contexts.ForEach(c => c.UpdatePopulator(this.Properties)); } } } diff --git a/src/SegmentDotNet/Populators/Contexts/GoogleAnalyitics.cs b/src/SegmentDotNet/Populators/Contexts/GoogleAnalyitics.cs index 44479c9..1f9a71e 100644 --- a/src/SegmentDotNet/Populators/Contexts/GoogleAnalyitics.cs +++ b/src/SegmentDotNet/Populators/Contexts/GoogleAnalyitics.cs @@ -4,9 +4,8 @@ using Newtonsoft.Json; using System.Linq; using System.Collections.Generic; - - [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class GoogleAnalyitics : IContext + + public class GoogleAnalyitics : IContextUpdater { public class GoogleAnalyiticsPayload { @@ -38,9 +37,12 @@ private string GetGoogleAnalyticsCookie() return cookie == null ? null : string.Join(".", cookie.Split('.').Reverse().Take(2).Reverse()); } - public void UpdateContext(Dictionary context) + public void UpdatePopulator(IDictionary properties) { - context.Add("Google Analyitics", this.Payload); + if (this.Payload.ClientId != null) + { + properties.Add("Google Analyitics", this.Payload); + } } } } diff --git a/src/SegmentDotNet/Populators/Contexts/IContext.cs b/src/SegmentDotNet/Populators/Contexts/IContext.cs deleted file mode 100644 index bf830a2..0000000 --- a/src/SegmentDotNet/Populators/Contexts/IContext.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace SegmentDotNet.Populators.Contexts -{ - using System.Collections.Generic; - - public interface IContext - { - void UpdateContext(Dictionary context); - } -} diff --git a/src/SegmentDotNet/Populators/Contexts/IContextUpdater.cs b/src/SegmentDotNet/Populators/Contexts/IContextUpdater.cs new file mode 100644 index 0000000..e77edd2 --- /dev/null +++ b/src/SegmentDotNet/Populators/Contexts/IContextUpdater.cs @@ -0,0 +1,6 @@ +namespace SegmentDotNet.Populators.Contexts +{ + public interface IContextUpdater : IPopulatorUpdater + { + } +} diff --git a/src/SegmentDotNet/Populators/Contexts/Library.cs b/src/SegmentDotNet/Populators/Contexts/Library.cs new file mode 100644 index 0000000..8cc0fdf --- /dev/null +++ b/src/SegmentDotNet/Populators/Contexts/Library.cs @@ -0,0 +1,15 @@ +namespace SegmentDotNet.Populators.Contexts +{ + using System.Collections.Generic; + using System.Diagnostics; + using System.Reflection; + + public class Library : IContextUpdater + { + public void UpdatePopulator(IDictionary properties) + { + properties.Add("library.name", "SegmentDotNet"); + properties.Add("library.version", FileVersionInfo.GetVersionInfo(typeof(Library).GetTypeInfo().Assembly.Location).ProductVersion); + } + } +} diff --git a/src/SegmentDotNet/Populators/Contexts/Request.cs b/src/SegmentDotNet/Populators/Contexts/Request.cs new file mode 100644 index 0000000..5a15888 --- /dev/null +++ b/src/SegmentDotNet/Populators/Contexts/Request.cs @@ -0,0 +1,34 @@ +namespace SegmentDotNet.Populators.Contexts +{ + using Microsoft.AspNetCore.Http; + using Microsoft.AspNetCore.Http.Features; + using System.Collections.Generic; + + public class Request : IContextUpdater + { + public Request(IHttpContextAccessor httpContextAccessor) + { + this.HttpContextAccessor = httpContextAccessor; + } + + protected IHttpContextAccessor HttpContextAccessor { get; set; } + + public void UpdatePopulator(IDictionary properties) + { + var context = this.HttpContextAccessor.HttpContext; + properties.Add("ip", context.Features.Get().RemoteIpAddress.ToString()); + properties.Add("page.path", context.Request.Path.ToString()); + if (context.Request.Headers.ContainsKey("Referer")) + { + properties.Add("page.referrer", context.Request.Headers["Referer"].ToString()); + } + + if (context.Request.Headers.ContainsKey("User-Agent")) + { + properties.Add("userAgent", context.Request.Headers["User-Agent"].ToString()); + } + + properties.Add("url", $"{context.Request.Scheme}://{context.Request.Host}{context.Request.Path}"); + } + } +} diff --git a/src/SegmentDotNet/Populators/IPopulatorUpdater.cs b/src/SegmentDotNet/Populators/IPopulatorUpdater.cs new file mode 100644 index 0000000..2138dd6 --- /dev/null +++ b/src/SegmentDotNet/Populators/IPopulatorUpdater.cs @@ -0,0 +1,9 @@ +namespace SegmentDotNet.Populators +{ + using System.Collections.Generic; + + public interface IPopulatorUpdater + { + void UpdatePopulator(IDictionary properties); + } +} diff --git a/src/SegmentDotNet/Populators/Integrations/Integrations.cs b/src/SegmentDotNet/Populators/Integrations/Integrations.cs index 2d9a5bf..46108e5 100644 --- a/src/SegmentDotNet/Populators/Integrations/Integrations.cs +++ b/src/SegmentDotNet/Populators/Integrations/Integrations.cs @@ -1,6 +1,11 @@ -namespace SegmentDotNet.Populators.Integrations +using System; + +namespace SegmentDotNet.Populators.Integrations { - public class Integrations + public class Integrations : Populator { + public override void Prepare() + { + } } } diff --git a/src/SegmentDotNet/Populators/Properties/Properties.cs b/src/SegmentDotNet/Populators/Properties/Properties.cs index 2ccd074..58dccc3 100644 --- a/src/SegmentDotNet/Populators/Properties/Properties.cs +++ b/src/SegmentDotNet/Populators/Properties/Properties.cs @@ -1,6 +1,11 @@ -namespace SegmentDotNet.Populators.Properties +using System; + +namespace SegmentDotNet.Populators.Properties { - public class Properties + public class Properties : Populator { + public override void Prepare() + { + } } } diff --git a/src/SegmentDotNet/Populators/Traits/Traits.cs b/src/SegmentDotNet/Populators/Traits/Traits.cs index 8d63672..012918f 100644 --- a/src/SegmentDotNet/Populators/Traits/Traits.cs +++ b/src/SegmentDotNet/Populators/Traits/Traits.cs @@ -1,6 +1,11 @@ -namespace SegmentDotNet.Populators.Traits +using System; + +namespace SegmentDotNet.Populators.Traits { - public class Traits + public class Traits : Populator { + public override void Prepare() + { + } } } diff --git a/src/SegmentDotNet/project.json b/src/SegmentDotNet/project.json index 845a96e..edcb7d1 100644 --- a/src/SegmentDotNet/project.json +++ b/src/SegmentDotNet/project.json @@ -5,6 +5,7 @@ "Microsoft.Extensions.Options": "1.0.0-rc2-final", "NETStandard.Library": "1.5.0-rc2-24027", "Newtonsoft.Json": "8.0.3", + "System.Diagnostics.FileVersionInfo": "4.0.0-rc2-24027", "System.Dynamic.Runtime": "4.0.11-rc2-24027" }, "frameworks": { diff --git a/test/SegmentDotNet.Tests/Client/SegmentClientTests.cs b/test/SegmentDotNet.Tests/Client/SegmentClientTests.cs index 0f0c83e..98bc136 100644 --- a/test/SegmentDotNet.Tests/Client/SegmentClientTests.cs +++ b/test/SegmentDotNet.Tests/Client/SegmentClientTests.cs @@ -55,7 +55,7 @@ public async Task Can_Group() [Fact] public async Task Can_Track() { - var track = new Track(null, this.GetDateTimeMock(), null); + var track = new Track(null, this.GetDateTimeMock(), null, null); track.UserId = "1234"; track.Event = "Can_Track Test"; await this.SetupClient().Track(track); diff --git a/test/SegmentDotNet.Tests/Populators/Contexts/GoogleAnalyiticsTests.cs b/test/SegmentDotNet.Tests/Populators/Contexts/GoogleAnalyiticsTests.cs index bafac42..6ebb900 100644 --- a/test/SegmentDotNet.Tests/Populators/Contexts/GoogleAnalyiticsTests.cs +++ b/test/SegmentDotNet.Tests/Populators/Contexts/GoogleAnalyiticsTests.cs @@ -4,6 +4,7 @@ using Moq; using Newtonsoft.Json; using Newtonsoft.Json.Linq; + using SegmentDotNet.Populators; using SegmentDotNet.Populators.Contexts; using System.Collections.Generic; using Xunit; @@ -20,12 +21,23 @@ public void Parses_Google_Analyitics_Cookie() [Fact] public void Serialize_With_Extensions() { - var context = new Context(new List() { this.SetupGoogleAnalyitics() }); + var context = new Context(new List() { this.SetupGoogleAnalyitics() }); context.Prepare(); var json = JObject.Parse(this.SetupClient().Serialize(context)); Assert.Equal("{\"Google Analyitics\":{\"clientId\":\"1033501218.1368477899\"}}", json.ToString(Formatting.None)); } + [Fact] + public void Handles_No_Cookie() + { + var httpContextAccessorMock = new Mock(); + httpContextAccessorMock.Setup(h => h.HttpContext.Request.Cookies["_ga"]).Returns(() => null); + var context = new Context(new List() { new GoogleAnalyitics(httpContextAccessorMock.Object) }); + context.Prepare(); + var json = JObject.Parse(this.SetupClient().Serialize(context)); + Assert.Equal("{}", json.ToString(Formatting.None)); + } + protected GoogleAnalyitics SetupGoogleAnalyitics() { var gaCookie = "GA1.2.1033501218.1368477899"; diff --git a/test/SegmentDotNet.Tests/Populators/Contexts/LibraryTests.cs b/test/SegmentDotNet.Tests/Populators/Contexts/LibraryTests.cs new file mode 100644 index 0000000..081144d --- /dev/null +++ b/test/SegmentDotNet.Tests/Populators/Contexts/LibraryTests.cs @@ -0,0 +1,22 @@ +namespace SegmentDotNet.Tests.Populators.Contexts +{ + using SegmentDotNet.Populators.Contexts; + using System.Collections.Generic; + using System.Diagnostics; + using System.Reflection; + using Xunit; + + public class LibraryTests + { + [Fact] + public void PopulatesDictionary() + { + var library = new Library(); + var dictionary = new Dictionary(); + library.UpdatePopulator(dictionary); + var version = FileVersionInfo.GetVersionInfo(typeof(Library).GetTypeInfo().Assembly.Location).ProductVersion; + Assert.Equal("SegmentDotNet", dictionary["library.name"]); + Assert.Equal(version, dictionary["library.version"]); + } + } +} diff --git a/test/SegmentDotNet.Tests/Populators/Contexts/RequestTests.cs b/test/SegmentDotNet.Tests/Populators/Contexts/RequestTests.cs new file mode 100644 index 0000000..7df7c49 --- /dev/null +++ b/test/SegmentDotNet.Tests/Populators/Contexts/RequestTests.cs @@ -0,0 +1,62 @@ +namespace SegmentDotNet.Tests.Populators.Contexts +{ + using Microsoft.AspNetCore.Http; + using Microsoft.AspNetCore.Http.Features; + using Moq; + using SegmentDotNet.Populators.Contexts; + using System.Collections.Generic; + using System.Net; + using Xunit; + + public class RequestTests + { + [Fact] + public void Updates_Dictionary() + { + var dictionary = this.UpdateDictionary(this.SetupMock().Object); + Assert.Equal("127.0.0.1", dictionary["ip"]); + Assert.Equal("/test/url.txt", dictionary["page.path"]); + Assert.Equal("http://segment.com", dictionary["page.referrer"]); + Assert.Equal("Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36", dictionary["userAgent"]); + Assert.Equal("http://segment.com/test/url.txt", dictionary["url"]); + } + + [Fact] + public void Handles_Missing_Referer() + { + var dictionary = this.UpdateDictionary(this.SetupMock(addReferer: false).Object); + Assert.False(dictionary.ContainsKey("page.referrer")); + } + + [Fact] + public void Handles_Missing_User_Agent() + { + var dictionary = this.UpdateDictionary(this.SetupMock(addUserAgent: false).Object); + Assert.False(dictionary.ContainsKey("userAgent")); + } + + protected IDictionary UpdateDictionary(IHttpContextAccessor mock) + { + var request = new Request(mock); + var dictionary = new Dictionary(); + request.UpdatePopulator(dictionary); + return dictionary; + } + + protected Mock SetupMock(bool addReferer = true, bool addUserAgent = true) + { + var contextAccessorMock = new Mock(); + contextAccessorMock.Setup(c => c.HttpContext.Features.Get().RemoteIpAddress).Returns(IPAddress.Parse("127.0.0.1")); + contextAccessorMock.Setup(c => c.HttpContext.Request.Path).Returns("/test/url.txt"); + contextAccessorMock.Setup(c => c.HttpContext.Request.Scheme).Returns("http"); + contextAccessorMock.Setup(c => c.HttpContext.Request.Host).Returns(new HostString("segment.com")); + var headerDictionary = new HeaderDictionary(); + if(addReferer) + headerDictionary.Add("Referer", "http://segment.com"); + if(addUserAgent) + headerDictionary.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36"); + contextAccessorMock.Setup(c => c.HttpContext.Request.Headers).Returns(headerDictionary); + return contextAccessorMock; + } + } +} diff --git a/test/SegmentDotNet.Tests/project.json b/test/SegmentDotNet.Tests/project.json index 3acc829..55971b1 100644 --- a/test/SegmentDotNet.Tests/project.json +++ b/test/SegmentDotNet.Tests/project.json @@ -6,7 +6,8 @@ "dotnet-test-xunit": "1.0.0-rc2-build10025", "xunit": "2.1.0", "Moq": "4.6.25-alpha", - "SegmentDotNet": "0.1.0-*" + "SegmentDotNet": "0.1.0-*", + "Microsoft.AspNetCore.Http": "1.0.0-rc2-final" }, "testRunner": "xunit", "frameworks": { From 3aa1f5fd46635fbf3c0dd79236b728d8d2d7fd8d Mon Sep 17 00:00:00 2001 From: William Roush Date: Wed, 29 Jun 2016 11:05:51 -0400 Subject: [PATCH 09/10] Update to .NET Core 1.0.0 --- src/SegmentDotNet/project.json | 12 ++++++------ test/SegmentDotNet.Tests/project.json | 20 +++++++------------- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/src/SegmentDotNet/project.json b/src/SegmentDotNet/project.json index edcb7d1..931715a 100644 --- a/src/SegmentDotNet/project.json +++ b/src/SegmentDotNet/project.json @@ -1,12 +1,12 @@ { "version": "0.1.0-*", "dependencies": { - "Microsoft.AspNetCore.Http.Abstractions": "1.0.0-rc2-final", - "Microsoft.Extensions.Options": "1.0.0-rc2-final", - "NETStandard.Library": "1.5.0-rc2-24027", - "Newtonsoft.Json": "8.0.3", - "System.Diagnostics.FileVersionInfo": "4.0.0-rc2-24027", - "System.Dynamic.Runtime": "4.0.11-rc2-24027" + "Microsoft.AspNetCore.Http.Abstractions": "1.0.0", + "Microsoft.Extensions.Options": "1.0.0", + "NETStandard.Library": "1.6.0", + "Newtonsoft.Json": "9.0.1", + "System.Diagnostics.FileVersionInfo": "4.0.0", + "System.Dynamic.Runtime": "4.0.11" }, "frameworks": { "netstandard1.5": { diff --git a/test/SegmentDotNet.Tests/project.json b/test/SegmentDotNet.Tests/project.json index 55971b1..073b139 100644 --- a/test/SegmentDotNet.Tests/project.json +++ b/test/SegmentDotNet.Tests/project.json @@ -2,12 +2,13 @@ "version": "0.1.0-*", "dependencies": { - "NETStandard.Library": "1.5.0-rc2-24027", - "dotnet-test-xunit": "1.0.0-rc2-build10025", - "xunit": "2.1.0", + "NETStandard.Library": "1.6.0", + "dotnet-test-xunit": "2.2.0-preview2-build1029", + "xunit": "2.2.0-beta2-build3300", "Moq": "4.6.25-alpha", "SegmentDotNet": "0.1.0-*", - "Microsoft.AspNetCore.Http": "1.0.0-rc2-final" + "Microsoft.AspNetCore.Http": "1.0.0", + "System.Diagnostics.TraceSource": "4.0.0" }, "testRunner": "xunit", "frameworks": { @@ -18,17 +19,10 @@ ], "dependencies": { "Microsoft.NETCore.App": { - "version": "1.0.0-rc2-3002702", + "version": "1.0.0", "type": "platform" - }, - "System.Diagnostics.TraceSource": "4.0.0-rc2-24027" + } } } - }, - "tools": { - "Microsoft.DotNet.Watcher.Tools": { - "version": "1.0.0-*", - "imports": "portable-net451+win8" - } } } From 10584739119eec4812c76e36c84a4e1c1aa3ae82 Mon Sep 17 00:00:00 2001 From: William Roush Date: Wed, 29 Jun 2016 11:13:41 -0400 Subject: [PATCH 10/10] Update SDK. --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index b51e28b..e793049 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "projects": [ "src", "test" ], "sdk": { - "version": "1.0.0-preview1-002702" + "version": "1.0.0-preview2-003121" } }