diff --git a/Keen.NET.Test/AccessKeyMock.cs b/Keen.NET.Test/AccessKeyMock.cs
new file mode 100644
index 0000000..682bfdd
--- /dev/null
+++ b/Keen.NET.Test/AccessKeyMock.cs
@@ -0,0 +1,35 @@
+using Keen.Core.AccessKey;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json.Linq;
+using Keen.Core;
+
+namespace Keen.Net.Test
+{
+ ///
+ /// AccessKeyMock provides an implementation of IAccessKeys with a constructor that
+ /// accepts delegates for each of the interface methods.
+ /// The purpose of this is to allow test methods to set up a customized
+ /// IAccessKeys for each test.
+ ///
+ class AccessKeysMock : IAccessKeys
+ {
+ private readonly IProjectSettings _settings;
+ private readonly Func _createAccessKey;
+
+ public AccessKeysMock(IProjectSettings projSettings,
+ Func createAccessKey = null)
+ {
+ _settings = projSettings;
+ _createAccessKey = createAccessKey ?? ((p, k) => new JObject());
+ }
+
+ public Task CreateAccessKey(AccessKey accesskey)
+ {
+ return Task.Run(() => _createAccessKey(accesskey, _settings));
+ }
+ }
+}
diff --git a/Keen.NET.Test/AccessKeyTests.cs b/Keen.NET.Test/AccessKeyTests.cs
new file mode 100644
index 0000000..4a2e301
--- /dev/null
+++ b/Keen.NET.Test/AccessKeyTests.cs
@@ -0,0 +1,91 @@
+using Keen.Core;
+using Keen.Core.AccessKey;
+using Keen.Core.Query;
+using Newtonsoft.Json.Linq;
+using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Keen.Net.Test
+{
+ [TestFixture]
+ class AccessKeyTests : TestBase
+ {
+ [Test]
+ public void CreateAccessKey_Success()
+ {
+ var settings = new ProjectSettingsProvider(projectId: "X", masterKey: SettingsEnv.MasterKey); // Replace X with respective value
+ var client = new KeenClient(settings);
+
+ if (UseMocks)
+ client.AccessKeys = new AccessKeysMock(settings,
+ createAccessKey: new Func((e, p) =>
+ {
+ Assert.True(p == settings, "Incorrect Settings");
+ Assert.NotNull(e.Name, "Expected a name for the newly created Key");
+ Assert.NotNull(e.Permitted, "Expected a list of high level actions this key can perform");
+ Assert.NotNull(e.Options, "Expected an object containing more details about the key’s permitted and restricted functionality");
+ if ((p == settings) && (e.Name == "TestAccessKey") && (e.IsActive) && e.Permitted.First() == "queries" && e.Options.CachedQueries.Allowed.First() == "my_cached_query")
+ return new JObject();
+ else
+ throw new Exception("Unexpected value");
+ }));
+
+ HashSet permissions = new HashSet() { "queries" };
+ List qFilters = new List() { new QueryFilter("customer.id", QueryFilter.FilterOperator.Equals(), "asdf12345z") };
+ CachedQueries cachedQueries = new CachedQueries();
+ cachedQueries.Allowed = new HashSet() { "my_cached_query" };
+ Options options = new Options()
+ {
+ Queries = new Core.AccessKey.Queries { Filters = qFilters },
+ CachedQueries = cachedQueries
+ };
+
+ Assert.DoesNotThrow(() => client.CreateAccessKey(new AccessKey { Name = "TestAccessKey", IsActive = true, Options = options, Permitted = permissions }));
+ }
+
+
+ [Test]
+ public void CreateAccessKey_With_All_Properties_Given_As_Null_Success()
+ {
+ var settings = new ProjectSettingsProvider(projectId: "X", masterKey: SettingsEnv.MasterKey); // Replace X with respective value
+ var client = new KeenClient(settings);
+
+ if (UseMocks)
+ client.AccessKeys = new AccessKeysMock(settings,
+ createAccessKey: new Func((e, p) =>
+ {
+ Assert.True(p == settings, "Incorrect Settings");
+ Assert.NotNull(e.Name, "Expected a name for the newly created Key");
+ Assert.NotNull(e.Permitted, "Expected a list of high level actions this key can perform");
+ Assert.NotNull(e.Options, "Expected an object containing more details about the key’s permitted and restricted functionality");
+ if ((p == settings) && (e.Name == "TestAccessKey") && (e.IsActive) && e.Permitted.First() == "queries" && e.Options.CachedQueries.Allowed.First() == "my_cached_query")
+ return new JObject();
+ else
+ throw new Exception("Unexpected value");
+ }));
+
+ HashSet permissions = new HashSet() { "queries" };
+ List qFilters = new List() { new QueryFilter("customer.id", QueryFilter.FilterOperator.Equals(), "asdf12345z") };
+ CachedQueries cachedQueries = new CachedQueries() { Allowed = null, Blocked = null };
+ SavedQueries savedQuaries = new SavedQueries() { Allowed = null, Blocked = null, Filters = null };
+ Datasets datasets = new Datasets() { Allowed = null, Blocked = null, Operations = null };
+ Writes writes = new Writes() { Autofill = null };
+ cachedQueries.Allowed = new HashSet() { "my_cached_query" };
+ Options options = new Options()
+ {
+ Queries = new Core.AccessKey.Queries { Filters = qFilters },
+ CachedQueries = cachedQueries,
+ SavedQueries = savedQuaries,
+ Datasets = datasets,
+ Writes = writes
+ };
+
+ Assert.DoesNotThrow(() => client.CreateAccessKey(new AccessKey { Name = "TestAccessKey", IsActive = true, Options = options, Permitted = permissions }));
+ }
+
+ }
+}
diff --git a/Keen.NET.Test/Keen.NET.Test.csproj b/Keen.NET.Test/Keen.NET.Test.csproj
index 3f05425..f8ad2ac 100644
--- a/Keen.NET.Test/Keen.NET.Test.csproj
+++ b/Keen.NET.Test/Keen.NET.Test.csproj
@@ -99,6 +99,8 @@
Properties\SharedVersionInfo.cs
+
+
diff --git a/Keen.NET.Test/KeenClientTest.cs b/Keen.NET.Test/KeenClientTest.cs
index 794d057..d76603f 100644
--- a/Keen.NET.Test/KeenClientTest.cs
+++ b/Keen.NET.Test/KeenClientTest.cs
@@ -1,4 +1,6 @@
using Keen.Core;
+using Keen.Core.AccessKey;
+using Keen.Core.Query;
using Keen.Core.EventCache;
using Moq;
using Newtonsoft.Json.Linq;
diff --git a/Keen/AccessKey/AccessKey.cs b/Keen/AccessKey/AccessKey.cs
new file mode 100644
index 0000000..44eb3dd
--- /dev/null
+++ b/Keen/AccessKey/AccessKey.cs
@@ -0,0 +1,83 @@
+using Keen.Core.Query;
+using System;
+using System.Collections.Generic;
+
+namespace Keen.Core.AccessKey
+{
+ ///
+ /// Model for AccessKey object
+ ///
+ public class AccessKey
+ {
+ public string Name { get; set; }
+ public bool IsActive { get; set; }
+ public ISet Permitted { get; set; }
+ public string Key { get; set; }
+ public Options Options { get; set; }
+ }
+
+ ///
+ /// When SavedQueries are permitted, the Access Key will have access to run saved queries.
+ ///
+ public class SavedQueries
+ {
+ public ISet Blocked { get; set; }
+ public IEnumerable Filters { get; set; }
+ public ISet Allowed { get; set; }
+ }
+
+ ///
+ /// When Queries are permitted, the Access Key will have the ability to do ad-hoc queries.
+ ///
+ public class Queries
+ {
+ public IEnumerable Filters { get; set; }
+ }
+
+ ///
+ /// When Writes are permitted, the Access Key will have the ability to stream data to Keen.
+ ///
+ public class Writes
+ {
+ public dynamic Autofill { get; set; } // "customer": { "id": "93iskds39kd93id", "name": "Ada Corp." }
+ }
+
+ ///
+ /// When Datasets are permitted, the Access Key will have access to getting a dataset definition, retrieving cached dataset results, and listing cached datasets definitions for a project.
+ ///
+ public class Datasets
+ {
+ public IEnumerable Operations { get; set; }
+ public IDictionary Allowed { get; set; }
+ public ISet Blocked { get; set; }
+ }
+
+ ///
+ /// Optionals limiting of allowed datasets in the access key by index
+ ///
+ public class AllowedDatasetIndexes
+ {
+ public Tuple IndexBy { get; set;}
+ }
+
+ ///
+ /// When CachedQueries are permitted, the Access Key will have access to retrieve results from cached queries.
+ ///
+ public class CachedQueries
+ {
+ public ISet Blocked { get; set; }
+ public ISet Allowed { get; set; }
+ }
+
+ ///
+ /// An object containing more details about the key’s permitted and restricted functionality.
+ ///
+ public class Options
+ {
+ public SavedQueries SavedQueries { get; set; }
+ public Writes Writes { get; set; }
+ public Datasets Datasets { get; set; }
+ public CachedQueries CachedQueries { get; set; }
+ public Queries Queries { get; set; }
+ }
+}
diff --git a/Keen/AccessKey/AccessKeys.cs b/Keen/AccessKey/AccessKeys.cs
new file mode 100644
index 0000000..7480cc5
--- /dev/null
+++ b/Keen/AccessKey/AccessKeys.cs
@@ -0,0 +1,100 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Newtonsoft.Json.Serialization;
+using System;
+using System.Threading.Tasks;
+
+namespace Keen.Core.AccessKey
+{
+ ///
+ /// AccessKeys implements the IAccessKeys interface which represents the Keen.IO Access Key API methods.
+ ///
+ public class AccessKeys : IAccessKeys
+ {
+ private readonly IKeenHttpClient _keenHttpClient;
+ private readonly string _accesKeyRelativeUrl;
+ private readonly string _readKey;
+ private readonly string _masterKey;
+
+ internal AccessKeys(IProjectSettings prjSettings,
+ IKeenHttpClientProvider keenHttpClientProvider)
+ {
+ if (null == prjSettings)
+ {
+ throw new ArgumentNullException(nameof(prjSettings),
+ "Project Settings must be provided.");
+ }
+
+ if (null == keenHttpClientProvider)
+ {
+ throw new ArgumentNullException(nameof(keenHttpClientProvider),
+ "A KeenHttpClient provider must be provided.");
+ }
+
+ if (string.IsNullOrWhiteSpace(prjSettings.KeenUrl) ||
+ !Uri.IsWellFormedUriString(prjSettings.KeenUrl, UriKind.Absolute))
+ {
+ throw new KeenException(
+ "A properly formatted KeenUrl must be provided via Project Settings.");
+ }
+
+ var serverBaseUrl = new Uri(prjSettings.KeenUrl);
+ _keenHttpClient = keenHttpClientProvider.GetForUrl(serverBaseUrl);
+ _accesKeyRelativeUrl = KeenHttpClient.GetRelativeUrl(prjSettings.ProjectId,
+ KeenConstants.AccessKeyResource);
+
+ _readKey = prjSettings.ReadKey;
+ _masterKey = prjSettings.MasterKey;
+ }
+
+ public async Task CreateAccessKey(AccessKey accesskey)
+ {
+ if (string.IsNullOrWhiteSpace(_masterKey))
+ {
+ throw new KeenException("An API WriteKey is required to add events.");
+ }
+
+ DefaultContractResolver contractResolver = new DefaultContractResolver
+ {
+ NamingStrategy = new SnakeCaseNamingStrategy()
+ };
+
+ var content = JsonConvert.SerializeObject(accesskey, new JsonSerializerSettings
+ {
+ ContractResolver = contractResolver,
+ Formatting = Formatting.Indented
+ }).ToSafeString();
+
+ var responseMsg = await _keenHttpClient
+ .PostAsync(_accesKeyRelativeUrl, _masterKey, content)
+ .ConfigureAwait(continueOnCapturedContext: false);
+
+ var responseString = await responseMsg
+ .Content
+ .ReadAsStringAsync()
+ .ConfigureAwait(continueOnCapturedContext: false);
+
+ JObject jsonResponse = null;
+
+ try
+ {
+ jsonResponse = JObject.Parse(responseString);
+ }
+ catch (Exception)
+ {
+ // To avoid any flow stoppers
+ }
+ if (!responseMsg.IsSuccessStatusCode)
+ {
+ throw new KeenException("AddEvents failed with status: " + responseMsg.StatusCode);
+ }
+
+ if (null == jsonResponse)
+ {
+ throw new KeenException("AddEvents failed with empty response from server.");
+ }
+
+ return jsonResponse;
+ }
+ }
+}
diff --git a/Keen/AccessKey/IAccessKeys.cs b/Keen/AccessKey/IAccessKeys.cs
new file mode 100644
index 0000000..96bc9ea
--- /dev/null
+++ b/Keen/AccessKey/IAccessKeys.cs
@@ -0,0 +1,18 @@
+using Newtonsoft.Json.Linq;
+using System.Threading.Tasks;
+
+namespace Keen.Core.AccessKey
+{
+ ///
+ /// Public interface for Access Key related functionalities
+ ///
+ public interface IAccessKeys
+ {
+ ///
+ /// Creates an Access Key
+ ///
+ ///
+ ///
+ Task CreateAccessKey(AccessKey accesskey);
+ }
+}
diff --git a/Keen/Keen.csproj b/Keen/Keen.csproj
index 5744708..b6bb48e 100644
--- a/Keen/Keen.csproj
+++ b/Keen/Keen.csproj
@@ -52,6 +52,9 @@
Properties\SharedVersionInfo.cs
+
+
+
diff --git a/Keen/KeenClient.cs b/Keen/KeenClient.cs
index b932e92..c4d7506 100644
--- a/Keen/KeenClient.cs
+++ b/Keen/KeenClient.cs
@@ -1,6 +1,7 @@
using Keen.Core.DataEnrichment;
using Keen.Core.EventCache;
using Keen.Core.Query;
+using Keen.Core.AccessKey;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
@@ -51,6 +52,12 @@ public class KeenClient
public IDataset Datasets { get; set; }
+ ///
+ /// AccessKeys provides direct access to the Keen.IO Access Keys API methods.
+ /// The default implementation can be overridden by setting a new implementation here.
+ ///
+ public IAccessKeys AccessKeys { get; set; }
+
///
/// Add a static global property. This property will be added to
/// every event.
@@ -118,8 +125,9 @@ private KeenClient(IProjectSettings prjSettings,
// implementation via their respective properties.
EventCollection = new EventCollection(_prjSettings, keenHttpClientProvider);
Event = new Event(_prjSettings, keenHttpClientProvider);
- Queries = new Queries(_prjSettings, keenHttpClientProvider);
- Datasets = new Datasets(_prjSettings, keenHttpClientProvider);
+ Queries = new Query.Queries(_prjSettings, keenHttpClientProvider);
+ AccessKeys = new AccessKeys(_prjSettings, keenHttpClientProvider);
+ Datasets = new Dataset.Datasets(_prjSettings, keenHttpClientProvider);
}
///
@@ -974,8 +982,7 @@ public IEnumerable
+
/// Get query results from a Cached Dataset.
///
/// Name of cached dataset to query.
@@ -1146,5 +1153,32 @@ public void DeleteDataset(string datasetName)
throw ex.TryUnwrap();
}
}
+
+ ///
+ ///
+ public void CreateAccessKey(AccessKey.AccessKey accessKey)
+ {
+ try
+ {
+ CreateAccessKeyAsync(accessKey).Wait();
+ }
+ catch (AggregateException ex)
+ {
+ Debug.WriteLine(ex.TryUnwrap());
+ }
+ }
+
+ ///
+ ///
+ private async Task CreateAccessKeyAsync(AccessKey.AccessKey accessKey)
+ {
+ if (null == accessKey)
+ throw new KeenException("Access Key required");
+
+ var createdKey = await AccessKeys.CreateAccessKey(accessKey)
+ .ConfigureAwait(false);
+
+ return createdKey;
+ }
}
}
diff --git a/Keen/KeenConstants.cs b/Keen/KeenConstants.cs
index 0b1c6a4..bb4574b 100644
--- a/Keen/KeenConstants.cs
+++ b/Keen/KeenConstants.cs
@@ -9,6 +9,9 @@ public class KeenConstants
private const string eventsResource = "events";
public static string EventsResource { get { return eventsResource; } protected set { ;} }
+ private const string accesskeyResource = "keys";
+ public static string AccessKeyResource { get { return accesskeyResource; } protected set {; } }
+
private const string queriesResource = "queries";
public static string QueriesResource { get { return queriesResource; } protected set { ;} }