Skip to content

Commit

Permalink
Separate tracking API from app API.
Browse files Browse the repository at this point in the history
  • Loading branch information
vegardlarsen committed Aug 25, 2022
1 parent d91fd89 commit 3625bff
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 95 deletions.
24 changes: 18 additions & 6 deletions CustomerIOSharp.Test/AuthorizationClass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,33 @@ protected AuthorizationClass()
{
if (string.IsNullOrWhiteSpace(SiteId))
{
throw new Exception("SiteId is missing from environment variables.");
throw new Exception("CIOS_SITE_ID is missing from environment variables.");
}

if (string.IsNullOrWhiteSpace(ApiKey))
{
throw new Exception("ApiKey is missing from environment variables.");
throw new Exception("CIOS_API_KEY is missing from environment variables.");
}

if (string.IsNullOrWhiteSpace(AppApiKey))
{
throw new Exception("CIOS_APP_API_KEY is missing from environment variables.");
}

if (SiteId == "SET_ME" || ApiKey == "SET_ME" || BroadcastCampaignId == 0 || BroadcastSegmentId == 0)
{
throw new Exception("Environment variables are set at default values, and should be updated. You need to set the environment variables in test.runsettings to run the tests locally.");
}
}

public static string SiteId => Environment.GetEnvironmentVariable("CustomerIOSharp_SiteId") ?? string.Empty;
public static string SiteId => Environment.GetEnvironmentVariable("CIOS_SITE_ID") ?? string.Empty;

public static string ApiKey => Environment.GetEnvironmentVariable("CIOS_API_KEY") ?? string.Empty;

public static string ApiKey => Environment.GetEnvironmentVariable("CustomerIOSharp_ApiKey") ?? string.Empty;
public static string AppApiKey => Environment.GetEnvironmentVariable("CIOS_APP_API_KEY") ?? string.Empty;

public static int BroadcastCampaignId => int.Parse(Environment.GetEnvironmentVariable("CustomerIOSharp_BroadcastCampaignId"));
public static int BroadcastCampaignId => int.Parse(Environment.GetEnvironmentVariable("CIOS_BROADCAST_CAMPAIGN_ID"));

public static int BroadcastSegmentId => int.Parse(Environment.GetEnvironmentVariable("CustomerIOSharp_BroadcastSegmentId"));
public static int BroadcastSegmentId => int.Parse(Environment.GetEnvironmentVariable("CIOS_BROADCAST_SEGMENT_ID"));
}
}
4 changes: 2 additions & 2 deletions CustomerIOSharp.Test/EventTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public class EventTests : AuthorizationClass
[Fact]
public async Task TrackEvent()
{
var customerIo = new CustomerIo(SiteId, ApiKey);
var customerIo = new TrackApi(SiteId, ApiKey);
await customerIo.TrackEventAsync("signup", new
{
Group = "trial",
Expand All @@ -21,7 +21,7 @@ public async Task TrackEvent()
[Fact]
public async Task TrackEventFailsWithoutIdentity()
{
var customerIo = new CustomerIo(SiteId, ApiKey);
var customerIo = new TrackApi(SiteId, ApiKey);
await Assert.ThrowsAsync<CustomerIoApiException>(async () =>
await customerIo.TrackEventAsync("signup", new
{
Expand Down
8 changes: 4 additions & 4 deletions CustomerIOSharp.Test/IdentityTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,28 @@ public class IdentityTests : AuthorizationClass
[Fact]
public async Task IdentifyAsyncFailsIfNotGivenIdentity()
{
var customerIo = new CustomerIo(SiteId, ApiKey);
var customerIo = new TrackApi(SiteId, ApiKey);
await Assert.ThrowsAsync<ArgumentNullException>(async () => await customerIo.IdentifyAsync());
}

[Fact]
public async Task IdentifyAsyncSucceedsWithStaticIdentity()
{
var customerIo = new CustomerIo(SiteId, ApiKey);
var customerIo = new TrackApi(SiteId, ApiKey);
await customerIo.IdentifyAsync(new CustomerDetails("from_static_identity", "[email protected]"));
}

[Fact]
public async Task IdentifyAsyncSucceedsWithIdentityFactory()
{
var customerIo = new CustomerIo(SiteId, ApiKey, new IdentityFactory());
var customerIo = new TrackApi(SiteId, ApiKey, new IdentityFactory());
await customerIo.IdentifyAsync();
}

[Fact]
public async Task IdentifyAsyncSucceedsWithIdentityFactoryAndCustomCustomerDetails()
{
var customerIo = new CustomerIo(SiteId, ApiKey, new IdentityFactoryWithExtraCustomerDetails());
var customerIo = new TrackApi(SiteId, ApiKey, new IdentityFactoryWithExtraCustomerDetails());
await customerIo.IdentifyAsync();
}

Expand Down
4 changes: 2 additions & 2 deletions CustomerIOSharp.Test/TriggerBroadcastTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public class TriggerBroadcastTests : AuthorizationClass
[Fact]
public async Task TriggerBroadcast()
{
var customerIo = new CustomerIo(SiteId, ApiKey);
var customerIo = new AppApi(AppApiKey);
await customerIo.TriggerBroadcastAsync(
BroadcastCampaignId,
new
Expand All @@ -21,7 +21,7 @@ await customerIo.TriggerBroadcastAsync(
[Fact]
public async Task TriggerBroadcastWithRecipientFilter()
{
var customerIo = new CustomerIo(SiteId, ApiKey);
var customerIo = new AppApi(AppApiKey);
await customerIo.TriggerBroadcastAsync(
BroadcastCampaignId,
new
Expand Down
47 changes: 47 additions & 0 deletions CustomerIOSharp/AppApi.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
namespace CustomerIOSharp
{
using System.Net.Http;
using System.Threading.Tasks;

public class AppApi
{
private const string ApiEndpoint = "https://api.customer.io/v1/api";

private readonly HttpClient _httpClient;

public AppApi(string appApiKey)
{
_httpClient = new HttpClient();

_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {appApiKey}");
}

/// <summary>
/// Track a custom event for a non-customer.
/// </summary>
/// <see cref="https://learn.customer.io/api/#apibroadcast_trigger" />
/// <param name="campaignId">The Campaign Id you wish to trigger</param>
/// <param name="data">Any related information you’d like to attach to this broadcast. These attributes can be used in the email/ action body of the triggered email. You can set any number of data key and values.</param>
/// <param name="recipientFilter">Allows you to pass in filters that will override any preset segment or recipient criteria. </param>
/// <see cref="https://learn.customer.io/documentation/api-triggered-broadcast-setup.html#step-1-define-recipients" />
/// <returns>Nothing if successful, throws if failed</returns>
/// <exception cref="CustomerIoApiException">If any code besides 200 OK is returned from the server.</exception>
public async Task TriggerBroadcastAsync(int campaignId, object data = null, object recipientFilter = null)
{
var wrappedData = new TriggerBroadcast
{
Data = data,
Recipients = recipientFilter
};

var resource = $"{ApiEndpoint}/campaigns/{campaignId.ToString()}/triggers";

await Utilities.CallMethodAsync(
_httpClient,
resource,
HttpMethod.Post,
wrappedData).ConfigureAwait(false);

}
}
}
83 changes: 8 additions & 75 deletions CustomerIOSharp/CustomerIO.cs → CustomerIOSharp/TrackApi.cs
Original file line number Diff line number Diff line change
@@ -1,50 +1,26 @@
namespace CustomerIOSharp
{
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;

public class CustomerIo
public class TrackApi
{
private const string TrackEndpoint = "https://track.customer.io/api/v1";
private const string ApiEndpoint = "https://api.customer.io/v1/api";

private readonly ICustomerFactory _customerFactory;

private readonly JsonSerializerSettings _jsonSerializerSettings;

private readonly HttpClient _httpClient;

public CustomerIo(string siteId, string apiKey, ICustomerFactory customerFactory = null)
public TrackApi(string siteId, string apiKey, ICustomerFactory customerFactory = null)
{
_customerFactory = customerFactory;

_httpClient = new HttpClient();


var token = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{siteId}:{apiKey}"));
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Basic {token}");

_jsonSerializerSettings = new JsonSerializerSettings()
{
MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Include,
ContractResolver = new CamelCasePropertyNamesContractResolver()
};

foreach (var converter in _jsonSerializerSettings.Converters.OfType<DateTimeConverterBase>().ToList())
{
_jsonSerializerSettings.Converters.Remove(converter);
}

_jsonSerializerSettings.Converters.Add(new UnixTimestampConverter());
}

public async Task IdentifyAsync(ICustomerDetails customer = null)
Expand All @@ -63,7 +39,7 @@ public async Task IdentifyAsync(ICustomerDetails customer = null)

var resource = $"{TrackEndpoint}/customers/{customer.Id}";

await CallMethodAsync(resource, HttpMethod.Put, customer).ConfigureAwait(false);
await Utilities.CallMethodAsync(_httpClient, resource, HttpMethod.Put, customer).ConfigureAwait(false);
}

public async Task DeleteCustomerAsync(string customerId = null)
Expand All @@ -82,7 +58,7 @@ public async Task DeleteCustomerAsync(string customerId = null)

var resource = $"{TrackEndpoint}/customers/{customerId}";

await CallMethodAsync(resource, HttpMethod.Delete, null).ConfigureAwait(false);
await Utilities.CallMethodAsync(_httpClient, resource, HttpMethod.Delete, null).ConfigureAwait(false);
}

/// <summary>
Expand Down Expand Up @@ -110,7 +86,8 @@ public async Task TrackEventAsync(string eventName, object data = null, DateTime

var resource = $"{TrackEndpoint}/customers/{customerId}/events";

await CallMethodAsync(
await Utilities.CallMethodAsync(
_httpClient,
resource,
HttpMethod.Post,
wrappedData).ConfigureAwait(false);
Expand All @@ -137,55 +114,11 @@ public async Task TrackNonCustomerEventAsync(string eventName, object data = nul

var resource = $"{TrackEndpoint}/events";

await CallMethodAsync(
await Utilities.CallMethodAsync(
_httpClient,
resource,
HttpMethod.Post,
wrappedData).ConfigureAwait(false);
}

/// <summary>
/// Track a custom event for a non-customer.
/// </summary>
/// <see cref="https://learn.customer.io/api/#apibroadcast_trigger" />
/// <param name="campaignId">The Campaign Id you wish to trigger</param>
/// <param name="data">Any related information you’d like to attach to this broadcast. These attributes can be used in the email/ action body of the triggered email. You can set any number of data key and values.</param>
/// <param name="recipientFilter">Allows you to pass in filters that will override any preset segment or recipient criteria. </param>
/// <see cref="https://learn.customer.io/documentation/api-triggered-broadcast-setup.html#step-1-define-recipients" />
/// <returns>Nothing if successful, throws if failed</returns>
/// <exception cref="CustomerIoApiException">If any code besides 200 OK is returned from the server.</exception>
public async Task TriggerBroadcastAsync(int campaignId, object data = null, object recipientFilter = null)
{
var wrappedData = new TriggerBroadcast
{
Data = data,
Recipients = recipientFilter
};

var resource = $"{ApiEndpoint}/campaigns/{campaignId.ToString()}/triggers";

await CallMethodAsync(
resource,
HttpMethod.Post,
wrappedData).ConfigureAwait(false);

}


private async Task CallMethodAsync(string resource, HttpMethod httpMethod, object data)
{

var requestMessage = new HttpRequestMessage(httpMethod, resource)
{
Content = new StringContent(
JsonConvert.SerializeObject(data, _jsonSerializerSettings),
Encoding.UTF8,
"application/json")
};
var result = await _httpClient.SendAsync(requestMessage).ConfigureAwait(false);
if (result.StatusCode != HttpStatusCode.OK)
{
throw new CustomerIoApiException(result.StatusCode, result.ReasonPhrase);
}
}
}
}
53 changes: 53 additions & 0 deletions CustomerIOSharp/Utilities.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
namespace CustomerIOSharp
{
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;

internal static class Utilities
{
static Utilities()
{
var settings = new JsonSerializerSettings()
{
MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Include,
ContractResolver = new CamelCasePropertyNamesContractResolver()
};

foreach (var converter in settings.Converters.OfType<DateTimeConverterBase>().ToList())
{
settings.Converters.Remove(converter);
}

settings.Converters.Add(new UnixTimestampConverter());

JsonSerializerSettings = settings;
}

internal static JsonSerializerSettings JsonSerializerSettings { get; }

internal static async Task CallMethodAsync(HttpClient client, string resource, HttpMethod httpMethod, object data)
{

var requestMessage = new HttpRequestMessage(httpMethod, resource)
{
Content = new StringContent(
JsonConvert.SerializeObject(data, Utilities.JsonSerializerSettings),
Encoding.UTF8,
"application/json")
};
var result = await client.SendAsync(requestMessage).ConfigureAwait(false);
if (result.StatusCode != HttpStatusCode.OK)
{
throw new CustomerIoApiException(result.StatusCode, result.ReasonPhrase);
}
}
}
}
Loading

0 comments on commit 3625bff

Please sign in to comment.