Skip to content
This repository has been archived by the owner on Jan 21, 2020. It is now read-only.

Commit

Permalink
Some utility refactoring, makes the MixpanelEvent class more useful
Browse files Browse the repository at this point in the history
This MixpanelEvent can be used in proxies and other places where a
single generic class is needed to serialize and deserialize Mixpanel
event data.

Bumped version to 0.1.3 to release a new nuget package.
  • Loading branch information
chris.nicola committed Nov 28, 2011
1 parent 9f8d26a commit eec694c
Show file tree
Hide file tree
Showing 10 changed files with 95 additions and 48 deletions.
2 changes: 1 addition & 1 deletion Mixpanel.NET.specs/Integration/MixPanelSpecs.cs
Expand Up @@ -37,7 +37,7 @@ public class when_sending_a_valid_event_object_to_mixpanel {
static bool _result;
}

public class MyCrazyTestEvent : TrackingEvent {
public class MyCrazyTestEvent : TrackingEventBase {
public string Data1 { get; set; }

public string Data2 { get; set; }
Expand Down
55 changes: 39 additions & 16 deletions Mixpanel.NET.specs/Unit/TrackerSpecs.cs
Expand Up @@ -3,6 +3,7 @@
using System.Web.Script.Serialization;
using FakeItEasy;
using Machine.Specifications;
using System.Linq;

namespace Mixpanel.NET.Specs.Unit {
public class tracker_context {
Expand Down Expand Up @@ -34,27 +35,50 @@ public class tracker_context {
}

public class when_sending_tracker_data_using_a_dictionary : tracker_context {
Because of = () => {
Establish that = () => {
_properties = new Dictionary<string, object> {{"prop1", 0}, {"prop2", "string"}};
_name = "My Event";
};

Because of = () => _result = MixpanelTracker.Track(_name, _properties);

It should_track_successfully = () => _result.ShouldBeTrue();
It should_send_the_event_name = () => SentData.ShouldHaveName(_name);
It should_send_the_dictionary_property_1 = () =>
SentData.ShouldHaveProperty(_properties.ElementAt(0).Key, _properties.ElementAt(0).Value);
It should_send_the_dictionary_property_2 = () =>
SentData.ShouldHaveProperty(_properties.ElementAt(1).Key, _properties.ElementAt(1).Value);
It should_send_to_the_mixpanel_tracking_url = () => SentToUri.ToString().ShouldStartWith(Resources.Track());

static bool _result;
static Dictionary<string, object> _properties;
static string _name;
}

public class when_sending_tracker_data_using_a_mixpanel_event : tracker_context {
Establish that = () => {
var properties = new Dictionary<string, object> {{"prop1", 0}, {"prop2", "string"}};
_result = MixpanelTracker.Track("Test", properties);
_event = new MixpanelEvent("My Event", properties);
};

Because of = () => _result = MixpanelTracker.Track(_event);

It should_track_successfully = () => _result.ShouldBeTrue();
It should_send_the_event_name = () => SentData.ShouldHaveName("Test");
It should_send_the_dictionary_property_1 = () => SentData.ShouldHaveProperty("prop1", 0);
It should_send_the_dictionary_property_2 = () => SentData.ShouldHaveProperty("prop2", "string");
It should_send_the_event_name = () => SentData.ShouldHaveName(_event.Event);
It should_send_the_dictionary_property_1 = () =>
SentData.ShouldHaveProperty(_event.Properties.ElementAt(0).Key, _event.Properties.ElementAt(0).Value);
It should_send_the_dictionary_property_2 = () =>
SentData.ShouldHaveProperty(_event.Properties.ElementAt(1).Key, _event.Properties.ElementAt(1).Value);
It should_send_to_the_mixpanel_tracking_url = () => SentToUri.ToString().ShouldStartWith(Resources.Track());

static bool _result;
static MixpanelEvent _event;
}

public class when_sending_tracker_data_using_an_object_with_default_naming_conventions : tracker_context {
Because of = () => {
_event = new MyEvent {
PropertyOne = 0, PropertyTwoFour = "string"
};
_result = MixpanelTracker.Track(_event);
};
Establish that = () => _event = new MyEvent { PropertyOne = 0, PropertyTwoFour = "string" };

Because of = () => _result = MixpanelTracker.Track(_event);

It should_track_successfully = () => _result.ShouldBeTrue();
It should_send_the_event_name = () => SentData.ShouldHaveName("My Event");
Expand All @@ -67,14 +91,13 @@ public class when_sending_tracker_data_using_an_object_with_default_naming_conve
}

public class when_sending_tracker_data_using_an_object_with_literal_serializatioin : tracker_context {
Because of = () => {
Establish that = () => {
MixpanelTracker = new MixpanelTracker("my token", FakeHttp, new TrackerOptions { LiteralSerialization = true });
_event = new MyEvent {
PropertyOne = 0, PropertyTwoFour = "string"
};
_result = MixpanelTracker.Track(_event);
_event = new MyEvent { PropertyOne = 0, PropertyTwoFour = "string" };
};

Because of = () => _result = MixpanelTracker.Track(_event);

It should_track_successfully = () => _result.ShouldBeTrue();
It should_send_the_event_name = () => SentData.ShouldHaveName("MyEvent");
It should_send_property_one = () => SentData.ShouldHaveProperty("PropertyOne", _event.PropertyOne);
Expand Down
5 changes: 3 additions & 2 deletions Mixpanel.NET/ITracker.cs
Expand Up @@ -3,7 +3,8 @@
namespace Mixpanel.NET
{
public interface IEventTracker {
bool Track(string @event, Dictionary<string, object> properties);
bool Track<T>(T @event, string bucket = null, bool test = false);
bool Track(string @event, IDictionary<string, object> properties);
bool Track(MixpanelEvent @event);
bool Track<T>(T @event);
}
}
3 changes: 2 additions & 1 deletion Mixpanel.NET/Mixpanel.NET.csproj
Expand Up @@ -43,13 +43,14 @@
<ItemGroup>
<Compile Include="DataApi.cs" />
<Compile Include="ITracker.cs" />
<Compile Include="MixpanelEvent.cs" />
<Compile Include="MixpanelExtension.cs" />
<Compile Include="MixpanelHttp.cs" />
<Compile Include="MixpanelTracker.cs" />
<Compile Include="Resources.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TrackerOptions.cs" />
<Compile Include="TrackingEvent.cs" />
<Compile Include="TrackingEventBase.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Expand Down
2 changes: 1 addition & 1 deletion Mixpanel.NET/Mixpanel.NET.nuspec
Expand Up @@ -2,7 +2,7 @@
<package >
<metadata>
<id>Mixpanel.NET</id>
<version>0.1.2</version>
<version>0.1.3</version>
<title>Mixpanel API integration for .NET</title>
<authors>Chris Nicola</authors>
<owners>Chris Nicola</owners>
Expand Down
27 changes: 27 additions & 0 deletions Mixpanel.NET/MixpanelEvent.cs
@@ -0,0 +1,27 @@
using System.Collections.Generic;

namespace Mixpanel.NET
{
/// <summary>
/// Structure which represents a standard Mixpanel event. Mixpanel events are each
/// given a name which is sent as "event" and a property bag sent as "properties".
/// The entire event is serialized to JSON and so the property bag must contain
/// only types supported by JSON serialization and should be flat (no nested properties).
///
/// This class is also useful for testing or building proxies where deserialization of the
/// events may be needed.
/// </summary>
public class MixpanelEvent
{
public MixpanelEvent() { }

public MixpanelEvent(string name, IDictionary<string,object> properties = null) {
Event = name;
Properties = properties ?? new Dictionary<string, object>();
}

public string Event { get; set; }

public IDictionary<string, object> Properties { get; set; }
}
}
10 changes: 10 additions & 0 deletions Mixpanel.NET/MixpanelExtension.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Linq;
Expand All @@ -24,6 +25,15 @@ public static class MixpanelExtension {
return query.TrimStart('?').Split('&').ToDictionary(x => x.Split('=')[0], x => x.Substring(x.Split('=')[0].Length + 1));
}

public static MixpanelEvent ToMixpanelEvent(this object @event, bool literalSerialization = false) {
var name = literalSerialization ? @event.GetType().Name : @event.GetType().Name.SplitCamelCase();
var properties = @event.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
var propertyBag = properties.ToDictionary(
x => literalSerialization ? x.Name : x.Name.SplitCamelCase(),
x => x.GetValue(@event, null));
return new MixpanelEvent(name, propertyBag);
}

public static MixpanelEvent ParseEvent(this string data) {
return new JavaScriptSerializer().Deserialize<MixpanelEvent>(data);
}
Expand Down
31 changes: 8 additions & 23 deletions Mixpanel.NET/MixpanelTracker.cs
Expand Up @@ -17,8 +17,7 @@ public class MixpanelTracker : IEventTracker
/// Creates a new Mixpanel tracker for a given API token
/// </summary>
/// <param name="token">The API token for MixPanel</param>
/// <param name="http">An implementation of IMixpanelHttp, <see cref="MixpanelHttp"/></param>
/// <param name="literialSerialization">
/// <param name="http">An implementation of IMixpanelHttp, <see cref="MixpanelHttp"/>
/// Determines if class names and properties will be serialized to JSON literally.
/// If false (the default) spaces will be inserted between camel-cased words for improved
/// readability on the reporting side.
Expand All @@ -29,9 +28,9 @@ public class MixpanelTracker : IEventTracker
_options = options ?? new TrackerOptions();
}

public bool Track(string @event, Dictionary<string, object> properties) {
public bool Track(string @event, IDictionary<string, object> properties) {
properties["token"] = _token;
if (!properties.ContainsKey("Time") || !properties.ContainsKey("time"))
if (!properties.ContainsKey("Time") || !properties.ContainsKey("time"))
properties["time"] = DateTime.UtcNow;
if ((!properties.ContainsKey("Bucket") || !properties.ContainsKey("bucket")) && _options.Bucket != null)
properties["bucket"] = _options.Bucket;
Expand All @@ -42,28 +41,14 @@ public class MixpanelTracker : IEventTracker
if (_options.Test) requestUriString += "&test=1";
var contents = _http.Get(requestUriString);
return contents == "1";
}
}

public bool Track<T>(T @event, string bucket = null, bool test = false) {
var name = _options.LiteralSerialization ? @event.GetType().Name : @event.GetType().Name.SplitCamelCase();
var properties = @event.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
var propertyBag = properties.ToDictionary(
x => _options.LiteralSerialization ? x.Name : x.Name.SplitCamelCase(),
x => x.GetValue(@event, null));
return Track(name, propertyBag);
public bool Track(MixpanelEvent @event) {
return Track(@event.Event, @event.Properties);
}
}

public class MixpanelEvent {
public MixpanelEvent() {
Properties = new Dictionary<string, object>();
}
public MixpanelEvent(string name, Dictionary<string, object> properties = null) {
Event = name;
Properties = properties ?? new Dictionary<string, object>();
public bool Track<T>(T @event) {
return Track(@event.ToMixpanelEvent(_options.LiteralSerialization));
}

public string Event { get; set; }
public Dictionary<string, object> Properties { get; set; }
}
}
4 changes: 2 additions & 2 deletions Mixpanel.NET/Properties/AssemblyInfo.cs
Expand Up @@ -32,5 +32,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.1.2.0")]
[assembly: AssemblyFileVersion("0.1.2.0")]
[assembly: AssemblyVersion("0.1.3.0")]
[assembly: AssemblyFileVersion("0.1.3.0")]
Expand Up @@ -2,9 +2,9 @@

namespace Mixpanel.NET
{
public abstract class TrackingEvent
public abstract class TrackingEventBase
{
public TrackingEvent() {
public TrackingEventBase() {
Time = DateTime.UtcNow;
}
public DateTime? Time { get; set; }
Expand Down

0 comments on commit eec694c

Please sign in to comment.