From 2bd8069a97360c928d5372956d77d7dad860cedf Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 11 Nov 2017 03:09:38 -0500 Subject: [PATCH 1/9] Remove the 3.5/4.5/PCL projects from the solution and get rid of the build/solution configs that were for netfx/netstandard subsets of those projects. --- Keen.sln | 61 +------------------------------------------------------- 1 file changed, 1 insertion(+), 60 deletions(-) diff --git a/Keen.sln b/Keen.sln index 5102f6e..9e69a9f 100644 --- a/Keen.sln +++ b/Keen.sln @@ -1,24 +1,8 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26730.0 +VisualStudioVersion = 15.0.27019.1 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Keen", "Keen\Keen.csproj", "{36D156BF-8523-42EC-9AD6-7C3AC05D699F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Keen.NET.Test", "Keen.NET.Test\Keen.NET.Test.csproj", "{644382BF-CF65-4DB6-AFD8-1ACDA2800D31}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Keen.Net", "Keen.Net\Keen.Net.csproj", "{0CD3383E-5267-48BC-99CD-1B3AE8AFF7BA}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Keen.NET_35", "Keen.NET_35\Keen.NET_35.csproj", "{A395C070-F971-48D5-A3DE-9263032CFB84}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Keen.NET_35.Test", "Keen.NET_35.Test\Keen.NET_35.Test.csproj", "{2EF6BF24-A068-47E2-ABBF-DE2666607D65}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B3285F3A-90CD-4116-B994-F4B0752FA95B}" - ProjectSection(SolutionItems) = preProject - SharedAssemblyInfo.cs = SharedAssemblyInfo.cs - SharedVersionInfo.cs = SharedVersionInfo.cs - EndProjectSection -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Keen.NetStandard", "Keen.NetStandard\Keen.NetStandard.csproj", "{711F4331-198A-4919-ACA8-C2B303E045A8}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Keen.NetStandard.Test", "Keen.NetStandard.Test\Keen.NetStandard.Test.csproj", "{DC84C790-A0B0-4D40-A835-8128177F8FA3}" @@ -26,58 +10,15 @@ EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU - netfx|Any CPU = netfx|Any CPU - netstandard|Any CPU = netstandard|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {36D156BF-8523-42EC-9AD6-7C3AC05D699F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {36D156BF-8523-42EC-9AD6-7C3AC05D699F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {36D156BF-8523-42EC-9AD6-7C3AC05D699F}.netfx|Any CPU.ActiveCfg = netfx|Any CPU - {36D156BF-8523-42EC-9AD6-7C3AC05D699F}.netfx|Any CPU.Build.0 = netfx|Any CPU - {36D156BF-8523-42EC-9AD6-7C3AC05D699F}.netstandard|Any CPU.ActiveCfg = Release|Any CPU - {36D156BF-8523-42EC-9AD6-7C3AC05D699F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {36D156BF-8523-42EC-9AD6-7C3AC05D699F}.Release|Any CPU.Build.0 = Release|Any CPU - {644382BF-CF65-4DB6-AFD8-1ACDA2800D31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {644382BF-CF65-4DB6-AFD8-1ACDA2800D31}.Debug|Any CPU.Build.0 = Debug|Any CPU - {644382BF-CF65-4DB6-AFD8-1ACDA2800D31}.netfx|Any CPU.ActiveCfg = netfx|Any CPU - {644382BF-CF65-4DB6-AFD8-1ACDA2800D31}.netfx|Any CPU.Build.0 = netfx|Any CPU - {644382BF-CF65-4DB6-AFD8-1ACDA2800D31}.netstandard|Any CPU.ActiveCfg = Release|Any CPU - {644382BF-CF65-4DB6-AFD8-1ACDA2800D31}.Release|Any CPU.ActiveCfg = Release|Any CPU - {644382BF-CF65-4DB6-AFD8-1ACDA2800D31}.Release|Any CPU.Build.0 = Release|Any CPU - {0CD3383E-5267-48BC-99CD-1B3AE8AFF7BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0CD3383E-5267-48BC-99CD-1B3AE8AFF7BA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0CD3383E-5267-48BC-99CD-1B3AE8AFF7BA}.netfx|Any CPU.ActiveCfg = netfx|Any CPU - {0CD3383E-5267-48BC-99CD-1B3AE8AFF7BA}.netfx|Any CPU.Build.0 = netfx|Any CPU - {0CD3383E-5267-48BC-99CD-1B3AE8AFF7BA}.netstandard|Any CPU.ActiveCfg = Release|Any CPU - {0CD3383E-5267-48BC-99CD-1B3AE8AFF7BA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0CD3383E-5267-48BC-99CD-1B3AE8AFF7BA}.Release|Any CPU.Build.0 = Release|Any CPU - {A395C070-F971-48D5-A3DE-9263032CFB84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A395C070-F971-48D5-A3DE-9263032CFB84}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A395C070-F971-48D5-A3DE-9263032CFB84}.netfx|Any CPU.ActiveCfg = netfx|Any CPU - {A395C070-F971-48D5-A3DE-9263032CFB84}.netfx|Any CPU.Build.0 = netfx|Any CPU - {A395C070-F971-48D5-A3DE-9263032CFB84}.netstandard|Any CPU.ActiveCfg = Release|Any CPU - {A395C070-F971-48D5-A3DE-9263032CFB84}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A395C070-F971-48D5-A3DE-9263032CFB84}.Release|Any CPU.Build.0 = Release|Any CPU - {2EF6BF24-A068-47E2-ABBF-DE2666607D65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2EF6BF24-A068-47E2-ABBF-DE2666607D65}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2EF6BF24-A068-47E2-ABBF-DE2666607D65}.netfx|Any CPU.ActiveCfg = netfx|Any CPU - {2EF6BF24-A068-47E2-ABBF-DE2666607D65}.netfx|Any CPU.Build.0 = netfx|Any CPU - {2EF6BF24-A068-47E2-ABBF-DE2666607D65}.netstandard|Any CPU.ActiveCfg = Release|Any CPU - {2EF6BF24-A068-47E2-ABBF-DE2666607D65}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2EF6BF24-A068-47E2-ABBF-DE2666607D65}.Release|Any CPU.Build.0 = Release|Any CPU {711F4331-198A-4919-ACA8-C2B303E045A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {711F4331-198A-4919-ACA8-C2B303E045A8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {711F4331-198A-4919-ACA8-C2B303E045A8}.netfx|Any CPU.ActiveCfg = netfx|Any CPU - {711F4331-198A-4919-ACA8-C2B303E045A8}.netstandard|Any CPU.ActiveCfg = Release|Any CPU - {711F4331-198A-4919-ACA8-C2B303E045A8}.netstandard|Any CPU.Build.0 = Release|Any CPU {711F4331-198A-4919-ACA8-C2B303E045A8}.Release|Any CPU.ActiveCfg = Release|Any CPU {711F4331-198A-4919-ACA8-C2B303E045A8}.Release|Any CPU.Build.0 = Release|Any CPU {DC84C790-A0B0-4D40-A835-8128177F8FA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DC84C790-A0B0-4D40-A835-8128177F8FA3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DC84C790-A0B0-4D40-A835-8128177F8FA3}.netfx|Any CPU.ActiveCfg = netfx|Any CPU - {DC84C790-A0B0-4D40-A835-8128177F8FA3}.netstandard|Any CPU.ActiveCfg = Debug|Any CPU - {DC84C790-A0B0-4D40-A835-8128177F8FA3}.netstandard|Any CPU.Build.0 = Debug|Any CPU {DC84C790-A0B0-4D40-A835-8128177F8FA3}.Release|Any CPU.ActiveCfg = Release|Any CPU {DC84C790-A0B0-4D40-A835-8128177F8FA3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection From e87347c2c5926b80ce454662e723ff92776eabb0 Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 11 Nov 2017 03:11:07 -0500 Subject: [PATCH 2/9] Remove the actual files in the old projects. --- Keen.NET.Test/AddOnsTest.cs | 114 -- .../ApiResponses/GetDatasetDefinition.json | 23 - .../ApiResponses/GetDatasetResults.json | 52 - .../ApiResponses/ListDatasetDefinitions.json | 60 - Keen.NET.Test/DataSetTests_Integration.cs | 435 ------ Keen.NET.Test/DatasetTests.cs | 161 --- Keen.NET.Test/DelegatingHandlerMock.cs | 29 - Keen.NET.Test/EventCacheTest.cs | 113 -- Keen.NET.Test/EventCollectionMock.cs | 48 - Keen.NET.Test/EventMock.cs | 40 - Keen.NET.Test/FuncHandler.cs | 64 - Keen.NET.Test/FunnelTest.cs | 500 ------- Keen.NET.Test/HttpClientHandlerMock.cs | 31 - Keen.NET.Test/HttpTests.cs | 155 --- Keen.NET.Test/IHttpMessageHandler.cs | 31 - Keen.NET.Test/Keen.NET.Test.csproj | 165 --- Keen.NET.Test/KeenClientTest.cs | 982 -------------- Keen.NET.Test/ProjectSettingsProviderTest.cs | 105 -- Keen.NET.Test/Properties/AssemblyInfo.cs | 17 - Keen.NET.Test/QueryTest.cs | 1187 ----------------- Keen.NET.Test/QueryTests_Integration.cs | 1121 ---------------- Keen.NET.Test/ScopedKeyTest.cs | 207 --- Keen.NET.Test/TestKeenHttpClientProvider.cs | 23 - Keen.NET.Test/TimeframeConverterTest.cs | 35 - Keen.NET.Test/UrlToMessageHandler.cs | 91 -- Keen.NET.Test/app.config | 11 - Keen.NET.Test/packages.config | 11 - Keen.NET_35.Test/AddOnsTest.cs | 109 -- Keen.NET_35.Test/EventCollectionMock.cs | 45 - Keen.NET_35.Test/EventMock.cs | 38 - Keen.NET_35.Test/Keen.NET_35.Test.csproj | 161 --- Keen.NET_35.Test/KeenClientTest.cs | 737 ---------- Keen.NET_35.Test/Properties/AssemblyInfo.cs | 17 - Keen.NET_35.Test/ScopedKeyTest.cs | 127 -- Keen.NET_35.Test/packages.config | 6 - Keen.NET_35/CachedEvent.cs | 33 - Keen.NET_35/DataEnrichment/EventAddOn.cs | 110 -- Keen.NET_35/DynamicPropertyValue.cs | 37 - Keen.NET_35/Event.cs | 120 -- Keen.NET_35/EventCacheMemory.cs | 36 - Keen.NET_35/EventCollection.cs | 103 -- Keen.NET_35/IDynamicPropertyValue.cs | 12 - Keen.NET_35/IEvent.cs | 21 - Keen.NET_35/IEventCache.cs | 9 - Keen.NET_35/IEventCollection.cs | 28 - Keen.NET_35/IProjectSettings.cs | 40 - Keen.NET_35/Keen.NET_35.csproj | 96 -- Keen.NET_35/KeenClient.cs | 329 ----- Keen.NET_35/KeenConstants.cs | 88 -- Keen.NET_35/KeenException.cs | 83 -- Keen.NET_35/KeenUtil.cs | 243 ---- Keen.NET_35/ProjectSettingsProvider.cs | 65 - Keen.NET_35/ProjectSettingsProviderEnv.cs | 26 - Keen.NET_35/Properties/AssemblyInfo.cs | 17 - Keen.NET_35/ScopedKey.cs | 161 --- Keen.NET_35/packages.config | 5 - Keen.Net/EventCacheMemory.cs | 49 - Keen.Net/Keen.Net.csproj | 125 -- Keen.Net/ProjectSettingsProviderEnv.cs | 29 - Keen.Net/ProjectSettingsProviderFile.cs | 33 - Keen.Net/Properties/AssemblyInfo.cs | 17 - Keen.Net/ScopedKey.cs | 161 --- Keen.Net/app.config | 11 - Keen.Net/packages.config | 9 - Keen/CachedEvent.cs | 31 - Keen/DataEnrichment/EventAddOn.cs | 111 -- Keen/Dataset/DatasetDefinition.cs | 76 -- Keen/Dataset/DatasetDefinitionCollection.cs | 26 - Keen/Dataset/Datasets.cs | 306 ----- Keen/Dataset/IDataset.cs | 60 - Keen/Dataset/QueryDefinitionExtensions.cs | 31 - Keen/DynamicPropertyValue.cs | 34 - Keen/Event.cs | 156 --- Keen/EventCachePortable.cs | 163 --- Keen/EventCollection.cs | 161 --- Keen/HttpClientCache.cs | 248 ---- Keen/IDynamicPropertyValue.cs | 8 - Keen/IEvent.cs | 24 - Keen/IEventCache.cs | 12 - Keen/IEventCollection.cs | 30 - Keen/IHttpClientProvider.cs | 66 - Keen/IKeenHttpClient.cs | 96 -- Keen/IKeenHttpClientProvider.cs | 23 - Keen/IProjectSettings.cs | 39 - Keen/Keen.csproj | 167 --- Keen/KeenClient.cs | 1150 ---------------- Keen/KeenConstants.cs | 102 -- Keen/KeenException.cs | 85 -- Keen/KeenHttpClient.cs | 230 ---- Keen/KeenHttpClientFactory.cs | 260 ---- Keen/KeenHttpClientProvider.cs | 26 - Keen/KeenUtil.cs | 222 --- Keen/ProjectSettingsProvider.cs | 62 - Keen/Properties/AssemblyInfo.cs | 14 - Keen/Query/FunnelResult.cs | 18 - Keen/Query/FunnelResultStep.cs | 34 - Keen/Query/FunnelStep.cs | 63 - Keen/Query/IQueries.cs | 32 - Keen/Query/IQueryTimeframe.cs | 7 - Keen/Query/MultiAnalysisParam.cs | 41 - Keen/Query/Queries.cs | 443 ------ Keen/Query/QueryAbsoluteTimeframe.cs | 33 - Keen/Query/QueryDefinition.cs | 54 - Keen/Query/QueryFilter.cs | 163 --- Keen/Query/QueryGroupValue.cs | 26 - Keen/Query/QueryInterval.cs | 82 -- Keen/Query/QueryIntervalValue.cs | 34 - Keen/Query/QueryRelativeTimeframe.cs | 210 --- Keen/Query/QueryType.cs | 54 - Keen/Query/TimeframeConverter.cs | 57 - Keen/app.config | 15 - Keen/packages.config | 9 - SharedAssemblyInfo.cs | 16 - SharedVersionInfo.cs | 26 - 114 files changed, 14583 deletions(-) delete mode 100644 Keen.NET.Test/AddOnsTest.cs delete mode 100644 Keen.NET.Test/ApiResponses/GetDatasetDefinition.json delete mode 100644 Keen.NET.Test/ApiResponses/GetDatasetResults.json delete mode 100644 Keen.NET.Test/ApiResponses/ListDatasetDefinitions.json delete mode 100644 Keen.NET.Test/DataSetTests_Integration.cs delete mode 100644 Keen.NET.Test/DatasetTests.cs delete mode 100644 Keen.NET.Test/DelegatingHandlerMock.cs delete mode 100644 Keen.NET.Test/EventCacheTest.cs delete mode 100644 Keen.NET.Test/EventCollectionMock.cs delete mode 100644 Keen.NET.Test/EventMock.cs delete mode 100644 Keen.NET.Test/FuncHandler.cs delete mode 100644 Keen.NET.Test/FunnelTest.cs delete mode 100644 Keen.NET.Test/HttpClientHandlerMock.cs delete mode 100644 Keen.NET.Test/HttpTests.cs delete mode 100644 Keen.NET.Test/IHttpMessageHandler.cs delete mode 100644 Keen.NET.Test/Keen.NET.Test.csproj delete mode 100644 Keen.NET.Test/KeenClientTest.cs delete mode 100644 Keen.NET.Test/ProjectSettingsProviderTest.cs delete mode 100644 Keen.NET.Test/Properties/AssemblyInfo.cs delete mode 100644 Keen.NET.Test/QueryTest.cs delete mode 100644 Keen.NET.Test/QueryTests_Integration.cs delete mode 100644 Keen.NET.Test/ScopedKeyTest.cs delete mode 100644 Keen.NET.Test/TestKeenHttpClientProvider.cs delete mode 100644 Keen.NET.Test/TimeframeConverterTest.cs delete mode 100644 Keen.NET.Test/UrlToMessageHandler.cs delete mode 100644 Keen.NET.Test/app.config delete mode 100644 Keen.NET.Test/packages.config delete mode 100644 Keen.NET_35.Test/AddOnsTest.cs delete mode 100644 Keen.NET_35.Test/EventCollectionMock.cs delete mode 100644 Keen.NET_35.Test/EventMock.cs delete mode 100644 Keen.NET_35.Test/Keen.NET_35.Test.csproj delete mode 100644 Keen.NET_35.Test/KeenClientTest.cs delete mode 100644 Keen.NET_35.Test/Properties/AssemblyInfo.cs delete mode 100644 Keen.NET_35.Test/ScopedKeyTest.cs delete mode 100644 Keen.NET_35.Test/packages.config delete mode 100644 Keen.NET_35/CachedEvent.cs delete mode 100644 Keen.NET_35/DataEnrichment/EventAddOn.cs delete mode 100644 Keen.NET_35/DynamicPropertyValue.cs delete mode 100644 Keen.NET_35/Event.cs delete mode 100644 Keen.NET_35/EventCacheMemory.cs delete mode 100644 Keen.NET_35/EventCollection.cs delete mode 100644 Keen.NET_35/IDynamicPropertyValue.cs delete mode 100644 Keen.NET_35/IEvent.cs delete mode 100644 Keen.NET_35/IEventCache.cs delete mode 100644 Keen.NET_35/IEventCollection.cs delete mode 100644 Keen.NET_35/IProjectSettings.cs delete mode 100644 Keen.NET_35/Keen.NET_35.csproj delete mode 100644 Keen.NET_35/KeenClient.cs delete mode 100644 Keen.NET_35/KeenConstants.cs delete mode 100644 Keen.NET_35/KeenException.cs delete mode 100644 Keen.NET_35/KeenUtil.cs delete mode 100644 Keen.NET_35/ProjectSettingsProvider.cs delete mode 100644 Keen.NET_35/ProjectSettingsProviderEnv.cs delete mode 100644 Keen.NET_35/Properties/AssemblyInfo.cs delete mode 100644 Keen.NET_35/ScopedKey.cs delete mode 100644 Keen.NET_35/packages.config delete mode 100644 Keen.Net/EventCacheMemory.cs delete mode 100644 Keen.Net/Keen.Net.csproj delete mode 100644 Keen.Net/ProjectSettingsProviderEnv.cs delete mode 100644 Keen.Net/ProjectSettingsProviderFile.cs delete mode 100644 Keen.Net/Properties/AssemblyInfo.cs delete mode 100644 Keen.Net/ScopedKey.cs delete mode 100644 Keen.Net/app.config delete mode 100644 Keen.Net/packages.config delete mode 100644 Keen/CachedEvent.cs delete mode 100644 Keen/DataEnrichment/EventAddOn.cs delete mode 100644 Keen/Dataset/DatasetDefinition.cs delete mode 100644 Keen/Dataset/DatasetDefinitionCollection.cs delete mode 100644 Keen/Dataset/Datasets.cs delete mode 100644 Keen/Dataset/IDataset.cs delete mode 100644 Keen/Dataset/QueryDefinitionExtensions.cs delete mode 100644 Keen/DynamicPropertyValue.cs delete mode 100644 Keen/Event.cs delete mode 100644 Keen/EventCachePortable.cs delete mode 100644 Keen/EventCollection.cs delete mode 100644 Keen/HttpClientCache.cs delete mode 100644 Keen/IDynamicPropertyValue.cs delete mode 100644 Keen/IEvent.cs delete mode 100644 Keen/IEventCache.cs delete mode 100644 Keen/IEventCollection.cs delete mode 100644 Keen/IHttpClientProvider.cs delete mode 100644 Keen/IKeenHttpClient.cs delete mode 100644 Keen/IKeenHttpClientProvider.cs delete mode 100644 Keen/IProjectSettings.cs delete mode 100644 Keen/Keen.csproj delete mode 100644 Keen/KeenClient.cs delete mode 100644 Keen/KeenConstants.cs delete mode 100644 Keen/KeenException.cs delete mode 100644 Keen/KeenHttpClient.cs delete mode 100644 Keen/KeenHttpClientFactory.cs delete mode 100644 Keen/KeenHttpClientProvider.cs delete mode 100644 Keen/KeenUtil.cs delete mode 100644 Keen/ProjectSettingsProvider.cs delete mode 100644 Keen/Properties/AssemblyInfo.cs delete mode 100644 Keen/Query/FunnelResult.cs delete mode 100644 Keen/Query/FunnelResultStep.cs delete mode 100644 Keen/Query/FunnelStep.cs delete mode 100644 Keen/Query/IQueries.cs delete mode 100644 Keen/Query/IQueryTimeframe.cs delete mode 100644 Keen/Query/MultiAnalysisParam.cs delete mode 100644 Keen/Query/Queries.cs delete mode 100644 Keen/Query/QueryAbsoluteTimeframe.cs delete mode 100644 Keen/Query/QueryDefinition.cs delete mode 100644 Keen/Query/QueryFilter.cs delete mode 100644 Keen/Query/QueryGroupValue.cs delete mode 100644 Keen/Query/QueryInterval.cs delete mode 100644 Keen/Query/QueryIntervalValue.cs delete mode 100644 Keen/Query/QueryRelativeTimeframe.cs delete mode 100644 Keen/Query/QueryType.cs delete mode 100644 Keen/Query/TimeframeConverter.cs delete mode 100644 Keen/app.config delete mode 100644 Keen/packages.config delete mode 100644 SharedAssemblyInfo.cs delete mode 100644 SharedVersionInfo.cs diff --git a/Keen.NET.Test/AddOnsTest.cs b/Keen.NET.Test/AddOnsTest.cs deleted file mode 100644 index 74db8ad..0000000 --- a/Keen.NET.Test/AddOnsTest.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System; -using System.Collections.Generic; -using Keen.Core; -using Keen.Core.DataEnrichment; -using NUnit.Framework; - -namespace Keen.Net.Test -{ - [TestFixture] - public class AddOnsTest : TestBase - { - [Test] - public void No_AddOn_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: (c, e, p) => - { - if (e["keen"].ToString().Contains("keen:ip_to_geo")) - throw new Exception("Unexpected values"); - }); - - Assert.DoesNotThrow(() => client.AddEvent("AddOnTest", new { an_ip = "70.187.8.97" })); - } - - [Test] - public void IpToGeo_Send_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: (c, e, p) => - { - if (!e["keen"].ToString().Contains("keen:ip_to_geo")) - throw new Exception("Unexpected values"); - }); - - var a = AddOn.IpToGeo("an_ip", "geocode"); - - Assert.DoesNotThrow(() => client.AddEvent("AddOnTest", new {an_ip = "70.187.8.97"}, new List {a})); - } - - [Test] - public void IpToGeo_MissingInput_Throws() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: (c, e, p) => - { - if (!e["keen"].ToString().Contains("\"ip\": \"an_ip\"")) - throw new KeenException("Unexpected values"); - }); - - var a = AddOn.IpToGeo("wrong_field", "geocode"); - - Assert.Throws(() => client.AddEvent("AddOnTest", new { an_ip = "70.187.8.97" }, new List { a })); - } - - - [Test] - public void UserAgentParser_Send_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: (c, e, p) => - { - if (!e["keen"].ToString().Contains("keen:ua_parser")) - throw new Exception("Unexpected values"); - }); - - var a = AddOn.UserAgentParser("user_agent_string", "user_agent_parsed"); - - Assert.DoesNotThrow(() => client.AddEvent("AddOnTest", new { user_agent_string = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36" }, new List { a })); - } - - [Test] - public void UrlParser_Send_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: (c, e, p) => - { - if (!e["keen"].ToString().Contains("keen:url_parser")) - throw new Exception("Unexpected values"); - }); - - var a = AddOn.UrlParser("url", "url_parsed"); - - Assert.DoesNotThrow(() => client.AddEvent("AddOnTest", new { url = "https://keen.io/docs/data-collection/data-enrichment/#anchor" }, new List { a })); - } - - [Test] - public void ReferrerParser_Send_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: (c, e, p) => - { - if (!e["keen"].ToString().Contains("keen:referrer_parser")) - throw new Exception("Unexpected values"); - }); - - var a = AddOn.ReferrerParser("referrer", "page", "referrer_parsed"); - - Assert.DoesNotThrow(() => client.AddEvent("AddOnTest", new { page = "", referrer = "" }, new List { a })); - } - - } -} diff --git a/Keen.NET.Test/ApiResponses/GetDatasetDefinition.json b/Keen.NET.Test/ApiResponses/GetDatasetDefinition.json deleted file mode 100644 index d225fdc..0000000 --- a/Keen.NET.Test/ApiResponses/GetDatasetDefinition.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "dataset_name": "count-purchases-gte-100-by-country-daily", - "display_name": "Count Daily Product Purchases Over $100 by Country", - "query": { - "project_id": "5011efa95f546f2ce2000000", - "analysis_type": "count", - "event_collection": "purchases", - "filters": [ - { - "property_name": "price", - "operator": "gte", - "property_value": 100 - } - ], - "timeframe": "this_500_days", - "interval": "daily", - "group_by": [ "ip_geo_info.country" ] - }, - "index_by": [ "product.id" ], - "last_scheduled_date": "2016-11-04T18:52:36.323Z", - "latest_subtimeframe_available": "2016-11-05T00:00:00.000Z", - "milliseconds_behind": 3600000 -} \ No newline at end of file diff --git a/Keen.NET.Test/ApiResponses/GetDatasetResults.json b/Keen.NET.Test/ApiResponses/GetDatasetResults.json deleted file mode 100644 index 9220db7..0000000 --- a/Keen.NET.Test/ApiResponses/GetDatasetResults.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "result": [ - { - "timeframe": { - "start": "2016-11-02T00:00:00.000Z", - "end": "2016-11-03T00:00:00.000Z" - }, - "value": [ - { - "item.name": "Golden Widget", - "result": 0 - }, - { - "item.name": "Silver Widget", - "result": 18 - }, - { - "item.name": "Bronze Widget", - "result": 1 - }, - { - "item.name": "Platinum Widget", - "result": 9 - } - ] - }, - { - "timeframe": { - "start": "2016-11-03T00:00:00.000Z", - "end": "2016-11-04T00:00:00.000Z" - }, - "value": [ - { - "item.name": "Golden Widget", - "result": 1 - }, - { - "item.name": "Silver Widget", - "result": 13 - }, - { - "item.name": "Bronze Widget", - "result": 0 - }, - { - "item.name": "Platinum Widget", - "result": 3 - } - ] - } - ] -} \ No newline at end of file diff --git a/Keen.NET.Test/ApiResponses/ListDatasetDefinitions.json b/Keen.NET.Test/ApiResponses/ListDatasetDefinitions.json deleted file mode 100644 index c7748f9..0000000 --- a/Keen.NET.Test/ApiResponses/ListDatasetDefinitions.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "datasets": [ - { - "project_id": "PROJECT_ID", - "organization_id": "ORGANIZATION_ID", - "dataset_name": "DATASET_NAME_1", - "display_name": "a first dataset wee", - "query": { - "project_id": "PROJECT_ID", - "analysis_type": "count", - "event_collection": "best collection", - "filters": [ - { - "property_name": "request.foo", - "operator": "lt", - "property_value": 300 - } - ], - "timeframe": "this_500_hours", - "timezone": "US/Pacific", - "interval": "hourly", - "group_by": [ - "exception.name" - ] - }, - "index_by": [ - "project.id" - ], - "last_scheduled_date": "2016-11-04T18:03:38.430Z", - "latest_subtimeframe_available": "2016-11-04T19:00:00.000Z", - "milliseconds_behind": 3600000 - }, - { - "project_id": "PROJECT_ID", - "organization_id": "ORGANIZATION_ID", - "dataset_name": "DATASET_NAME_10", - "display_name": "tenth dataset wee", - "query": { - "project_id": "PROJECT_ID", - "analysis_type": "count", - "event_collection": "tenth best collection", - "filters": [], - "timeframe": "this_500_days", - "timezone": "UTC", - "interval": "daily", - "group_by": [ - "analysis_type" - ] - }, - "index_by": [ - "project.organization.id" - ], - "last_scheduled_date": "2016-11-04T19:28:36.639Z", - "latest_subtimeframe_available": "2016-11-05T00:00:00.000Z", - "milliseconds_behind": 3600000 - } - ], - "next_page_url": "https://api.keen.io/3.0/projects/PROJECT_ID/datasets?limit=LIMIT&after_name=DATASET_NAME_10", - "count": 4 -} \ No newline at end of file diff --git a/Keen.NET.Test/DataSetTests_Integration.cs b/Keen.NET.Test/DataSetTests_Integration.cs deleted file mode 100644 index 4169380..0000000 --- a/Keen.NET.Test/DataSetTests_Integration.cs +++ /dev/null @@ -1,435 +0,0 @@ -using Keen.Core; -using Keen.Core.Dataset; -using Keen.Core.Query; -using Moq; -using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; - - -namespace Keen.Net.Test -{ - [TestFixture] - public class DatasetTests_Integration : TestBase - { - private const string _datasetName = "video-view"; - private const string _indexBy = "12"; - private const string _timeframe = "this_12_days"; - - - [Test] - public void Results_Success() - { - var apiResponse = File.ReadAllText($"{GetApiResponsesPath()}/GetDatasetResults.json"); - IKeenHttpClientProvider httpClientProvider = null; - - if (UseMocks) - { - httpClientProvider = GetMockHttpClientProviderForGetAsync(apiResponse); - } - - var client = new KeenClient(SettingsEnv, httpClientProvider); - var dataset = client.QueryDataset(_datasetName, _indexBy, _timeframe); - - Assert.IsNotNull(dataset); - Assert.IsNotNull(dataset["result"]); - } - - [Test] - public void Definition_Success() - { - var apiResponse = File.ReadAllText($"{GetApiResponsesPath()}/GetDatasetDefinition.json"); - IKeenHttpClientProvider httpClientProvider = null; - - if (UseMocks) - { - httpClientProvider = GetMockHttpClientProviderForGetAsync(apiResponse); - } - - var client = new KeenClient(SettingsEnv, httpClientProvider); - var dataset = client.GetDatasetDefinition(_datasetName); - - AssertDatasetIsPopulated(dataset); - } - - [Test] - public void ListDefinitions_Success() - { - var apiResponse = File.ReadAllText($"{GetApiResponsesPath()}/ListDatasetDefinitions.json"); - IKeenHttpClientProvider httpClientProvider = null; - - if (UseMocks) - { - httpClientProvider = GetMockHttpClientProviderForGetAsync(apiResponse); - } - - var client = new KeenClient(SettingsEnv, httpClientProvider); - var datasetCollection = client.ListDatasetDefinitions(); - - Assert.IsNotNull(datasetCollection); - Assert.IsNotNull(datasetCollection.Datasets); - Assert.IsTrue(datasetCollection.Datasets.Any()); - Assert.IsTrue(!string.IsNullOrWhiteSpace(datasetCollection.NextPageUrl)); - - foreach (var item in datasetCollection.Datasets) - { - AssertDatasetIsPopulated(item); - } - } - - [Test] - public void ListAllDefinitions_Success() - { - var apiResponse = File.ReadAllText($"{GetApiResponsesPath()}/ListDatasetDefinitions.json"); - IKeenHttpClientProvider httpClientProvider = null; - - if (UseMocks) - { - httpClientProvider = GetMockHttpClientProviderForGetAsync(apiResponse); - } - - var client = new KeenClient(SettingsEnv, httpClientProvider); - var datasetCollection = client.ListAllDatasetDefinitions(); - - Assert.IsNotNull(datasetCollection); - Assert.IsTrue(datasetCollection.Any()); - - foreach (var item in datasetCollection) - { - AssertDatasetIsPopulated(item); - } - } - - [Test] - public void Delete_Success() - { - IKeenHttpClientProvider httpClientProvider = null; - - if (UseMocks) - { - httpClientProvider = GetMockHttpClientProviderForDeleteAsync(string.Empty); - } - - var client = new KeenClient(SettingsEnv, httpClientProvider); - - client.DeleteDataset("datasetName"); - } - - [Test] - public void CreateDataset_Success() - { - var apiResponse = File.ReadAllText($"{GetApiResponsesPath()}/GetDatasetDefinition.json"); - - IKeenHttpClientProvider httpClientProvider = null; - - if (UseMocks) - { - httpClientProvider = GetMockHttpClientProviderForPutAsync(apiResponse); - } - - var client = new KeenClient(SettingsEnv, httpClientProvider); - var newDataSet = CreateDatasetDefinition(); - var dataset = client.CreateDataset(newDataSet); - - AssertDatasetIsPopulated(dataset); - } - - [Test] - public void DatasetValidation_Throws() - { - var dataset = new DatasetDefinition(); - - Assert.Throws(() => dataset.Validate()); - - dataset.DatasetName = "count-purchases-gte-100-by-country-daily"; - - Assert.Throws(() => dataset.Validate()); - - dataset.DisplayName = "Count Daily Product Purchases Over $100 by Country"; - - Assert.Throws(() => dataset.Validate()); - - dataset.IndexBy = new List { "product.id" }; - - Assert.Throws(() => dataset.Validate()); - - dataset.Query = new QueryDefinition(); - - Assert.Throws(() => dataset.Validate()); - - dataset.Query.AnalysisType = "count"; - - Assert.Throws(() => dataset.Validate()); - - dataset.Query.EventCollection = "purchases"; - - Assert.Throws(() => dataset.Validate()); - - dataset.Query.Timeframe = "this_500_days"; - - Assert.Throws(() => dataset.Validate()); - - dataset.Query.Interval = "daily"; - - Assert.DoesNotThrow(() => dataset.Validate()); - } - - [Test] - public void Results_Throws() - { - IKeenHttpClientProvider httpClientProvider = null; - - if (UseMocks) - { - httpClientProvider = GetMockHttpClientProviderForGetAsync("{}", HttpStatusCode.InternalServerError); - } - - var client = new KeenClient(SettingsEnv, httpClientProvider); - - Assert.Throws(() => client.QueryDataset(null, _indexBy, _timeframe)); - Assert.Throws(() => client.QueryDataset(_datasetName, null, _timeframe)); - Assert.Throws(() => client.QueryDataset(_datasetName, _indexBy, null)); - - Assert.Throws(() => client.QueryDataset(_datasetName, _indexBy, _timeframe)); - - var brokenClient = new KeenClient(new ProjectSettingsProvider("5011efa95f546f2ce2000000", - null, - Environment.GetEnvironmentVariable("KEEN_WRITE_KEY") ?? "", - Environment.GetEnvironmentVariable("KEEN_READ_KEY") ?? "", - Environment.GetEnvironmentVariable("KEEN_SERVER_URL") ?? KeenConstants.ServerAddress + "/" + KeenConstants.ApiVersion + "/"), - httpClientProvider); - - Assert.Throws(() => brokenClient.QueryDataset(_datasetName, _indexBy, _timeframe)); - } - - [Test] - public void Definition_Throws() - { - IKeenHttpClientProvider httpClientProvider = null; - - if (UseMocks) - { - httpClientProvider = GetMockHttpClientProviderForGetAsync("{}", HttpStatusCode.InternalServerError); - } - - var client = new KeenClient(SettingsEnv, httpClientProvider); - - Assert.Throws(() => client.GetDatasetDefinition(null)); - Assert.Throws(() => client.GetDatasetDefinition(_datasetName)); - - var brokenClient = new KeenClient(new ProjectSettingsProvider("5011efa95f546f2ce2000000", - null, - Environment.GetEnvironmentVariable("KEEN_WRITE_KEY") ?? "", - Environment.GetEnvironmentVariable("KEEN_READ_KEY") ?? "", - Environment.GetEnvironmentVariable("KEEN_SERVER_URL") ?? KeenConstants.ServerAddress + "/" + KeenConstants.ApiVersion + "/"), - httpClientProvider); - - Assert.Throws(() => brokenClient.GetDatasetDefinition(_datasetName)); - } - - [Test] - public void ListDefinitions_Throws() - { - IKeenHttpClientProvider httpClientProvider = null; - - if (UseMocks) - { - httpClientProvider = GetMockHttpClientProviderForGetAsync("{}", HttpStatusCode.InternalServerError); - } - - var client = new KeenClient(SettingsEnv, httpClientProvider); - - Assert.Throws(() => client.ListDatasetDefinitions()); - - var brokenClient = new KeenClient(new ProjectSettingsProvider("5011efa95f546f2ce2000000", - null, - Environment.GetEnvironmentVariable("KEEN_WRITE_KEY") ?? "", - Environment.GetEnvironmentVariable("KEEN_READ_KEY") ?? "", - Environment.GetEnvironmentVariable("KEEN_SERVER_URL") ?? KeenConstants.ServerAddress + "/" + KeenConstants.ApiVersion + "/"), - httpClientProvider); - - Assert.Throws(() => brokenClient.ListDatasetDefinitions()); - } - - [Test] - public void DeleteDataset_Throws() - { - IKeenHttpClientProvider httpClientProvider = null; - - if (UseMocks) - { - httpClientProvider = GetMockHttpClientProviderForDeleteAsync("{}", HttpStatusCode.InternalServerError); - } - - var client = new KeenClient(SettingsEnv, httpClientProvider); - - Assert.Throws(() => client.DeleteDataset(null)); - Assert.Throws(() => client.DeleteDataset(_datasetName)); - - var brokenClient = new KeenClient(new ProjectSettingsProvider("5011efa95f546f2ce2000000", - null, - Environment.GetEnvironmentVariable("KEEN_WRITE_KEY") ?? "", - Environment.GetEnvironmentVariable("KEEN_READ_KEY") ?? "", - Environment.GetEnvironmentVariable("KEEN_SERVER_URL") ?? KeenConstants.ServerAddress + "/" + KeenConstants.ApiVersion + "/"), - httpClientProvider); - - Assert.Throws(() => brokenClient.DeleteDataset(_datasetName)); - } - - [Test] - public void CreateDataset_Throws() - { - IKeenHttpClientProvider httpClientProvider = null; - - if (UseMocks) - { - httpClientProvider = GetMockHttpClientProviderForDeleteAsync("{}", HttpStatusCode.InternalServerError); - } - - var client = new KeenClient(SettingsEnv, httpClientProvider); - - Assert.Throws(() => client.CreateDataset(null)); - - var brokenClient = new KeenClient(new ProjectSettingsProvider("5011efa95f546f2ce2000000", - null, - Environment.GetEnvironmentVariable("KEEN_WRITE_KEY") ?? "", - Environment.GetEnvironmentVariable("KEEN_READ_KEY") ?? "", - Environment.GetEnvironmentVariable("KEEN_SERVER_URL") ?? KeenConstants.ServerAddress + "/" + KeenConstants.ApiVersion + "/"), - httpClientProvider); - - Assert.Throws(() => brokenClient.CreateDataset(CreateDatasetDefinition())); - } - - private string GetApiResponsesPath() - { - var localPath = AppDomain.CurrentDomain.BaseDirectory; - var apiResponsesPath = $"{localPath}/ApiResponses"; - - return apiResponsesPath; - } - - private IKeenHttpClientProvider GetMockHttpClientProviderForGetAsync(string response, HttpStatusCode status = HttpStatusCode.OK) - { - var httpResponseMessage = new HttpResponseMessage - { - Content = new StringContent(response), - StatusCode = status - }; - - var mockHttpClient = new Mock(); - mockHttpClient.Setup(m => m.GetAsync( - It.IsAny(), - It.IsAny())) - .Returns(Task.FromResult(httpResponseMessage)); - - return new TestKeenHttpClientProvider - { - ProvideKeenHttpClient = (url) => mockHttpClient.Object - }; - } - - private IKeenHttpClientProvider GetMockHttpClientProviderForPutAsync(string response) - { - var httpResponseMessage = new HttpResponseMessage - { - Content = new StringContent(response), - StatusCode = HttpStatusCode.Created - }; - - var mockHttpClient = new Mock(); - mockHttpClient.Setup(m => m.PutAsync( - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns(Task.FromResult(httpResponseMessage)); - - return new TestKeenHttpClientProvider - { - ProvideKeenHttpClient = (url) => mockHttpClient.Object - }; - } - - private IKeenHttpClientProvider GetMockHttpClientProviderForDeleteAsync(string response, HttpStatusCode status = HttpStatusCode.NoContent) - { - var httpResponseMessage = new HttpResponseMessage - { - Content = new StringContent(response), - StatusCode = status - }; - - var mockHttpClient = new Mock(); - mockHttpClient.Setup(m => m.DeleteAsync( - It.IsAny(), - It.IsAny())) - .Returns(Task.FromResult(httpResponseMessage)); - - return new TestKeenHttpClientProvider - { - ProvideKeenHttpClient = (url) => mockHttpClient.Object - }; - } - - private void AssertDatasetIsPopulated(DatasetDefinition dataset) - { - Assert.IsTrue(!string.IsNullOrWhiteSpace(dataset.DatasetName)); - Assert.IsTrue(!string.IsNullOrWhiteSpace(dataset.DisplayName)); - Assert.IsNotEmpty(dataset.IndexBy); - - if (UseMocks) - { - Assert.IsNotNull(dataset.LastScheduledDate); - Assert.IsNotNull(dataset.LatestSubtimeframeAvailable); - } - - Assert.IsNotNull(dataset.Query); - Assert.IsTrue(!string.IsNullOrWhiteSpace(dataset.Query.ProjectId)); - Assert.IsTrue(!string.IsNullOrWhiteSpace(dataset.Query.AnalysisType)); - Assert.IsTrue(!string.IsNullOrWhiteSpace(dataset.Query.EventCollection)); - Assert.IsTrue(!string.IsNullOrWhiteSpace(dataset.Query.Timeframe)); - Assert.IsTrue(!string.IsNullOrWhiteSpace(dataset.Query.Interval)); - - // TODO : We'll need to do some setup to actually get this to run automatically - // with !UseMocks...and take into account that it can "take up to an hour for a newly - // created dataset to compute results for the first time." - Assert.IsNotNull(dataset.Query.GroupBy); - Assert.IsTrue(dataset.Query.GroupBy.Count() == 1); - - if (dataset.Query.Filters != null) - { - foreach (var filter in dataset.Query.Filters) - { - AssertFilterIsPopulated(filter); - } - } - } - - private void AssertFilterIsPopulated(QueryFilter filter) - { - Assert.IsNotNull(filter); - Assert.IsTrue(!string.IsNullOrWhiteSpace(filter.PropertyName)); - Assert.IsTrue(!string.IsNullOrWhiteSpace(filter.Operator)); - } - - private DatasetDefinition CreateDatasetDefinition() - { - return new DatasetDefinition - { - DatasetName = "count-purchases-gte-100-by-country-daily", - DisplayName = "Count Daily Product Purchases Over $100 by Country", - IndexBy = new List { "product.id" }, - Query = new QueryDefinition - { - AnalysisType = "count", - EventCollection = "purchases", - Timeframe = "this_500_days", - Interval = "daily" - } - }; - } - } -} diff --git a/Keen.NET.Test/DatasetTests.cs b/Keen.NET.Test/DatasetTests.cs deleted file mode 100644 index ae9bce5..0000000 --- a/Keen.NET.Test/DatasetTests.cs +++ /dev/null @@ -1,161 +0,0 @@ -using Keen.Core; -using Keen.Core.Dataset; -using Keen.Core.Query; -using Moq; -using Newtonsoft.Json.Linq; -using NUnit.Framework; -using System.Collections.Generic; -using System.Threading.Tasks; - - -namespace Keen.Net.Test -{ - [TestFixture] - public class DatasetTests : TestBase - { - private const string _datasetName = "video-view"; - private const string _indexBy = "12"; - private const int _listDatasetLimit = 1; - - - [Test] - public void GetDatasetResults_Success() - { - var timeframe = QueryRelativeTimeframe.ThisNMinutes(12); - var result = new JObject(); - var client = new KeenClient(SettingsEnv); - - Mock datasetMock = null; - - if (UseMocks) - { - datasetMock = new Mock(); - datasetMock.Setup(m => m.GetResultsAsync( - It.Is(n => n == _datasetName), - It.Is(i => i == _indexBy), - It.Is(t => t == timeframe.ToString()))) - .ReturnsAsync(result); - - client.Datasets = datasetMock.Object; - } - - var dataset = client.QueryDataset(_datasetName, _indexBy, timeframe.ToString()); - Assert.IsNotNull(dataset); - - datasetMock?.VerifyAll(); - } - - [Test] - public void GetDatasetDefinition_Success() - { - var result = new DatasetDefinition(); - var client = new KeenClient(SettingsEnv); - Mock datasetMock = null; - - if (UseMocks) - { - datasetMock = new Mock(); - datasetMock.Setup(m => m.GetDefinitionAsync( - It.Is(n => n == _datasetName))) - .ReturnsAsync(result); - - client.Datasets = datasetMock.Object; - } - - var datasetDefinition = client.GetDatasetDefinition(_datasetName); - Assert.IsNotNull(datasetDefinition); - - datasetMock?.VerifyAll(); - } - - [Test] - public void ListDatasetDefinitions_Success() - { - var result = new DatasetDefinitionCollection(); - var client = new KeenClient(SettingsEnv); - Mock datasetMock = null; - - if (UseMocks) - { - datasetMock = new Mock(); - datasetMock.Setup(m => m.ListDefinitionsAsync( - It.Is(n => n == _listDatasetLimit), - It.Is(n => n == _datasetName))) - .ReturnsAsync(result); - - client.Datasets = datasetMock.Object; - } - - var datasetDefinitionCollection = client.ListDatasetDefinitions(_listDatasetLimit, _datasetName); - Assert.IsNotNull(datasetDefinitionCollection); - - datasetMock?.VerifyAll(); - } - - [Test] - public void ListDatasetAllDefinitions_Success() - { - IEnumerable result = new List(); - var client = new KeenClient(SettingsEnv); - Mock datasetMock = null; - - if (UseMocks) - { - datasetMock = new Mock(); - datasetMock.Setup(m => m.ListAllDefinitionsAsync()) - .ReturnsAsync(result); - - client.Datasets = datasetMock.Object; - } - - var datasetDefinitions = client.ListAllDatasetDefinitions(); - Assert.IsNotNull(datasetDefinitions); - - datasetMock?.VerifyAll(); - } - - [Test] - public void CreateDataset_Success() - { - var result = new DatasetDefinition(); - var client = new KeenClient(SettingsEnv); - Mock datasetMock = null; - - if (UseMocks) - { - datasetMock = new Mock(); - datasetMock.Setup(m => m.CreateDatasetAsync( - It.Is(n => n != null))) - .ReturnsAsync(result); - - client.Datasets = datasetMock.Object; - } - - var datasetDefinition = client.CreateDataset(new DatasetDefinition()); - Assert.IsNotNull(datasetDefinition); - - datasetMock?.VerifyAll(); - } - - [Test] - public void DeleteDataset_Success() - { - var client = new KeenClient(SettingsEnv); - Mock datasetMock = null; - - if (UseMocks) - { - datasetMock = new Mock(); - datasetMock.Setup(m => m.DeleteDatasetAsync( - It.Is(n => n == _datasetName))) - .Returns(Task.Delay(0)); - - client.Datasets = datasetMock.Object; - } - - client.DeleteDataset(_datasetName); - - datasetMock?.VerifyAll(); - } - } -} diff --git a/Keen.NET.Test/DelegatingHandlerMock.cs b/Keen.NET.Test/DelegatingHandlerMock.cs deleted file mode 100644 index 288e7f1..0000000 --- a/Keen.NET.Test/DelegatingHandlerMock.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; - - -namespace Keen.Net.Test -{ - /// - /// Wraps an and allows for using it as a DelegatingHandler. - /// If the IHttpMessageHandler doesn't already have a default action set, we'll have it call - /// our own base SendAsync() which will forward the request down the handler chain. - /// - internal class DelegatingHandlerMock : DelegatingHandler - { - private readonly IHttpMessageHandler _handler; - - internal DelegatingHandlerMock(IHttpMessageHandler handler) - { - _handler = handler; - _handler.DefaultAsync = (_handler.DefaultAsync ?? base.SendAsync); - } - - protected override Task SendAsync(HttpRequestMessage request, - CancellationToken cancellationToken) - { - return _handler.SendAsync(request, cancellationToken); - } - } -} diff --git a/Keen.NET.Test/EventCacheTest.cs b/Keen.NET.Test/EventCacheTest.cs deleted file mode 100644 index 135a71a..0000000 --- a/Keen.NET.Test/EventCacheTest.cs +++ /dev/null @@ -1,113 +0,0 @@ -using Keen.Core; -using Keen.Core.EventCache; -using Newtonsoft.Json.Linq; -using NUnit.Framework; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - - -namespace Keen.Net.Test -{ - [TestFixture] - public class EventCacheTest : TestBase - { - static readonly object[] Providers = - { - new object[] { new EventCacheMemory() }, - new object[] { EventCachePortable.New() } - }; - - [Test] - [TestCaseSource("Providers")] - public void AddEvent_Null_Throws(IEventCache cache) - { - Assert.ThrowsAsync(() => cache.Add(null)); - } - - [Test] - [TestCaseSource("Providers")] - public async Task AddEvent_ValidObject_Success(IEventCache cache) - { - await cache.Add(new CachedEvent("url", JObject.FromObject( new { AProperty = "AValue" }))); - } - - [Test] - [TestCaseSource("Providers")] - public async Task AddEvent_AddNotEmpty_Success(IEventCache cache) - { - await cache.Clear(); - Assert.Null(await cache.TryTake()); - await cache.Add(new CachedEvent("url", JObject.FromObject( new { AProperty = "AValue" }))); - Assert.NotNull(await cache.TryTake()); - } - - [Test] - [TestCaseSource("Providers")] - public async Task AddEvent_AddClearEmpty_Success(IEventCache cache) - { - await cache.Add( new CachedEvent("url", JObject.FromObject( new { AProperty = "AValue" }))); - await cache.Clear(); - Assert.Null(await cache.TryTake()); - } - - [Test] - [TestCaseSource("Providers")] - public async Task AddEvent_Iterate_Success(IEventCache cache) - { - await cache.Clear(); - await cache.Add( new CachedEvent("url", JObject.FromObject( new { AProperty = "AValue" }))); - await cache.Add( new CachedEvent("url", JObject.FromObject( new { AProperty = "AValue" }))); - Assert.NotNull(await cache.TryTake()); - Assert.NotNull(await cache.TryTake()); - Assert.Null(await cache.TryTake()); - } - - [Test] - [TestCaseSource("Providers")] - public void CachingPCL_SendEmptyEvents_Success(IEventCache cache) - { - var client = new KeenClient(SettingsEnv, cache); - Assert.DoesNotThrow(client.SendCachedEvents); - } - - [Test] - [TestCaseSource("Providers")] - public void CachingPCL_ClearEvents_Success(IEventCache cache) - { - var client = new KeenClient(SettingsEnv, cache); - Assert.DoesNotThrow(() => client.EventCache.Clear()); - } - - [Test] - [TestCaseSource("Providers")] - public void CachingPCL_AddEvents_Success(IEventCache cache) - { - var client = new KeenClient(SettingsEnv, cache); - - Assert.DoesNotThrow(() => client.AddEvent("CachedEventTest", new { AProperty = "AValue" })); - Assert.DoesNotThrow(() => client.AddEvent("CachedEventTest", new { AProperty = "AValue" })); - Assert.DoesNotThrow(() => client.AddEvent("CachedEventTest", new { AProperty = "AValue" })); - } - - [Test] - [TestCaseSource("Providers")] - public async Task CachingPCL_SendEventsParallel_Success(IEventCache cache) - { - await cache.Clear(); - var client = new KeenClient(SettingsEnv, cache); - if (UseMocks) - client.Event = new EventMock(SettingsEnv, - addEvents: (e, p) => new List()); - - (from i in Enumerable.Range(1,100) - select new { AProperty = "AValue" }) - .AsParallel() - .ForAll(e=>client.AddEvent("CachedEventTest", e)); - - await client.SendCachedEventsAsync(); - Assert.Null(await client.EventCache.TryTake(), "Cache is empty"); - } - - } -} diff --git a/Keen.NET.Test/EventCollectionMock.cs b/Keen.NET.Test/EventCollectionMock.cs deleted file mode 100644 index 1b4b035..0000000 --- a/Keen.NET.Test/EventCollectionMock.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Keen.Core; -using Newtonsoft.Json.Linq; -using System; -using System.Threading.Tasks; - - -namespace Keen.Net.Test -{ - /// - /// EventCollectionMock provides an implementation of IEventCollection 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 - /// IEventCollection for each test. - /// - class EventCollectionMock : IEventCollection - { - private readonly IProjectSettings _settings; - private readonly Func _getSchema; - private readonly Action _deleteCollection; - private readonly Action _addEvent; - - public Task GetSchema(string collection) - { - return Task.Run(()=>_getSchema(collection, _settings)); - } - - public Task DeleteCollection(string collection) - { - return Task.Run(() => _deleteCollection(collection, _settings)); - } - - public Task AddEvent(string collection, JObject anEvent) - { - return Task.Run(() => _addEvent(collection, anEvent, _settings)); - } - - public EventCollectionMock(IProjectSettings prjSettings, - Func getSchema = null, - Action deleteCollection = null, - Action addEvent = null) - { - _settings = prjSettings; - _getSchema = getSchema ?? ((s, p) => new JObject()); - _deleteCollection = deleteCollection ?? ((s, p) => { }); - _addEvent = addEvent ?? ((s, p, e) => { }); - } - } -} diff --git a/Keen.NET.Test/EventMock.cs b/Keen.NET.Test/EventMock.cs deleted file mode 100644 index b8a7a3e..0000000 --- a/Keen.NET.Test/EventMock.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Keen.Core.EventCache; - -namespace Keen.Core -{ - /// - /// EventMock provides an implementation of IEvent 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 - /// IEvent for each test. - /// - class EventMock : IEvent - { - private readonly IProjectSettings _settings; - private readonly Func _getSchemas; - private readonly Func> _addEvents; - - public Task GetSchemas() - { - return Task.Run(() => _getSchemas(_settings)); - } - - public Task> AddEvents(JObject events) - { - return Task.Run(() => _addEvents(events, _settings)); - } - - public EventMock(IProjectSettings prjSettings, - Func getSchemas = null, - Func> addEvents = null) - { - _settings = prjSettings; - _getSchemas = getSchemas ?? ((p) => new JArray()); - _addEvents = addEvents ?? ((p, e) => new List()); - } - } -} diff --git a/Keen.NET.Test/FuncHandler.cs b/Keen.NET.Test/FuncHandler.cs deleted file mode 100644 index 8ce734d..0000000 --- a/Keen.NET.Test/FuncHandler.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; - - -namespace Keen.Net.Test -{ - /// - /// An that has pre/post/default message handlers functors, - /// as well as a Func<> that produces the actual HttpResponseMessage. These can all be set by - /// test code and will be called if available. There are defaults in place that essentially do - /// nothing, but client code should make sure DefaultAsync gets set, either by a wrapper or - /// explicitly. - /// - internal class FuncHandler : IHttpMessageHandler - { - internal Action PreProcess = (request, ct) => { }; - - internal Func> ProduceResultAsync = - (request, ct) => Task.FromResult(null); - - internal Func PostProcess = (request, response) => response; - - internal bool DeferToDefault { get; set; } = true; - - public Func> DefaultAsync { get; set; } - - - public async Task SendAsync( - HttpRequestMessage request, - CancellationToken cancellationToken) - { - PreProcess(request, cancellationToken); - HttpResponseMessage response = - await ProduceResultAsync(request, cancellationToken).ConfigureAwait(false); - - // Pass it along down the line if we didn't create a result already. - if (null == response) - { - if (DeferToDefault) - { - response = await DefaultAsync(request, cancellationToken).ConfigureAwait(false); - } - else - { - // Create a dummy successful response so HttpClient doesn't just throw always. - response = await HttpTests.CreateJsonStringResponseAsync(new { dummy = "" }) - .ConfigureAwait(false); - } - } - - PostProcess(request, response); - - return response; - } - } -} diff --git a/Keen.NET.Test/FunnelTest.cs b/Keen.NET.Test/FunnelTest.cs deleted file mode 100644 index b9da281..0000000 --- a/Keen.NET.Test/FunnelTest.cs +++ /dev/null @@ -1,500 +0,0 @@ -using Keen.Core; -using Keen.Core.Query; -using Moq; -using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - - -namespace Keen.Net.Test -{ - [TestFixture] - public class FunnelTest : TestBase - { - public FunnelTest() - { - UseMocks = true; - } - - const string FunnelColA = "FunnelTestA"; - const string FunnelColB = "FunnelTestB"; - const string FunnelColC = "FunnelTestC"; - - [OneTimeSetUp] - public override void Setup() - { - base.Setup(); - - // If not using mocks, set up conditions on the server - if (!UseMocks) - { - var client = new KeenClient(SettingsEnv); - - client.DeleteCollection(FunnelColA); - client.DeleteCollection(FunnelColB); - client.DeleteCollection(FunnelColC); - - client.AddEvent(FunnelColA, new { id = 1, name = new { first = "sam", last = "w" } }); - client.AddEvent(FunnelColA, new { id = 2, name = new { first = "dean", last = "w" } }); - client.AddEvent(FunnelColA, new { id = 3, name = new { first = "crowly", last = "" } }); - - client.AddEvent(FunnelColB, new { id = 1, name = new { first = "sam", last = "w" } }); - client.AddEvent(FunnelColB, new { id = 2, name = new { first = "dean", last = "w" } }); - - client.AddEvent(FunnelColC, new { id = 1, name = new { first = "sam", last = "w" } }); - - Thread.Sleep(8000); // Give it a moment to show up. Queries will fail if run too soon. - } - } - - [Test] - public async Task Funnel_Simple_Success() - { - var client = new KeenClient(SettingsEnv); - var timeframe = QueryRelativeTimeframe.ThisHour(); - - IEnumerable funnelsteps = new List - { - new FunnelStep - { - EventCollection = FunnelColA, - ActorProperty = "id", - }, - new FunnelStep - { - EventCollection = FunnelColB, - ActorProperty = "id" - }, - }; - - var expected = new FunnelResult - { - Steps = new [] - { - new FunnelResultStep - { - EventCollection = FunnelColA, - }, - new FunnelResultStep - { - EventCollection = FunnelColB, - }, - }, - Result = new[] { 3, 2 } - }; - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Funnel( - It.Is>(f => f.Equals(funnelsteps)), - It.Is(t => t == timeframe), - It.Is(t => t == "") - )) - .Returns(Task.FromResult(expected)); - - client.Queries = queryMock.Object; - } - - var reply = (await client.QueryFunnelAsync(funnelsteps, timeframe)); - Assert.NotNull(reply); - Assert.NotNull(reply.Result); - Assert.NotNull(reply.Steps); - Assert.AreEqual(reply.Steps.Count(), 2); - - if (null != queryMock) - queryMock.VerifyAll(); - } - - [Test] - public async Task Funnel_Inverted_Success() - { - var client = new KeenClient(SettingsEnv); - var timeframe = QueryRelativeTimeframe.ThisHour(); - - IEnumerable funnelsteps = new List - { - new FunnelStep - { - EventCollection = FunnelColA, - ActorProperty = "id", - }, - new FunnelStep - { - EventCollection = FunnelColB, - ActorProperty = "id", - Inverted = true - }, - }; - - var expected = new FunnelResult - { - Steps = new [] - { - new FunnelResultStep - { - EventCollection = FunnelColA, - }, - new FunnelResultStep - { - EventCollection = FunnelColB, - }, - }, - Result = new [] { 3, 1 } - }; - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Funnel( - It.Is>(f => f.Equals(funnelsteps)), - It.Is(t => t == timeframe), - It.Is(t => t == "") - )) - .Returns(Task.FromResult(expected)); - - client.Queries = queryMock.Object; - } - - var reply = (await client.QueryFunnelAsync(funnelsteps, timeframe)); - Assert.NotNull(reply); - Assert.NotNull(reply.Result); - Assert.True(reply.Result.SequenceEqual(expected.Result)); - Assert.NotNull(reply.Steps); - Assert.AreEqual(reply.Steps.Count(), 2); - - if (null != queryMock) - queryMock.VerifyAll(); - } - - [Test] - public async Task Funnel_Optional_Success() - { - var client = new KeenClient(SettingsEnv); - var timeframe = QueryRelativeTimeframe.ThisHour(); - - IEnumerable funnelsteps = new [] - { - new FunnelStep - { - EventCollection = FunnelColA, - ActorProperty = "id", - }, - new FunnelStep - { - EventCollection = FunnelColB, - ActorProperty = "id", - Optional = true, - }, - new FunnelStep - { - EventCollection = FunnelColC, - ActorProperty = "id" - }, - }; - - var expected = new FunnelResult - { - Steps = new [] - { - new FunnelResultStep - { - EventCollection = FunnelColA, - }, - new FunnelResultStep - { - EventCollection = FunnelColB, - }, - new FunnelResultStep - { - EventCollection = FunnelColC, - }, - }, - Result = new [] { 3, 2, 1 } - }; - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Funnel( - It.Is>(f => f.Equals(funnelsteps)), - It.Is(t => t == timeframe), - It.Is(t => t == "") - )) - .Returns(Task.FromResult(expected)); - - client.Queries = queryMock.Object; - } - - var reply = (await client.QueryFunnelAsync(funnelsteps, timeframe)); - Assert.NotNull(reply); - Assert.NotNull(reply.Result); - Assert.True(reply.Result.SequenceEqual(expected.Result)); - Assert.NotNull(reply.Steps); - Assert.AreEqual(reply.Steps.Count(), 3); - - if (null != queryMock) - queryMock.VerifyAll(); - } - - [Test] - public async Task Funnel_ValidFilter_Success() - { - var client = new KeenClient(SettingsEnv); - var timeframe = QueryRelativeTimeframe.ThisHour(); - var filters = new List { new QueryFilter("id", QueryFilter.FilterOperator.GreaterThanOrEqual(), 0) }; - - IEnumerable funnelsteps = new [] - { - new FunnelStep - { - EventCollection = FunnelColA, - ActorProperty = "id", - Filters = filters, - }, - new FunnelStep - { - EventCollection = FunnelColB, - ActorProperty = "id" - }, - }; - - var expected = new FunnelResult - { - Steps = new [] - { - new FunnelResultStep - { - EventCollection = FunnelColA, - Filters = filters - }, - new FunnelResultStep - { - EventCollection = FunnelColB, - }, - }, - Result = new [] { 3, 2 } - }; - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Funnel( - It.Is>(f => f.Equals(funnelsteps)), - It.Is(t => t == timeframe), - It.Is(t => t == "") - )) - .Returns(Task.FromResult(expected)); - - client.Queries = queryMock.Object; - } - - var reply = (await client.QueryFunnelAsync(funnelsteps, timeframe)); - Assert.NotNull(reply); - Assert.NotNull(reply.Result); - Assert.True(reply.Result.SequenceEqual(expected.Result)); - Assert.NotNull(reply.Steps); - Assert.AreEqual(reply.Steps.Count(), 2); - - if (null != queryMock) - queryMock.VerifyAll(); - } - - [Test] - public async Task Funnel_ValidTimeframe_Success() - { - var client = new KeenClient(SettingsEnv); - var timeframe = QueryRelativeTimeframe.ThisHour(); - - IEnumerable funnelsteps = new [] - { - new FunnelStep - { - EventCollection = FunnelColA, - ActorProperty = "id", - }, - new FunnelStep - { - EventCollection = FunnelColB, - ActorProperty = "id", - }, - }; - - var expected = new FunnelResult - { - Steps = new [] - { - new FunnelResultStep - { - EventCollection = FunnelColA, - }, - new FunnelResultStep - { - EventCollection = FunnelColB, - }, - }, - Result = new [] { 3, 2 } - }; - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Funnel( - It.Is>(f => f.Equals(funnelsteps)), - It.Is(t => t == timeframe), - It.Is(t => t == "") - )) - .Returns(Task.FromResult(expected)); - - client.Queries = queryMock.Object; - } - - var reply = (await client.QueryFunnelAsync(funnelsteps, timeframe)); - Assert.NotNull(reply); - Assert.NotNull(reply.Result); - Assert.True(reply.Result.SequenceEqual(expected.Result)); - Assert.NotNull(reply.Steps); - Assert.AreEqual(reply.Steps.Count(), 2); - - if (null != queryMock) - queryMock.VerifyAll(); - } - - [Test] - public async Task Funnel_ValidTimeframeInSteps_Success() - { - var client = new KeenClient(SettingsEnv); - - IEnumerable funnelsteps = new [] - { - new FunnelStep - { - EventCollection = FunnelColA, - ActorProperty = "id", - Timeframe = QueryRelativeTimeframe.ThisMonth(), - }, - new FunnelStep - { - EventCollection = FunnelColB, - ActorProperty = "id", - Timeframe = new QueryAbsoluteTimeframe(DateTime.Now.AddDays(-30), DateTime.Now), - }, - }; - - var expected = new FunnelResult - { - Steps = new[] - { - new FunnelResultStep - { - EventCollection = FunnelColA, - }, - new FunnelResultStep - { - EventCollection = FunnelColB, - }, - }, - Result = new[] { 3, 2 } - }; - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Funnel( - It.Is>(f => f.Equals(funnelsteps)), - It.Is(t => null == t), - It.Is(t => t == "") - )) - .Returns(Task.FromResult(expected)); - - client.Queries = queryMock.Object; - } - - var reply = (await client.QueryFunnelAsync(funnelsteps, null)); - Assert.NotNull(reply); - Assert.NotNull(reply.Result); - Assert.True(reply.Result.SequenceEqual(expected.Result)); - Assert.NotNull(reply.Steps); - Assert.AreEqual(reply.Steps.Count(), 2); - - if (null != queryMock) - queryMock.VerifyAll(); - } - - [Test] - public async Task Funnel_WithActors_Success() - { - var client = new KeenClient(SettingsEnv); - var timeframe = QueryRelativeTimeframe.ThisHour(); - - IEnumerable funnelsteps = new [] - { - new FunnelStep - { - EventCollection = FunnelColA, - ActorProperty = "id", - WithActors = true - }, - new FunnelStep - { - EventCollection = FunnelColB, - ActorProperty = "id" - }, - }; - - var expected = new FunnelResult - { - Actors = new []{ new []{"sam", "dean"}, null}, - Steps = new [] - { - new FunnelResultStep - { - EventCollection = FunnelColA, - WithActors = true - }, - new FunnelResultStep - { - EventCollection = FunnelColB, - }, - }, - Result = new [] { 3, 2 } - }; - - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Funnel( - It.Is>(f => f.Equals(funnelsteps)), - It.Is(t => t == timeframe), - It.Is(t => t == "") - )) - .Returns(Task.FromResult(expected)); - - client.Queries = queryMock.Object; - } - - var reply = (await client.QueryFunnelAsync(funnelsteps, timeframe)); - Assert.NotNull(reply); - Assert.NotNull(reply.Actors); - Assert.AreEqual(reply.Actors.Count(), 2); - Assert.NotNull(reply.Result); - Assert.True(reply.Result.SequenceEqual(expected.Result)); - Assert.NotNull(reply.Steps); - Assert.AreEqual(reply.Steps.Count(), 2); - - if (null != queryMock) - queryMock.VerifyAll(); - } - } -} diff --git a/Keen.NET.Test/HttpClientHandlerMock.cs b/Keen.NET.Test/HttpClientHandlerMock.cs deleted file mode 100644 index 06b4f09..0000000 --- a/Keen.NET.Test/HttpClientHandlerMock.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; - - -namespace Keen.Net.Test -{ - /// - /// Wraps an and allows for using it as an HttpClientHandler. - /// If the IHttpMessageHandler doesn't already have a default action set, we'll have it call - /// our own base SendAsync() which will forward the request to the actual HttpClientHandler - /// implementation, with all the configuration and proxies and such, which may actually go out - /// over the network. - /// - internal class HttpClientHandlerMock : HttpClientHandler - { - internal readonly IHttpMessageHandler _handler; - - internal HttpClientHandlerMock(IHttpMessageHandler handler) - { - _handler = handler; - _handler.DefaultAsync = (_handler.DefaultAsync ?? base.SendAsync); - } - - protected override Task SendAsync(HttpRequestMessage request, - CancellationToken cancellationToken) - { - return _handler.SendAsync(request, cancellationToken); - } - } -} diff --git a/Keen.NET.Test/HttpTests.cs b/Keen.NET.Test/HttpTests.cs deleted file mode 100644 index 25f5205..0000000 --- a/Keen.NET.Test/HttpTests.cs +++ /dev/null @@ -1,155 +0,0 @@ -using Keen.Core; -using Keen.Core.Query; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using NUnit.Framework; -using System; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading.Tasks; - - -namespace Keen.Net.Test -{ - [TestFixture] - internal class HttpTests : TestBase - { - [Test] - public void GetSdkVersion_Success() - { - string sdkVersion = KeenUtil.GetSdkVersion(); - - Assert.IsNotNull(sdkVersion); - Assert.IsNotEmpty(sdkVersion); - Assert.IsTrue(sdkVersion.StartsWith(".net")); - } - - [Test] - public async Task DefaultHeaders_Success() - { - object responseData = new[] { new { result = 2 } }; - - var handler = new FuncHandler() - { - PreProcess = (req, ct) => - { - // Make sure the default headers are in place - Assert.IsTrue(req.Headers.Contains("Keen-Sdk")); - Assert.AreEqual(KeenUtil.GetSdkVersion(), req.Headers.GetValues("Keen-Sdk").Single()); - - Assert.IsTrue(req.Headers.Contains("Authorization")); - - var key = req.Headers.GetValues("Authorization").Single(); - Assert.IsTrue(SettingsEnv.ReadKey == key || - SettingsEnv.WriteKey == key || - SettingsEnv.MasterKey == key); - }, - ProduceResultAsync = (req, ct) => - { - return CreateJsonStringResponseAsync(responseData); - }, - DeferToDefault = false - }; - - var client = new KeenClient(SettingsEnv, new TestKeenHttpClientProvider() - { - ProvideKeenHttpClient = - (url) => KeenHttpClientFactory.Create(url, - new HttpClientCache(), - null, - new DelegatingHandlerMock(handler)) - }); - - // Try out all the endpoints - Assert.DoesNotThrow(() => client.GetSchemas()); - - // Remaining operations expect an object, not an array of objects - responseData = new { result = 2 }; - - var @event = new { AProperty = "AValue" }; - Assert.DoesNotThrow(() => client.AddEvent("AddEventTest", @event)); - Assert.DoesNotThrow(() => client.AddEvents("AddEventTest", new[] { @event })); - - Assert.DoesNotThrow(() => client.DeleteCollection("DeleteColTest")); - Assert.IsNotNull(client.GetSchema("AddEventTest")); - - // Currently all the queries/extraction go through the same KeenWebApiRequest() call. - var count = await client.QueryAsync( - QueryType.Count(), - "testCollection", - "", - QueryRelativeTimeframe.ThisMonth()); - - Assert.IsNotNull(count); - Assert.AreEqual("2", count); - } - - internal static Task CreateJsonStringResponseAsync(object data) - { - return HttpTests.CreateJsonStringResponseAsync(data, HttpStatusCode.OK); - } - - internal static Task CreateJsonStringResponseAsync( - object data, - HttpStatusCode statusCode) - { - HttpResponseMessage mockResponse = new HttpResponseMessage(statusCode); - var dataStr = (data is Array ? JArray.FromObject(data).ToString(Formatting.None) : - JObject.FromObject(data).ToString(Formatting.None)); - var content = new StringContent(dataStr); - content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); - mockResponse.Content = content; - - return Task.FromResult(mockResponse); - } - - // For communicating errors in mock responses. - internal static Task CreateJsonStringResponseAsync( - HttpStatusCode statusCode, - string message, - string errorCode) - { - var content = new StringContent(JObject.FromObject( - // Matches what the server returns and KeenUtil.CheckApiErrorCode() expects. - new - { - message = message, - error_code = errorCode - }).ToString(Formatting.None)); - content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); - - HttpResponseMessage mockResponse = new HttpResponseMessage(statusCode) - { - Content = content - }; - - return Task.FromResult(mockResponse); - } - - internal static Uri GetUriForResource(IProjectSettings projectSettings, string resource) - { - string keenUrl = projectSettings.KeenUrl; - string projectId = projectSettings.ProjectId; - - return new Uri($"{keenUrl}projects/{projectId}/{resource}"); - } - - internal static Task ValidateRequest(HttpRequestMessage request, string expectedRequestBody) - { - return HttpTests.ValidateRequest(request, JObject.Parse(expectedRequestBody)); - } - - internal static async Task ValidateRequest(HttpRequestMessage request, JObject expectedRequestJson) - { - // Request should have a body - Assert.NotNull(request.Content); - - string requestBody = await request.Content.ReadAsStringAsync().ConfigureAwait(false); - var requestJson = JObject.Parse(requestBody); - - Assert.IsTrue(JToken.DeepEquals(expectedRequestJson, requestJson)); - } - } -} diff --git a/Keen.NET.Test/IHttpMessageHandler.cs b/Keen.NET.Test/IHttpMessageHandler.cs deleted file mode 100644 index 60e16ca..0000000 --- a/Keen.NET.Test/IHttpMessageHandler.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; - - -namespace Keen.Net.Test -{ - /// - /// Represents the main functionality needed to override both HttpClientHandler and - /// DelegatingHandler. This can be useful for implementing test code in pass-through fakes - /// where we want to alter some behavior, but let the rest execute normally. If the test just - /// tests/mutates and forwards to another handler, it can implement this interface and be used - /// in place of either type of handler in tests. - /// - /// It's a bad idea to reuse instances of this type, since the wrappers as well as the - /// HttpClient and pipeline code mess with their properties. Weirdness ensues, so create a - /// fresh instance every time at the point where you create the wrapper or stick it in the - /// pipeline, and don't store a reference to it unless it's needed to check later in an - /// assert or some verification logic, but generally don't reuse it. - /// - internal interface IHttpMessageHandler - { - Task SendAsync(HttpRequestMessage request, - CancellationToken cancellationToken); - - Func> DefaultAsync { get; set; } - } -} diff --git a/Keen.NET.Test/Keen.NET.Test.csproj b/Keen.NET.Test/Keen.NET.Test.csproj deleted file mode 100644 index 3f05425..0000000 --- a/Keen.NET.Test/Keen.NET.Test.csproj +++ /dev/null @@ -1,165 +0,0 @@ - - - - - Debug - AnyCPU - {644382BF-CF65-4DB6-AFD8-1ACDA2800D31} - Library - Properties - Keen.Net.Test - Keen.Net.Test - v4.5 - 512 - 11ade238 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - bin\netfx\ - TRACE - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - - - - ..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll - True - - - ..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll - True - - - ..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll - True - - - ..\packages\Moq.4.2.1510.2205\lib\net40\Moq.dll - True - - - ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll - True - - - ..\packages\NUnit.3.2.0\lib\net45\nunit.framework.dll - True - - - ..\packages\PCLStorage.1.0.2\lib\net45\PCLStorage.dll - True - - - ..\packages\PCLStorage.1.0.2\lib\net45\PCLStorage.Abstractions.dll - True - - - - - - - ..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll - True - - - ..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll - True - - - - - - - - - - - - Properties\SharedAssemblyInfo.cs - - - Properties\SharedVersionInfo.cs - - - - - - - - - - - - - - - - - - - - - - - - - - Always - - - Always - - - Always - - - - - - - {0cd3383e-5267-48bc-99cd-1b3ae8aff7ba} - Keen.Net - - - {36d156bf-8523-42ec-9ad6-7c3ac05d699f} - Keen - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - \ No newline at end of file diff --git a/Keen.NET.Test/KeenClientTest.cs b/Keen.NET.Test/KeenClientTest.cs deleted file mode 100644 index 794d057..0000000 --- a/Keen.NET.Test/KeenClientTest.cs +++ /dev/null @@ -1,982 +0,0 @@ -using Keen.Core; -using Keen.Core.EventCache; -using Moq; -using Newtonsoft.Json.Linq; -using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.Dynamic; -using System.Linq; -using System.Threading.Tasks; - - -namespace Keen.Net.Test -{ - public class TestBase - { - public static bool UseMocks = true; - public IProjectSettings SettingsEnv; - - [OneTimeSetUp] - public virtual void Setup() - { - if (UseMocks) - SetupEnv(); - SettingsEnv = new ProjectSettingsProviderEnv(); - } - - [OneTimeTearDown] - public virtual void TearDown() - { - if (UseMocks) - ResetEnv(); - } - - public static void SetupEnv() - { - foreach (var s in new[] { "KEEN_PROJECT_ID", "KEEN_MASTER_KEY", "KEEN_WRITE_KEY", "KEEN_READ_KEY" }) - Environment.SetEnvironmentVariable(s, "0123456789ABCDEF0123456789ABCDEF"); - } - - public static void ResetEnv() - { - foreach (var s in new[] { "KEEN_PROJECT_ID", "KEEN_MASTER_KEY", "KEEN_WRITE_KEY", "KEEN_READ_KEY" }) - Environment.SetEnvironmentVariable(s, null); - } - } - - [TestFixture] - public class BulkEventTest : TestBase - { - [Test] - public void AddEvents_InvalidProject_Throws() - { - var settings = new ProjectSettingsProvider(projectId: "X", writeKey: SettingsEnv.WriteKey); - var client = new KeenClient(settings); - if (UseMocks) - client.Event = new EventMock(settings, - addEvents: new Func>((e, p) => - { - if ((p == settings) - &&(p.ProjectId=="X")) - throw new KeenException(); - else - throw new Exception("Unexpected value"); - })); - Assert.Throws(() => client.AddEvents("AddEventTest", new []{ new {AProperty = "AValue" }})); - } - - - [Test] - public void AddEvents_PartialFailure_Throws() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.Event = new EventMock(SettingsEnv, - addEvents: new Func>((e, p) => - { - var err = e.SelectToken("$.AddEventTest[2]") as JObject; - if (null == err) - throw new Exception("Unexpected error, test data not found"); - - return new List(){new CachedEvent("AddEventTest", e)}; - })); - - object invalidEvent = new ExpandoObject(); - ((IDictionary)invalidEvent).Add("$" + new string('.', 260), "AValue"); - - var events = (from i in Enumerable.Range(1, 2) - select new { AProperty = "AValue" + i }).ToList(); - events.Add(invalidEvent); - - Assert.Throws(() => client.AddEvents("AddEventTest", events)); - } - - [Test] - public void AddEvents_NoCache_Success() - { - var client = new KeenClient(SettingsEnv); - var done = false; - if (UseMocks) - client.Event = new EventMock(SettingsEnv, - addEvents: new Func>((e, p) => - { - done = true; - Assert.True(p == SettingsEnv, "Incorrect settings"); - Assert.NotNull(e.Property("AddEventTest"), "Expected collection not found"); - Assert.AreEqual(e.Property("AddEventTest").Value.AsEnumerable().Count(), 3, "Expected exactly 3 collection members"); - foreach (dynamic q in ((dynamic)e).AddEventTest) - Assert.NotNull(q.keen.timestamp, "keen.timestamp properties should exist"); - return new List(); - })); - - var events = from i in Enumerable.Range(1,3) - select new { AProperty = "AValue" + i}; - - Assert.DoesNotThrow(() => client.AddEvents("AddEventTest", events)); - Assert.True((UseMocks && done) || !UseMocks, "AddEvent mock was not called"); - } - - [Test] - public void AddEvents_Cache_Success() - { - var client = new KeenClient(SettingsEnv, new EventCacheMemory()); - if (UseMocks) - client.Event = new EventMock(SettingsEnv, - addEvents: new Func>((e, p) => - { - // Should not be called with caching enabled - Assert.Fail(); - return new List(); - })); - - var events = from i in Enumerable.Range(1, 3) - select new { AProperty = "AValue" + i }; - - Assert.DoesNotThrow(() => client.AddEvents("AddEventTest", events)); - - // reset the AddEvents mock - if (UseMocks) - client.Event = new EventMock(SettingsEnv, - addEvents: new Func>((e, p) => - { - Assert.True(p == SettingsEnv, "Incorrect settings"); - Assert.NotNull(e.Property("AddEventTest"), "Expected collection not found"); - Assert.AreEqual(e.Property("AddEventTest").Value.AsEnumerable().Count(), 3, "Expected exactly 3 collection members"); - foreach (dynamic q in ((dynamic)e).AddEventTest) - Assert.NotNull(q.keen.timestamp, "keen.timestamp properties should exist"); - return new List(); - })); - Assert.DoesNotThrow(() => client.SendCachedEvents()); - } - } - - [TestFixture] - public class KeenClientTest : TestBase - { - [Test] - public void Constructor_ProjectSettingsNull_Throws() - { - Assert.Throws(() => new KeenClient(null)); - } - - [Test] - public void Constructor_ProjectSettingsNoProjectID_Throws() - { - var settings = new ProjectSettingsProvider(projectId: "", masterKey: "X", writeKey: "X"); - Assert.Throws(() => new KeenClient(settings)); - } - - [Test] - public void Constructor_ProjectSettingsNoMasterOrWriteKeys_Throws() - { - var settings = new ProjectSettingsProvider(projectId: "X"); - Assert.Throws(() => new KeenClient(settings)); - } - - [Test] - public void GetCollectionSchema_NullProjectId_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.Throws(() => client.GetSchema(null)); - } - - [Test] - public void GetCollectionSchema_EmptyProjectId_Throws() - { - var settings = new ProjectSettingsProvider(projectId: "X", masterKey: SettingsEnv.MasterKey); - var client = new KeenClient(settings); - Assert.Throws(() => client.GetSchema("")); - } - - [Test] - public void GetCollectionSchemas_Success() - { - var client = new KeenClient(SettingsEnv); - - var expected = new JArray(); // todo: better mock return - if (UseMocks) - { - var eventMock = new Mock(); - eventMock.Setup(m => m.GetSchemas()) - .Returns(Task.FromResult(expected)); - - client.Event = eventMock.Object; - } - - var reply = client.GetSchemas(); - Assert.NotNull(reply); - } - - - [Test] - public void GetCollectionSchema_InvalidProjectId_Throws() - { - var settings = new ProjectSettingsProvider(projectId: "X", - readKey: SettingsEnv.ReadKey); - var client = new KeenClient(settings); - if (UseMocks) - client.EventCollection = new EventCollectionMock(settings, - getSchema: new Func((c, p) => - { - if ((p == settings) && (c == "X")) - throw new KeenResourceNotFoundException(); - else - throw new Exception("Unexpected value"); - })); - Assert.Throws(() => client.GetSchema("X")); - } - - [Test] - public void GetCollectionSchema_ValidProjectIdInvalidSchema_Throws() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - getSchema: new Func((c, p) => - { - if ((p == SettingsEnv) && (c == "DoesntExist")) - throw new KeenResourceNotFoundException(); - return new JObject(); - })); - - Assert.Throws(() => client.GetSchema("DoesntExist")); - } - - [Test] - public void GetCollectionSchema_ValidProject_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - getSchema: new Func((c, p) => - { - if ((p == SettingsEnv) && (c == "AddEventTest")) - return JObject.Parse("{\"properties\":{\"AProperty\": \"string\"}}"); - else - throw new KeenResourceNotFoundException(c); - })); - - Assert.DoesNotThrow(() => - { - dynamic response = client.GetSchema("AddEventTest"); - Assert.NotNull(response["properties"]); - Assert.NotNull(response["properties"]["AProperty"]); - Assert.True((string)response["properties"]["AProperty"] == "string"); - }); - } - - [Test] - public void AddEvent_InvalidProjectId_Throws() - { - var settings = new ProjectSettingsProvider(projectId: "X", writeKey: SettingsEnv.WriteKey); - var client = new KeenClient(settings); - if (UseMocks) - client.EventCollection = new EventCollectionMock(settings, - addEvent: new Action((c, e, p) => - { - if ((p == settings) && (c == "X") && (e["X"].Value() == "X")) - throw new KeenResourceNotFoundException(c); - })); - - Assert.Throws(() => client.AddEvent("X", new { X = "X" })); - } - - [Test] - public void AddEvent_ValidProjectIdInvalidWriteKey_Throws() - { - var settings = new ProjectSettingsProvider(projectId: SettingsEnv.ProjectId, writeKey: "X"); - var client = new KeenClient(settings); - if (UseMocks) - client.EventCollection = new EventCollectionMock(settings, - addEvent: new Action((c, e, p) => - { - if ((p == settings) && (c == "X") && (e["X"].Value() == "X")) - throw new KeenInvalidApiKeyException(c); - })); - Assert.Throws(() => client.AddEvent("X", new { X = "X" })); - } - - [Test] - public void AddEvent_InvalidCollectionNameBlank_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.Throws(() => client.AddEvent("", new { AProperty = "AValue" })); - } - - [Test] - public void AddEvent_InvalidCollectionNameNull_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.Throws(() => client.AddEvent(null, new { AProperty = "AValue" })); - } - - [Test] - public void AddEvent_InvalidCollectionNameDollarSign_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.Throws(() => client.AddEvent("$Invalid", new { AProperty = "AValue" })); - } - - [Test] - public void AddEvent_InvalidCollectionNameTooLong_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.Throws(() => client.AddEvent(new String('X', 257), new { AProperty = "AValue" })); - } - - [Test] - public void AddEvent_InvalidKeenNamespaceProperty_Throws() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: new Action((c,e,p) => - { - if ((p == SettingsEnv) - && (c == "X") - && (null != e.Property("keen")) - && (e.Property("keen").Value.GetType()==typeof(JObject)) - && (null!=((JObject)e.Property("keen").Value).Property("AProperty"))) - throw new KeenInvalidKeenNamespacePropertyException(); - else - throw new Exception("Unexpected value"); - })); - - Assert.Throws(() => client.AddEvent("X", new { keen = new { AProperty = "AValue" } })); - } - - [Test] - public void AddEvent_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: new Action((c, e, p) => - { - if ((p == SettingsEnv) && (c == "AddEventTest") && (e["AProperty"].Value()=="AValue")) - return; - else - throw new Exception("Unexpected values"); - })); - Assert.DoesNotThrow(() => client.AddEvent("AddEventTest", new { AProperty = "AValue" })); - } - - [Test] - public void AddEvent_ScopedKeyWrite_Success() - { - var scope = "{\"timestamp\": \"2014-02-25T22:09:27.320082\", \"allowed_operations\": [\"write\"]}"; - var scopedKey = ScopedKey.EncryptString(SettingsEnv.MasterKey, scope); - var settings = new ProjectSettingsProvider(masterKey: SettingsEnv.MasterKey, projectId: SettingsEnv.ProjectId, writeKey: scopedKey); - - var client = new KeenClient(settings); - - if (UseMocks) - client.EventCollection = new EventCollectionMock(settings, - addEvent: new Action((c, e, p) => - { - var key = JObject.Parse(ScopedKey.Decrypt(p.MasterKey, p.WriteKey)); - - if ((key["allowed_operations"].Values().First() == "write") && (p == settings) && (c == "AddEventTest") && (e["AProperty"].Value() == "CustomKey")) - return; - else - throw new Exception("Unexpected value"); - })); - - Assert.DoesNotThrow(() => client.AddEvent("AddEventTest", new { AProperty = "CustomKey" })); - } - - //[Test] - //public void AddEvent_MultipleEventsInvalidCollection_Throws() - //{ - // var settings = new ProjectSettingsProviderEnv(); - // var client = new KeenClient(settings); - // var collection = new - // { - // AddEventTest = from i in Enumerable.Range(1, 10) - // select new { AProperty = "AValue" + i }, - // InvalidCollection = 2, - // }; - // Assert.Throws(() => client.AddEvents(collection)); - //} - - //[Test] - //public void AddEvent_MultipleEventsInvalidItem_Throws() - //{ - // var settings = new ProjectSettingsProviderEnv(); - // var client = new KeenClient(settings); - // var collection = new { AddEventTest = new List() }; - - // foreach( var k in new[]{ "ValidProperty", "Invalid.Property" }) - // { - // IDictionary item = new ExpandoObject(); - // item.Add(k, "AValue"); - // collection.AddEventTest.Add(item); - // } - - // Assert.DoesNotThrow(() => client.AddEvents(collection)); - //} - - //[Test] - //public void AddEvent_MultipleEventsAnonymous_Success() - //{ - // var settings = new ProjectSettingsProviderEnv(); - // var client = new KeenClient(settings); - // var collection = new - // { - // AddEventTest = from i in Enumerable.Range(1, 10) - // select new { AProperty = "AValue" + i } - // }; - // Assert.DoesNotThrow(() => client.AddEvents(collection)); - //} - - //[Test] - //public void AddEvent_MultipleEventsExpando_Success() - //{ - // var settings = new ProjectSettingsProviderEnv(); - // var client = new KeenClient(settings); - - // dynamic collection = new ExpandoObject(); - // collection.AddEventTest = new List(); - // foreach( var i in Enumerable.Range(1,10)) - // { - // dynamic anEvent = new ExpandoObject(); - // anEvent.AProperty = "AValue" + i; - // collection.AddEventTest.Add(anEvent); - // } - - // Assert.DoesNotThrow(() => client.AddEvents(collection)); - //} - - //private class TestCollection - //{ - // public class TestEvent - // { - // public string AProperty { get; set; } - // } - // public List AddEventTest { get; set; } - //} - - //[Test] - //public void AddEvent_MultipleEvents_Success() - //{ - // var settings = new ProjectSettingsProviderEnv(); - // var client = new KeenClient(settings); - - // var collection = new TestCollection() - // { - // AddEventTest = (from i in Enumerable.Range(1, 10) - // select new TestCollection.TestEvent() { AProperty = "AValue"+i}).ToList() - // }; - - // Assert.DoesNotThrow(() => client.AddEvents(collection)); - //} - - [Test] - public void DeleteCollection_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - deleteCollection: new Action((c, p) => - { - if ((p == SettingsEnv) && (c == "DeleteColTest")) - return; - else - throw new Exception("Unexpected value"); - })); - - // Idempotent, does not matter if collection does not exist. - Assert.DoesNotThrow(() => client.DeleteCollection("DeleteColTest")); - } - } - - [TestFixture] - public class KeenClientGlobalPropertyTests : TestBase - { - [Test] - public void AddGlobalProperty_SimpleValue_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: new Action((c, e, p) => - { - if ((p == SettingsEnv) && (c == "AddEventTest") - && (e["AProperty"].Value() == "AValue") - && (e["AGlobal"].Value() == "AValue")) - return; - else - throw new Exception("Unexpected value"); - })); - - Assert.DoesNotThrow(() => - { - client.AddGlobalProperty("AGlobal", "AValue"); - client.AddEvent("AddEventTest", new { AProperty = "AValue" }); - }); - } - - [Test] - public void AddGlobalProperty_InvalidValueNameDollar_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.Throws(() => client.AddGlobalProperty("$AGlobal", "AValue")); - } - - [Test] - public void AddGlobalProperty_InvalidValueNamePeriod_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.Throws(() => client.AddGlobalProperty("A.Global", "AValue")); - } - - [Test] - public void AddGlobalProperty_InvalidValueNameLength_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.Throws(() => client.AddGlobalProperty(new String('A', 256), "AValue")); - } - - [Test] - public void AddGlobalProperty_InvalidValueNameNull_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.Throws(() => client.AddGlobalProperty(null, "AValue")); - } - - - [Test] - public void AddGlobalProperty_InvalidValueNameBlank_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.Throws(() => client.AddGlobalProperty("", "AValue")); - } - - [Test] - public void AddGlobalProperty_ObjectValue_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: new Action((c, e, p) => - { - if ((p == SettingsEnv) && (c == "AddEventTest") - && (e["AProperty"].Value() == "AValue") - && (e["AGlobal"]["AProperty"].Value() == "AValue")) - return; - else - throw new Exception("Unexpected value"); - })); - - Assert.DoesNotThrow(() => - { - client.AddGlobalProperty("AGlobal", new { AProperty = "AValue" }); - client.AddEvent("AddEventTest", new { AProperty = "AValue" }); - }); - } - - [Test] - public void AddGlobalProperty_CollectionValue_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: new Action((c, e, p) => - { - if ((p == SettingsEnv) && (c == "AddEventTest") - && (e["AProperty"].Value() == "AValue") - && (e["AGlobal"].Values().All((x) => (x == 1) || (x == 2) || (x == 3)))) - return; - else - throw new Exception("Unexpected value"); - })); - - Assert.DoesNotThrow(() => - { - client.AddGlobalProperty("AGlobal", new[] { 1, 2, 3, }); - client.AddEvent("AddEventTest", new { AProperty = "AValue" }); - }); - } - - [Test] - public void AddGlobalProperty_DelegateSimpleValue_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: new Action((c, e, p) => - { - if ((p == SettingsEnv) && (c == "AddEventTest") - && (e["AProperty"].Value() == "AValue") - && (e["AGlobal"]!=null)) - return; - else - throw new Exception("Unexpected value"); - })); - - Assert.DoesNotThrow(() => - { - client.AddGlobalProperty("AGlobal", new DynamicPropertyValue(() => DateTime.Now.Millisecond)); - client.AddEvent("AddEventTest", new { AProperty = "AValue" }); - client.AddEvent("AddEventTest", new { AProperty = "AValue" }); - }); - } - - [Test] - public void AddGlobalProperty_DelegateArrayValue_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: new Action((c, e, p) => - { - if ((p == SettingsEnv) && (c == "AddEventTest") - && (e["AProperty"].Value() == "AValue") - && (e["AGlobal"].Values().All((x) => (x == 1) || (x == 2) || (x == 3)))) - return; - else - throw new Exception("Unexpected value"); - })); - - Assert.DoesNotThrow(() => - { - client.AddGlobalProperty("AGlobal", new DynamicPropertyValue(() => new[] { 1, 2, 3 })); - client.AddEvent("AddEventTest", new { AProperty = "AValue" }); - }); - } - - [Test] - public void AddGlobalProperty_DelegateObjectValue_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: new Action((c, e, p) => - { - if ((p == SettingsEnv) && (c == "AddEventTest") - && (e["AProperty"].Value() == "AValue") - && (e["AGlobal"].Value()["SubProp1"].Value() == "Value")) - return; - else - throw new Exception("Unexpected value"); - })); - - Assert.DoesNotThrow(() => - { - client.AddGlobalProperty("AGlobal", new DynamicPropertyValue(() => new { SubProp1 = "Value", SubProp2 = "Value" })); - client.AddEvent("AddEventTest", new { AProperty = "AValue" }); - }); - } - - [Test] - public void AddGlobalProperty_DelegateNullDynamicValue_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.Throws(() => { client.AddGlobalProperty("AGlobal", new DynamicPropertyValue(() => null)); }); - } - - [Test] - public void AddGlobalProperty_DelegateNullValueProvider_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.Throws(() => { client.AddGlobalProperty("AGlobal", null); }); - } - - [Test] - public void AddGlobalProperty_DelegateValueProviderNullReturn_Throws() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: new Action((c, e, p) => - { - if ((p == SettingsEnv) && (c == "AddEventTest") - && (e["AProperty"].Value() == "AValue") - && (e["AGlobal"].Value() == "value")) - return; - else - throw new Exception("Unexpected value"); - })); - - var i = 0; - // return valid for the first two tests, then null - client.AddGlobalProperty("AGlobal", new DynamicPropertyValue(() => i++ > 1 ? null : "value")); - // This is the second test (AddGlobalProperty runs the first) - Assert.DoesNotThrow(() => client.AddEvent("AddEventTest", new { AProperty = "AValue" })); - // Third test should fail. - Assert.Throws(() => { client.AddEvent("AddEventTest", new { AProperty = "AValue" }); }); - } - - [Test] - public void AddGlobalProperty_DelegateValueProviderThrows_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.Throws(() => client.AddGlobalProperty("AGlobal", new DynamicPropertyValue(() => { throw new Exception("test exception"); }))); - } - } - - [TestFixture] - public class KeenClientCachingTest : TestBase - { - [Test] - public void Caching_SendEmptyEvents_Success() - { - var client = new KeenClient(SettingsEnv, new EventCacheMemory()); - Assert.DoesNotThrow(() => client.SendCachedEvents()); - } - - [Test] - public void Caching_ClearEvents_Success() - { - var client = new KeenClient(SettingsEnv, new EventCacheMemory()); - Assert.DoesNotThrow(() => client.EventCache.Clear()); - } - - [Test] - public void Caching_AddEvents_Success() - { - var client = new KeenClient(SettingsEnv, new EventCacheMemory()); - - Assert.DoesNotThrow(() => client.AddEvent("CachedEventTest", new { AProperty = "AValue" })); - Assert.DoesNotThrow(() => client.AddEvent("CachedEventTest", new { AProperty = "AValue" })); - Assert.DoesNotThrow(() => client.AddEvent("CachedEventTest", new { AProperty = "AValue" })); - } - - [Test] - public async Task Caching_SendEvents_Success() - { - var client = new KeenClient(SettingsEnv, new EventCacheMemory()); - if (UseMocks) - client.Event = new EventMock(SettingsEnv, - addEvents: new Func>((e, p) => - { - if ((p == SettingsEnv) - && (null!=e.Property("CachedEventTest")) - && (e.Property("CachedEventTest").Value.Children().Count()==3)) - return new List(); - else - throw new Exception("Unexpected value"); - })); - - client.AddEvent("CachedEventTest", new { AProperty = "AValue" }); - client.AddEvent("CachedEventTest", new { AProperty = "AValue" }); - client.AddEvent("CachedEventTest", new { AProperty = "AValue" }); - - Assert.DoesNotThrow(() => client.SendCachedEvents()); - Assert.Null(await client.EventCache.TryTake(), "Cache is empty"); - } - - [Test] - public async Task Caching_SendManyEvents_Success() - { - var client = new KeenClient(SettingsEnv, new EventCacheMemory()); - var total = 0; - if (UseMocks) - client.Event = new EventMock(SettingsEnv, - addEvents: new Func>((e, p) => - { - if ((p == SettingsEnv) - && (null != e.Property("CachedEventTest")) - && ((e.Property("CachedEventTest").Value.Children().Count() == KeenConstants.BulkBatchSize))) - { - total += e.Property("CachedEventTest").Value.Children().Count(); - return new List(); - } - else - throw new Exception("Unexpected value"); - })); - - for (int i = 0; i < KeenConstants.BulkBatchSize; i++) - client.AddEvent("CachedEventTest", new { AProperty = "AValue" }); - - Assert.DoesNotThrow(() => client.SendCachedEvents()); - Assert.Null(await client.EventCache.TryTake(), "Cache is empty"); - Assert.True( !UseMocks || ( total == KeenConstants.BulkBatchSize)); - } - - [Test] - public void Caching_SendInvalidEvents_Throws() - { - var client = new KeenClient(SettingsEnv, new EventCacheMemory()); - if (UseMocks) - client.Event = new EventMock(SettingsEnv, - addEvents: new Func>((e, p) => - { - if (p == SettingsEnv) - throw new KeenBulkException("Mock exception", - new List(){new CachedEvent("CachedEventTest", e, new Exception())}); - else - throw new Exception("Unexpected value"); - })); - - var anEvent = new JObject(); - anEvent.Add("AProperty", "AValue"); - client.AddEvent("CachedEventTest", anEvent); - - anEvent.Add("keen", JObject.FromObject(new {invalidPropName = "value"} )); - client.AddEvent("CachedEventTest", anEvent); - Assert.DoesNotThrow(() => - { - try - { - client.SendCachedEvents(); - } - catch (KeenBulkException ex) - { - if (ex.FailedEvents.Count() != 1) - throw new Exception("Expected 1 failed event."); - } - }); - } - } - - [TestFixture] - public class AsyncTests : TestBase - { - [Test] - public async Task Async_DeleteCollection_Success() - { - var client = new KeenClient(SettingsEnv); - - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - deleteCollection: new Action((c, p) => - { - if ((p == SettingsEnv) && (c == "DeleteColTest")) - return; - else - throw new Exception("Unexpected value"); - })); - - await client.DeleteCollectionAsync("DeleteColTest"); - } - - [Test] - public void Async_GetCollectionSchema_NullProjectId_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.ThrowsAsync( () => client.GetSchemaAsync(null)); - } - - [Test] - public void Async_AddEvent_InvalidProjectId_Throws() - { - var settings = new ProjectSettingsProvider(projectId: "X", writeKey: SettingsEnv.WriteKey); - var client = new KeenClient(settings); - if (UseMocks) - client.EventCollection = new EventCollectionMock(settings, - addEvent: new Action((c, e, p) => - { - if ((p == settings) && (c == "AddEventTest") && (e["AProperty"].Value() == "Value")) - throw new KeenResourceNotFoundException(c); - })); - - Assert.ThrowsAsync( () => client.AddEventAsync("AddEventTest", new { AProperty = "Value" })); - } - - [Test] - public void Async_AddEvent_ValidProjectIdInvalidWriteKey_Throws() - { - var settings = new ProjectSettingsProvider(projectId: SettingsEnv.ProjectId, writeKey: "X"); - var client = new KeenClient(settings); - if (UseMocks) - client.EventCollection = new EventCollectionMock(settings, - addEvent: new Action((c, e, p) => - { - if ((p == settings) && (c == "AddEventTest") && (e["AProperty"].Value() == "Value")) - throw new KeenInvalidApiKeyException(c); - })); - - Assert.ThrowsAsync(() => client.AddEventAsync("AddEventTest", new { AProperty = "Value" })); - } - - [Test] - public async Task Async_AddEvent_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: new Action((c, e, p) => - { - if ((p == SettingsEnv) && (c == "AddEventTest") && (e["AProperty"].Value() == "Value")) - throw new KeenResourceNotFoundException(c); - })); - - await client.AddEventAsync("AddEventTest", new { AProperty = "AValue" }); - } - - [Test] - public void Async_AddEvents_NullCollection_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.ThrowsAsync( () => client.AddEventsAsync("AddEventTest", null)); - } - - [Test] - public async Task Async_AddEvents_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.Event = new EventMock(SettingsEnv, - addEvents: new Func>((e, p) => - { - Assert.AreEqual(p, SettingsEnv, "Unexpected settings object"); - Assert.AreEqual(e.Property("AddEventTest").Value.AsEnumerable().Count(), 3, "Expected exactly 3 collection members"); - return new List(); - })); - - var events = from e in Enumerable.Range(1, 3) - select new { AProperty = "Value" + e }; - - await client.AddEventsAsync("AddEventTest", events); - } - - [Test] - public async Task Async_Caching_SendEvents_Success() - { - var client = new KeenClient(SettingsEnv, new EventCacheMemory()); - if (UseMocks) - client.Event = new EventMock(SettingsEnv, - addEvents: new Func>((e, p) => - { - Assert.AreEqual(p, SettingsEnv, "Unexpected settings object"); - Assert.AreEqual(e.Property("CachedEventTest").Value.AsEnumerable().Count(), 3, "Expected exactly 3 collection members"); - return new List(); - })); - - client.AddEvent("CachedEventTest", new { AProperty = "AValue" }); - client.AddEvent("CachedEventTest", new { AProperty = "AValue" }); - client.AddEvent("CachedEventTest", new { AProperty = "AValue" }); - - await client.SendCachedEventsAsync(); - Assert.Null(await client.EventCache.TryTake(), "Cache should be empty"); - } - - [Test] - public async Task Async_Caching_SendInvalidEvents_Throws() - { - var client = new KeenClient(SettingsEnv, new EventCacheMemory()); - if (UseMocks) - client.Event = new EventMock(SettingsEnv, - addEvents: new Func>((e, p) => - { - var err = e.SelectToken("$.AddEventTest[2]") as JObject; - if (null == err) - throw new Exception("Unexpected error, test data not found"); - - return new List() { new CachedEvent("AddEventTest", e) }; - })); - - object invalidEvent = new ExpandoObject(); - ((IDictionary)invalidEvent).Add("$" + new string('.', 260), "AValue"); - - var events = (from i in Enumerable.Range(1, 2) - select new { AProperty = "AValue" + i }).ToList(); - events.Add(invalidEvent); - - await client.AddEventsAsync("AddEventTest", events); - Assert.ThrowsAsync( () => client.SendCachedEventsAsync()); - } - } -} diff --git a/Keen.NET.Test/ProjectSettingsProviderTest.cs b/Keen.NET.Test/ProjectSettingsProviderTest.cs deleted file mode 100644 index 338e377..0000000 --- a/Keen.NET.Test/ProjectSettingsProviderTest.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using NUnit.Framework; - -using Keen.Core; -using System.IO; - -namespace Keen.Net.Test -{ - [TestFixture] - public class ProjectSettingsProviderTest - { - [Test] - public void Settings_DefaultInputs_Success() - { - Assert.DoesNotThrow(() => new ProjectSettingsProvider("X", null)); - } - - [Test] - public void Settings_AllNull_Success() - { - Assert.DoesNotThrow(() => new ProjectSettingsProvider(null)); - } - - [Test] - public void SettingsProviderEnv_VarsNotSet_Throws() - { - Environment.SetEnvironmentVariable(KeenConstants.KeenProjectId, null); - Environment.SetEnvironmentVariable(KeenConstants.KeenMasterKey, null); - Environment.SetEnvironmentVariable(KeenConstants.KeenWriteKey, null); - Environment.SetEnvironmentVariable(KeenConstants.KeenReadKey, null); - - var settings = new ProjectSettingsProviderEnv(); - Assert.Throws(() => new KeenClient(settings)); - } - - [Test] - public void SettingsProviderEnv_VarsSet_Success() - { - Environment.SetEnvironmentVariable(KeenConstants.KeenProjectId, "X"); - Environment.SetEnvironmentVariable(KeenConstants.KeenMasterKey, "X"); - Environment.SetEnvironmentVariable(KeenConstants.KeenWriteKey, "X"); - Environment.SetEnvironmentVariable(KeenConstants.KeenReadKey, "X"); - - var settings = new ProjectSettingsProviderEnv(); - Assert.DoesNotThrow(() => new KeenClient(settings)); - } - - [Test] - public void SettingsProviderEnv_VarsSet_SettingsAreCorrect() - { - var projectId = "projectId"; - var masterKey = "masterKey"; - var writeKey = "writeKey"; - var readKey = "readKey"; - - Environment.SetEnvironmentVariable(KeenConstants.KeenProjectId, projectId); - Environment.SetEnvironmentVariable(KeenConstants.KeenMasterKey, masterKey); - Environment.SetEnvironmentVariable(KeenConstants.KeenWriteKey, writeKey); - Environment.SetEnvironmentVariable(KeenConstants.KeenReadKey, readKey); - - var settings = new ProjectSettingsProviderEnv(); - Assert.AreEqual(settings.ProjectId, projectId, "Project id wasn't properly set"); - Assert.AreEqual(settings.MasterKey, masterKey, "Master key wasn't properly set"); - Assert.AreEqual(settings.WriteKey, writeKey, "Write key wasn't properly set"); - Assert.AreEqual(settings.ReadKey, readKey, "Read key wasn't properly set"); - } - - [Test] - public void SettingsProviderFile_InvalidFile_Throws() - { - var fp = Path.GetTempFileName(); - try - { - File.WriteAllText(fp, "X\nX"); - - Assert.Throws(() => new ProjectSettingsProviderFile(fp)); - } - finally - { - File.Delete(fp); - } - } - - [Test] - public void SettingsProviderFile_ValidFile_Success() - { - var fp = Path.GetTempFileName(); - try - { - File.WriteAllText(fp, "X\nX\nX\nX"); - - Assert.DoesNotThrow(() => new ProjectSettingsProviderFile(fp)); - } - finally - { - File.Delete(fp); - } - } - - } -} diff --git a/Keen.NET.Test/Properties/AssemblyInfo.cs b/Keen.NET.Test/Properties/AssemblyInfo.cs deleted file mode 100644 index 18af25a..0000000 --- a/Keen.NET.Test/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - - -// Friendly name and description for this assembly. -[assembly: AssemblyTitle("Keen.Net.Test")] -[assembly: AssemblyDescription("Keen IO SDK Tests for .NET 4/4.5+")] - -// 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("1789297c-6b46-4aee-8c8e-b7c5330b6470")] - -// See also: SharedAssemblyInfo.cs and SharedVersionInfo.cs diff --git a/Keen.NET.Test/QueryTest.cs b/Keen.NET.Test/QueryTest.cs deleted file mode 100644 index 9b555f8..0000000 --- a/Keen.NET.Test/QueryTest.cs +++ /dev/null @@ -1,1187 +0,0 @@ -using Keen.Core; -using Keen.Core.Query; -using Moq; -using Newtonsoft.Json.Linq; -using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Dynamic; -using System.Linq; -using System.Threading.Tasks; - - -namespace Keen.Net.Test -{ - [TestFixture] - public class QueryTest : TestBase - { - const string testCol = "QueryTestCol"; - - public QueryTest() - { - UseMocks = true; - } - - [OneTimeSetUp] - public override void Setup() - { - base.Setup(); - - // If not using mocks, set up conditions on the server - if (!UseMocks) - { - var client = new KeenClient(SettingsEnv); - //client.DeleteCollection(testCol); - client.AddEvent(testCol, new { field1 = "99999999" }); - } - } - - [Test] - public void ReadKeyOnly_Success() - { - var settings = new ProjectSettingsProvider(SettingsEnv.ProjectId, readKey: SettingsEnv.ReadKey); - var client = new KeenClient(settings); - - if (!UseMocks) - { - // Server is required for this test - // Also, test depends on existance of collection "AddEventTest" - Assert.DoesNotThrow(() => client.Query(QueryType.Count(), "AddEventTest", "", QueryRelativeTimeframe.ThisHour())); - } - } - - [Test] - public async Task AvailableQueries_Success() - { - var client = new KeenClient(SettingsEnv); - - Mock queryMock = null; - if (UseMocks) - { - // A few values that should be present and are unlikely to change - IEnumerable> testResult = new List>() - { - new KeyValuePair("minimum", "url" ), - new KeyValuePair("average", "url" ), - new KeyValuePair("maximum", "url" ), - new KeyValuePair("count_url", "url" ), - }; - - queryMock = new Mock(); - queryMock.Setup(m => m.AvailableQueries()) - .Returns(Task.FromResult(testResult)); - - client.Queries = queryMock.Object; - } - - var response = await client.GetQueries(); - Assert.True(response.Any(p => p.Key == "minimum")); - Assert.True(response.Any(p => p.Key == "average")); - Assert.True(response.Any(p => p.Key == "maximum")); - Assert.True(response.Any(p => p.Key == "count_url")); - if (null != queryMock) - queryMock.Verify(m => m.AvailableQueries()); - } - - [Test] - public void Query_InvalidCollection_Throws() - { - var client = new KeenClient(SettingsEnv); - var timeframe = QueryRelativeTimeframe.PreviousHour(); - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Metric( - It.Is(q => q == QueryType.Count()), - It.Is(c => c == null), - It.Is(p => p == ""), - It.Is(t => t == timeframe), - It.Is>(f => f == null), - It.Is(z => z == "") - )) - .Throws(new ArgumentNullException()); - - client.Queries = queryMock.Object; - } - - Assert.ThrowsAsync(() => client.QueryAsync(QueryType.Count(), null, "", timeframe, null)); - } - - [Test] - public async Task Query_ValidAbsolute_Success() - { - var client = new KeenClient(SettingsEnv); - var timeframe = new QueryAbsoluteTimeframe(DateTime.Now.AddDays(-1), DateTime.Now); - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Metric( - It.Is(q => q == QueryType.Count()), - It.Is(c => c == testCol), - It.Is(p => p == ""), - It.Is(t => t == timeframe), - It.Is>(f => f == null), - It.Is(z => z == ""))) - .Returns(Task.FromResult("0")); - - client.Queries = queryMock.Object; - } - - var count = await client.QueryAsync(QueryType.Count(), testCol, "", timeframe, null); - Assert.IsNotNull(count, "expected valid count"); - - if (null != queryMock) - queryMock.VerifyAll(); - } - - [Test] - public async Task Query_ValidRelativeGroup_Success() - { - var client = new KeenClient(SettingsEnv); - var timeframe = QueryRelativeTimeframe.PreviousNDays(2); - var groupby = "field1"; - IEnumerable> reply = new List>() - { - new QueryGroupValue( "0", "field1" ), - new QueryGroupValue( "0", "field1" ), - }; - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Metric( - It.Is(q => q == QueryType.Count()), - It.Is(c => c == testCol), - It.Is(p => p == ""), - It.Is(g => g == groupby), - It.Is(t => t == timeframe), - It.Is>(f => f == null), - It.Is(z => z == ""))) - .Returns(Task.FromResult(reply)); - - client.Queries = queryMock.Object; - } - - var count = (await client.QueryGroupAsync(QueryType.Count(), testCol, "", groupby, timeframe)).ToList(); - Assert.IsNotNull(count); - - if (null != queryMock) - queryMock.VerifyAll(); - } - - [Test] - public async Task Query_ValidRelativeGroupInterval_Success() - { - var client = new KeenClient(SettingsEnv); - var timeframe = QueryRelativeTimeframe.PreviousNDays(2); - var interval = QueryInterval.EveryNHours(2); - var groupby = "field1"; - - IEnumerable>>> reply = new List>>>() - { - new QueryIntervalValue>>( - new List>() - { - new QueryGroupValue( "1", "field1" ), - new QueryGroupValue( "1", "field1" ), - }, - DateTime.Now, DateTime.Now.AddSeconds(2) - ), - new QueryIntervalValue>>( - new List>() - { - new QueryGroupValue( "2", "field1" ), - new QueryGroupValue( "2", "field1" ), - }, - DateTime.Now, DateTime.Now.AddSeconds(2) - ), - }; - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Metric( - It.Is(q => q == QueryType.Count()), - It.Is(c => c == testCol), - It.Is(p => p == ""), - It.Is(g => g == groupby), - It.Is(t => t == timeframe), - It.Is(i => i == interval), - It.Is>(f => f == null), - It.Is(z => z == ""))) - .Returns(Task.FromResult(reply)); - - client.Queries = queryMock.Object; - } - - var count = (await client.QueryIntervalGroupAsync(QueryType.Count(), testCol, "", groupby, timeframe, interval)).ToList(); - Assert.IsNotNull(count); - - if (null != queryMock) - { - queryMock.VerifyAll(); - } - } - - [Test] - public async Task Query_ValidAbsoluteInterval_Success() - { - var client = new KeenClient(SettingsEnv); - var timeframe = new QueryAbsoluteTimeframe(DateTime.Now.AddDays(-1), DateTime.Now); - var interval = QueryInterval.EveryNMinutes(5); - IEnumerable> result = - new List>() { new QueryIntervalValue("0", timeframe.Start, timeframe.End) }; - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Metric( - It.Is(q => q == QueryType.Count()), - It.Is(c => c == testCol), - It.Is(p => p == ""), - It.Is(t => t == timeframe), - It.Is(i => i == interval), - It.IsAny>(), - It.Is(z => z == ""))) - .Returns(Task.FromResult(result)); - - client.Queries = queryMock.Object; - } - - var counts = (await client.QueryIntervalAsync(QueryType.Count(), testCol, "", timeframe, interval)).ToList(); - Assert.IsNotNull(counts); - - if (null != queryMock) - queryMock.VerifyAll(); - } - - [Test] - public async Task Query_ValidRelative_Success() - { - var client = new KeenClient(SettingsEnv); - var timeframe = QueryRelativeTimeframe.ThisMinute(); - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Metric( - It.Is(q => q == QueryType.Count()), - It.Is(c => c == testCol), - It.Is(p => p == ""), - It.Is(t => t == timeframe), - It.Is>(f => f == null), - It.Is(z => z == ""))) - .Returns(Task.FromResult("0")); - - client.Queries = queryMock.Object; - } - - var count = await client.QueryAsync(QueryType.Count(), testCol, "", timeframe, null); - Assert.IsNotNull(count); - - if (null != queryMock) - { - queryMock.VerifyAll(); - } - } - - [Test] - public async Task Query_ValidRelativeInterval_Success() - { - var client = new KeenClient(SettingsEnv); - var interval = QueryInterval.EveryNMinutes(5); - var timeframe = QueryRelativeTimeframe.ThisMinute(); - IEnumerable> result = - new List>() { new QueryIntervalValue("0", DateTime.Now.AddMinutes(-5), DateTime.Now) }; - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Metric( - It.Is(q => q == QueryType.Count()), - It.Is(c => c == testCol), - It.Is(p => p == ""), - It.Is(t => t == timeframe), - It.Is(i => i == interval), - It.Is>(f => f == null), - It.Is(z => z == ""))) - .Returns(Task.FromResult(result)); - - client.Queries = queryMock.Object; - } - - var counts = (await client.QueryIntervalAsync(QueryType.Count(), testCol, "", timeframe, interval)).ToList(); - Assert.IsNotNull(counts); - - if (null != queryMock) - queryMock.VerifyAll(); - } - - [Test] - public async Task Query_ValidFilter_Success() - { - var client = new KeenClient(SettingsEnv); - var filters = new List() { new QueryFilter("field1", QueryFilter.FilterOperator.GreaterThan(), "1") }; - var timeframe = QueryRelativeTimeframe.ThisHour(); - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Metric( - It.Is(q => q == QueryType.Count()), - It.Is(c => c == testCol), - It.Is(p => p == ""), - It.Is(t => t == timeframe), - It.Is>(f => f == filters), - It.Is(z => z == ""))) - .Returns(Task.FromResult("1")); - - client.Queries = queryMock.Object; - } - - var count = await client.QueryAsync(QueryType.Count(), testCol, "", timeframe, filters); - Assert.IsNotNull(count); - - if (null != queryMock) - { - queryMock.VerifyAll(); - } - } - - [Test] - public async Task CountUnique_ValidAbsolute_Success() - { - var client = new KeenClient(SettingsEnv); - var timeframe = new QueryAbsoluteTimeframe(DateTime.Now.AddDays(-1), DateTime.Now); - var prop = "field1"; - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Metric( - It.Is(q => q == QueryType.CountUnique()), - It.Is(c => c == testCol), - It.Is(p => p == prop), - It.Is(t => t == timeframe), - It.Is>(f => f == null), - It.Is(t => t == "") - )) - .Returns(Task.FromResult("0")); - - client.Queries = queryMock.Object; - } - - var count = await client.QueryAsync(QueryType.CountUnique(), testCol, prop, timeframe); - Assert.IsNotNull(count); - - if (null != queryMock) - queryMock.VerifyAll(); - } - - [Test] - public async Task Minimum_ValidAbsolute_Success() - { - var client = new KeenClient(SettingsEnv); - var timeframe = new QueryAbsoluteTimeframe(DateTime.Now.AddDays(-1), DateTime.Now); - var prop = "field1"; - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Metric( - It.Is(q => q == QueryType.Minimum()), - It.Is(c => c == testCol), - It.Is(p => p == prop), - It.Is(t => t == timeframe), - It.Is>(f => f == null), - It.Is(t => t == "") - )) - .Returns(Task.FromResult("0")); - - client.Queries = queryMock.Object; - } - - var count = await client.QueryAsync(QueryType.Minimum(), testCol, prop, timeframe); - Assert.IsNotNull(count); - - if (null != queryMock) - queryMock.VerifyAll(); - } - - [Test] - public async Task Maximum_ValidAbsolute_Success() - { - var client = new KeenClient(SettingsEnv); - var timeframe = new QueryAbsoluteTimeframe(DateTime.Now.AddDays(-1), DateTime.Now); - var prop = "field1"; - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Metric( - It.Is(q => q == QueryType.Maximum()), - It.Is(c => c == testCol), - It.Is(p => p == prop), - It.Is(t => t == timeframe), - It.Is>(f => f == null), - It.Is(t => t == "") - )) - .Returns(Task.FromResult("0")); - - client.Queries = queryMock.Object; - } - - var count = await client.QueryAsync(QueryType.Maximum(), testCol, prop, timeframe); - Assert.IsNotNull(count); - - if (null != queryMock) - queryMock.VerifyAll(); - } - - [Test] - public async Task Average_ValidAbsolute_Success() - { - var client = new KeenClient(SettingsEnv); - var timeframe = new QueryAbsoluteTimeframe(DateTime.Now.AddDays(-1), DateTime.Now); - var prop = "field1"; - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Metric( - It.Is(q => q == QueryType.Average()), - It.Is(c => c == testCol), - It.Is(p => p == prop), - It.Is(t => t == timeframe), - It.Is>(f => f == null), - It.Is(t => t == "") - )) - .Returns(Task.FromResult("0.0")); - - client.Queries = queryMock.Object; - } - - await client.QueryAsync(QueryType.Average(), testCol, prop, timeframe); - - if (null != queryMock) - queryMock.VerifyAll(); - } - - [Test] - public async Task Sum_ValidAbsolute_Success() - { - var client = new KeenClient(SettingsEnv); - var timeframe = new QueryAbsoluteTimeframe(DateTime.Now.AddDays(-1), DateTime.Now); - var prop = "field1"; - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Metric( - It.Is(q => q == QueryType.Sum()), - It.Is(c => c == testCol), - It.Is(p => p == prop), - It.Is(t => t == timeframe), - It.Is>(f => f == null), - It.Is(t => t == "") - )) - .Returns(Task.FromResult("0.0")); - - client.Queries = queryMock.Object; - } - - await client.QueryAsync(QueryType.Sum(), testCol, prop, timeframe); - - if (null != queryMock) - queryMock.VerifyAll(); - } - - [Test] - public async Task SelectUnique_ValidAbsolute_Success() - { - var client = new KeenClient(SettingsEnv); - var timeframe = new QueryAbsoluteTimeframe(DateTime.Now.AddDays(-1), DateTime.Now); - var prop = "field1"; - var result = "hello,goodbye,I'm late"; - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Metric( - It.Is(q => q == QueryType.SelectUnique()), - It.Is(c => c == testCol), - It.Is(p => p == prop), - It.Is(t => t == timeframe), - It.Is>(f => f == null), - It.Is(t => t == "") - )) - .Returns(Task.FromResult(result)); - - client.Queries = queryMock.Object; - } - - var reply = await client.QueryAsync(QueryType.SelectUnique(), testCol, prop, timeframe); - - if (null != queryMock) - queryMock.VerifyAll(); - } - - [Test] - public async Task SelectUnique_ValidRelative_Success() - { - var client = new KeenClient(SettingsEnv); - var prop = "field1"; - var timeframe = QueryRelativeTimeframe.ThisMinute(); - var result = "hello,goodbye,I'm late"; - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Metric( - It.Is(q => q == QueryType.SelectUnique()), - It.Is(c => c == testCol), - It.Is(p => p == prop), - It.Is(t => t == timeframe), - It.Is>(f => f == null), - It.Is(t => t == "") - )) - .Returns(Task.FromResult(result)); - - client.Queries = queryMock.Object; - } - - await client.QueryAsync(QueryType.SelectUnique(), testCol, prop, timeframe); - - if (null != queryMock) - queryMock.VerifyAll(); - } - - [Test] - public async Task SelectUnique_ValidRelativeGroup_Success() - { - var client = new KeenClient(SettingsEnv); - var prop = "field1"; - var groupby = "field1"; - var timeframe = QueryRelativeTimeframe.PreviousNDays(5); - IEnumerable> reply = new List>() - { - new QueryGroupValue( "hello,goodbye,I'm late", "field1" ), - new QueryGroupValue( "hello,goodbye,I'm late", "field1" ), - }; - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Metric( - It.Is(q => q == QueryType.SelectUnique()), - It.Is(c => c == testCol), - It.Is(p => p == prop), - It.Is(g => g == groupby), - It.Is(t => t == timeframe), - It.Is>(f => f == null), - It.Is(t => t == "") - )) - .Returns(Task.FromResult(reply)); - - client.Queries = queryMock.Object; - } - - (await client.QueryGroupAsync(QueryType.SelectUnique(), testCol, prop, groupby, timeframe, null)).ToList(); - - if (null != queryMock) - queryMock.VerifyAll(); - } - - [Test] - public async Task SelectUnique_ValidFilter_Success() - { - var client = new KeenClient(SettingsEnv); - var prop = "field1"; - var timeframe = QueryRelativeTimeframe.ThisHour(); - var filters = new List() { new QueryFilter("field1", QueryFilter.FilterOperator.GreaterThan(), "1") }; - var result = "hello,goodbye,I'm late"; - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Metric( - It.Is(q => q == QueryType.SelectUnique()), - It.Is(c => c == testCol), - It.Is(p => p == prop), - It.Is(t => t == timeframe), - It.Is>(f => f == filters), - It.Is(t => t == "") - )) - .Returns(Task.FromResult(result)); - - client.Queries = queryMock.Object; - } - - var reply = (await client.QueryAsync(QueryType.SelectUnique(), testCol, prop, timeframe, filters)).ToList(); - - if (null != queryMock) - queryMock.VerifyAll(); - } - - [Test] - public async Task SelectUnique_ValidAbsoluteInterval_Success() - { - var client = new KeenClient(SettingsEnv); - var prop = "field1"; - var timeframe = new QueryAbsoluteTimeframe(DateTime.Now.AddDays(-1), DateTime.Now); - var interval = QueryInterval.EveryNMinutes(5); - var resultl = "hello,goodbye,I'm late"; - IEnumerable> result = - new List>() { new QueryIntervalValue(resultl, timeframe.Start, timeframe.End) }; - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Metric( - It.Is(q => q == QueryType.SelectUnique()), - It.Is(c => c == testCol), - It.Is(p => p == prop), - It.Is(t => t == timeframe), - It.Is(i => i == interval), - It.Is>(f => f == null), - It.Is(t => t == "") - )) - .Returns(Task.FromResult(result)); - - client.Queries = queryMock.Object; - } - - var counts = (await client.QueryIntervalAsync(QueryType.SelectUnique(), testCol, prop, timeframe, interval)).ToList(); - - if (null != queryMock) - queryMock.VerifyAll(); - } - - [Test] - public async Task SelectUnique_ValidAbsoluteIntervalGroup_Success() - { - var client = new KeenClient(SettingsEnv); - var prop = "field1"; - var timeframe = new QueryAbsoluteTimeframe(DateTime.Now.AddDays(-1), DateTime.Now); - var interval = QueryInterval.EveryNHours(4); - var groupby = "field1"; - var resultl = "hello,goodbye,I'm late"; - - IEnumerable>>> result = - new List>>>() - { - new QueryIntervalValue>>( - new List>(){ - new QueryGroupValue(resultl, "abc"), - new QueryGroupValue(resultl, "def") - }, - timeframe.Start, timeframe.End - ), - new QueryIntervalValue>>( - new List>(){ - new QueryGroupValue(resultl, "abc"), - new QueryGroupValue(resultl, "def") - }, - timeframe.Start, timeframe.End - ), - }; - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Metric( - It.Is(q => q == QueryType.SelectUnique()), - It.Is(c => c == testCol), - It.Is(p => p == prop), - It.Is(g => g == groupby), - It.Is(t => t == timeframe), - It.Is(i => i == interval), - It.Is>(f => f == null), - It.Is(t => t == "") - )) - .Returns(Task.FromResult(result)); - - client.Queries = queryMock.Object; - } - - var counts = (await client.QueryIntervalGroupAsync(QueryType.SelectUnique(), testCol, prop, groupby, timeframe, interval)).ToList(); - - if (null != queryMock) - queryMock.VerifyAll(); - } - - [Test] - public async Task SelectUnique_ValidRelativeInterval_Success() - { - var client = new KeenClient(SettingsEnv); - var prop = "field1"; - var interval = QueryInterval.EveryNMinutes(5); - var timeframe = QueryRelativeTimeframe.ThisMinute(); - var resultl = "hello,goodbye,I'm late"; - IEnumerable> result = - new List>() { new QueryIntervalValue(resultl, DateTime.Now.AddMinutes(-5), DateTime.Now) }; - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Metric( - It.Is(q => q == QueryType.SelectUnique()), - It.Is(c => c == testCol), - It.Is(p => p == prop), - It.Is(t => t == timeframe), - It.Is(i => i == interval), - It.Is>(f => f == null), - It.Is(t => t == "") - )) - .Returns(Task.FromResult(result)); - - client.Queries = queryMock.Object; - } - - var reply = (await client.QueryIntervalAsync(QueryType.SelectUnique(), testCol, prop, timeframe, interval)).ToList(); - - if (null != queryMock) - queryMock.VerifyAll(); - } - - [Test] - public async Task ExtractResource_ValidAbsolute_Success() - { - var client = new KeenClient(SettingsEnv); - var timeframe = new QueryAbsoluteTimeframe(DateTime.Now.AddDays(-1), DateTime.Now); - dynamic eo = new ExpandoObject(); - eo.field1 = "8888"; - IEnumerable result = new List() { eo, eo }; - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Extract( - It.Is(c => c == testCol), - It.Is(t => t == timeframe), - It.Is>(f => f == null), - It.Is(l => l == 0), - It.Is(t => t == "") - )) - .Returns(Task.FromResult(result)); - - client.Queries = queryMock.Object; - } - - var reply = (await client.QueryExtractResourceAsync(testCol, timeframe)).ToList(); - - if (null != queryMock) - queryMock.VerifyAll(); - } - - [Test] - public async Task ExtractResource_ValidRelative_Success() - { - var client = new KeenClient(SettingsEnv); - var timeframe = QueryRelativeTimeframe.ThisMinute(); - dynamic eo = new ExpandoObject(); - eo.field1 = "8888"; - IEnumerable result = new List() { eo, eo }; - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Extract( - It.Is(c => c == testCol), - It.Is(t => t == timeframe), - It.Is>(f => f == null), - It.Is(l => l == 0), - It.Is(t => t == "") - )) - .Returns(Task.FromResult(result)); - - client.Queries = queryMock.Object; - } - - var reply = (await client.QueryExtractResourceAsync(testCol, timeframe)).ToList(); - - if (null != queryMock) - queryMock.VerifyAll(); - } - - [Test] - public async Task ExtractResource_ValidFilter_Success() - { - var client = new KeenClient(SettingsEnv); - var timeframe = QueryRelativeTimeframe.ThisHour(); - var filters = new List() { new QueryFilter("field1", QueryFilter.FilterOperator.GreaterThan(), "1") }; - dynamic eo = new ExpandoObject(); - eo.field1 = "8888"; - IEnumerable result = new List() { eo, eo }; - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.Extract( - It.Is(c => c == testCol), - It.Is(t => t == timeframe), - It.Is>(f => f == filters), - It.Is(l => l == 0), - It.Is(t => t == "") - )) - .Returns(Task.FromResult(result)); - - client.Queries = queryMock.Object; - } - - var reply = (await client.QueryExtractResourceAsync(testCol, timeframe, filters)).ToList(); - - if (null != queryMock) - queryMock.VerifyAll(); - } - - [Test] - public async Task MultiAnalysis_Valid_Success() - { - var client = new KeenClient(SettingsEnv); - var timeframe = QueryRelativeTimeframe.ThisHour(); - - IEnumerable param = new List() - { - new MultiAnalysisParam("first", MultiAnalysisParam.Metric.Count()), - new MultiAnalysisParam("second", MultiAnalysisParam.Metric.Maximum("field1")), - new MultiAnalysisParam("third", MultiAnalysisParam.Metric.Minimum("field1")), - }; - IDictionary result = new Dictionary(); - result.Add("second", "fff"); - result.Add("third", "aaa"); - result.Add("first", "123"); - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.MultiAnalysis( - It.Is(c => c == testCol), - It.Is>(p => p == param), - It.Is(t => t == timeframe), - It.Is>(f => f == null), - It.Is(tz => tz == "") - )) - .Returns(Task.FromResult(result)); - - client.Queries = queryMock.Object; - } - - var reply = await client.QueryMultiAnalysisAsync(testCol, param, timeframe, null, ""); - - if (null != queryMock) - { - Assert.AreEqual(reply.Count(), result.Count()); - queryMock.VerifyAll(); - } - } - - [Test] - public async Task MultiAnalysis_ValidRelativeTimeFrame_Success() - { - var client = new KeenClient(SettingsEnv); - var timeframe = QueryRelativeTimeframe.PreviousNDays(2); - IEnumerable param = new List() - { - new MultiAnalysisParam("first", MultiAnalysisParam.Metric.Count()), - new MultiAnalysisParam("second", MultiAnalysisParam.Metric.Maximum("field1")), - new MultiAnalysisParam("third", MultiAnalysisParam.Metric.Minimum("field1")), - }; - IDictionary result = new Dictionary(); - result.Add("second", "fff"); - result.Add("third", "aaa"); - result.Add("first", "123"); - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.MultiAnalysis( - It.Is(c => c == testCol), - It.Is>(p => p == param), - It.Is(t => t == timeframe), - It.Is>(f => f == null), - It.Is(tz => tz == "") - )) - .Returns(Task.FromResult(result)); - - client.Queries = queryMock.Object; - } - - var reply = await client.QueryMultiAnalysisAsync(testCol, param, timeframe, null, ""); - - if (null != queryMock) - { - Assert.AreEqual(reply.Count(), result.Count()); - queryMock.VerifyAll(); - } - } - - [Test] - public async Task MultiAnalysis_ValidGroupBy_Success() - { - var client = new KeenClient(SettingsEnv); - var timeframe = QueryRelativeTimeframe.ThisHour(); - var groupby = "field1"; - IEnumerable param = new List() - { - new MultiAnalysisParam("first", MultiAnalysisParam.Metric.Count()), - new MultiAnalysisParam("second", MultiAnalysisParam.Metric.Maximum("field1")), - new MultiAnalysisParam("third", MultiAnalysisParam.Metric.Minimum("field1")), - }; - var dict = new Dictionary(); - dict.Add("second", "fff"); - dict.Add("third", "aaa"); - dict.Add("first", "123"); - dict.Add(groupby, "123"); - IEnumerable>> result = new List>>() - { - new QueryGroupValue>(dict, groupby), - new QueryGroupValue>(dict, groupby), - }; - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.MultiAnalysis( - It.Is(c => c == testCol), - It.Is>(p => p == param), - It.Is(t => t == timeframe), - It.Is>(f => f == null), - It.Is(g => g == groupby), - It.Is(tz => tz == "") - )) - .Returns(Task.FromResult(result)); - - client.Queries = queryMock.Object; - } - - var reply = (await client.QueryMultiAnalysisGroupAsync(testCol, param, timeframe, null, groupby, "")).ToList(); - - if (null != queryMock) - { - Assert.AreEqual(reply.Count(), result.Count()); - queryMock.VerifyAll(); - } - } - - [Test] - public async Task MultiAnalysis_ValidIntervalGroupBy_Success() - { - var client = new KeenClient(SettingsEnv); - var timeframe = QueryRelativeTimeframe.PreviousNDays(3); - var interval = QueryInterval.Daily(); - var groupby = "field1"; - IEnumerable param = new List() - { - new MultiAnalysisParam("first", MultiAnalysisParam.Metric.Count()), - new MultiAnalysisParam("second", MultiAnalysisParam.Metric.Maximum("field1")), - new MultiAnalysisParam("third", MultiAnalysisParam.Metric.Minimum("field1")), - }; - - var dict = new Dictionary(); - dict.Add("second", "fff"); - dict.Add("third", "aaa"); - dict.Add("first", "123"); - dict.Add(groupby, "123"); - IEnumerable>>>> result = - new List>>>>() - { - new QueryIntervalValue>>>( - new List>>(){ - new QueryGroupValue>(dict, groupby), - new QueryGroupValue>(dict, groupby) - }, - DateTime.Now, DateTime.Now.AddSeconds(2) - ), - new QueryIntervalValue>>>( - new List>>(){ - new QueryGroupValue>(dict, groupby), - new QueryGroupValue>(dict, groupby) - }, - DateTime.Now, DateTime.Now.AddSeconds(2) - ), - }; - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.MultiAnalysis( - It.Is(c => c == testCol), - It.Is>(p => p == param), - It.Is(t => t == timeframe), - It.Is(i => i == interval), - It.Is>(f => f == null), - It.Is(g => g == groupby), - It.Is(tz => tz == "") - )) - .Returns(Task.FromResult(result)); - - client.Queries = queryMock.Object; - } - - var reply = (await client.QueryMultiAnalysisIntervalGroupAsync(testCol, param, timeframe, interval, null, groupby, "")).ToList(); - - if (null != queryMock) - queryMock.VerifyAll(); - } - - [Test] - public async Task MultiAnalysis_ValidInterval_Success() - { - var client = new KeenClient(SettingsEnv); - IEnumerable param = new List() - { - new MultiAnalysisParam("first", MultiAnalysisParam.Metric.Count()), - new MultiAnalysisParam("second", MultiAnalysisParam.Metric.Maximum("field1")), - new MultiAnalysisParam("third", MultiAnalysisParam.Metric.Minimum("field1")), - }; - var timeframe = QueryRelativeTimeframe.PreviousNDays(3); - var interval = QueryInterval.Daily(); - IEnumerable>> result = new List>>(); - foreach (var i in Enumerable.Range(1, 3)) - { - var dic = new Dictionary(); - dic.Add("second", "fff"); - dic.Add("third", "aaa"); - dic.Add("first", "123"); - - var qv = new QueryIntervalValue>(dic, DateTime.Now, DateTime.Now.AddSeconds(2)); - ((List>>)result).Add(qv); - } - - Mock queryMock = null; - if (UseMocks) - { - queryMock = new Mock(); - queryMock.Setup(m => m.MultiAnalysis( - It.Is(c => c == testCol), - It.Is>(p => p == param), - It.Is(t => t == timeframe), - It.Is(i => i == interval), - It.Is>(f => f == null), - It.Is(tz => tz == "") - )) - .Returns(Task.FromResult(result)); - - client.Queries = queryMock.Object; - } - - var reply = (await client.QueryMultiAnalysisIntervalAsync(testCol, param, timeframe, interval)).ToList(); - - if (null != queryMock) - { - queryMock.VerifyAll(); - Assert.AreEqual(reply.Count(), result.Count()); - } - } - } - - [TestFixture] - public class QueryFilterTest - { - [Test] - public void Constructor_InvalidProperty_Throws() - { - Assert.Throws(() => new QueryFilter(null, QueryFilter.FilterOperator.Equals(), "val")); - } - - [Test] - public void Constructor_InvalidOp_Throws() - { - Assert.Throws(() => new QueryFilter("prop", null, "val")); - } - - [Test] - public void Constructor_NullPropertyValue_Success() - { - Assert.DoesNotThrow(() => new QueryFilter("prop", QueryFilter.FilterOperator.Equals(), null)); - } - - [Test] - public void Constructor_ValidParams_Success() - { - Assert.DoesNotThrow(() => new QueryFilter("prop", QueryFilter.FilterOperator.Equals(), "val")); - } - - [Test] - public void Serialize_SimpleValue_Success() - { - var filter = new QueryFilter("prop", QueryFilter.FilterOperator.Equals(), "val"); - - var json = JObject.FromObject(filter).ToString(Newtonsoft.Json.Formatting.None); - - const string expectedJson = "{" + - "\"property_name\":\"prop\"," + - "\"operator\":\"eq\"," + - "\"property_value\":\"val\"" + - "}"; - Assert.AreEqual(expectedJson, json); - } - - [Test] - public void Serialize_NullValue_Success() - { - var filter = new QueryFilter("prop", QueryFilter.FilterOperator.Equals(), null); - - var json = JObject.FromObject(filter).ToString(Newtonsoft.Json.Formatting.None); - - const string expectedJson = "{\"property_name\":\"prop\"," + - "\"operator\":\"eq\"," + - "\"property_value\":null}"; - - Assert.AreEqual(expectedJson, json); - } - - [Test] - public void Serialize_GeoValue_Success() - { - var filter = new QueryFilter("prop", QueryFilter.FilterOperator.Within(), new QueryFilter.GeoValue(10.0, 10.0, 5.0)); - - var json = JObject.FromObject(filter).ToString(Newtonsoft.Json.Formatting.None); - Trace.WriteLine(json); - const string expectedJson = "{" + - "\"property_name\":\"prop\"," + - "\"operator\":\"within\"," + - "\"property_value\":{" + - "\"coordinates\":[" + - "10.0," + - "10.0" + - "]," + - "\"max_distance_miles\":5.0" + - "}" + - "}"; - - Assert.AreEqual(expectedJson, json); - } - } -} diff --git a/Keen.NET.Test/QueryTests_Integration.cs b/Keen.NET.Test/QueryTests_Integration.cs deleted file mode 100644 index 2dfa01d..0000000 --- a/Keen.NET.Test/QueryTests_Integration.cs +++ /dev/null @@ -1,1121 +0,0 @@ -using Keen.Core.Query; -using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using System.Linq; -using System.Text; -using Newtonsoft.Json.Linq; -using Newtonsoft.Json; -using System.Web; -using Keen.Core; - -namespace Keen.Net.Test -{ - /// - /// Integration tests for Queries. These will exercise more than unit tests, like the - /// integration between KeenClient, Queries and KeenHttpClient. - /// - class QueryTests_Integration : TestBase - { - [Test] - public async Task QueryFilter_NotContains_Success() - { - var queriesUrl = HttpTests.GetUriForResource(SettingsEnv, - KeenConstants.QueriesResource); - - var handler = new FuncHandler() - { - PreProcess = (req, ct) => - { - var queryStr = req.RequestUri.Query; - - // Make sure our filter properties are in the query string - Assert.IsTrue(queryStr.Contains("propertyName") && - queryStr.Contains("four") && - queryStr.Contains(QueryFilter.FilterOperator.NotContains())); - }, - ProduceResultAsync = (req, ct) => - { - return HttpTests.CreateJsonStringResponseAsync(new { result = 2 }); - }, - DeferToDefault = false - }; - - // NOTE : This example shows use of UrlToMessageHandler, but since we only make one - // request to a single endpoint, we could just directly use the FuncHandler here. - var urlHandler = new UrlToMessageHandler( - new Dictionary - { - { queriesUrl, handler } - }) - { DeferToDefault = false }; - - var client = new KeenClient(SettingsEnv, new TestKeenHttpClientProvider() - { - ProvideKeenHttpClient = - (url) => KeenHttpClientFactory.Create(url, - new HttpClientCache(), - null, - new DelegatingHandlerMock(urlHandler)) - }); - - var filters = new List - { - new QueryFilter("propertyName", QueryFilter.FilterOperator.NotContains(), "four") - }; - - var count = await client.QueryAsync( - QueryType.Count(), - "testCollection", - "", - QueryRelativeTimeframe.ThisMonth(), - filters); - - Assert.IsNotNull(count); - Assert.AreEqual("2", count); - } - - [Test] - public async Task QueryFilter_NullPropertyValue_Success() - { - // TODO : Consolidate this FuncHandler/KeenClient setup into a helper method. - - var handler = new FuncHandler() - { - PreProcess = (req, ct) => - { - var queryStr = req.RequestUri.Query; - - // Make sure our filter properties are in the query string - Assert.IsTrue(queryStr.Contains("propertyName") && - queryStr.Contains("null") && - queryStr.Contains(QueryFilter.FilterOperator.Equals())); - }, - ProduceResultAsync = (req, ct) => - { - return HttpTests.CreateJsonStringResponseAsync(new { result = 2 }); - }, - DeferToDefault = false - }; - - var client = new KeenClient(SettingsEnv, new TestKeenHttpClientProvider() - { - ProvideKeenHttpClient = - (url) => KeenHttpClientFactory.Create(url, - new HttpClientCache(), - null, - new DelegatingHandlerMock(handler)) - }); - - var filters = new List - { - new QueryFilter("propertyName", QueryFilter.FilterOperator.Equals(), null) - }; - - var count = await client.QueryAsync( - QueryType.Count(), - "testCollection", - "", - QueryRelativeTimeframe.ThisMonth(), - filters); - - Assert.IsNotNull(count); - Assert.AreEqual("2", count); - } - - [Test] - public async Task Query_AvailableQueries_Success() - { - var queriesResource = HttpTests.GetUriForResource(SettingsEnv, KeenConstants.QueriesResource); - - var expectedQueries = new Dictionary() - { - { "select_unique_url", $"{queriesResource.AbsolutePath}/select_unique"}, - { "minimum", $"{queriesResource.AbsolutePath}/minimum" }, - { "extraction_url", $"{queriesResource.AbsolutePath}/extraction" }, - { "percentile", $"{queriesResource.AbsolutePath}/percentile" }, - { "funnel_url", $"{queriesResource.AbsolutePath}/funnel" }, - { "average", $"{queriesResource.AbsolutePath}/average" }, - { "median", $"{queriesResource.AbsolutePath}/median" }, - { "maximum", $"{queriesResource.AbsolutePath}/maximum" }, - { "count_url", $"{queriesResource.AbsolutePath}/count" }, - { "count_unique_url", $"{queriesResource.AbsolutePath}/count_unique" }, - { "sum", $"{queriesResource.AbsolutePath}/sum"} - }; - - FuncHandler handler = new FuncHandler() - { - ProduceResultAsync = (request, ct) => - { - return HttpTests.CreateJsonStringResponseAsync(expectedQueries); - } - }; - - var client = new KeenClient(SettingsEnv, new TestKeenHttpClientProvider() - { - ProvideKeenHttpClient = - (url) => KeenHttpClientFactory.Create(url, - new HttpClientCache(), - null, - new DelegatingHandlerMock(handler)) - }); - - var actualQueries = await client.GetQueries(); - - Assert.AreEqual(expectedQueries.Count, actualQueries.Count()); - foreach (var expectedQuery in expectedQueries) - { - Assert.That(actualQueries.Contains(expectedQuery)); - } - } - - class QueryParameters - { - internal string EventCollection = "myEvents"; - internal QueryType Analysis = QueryType.Count(); - internal string TargetProperty; - internal String GroupBy; - internal QueryRelativeTimeframe Timeframe; - internal QueryInterval Interval; - - internal virtual string GetResourceName() => Analysis; - - internal virtual List> GetQueryParameters() - { - var queryParameters = new List>(); - - if (null != EventCollection) { queryParameters.Add(new KeyValuePair(KeenConstants.QueryParmEventCollection, EventCollection)); } - if (null != TargetProperty) { queryParameters.Add(new KeyValuePair(KeenConstants.QueryParmTargetProperty, TargetProperty)); } - if (null != GroupBy) { queryParameters.Add(new KeyValuePair(KeenConstants.QueryParmGroupBy, GroupBy)); } - if (null != Timeframe) { queryParameters.Add(new KeyValuePair(KeenConstants.QueryParmTimeframe, Timeframe.ToString())); } - if (null != Interval) { queryParameters.Add(new KeyValuePair(KeenConstants.QueryParmInterval, Interval)); } - - return queryParameters; - } - - internal MultiAnalysisParam GetMultiAnalysisParameter(string label) - { - return new MultiAnalysisParam( - label, - String.IsNullOrEmpty(TargetProperty) ? - new MultiAnalysisParam.Metric(Analysis) : - new MultiAnalysisParam.Metric(Analysis, TargetProperty)); - } - } - - class ExtractionParameters : QueryParameters - { - internal ExtractionParameters() - { - Analysis = null; - } - - internal override string GetResourceName() => KeenConstants.QueryExtraction; - } - - class FunnelParameters : QueryParameters - { - internal IEnumerable Steps; - - internal FunnelParameters() - { - EventCollection = null; - Analysis = null; - } - - internal override string GetResourceName() => KeenConstants.QueryFunnel; - - internal override List> GetQueryParameters() - { - var parameters = base.GetQueryParameters(); - parameters.Add(new KeyValuePair( - KeenConstants.QueryParmSteps, - JArray.FromObject(Steps).ToString(Newtonsoft.Json.Formatting.None))); - return parameters; - } - } - - class MultiAnalysisParameters : QueryParameters - { - internal IEnumerable Analyses; - internal IList Labels; - - internal MultiAnalysisParameters() - { - Analysis = null; - } - - internal override string GetResourceName() => KeenConstants.QueryMultiAnalysis; - - internal override List> GetQueryParameters() - { - var parameters = base.GetQueryParameters(); - - var multiAnalysisParameters = GetMultiAnalysisParameters(); - - var jObjects = multiAnalysisParameters.Select(x => - new JProperty(x.Label, JObject.FromObject( - string.IsNullOrEmpty(x.TargetProperty) ? - (object)new { analysis_type = x.Analysis } : - new { analysis_type = x.Analysis, target_property = x.TargetProperty }))); - - var analysesJson = JsonConvert.SerializeObject( - new JObject(jObjects), - Formatting.None, - new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); - - parameters.Add(new KeyValuePair( - KeenConstants.QueryParmAnalyses, - analysesJson)); - - return parameters; - } - - internal IEnumerable GetMultiAnalysisParameters() - { - return Analyses.Zip(Labels, (parameters, label) => parameters.GetMultiAnalysisParameter(label)); - } - } - - FuncHandler CreateQueryRequestHandler(QueryParameters queryParameters, object response) - { - var parameters = queryParameters.GetQueryParameters(); - StringBuilder queryStringBuilder = new StringBuilder(); - foreach (var parameter in parameters) - { - if (queryStringBuilder.Length != 0) - { - queryStringBuilder.Append('&'); - } - - queryStringBuilder.Append($"{parameter.Key}={Uri.EscapeDataString(parameter.Value)}"); - } - - return new FuncHandler() - { - ProduceResultAsync = (request, ct) => - { - var expectedPath = $"{HttpTests.GetUriForResource(SettingsEnv, KeenConstants.QueriesResource)}/" + - $"{queryParameters.GetResourceName()}"; - - string actualPath = String.Format( - "{0}{1}{2}{3}", - request.RequestUri.Scheme, - Uri.SchemeDelimiter, - request.RequestUri.Authority, - request.RequestUri.AbsolutePath); - - Assert.AreEqual(expectedPath, actualPath); - - var expectedQueryStringCollection = HttpUtility.ParseQueryString(queryStringBuilder.ToString()); - var actualQueryStringCollection = HttpUtility.ParseQueryString(request.RequestUri.Query); - Assert.AreEqual(expectedQueryStringCollection.Count, actualQueryStringCollection.Count); - foreach (var key in expectedQueryStringCollection.AllKeys) - { - Assert.AreEqual(expectedQueryStringCollection[key], actualQueryStringCollection[key]); - } - - return HttpTests.CreateJsonStringResponseAsync(response); - } - }; - } - - KeenClient CreateQueryTestKeenClient(QueryParameters queryParameters, object response) - { - var handler = CreateQueryRequestHandler(queryParameters, response); - - return new KeenClient(SettingsEnv, new TestKeenHttpClientProvider() - { - ProvideKeenHttpClient = - (url) => KeenHttpClientFactory.Create(url, - new HttpClientCache(), - null, - new DelegatingHandlerMock(handler)) - }); - } - - [Test] - public async Task Query_SimpleCount_Success() - { - var queryParameters = new QueryParameters(); - - string expectedResult = "10"; - - var expectedResponse = new Dictionary() - { - { "result", expectedResult}, - }; - - var client = CreateQueryTestKeenClient(queryParameters, expectedResponse); - - var actualResult = await client.Queries.Metric(queryParameters.Analysis, queryParameters.EventCollection, null); - - Assert.AreEqual(expectedResult, actualResult); - } - - [Test] - public async Task Query_SimpleSelectUnique_Success() - { - var queryParameters = new QueryParameters() - { - Analysis = QueryType.SelectUnique(), - TargetProperty = "targetProperty" - }; - - string[] results = - { - "this", - "that", - "theOtherThing" - }; - - var expectedResponse = new - { - result = results, - }; - - var client = CreateQueryTestKeenClient(queryParameters, expectedResponse); - - var actualResult = await client.Queries.Metric( - queryParameters.Analysis, - queryParameters.EventCollection, - queryParameters.TargetProperty); - - string expectedResultString = String.Join(",", results); - Assert.AreEqual(expectedResultString, actualResult); - } - - [Test] - public async Task Query_SimpleAverage_Success() - { - var queryParameters = new QueryParameters() - { - Analysis = QueryType.Average(), - TargetProperty = "someProperty" - }; - - string expectedResult = "10"; - - var expectedResponse = new Dictionary() - { - { "result", expectedResult}, - }; - - var client = CreateQueryTestKeenClient(queryParameters, expectedResponse); - - var actualResult = await client.Queries.Metric( - queryParameters.Analysis, - queryParameters.EventCollection, - queryParameters.TargetProperty); - - Assert.AreEqual(expectedResult, actualResult); - } - - [Test] - public async Task Query_SimpleCountGroupBy_Success() - { - var queryParameters = new QueryParameters() - { - GroupBy = "someGroupProperty" - }; - - var expectedResults = new List() { "10", "20" }; - var expectedGroups = new List() { "group1", "group2" }; - var expectedGroupResults = expectedResults.Zip( - expectedGroups, - (result, group) => new Dictionary() - { - { queryParameters.GroupBy, group }, - { "result", result } - }); - - var expectedResponse = new - { - result = expectedGroupResults - }; - - var client = CreateQueryTestKeenClient(queryParameters, expectedResponse); - - var actualResult = await client.Queries.Metric( - queryParameters.Analysis, - queryParameters.EventCollection, - null, - queryParameters.GroupBy); - - Assert.AreEqual(expectedGroupResults.Count(), actualResult.Count()); - foreach (var actualGroupResult in actualResult) - { - var expectedGroupResult = expectedGroupResults.Where((result) => result[queryParameters.GroupBy] == actualGroupResult.Group).First(); - Assert.AreEqual(expectedGroupResult["result"], actualGroupResult.Value); - } - } - - [Test] - public async Task Query_SimpleSelectUniqueGroupBy_Success() - { - var queryParameters = new QueryParameters() - { - Analysis = QueryType.SelectUnique(), - TargetProperty = "someProperty", - GroupBy = "someGroupProperty" - }; - - var expectedResults = new List() { new string[] { "10", "20" }, new string[] { "30", "40" } }; - var expectedGroups = new List() { "group1", "group2" }; - var expectedGroupResults = expectedResults.Zip( - expectedGroups, - (result, group) => new - { - someGroupProperty = group, - result = result - }); - - var expectedResponse = new - { - result = expectedGroupResults - }; - - var client = CreateQueryTestKeenClient(queryParameters, expectedResponse); - - var actualResult = await client.Queries.Metric( - queryParameters.Analysis, - queryParameters.EventCollection, - queryParameters.TargetProperty, - queryParameters.GroupBy); - - Assert.AreEqual(expectedGroupResults.Count(), actualResult.Count()); - foreach (var actualGroupResult in actualResult) - { - var expectedGroupResult = expectedGroupResults.Where((result) => result.someGroupProperty == actualGroupResult.Group).First(); - Assert.AreEqual(string.Join(",", expectedGroupResult.result), actualGroupResult.Value); - } - } - - [Test] - public async Task Query_SimpleCountInterval_Success() - { - var queryParameters = new QueryParameters() - { - TargetProperty = "someProperty", - Timeframe = QueryRelativeTimeframe.ThisNHours(2), - Interval = QueryInterval.EveryNHours(1) - }; - - var expectedCounts = new List() { "10", "20" }; - var expectedTimeframes = new List() - { - new QueryAbsoluteTimeframe(DateTime.Now.AddHours(-2), DateTime.Now.AddHours(-1)), - new QueryAbsoluteTimeframe(DateTime.Now.AddHours(-1), DateTime.Now) - }; - var expectedResults = expectedCounts.Zip( - expectedTimeframes, - (count, time) => new { timeframe = time, value = count }); - - var expectedResponse = new - { - result = expectedResults - }; - - var client = CreateQueryTestKeenClient(queryParameters, expectedResponse); - - var actualResults = await client.Queries.Metric( - queryParameters.Analysis, - queryParameters.EventCollection, - queryParameters.TargetProperty, - queryParameters.Timeframe, - queryParameters.Interval); - - Assert.AreEqual(expectedResults.Count(), actualResults.Count()); - var expectedEnumerator = expectedResults.GetEnumerator(); - var actualEnumerator = actualResults.GetEnumerator(); - while (expectedEnumerator.MoveNext() && actualEnumerator.MoveNext()) - { - var expected = expectedEnumerator.Current; - var actual = actualEnumerator.Current; - Assert.AreEqual(expected.timeframe.Start, actual.Start); - Assert.AreEqual(expected.timeframe.End, actual.End); - Assert.AreEqual(expected.value, actual.Value); - } - } - - [Test] - public async Task Query_SimpleSelectUniqueInterval_Success() - { - var queryParameters = new QueryParameters() - { - Analysis = QueryType.SelectUnique(), - TargetProperty = "someProperty", - Timeframe = QueryRelativeTimeframe.ThisNHours(2), - Interval = QueryInterval.EveryNHours(1) - }; - - var expectedCounts = new List() { new string[] { "10", "20" }, new string[] { "30", "40" } }; - var expectedTimeframes = new List() - { - new QueryAbsoluteTimeframe(DateTime.Now.AddHours(-2), DateTime.Now.AddHours(-1)), - new QueryAbsoluteTimeframe(DateTime.Now.AddHours(-1), DateTime.Now) - }; - var expectedResults = expectedCounts.Zip( - expectedTimeframes, - (count, time) => new { timeframe = time, value = count }); - - var expectedResponse = new - { - result = expectedResults - }; - - var client = CreateQueryTestKeenClient(queryParameters, expectedResponse); - - var actualResults = await client.Queries.Metric( - queryParameters.Analysis, - queryParameters.EventCollection, - queryParameters.TargetProperty, - queryParameters.Timeframe, - queryParameters.Interval); - - Assert.AreEqual(expectedResults.Count(), actualResults.Count()); - var expectedEnumerator = expectedResults.GetEnumerator(); - var actualEnumerator = actualResults.GetEnumerator(); - while (expectedEnumerator.MoveNext() && actualEnumerator.MoveNext()) - { - var expected = expectedEnumerator.Current; - var actual = actualEnumerator.Current; - Assert.AreEqual(expected.timeframe.Start, actual.Start); - Assert.AreEqual(expected.timeframe.End, actual.End); - Assert.AreEqual(string.Join(",", expected.value), actual.Value); - } - } - - [Test] - public async Task Query_SimpleCountGroupByInterval_Success() - { - var queryParameters = new QueryParameters() - { - TargetProperty = "someProperty", - Timeframe = QueryRelativeTimeframe.ThisNHours(2), - Interval = QueryInterval.EveryNHours(1), - GroupBy = "someGroupProperty" - }; - - var expectedResults = new[] - { - new - { - timeframe = new QueryAbsoluteTimeframe(DateTime.Now.AddHours(-2), DateTime.Now.AddHours(-1)), - value = new List> - { - new Dictionary{ { queryParameters.GroupBy, "group1" }, { "result", "10" } }, - new Dictionary{ { queryParameters.GroupBy, "group2" }, { "result", "20" } }, - } - }, - new - { - timeframe = new QueryAbsoluteTimeframe(DateTime.Now.AddHours(-1), DateTime.Now), - value = new List> - { - new Dictionary{ { queryParameters.GroupBy, "group1" }, { "result", "30" } }, - new Dictionary{ { queryParameters.GroupBy, "group2" }, { "result", "40" } }, - } - } - }; - - var expectedResponse = new - { - result = expectedResults - }; - - var client = CreateQueryTestKeenClient(queryParameters, expectedResponse); - - var actualResults = await client.Queries.Metric( - queryParameters.Analysis, - queryParameters.EventCollection, - queryParameters.TargetProperty, - queryParameters.GroupBy, - queryParameters.Timeframe, - queryParameters.Interval); - - Assert.AreEqual(expectedResults.Count(), actualResults.Count()); - var actualEnumerator = actualResults.GetEnumerator(); - foreach (var expected in expectedResults) - { - actualEnumerator.MoveNext(); - var actual = actualEnumerator.Current; - // Validate the interval is correct - Assert.AreEqual(expected.timeframe.Start, actual.Start); - Assert.AreEqual(expected.timeframe.End, actual.End); - - // Validate the results for the group within the time interval - Assert.AreEqual(expected.value.Count, actual.Value.Count()); - var actualGroupResultEnumerator = actual.Value.GetEnumerator(); - foreach (var expectedGroupResult in expected.value) - { - actualGroupResultEnumerator.MoveNext(); - var actualGroupResult = actualGroupResultEnumerator.Current; - Assert.AreEqual(expectedGroupResult[queryParameters.GroupBy], actualGroupResult.Group); - Assert.AreEqual(expectedGroupResult["result"], actualGroupResult.Value); - } - } - } - - [Test] - public async Task Query_SimpleSelectUniqueGroupByInterval_Success() - { - var queryParameters = new QueryParameters() - { - Analysis = QueryType.SelectUnique(), - TargetProperty = "someProperty", - Timeframe = QueryRelativeTimeframe.ThisNHours(2), - Interval = QueryInterval.EveryNHours(1), - GroupBy = "someGroupProperty" - }; - - string resultsJson = @"[ - { - ""timeframe"": { - ""start"": ""2017-10-14T00:00:00.000Z"", - ""end"": ""2017-10-15T00:00:00.000Z"" - }, - ""value"": [ - { - ""someGroupProperty"": ""group1"", - ""result"": [ - ""10"", - ""20"" - ] - }, - { - ""someGroupProperty"": ""group2"", - ""result"": [ - ""30"", - ""40"" - ] - } - ] - }, - { - ""timeframe"": { - ""start"": ""2017-10-15T00:00:00.000Z"", - ""end"": ""2017-10-16T00:00:00.000Z"" - }, - ""value"": [ - { - ""someGroupProperty"": ""group1"", - ""result"": [ - ""50"", - ""60"" - ] - }, - { - ""someGroupProperty"": ""group2"", - ""result"": [ - ""70"", - ""80"" - ] - } - ] - } - ]"; - - var expectedResults = JArray.Parse(resultsJson); - - var expectedResponse = new - { - result = expectedResults - }; - - var client = CreateQueryTestKeenClient(queryParameters, expectedResponse); - - var actualResults = await client.Queries.Metric( - queryParameters.Analysis, - queryParameters.EventCollection, - queryParameters.TargetProperty, - queryParameters.GroupBy, - queryParameters.Timeframe, - queryParameters.Interval); - - Assert.AreEqual(expectedResults.Count(), actualResults.Count()); - var actualEnumerator = actualResults.GetEnumerator(); - foreach (var expected in expectedResults) - { - actualEnumerator.MoveNext(); - var actual = actualEnumerator.Current; - // Validate the interval is correct - Assert.AreEqual(DateTime.Parse(expected["timeframe"]["start"].Value()), actual.Start); - Assert.AreEqual(DateTime.Parse(expected["timeframe"]["end"].Value()), actual.End); - - // Validate the results for the group within the time interval - Assert.AreEqual(expected["value"].Count(), actual.Value.Count()); - var actualGroupResultEnumerator = actual.Value.GetEnumerator(); - foreach (var expectedGroupResult in expected["value"]) - { - actualGroupResultEnumerator.MoveNext(); - var actualGroupResult = actualGroupResultEnumerator.Current; - Assert.AreEqual(expectedGroupResult[queryParameters.GroupBy].Value(), actualGroupResult.Group); - Assert.AreEqual(string.Join(",", expectedGroupResult["result"].Values()), actualGroupResult.Value); - } - } - } - - [Test] - public async Task Query_SimpleExtraction_Success() - { - var queryParameters = new ExtractionParameters() - { - Timeframe = QueryRelativeTimeframe.ThisNHours(2), - }; - - string resultsJson = @"[ - { - ""keen"": { - ""created_at"": ""2012-07-30T21:21:46.566000+00:00"", - ""timestamp"": ""2012-07-30T21:21:46.566000+00:00"", - ""id"": """" - }, - ""user"": { - ""email"": ""dan@keen.io"", - ""id"": ""4f4db6c7777d66ffff000000"" - }, - ""user_agent"": { - ""browser"": ""chrome"", - ""browser_version"": ""20.0.1132.57"", - ""platform"": ""macos"" - } - }, - { - ""keen"": { - ""created_at"": ""2012-07-30T21:40:05.386000+00:00"", - ""timestamp"": ""2012-07-30T21:40:05.386000+00:00"", - ""id"": """" - }, - ""user"": { - ""email"": ""michelle@keen.io"", - ""id"": ""4fa2cccccf546ffff000006"" - }, - ""user_agent"": { - ""browser"": ""chrome"", - ""browser_version"": ""20.0.1132.57"", - ""platform"": ""macos"" - } - } - ]"; - - var expectedResults = JArray.Parse(resultsJson); - - var expectedResponse = new - { - result = expectedResults - }; - - var client = CreateQueryTestKeenClient(queryParameters, expectedResponse); - - var actualResults = await client.Queries.Extract( - queryParameters.EventCollection, - queryParameters.Timeframe); - - Assert.AreEqual(expectedResults.Count(), actualResults.Count()); - var actualEnumerator = actualResults.GetEnumerator(); - foreach (var expected in expectedResults) - { - actualEnumerator.MoveNext(); - JToken actual = actualEnumerator.Current; - // Validate the result is correct - Assert.AreEqual(expected["user"]["email"].Value(), actual["user"]["email"].Value()); - Assert.AreEqual(expected["user"]["id"].Value(), actual["user"]["id"].Value()); - Assert.AreEqual(expected["user_agent"]["browser"].Value(), actual["user_agent"]["browser"].Value()); - } - } - - [Test] - public async Task Query_SimpleFunnel_Success() - { - var queryParameters = new FunnelParameters() - { - Steps = new FunnelStep[] - { - new FunnelStep() {EventCollection = "signed up", ActorProperty = "visitor.guid", Timeframe = QueryRelativeTimeframe.ThisNDays(7)}, - new FunnelStep() {EventCollection = "completed profile", ActorProperty = "user.guid", Timeframe = QueryRelativeTimeframe.ThisNDays(7)}, - new FunnelStep() {EventCollection = "referred user", ActorProperty = "user.guid", Timeframe = QueryRelativeTimeframe.ThisNDays(7)}, - } - }; - - string responseJson = @"{ - ""result"": [ - 3, - 1, - 0 - ], - ""steps"": [ - { - ""actor_property"": ""visitor.guid"", - ""event_collection"": ""signed up"", - ""timeframe"": ""this_7_days"" - }, - { - ""actor_property"": ""user.guid"", - ""event_collection"": ""completed profile"", - ""timeframe"": ""this_7_days"" - }, - { - ""actor_property"": ""user.guid"", - ""event_collection"": ""referred user"", - ""timeframe"": ""this_7_days"" - } - ] - }"; - - var expectedResponse = JObject.Parse(responseJson); - - var client = CreateQueryTestKeenClient(queryParameters, expectedResponse); - - var actualResults = await client.Queries.Funnel( - queryParameters.Steps); - - var expectedResults = expectedResponse["result"]; - - Assert.AreEqual(expectedResults.Count(), actualResults.Result.Count()); - var actualEnumerator = actualResults.Result.GetEnumerator(); - foreach (var expected in expectedResults) - { - actualEnumerator.MoveNext(); - var actual = actualEnumerator.Current; - // Validate the result is correct - Assert.AreEqual(expected.Value(), actual); - } - } - - [Test] - public async Task Query_SimpleMultiAnalysis_Success() - { - var queryParameters = new MultiAnalysisParameters() - { - Labels = new string[] - { - "first analysis", - "second analysis" - }, - Analyses = new QueryParameters[] - { - new QueryParameters(), - new QueryParameters() - { - Analysis = QueryType.Average(), - TargetProperty = "targetProperty" - } - } - }; - - string responseJson = $"{{\"result\":{{ \"{queryParameters.Labels[0]}\" : 12345, \"{queryParameters.Labels[1]}\" : 54321 }} }}"; - - var expectedResponse = JObject.Parse(responseJson); - - var client = CreateQueryTestKeenClient(queryParameters, expectedResponse); - - var actualResults = await client.Queries.MultiAnalysis( - queryParameters.EventCollection, - queryParameters.GetMultiAnalysisParameters(), - timeframe: null, - filters: null, - timezone: null); - - var expectedResults = expectedResponse["result"]; - - Assert.AreEqual(expectedResults.Count(), actualResults.Count()); - foreach (var label in queryParameters.Labels) - { - // Validate the result is correct - Assert.AreEqual(expectedResults[label].Value(), int.Parse(actualResults[label])); - } - } - - [Test] - public async Task Query_SimpleMultiAnalysisGroupBy_Success() - { - var queryParameters = new MultiAnalysisParameters() - { - Labels = new string[] - { - "first analysis", - "second analysis" - }, - Analyses = new QueryParameters[] - { - new QueryParameters(), - new QueryParameters() - { - Analysis = QueryType.Average(), - TargetProperty = "targetProperty" - } - }, - GroupBy = "groupByProperty" - }; - - string responseJson = $"{{\"result\":[" + - $"{{\"{queryParameters.GroupBy}\":\"group1\",\"{queryParameters.Labels[0]}\":12345,\"{queryParameters.Labels[1]}\":54321}}," + - $"{{\"{queryParameters.GroupBy}\":\"group2\",\"{queryParameters.Labels[0]}\":67890,\"{queryParameters.Labels[1]}\":9876}}" + - $"]}}"; - - var expectedResponse = JObject.Parse(responseJson); - - var client = CreateQueryTestKeenClient(queryParameters, expectedResponse); - - var actualResults = await client.Queries.MultiAnalysis( - queryParameters.EventCollection, - queryParameters.GetMultiAnalysisParameters(), - null, - null, - queryParameters.GroupBy, - null); - - var expectedResults = expectedResponse["result"]; - - Assert.AreEqual(expectedResults.Count(), actualResults.Count()); - foreach (var group in new string[] { "group1", "group2" }) - { - var actualGroupResult = actualResults.Where((result) => result.Group == group).First(); - var expectedGroupResult = expectedResults.Where((result) => result[queryParameters.GroupBy].Value() == group).First(); - foreach (var label in queryParameters.Labels) - { - // Validate the result is correct - Assert.AreEqual(expectedGroupResult[label].Value(), int.Parse(actualGroupResult.Value[label])); - } - } - } - - [Test] - public async Task Query_SimpleMultiAnalysisInterval_Success() - { - var queryParameters = new MultiAnalysisParameters() - { - Labels = new string[] - { - "first analysis", - "second analysis" - }, - Analyses = new QueryParameters[] - { - new QueryParameters(), - new QueryParameters() - { - Analysis = QueryType.Average(), - TargetProperty = "targetProperty" - } - }, - Interval = QueryInterval.Daily() - }; - - string responseJson = "{\"result\":[" + - "{\"timeframe\":{\"start\":\"2017-10-14T00:00:00.000Z\",\"end\":\"2017-10-15T00:00:00.000Z\"}," + - "\"value\":{" + - $"\"{queryParameters.Labels[0]}\":12345,\"{queryParameters.Labels[1]}\":54321}}" + - "}," + - "{\"timeframe\":{\"start\":\"2017-10-15T00:00:00.000Z\",\"end\":\"2017-10-16T00:00:00.000Z\"}," + - "\"value\":{" + - $"\"{queryParameters.Labels[0]}\":123,\"{queryParameters.Labels[1]}\":321}}" + - "}" + - "]}"; - - var expectedResponse = JObject.Parse(responseJson); - - var client = CreateQueryTestKeenClient(queryParameters, expectedResponse); - - var actualResults = await client.Queries.MultiAnalysis( - queryParameters.EventCollection, - queryParameters.GetMultiAnalysisParameters(), - timeframe: null, - interval: queryParameters.Interval, - filters: null, - timezone: null); - - var expectedResults = expectedResponse["result"]; - - Assert.AreEqual(expectedResults.Count(), actualResults.Count()); - var actualResultsEnumerator = actualResults.GetEnumerator(); - foreach (var expectedResult in expectedResults) - { - actualResultsEnumerator.MoveNext(); - var actualResult = actualResultsEnumerator.Current; - - Assert.AreEqual(DateTime.Parse(expectedResult["timeframe"]["start"].Value()), actualResult.Start); - Assert.AreEqual(DateTime.Parse(expectedResult["timeframe"]["end"].Value()), actualResult.End); - - foreach (var label in queryParameters.Labels) - { - // Validate the result is correct - Assert.AreEqual(expectedResult["value"][label].Value(), int.Parse(actualResult.Value[label])); - } - } - } - - [Test] - public async Task Query_SimpleMultiAnalysisIntervalGroupBy_Success() - { - var queryParameters = new MultiAnalysisParameters() - { - Labels = new string[] - { - "first analysis", - "second analysis" - }, - Analyses = new QueryParameters[] - { - new QueryParameters(), - new QueryParameters() - { - Analysis = QueryType.Average(), - TargetProperty = "targetProperty" - } - }, - GroupBy = "groupByProperty", - Interval = QueryInterval.Daily() - }; - - string responseJson = "{\"result\":[" + - "{\"timeframe\":{\"start\":\"2017-10-14T00:00:00.000Z\",\"end\":\"2017-10-15T00:00:00.000Z\"}," + - "\"value\":[" + - $"{{\"{queryParameters.GroupBy}\":\"group1\",\"{queryParameters.Labels[0]}\":12345,\"{queryParameters.Labels[1]}\":54321}}," + - $"{{\"{queryParameters.GroupBy}\":\"group2\",\"{queryParameters.Labels[0]}\":67890,\"{queryParameters.Labels[1]}\":9876}}" + - "]}," + - "{\"timeframe\":{\"start\":\"2017-10-15T00:00:00.000Z\",\"end\":\"2017-10-16T00:00:00.000Z\"}," + - "\"value\":[" + - $"{{\"{queryParameters.GroupBy}\":\"group1\",\"{queryParameters.Labels[0]}\":123,\"{queryParameters.Labels[1]}\":321}}," + - $"{{\"{queryParameters.GroupBy}\":\"group2\",\"{queryParameters.Labels[0]}\":456,\"{queryParameters.Labels[1]}\":654}}" + - "]}" + - "]}"; - - var expectedResponse = JObject.Parse(responseJson); - - var client = CreateQueryTestKeenClient(queryParameters, expectedResponse); - - var actualResults = await client.Queries.MultiAnalysis( - queryParameters.EventCollection, - queryParameters.GetMultiAnalysisParameters(), - groupby: queryParameters.GroupBy, - interval: queryParameters.Interval); - - var expectedResults = expectedResponse["result"]; - - Assert.AreEqual(expectedResults.Count(), actualResults.Count()); - var actualResultsEnumerator = actualResults.GetEnumerator(); - foreach (var expectedResult in expectedResults) - { - actualResultsEnumerator.MoveNext(); - var actualResult = actualResultsEnumerator.Current; - - Assert.AreEqual(DateTime.Parse(expectedResult["timeframe"]["start"].Value()), actualResult.Start); - Assert.AreEqual(DateTime.Parse(expectedResult["timeframe"]["end"].Value()), actualResult.End); - - foreach (var group in new string[] { "group1", "group2" }) - { - var expectedGroupResult = expectedResult["value"].Where((groupResult) => groupResult[queryParameters.GroupBy].Value() == group).First(); - var actualGroupResult = actualResult.Value.Where((groupResult) => groupResult.Group == group).First(); - - foreach (var label in queryParameters.Labels) - { - Assert.AreEqual(expectedGroupResult[label].Value(), int.Parse(actualGroupResult.Value[label])); - } - } - } - } - } -} diff --git a/Keen.NET.Test/ScopedKeyTest.cs b/Keen.NET.Test/ScopedKeyTest.cs deleted file mode 100644 index e2e667e..0000000 --- a/Keen.NET.Test/ScopedKeyTest.cs +++ /dev/null @@ -1,207 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using NUnit.Framework; -using Keen.Core; -using Newtonsoft.Json.Linq; -using System.Diagnostics; -using System.Dynamic; -using System.Collections; - -namespace Keen.Net.Test -{ - [TestFixture] - public class ScopedKeyTest : TestBase - { - [Test] - public void Encrypt_32CharKey_Success() - { - Assert.DoesNotThrow(() => ScopedKey.Encrypt("0123456789abcdef0123456789abcdef", null)); - } - - [Test] - public void Encrypt_64CharKey_Success() - { - Assert.DoesNotThrow(() => ScopedKey.Encrypt("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", null)); - } - - [Test] - public void Encrypt_32CharKeyWithFilter_Success() - { - var settings = new ProjectSettingsProvider("projId", "0123456789abcdef0123456789abcdef"); - - IDictionary filter = new ExpandoObject(); - filter.Add("property_name", "account_id"); - filter.Add("operator", "eq"); - filter.Add("property_value", 123); - - dynamic secOpsIn = new ExpandoObject(); - secOpsIn.filters = new List() { filter }; - secOpsIn.allowed_operations = new List() { "read" }; - Assert.DoesNotThrow(() => - { - var scopedKey = ScopedKey.Encrypt(settings.MasterKey, (object)secOpsIn); - var decrypted = ScopedKey.Decrypt(settings.MasterKey, scopedKey); - var secOpsOut = JObject.Parse(decrypted); - Assert.True(secOpsIn.allowed_operations[0] == (string)(secOpsOut["allowed_operations"].First())); - }); - } - - [Test] - public void Encrypt_64CharKeyWithFilter_Success() - { - var settings = new ProjectSettingsProvider("projId", "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"); - - IDictionary filter = new ExpandoObject(); - filter.Add("property_name", "account_id"); - filter.Add("operator", "eq"); - filter.Add("property_value", 123); - - dynamic secOpsIn = new ExpandoObject(); - secOpsIn.filters = new List() { filter }; - secOpsIn.allowed_operations = new List() { "read" }; - Assert.DoesNotThrow(() => - { - var scopedKey = ScopedKey.Encrypt(settings.MasterKey, (object)secOpsIn); - var decrypted = ScopedKey.Decrypt(settings.MasterKey, scopedKey); - var secOpsOut = JObject.Parse(decrypted); - Assert.True(secOpsIn.allowed_operations[0] == (string)(secOpsOut["allowed_operations"].First())); - }); - } - - [Test] - public void Encrypt_NullObject_Success() - { - Assert.DoesNotThrow(() => ScopedKey.Encrypt("0123456789ABCDEF0123456789ABCDEF", null)); - } - - [Test] - public void Encrypt_NullKey_Throws() - { - Assert.Throws(() => ScopedKey.Encrypt(null, new { X = "X" })); - } - - public void Encrypt_BlankKey_Throws() - { - Assert.Throws(() => ScopedKey.Encrypt("", new { X = "X" })); - } - - [Test] - public void Encrypt_PopulatedObject_Success() - { - Assert.DoesNotThrow(() => - { - var settings = new ProjectSettingsProviderEnv(); - - dynamic secOps = new ExpandoObject(); - - IDictionary filter = new ExpandoObject(); - filter.Add("property_name", "account_id" ); - filter.Add("operator", "eq" ); - filter.Add("property_value", 123 ); - secOps.filters = new List(){ filter }; - secOps.allowed_operations = new List(){ "read" }; - - var scopedKey = ScopedKey.Encrypt(settings.MasterKey, (object)secOps); - }); - } - - [Test] - public void RoundTrip_PopulatedObject_Success() - { - var settings = new ProjectSettingsProviderEnv(); - - IDictionary filter = new ExpandoObject(); - filter.Add("property_name", "account_id"); - filter.Add("operator", "eq"); - filter.Add("property_value", 123); - - dynamic secOpsIn = new ExpandoObject(); - secOpsIn.filters = new List() { filter }; - secOpsIn.allowed_operations = new List() { "read" }; - Assert.DoesNotThrow(() => - { - var scopedKey = ScopedKey.Encrypt(settings.MasterKey, (object)secOpsIn); - var decrypted = ScopedKey.Decrypt(settings.MasterKey, scopedKey); - var secOpsOut = JObject.Parse(decrypted); - Assert.True(secOpsIn.allowed_operations[0] == (string)(secOpsOut["allowed_operations"].First())); - }); - } - - [Test] - public void RoundTrip_PopulatedObject_WithIV_Success() - { - var settings = new ProjectSettingsProviderEnv(); - var IV = "C0FFEEC0FFEEC0FFEEC0FFEEC0FFEEC0"; - - IDictionary filter = new ExpandoObject(); - filter.Add("property_name", "account_id"); - filter.Add("operator", "eq"); - filter.Add("property_value", 123); - - dynamic secOpsIn = new ExpandoObject(); - secOpsIn.filters = new List() { filter }; - secOpsIn.allowed_operations = new List() { "read" }; - Assert.DoesNotThrow(() => - { - var scopedKey = ScopedKey.Encrypt(settings.MasterKey, (object)secOpsIn, IV); - var decrypted = ScopedKey.Decrypt(settings.MasterKey, scopedKey); - var secOpsOut = JObject.Parse(decrypted); - Assert.True(secOpsIn.allowed_operations[0] == (string)(secOpsOut["allowed_operations"].First())); - }); - } - - [Test] - public void Decrypt_WriteKey_Success() - { - const string plainText = "{\"filters\": [{\"property_name\": \"vendor_id\",\"operator\": \"eq\",\"property_value\": \"abc\"}],\"allowed_operations\": [ \"write\" ]}"; - var testKey = SettingsEnv.MasterKey; - var cryptText = SettingsEnv.ReadKey; - - if (UseMocks) - { - cryptText = - "BAA51D1D03D49C1159E7298762AAC26493B20F579988E1EDA4613305F08E01CB702886F0FCB5312E5E18C6315A8049700816CA35BD952C75EB694AAA4A95535EE13CD9D5D8C97A215B4790638EA1DA3DB9484A0133D5289E2A22D5C2952E1F708540722EA832B093E147495A70ADF534242E961FDE3F0275E20D58F22B23F4BAE2A61518CB943818ABEF547DD68F68FE"; - testKey = "0123456789ABCDEF0123456789ABCDEF"; // ensure the key matches what cryptText was encrypted with - } - - var decrypted = ScopedKey.Decrypt(testKey, cryptText); - if (UseMocks) - Assert.True(decrypted.Equals(plainText)); - else - Assert.That(decrypted.IndexOf("timestamp") > 0); - } - - [Test] - public void Roundtrip_RndIV_Success() - { - const string vendorGuid = "abc"; - const bool isRead = false; - - var str = "{\"filters\": [{\"property_name\": \"vendor_id\",\"operator\": \"eq\",\"property_value\": \"VENDOR_GUID\"}],\"allowed_operations\": [ \"READ_OR_WRITE\" ]}"; - - str = str.Replace("VENDOR_GUID", vendorGuid); - - str = str.Replace("READ_OR_WRITE", isRead ? "read" : "write"); - - var rnd = new System.Security.Cryptography.RNGCryptoServiceProvider(); - var bytes = new byte[16]; - rnd.GetBytes(bytes); - - var iv = String.Concat(bytes.Select(b => b.ToString("X2"))); Trace.WriteLine("IV: " + iv); - - Trace.WriteLine("plaintext: " + str); - var scopedKey = ScopedKey.EncryptString(SettingsEnv.MasterKey, str, iv); - Trace.WriteLine("encrypted: " + scopedKey); - var decrypted = ScopedKey.Decrypt(SettingsEnv.MasterKey, scopedKey); - Trace.WriteLine("decrypted: " + decrypted); - - // Make sure the input string exactly matches the decrypted string. This input isn't of - // a length that is a multiple of block size or key size, so this would have required - // manual padding in the past. The decrypted string shouldn't have any padding now. - Assert.AreEqual(str, decrypted); - } - } -} diff --git a/Keen.NET.Test/TestKeenHttpClientProvider.cs b/Keen.NET.Test/TestKeenHttpClientProvider.cs deleted file mode 100644 index c518af2..0000000 --- a/Keen.NET.Test/TestKeenHttpClientProvider.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Keen.Core; -using System; - - -namespace Keen.Net.Test -{ - /// - /// An implementation of that behaves just like - /// by default, but allows for easily overriding this - /// behavior with a Func<> for use in tests. - /// - internal class TestKeenHttpClientProvider : IKeenHttpClientProvider - { - internal Func ProvideKeenHttpClient = - (url) => KeenHttpClientFactory.Create(url, HttpClientCache.Instance); - - - public IKeenHttpClient GetForUrl(Uri baseUrl) - { - return ProvideKeenHttpClient(baseUrl); - } - } -} diff --git a/Keen.NET.Test/TimeframeConverterTest.cs b/Keen.NET.Test/TimeframeConverterTest.cs deleted file mode 100644 index cf541f4..0000000 --- a/Keen.NET.Test/TimeframeConverterTest.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using NUnit.Framework; -using Newtonsoft.Json; -using Keen.Core.Query; - -namespace Keen.NetStandard.Test -{ - [TestFixture] - class TimeframeConverterTest - { - [Test] - public void Test_TimeframeConverter_Serializes() - { - var timeframeString = "this_4_hours"; - var timeframe = QueryRelativeTimeframe.Create(timeframeString); - - string json = JsonConvert.SerializeObject(timeframe); - - Assert.AreEqual($"\"{timeframeString}\"", json); - } - - [Test] - public void Test_TimeframeConverter_Deserializes() - { - var timeframeString = "this_4_hours"; - var timeframeJson = $"\"{timeframeString}\""; - - var timeframe = JsonConvert.DeserializeObject(timeframeJson); - - Assert.AreEqual(timeframeString, timeframe.ToString()); - } - } -} diff --git a/Keen.NET.Test/UrlToMessageHandler.cs b/Keen.NET.Test/UrlToMessageHandler.cs deleted file mode 100644 index 5683ba9..0000000 --- a/Keen.NET.Test/UrlToMessageHandler.cs +++ /dev/null @@ -1,91 +0,0 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading; -using System.Threading.Tasks; - - -namespace Keen.Net.Test -{ - /// - /// An that matches request URIs with other - /// IHttpMessageHandler instances. If configured as such, matching can be done such that when - /// a precise match for a URL isn't found, any base URL entry will be used. This would mean one - /// could match, say, any queries under a certain project ID without specifying all the - /// query parameters, if that's useful. Another entry could match events, for example. By - /// default, if no strict or loose match is found, this will try to forward to DefaultAsync, so - /// either make sure that is set by a wrapper or explicitly, or configure to not defer to - /// the default action. - /// - /// This is handy to set up some validation and return canned responses for sets of URLs. - /// - /// By sticking IHttpMessageHandler instances in this mapping, their DefaultAsync properties - /// get forwarded to the DefaultAsync of this handler. That means if a match is found, and the - /// handler used decides to call DefaultAsync, it will call this handler's default action, - /// bypassing DeferToDefault, so make sure the contained handlers know whether or not to call - /// default. TODO : Evaluate this behavior since maybe the defaults here are confusing. - /// - /// - internal class UrlToMessageHandler : IHttpMessageHandler - { - private readonly IDictionary _urlsToHandlers; - - public Func> DefaultAsync { get; set; } - - internal bool DeferToDefault { get; set; } = true; - - internal bool MatchBaseUrls { get; set; } = true; - - internal UrlToMessageHandler(IDictionary urlsToHandlers) - { - _urlsToHandlers = new Dictionary(urlsToHandlers); - - foreach (var handler in _urlsToHandlers.Values) - { - // Lazily dispatch to whatever our default handler gets set to. - // Be careful because trying to reuse handler instances will lead to strange - // outcomes w.r.t. the default behavior of these IHttpMessageHandlers. - handler.DefaultAsync = (request, ct) => DefaultAsync(request, ct); - } - } - - public async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - HttpResponseMessage response = null; - IHttpMessageHandler handler = null; - - // First check for a perfect match, or if there's a key that's a base url of the request - // url, match it if client code chooses to accept that. - if (_urlsToHandlers.TryGetValue(request.RequestUri, out handler) || - (MatchBaseUrls && null != (handler = _urlsToHandlers.FirstOrDefault( - entry => entry.Key.IsBaseOf(request.RequestUri)).Value))) - { - response = - await handler.SendAsync(request, cancellationToken).ConfigureAwait(false); - } - else if (DeferToDefault) - { - response = await DefaultAsync(request, cancellationToken).ConfigureAwait(false); - } - else - { - Console.WriteLine(string.Format("WARNING: No validator found for absolute URI: {0}", - request.RequestUri.AbsoluteUri)); - - // No handler found, so return 404 - response = await HttpTests.CreateJsonStringResponseAsync( - HttpStatusCode.NotFound, "Resource not found.", "ResourceNotFoundError") - .ConfigureAwait(false); - } - - return response; - } - } -} diff --git a/Keen.NET.Test/app.config b/Keen.NET.Test/app.config deleted file mode 100644 index 940d25c..0000000 --- a/Keen.NET.Test/app.config +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/Keen.NET.Test/packages.config b/Keen.NET.Test/packages.config deleted file mode 100644 index 6a56d7e..0000000 --- a/Keen.NET.Test/packages.config +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/Keen.NET_35.Test/AddOnsTest.cs b/Keen.NET_35.Test/AddOnsTest.cs deleted file mode 100644 index 5c2841e..0000000 --- a/Keen.NET_35.Test/AddOnsTest.cs +++ /dev/null @@ -1,109 +0,0 @@ -using System; -using System.Collections.Generic; -using Keen.NET_35.DataEnrichment; -using NUnit.Framework; - -namespace Keen.NET_35.Test -{ - [TestFixture] - public class AddOnsTest : TestBase - { - [Test] - public void IpToGeo_Send_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: (c, e, p) => - { - if (!e["keen"].ToString().Contains("keen:ip_to_geo")) - throw new Exception("Unexpected values"); - }); - - var a = AddOn.IpToGeo("an_ip", "geocode"); - - Assert.DoesNotThrow(() => client.AddEvent("AddOnTest", new {an_ip = "70.187.8.97"}, new List {a})); - } - - [Test] - public void IpToGeo_MissingInput_Throws() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: (c, e, p) => - { - if (!e["keen"].ToString().Contains("\"ip\": \"an_ip\"")) - throw new KeenException("Unexpected values"); - }); - - var a = AddOn.IpToGeo("wrong_field", "geocode"); - - Assert.Throws( - () => client.AddEvent("AddOnTest", new {an_ip = "70.187.8.97"}, new List {a})); - } - - - [Test] - public void UserAgentParser_Send_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: (c, e, p) => - { - if (!e["keen"].ToString().Contains("keen:ua_parser")) - throw new Exception("Unexpected values"); - }); - - var a = AddOn.UserAgentParser("user_agent_string", "user_agent_parsed"); - - Assert.DoesNotThrow( - () => - client.AddEvent("AddOnTest", - new - { - user_agent_string = - "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36" - }, new List {a})); - } - - [Test] - public void UrlParser_Send_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: (c, e, p) => - { - if (!e["keen"].ToString().Contains("keen:url_parser")) - throw new Exception("Unexpected values"); - }); - - var a = AddOn.UrlParser("url", "url_parsed"); - - Assert.DoesNotThrow( - () => - client.AddEvent("AddOnTest", - new {url = "https://keen.io/docs/data-collection/data-enrichment/#anchor"}, new List {a})); - } - - [Test] - public void ReferrerParser_Send_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: (c, e, p) => - { - if (!e["keen"].ToString().Contains("keen:referrer_parser")) - throw new Exception("Unexpected values"); - }); - - var a = AddOn.ReferrerParser("referrer", "page", "referrer_parsed"); - - Assert.DoesNotThrow(() => client.AddEvent("AddOnTest", new {page = "", referrer = ""}, new List {a})); - } - - } -} diff --git a/Keen.NET_35.Test/EventCollectionMock.cs b/Keen.NET_35.Test/EventCollectionMock.cs deleted file mode 100644 index 8bf5cbb..0000000 --- a/Keen.NET_35.Test/EventCollectionMock.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using Newtonsoft.Json.Linq; - -namespace Keen.NET_35.Test -{ - /// - /// EventCollectionMock provides an implementation of IEventCollection 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 - /// IEventCollection for each test. - /// - class EventCollectionMock : IEventCollection - { - private readonly IProjectSettings _settings; - private readonly Func _getSchema; - private readonly Action _deleteCollection; - private readonly Action _addEvent; - - public JObject GetSchema(string collection) - { - return _getSchema(collection, _settings); - } - - public void DeleteCollection(string collection) - { - _deleteCollection(collection, _settings); - } - - public void AddEvent(string collection, JObject anEvent) - { - _addEvent(collection, anEvent, _settings); - } - - public EventCollectionMock(IProjectSettings prjSettings, - Func getSchema = null, - Action deleteCollection = null, - Action addEvent = null) - { - _settings = prjSettings; - _getSchema = getSchema ?? ((s, p) => new JObject()); - _deleteCollection = deleteCollection ?? ((s, p) => { }); - _addEvent = addEvent ?? ((s, p, e) => { }); - } - } -} diff --git a/Keen.NET_35.Test/EventMock.cs b/Keen.NET_35.Test/EventMock.cs deleted file mode 100644 index 6c8eff1..0000000 --- a/Keen.NET_35.Test/EventMock.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; - -namespace Keen.NET_35.Test -{ - /// - /// EventMock provides an implementation of IEvent 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 - /// IEvent for each test. - /// - class EventMock : IEvent - { - private readonly IProjectSettings _settings; - private readonly Func _getSchemas; - private readonly Func> _addEvents; - - public JArray GetSchemas() - { - return _getSchemas(_settings); - } - - public IEnumerable AddEvents(JObject events) - { - return _addEvents(events, _settings); - } - - public EventMock(IProjectSettings prjSettings, - Func getSchemas = null, - Func> addEvents = null) - { - _settings = prjSettings; - _getSchemas = getSchemas ?? (p => new JArray()); - _addEvents = addEvents ?? ((p, e) => new List()); - } - } -} diff --git a/Keen.NET_35.Test/Keen.NET_35.Test.csproj b/Keen.NET_35.Test/Keen.NET_35.Test.csproj deleted file mode 100644 index ca9cbfd..0000000 --- a/Keen.NET_35.Test/Keen.NET_35.Test.csproj +++ /dev/null @@ -1,161 +0,0 @@ - - - - Debug - AnyCPU - {2EF6BF24-A068-47E2-ABBF-DE2666607D65} - Library - Properties - Keen.NET_35.Test - Keen.NET_35.Test - v4.0 - 512 - {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 10.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages - False - UnitTest - - - - 12.0 - - false - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - true - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - bin\netfx\ - TRACE - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - - - - ..\packages\Moq.4.2.1510.2205\lib\net40\Moq.dll - True - - - ..\packages\Newtonsoft.Json.9.0.1\lib\net40\Newtonsoft.Json.dll - True - - - ..\packages\NUnit.3.2.0\lib\net40\nunit.framework.dll - True - - - - - - - - - - - - - - Properties\SharedAssemblyInfo.cs - - - Properties\SharedVersionInfo.cs - - - - - - - - - - - - - - False - Microsoft .NET Framework 4 %28x86 and x64%29 - true - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - false - - - False - Windows Installer 4.5 - true - - - - - {a395c070-f971-48d5-a3de-9263032cfb84} - Keen.NET_35 - - - - - - - False - - - False - - - False - - - False - - - - - - - - \ No newline at end of file diff --git a/Keen.NET_35.Test/KeenClientTest.cs b/Keen.NET_35.Test/KeenClientTest.cs deleted file mode 100644 index e764bb2..0000000 --- a/Keen.NET_35.Test/KeenClientTest.cs +++ /dev/null @@ -1,737 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Moq; -using NUnit.Framework; -using Newtonsoft.Json.Linq; - -namespace Keen.NET_35.Test -{ - public class TestBase - { - public static bool UseMocks = true; - public IProjectSettings SettingsEnv; - - [OneTimeSetUp] - public virtual void Setup() - { - if (UseMocks) - SetupEnv(); - SettingsEnv = new ProjectSettingsProviderEnv(); - } - - [OneTimeTearDown] - public virtual void TearDown() - { - if (UseMocks) - ResetEnv(); - } - - public static void SetupEnv() - { - foreach (var s in new[] { "KEEN_PROJECT_ID", "KEEN_MASTER_KEY", "KEEN_WRITE_KEY", "KEEN_READ_KEY" }) - Environment.SetEnvironmentVariable(s, "0123456789ABCDEF0123456789ABCDEF"); - } - - public static void ResetEnv() - { - foreach (var s in new[] { "KEEN_PROJECT_ID", "KEEN_MASTER_KEY", "KEEN_WRITE_KEY", "KEEN_READ_KEY" }) - Environment.SetEnvironmentVariable(s, null); - } - - } - - [TestFixture] - public class BulkEventTest : TestBase - { - [Test] - public void AddEvents_InvalidProject_Throws() - { - var settings = new ProjectSettingsProvider(projectId: "X", writeKey: SettingsEnv.WriteKey); - var client = new KeenClient(settings); - if (UseMocks) - client.Event = new EventMock(settings, - addEvents: (e, p) => - { - if ((p == settings) - &&(p.ProjectId=="X")) - throw new KeenResourceNotFoundException(); - throw new Exception("Unexpected value"); - }); - Assert.Throws(() => client.AddEvents("AddEventTest", new []{ new {AProperty = "AValue" }})); - } - - - //[Test] - //public void AddEvents_PartialFailure_Throws() - //{ - // var client = new KeenClient(SettingsEnv); - // if (UseMocks) - // client.Event = new EventMock(SettingsEnv, - // addEvents: (e, p) => - // { - // var err = e.SelectToken("$.AddEventTest[2]") as JObject; - // if (null == err) - // throw new Exception("Unexpected error, test data not found"); - - // return new List {new CachedEvent("AddEventTest", e)}; - // }); - - // object invalidEvent = new ExpandoObject(); - // ((IDictionary)invalidEvent).Add("$" + new string('.', 260), "AValue"); - - // var events = (from i in Enumerable.Range(1, 2) - // select new { AProperty = "AValue" + i }).ToList(); - // events.Add(invalidEvent); - - // Assert.Throws(() => client.AddEvents("AddEventTest", events)); - //} - - [Test] - public void AddEvents_NoCache_Success() - { - var client = new KeenClient(SettingsEnv); - var done = false; - if (UseMocks) - client.Event = new EventMock(SettingsEnv, - addEvents: (e, p) => - { - done = true; - Assert.True(p == SettingsEnv, "Incorrect settings"); - Assert.NotNull(e.Property("AddEventTest"), "Expected collection not found"); - Assert.AreEqual(e.Property("AddEventTest").Value.AsEnumerable().Count(), 3, "Expected exactly 3 collection members"); - foreach (var q in e["AddEventTest"]) - Assert.NotNull(q["keen"]["timestamp"], "keen.timestamp properties should exist"); - return new List(); - }); - - var events = from i in Enumerable.Range(1,3) - select new { AProperty = "AValue" + i}; - - Assert.DoesNotThrow(() => client.AddEvents("AddEventTest", events)); - Assert.True((UseMocks && done) || !UseMocks, "AddEvent mock was not called"); - } - - [Test] - public void AddEvents_Cache_Success() - { - var client = new KeenClient(SettingsEnv, new EventCacheMemory()); - if (UseMocks) - client.Event = new EventMock(SettingsEnv, - addEvents: (e, p) => - { - // Should not be called with caching enabled - Assert.Fail(); - return new List(); - }); - - var events = from i in Enumerable.Range(1, 3) - select new { AProperty = "AValue" + i }; - - Assert.DoesNotThrow(() => client.AddEvents("AddEventTest", events)); - - // reset the AddEvents mock - if (UseMocks) - client.Event = new EventMock(SettingsEnv, - addEvents: (e, p) => - { - Assert.True(p == SettingsEnv, "Incorrect settings"); - Assert.NotNull(e.Property("AddEventTest"), "Expected collection not found"); - Assert.AreEqual(e.Property("AddEventTest").Value.AsEnumerable().Count(), 3, "Expected exactly 3 collection members"); - foreach (var q in e["AddEventTest"]) - Assert.NotNull(q["keen"]["timestamp"], "keen.timestamp properties should exist"); - return new List(); - }); - Assert.DoesNotThrow(client.SendCachedEvents); - } - } - - [TestFixture] - public class KeenClientTest : TestBase - { - [Test] - public void Constructor_ProjectSettingsNull_Throws() - { - Assert.Throws(() => new KeenClient(null)); - } - - [Test] - public void Constructor_ProjectSettingsNoProjectID_Throws() - { - var settings = new ProjectSettingsProvider(projectId: "", masterKey: "X", writeKey: "X"); - Assert.Throws(() => new KeenClient(settings)); - } - - [Test] - public void Constructor_ProjectSettingsNoMasterOrWriteKeys_Throws() - { - var settings = new ProjectSettingsProvider(projectId: "X"); - Assert.Throws(() => new KeenClient(settings)); - } - - [Test] - public void GetCollectionSchema_NullProjectId_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.Throws(() => client.GetSchema(null)); - } - - [Test] - public void GetCollectionSchema_EmptyProjectId_Throws() - { - var settings = new ProjectSettingsProvider("X", SettingsEnv.MasterKey); - var client = new KeenClient(settings); - Assert.Throws(() => client.GetSchema("")); - } - - [Test] - public void GetCollectionSchemas_Success() - { - var client = new KeenClient(SettingsEnv); - - var expected = new JArray(); // todo: better mock return - if (UseMocks) - { - var eventMock = new Mock(); - eventMock.Setup(m => m.GetSchemas()) - .Returns(()=>expected); - - client.Event = eventMock.Object; - } - - var reply = client.GetSchemas(); - Assert.NotNull(reply); - } - - [Test] - public void GetCollectionSchema_InvalidProjectId_Throws() - { - var settings = new ProjectSettingsProvider("X", SettingsEnv.MasterKey); - var client = new KeenClient(settings); - if (UseMocks) - client.EventCollection = new EventCollectionMock(settings, - getSchema: (c, p) => - { - if ((p == settings) && (c == "X")) - throw new KeenException(); - throw new Exception("Unexpected value"); - }); - Assert.Throws(() => client.GetSchema("X")); - } - - [Test] - public void GetCollectionSchema_ValidProjectIdInvalidSchema_Throws() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - getSchema: (c, p) => - { - if ((p == SettingsEnv) && (c == "DoesntExist")) - throw new KeenException(); - return new JObject(); - }); - - Assert.Throws(() => client.GetSchema("DoesntExist")); - } - - [Test] - public void GetCollectionSchema_ValidProject_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - getSchema: (c, p) => - { - if ((p == SettingsEnv) && (c == "AddEventTest")) - return JObject.Parse("{\"properties\":{\"AProperty\": \"string\"}}"); - throw new KeenResourceNotFoundException(c); - }); - - Assert.DoesNotThrow(() => - { - var response = client.GetSchema("AddEventTest"); - Assert.NotNull(response["properties"]); - Assert.NotNull(response["properties"]["AProperty"]); - Assert.True((string)response["properties"]["AProperty"] == "string"); - }); - } - - [Test] - public void AddEvent_InvalidProjectId_Throws() - { - var settings = new ProjectSettingsProvider(projectId: "X", writeKey: SettingsEnv.WriteKey); - var client = new KeenClient(settings); - if (UseMocks) - client.EventCollection = new EventCollectionMock(settings, - addEvent: (c, e, p) => - { - if ((p == settings) && (c == "X") && (e["X"].Value() == "X")) - throw new KeenResourceNotFoundException(c); - }); - - Assert.Throws(() => client.AddEvent("X", new { X = "X" })); - } - - [Test] - public void AddEvent_ValidProjectIdInvalidWriteKey_Throws() - { - var settings = new ProjectSettingsProvider(projectId: SettingsEnv.ProjectId, writeKey: "X"); - var client = new KeenClient(settings); - if (UseMocks) - client.EventCollection = new EventCollectionMock(settings, - addEvent: (c, e, p) => - { - if ((p == settings) && (c == "X") && (e["X"].Value() == "X")) - throw new KeenInvalidApiKeyException(c); - }); - Assert.Throws(() => client.AddEvent("X", new { X = "X" })); - } - - [Test] - public void AddEvent_InvalidCollectionNameBlank_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.Throws(() => client.AddEvent("", new { AProperty = "AValue" })); - } - - [Test] - public void AddEvent_InvalidCollectionNameNull_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.Throws(() => client.AddEvent(null, new { AProperty = "AValue" })); - } - - [Test] - public void AddEvent_InvalidCollectionNameDollarSign_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.Throws(() => client.AddEvent("$Invalid", new { AProperty = "AValue" })); - } - - [Test] - public void AddEvent_InvalidCollectionNameTooLong_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.Throws(() => client.AddEvent(new String('X', 257), new { AProperty = "AValue" })); - } - - [Test] - public void AddEvent_InvalidKeenNamespaceProperty_Throws() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: (c,e,p) => - { - if ((p == SettingsEnv) - && (c == "X") - && (null != e.Property("keen")) - && (e.Property("keen").Value.GetType()==typeof(JObject)) - && (null!=((JObject)e.Property("keen").Value).Property("AProperty"))) - throw new KeenInvalidKeenNamespacePropertyException(); - throw new Exception("Unexpected value"); - }); - - Assert.Throws(() => client.AddEvent("X", new { keen = new { AProperty = "AValue" } })); - } - - [Test] - public void AddEvent_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: (c, e, p) => - { - if ((p == SettingsEnv) && (c == "AddEventTest") && (e["AProperty"].Value()=="AValue")) - return; - throw new Exception("Unexpected values"); - }); - Assert.DoesNotThrow(() => client.AddEvent("AddEventTest", new { AProperty = "AValue" })); - } - - [Test] - public void AddEvent_ScopedKeyWrite_Success() - { - const string scope = "{\"timestamp\": \"2014-02-25T22:09:27.320082\", \"allowed_operations\": [\"write\"]}"; - var scopedKey = ScopedKey.EncryptString(SettingsEnv.MasterKey, scope); - var settings = new ProjectSettingsProvider(masterKey: SettingsEnv.MasterKey, projectId: SettingsEnv.ProjectId, writeKey: scopedKey); - - var client = new KeenClient(settings); - - if (UseMocks) - client.EventCollection = new EventCollectionMock(settings, - addEvent: (c, e, p) => - { - var key = JObject.Parse(ScopedKey.Decrypt(p.MasterKey, p.WriteKey)); - - if ((key["allowed_operations"].Values().First() == "write") && (p == settings) && (c == "AddEventTest") && (e["AProperty"].Value() == "CustomKey")) - return; - - throw new Exception("Unexpected value"); - }); - - Assert.DoesNotThrow(() => client.AddEvent("AddEventTest", new { AProperty = "CustomKey" })); - } - - [Test] - public void DeleteCollection_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - deleteCollection: (c, p) => - { - if ((p == SettingsEnv) && (c == "DeleteColTest")) - return; - throw new Exception("Unexpected value"); - }); - - // Idempotent, does not matter if collection does not exist. - Assert.DoesNotThrow(() => client.DeleteCollection("DeleteColTest")); - } - - [Test] - public void GetSdkVersion_Success() - { - // TODO : Decide on a better place for this when we break out tests and do a CC push. - string sdkVersion = KeenUtil.GetSdkVersion(); - - Assert.IsNotNull(sdkVersion); - Assert.IsNotEmpty(sdkVersion); - Assert.IsTrue(sdkVersion.StartsWith(".net")); - } - } - - [TestFixture] - public class KeenClientGlobalPropertyTests : TestBase - { - [Test] - public void AddGlobalProperty_SimpleValue_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: (c, e, p) => - { - if ((p == SettingsEnv) && (c == "AddEventTest") - && (e["AProperty"].Value() == "AValue") - && (e["AGlobal"].Value() == "AValue")) - return; - throw new Exception("Unexpected value"); - }); - - Assert.DoesNotThrow(() => - { - client.AddGlobalProperty("AGlobal", "AValue"); - client.AddEvent("AddEventTest", new { AProperty = "AValue" }); - }); - } - - [Test] - public void AddGlobalProperty_InvalidValueNameDollar_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.Throws(() => client.AddGlobalProperty("$AGlobal", "AValue")); - } - - [Test] - public void AddGlobalProperty_InvalidValueNamePeriod_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.Throws(() => client.AddGlobalProperty("A.Global", "AValue")); - } - - [Test] - public void AddGlobalProperty_InvalidValueNameLength_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.Throws(() => client.AddGlobalProperty(new String('A', 256), "AValue")); - } - - [Test] - public void AddGlobalProperty_InvalidValueNameNull_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.Throws(() => client.AddGlobalProperty(null, "AValue")); - } - - - [Test] - public void AddGlobalProperty_InvalidValueNameBlank_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.Throws(() => client.AddGlobalProperty("", "AValue")); - } - - [Test] - public void AddGlobalProperty_ObjectValue_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: (c, e, p) => - { - if ((p == SettingsEnv) && (c == "AddEventTest") - && (e["AProperty"].Value() == "AValue") - && (e["AGlobal"]["AProperty"].Value() == "AValue")) - return; - throw new Exception("Unexpected value"); - }); - - Assert.DoesNotThrow(() => - { - client.AddGlobalProperty("AGlobal", new { AProperty = "AValue" }); - client.AddEvent("AddEventTest", new { AProperty = "AValue" }); - }); - } - - [Test] - public void AddGlobalProperty_CollectionValue_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: (c, e, p) => - { - if ((p == SettingsEnv) && (c == "AddEventTest") - && (e["AProperty"].Value() == "AValue") - && (e["AGlobal"].Values().All(x => (x == 1) || (x == 2) || (x == 3)))) - return; - throw new Exception("Unexpected value"); - }); - - Assert.DoesNotThrow(() => - { - client.AddGlobalProperty("AGlobal", new[] { 1, 2, 3}); - client.AddEvent("AddEventTest", new { AProperty = "AValue" }); - }); - } - - [Test] - public void AddGlobalProperty_DelegateSimpleValue_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: (c, e, p) => - { - if ((p == SettingsEnv) && (c == "AddEventTest") - && (e["AProperty"].Value() == "AValue") - && (e["AGlobal"]!=null)) - return; - throw new Exception("Unexpected value"); - }); - - Assert.DoesNotThrow(() => - { - client.AddGlobalProperty("AGlobal", new DynamicPropertyValue(() => DateTime.Now.Millisecond)); - client.AddEvent("AddEventTest", new { AProperty = "AValue" }); - client.AddEvent("AddEventTest", new { AProperty = "AValue" }); - }); - } - - [Test] - public void AddGlobalProperty_DelegateArrayValue_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: (c, e, p) => - { - if ((p == SettingsEnv) && (c == "AddEventTest") - && (e["AProperty"].Value() == "AValue") - && (e["AGlobal"].Values().All(x => (x == 1) || (x == 2) || (x == 3)))) - return; - throw new Exception("Unexpected value"); - }); - - Assert.DoesNotThrow(() => - { - client.AddGlobalProperty("AGlobal", new DynamicPropertyValue(() => new[] { 1, 2, 3 })); - client.AddEvent("AddEventTest", new { AProperty = "AValue" }); - }); - } - - [Test] - public void AddGlobalProperty_DelegateObjectValue_Success() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: (c, e, p) => - { - if ((p == SettingsEnv) && (c == "AddEventTest") - && (e["AProperty"].Value() == "AValue") - && (e["AGlobal"].Value()["SubProp1"].Value() == "Value")) - return; - throw new Exception("Unexpected value"); - }); - - Assert.DoesNotThrow(() => - { - client.AddGlobalProperty("AGlobal", new DynamicPropertyValue(() => new { SubProp1 = "Value", SubProp2 = "Value" })); - client.AddEvent("AddEventTest", new { AProperty = "AValue" }); - }); - } - - [Test] - public void AddGlobalProperty_DelegateNullDynamicValue_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.Throws(() => client.AddGlobalProperty("AGlobal", new DynamicPropertyValue(() => null))); - } - - [Test] - public void AddGlobalProperty_DelegateNullValueProvider_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.Throws(() => client.AddGlobalProperty("AGlobal", null)); - } - - [Test] - public void AddGlobalProperty_DelegateValueProviderNullReturn_Throws() - { - var client = new KeenClient(SettingsEnv); - if (UseMocks) - client.EventCollection = new EventCollectionMock(SettingsEnv, - addEvent: (c, e, p) => - { - if ((p == SettingsEnv) && (c == "AddEventTest") - && (e["AProperty"].Value() == "AValue") - && (e["AGlobal"].Value() == "value")) - return; - throw new Exception("Unexpected value"); - }); - - var i = 0; - // return valid for the first two tests, then null - client.AddGlobalProperty("AGlobal", new DynamicPropertyValue(() => i++ > 1 ? null : "value")); - // This is the second test (AddGlobalProperty runs the first) - Assert.DoesNotThrow(() => client.AddEvent("AddEventTest", new { AProperty = "AValue" })); - // Third test should fail. - Assert.Throws(() => client.AddEvent("AddEventTest", new { AProperty = "AValue" })); - } - - [Test] - public void AddGlobalProperty_DelegateValueProviderThrows_Throws() - { - var client = new KeenClient(SettingsEnv); - Assert.Throws(() => client.AddGlobalProperty("AGlobal", new DynamicPropertyValue(() => { throw new Exception("test exception"); }))); - } - } - - [TestFixture] - public class KeenClientCachingTest : TestBase - { - [Test] - public void Caching_SendEmptyEvents_Success() - { - var client = new KeenClient(SettingsEnv, new EventCacheMemory()); - Assert.DoesNotThrow(client.SendCachedEvents); - } - - [Test] - public void Caching_ClearEvents_Success() - { - var client = new KeenClient(SettingsEnv, new EventCacheMemory()); - Assert.DoesNotThrow(() => client.EventCache.Clear()); - } - - [Test] - public void Caching_AddEvents_Success() - { - var client = new KeenClient(SettingsEnv, new EventCacheMemory()); - - Assert.DoesNotThrow(() => client.AddEvent("CachedEventTest", new { AProperty = "AValue" })); - Assert.DoesNotThrow(() => client.AddEvent("CachedEventTest", new { AProperty = "AValue" })); - Assert.DoesNotThrow(() => client.AddEvent("CachedEventTest", new { AProperty = "AValue" })); - } - - [Test] - public void Caching_SendEvents_Success() - { - var client = new KeenClient(SettingsEnv, new EventCacheMemory()); - if (UseMocks) - client.Event = new EventMock(SettingsEnv, - addEvents: (e, p) => - { - if ((p == SettingsEnv) - && (null!=e.Property("CachedEventTest")) - && (e.Property("CachedEventTest").Value.Children().Count()==3)) - return new List(); - throw new Exception("Unexpected value"); - }); - - client.AddEvent("CachedEventTest", new { AProperty = "AValue" }); - client.AddEvent("CachedEventTest", new { AProperty = "AValue" }); - client.AddEvent("CachedEventTest", new { AProperty = "AValue" }); - - Assert.DoesNotThrow(client.SendCachedEvents); - Assert.Null(client.EventCache.TryTake(), "Cache is empty"); - } - - [Test] - public void Caching_SendManyEvents_Success() - { - var client = new KeenClient(SettingsEnv, new EventCacheMemory()); - var total = 0; - if (UseMocks) - client.Event = new EventMock(SettingsEnv, - addEvents: (e, p) => - { - if ((p == SettingsEnv) - && (null != e.Property("CachedEventTest")) - && ((e.Property("CachedEventTest").Value.Children().Count() == KeenConstants.BulkBatchSize))) - { - total += e.Property("CachedEventTest").Value.Children().Count(); - return new List(); - } - throw new Exception("Unexpected value"); - }); - - for (int i = 0; i < KeenConstants.BulkBatchSize; i++) - client.AddEvent("CachedEventTest", new { AProperty = "AValue" }); - - Assert.DoesNotThrow(client.SendCachedEvents); - Assert.Null(client.EventCache.TryTake(), "Cache is empty"); - Assert.True( !UseMocks || ( total == KeenConstants.BulkBatchSize)); - } - - [Test] - public void Caching_SendInvalidEvents_Throws() - { - var client = new KeenClient(SettingsEnv, new EventCacheMemory()); - if (UseMocks) - client.Event = new EventMock(SettingsEnv, - addEvents: (e, p) => - { - if (p == SettingsEnv) - throw new KeenBulkException("Mock exception", - new List {new CachedEvent("CachedEventTest", e, new Exception())}); - throw new Exception("Unexpected value"); - }); - - var anEvent = new JObject {{"AProperty", "AValue"}}; - client.AddEvent("CachedEventTest", anEvent); - - anEvent.Add("keen", JObject.FromObject(new {invalidPropName = "value"} )); - client.AddEvent("CachedEventTest", anEvent); - Assert.DoesNotThrow(() => - { - try - { - client.SendCachedEvents(); - } - catch (KeenBulkException ex) - { - if (ex.FailedEvents.Count() != 1) - throw new Exception("Expected 1 failed event."); - } - }); - } - } - -} diff --git a/Keen.NET_35.Test/Properties/AssemblyInfo.cs b/Keen.NET_35.Test/Properties/AssemblyInfo.cs deleted file mode 100644 index 0f88251..0000000 --- a/Keen.NET_35.Test/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - - -// Friendly name and description for this assembly. -[assembly: AssemblyTitle("Keen.NET_35.Test")] -[assembly: AssemblyDescription("Keen IO SDK Tests for .NET 3.5")] - -// 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("f5d2f775-15dd-4df2-a291-9f9405e48089")] - -// See also: SharedAssemblyInfo.cs and SharedVersionInfo.cs diff --git a/Keen.NET_35.Test/ScopedKeyTest.cs b/Keen.NET_35.Test/ScopedKeyTest.cs deleted file mode 100644 index 6a22ae7..0000000 --- a/Keen.NET_35.Test/ScopedKeyTest.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Dynamic; -using System.Linq; -using NUnit.Framework; -using Newtonsoft.Json.Linq; -using System.Diagnostics; - - -namespace Keen.NET_35.Test -{ - [TestFixture] - public class ScopedKeyTest : TestBase - { - [Test] - public void Encrypt_NullObject_Success() - { - Assert.DoesNotThrow(() => ScopedKey.Encrypt("0123456789ABCDEF0123456789ABCDEF", null)); - } - - [Test] - public void Encrypt_NullKey_Throws() - { - Assert.Throws(() => ScopedKey.Encrypt(null, new { X = "X" })); - } - - public void Encrypt_BlankKey_Throws() - { - Assert.Throws(() => ScopedKey.Encrypt("", new { X = "X" })); - } - - [Test] - public void Encrypt_PopulatedObject_Success() - { - Assert.DoesNotThrow(() => - { - var settings = new ProjectSettingsProviderEnv(); - - const string str = "{\"filters\": [{\"property_name\": \"account_id\",\"operator\": \"eq\",\"property_value\": 123}],\"allowed_operations\": [ \"read\" ]}"; - var secOps = JObject.Parse(str); - var scopedKey = ScopedKey.Encrypt(settings.MasterKey, secOps); - }); - } - - [Test] - public void RoundTrip_PopulatedObject_Success() - { - var settings = new ProjectSettingsProviderEnv(); - - const string str = "{\"filters\": [{\"property_name\": \"account_id\",\"operator\": \"eq\",\"property_value\": 123}],\"allowed_operations\": [ \"read\" ]}"; - var secOps = JObject.Parse(str); - - Assert.DoesNotThrow(() => - { - var scopedKey = ScopedKey.Encrypt(settings.MasterKey, secOps); - Console.WriteLine(scopedKey); - var decrypted = ScopedKey.Decrypt(settings.MasterKey, scopedKey); - var secOpsOut = JObject.Parse(decrypted); - Assert.True((string)secOps["allowed_operations"].First() == (string)(secOpsOut["allowed_operations"].First())); - }); - } - - [Test] - public void RoundTrip_PopulatedObject_WithIV_Success() - { - var settings = new ProjectSettingsProviderEnv(); - const string IV = "C0FFEEC0FFEEC0FFEEC0FFEEC0FFEEC0"; - - const string str = "{\"filters\": [{\"property_name\": \"account_id\",\"operator\": \"eq\",\"property_value\": 123}],\"allowed_operations\": [ \"read\" ]}"; - var secOps = JObject.Parse(str); - - Assert.DoesNotThrow(() => - { - var scopedKey = ScopedKey.Encrypt(settings.MasterKey, secOps, IV); - var decrypted = ScopedKey.Decrypt(settings.MasterKey, scopedKey); - var secOpsOut = JObject.Parse(decrypted); - Assert.True((string)secOps["allowed_operations"].First() == (string)(secOpsOut["allowed_operations"].First())); - }); - } - - [Test] - public void Decrypt_WriteKey_Success() - { - const string plainText = "{\"filters\": [{\"property_name\": \"vendor_id\",\"operator\": \"eq\",\"property_value\": \"abc\"}],\"allowed_operations\": [ \"write\" ]}"; - var testKey = SettingsEnv.MasterKey; - var cryptText = SettingsEnv.ReadKey; - - if (UseMocks) - { - cryptText = - "BAA51D1D03D49C1159E7298762AAC26493B20F579988E1EDA4613305F08E01CB702886F0FCB5312E5E18C6315A8049700816CA35BD952C75EB694AAA4A95535EE13CD9D5D8C97A215B4790638EA1DA3DB9484A0133D5289E2A22D5C2952E1F708540722EA832B093E147495A70ADF534242E961FDE3F0275E20D58F22B23F4BAE2A61518CB943818ABEF547DD68F68FE"; - testKey = "0123456789ABCDEF0123456789ABCDEF"; // ensure the key matches what cryptText was encrypted with - } - - var decrypted = ScopedKey.Decrypt(testKey, cryptText); - if (UseMocks) - Assert.True(decrypted.Equals(plainText)); - else - Assert.That(decrypted.IndexOf("timestamp") > 0); - - } - - [Test] - public void Roundtrip_RndIV_Success() - { - const string vendor_guid = "abc"; - const bool isRead = false; - - var str = "{\"filters\": [{\"property_name\": \"vendor_id\",\"operator\": \"eq\",\"property_value\": \"VENDOR_GUID\"}],\"allowed_operations\": [ \"READ_OR_WRITE\" ]}"; - - str = str.Replace("VENDOR_GUID", vendor_guid); - - if (isRead) str = str.Replace("READ_OR_WRITE", "read"); - else str = str.Replace("READ_OR_WRITE", "write"); - - var rnd = new System.Security.Cryptography.RNGCryptoServiceProvider(); - byte[] bytes = new byte[16]; - rnd.GetBytes(bytes); - - var IV = String.Concat(bytes.Select(b => b.ToString("X2"))); Trace.WriteLine("IV: " + IV); - - var scopedKey = ScopedKey.EncryptString(SettingsEnv.MasterKey, str, IV );//System.Text.Encoding.Default.GetString(bytes)); - var decrypted = ScopedKey.Decrypt(SettingsEnv.MasterKey, scopedKey); - Trace.WriteLine("decrypted: " + decrypted); - } - } -} diff --git a/Keen.NET_35.Test/packages.config b/Keen.NET_35.Test/packages.config deleted file mode 100644 index 8a03236..0000000 --- a/Keen.NET_35.Test/packages.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Keen.NET_35/CachedEvent.cs b/Keen.NET_35/CachedEvent.cs deleted file mode 100644 index d8f48f7..0000000 --- a/Keen.NET_35/CachedEvent.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Keen.NET_35 -{ - /// - /// CachedEvent is a container for user event data which associates the - /// target event collection name and, if an error occurs during submission, - /// the exception instance. - /// - public class CachedEvent - { - public string Collection { get; set; } - public JObject Event { get; set; } - public Exception Error { get; set; } - - public CachedEvent(string collection, JObject e, Exception err = null) - { - Collection = collection; - Event = e; - Error = err; - } - - public override string ToString() - { - return string.Format("CachedEvent:{{\n\"Collection\": \"{0}\",\n\"Event\":{1},\n\"Error\":\"{2}:{3}\"\n}}", - Collection, Event, Error == null ? "null" : Error.GetType().Name, Error == null ? "" : Error.Message); - } - } -} diff --git a/Keen.NET_35/DataEnrichment/EventAddOn.cs b/Keen.NET_35/DataEnrichment/EventAddOn.cs deleted file mode 100644 index 8180fd1..0000000 --- a/Keen.NET_35/DataEnrichment/EventAddOn.cs +++ /dev/null @@ -1,110 +0,0 @@ -using System.Collections.Generic; -using Newtonsoft.Json; - -namespace Keen.NET_35.DataEnrichment -{ - /// - /// Represents a Data Enrichment add-on. - /// - /// https://keen.io/docs/data-collection/data-enrichment/ - /// - /// - public sealed class AddOn - { - /// - /// Name of the add-on - /// - [JsonProperty(PropertyName = "name")] - public string Name { get; private set; } - - /// - /// Parameters required by the add-on - /// - [JsonProperty(PropertyName = "input")] - public Dictionary Input { get; private set; } - - /// - /// Target property name where the enriched data should be stored. - /// - [JsonProperty(PropertyName = "output")] - public string Output { get; private set; } - - /// Name of the data enhancement add-on. - /// Name-value pairs of input parameters required by the add-on. - /// Target property name for the enriched data. - public AddOn(string name, IDictionary input, string output) - { - if (output.StartsWith("keen.", System.StringComparison.Ordinal)) - throw new KeenInvalidPropertyNameException( - "Add-on event output name may not be in the keen namespace:" + output); - - Name = name; - Input = new Dictionary(input); - Output = output; - } - - /// - /// Build and return an IpToGeo Data Enhancement add-on. This add-on reads - /// an IP address from the field identified by the input parameter and writes - /// data about the geographical location to the field identfied by the output parameter. - /// - /// Name of field to store the geographical information - /// Name of field containing an IP address - /// - public static AddOn IpToGeo(string ipField, string outputField) - { - return new AddOn("keen:ip_to_geo", - new Dictionary { { "ip", ipField } }, - outputField); - } - - /// - /// Build and return a User-Agent Data Enhancement add-on. This add-on reads - /// a user agent string from the field identified by the input parameter and parses it - /// into the device, browser, browser version, OS, and OS version fields and stores that - /// data in the field identfied by the output parameter. - /// - /// Name of field to store the parsed user agent field - /// Name of field containing the user agent string - /// - public static AddOn UserAgentParser(string userAgentString, string outputField) - { - return new AddOn("keen:ua_parser", - new Dictionary { { "ua_string", userAgentString } }, - outputField); - } - - /// - /// Build and return a URL Parser Data Enhancement add-on. This add-on reads - /// a well-formed URL from the field identified by the input parameter and parses - /// it into it's components for easier filtering. The components are stored in the - /// field identified by the output parameter. - /// - /// Name of field containing the URL to parse - /// Name of field to store the parsed url components - /// - public static AddOn UrlParser(string urlField, string outputField) - { - return new AddOn("keen:url_parser", - new Dictionary { { "url", urlField } }, - outputField); - } - - /// - /// Build and return a Referrer Parser Data Enhancement add-on. This add-on reads - /// a well-formed referrer URL from the field identified by the input parameter and - /// parses it into it's components. The components are stored in the field identified - /// by the output parameter. - /// - /// Name of field containing the URL of the current page - /// Name of field to store the parsed referrer data. - /// Name of field containing the referrer URL - /// - public static AddOn ReferrerParser(string referrerUrlField, string pageUrlField, string outputField) - { - return new AddOn("keen:referrer_parser", - new Dictionary { { "referrer_url", referrerUrlField }, { "page_url", pageUrlField } }, - outputField); - } - } -} \ No newline at end of file diff --git a/Keen.NET_35/DynamicPropertyValue.cs b/Keen.NET_35/DynamicPropertyValue.cs deleted file mode 100644 index bc0aa65..0000000 --- a/Keen.NET_35/DynamicPropertyValue.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; - -namespace Keen.NET_35 -{ - /// - /// An instance of DynamicPropertyValue containing a delegate - /// can be added to the GlobalProperties collection. When AddEvent - /// inserts GlobalProperties into an event, the delegate will be - /// executed to provide the value of the property. - /// - public class DynamicPropertyValue : IDynamicPropertyValue - { - private Func _value; - - /// - /// Call the delegate that produces the property value - /// - /// The value produced by the delegate - public object Value() - { - return _value(); - } - - /// - /// - /// - /// A delegate that will be called each time the property value is required - public DynamicPropertyValue(Func value) - { - _value = value; - } - } -} diff --git a/Keen.NET_35/Event.cs b/Keen.NET_35/Event.cs deleted file mode 100644 index aa8bcba..0000000 --- a/Keen.NET_35/Event.cs +++ /dev/null @@ -1,120 +0,0 @@ -using Newtonsoft.Json.Linq; -using RestSharp; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Keen.NET_35 -{ - /// - /// Event implements the IEvent interface which represents the Keen.IO Event API methods. - /// - internal class Event : IEvent - { - private readonly IProjectSettings _prjSettings; - private readonly string _serverUrl; - - /// - /// Get details of all schemas in the project. - /// - /// - public JArray GetSchemas() - { - try - { - var client = new RestClient(_serverUrl); - var request = new RestRequest("", Method.GET); - request.AddHeader("Authorization", _prjSettings.MasterKey); - request.AddHeader("Keen-Sdk", KeenUtil.GetSdkVersion()); - - var serverResponse = client.Execute(request); - if (serverResponse == null) - throw new KeenException("No response from host"); - if (!serverResponse.ErrorMessage.IsNullOrWhiteSpace()) - throw new KeenException("GetSchemas failed with status: " + serverResponse.ErrorMessage); - - JArray jsonResponse = null; - try - { - // The response should be an array. An error will cause a parse failure. - jsonResponse = JArray.Parse(serverResponse.Content); - } - catch (Exception) - { - var obj = JObject.Parse(serverResponse.Content); - KeenUtil.CheckApiErrorCode(obj); - } - - return jsonResponse; - } - catch (Exception ex) - { - throw new KeenException("GetSchemas failed", ex); - } - } - - /// - /// Add all events in a single request. - /// - /// - /// - public IEnumerable AddEvents(JObject events) - { - JObject jsonResponse = null; - try - { - var client = new RestClient(_serverUrl); - var request = new RestRequest("", Method.POST); - request.AddHeader("Authorization", _prjSettings.WriteKey); - request.AddHeader("Keen-Sdk", KeenUtil.GetSdkVersion()); - request.AddParameter("application/json", events.ToString(), ParameterType.RequestBody); - - var serverResponse = client.Execute(request); - if (serverResponse == null) - throw new KeenException("No response from host"); - if (!serverResponse.ErrorMessage.IsNullOrWhiteSpace()) - throw new KeenException("AddEvents failed with status: " + serverResponse.ErrorMessage); - - if (!serverResponse.Content.IsNullOrWhiteSpace()) - jsonResponse = JObject.Parse(serverResponse.Content); - } - catch (Exception ex) - { - throw new KeenException("AddEvents failed", ex); - } - KeenUtil.CheckApiErrorCode(jsonResponse); - - try - { - - // error checking, return failed events in the list, - // or if the HTTP response is a failure, throw. - var failedItems = from respCols in jsonResponse.Properties() - from eventsCols in events.Properties() - where respCols.Name == eventsCols.Name - let collection = respCols.Name - let combined = eventsCols.Children().Children() - .Zip(respCols.Children().Children(), - (e, r) => new {eventObj = (JObject) e, result = (JObject) r}) - from e in combined - where !(bool) (e.result.Property("success").Value) - select new CachedEvent(collection, e.eventObj, KeenUtil.GetBulkApiError(e.result)); - - return failedItems; - } - catch (Exception ex) - { - throw new KeenException("AddEvents failed", ex); - } - } - - public Event(IProjectSettings prjSettings) - { - _prjSettings = prjSettings; - - _serverUrl = string.Format("{0}projects/{1}/{2}", - _prjSettings.KeenUrl, _prjSettings.ProjectId, KeenConstants.EventsResource); - } - - } -} diff --git a/Keen.NET_35/EventCacheMemory.cs b/Keen.NET_35/EventCacheMemory.cs deleted file mode 100644 index e3a10fc..0000000 --- a/Keen.NET_35/EventCacheMemory.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Collections.Generic; -using System.Linq; - -namespace Keen.NET_35 -{ - /// - /// This is a simple memory-based cache provider. It has no cache-expiration policy. - /// To use, pass an instance of this class when constructing KeenClient - /// - /// - public class EventCacheMemory : IEventCache - { - private readonly Queue events = new Queue(); - - public void Add(CachedEvent e) - { - if (null == e) - throw new KeenException("Cached events may not be null"); - - lock (events) - events.Enqueue(e); - } - - public void Clear() - { - lock (events) - events.Clear(); - } - - public CachedEvent TryTake() - { - lock (events) - return events.Any() ? events.Dequeue() : null; - } - } -} diff --git a/Keen.NET_35/EventCollection.cs b/Keen.NET_35/EventCollection.cs deleted file mode 100644 index 057ee8f..0000000 --- a/Keen.NET_35/EventCollection.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System.Diagnostics; -using Newtonsoft.Json.Linq; -using RestSharp; -using System; - -namespace Keen.NET_35 -{ - /// - /// EventCollection implements the IEventCollection interface which represents the Keen.IO EventCollection API methods. - /// - internal class EventCollection : IEventCollection - { - private readonly string _serverUrl; - private readonly IProjectSettings _prjSettings; - - public JObject GetSchema(string collection) - { - try - { - var client = new RestClient(_serverUrl); - var request = new RestRequest(collection, Method.GET); - request.AddHeader("Authorization", _prjSettings.MasterKey); - request.AddHeader("Keen-Sdk", KeenUtil.GetSdkVersion()); - - var serverResponse = client.Execute(request); - if (serverResponse==null) - throw new KeenException("No response from host"); - if (!serverResponse.ErrorMessage.IsNullOrWhiteSpace()) - throw new KeenException("GetSchema failed with status: " + serverResponse.ErrorMessage); - var response = JObject.Parse(serverResponse.Content); - KeenUtil.CheckApiErrorCode(response); - return response; - } - catch (Exception ex) - { - throw new KeenException("GetSchema failed", ex); - } - } - - public void DeleteCollection(string collection) - { - JObject jsonResponse = null; - try - { - var client = new RestClient(_serverUrl); - var request = new RestRequest(collection, Method.DELETE); - request.AddHeader("Authorization", _prjSettings.MasterKey); - request.AddHeader("Keen-Sdk", KeenUtil.GetSdkVersion()); - - var serverResponse = client.Execute(request); - if (serverResponse == null) - throw new KeenException("No response from host"); - - if (!serverResponse.ErrorMessage.IsNullOrWhiteSpace()) - throw new KeenException("DeleteCollection failed with error: " + serverResponse.ErrorMessage); - - if (!serverResponse.Content.IsNullOrWhiteSpace()) - jsonResponse = JObject.Parse(serverResponse.Content); - } - catch (Exception ex) - { - throw new KeenException("DeleteCollection failed " + ex.Message, ex); - } - // throw an exception with information from the json response - KeenUtil.CheckApiErrorCode(jsonResponse); - } - - public void AddEvent(string collection, JObject anEvent) - { - JObject jsonResponse = null; - try - { - var client = new RestClient(_serverUrl); - var request = new RestRequest(collection, Method.POST); - request.AddHeader("Authorization", _prjSettings.WriteKey); - request.AddHeader("Keen-Sdk", KeenUtil.GetSdkVersion()); - request.AddParameter("application/json", anEvent.ToString(), ParameterType.RequestBody); - - var serverResponse = client.Execute(request); - if (serverResponse==null) - throw new KeenException("No response from host"); - if (!serverResponse.ErrorMessage.IsNullOrWhiteSpace()) - throw new KeenException("AddEvent failed with status: " + serverResponse.ErrorMessage); - - if (!serverResponse.Content.IsNullOrWhiteSpace()) - jsonResponse = JObject.Parse(serverResponse.Content); - } - catch (Exception ex) - { - throw new KeenException("AddEvent failed", ex); - } - KeenUtil.CheckApiErrorCode(jsonResponse); - } - - public EventCollection(IProjectSettings prjSettings) - { - _prjSettings = prjSettings; - - _serverUrl = string.Format("{0}projects/{1}/{2}/", - _prjSettings.KeenUrl, _prjSettings.ProjectId, KeenConstants.EventsResource); - } - } -} diff --git a/Keen.NET_35/IDynamicPropertyValue.cs b/Keen.NET_35/IDynamicPropertyValue.cs deleted file mode 100644 index 9ced09a..0000000 --- a/Keen.NET_35/IDynamicPropertyValue.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Keen.NET_35 -{ - interface IDynamicPropertyValue - { - object Value(); - } -} diff --git a/Keen.NET_35/IEvent.cs b/Keen.NET_35/IEvent.cs deleted file mode 100644 index 76b85c4..0000000 --- a/Keen.NET_35/IEvent.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Newtonsoft.Json.Linq; -using System.Collections.Generic; - -namespace Keen.NET_35 -{ - public interface IEvent - { - /// - /// Return schema information for all the event collections in this project. - /// - /// - JArray GetSchemas(); - - /// - /// Insert multiple events in one or more collections in a single request. - /// - /// - /// Enumerable containing any rejected events - IEnumerable AddEvents(JObject events); - } -} diff --git a/Keen.NET_35/IEventCache.cs b/Keen.NET_35/IEventCache.cs deleted file mode 100644 index 4609217..0000000 --- a/Keen.NET_35/IEventCache.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Keen.NET_35 -{ - public interface IEventCache - { - void Add(CachedEvent e); - CachedEvent TryTake(); - void Clear(); - } -} diff --git a/Keen.NET_35/IEventCollection.cs b/Keen.NET_35/IEventCollection.cs deleted file mode 100644 index a0a0f4a..0000000 --- a/Keen.NET_35/IEventCollection.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Newtonsoft.Json.Linq; - -namespace Keen.NET_35 -{ - public interface IEventCollection - { - /// - /// Returns schema information for this event collection. - /// - /// - /// - JObject GetSchema(string collection); - - /// - /// Delete the entire event collection. - /// - /// Name of collection - void DeleteCollection(string collection); - - /// - /// Insert one event at a time in a single request. - /// - /// Name of collection - /// Event data to insert - /// - void AddEvent(string collection, JObject anEvent); - } -} diff --git a/Keen.NET_35/IProjectSettings.cs b/Keen.NET_35/IProjectSettings.cs deleted file mode 100644 index 2847e33..0000000 --- a/Keen.NET_35/IProjectSettings.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Keen.NET_35 -{ - /// - /// Values required to access a Keen project - /// - public interface IProjectSettings - { - /// - /// The Keen.IO URL for this project. Usually this will be the - /// server address and API version. - /// - string KeenUrl { get; } - - /// - /// The Project ID, identifying the data silo to be accessed. - /// - string ProjectId { get; } - - /// - /// The Master API key, required for getting a collection schema - /// or deleting the entire event collection. - /// - string MasterKey { get; } - - /// - /// The Write API key, required for inserting events. - /// - string WriteKey { get; } - - /// - /// The Read API key, used with query requests. - /// - string ReadKey { get; } - } -} diff --git a/Keen.NET_35/Keen.NET_35.csproj b/Keen.NET_35/Keen.NET_35.csproj deleted file mode 100644 index 55d0e40..0000000 --- a/Keen.NET_35/Keen.NET_35.csproj +++ /dev/null @@ -1,96 +0,0 @@ - - - - - Debug - AnyCPU - {A395C070-F971-48D5-A3DE-9263032CFB84} - Library - Properties - Keen.NET_35 - Keen.NET_35 - v3.5 - 512 - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - bin\netfx\ - TRACE - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - - - - ..\packages\Newtonsoft.Json.9.0.1\lib\net35\Newtonsoft.Json.dll - True - - - ..\packages\RestSharp.105.2.3\lib\net35\RestSharp.dll - True - - - - - - - - - - - Properties\SharedAssemblyInfo.cs - - - Properties\SharedVersionInfo.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Keen.NET_35/KeenClient.cs b/Keen.NET_35/KeenClient.cs deleted file mode 100644 index b474fc6..0000000 --- a/Keen.NET_35/KeenClient.cs +++ /dev/null @@ -1,329 +0,0 @@ -using Keen.NET_35.DataEnrichment; -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Keen.NET_35 -{ - public class KeenClient - { - private readonly IProjectSettings _prjSettings; - private readonly Dictionary _globalProperties = new Dictionary(); - - /// - /// EventCollection provides direct access to the Keen.IO EventCollection API methods. - /// It is not normally necessary to use this property. - /// The default implementation can be overridden by setting a new implementation here. - /// - public IEventCollection EventCollection { get; set; } - - /// - /// Event provides direct access to the Keen.IO Event API methods. - /// It is not normally necessary to use this property. - /// The default implementation can be overridden by setting a new implementation here. - /// - public IEvent Event { get; set; } - - /// - /// EventCache provides a caching implementation allowing events to be cached locally - /// instead of being sent one at a time. It is not normally necessary to use this property. - /// The implementation is responsible for cache maintenance policy, such as trimming - /// old entries to avoid excessive cache size. - /// - public IEventCache EventCache { get; private set; } - - /// - /// Add a static global property. This property will be added to - /// every event. - /// - /// Property name - /// Property value. This may be a simple value, array, or object, - /// or an object that supports IDynamicPropertyValue returning one of those. - public void AddGlobalProperty(string property, object value) - { - // Verify that the property name is allowable, and that the value is populated. - KeenUtil.ValidatePropertyName(property); - if (value == null) - throw new KeenException("Global properties must have a non-null value."); - var dynProp = value as IDynamicPropertyValue; - if (dynProp != null) - // Execute the property once before it is needed to check the value - ExecDynamicPropertyValue(property, dynProp); - - _globalProperties.Add(property, value); - } - - private void ExecDynamicPropertyValue(string propName, IDynamicPropertyValue dynProp) - { - object result; - try - { - result = dynProp.Value(); - } - catch (Exception e) - { - throw new KeenException(string.Format("Dynamic property \"{0}\" execution failure", propName), e); - } - if (result == null) - throw new KeenException(string.Format("Dynamic property \"{0}\" execution returned null", propName)); - } - - /// - /// - /// - /// A ProjectSettings instance containing the ProjectId and API keys - public KeenClient(IProjectSettings prjSettings) - { - // Preconditions - if (null == prjSettings) - throw new KeenException("An IProjectSettings instance is required."); - if (prjSettings.ProjectId.IsNullOrWhiteSpace()) - throw new KeenException("A Project ID is required."); - if ((prjSettings.MasterKey.IsNullOrWhiteSpace() - && prjSettings.WriteKey.IsNullOrWhiteSpace())) - throw new KeenException("A Master or Write API key is required."); - if (prjSettings.KeenUrl.IsNullOrWhiteSpace()) - throw new KeenException("A URL for the server address is required."); - - _prjSettings = prjSettings; - // The EventCollection and Event interface normally should not need to - // be set by callers, so the default implementation is set up here. Users - // may override these by injecting an implementation via the property. - EventCollection = new EventCollection(_prjSettings); - Event = new Event(_prjSettings); - } - - /// - /// - /// - /// A ProjectSettings instance containing the ProjectId and API keys - /// An IEventCache instance providing a caching strategy - public KeenClient(IProjectSettings prjSettings, IEventCache eventCache) - : this(prjSettings) - { - EventCache = eventCache; - } - - /// - /// Delete the specified collection. Deletion may be denied for collections with many events. - /// Master API key is required. - /// - /// Name of collection to delete. - public void DeleteCollection(string collection) - { - // Preconditions - KeenUtil.ValidateEventCollectionName(collection); - if (_prjSettings.MasterKey.IsNullOrWhiteSpace()) - throw new KeenException("Master API key is requried for DeleteCollection"); - - EventCollection.DeleteCollection(collection); - } - - /// - /// Return schema information for all the event collections in this project. - /// - /// - public JArray GetSchemas() - { - // Preconditions - if (_prjSettings.MasterKey.IsNullOrWhiteSpace()) - throw new KeenException("Master API key is requried for GetSchemas"); - - return Event.GetSchemas(); - } - - /// - /// Retrieve the schema for the specified collection. This requires - /// a value for the project settings Master API key. - /// - /// - public JObject GetSchema(string collection) - { - // Preconditions - KeenUtil.ValidateEventCollectionName(collection); - if (_prjSettings.MasterKey.IsNullOrWhiteSpace()) - throw new KeenException("Master API key is requried for GetSchema"); - - return EventCollection.GetSchema(collection); - } - - /// - /// Insert multiple events in a single request. - /// - /// Collection name - /// Collection of events to add - /// Optional collection of Data Enhancement Add-ons - public void AddEvents(string collection, IEnumerable eventsInfo, IEnumerable addOns = null) - { - if (null == eventsInfo) - throw new KeenException("AddEvents eventsInfo may not be null"); - if (_prjSettings.WriteKey.IsNullOrWhiteSpace()) - throw new KeenException("Write API key is requried for AddEvents"); - - var mainCache = EventCache; - var localCache = new List(); - - // prepare each object to add global properties and timestamp, then either - // add to the main cache if it exists, or if not to the local object list. - foreach (var jEvent in eventsInfo.Select(e=>PrepareUserObject(e, addOns))) - { - if (null != mainCache) - mainCache.Add(new CachedEvent(collection, jEvent)); - else - localCache.Add(jEvent); - } - - // if the local object list has data (caching is not enabled), go - // ahead and send it using the bulk interface. - if (!localCache.Any()) return; - - var errs = AddEventsBulk(collection, localCache); - // ReSharper disable PossibleMultipleEnumeration - if (errs.Any()) - throw new KeenBulkException("One or more events was rejected during the bulk add operation", errs); - // ReSharper restore PossibleMultipleEnumeration - } - - - /// - /// Add a collection of events to the specified collection. Assumes that - /// objects in the collection have already been through AddEvent to receive - /// global properties. - /// - /// Collection name - /// Collection of events to add - /// Enumerable of any rejected events - private IEnumerable AddEventsBulk(string collection, IEnumerable eventsInfo) - { - if (null == eventsInfo) - throw new KeenException("AddEvents eventsInfo may not be null"); - if (!eventsInfo.Any()) - return new List(); - // Build a container object with a property to identify the collection - var jEvent = new JObject {{collection, JToken.FromObject(eventsInfo)}}; - - // Use the bulk interface to add events - return Event.AddEvents(jEvent); - } - - /// - /// Convert a user-supplied object to a JObject that can be sent to the Keen.IO API. - /// - /// This writes any global properies to the object and records the time. - /// - /// - /// Optional collection of Data Enhancement Add-ons - /// - private JObject PrepareUserObject(object eventInfo, IEnumerable addOns) - { - var jEvent = JObject.FromObject(eventInfo); - - // Add global properties to the event - foreach (var p in _globalProperties) - { - // If the property value is an IDynamicPropertyValue, - // exec the Value() to generate the property value. - var dynProp = p.Value as IDynamicPropertyValue; - if (dynProp == null) - { - KeenUtil.ValidatePropertyName(p.Key); - jEvent.Add(p.Key, JToken.FromObject(p.Value)); - } - else - { - var val = dynProp.Value(); - if (null == val) - throw new KeenException(string.Format("Dynamic property \"{0}\" returned a null value", p.Key)); - jEvent.Add(p.Key, JToken.FromObject(val)); - } - } - - // Ensure this event has a 'keen' object of the correct type - if (null == jEvent.Property("keen")) - jEvent.Add("keen", new JObject()); - else if (jEvent.Property("keen").Value.GetType() != typeof(JObject)) - throw new KeenException(string.Format("Value of property \"keen\" must be an object, is {0}", jEvent.Property("keen").GetType())); - - var keen = ((JObject)jEvent.Property("keen").Value); - - if (null != addOns && addOns.Any()) - keen.Add("addons", JArray.FromObject(addOns)); - - // Set the keen.timestamp if it has not already been set - if (null == keen.Property("timestamp")) - keen.Add("timestamp", DateTime.Now); - - return jEvent; - } - - /// - /// Add a single event to the specified collection. - /// - /// Collection name - /// An object representing the event to be added. - /// Optional collection of Data Enhancement Add-ons - public void AddEvent(string collection, object eventInfo, IEnumerable addOns = null) - { - // Preconditions - KeenUtil.ValidateEventCollectionName(collection); - if (null == eventInfo) - throw new KeenException("Event data is required."); - if (_prjSettings.WriteKey.IsNullOrWhiteSpace()) - throw new KeenException("Write API key is requried for AddEvent"); - - var jEvent = PrepareUserObject(eventInfo, addOns); - - // If an event cache has been provided, cache this event insead of sending it. - if (null != EventCache) - EventCache.Add(new CachedEvent(collection, jEvent)); - else - EventCollection.AddEvent(collection, jEvent); - } - - /// - /// Submit all events found in the event cache. If an events are rejected by the server, - /// KeenCacheException will be thrown with a listing of the rejected events, each with - /// the error message it received. - /// - public void SendCachedEvents() - { - if (null == EventCache) - throw new KeenException("Event caching is not enabled"); - - CachedEvent e; - var batches = new Dictionary>(); - var failedEvents = new List(); - - Func> getListFor = c => - { - if (batches.ContainsKey(c)) - return batches[c]; - var l = new List(); - batches.Add(c, l); - return l; - }; - - // Take items from the cache and sort them by collection - while (null != (e = EventCache.TryTake())) - { - var batch = getListFor(e.Collection); - batch.Add(e); - - // If this collection has reached the maximum batch size, send it - if (batch.Count != KeenConstants.BulkBatchSize) continue; - - failedEvents.AddRange(AddEventsBulk(e.Collection, batch.Select(n => n.Event))); - batch.Clear(); - } - - // Send the remainder of all the collections - foreach (var c in batches.Where(b => b.Value.Any())) - failedEvents.AddRange(AddEventsBulk(c.Key, c.Value.Select(n => n.Event))); - - // if there where any failures, throw and include the errored items and details. - if (failedEvents.Any()) - throw new KeenBulkException("One or more cached events could not be submitted", failedEvents); - } - } -} diff --git a/Keen.NET_35/KeenConstants.cs b/Keen.NET_35/KeenConstants.cs deleted file mode 100644 index 9d812bf..0000000 --- a/Keen.NET_35/KeenConstants.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Keen.NET_35 -{ - public class KeenConstants - { - private const string serverAddress = "http://api.keen.io"; - public static string ServerAddress { get { return serverAddress; } protected set { ;} } - - private const string eventsResource = "events"; - public static string EventsResource { get { return eventsResource; } protected set { ;} } - - private const string queriesResource = "queries"; - public static string QueriesResource { get { return queriesResource; } protected set { ;} } - - private const string queryCount = "count"; - public static string QueryCount { get { return queryCount; } protected set { ;} } - - private const string queryCountUnique = "count_unique"; - public static string QueryCountUnique { get { return queryCountUnique; } protected set { ;} } - - private const string queryMinimum = "minimum"; - public static string QueryMinimum { get { return queryMinimum; } protected set { ;} } - - private const string queryMaximum = "maximum"; - public static string QueryMaximum { get { return queryMaximum; } protected set { ;} } - - private const string queryAverage = "average"; - public static string QueryAverage { get { return queryAverage; } protected set { ;} } - - private const string querySum = "sum"; - public static string QuerySum { get { return querySum; } protected set { ;} } - - private const string querySelectUnique = "select_unique"; - public static string QuerySelectUnique { get { return querySelectUnique; } protected set { ;} } - - private const string queryExtraction = "extraction"; - public static string QueryExtraction { get { return queryExtraction; } protected set { ;} } - - private const string queryFunnel = "funnel"; - public static string QueryFunnel { get { return queryFunnel; } protected set { ;} } - - private const string queryMultiAnalysis = "multi_analysis"; - public static string QueryMultiAnalysis { get { return queryMultiAnalysis; } protected set { ;} } - - private const string queryParmEventCollection = "event_collection"; - public static string QueryParmEventCollection { get { return queryParmEventCollection; } protected set { ;} } - - private const string queryParmTargetProperty = "target_property"; - public static string QueryParmTargetProperty { get { return queryParmTargetProperty; } protected set { ;} } - - private const string queryParmTimeframe = "timeframe"; - public static string QueryParmTimeframe { get { return queryParmTimeframe; } protected set { ;} } - - private const string queryParmGroupBy = "group_by"; - public static string QueryParmGroupBy { get { return queryParmGroupBy; } protected set { ;} } - - private const string queryParmInterval = "interval"; - public static string QueryParmInterval { get { return queryParmInterval; } protected set { ;} } - - private const string queryParmTimezone = "timezone"; - public static string QueryParmTimezone { get { return queryParmTimezone; } protected set { ;} } - - private const string queryParmFilters = "filters"; - public static string QueryParmFilters { get { return queryParmFilters; } protected set { ;} } - - private const string queryParmEmail = "email"; - public static string QueryParmEmail { get { return queryParmEmail; } protected set { ;} } - - private const string queryParmLatest = "latest"; - public static string QueryParmLatest { get { return queryParmLatest; } protected set { ;} } - - private const string queryParmSteps = "steps"; - public static string QueryParmSteps { get { return queryParmSteps; } protected set { ;} } - - private const string queryParmAnalyses = "analyses"; - public static string QueryParmAnalyses { get { return queryParmAnalyses; } protected set { ;} } - - private const string apiVersion = "3.0"; - public static string ApiVersion { get { return apiVersion; } protected set { ;} } - - private const int bulkBatchSize = 1000; - public static int BulkBatchSize { get { return bulkBatchSize; } protected set { ;} } - } -} diff --git a/Keen.NET_35/KeenException.cs b/Keen.NET_35/KeenException.cs deleted file mode 100644 index 3335da8..0000000 --- a/Keen.NET_35/KeenException.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Keen.NET_35 -{ - public class KeenException : Exception - { - public KeenException() { } - public KeenException(string message) : base(message) { } - public KeenException(string message, Exception inner) : base(message, inner) { } - } - - public class KeenInvalidApiKeyException : KeenException - { - public KeenInvalidApiKeyException() { } - public KeenInvalidApiKeyException(string message) : base(message) { } - public KeenInvalidApiKeyException(string message, Exception inner) : base(message, inner) { } - } - - public class KeenResourceNotFoundException : KeenException - { - public KeenResourceNotFoundException() { } - public KeenResourceNotFoundException(string message) : base(message) { } - public KeenResourceNotFoundException(string message, Exception inner) : base(message, inner) { } - } - - public class KeenNamespaceTypeException : KeenException - { - public KeenNamespaceTypeException() { } - public KeenNamespaceTypeException(string message) : base(message) { } - public KeenNamespaceTypeException(string message, Exception inner) : base(message, inner) { } - } - - public class KeenInvalidEventException : KeenException - { - public KeenInvalidEventException() { } - public KeenInvalidEventException(string message) : base(message) { } - public KeenInvalidEventException(string message, Exception inner) : base(message, inner) { } - } - - public class KeenListsOfNonPrimitivesNotAllowedException : KeenException - { - public KeenListsOfNonPrimitivesNotAllowedException() { } - public KeenListsOfNonPrimitivesNotAllowedException(string message) : base(message) { } - public KeenListsOfNonPrimitivesNotAllowedException(string message, Exception inner) : base(message, inner) { } - } - - public class KeenInvalidBatchException : KeenException - { - public KeenInvalidBatchException() { } - public KeenInvalidBatchException(string message) : base(message) { } - public KeenInvalidBatchException(string message, Exception inner) : base(message, inner) { } - } - - public class KeenInternalServerErrorException : KeenException - { - public KeenInternalServerErrorException() { } - public KeenInternalServerErrorException(string message) : base(message) { } - public KeenInternalServerErrorException(string message, Exception inner) : base(message, inner) { } - } - - public class KeenInvalidKeenNamespacePropertyException: KeenException - { - public KeenInvalidKeenNamespacePropertyException() { } - public KeenInvalidKeenNamespacePropertyException(string message) : base(message) { } - public KeenInvalidKeenNamespacePropertyException(string message, Exception inner) : base(message, inner) { } - } - - public class KeenInvalidPropertyNameException: KeenException - { - public KeenInvalidPropertyNameException() { } - public KeenInvalidPropertyNameException(string message) : base(message) { } - public KeenInvalidPropertyNameException(string message, Exception inner) : base(message, inner) { } - } - - public class KeenBulkException : KeenException - { - private IEnumerable _failedEvents; - public IEnumerable FailedEvents { get { return _failedEvents; } protected set { ; } } - public KeenBulkException(IEnumerable failedEvents) { _failedEvents = failedEvents; } - public KeenBulkException(string message, IEnumerable failedEvents ) : base(message) { _failedEvents = failedEvents; } - } -} diff --git a/Keen.NET_35/KeenUtil.cs b/Keen.NET_35/KeenUtil.cs deleted file mode 100644 index 9568f74..0000000 --- a/Keen.NET_35/KeenUtil.cs +++ /dev/null @@ -1,243 +0,0 @@ -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Reflection; -using System.Text.RegularExpressions; - -namespace Keen.NET_35 -{ - public static class KeenUtil - { - private static readonly string SdkVersion; - - static KeenUtil() - { - string version = GetAssemblyInformationalVersion(); - version = (version.IsNullOrWhiteSpace() ? "0.0.0" : version); - SdkVersion = string.Format(".net_35-{0}", version); - } - - /// - /// Retrieve a string representing the current version of the Keen IO SDK, as defined by - /// the AssemblyInformationVersion. - /// - /// The SDK version string. - public static string GetSdkVersion() - { - return SdkVersion; - } - - private static string GetAssemblyInformationalVersion() - { - string assemblyInformationalVersion = string.Empty; - AssemblyInformationalVersionAttribute[] attributes = null; - - try - { - attributes = Assembly.GetExecutingAssembly() - .GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false) - as AssemblyInformationalVersionAttribute[]; - } - catch (Exception e) - { - Debug.WriteLine("Caught exception trying to read AssemblyInformationalVersion: " + e); - } - - if (null != attributes && 0 < attributes.Length) - { - assemblyInformationalVersion = attributes[0].InformationalVersion; - } - - return assemblyInformationalVersion; - } - - public static bool IsNullOrWhiteSpace(this string s) - { - return s == null || string.IsNullOrEmpty(s.Trim()); - } - - private static readonly HashSet ValidCollectionNames = new HashSet(); - - public static string ToSafeString(this object obj) - { - return (obj ?? string.Empty).ToString(); - } - - public static int? TryGetInt(this string s) - { - int i; - return int.TryParse(s, out i) ? (int?) i : null; - } - - public static double? TryGetDouble(this string s) - { - double i; - return double.TryParse(s, out i) ? (double?) i : null; - } - - /// - /// Apply property name restrictions. Throws KeenException with an - /// explanation if a collection name is unacceptable. - /// - /// - public static void ValidatePropertyName(string property) - { - if (property.IsNullOrWhiteSpace()) - throw new KeenException("Property name may not be null or whitespace"); - - if (property.Length >= 256) - throw new KeenException("Property name must be less than 256 characters"); - - if (property.StartsWith("$")) - throw new KeenException("Property name may not start with \"$\""); - - if (property.Contains(".")) - throw new KeenException("Property name may not contain \".\""); - } - - /// - /// Apply the collection name restrictions. Throws KeenException with an - /// explanation if a collection name is unacceptable. - /// - /// - public static void ValidateEventCollectionName(string collection) - { - // Avoid cost of re-checking collection names that have already been validated. - // There is a race condition here, but it's harmless and does not justify the - // overhead of synchronization. - if (ValidCollectionNames.Contains(collection)) - return; - - if (null == collection) - throw new KeenException("Event collection name may not be null."); - if (collection.IsNullOrWhiteSpace()) - throw new KeenException("Event collection name may not be blank."); - if (collection.Length > 64) - throw new KeenException("Event collection name may not be longer than 64 characters."); - if (new Regex("[^\x00-\x7F]").Match(collection).Success) - throw new KeenException("Event collection name must contain only Ascii characters."); - if (collection.Contains("$")) - throw new KeenException("Event collection name may not contain \"$\"."); - if (collection.StartsWith("_")) - throw new KeenException("Event collection name may not begin with \"_\"."); - - ValidCollectionNames.Add(collection); - } - - /// - /// Check the 'error' field on a bulk insert operation response and return - /// the appropriate exception. - /// - /// Deserialized json response from a Keen API call. - public static Exception GetBulkApiError(JObject apiResponse) - { - var error = apiResponse.SelectToken("$.error"); - if (null == error) - return null; - - var errCode = error.SelectToken("$.name").ToString(); - var message = error.SelectToken("$.description").ToString(); - switch (errCode) - { - case "InvalidApiKeyError": - return new KeenInvalidApiKeyException(message); - - case "ResourceNotFoundError": - return new KeenResourceNotFoundException(message); - - case "NamespaceTypeError": - return new KeenNamespaceTypeException(message); - - case "InvalidEventError": - return new KeenInvalidEventException(message); - - case "ListsOfNonPrimitivesNotAllowedError": - return new KeenListsOfNonPrimitivesNotAllowedException(message); - - case "InvalidBatchError": - return new KeenInvalidBatchException(message); - - case "InternalServerError": - return new KeenInternalServerErrorException(message); - - case "InvalidKeenNamespaceProperty": - return new KeenInvalidKeenNamespacePropertyException(message); - - case "InvalidPropertyNameError": - return new KeenInvalidPropertyNameException(message); - - default: - Debug.WriteLine(string.Format("Unhandled error_code \"{0}\" : \"{1}\"", errCode, message)); - return new KeenException(errCode + " : " + message); - } - } - - - /// - /// Check the 'error_code' field and throw the appropriate exception if non-null. - /// - /// Deserialized json response from a Keen API call. - public static void CheckApiErrorCode(JObject apiResponse) - { - if (apiResponse == null) return; - if (apiResponse["error_code"] == null) return; - - var err = apiResponse["error_code"].Value(); - var msg = apiResponse["message"].Value(); - - switch (err) - { - case "InvalidApiKeyError": - throw new KeenInvalidApiKeyException(msg); - - case "ResourceNotFoundError": - throw new KeenResourceNotFoundException(msg); - - case "NamespaceTypeError": - throw new KeenNamespaceTypeException(msg); - - case "InvalidEventError": - throw new KeenInvalidEventException(msg); - - case "ListsOfNonPrimitivesNotAllowedError": - throw new KeenListsOfNonPrimitivesNotAllowedException(msg); - - case "InvalidBatchError": - throw new KeenInvalidBatchException(msg); - - case "InternalServerError": - throw new KeenInternalServerErrorException(msg); - - case "InvalidKeenNamespaceProperty": - throw new KeenInvalidKeenNamespacePropertyException(msg); - - default: - Debug.WriteLine(string.Format("Unhandled error_code \"{0}\" : \"{1}\"", err, msg)); - throw new KeenException(err + " : " + msg); - } - } - - public static IEnumerable Zip - (this IEnumerable first, - IEnumerable second, - Func resultSelector) - { - if (first == null) throw new ArgumentNullException("first"); - if (second == null) throw new ArgumentNullException("second"); - if (resultSelector == null) throw new ArgumentNullException("resultSelector"); - return ZipIterator(first, second, resultSelector); - } - - private static IEnumerable ZipIterator - (IEnumerable first, - IEnumerable second, - Func resultSelector) - { - using (var e1 = first.GetEnumerator()) - using (var e2 = second.GetEnumerator()) - while (e1.MoveNext() && e2.MoveNext()) - yield return resultSelector(e1.Current, e2.Current); - } - } -} diff --git a/Keen.NET_35/ProjectSettingsProvider.cs b/Keen.NET_35/ProjectSettingsProvider.cs deleted file mode 100644 index 6255240..0000000 --- a/Keen.NET_35/ProjectSettingsProvider.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; - -namespace Keen.NET_35 -{ - public class ProjectSettingsProvider : IProjectSettings - { - /// - /// The Keen.IO URL for this project. Usually this will be the - /// server address and API version. - /// - public string KeenUrl { get; protected set; } - - /// - /// The Project ID, identifying the data silo to be accessed. - /// - public string ProjectId { get; protected set; } - - /// - /// The Master API key, required for getting a collection schema - /// or deleting the entire event collection. - /// - public string MasterKey { get; protected set; } - - /// - /// The Write API key, required for inserting events. - /// - public string WriteKey { get; protected set; } - - /// - /// The Read API key, used with query requests. - /// - public string ReadKey { get; protected set; } - - /// - /// Obtains project setting values as constructor parameters - /// - /// Base Keen.IO service URL, required - /// Keen project id, required - /// Master API key, required for getting schema or deleting collections - /// Write API key, required for inserting events - /// Read API key - public ProjectSettingsProvider(string projectId, string masterKey = "", string writeKey = "", string readKey = "", string keenUrl = null) - { - KeenUrl = keenUrl ?? KeenConstants.ServerAddress + "/" + KeenConstants.ApiVersion + "/"; - ProjectId = projectId; - MasterKey = masterKey; - WriteKey = writeKey; - ReadKey = readKey; - } - - protected ProjectSettingsProvider() - { - } - - public override string ToString() - { - return string.Format("ProjectSettingsProviderEnv:{{\nKeenUrl:{0}; \nProjectId:{1}; \nMasterKey:{2}; \nWriteKey:{3}; \nReadKey:{4};\n}}", - KeenUrl, ProjectId, MasterKey, WriteKey, ReadKey); - } - } -} diff --git a/Keen.NET_35/ProjectSettingsProviderEnv.cs b/Keen.NET_35/ProjectSettingsProviderEnv.cs deleted file mode 100644 index b48e230..0000000 --- a/Keen.NET_35/ProjectSettingsProviderEnv.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; - -namespace Keen.NET_35 -{ - /// - /// Project settings provider which reads project settings from environment variables. - /// - public class ProjectSettingsProviderEnv : ProjectSettingsProvider - { - /// - /// Reads the project settings from environment variables - /// Project ID should be in variable KEEN_PROJECT_ID - /// Master Key should be in variable KEEN_MASTER_ID - /// Write Key should be in variable KEEN_WRITE_ID - /// ReadKey should be in variable KEEN_READ_ID - /// - public ProjectSettingsProviderEnv() - { - KeenUrl = Environment.GetEnvironmentVariable("KEEN_SERVER_URL") ?? KeenConstants.ServerAddress + "/" + KeenConstants.ApiVersion + "/"; - ProjectId = Environment.GetEnvironmentVariable("KEEN_PROJECT_ID") ?? ""; - MasterKey = Environment.GetEnvironmentVariable("KEEN_MASTER_KEY") ?? ""; - WriteKey = Environment.GetEnvironmentVariable("KEEN_WRITE_KEY") ?? ""; - ReadKey = Environment.GetEnvironmentVariable("KEEN_READ_KEY") ?? ""; - } - } -} diff --git a/Keen.NET_35/Properties/AssemblyInfo.cs b/Keen.NET_35/Properties/AssemblyInfo.cs deleted file mode 100644 index d7a8a4b..0000000 --- a/Keen.NET_35/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - - -// Friendly name and description for this assembly. -[assembly: AssemblyTitle("Keen.NET_35")] -[assembly: AssemblyDescription("Keen IO SDK for .NET 3.5")] - -// 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("4daa720a-3a64-4bce-b5f2-5f8d1c21a881")] - -// See also: SharedAssemblyInfo.cs and SharedVersionInfo.cs diff --git a/Keen.NET_35/ScopedKey.cs b/Keen.NET_35/ScopedKey.cs deleted file mode 100644 index bfe4deb..0000000 --- a/Keen.NET_35/ScopedKey.cs +++ /dev/null @@ -1,161 +0,0 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; -using System.IO; -using System.Linq; -using System.Security.Cryptography; -using System.Text; - - -namespace Keen.NET_35 -{ - /// - /// ScopedKey provides encryption and decryption functions which can be used to create - /// and read scoped keys, such as the API Read and Write keys. - /// - public static class ScopedKey - { - private static readonly int KeySizeShort = 32; - private static readonly int KeySizeLong = 64; - private static readonly int IVHexSize = 32; - - - /// - /// Encrypt an object containing security options to create a scoped key. - /// - /// Master API key - /// An object that can be serialized to produce JSON formatted Security Options - /// Optional IV, normally not required - /// - public static string Encrypt(string apiKey, object secOptions, string IV = null) - { - var secOptionsJson = JObject.FromObject(secOptions ?? new object()).ToString(Formatting.None); - - return EncryptString( apiKey, secOptionsJson, IV); - } - - /// - /// Encrypt a string containing JSON formatted Security Options to create a scoped key. - /// - /// Master API key - /// Security Options in JSON format - /// Optional IV, normally not required - /// Hex-encoded scoped key - public static string EncryptString(string apiKey, string secOptions, string IV = null) - { - try - { - if (!(null == IV || IV.Length == IVHexSize)) - throw new KeenException(string.Format("Hex-encoded IV must be exactly {0} bytes, got {1}", IVHexSize, IV.Length)); - - IV = IV ?? ""; - secOptions = secOptions ?? ""; - - using (var aesAlg = GetAes(ConvertKey(apiKey), IV)) - using (var encryptor = aesAlg.CreateEncryptor()) - using (var msCrypt = new MemoryStream()) - { - using (var csCrypt = new CryptoStream(msCrypt, encryptor, CryptoStreamMode.Write)) - using (var swCrypt = new StreamWriter(csCrypt)) - swCrypt.Write(secOptions); - - return ByteToHex(aesAlg.IV) + ByteToHex(msCrypt.ToArray()); - } - } - catch (Exception e) - { - throw new KeenException("Encryption error", e); - } - } - - /// - /// Decrypt an existing scoped key. - /// - /// Master API key - /// Scoped key to be decrypted - /// JSON formatted Security Options - public static string Decrypt(string apiKey, string scopedKey) - { - try - { - scopedKey = scopedKey ?? ""; - - // The IV is stored at the front of the string - var IV = scopedKey.Substring(0, IVHexSize); - - // Encrypted data is stored after the IV part of the key - var cryptHex = scopedKey.Substring(IVHexSize, scopedKey.Length - IVHexSize); - - using (var aesAlg = GetAes(ConvertKey(apiKey), IV)) - using (var decryptor = aesAlg.CreateDecryptor()) - using (var msCrypt = new MemoryStream(HexToByte(cryptHex))) - using (var csCrypt = new CryptoStream(msCrypt, decryptor, CryptoStreamMode.Read)) - using (var srCrypt = new StreamReader(csCrypt)) - return srCrypt.ReadToEnd(); - } - catch (Exception ex) - { - throw new KeenException("Decryption error" + ex.Message, ex); - } - } - - /// - /// Convert an apiKey string to a byte array. - /// - /// - /// - private static byte[] ConvertKey(string apiKey) - { - // Validate that the key matches the expected length of a Keen API Master key. - if (string.IsNullOrEmpty(apiKey) || ((apiKey.Length != KeySizeShort) && (apiKey.Length != KeySizeLong))) - throw new KeenException(string.Format("Keen API Key must be either 32 or 64 characters")); - - byte[] aesKey; - if (apiKey.Length == KeySizeLong) - aesKey = HexToByte(apiKey); - else - aesKey = Encoding.UTF8.GetBytes(apiKey); - - return aesKey; - } - - /// - /// Set up an Aes instance with the correct mode, key and IV - /// - /// Encryption key - /// Initialization Vector, if left blank one will be generated. - /// - private static Aes GetAes(byte[] Key, string IV) - { - var aesAlg = AesCryptoServiceProvider.Create(); - aesAlg.KeySize = KeySizeShort * 8; // key size in bits - aesAlg.Mode = CipherMode.CBC; - aesAlg.Padding = PaddingMode.PKCS7; - aesAlg.Key = Key; - - if (!string.IsNullOrEmpty(IV)) - aesAlg.IV = HexToByte(IV); - - return aesAlg; - } - - private static string ByteToHex(byte[] a) - { - return String.Concat(a.Select(b => b.ToString("X2")).ToArray()); - } - - private static byte[] HexToByte(string hex) - { - if (hex.Length % 2 == 1) - throw new Exception("Hex string must have an even number of characters"); - - Func hexMap = (h) => h - (h < 58 ? 48 : (h < 97 ? 55 : 87)); - - var result = new byte[hex.Length >> 1]; - for (int i = 0; i < (hex.Length >> 1); ++i) - result[i] = (byte)((hexMap(hex[i << 1]) << 4) + (hexMap(hex[(i << 1) + 1]))); - - return result; - } - } -} diff --git a/Keen.NET_35/packages.config b/Keen.NET_35/packages.config deleted file mode 100644 index b3a36fd..0000000 --- a/Keen.NET_35/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/Keen.Net/EventCacheMemory.cs b/Keen.Net/EventCacheMemory.cs deleted file mode 100644 index 3e69ed2..0000000 --- a/Keen.Net/EventCacheMemory.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Keen.Core; -using Keen.Core.EventCache; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - - -namespace Keen.Net -{ - /// - /// This is a simple memory-based cache provider. It has no cache-expiration policy. - /// To use, pass an instance of this class when constructing KeenClient - /// - /// - public class EventCacheMemory : IEventCache - { - private Queue events = new Queue(); - - public Task Add(CachedEvent e) - { - if (null == e) - throw new KeenException("Cached events may not be null"); - - return Task.Run(() => - { - lock (events) - events.Enqueue(e); - }); - } - - public Task Clear() - { - return Task.Run(() => - { - lock (events) - events.Clear(); - }); - } - - public Task TryTake() - { - return Task.Run(() => - { - lock (events) - return events.Any() ? events.Dequeue() : null; - }); - } - } -} diff --git a/Keen.Net/Keen.Net.csproj b/Keen.Net/Keen.Net.csproj deleted file mode 100644 index b850817..0000000 --- a/Keen.Net/Keen.Net.csproj +++ /dev/null @@ -1,125 +0,0 @@ - - - - - Debug - AnyCPU - {0CD3383E-5267-48BC-99CD-1B3AE8AFF7BA} - Library - Properties - Keen.Net - Keen.Net - v4.5 - 512 - a30e4430 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - bin\netfx\ - TRACE - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - - - - ..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll - True - - - ..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll - True - - - ..\packages\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll - True - - - ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll - True - - - ..\packages\PCLStorage.1.0.2\lib\net45\PCLStorage.dll - True - - - ..\packages\PCLStorage.1.0.2\lib\net45\PCLStorage.Abstractions.dll - True - - - - - - - ..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll - True - - - ..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll - True - - - - - - - - - - - - Properties\SharedAssemblyInfo.cs - - - Properties\SharedVersionInfo.cs - - - - - - - - - - {36d156bf-8523-42ec-9ad6-7c3ac05d699f} - Keen - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - \ No newline at end of file diff --git a/Keen.Net/ProjectSettingsProviderEnv.cs b/Keen.Net/ProjectSettingsProviderEnv.cs deleted file mode 100644 index ec6cc47..0000000 --- a/Keen.Net/ProjectSettingsProviderEnv.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Keen.Core; -using System; - - -namespace Keen.Net -{ - /// - /// Project settings provider which reads project settings from environment variables. - /// - public class ProjectSettingsProviderEnv : ProjectSettingsProvider - { - /// - /// Reads the project settings from environment variables - /// Project ID should be in variable KEEN_PROJECT_ID - /// Master Key should be in variable KEEN_MASTER_KEY - /// Write Key should be in variable KEEN_WRITE_KEY - /// ReadKey should be in variable KEEN_READ_KEY - /// Keen.IO API url should be in variable KEEN_SERVER_URL - /// - public ProjectSettingsProviderEnv() - { - KeenUrl = Environment.GetEnvironmentVariable("KEEN_SERVER_URL") ?? KeenConstants.ServerAddress + "/" + KeenConstants.ApiVersion + "/"; - ProjectId = Environment.GetEnvironmentVariable("KEEN_PROJECT_ID") ?? ""; - MasterKey = Environment.GetEnvironmentVariable("KEEN_MASTER_KEY") ?? ""; - WriteKey = Environment.GetEnvironmentVariable("KEEN_WRITE_KEY") ?? ""; - ReadKey = Environment.GetEnvironmentVariable("KEEN_READ_KEY") ?? ""; - } - } -} diff --git a/Keen.Net/ProjectSettingsProviderFile.cs b/Keen.Net/ProjectSettingsProviderFile.cs deleted file mode 100644 index 97cc24f..0000000 --- a/Keen.Net/ProjectSettingsProviderFile.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Keen.Core; -using System.IO; - - -namespace Keen.Net -{ - /// - /// Project settings provider which reads project settings from a text file. - /// - public class ProjectSettingsProviderFile : ProjectSettingsProvider - { - /// - /// Reads the project settings from a text file. - /// Each setting takes one line, in the order Project ID, - /// Master Key, Write Key, Read Key. Unused values should be represented - /// with a blank line. - /// - public ProjectSettingsProviderFile(string filePath) - { - // TODO : Add Keen Server URL as one of the lines, optionally. - // TODO : Master key maybe should be de-emphasized and not be first. - // TODO : Share init of properties with base class implementation. - var values = File.ReadAllLines(filePath); - if (values.Length != 4) - throw new KeenException("Invalid project settings file, file must contain exactly 4 lines: " + filePath); - - ProjectId = values[0]; - MasterKey = values[1]; - WriteKey = values[2]; - ReadKey = values[3]; - } - } -} diff --git a/Keen.Net/Properties/AssemblyInfo.cs b/Keen.Net/Properties/AssemblyInfo.cs deleted file mode 100644 index d98703a..0000000 --- a/Keen.Net/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - - -// Friendly name and description for this assembly. -[assembly: AssemblyTitle("Keen.Net")] -[assembly: AssemblyDescription("Keen IO SDK for .NET 4/4.5+")] - -// 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("9b50f64b-4d04-40f5-bfa5-96d8a4a0ed7d")] - -// See also: SharedAssemblyInfo.cs and SharedVersionInfo.cs diff --git a/Keen.Net/ScopedKey.cs b/Keen.Net/ScopedKey.cs deleted file mode 100644 index bb7b5e3..0000000 --- a/Keen.Net/ScopedKey.cs +++ /dev/null @@ -1,161 +0,0 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; -using System.IO; -using System.Linq; -using System.Security.Cryptography; -using System.Text; - - -namespace Keen.Core -{ - /// - /// ScopedKey provides encryption and decryption functions which can be used to create - /// and read scoped keys, such as the API Read and Write keys. - /// - public static class ScopedKey - { - private static readonly int KeySizeShort = 32; - private static readonly int KeySizeLong = 64; - private static readonly int IVHexSize = 32; - - - /// - /// Encrypt an object containing security options to create a scoped key. - /// - /// Master API key - /// An object that can be serialized to produce JSON formatted Security Options - /// Optional IV, normally not required - /// Hex-encoded scoped key - public static string Encrypt(string apiKey, object secOptions, string IV = null) - { - var secOptionsJson = JObject.FromObject(secOptions ?? new object()).ToString(Formatting.None); - - return EncryptString(apiKey, secOptionsJson, IV); - } - - /// - /// Encrypt a string containing JSON formatted Security Options to create a scoped key. - /// - /// Master API key - /// Security Options in JSON format - /// Optional IV, normally not required - /// Hex-encoded scoped key - public static string EncryptString(string apiKey, string secOptions, string IV = null) - { - try - { - if (!(null == IV || IV.Length == IVHexSize)) - throw new KeenException(string.Format("Hex-encoded IV must be exactly {0} bytes, got {1}", IVHexSize, IV.Length)); - - IV = IV ?? ""; - secOptions = secOptions ?? ""; - - using (var aesAlg = GetAes(ConvertKey(apiKey), IV)) - using (var encryptor = aesAlg.CreateEncryptor()) - using (var msCrypt = new MemoryStream()) - { - using (var csCrypt = new CryptoStream(msCrypt, encryptor, CryptoStreamMode.Write)) - using (var swCrypt = new StreamWriter(csCrypt)) - swCrypt.Write(secOptions); - - return ByteToHex(aesAlg.IV) + ByteToHex(msCrypt.ToArray()); - } - } - catch (Exception e) - { - throw new KeenException("Encryption error", e); - } - } - - /// - /// Decrypt an existing scoped key. - /// - /// Master API key - /// Scoped key to be decrypted - /// JSON formatted Security Options - public static string Decrypt(string apiKey, string scopedKey) - { - try - { - scopedKey = scopedKey ?? ""; - - // The IV is stored at the front of the string - var IV = scopedKey.Substring(0, IVHexSize); - - // Encrypted data is stored after the IV part of the key - var cryptHex = scopedKey.Substring(IVHexSize, scopedKey.Length - IVHexSize); - - using (var aesAlg = GetAes(ConvertKey(apiKey), IV)) - using (var decryptor = aesAlg.CreateDecryptor()) - using (var msCrypt = new MemoryStream(HexToByte(cryptHex))) - using (var csCrypt = new CryptoStream(msCrypt, decryptor, CryptoStreamMode.Read)) - using (var srCrypt = new StreamReader(csCrypt)) - return srCrypt.ReadToEnd(); - } - catch (Exception ex) - { - throw new KeenException("Decryption error", ex); - } - } - - /// - /// Convert an apiKey string to a byte array. - /// - /// The key used to encrypt. - /// A byte array of the correct size and format for the key length. - private static byte[] ConvertKey(string apiKey) - { - // Validate that the key matches the expected length of a Keen API Master key. - if (string.IsNullOrWhiteSpace(apiKey) || ((apiKey.Length != KeySizeShort) && (apiKey.Length != KeySizeLong))) - throw new KeenException(string.Format("Keen API Key must be either 32 or 64 characters")); - - byte[] aesKey; - if (apiKey.Length == KeySizeLong) - aesKey = HexToByte(apiKey); - else - aesKey = Encoding.UTF8.GetBytes(apiKey); - - return aesKey; - } - - /// - /// Set up an Aes instance with the correct mode, key and IV - /// - /// Encryption key - /// Initialization Vector, if left blank one will be generated. - /// An AES instance appropriate for encrypting with this key and IV. - private static Aes GetAes(byte[] Key, string IV) - { - var aesAlg = AesCryptoServiceProvider.Create(); - aesAlg.KeySize = KeySizeShort * 8; // key size in bits - aesAlg.Mode = CipherMode.CBC; - aesAlg.Padding = PaddingMode.PKCS7; - aesAlg.Key = Key; - - if (!string.IsNullOrEmpty(IV)) - aesAlg.IV = HexToByte(IV); - - return aesAlg; - } - - private static string ByteToHex(byte[] a) - { - return String.Concat(a.Select(b => b.ToString("X2")).ToArray()); - } - - private static byte[] HexToByte(string hex) - { - if (hex.Length % 2 == 1) - throw new Exception("Hex string must have an even number of characters"); - - Func hexMap = (h) => h - (h < 58 ? 48 : (h < 97 ? 55 : 87)); - - var result = new byte[hex.Length >> 1]; - for (int i = 0; i < (hex.Length >> 1); ++i) - result[i] = (byte)((hexMap(hex[i << 1]) << 4) + (hexMap(hex[(i << 1) + 1]))); - - return result; - } - } -} diff --git a/Keen.Net/app.config b/Keen.Net/app.config deleted file mode 100644 index 940d25c..0000000 --- a/Keen.Net/app.config +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/Keen.Net/packages.config b/Keen.Net/packages.config deleted file mode 100644 index d24f9e0..0000000 --- a/Keen.Net/packages.config +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/Keen/CachedEvent.cs b/Keen/CachedEvent.cs deleted file mode 100644 index 9cc2714..0000000 --- a/Keen/CachedEvent.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Newtonsoft.Json.Linq; -using System; - - -namespace Keen.Core.EventCache -{ - /// - /// CachedEvent is a container for user event data which associates the - /// target event collection name and, if an error occurs during submission, - /// the exception instance. - /// - public class CachedEvent - { - public string Collection { get; set; } - public JObject Event { get; set; } - public Exception Error { get; set; } - - public CachedEvent(string collection, JObject e, Exception err = null) - { - Collection = collection; - Event = e; - Error = err; - } - - public override string ToString() - { - return string.Format("CachedEvent:{{\n\"Collection\": \"{0}\",\n\"Event\":{1},\n\"Error\":\"{2}:{3}\"\n}}", - Collection, Event, Error == null ? "null" : Error.GetType().Name, Error == null ? "" : Error.Message); - } - } -} diff --git a/Keen/DataEnrichment/EventAddOn.cs b/Keen/DataEnrichment/EventAddOn.cs deleted file mode 100644 index 3488625..0000000 --- a/Keen/DataEnrichment/EventAddOn.cs +++ /dev/null @@ -1,111 +0,0 @@ -using Newtonsoft.Json; -using System.Collections.Generic; - - -namespace Keen.Core.DataEnrichment -{ - /// - /// Represents a Data Enrichment add-on. - /// - /// https://keen.io/docs/data-collection/data-enrichment/ - /// - /// - public sealed class AddOn - { - /// - /// Name of the add-on - /// - [JsonProperty(PropertyName = "name")] - public string Name { get; private set; } - - /// - /// Parameters required by the add-on - /// - [JsonProperty(PropertyName = "input")] - public Dictionary Input { get; private set; } - - /// - /// Target property name where the enriched data should be stored. - /// - [JsonProperty(PropertyName = "output")] - public string Output { get; private set; } - - /// Name of the data enhancement add-on. - /// Name-value pairs of input parameters required by the add-on. - /// Target property name for the enriched data. - public AddOn(string name, IDictionary input, string output) - { - if (output.StartsWith("keen.")) - throw new KeenInvalidPropertyNameException( - "Add-on event output name may not be in the keen namespace:" + output); - - Name = name; - Input = new Dictionary(input); - Output = output; - } - - /// - /// Build and return an IpToGeo Data Enhancement add-on. This add-on reads - /// an IP address from the field identified by the input parameter and writes - /// data about the geographical location to the field identified by the output parameter. - /// - /// Name of field to store the geographical information - /// Name of field containing an IP address - /// - public static AddOn IpToGeo(string ipField, string outputField) - { - return new AddOn("keen:ip_to_geo", - new Dictionary { { "ip", ipField } }, - outputField); - } - - /// - /// Build and return a User-Agent Data Enhancement add-on. This add-on reads - /// a user agent string from the field identified by the input parameter and parses it - /// into the device, browser, browser version, OS, and OS version fields and stores that - /// data in the field identified by the output parameter. - /// - /// Name of field to store the parsed user agent field - /// Name of field containing the user agent string - /// - public static AddOn UserAgentParser(string userAgentString, string outputField) - { - return new AddOn("keen:ua_parser", - new Dictionary { { "ua_string", userAgentString } }, - outputField); - } - - /// - /// Build and return a URL Parser Data Enhancement add-on. This add-on reads - /// a well-formed URL from the field identified by the input parameter and parses - /// it into it's components for easier filtering. The components are stored in the - /// field identified by the output parameter. - /// - /// Name of field containing the URL to parse - /// Name of field to store the parsed url components - /// - public static AddOn UrlParser(string urlField, string outputField) - { - return new AddOn("keen:url_parser", - new Dictionary { { "url", urlField } }, - outputField); - } - - /// - /// Build and return a Referrer Parser Data Enhancement add-on. This add-on reads - /// a well-formed referrer URL from the field identified by the input parameter and - /// parses it into it's components. The components are stored in the field identified - /// by the output parameter. - /// - /// Name of field containing the URL of the current page - /// Name of field to store the parsed referrer data. - /// Name of field containing the referrer URL - /// - public static AddOn ReferrerParser(string referrerUrlField, string pageUrlField, string outputField) - { - return new AddOn("keen:referrer_parser", - new Dictionary { { "referrer_url", referrerUrlField }, { "page_url", pageUrlField } }, - outputField); - } - } -} \ No newline at end of file diff --git a/Keen/Dataset/DatasetDefinition.cs b/Keen/Dataset/DatasetDefinition.cs deleted file mode 100644 index 4439346..0000000 --- a/Keen/Dataset/DatasetDefinition.cs +++ /dev/null @@ -1,76 +0,0 @@ -using Keen.Core.Query; -using System; -using System.Collections.Generic; -using System.Linq; - - -namespace Keen.Core.Dataset -{ - public class DatasetDefinition - { - /// - /// Name of the dataset, which is used as an identifier. Must be unique per project. - /// - public string DatasetName { get; set; } - - /// - /// The human-readable string name for your Cached Dataset. - /// - public string DisplayName { get; set; } - - /// - /// Holds information describing the query which is cached by this Cached Dataset. - /// - public QueryDefinition Query { get; set; } - - /// - /// When the most recent computation was queued. - /// - public DateTime? LastScheduledDate { get; set; } - - /// - /// The most recent interval that has been computed for the Cached Dataset. - /// - public DateTime? LatestSubtimeframeAvailable { get; set; } - - /// - /// The difference between now and the most recent datapoint computed. - /// - public long MillisecondsBehind { get; set; } - - /// - /// The event property name of string values results are retrieved by. - /// - public IEnumerable IndexBy { get; set; } - } - - internal static class DatasetDefinitionExtensions - { - public static void Validate(this DatasetDefinition dataset) - { - if (string.IsNullOrWhiteSpace(dataset.DatasetName)) - { - throw new KeenException("DatasetDefinition must have a name."); - } - - if (string.IsNullOrWhiteSpace(dataset.DisplayName)) - { - throw new KeenException("DatasetDefinition must have a display name."); - } - - if (null == dataset.IndexBy || - string.IsNullOrWhiteSpace(dataset.IndexBy.FirstOrDefault())) - { - throw new KeenException("DatasetDefinition must specify a property by which to " + - "index."); - } - - if (null == dataset.Query) - { - throw new KeenException("DatasetDefinition must contain a query to be cached."); - } - - dataset.Query.ValidateForCachedDataset(); - } - } -} diff --git a/Keen/Dataset/DatasetDefinitionCollection.cs b/Keen/Dataset/DatasetDefinitionCollection.cs deleted file mode 100644 index adf6a10..0000000 --- a/Keen/Dataset/DatasetDefinitionCollection.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Collections.Generic; - - -namespace Keen.Core.Dataset -{ - /// - /// A model for a collection of DatasetDefinitions with paging information. - /// - public class DatasetDefinitionCollection - { - /// - /// A list of the DatasetDefinitions returned in this page. - /// - public IEnumerable Datasets { get; set; } - - /// - /// The url of the next page of Dataset definitions. - /// - public string NextPageUrl { get; set; } - - /// - /// The total count of Cached Datasets for this project. - /// - public int Count { get; set; } - } -} diff --git a/Keen/Dataset/Datasets.cs b/Keen/Dataset/Datasets.cs deleted file mode 100644 index 6fc9ddb..0000000 --- a/Keen/Dataset/Datasets.cs +++ /dev/null @@ -1,306 +0,0 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using Newtonsoft.Json.Serialization; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Threading.Tasks; - - -namespace Keen.Core.Dataset -{ - /// - /// Datasets implements the IDataset interface which represents the Keen.IO Cached Datasets - /// API methods. - /// - internal class Datasets : IDataset - { - private const int MAX_DATASET_DEFINITION_LIST_LIMIT = 100; - private static readonly JsonSerializerSettings SERIALIZER_SETTINGS = - new JsonSerializerSettings - { - ContractResolver = new DefaultContractResolver - { - NamingStrategy = new SnakeCaseNamingStrategy() - }, - DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate, - Formatting = Formatting.None - }; - - private readonly IKeenHttpClient _keenHttpClient; - private readonly string _cachedDatasetRelativeUrl; - private readonly string _masterKey; - - - internal Datasets(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); - _cachedDatasetRelativeUrl = - KeenHttpClient.GetRelativeUrl(prjSettings.ProjectId, - KeenConstants.DatasetsResource); - - _masterKey = prjSettings.MasterKey; - } - - public async Task GetResultsAsync(string datasetName, - string indexBy, - string timeframe) - { - if (string.IsNullOrWhiteSpace(datasetName)) - { - throw new KeenException("A dataset name is required."); - } - - if (string.IsNullOrWhiteSpace(indexBy)) - { - throw new KeenException("A value to index by is required."); - } - - if (string.IsNullOrWhiteSpace(timeframe)) - { - throw new KeenException("A timeframe by is required."); - } - - if (string.IsNullOrWhiteSpace(_masterKey)) - { - throw new KeenException("An API masterkey is required to get dataset results."); - } - - var datasetResultsUrl = $"{GetDatasetUrl(datasetName)}/results"; - - var url = $"{datasetResultsUrl}?index_by={indexBy}&timeframe={timeframe}"; - - var responseMsg = await _keenHttpClient - .GetAsync(url, _masterKey) - .ConfigureAwait(continueOnCapturedContext: false); - - var responseString = await responseMsg - .Content - .ReadAsStringAsync() - .ConfigureAwait(continueOnCapturedContext: false); - - var response = JObject.Parse(responseString); - - KeenUtil.CheckApiErrorCode(response); - - if (!responseMsg.IsSuccessStatusCode) - { - throw new KeenException($"Request failed with status: {responseMsg.StatusCode}"); - } - - return response; - } - - public async Task GetDefinitionAsync(string datasetName) - { - if (string.IsNullOrWhiteSpace(datasetName)) - { - throw new KeenException("A dataset name is required."); - } - - if (string.IsNullOrWhiteSpace(_masterKey)) - { - throw new KeenException("An API masterkey is required to get dataset results."); - } - - var responseMsg = await _keenHttpClient - .GetAsync(GetDatasetUrl(datasetName), _masterKey) - .ConfigureAwait(continueOnCapturedContext: false); - - var responseString = await responseMsg - .Content - .ReadAsStringAsync() - .ConfigureAwait(continueOnCapturedContext: false); - - var response = JObject.Parse(responseString); - - KeenUtil.CheckApiErrorCode(response); - - if (!responseMsg.IsSuccessStatusCode) - { - throw new KeenException($"Request failed with status: {responseMsg.StatusCode}"); - } - - return JsonConvert.DeserializeObject(responseString, - SERIALIZER_SETTINGS); - } - - public async Task ListDefinitionsAsync( - int limit = 10, - string afterName = null) - { - if (string.IsNullOrWhiteSpace(_masterKey)) - { - throw new KeenException("An API masterkey is required to get dataset results."); - } - - var datasetResultsUrl = $"{_cachedDatasetRelativeUrl}?limit={limit}"; - - if (!string.IsNullOrWhiteSpace(afterName)) - { - datasetResultsUrl += $"&after_name={afterName}"; - } - - var responseMsg = await _keenHttpClient - .GetAsync(datasetResultsUrl, _masterKey) - .ConfigureAwait(continueOnCapturedContext: false); - - var responseString = await responseMsg - .Content - .ReadAsStringAsync() - .ConfigureAwait(continueOnCapturedContext: false); - - var response = JObject.Parse(responseString); - - KeenUtil.CheckApiErrorCode(response); - - if (!responseMsg.IsSuccessStatusCode) - { - throw new KeenException($"Request failed with status: {responseMsg.StatusCode}"); - } - - return JsonConvert.DeserializeObject(responseString, - SERIALIZER_SETTINGS); - } - - public async Task> ListAllDefinitionsAsync() - { - var allDefinitions = new List(); - var firstSet = await ListDefinitionsAsync(MAX_DATASET_DEFINITION_LIST_LIMIT) - .ConfigureAwait(continueOnCapturedContext: false); - - if (null == firstSet?.Datasets) - { - throw new KeenException("Failed to fetch definition list"); - } - - if (!firstSet.Datasets.Any()) - { - return allDefinitions; - } - - if (firstSet.Count <= firstSet.Datasets.Count()) - { - return firstSet.Datasets; - } - - allDefinitions.AddRange(firstSet.Datasets); - - do - { - var nextSet = await ListDefinitionsAsync(MAX_DATASET_DEFINITION_LIST_LIMIT, - allDefinitions.Last().DatasetName) - .ConfigureAwait(continueOnCapturedContext: false); - - if (null == nextSet?.Datasets || !nextSet.Datasets.Any()) - { - throw new KeenException("Failed to fetch definition list"); - } - - allDefinitions.AddRange(nextSet.Datasets); - } while (firstSet.Count > allDefinitions.Count); - - return allDefinitions; - } - - public async Task DeleteDatasetAsync(string datasetName) - { - if (string.IsNullOrWhiteSpace(datasetName)) - { - throw new KeenException("A dataset name is required."); - } - - if (string.IsNullOrWhiteSpace(_masterKey)) - { - throw new KeenException("An API masterkey is required to get dataset results."); - } - - var responseMsg = await _keenHttpClient - .DeleteAsync(GetDatasetUrl(datasetName), _masterKey) - .ConfigureAwait(continueOnCapturedContext: false); - - var responseString = await responseMsg - .Content - .ReadAsStringAsync() - .ConfigureAwait(continueOnCapturedContext: false); - - if (HttpStatusCode.NoContent == responseMsg.StatusCode) - { - return; - } - - var response = JObject.Parse(responseString); - - KeenUtil.CheckApiErrorCode(response); - - throw new KeenException($"Request failed with status: {responseMsg.StatusCode}"); - } - - public async Task CreateDatasetAsync(DatasetDefinition dataset) - { - if (string.IsNullOrWhiteSpace(_masterKey)) - { - throw new KeenException("An API masterkey is required to get dataset results."); - } - - // Validate - if (null == dataset) - { - throw new KeenException("An instance of DatasetDefinition must be provided"); - } - - // This throws if dataset is not valid. - dataset.Validate(); - - var content = JsonConvert.SerializeObject(dataset, SERIALIZER_SETTINGS); - - var responseMsg = await _keenHttpClient - .PutAsync(GetDatasetUrl(dataset.DatasetName), _masterKey, content) - .ConfigureAwait(continueOnCapturedContext: false); - - var responseString = await responseMsg - .Content - .ReadAsStringAsync() - .ConfigureAwait(continueOnCapturedContext: false); - - var response = JObject.Parse(responseString); - - KeenUtil.CheckApiErrorCode(response); - - if (!responseMsg.IsSuccessStatusCode) - { - throw new KeenException($"Request failed with status: {responseMsg.StatusCode}"); - } - - return JsonConvert.DeserializeObject(responseString, - SERIALIZER_SETTINGS); - } - - private string GetDatasetUrl(string datasetName = null) - { - return $"{_cachedDatasetRelativeUrl}/{datasetName}"; - } - } -} diff --git a/Keen/Dataset/IDataset.cs b/Keen/Dataset/IDataset.cs deleted file mode 100644 index e24b1f5..0000000 --- a/Keen/Dataset/IDataset.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Newtonsoft.Json.Linq; -using System.Collections.Generic; -using System.Threading.Tasks; - - -namespace Keen.Core.Dataset -{ - public interface IDataset - { - /// - /// Get query results from a Cached Dataset. - /// - /// Name of cached dataset to query. - /// The string property value by which to retrieve results. - /// Limits retrieval of results to a specific portion of the - /// Cached Dataset - /// A JObject containing query results and metadata defining the cached - /// dataset. - Task GetResultsAsync(string datasetName, string indexBy, string timeframe); - - /// - /// Get the definition of your cached dataset. - /// - /// Name of cached dataset for which to retrieve the - /// definition. - /// An DatasetDefinition containing metadata about a cached dataset. - Task GetDefinitionAsync(string datasetName); - - /// - /// Lists the first n cached dataset definitions in your project. - /// - /// How many cached dataset definitions to return at a time (1-100). - /// Defaults to 10. - /// A cursor for use in pagination. afterName is the Cached Dataset - /// name that defines your place in the list. - Task ListDefinitionsAsync(int limit = 10, - string afterName = null); - - /// - /// Lists all the dataset definitions in the project. - /// - /// An enumerable of DatasetDefinitions. - Task> ListAllDefinitionsAsync(); - - /// - /// Delete a Cached Dataset - /// - /// The name of the dataset to be deleted. - Task DeleteDatasetAsync(string datasetName); - - /// - /// Creates a new Cached Dataset - /// - /// An instance of DatasetDefinition. At minimum, it must have - /// DatasetName, DisplayName, IndexBy and Query populated. - /// An instance of DatasetDefinition populated with more information about the - /// create Dataset. - Task CreateDatasetAsync(DatasetDefinition dataset); - } -} diff --git a/Keen/Dataset/QueryDefinitionExtensions.cs b/Keen/Dataset/QueryDefinitionExtensions.cs deleted file mode 100644 index 616d966..0000000 --- a/Keen/Dataset/QueryDefinitionExtensions.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Keen.Core.Query; - - -namespace Keen.Core.Dataset -{ - internal static class QueryDefinitionExtensions - { - public static void ValidateForCachedDataset(this QueryDefinition query) - { - if (string.IsNullOrWhiteSpace(query.AnalysisType)) - { - throw new KeenException("QueryDefinition must have an analysis type"); - } - - if (string.IsNullOrWhiteSpace(query.EventCollection)) - { - throw new KeenException("QueryDefinition must specify an event collection"); - } - - if (string.IsNullOrWhiteSpace(query.Timeframe)) - { - throw new KeenException("QueryDefinition must specify a timeframe"); - } - - if (string.IsNullOrWhiteSpace(query.Interval)) - { - throw new KeenException("QueryDefinition must specify an interval"); - } - } - } -} diff --git a/Keen/DynamicPropertyValue.cs b/Keen/DynamicPropertyValue.cs deleted file mode 100644 index 5afe658..0000000 --- a/Keen/DynamicPropertyValue.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; - - -namespace Keen.Core -{ - /// - /// An instance of DynamicPropertyValue containing a delegate - /// can be added to the GlobalProperties collection. When AddEvent - /// inserts GlobalProperties into an event, the delegate will be - /// executed to provide the value of the property. - /// - public class DynamicPropertyValue : IDynamicPropertyValue - { - private Func _value; - - /// - /// Call the delegate that produces the property value - /// - /// The value produced by the delegate - public object Value() - { - return _value(); - } - - /// - /// - /// - /// A delegate that will be called each time the property value is required - public DynamicPropertyValue(Func value) - { - _value = value; - } - } -} diff --git a/Keen/Event.cs b/Keen/Event.cs deleted file mode 100644 index ec6a690..0000000 --- a/Keen/Event.cs +++ /dev/null @@ -1,156 +0,0 @@ -using Keen.Core.EventCache; -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - - -namespace Keen.Core -{ - /// - /// Event implements the IEvent interface which represents the Keen.IO Event API methods. - /// - internal class Event : IEvent - { - private readonly IKeenHttpClient _keenHttpClient; - private readonly string _eventsRelativeUrl; - private readonly string _readKey; - private readonly string _writeKey; - - - internal Event(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); - _eventsRelativeUrl = KeenHttpClient.GetRelativeUrl(prjSettings.ProjectId, - KeenConstants.EventsResource); - - _readKey = prjSettings.ReadKey; - _writeKey = prjSettings.WriteKey; - } - - - /// - /// Get details of all schemas in the project. - /// - /// - public async Task GetSchemas() - { - if (string.IsNullOrWhiteSpace(_readKey)) - { - throw new KeenException("An API ReadKey is required to get schemas."); - } - - var responseMsg = await _keenHttpClient - .GetAsync(_eventsRelativeUrl, _readKey) - .ConfigureAwait(continueOnCapturedContext: false); - - var responseString = await responseMsg - .Content - .ReadAsStringAsync() - .ConfigureAwait(continueOnCapturedContext: false); - - var response = JArray.Parse(responseString); - - // error checking, throw an exception with information from the json - // response if available, then check the HTTP response. - KeenUtil.CheckApiErrorCode(response); - - if (!responseMsg.IsSuccessStatusCode) - { - throw new KeenException("GetSchemas failed with status: " + - responseMsg.StatusCode); - } - - return response; - } - - /// - /// Add all events in a single request. - /// - /// - /// - public async Task> AddEvents(JObject events) - { - if (string.IsNullOrWhiteSpace(_writeKey)) - { - throw new KeenException("An API WriteKey is required to add events."); - } - - var content = events.ToString(); - - var responseMsg = await _keenHttpClient - .PostAsync(_eventsRelativeUrl, _writeKey, content) - .ConfigureAwait(continueOnCapturedContext: false); - - var responseString = await responseMsg - .Content - .ReadAsStringAsync() - .ConfigureAwait(continueOnCapturedContext: false); - - JObject jsonResponse = null; - - try - { - // Normally the response content should be parsable JSON, - // but if the server returned a 404 error page or something - // like that, this will throw. - jsonResponse = JObject.Parse(responseString); - - // TODO : Why do we not call KeenUtil.CheckApiErrorCode(jsonResponse); ?? - } - catch (Exception) - { - } - - 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."); - } - - // error checking, return failed events in the list, - // or if the HTTP response is a failure, throw. - var failedItems = - from respCols in jsonResponse.Properties() - from eventsCols in events.Properties() - where respCols.Name == eventsCols.Name - let collection = respCols.Name - let combined = eventsCols.Children().Children() - .Zip(respCols.Children().Children(), - (e, r) => new { eventObj = (JObject)e, result = (JObject)r }) - from e in combined - where !(bool)(e.result.Property("success").Value) - select new CachedEvent(collection, - e.eventObj, - KeenUtil.GetBulkApiError(e.result)); - - return failedItems; - } - } -} diff --git a/Keen/EventCachePortable.cs b/Keen/EventCachePortable.cs deleted file mode 100644 index b3b18ac..0000000 --- a/Keen/EventCachePortable.cs +++ /dev/null @@ -1,163 +0,0 @@ -using Keen.Core.EventCache; -using Newtonsoft.Json.Linq; -using PCLStorage; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading.Tasks; - - -namespace Keen.Core -{ - /// - /// EventCachePortable implements the IEventCache interface using - /// file-based storage via the PCLStorage library. It has no - /// cache-expiration policy. - /// To use, pass an instance of this class when constructing KeenClient. - /// To construct a new instance, call the static New() method. - /// - public class EventCachePortable : IEventCache - { - private static Queue events = new Queue(); - - private EventCachePortable() { } - - /// - /// Create, initialize and return an instance of EventCachePortable. - /// - /// - public static EventCachePortable New() - { - try - { - return NewAsync().Result; - } - catch (AggregateException ex) - { - Debug.WriteLine(ex.TryUnwrap()); - throw ex.TryUnwrap(); - } - } - - /// - /// Create, initialize and return an instance of EventCachePortable. - /// - /// - public static async Task NewAsync() - { - var instance = new EventCachePortable(); - - var keenFolder = await GetKeenFolder() - .ConfigureAwait(continueOnCapturedContext: false); - var files = (await keenFolder.GetFilesAsync().ConfigureAwait(continueOnCapturedContext: false)).ToList(); - - lock(events) - if (events.Any()) - foreach (var f in files) - events.Enqueue(f.Name); - - return instance; - } - - public async Task Add(CachedEvent e) - { - if (null == e) - throw new KeenException("Cached events may not be null"); - - var keenFolder = await GetKeenFolder() - .ConfigureAwait(continueOnCapturedContext: false); - - IFile file; - var attempts = 0; - var done = false; - string name = null; - do - { - attempts++; - - // Avoid race conditions in parallel environment by locking on the events queue - // and generating and inserting a unique name within the lock. CreateFileAsync has - // a CreateCollisionOption.GenerateUniqueName, but it will return the same name - // multiple times when called from parallel tasks. - // If creating and writing the file fails, loop around and generate a new name. - if (string.IsNullOrEmpty(name)) - lock (events) - { - var i = 0; - while (events.Contains(name = e.Collection + i++)) - ; - events.Enqueue(name); - } - - Exception lastErr = null; - try - { - file = await keenFolder.CreateFileAsync(name, CreationCollisionOption.FailIfExists) - .ConfigureAwait(continueOnCapturedContext: false); - - var content = JObject.FromObject(e).ToString(); - - await file.WriteAllTextAsync(content) - .ConfigureAwait(continueOnCapturedContext: false); - - done = true; - } - catch (Exception ex) - { - lastErr = ex; - } - - // If the file was not created, not written, or partially written, - // the events queue may be left with a file name that references a - // file that is nonexistent, empty, or invalid. It's easier to handle - // this when the queue is read than to try to dequeue the name. - if (attempts > 100) - throw new KeenException("Persistent failure while saving file, aborting", lastErr); - } while (!done); - } - - public async Task TryTake() - { - var keenFolder = await GetKeenFolder() - .ConfigureAwait(continueOnCapturedContext: false); - if (!events.Any()) - return null; - - string fileName; - lock(events) - fileName = events.Dequeue(); - - var file = await keenFolder.GetFileAsync(fileName) - .ConfigureAwait(continueOnCapturedContext: false); - var content = await file.ReadAllTextAsync() - .ConfigureAwait(continueOnCapturedContext: false); - var ce = JObject.Parse(content); - - var item = new CachedEvent((string)ce.SelectToken("Collection"), (JObject)ce.SelectToken("Event"), ce.SelectToken("Error").ToObject()); - await file.DeleteAsync() - .ConfigureAwait(continueOnCapturedContext: false); - return item; - } - - public async Task Clear() - { - var keenFolder = await GetKeenFolder() - .ConfigureAwait(continueOnCapturedContext: false); - lock(events) - events.Clear(); - await keenFolder.DeleteAsync() - .ConfigureAwait(continueOnCapturedContext: false); - await GetKeenFolder() - .ConfigureAwait(continueOnCapturedContext: false); - } - - private static Task GetKeenFolder() - { - IFolder rootFolder = FileSystem.Current.LocalStorage; - var keenFolderTask = rootFolder.CreateFolderAsync("KeenCache", CreationCollisionOption.OpenIfExists); - - return keenFolderTask; - } - } -} diff --git a/Keen/EventCollection.cs b/Keen/EventCollection.cs deleted file mode 100644 index eb5fc24..0000000 --- a/Keen/EventCollection.cs +++ /dev/null @@ -1,161 +0,0 @@ -using Newtonsoft.Json.Linq; -using System; -using System.Threading.Tasks; - - -namespace Keen.Core -{ - /// - /// EventCollection implements the IEventCollection interface which represents the Keen.IO - /// EventCollection API methods. - /// - internal class EventCollection : IEventCollection - { - private readonly IKeenHttpClient _keenHttpClient; - private readonly string _eventsRelativeUrl; - private readonly string _readKey; - private readonly string _writeKey; - private readonly string _masterKey; - - - internal EventCollection(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); - _eventsRelativeUrl = KeenHttpClient.GetRelativeUrl(prjSettings.ProjectId, - KeenConstants.EventsResource); - - // TODO : It's possible we may want to change back to dynamically grabbing the keys - // from a stored IProjectSettings so client code can lazily assign keys. It creates a - // minor potential race condition, but will allow for scenarios like creating a - // KeenClient instance with only a master key in order to generate/acquire access keys - // to then set as the other keys. Otherwise a new KeenClient must be created or at - // least a new instance of the IEventCollection/IEvent/IQueries implementations. - - _readKey = prjSettings.ReadKey; - _writeKey = prjSettings.WriteKey; - _masterKey = prjSettings.MasterKey; - } - - - public async Task GetSchema(string collection) - { - // TODO : So much of this code, both in the constructor and in the actual message - // dispatch, response parsing and error checking is copy/paste across Queries, Event - // and EventCollection everywhere we use KeenHttpClient. We could shove some of that - // into shared factory functionality (for the ctor stuff) and some of it into the - // KeenHttpClient (for the dispatch/response portions). - - - if (string.IsNullOrWhiteSpace(_readKey)) - { - throw new KeenException("An API ReadKey is required to get collection schema."); - } - - var responseMsg = await _keenHttpClient - .GetAsync(GetCollectionUrl(collection), _readKey) - .ConfigureAwait(continueOnCapturedContext: false); - - var responseString = await responseMsg - .Content - .ReadAsStringAsync() - .ConfigureAwait(continueOnCapturedContext: false); - - dynamic response = JObject.Parse(responseString); - - // error checking, throw an exception with information from the json - // response if available, then check the HTTP response. - KeenUtil.CheckApiErrorCode(response); - - if (!responseMsg.IsSuccessStatusCode) - { - throw new KeenException("GetSchema failed with status: " + responseMsg.StatusCode); - } - - return response; - } - - public async Task DeleteCollection(string collection) - { - if (string.IsNullOrWhiteSpace(_masterKey)) - { - throw new KeenException("An API MasterKey is required to delete a collection."); - } - - var responseMsg = await _keenHttpClient - .DeleteAsync(GetCollectionUrl(collection), _masterKey) - .ConfigureAwait(continueOnCapturedContext: false); - - if (!responseMsg.IsSuccessStatusCode) - { - throw new KeenException("DeleteCollection failed with status: " + responseMsg.StatusCode); - } - } - - public async Task AddEvent(string collection, JObject anEvent) - { - if (string.IsNullOrWhiteSpace(_writeKey)) - { - throw new KeenException("An API WriteKey is required to add events."); - } - - var content = anEvent.ToString(); - - var responseMsg = await _keenHttpClient - .PostAsync(GetCollectionUrl(collection), _writeKey, content) - .ConfigureAwait(continueOnCapturedContext: false); - - var responseString = await responseMsg - .Content - .ReadAsStringAsync() - .ConfigureAwait(continueOnCapturedContext: false); - - JObject jsonResponse = null; - - try - { - // Normally the response content should be parsable JSON, - // but if the server returned a 404 error page or something - // like that, this will throw. - jsonResponse = JObject.Parse(responseString); - } - catch (Exception) - { - } - - // error checking, throw an exception with information from the - // json response if available, then check the HTTP response. - KeenUtil.CheckApiErrorCode(jsonResponse); - - if (!responseMsg.IsSuccessStatusCode) - { - throw new KeenException("AddEvent failed with status: " + responseMsg.StatusCode); - } - } - - private string GetCollectionUrl(string collection) - { - return $"{_eventsRelativeUrl}/{collection}"; - } - } -} diff --git a/Keen/HttpClientCache.cs b/Keen/HttpClientCache.cs deleted file mode 100644 index 8fc7c72..0000000 --- a/Keen/HttpClientCache.cs +++ /dev/null @@ -1,248 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; - - -namespace Keen.Core -{ - /// - /// An implementation of that caches HttpClient instances in - /// a dictionary mapping the base URL to a WeakReference to the actual instance. A PRO to this - /// approach is that HttpClient instances will automatically be evicted when no more strong - /// refs exist and the GC collects. A CON to using WeakReference, besides it not being generic - /// in the current version of the PCL and being a fairly heavyweight class, is that rapid - /// creation and releasing of owning instances like the KeenClient can still allow for the GC - /// to aggressively clean up HttpClient instances. Recommended usage of KeenClient shouldn't - /// make this a common problem, but at some point this cache can evolve to be more intelligent - /// about keeping instances alive deliberately. - /// - internal class HttpClientCache : IHttpClientProvider - { - // A singleton cache that can optionally be used and shared. If new caches need to be - // created, use the internal ctor. One use case for this might be to have multiple - // HttpClient instances with different configurations cached for the same URL for use in - // different sets of client modules. - internal static HttpClientCache Instance { get; } = new HttpClientCache(); - - // Explicit static constructor, no beforefieldinit - static HttpClientCache() { } - - - private readonly object _cacheLock; - - // NOTE : We should use ConcurrentDictionary> here if/when we upgrade the PCL - // profile to something >= .NET 4.0. - - // NOTE : Use WeakReference in 4.5+ - private readonly IDictionary _httpClients; - - - // No external construction - internal HttpClientCache() - { - _cacheLock = new object(); - _httpClients = new Dictionary(); - } - - - /// - /// Retrieve an existing HttpClient for the given URL, or throw if it doesn't exist. - /// - /// The base URL the HttpClient is tied to. - /// The HttpClient which is expected to exist. - public HttpClient this[Uri baseUrl] - { - get - { - HttpClient httpClient = null; - - lock (_cacheLock) - { - WeakReference weakRef = null; - - if (!_httpClients.TryGetValue(baseUrl, out weakRef)) - { - throw new KeenException( - string.Format("No existing HttpClient for baseUrl \"{0}\"", baseUrl)); - } - - httpClient = weakRef.Target as HttpClient; - - if (null == httpClient) - { - throw new KeenException( - string.Format("Existing HttpClient for baseUrl \"{0}\" has been" + - "garbage collected.", baseUrl)); - } - } - - return httpClient; - } - } - - /// - /// Retrieve an existing HttpClient for the given URL, or create one with the given - /// handlers and headers. - /// - /// The base URL the HttpClient is tied to. - /// A factory function to create a handler chain. - /// Any headers that all requests to this URL should add by - /// default. - /// An HttpClient configured to handle requests for the given URL. - public HttpClient GetOrCreateForUrl( - Uri baseUrl, - Func getHandlerChain = null, - IEnumerable> defaultHeaders = null) - { - Action configure = null; - - if (null != defaultHeaders && Enumerable.Any(defaultHeaders)) - { - configure = (httpClientToConfigure) => - { - foreach (var header in defaultHeaders) - { - httpClientToConfigure.DefaultRequestHeaders.Add(header.Key, header.Value); - } - }; - } - - - HttpClient httpClient = GetOrCreateForUrl(baseUrl, getHandlerChain, configure); - - return httpClient; - } - - /// - /// Retrieve an existing HttpClient for the given URL, or create one with the given - /// handlers and configuration functor. - /// - /// The base URL the HttpClient is tied to. - /// A factory function to create a handler chain. - /// An action that takes the newly created HttpClient and - /// configures it however needed before it is stored and/or returned. - /// An HttpClient configured to handle requests for the given URL. - public HttpClient GetOrCreateForUrl( - Uri baseUrl, - Func getHandlerChain = null, - Action configure = null) - { - if (null == baseUrl) - { - throw new ArgumentNullException(nameof(baseUrl), - string.Format("Cannot use a null {0} as a key.", nameof(baseUrl))); - } - - HttpClient httpClient = null; - - lock (_cacheLock) - { - WeakReference weakRef = null; - - if (!_httpClients.TryGetValue(baseUrl, out weakRef) || - null == (httpClient = weakRef.Target as HttpClient)) - { - // If no handler chain is provided, a plain HttpClientHandler with no - // configuration whatsoever is installed. - httpClient = new HttpClient(getHandlerChain?.Invoke() ?? new HttpClientHandler()); - httpClient.BaseAddress = baseUrl; - - configure?.Invoke(httpClient); - - // Reuse the WeakReference if we already had an entry for this url. - if (null == weakRef) - { - _httpClients[baseUrl] = new WeakReference(httpClient); - } - else - { - weakRef.Target = httpClient; - } - } - } - - return httpClient; - } - - /// - /// Remove any cached HttpClients associated with the given URL. - /// - /// The base URL for which any cached HttpClient instances should - /// be purged. - public void RemoveForUrl(Uri baseUrl) - { - if (null == baseUrl) - { - throw new ArgumentNullException(nameof(baseUrl), - string.Format("Cannot use a null {0} as a key.", nameof(baseUrl))); - } - - lock (_cacheLock) - { - _httpClients.Remove(baseUrl); - } - } - - /// - /// Can this provider return an HttpClient instance for the given URL? For this - /// implementation, we'll check if an entry exists in the cache and if the WeakReference - /// is still valid and a strong ref can be taken. - /// - /// The base URL for which we'd like to know if an HttpClient can be - /// provided. - /// True if this provider could return an HttpClient for the given URL, false - /// otherwise. - public bool ExistsForUrl(Uri baseUrl) - { - if (null == baseUrl) - { - // We can't have null keys, so we wouldn't ever be able to look up this URL. - return false; - } - - lock (_cacheLock) - { - WeakReference weakRef = null; - bool exists = (_httpClients.TryGetValue(baseUrl, out weakRef) && - (null != weakRef.Target as HttpClient)); - - return exists; - } - } - - /// - /// Drop all HttpClient instances from the cache, no matter the URL. - /// - internal void Clear() - { - lock (_cacheLock) - { - _httpClients.Clear(); - } - } - - /// - /// Override the HttpClient provided for a given URL. This tests and replaces or inserts - /// all in one atomic operation. This will likely be useful for testing. - /// - /// URL to override. - /// HttpClient instance that will do the overriding. - internal void OverrideForUrl(Uri baseUrl, HttpClient httpClient) - { - lock (_cacheLock) - { - WeakReference weakRef = null; - - if (!_httpClients.TryGetValue(baseUrl, out weakRef)) - { - _httpClients[baseUrl] = new WeakReference(httpClient); - } - else - { - weakRef.Target = httpClient; - } - } - } - } -} diff --git a/Keen/IDynamicPropertyValue.cs b/Keen/IDynamicPropertyValue.cs deleted file mode 100644 index 9015095..0000000 --- a/Keen/IDynamicPropertyValue.cs +++ /dev/null @@ -1,8 +0,0 @@ - -namespace Keen.Core -{ - interface IDynamicPropertyValue - { - object Value(); - } -} diff --git a/Keen/IEvent.cs b/Keen/IEvent.cs deleted file mode 100644 index 568d4b2..0000000 --- a/Keen/IEvent.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Keen.Core.EventCache; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; -using System.Threading.Tasks; - - -namespace Keen.Core -{ - public interface IEvent - { - /// - /// Return schema information for all the event collections in this project. - /// - /// - Task GetSchemas(); - - /// - /// Insert multiple events in one or more collections in a single request. - /// - /// - /// Enumerable containing any rejected events - Task> AddEvents(JObject events); - } -} diff --git a/Keen/IEventCache.cs b/Keen/IEventCache.cs deleted file mode 100644 index aaf3325..0000000 --- a/Keen/IEventCache.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Threading.Tasks; - - -namespace Keen.Core.EventCache -{ - public interface IEventCache - { - Task Add(CachedEvent e); - Task TryTake(); - Task Clear(); - } -} diff --git a/Keen/IEventCollection.cs b/Keen/IEventCollection.cs deleted file mode 100644 index dfb96c9..0000000 --- a/Keen/IEventCollection.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Newtonsoft.Json.Linq; -using System.Threading.Tasks; - - -namespace Keen.Core -{ - public interface IEventCollection - { - /// - /// Returns schema information for this event collection. - /// - /// - /// - Task GetSchema(string collection); - - /// - /// Delete the entire event collection. - /// - /// Name of collection - Task DeleteCollection(string collection); - - /// - /// Insert one event at a time in a single request. - /// - /// Name of collection - /// Event data to insert - /// - Task AddEvent(string collection, JObject anEvent); - } -} diff --git a/Keen/IHttpClientProvider.cs b/Keen/IHttpClientProvider.cs deleted file mode 100644 index e7db7d1..0000000 --- a/Keen/IHttpClientProvider.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; - - -namespace Keen.Core -{ - /// - /// Represents a type that can provide an HttpClient for a given URL. It could act as a cache - /// by returning pre-existing instances, or create as necessary, or always create given the - /// optional configuration parameters pass in. - /// - public interface IHttpClientProvider - { - /// - /// Retrieve an existing HttpClient for the given URL. - /// - /// The base URL the HttpClient is tied to. - /// The HttpClient which is expected to exist. - HttpClient this[Uri baseUrl] { get; } - - /// - /// Retrieve an existing HttpClient for the given URL, or create one with the given - /// handlers and configuration functor. - /// - /// The base URL the HttpClient is tied to. - /// A factory function to create a handler chain. - /// Any headers that all requests to this URL should add by - /// default. - /// An HttpClient configured to handle requests for the given URL. - HttpClient GetOrCreateForUrl( - Uri baseUrl, - Func getHandlerChain = null, - IEnumerable> defaultHeaders = null - ); - - /// - /// Retrieve an existing HttpClient for the given URL, or create one with the given - /// handlers and configuration functor. - /// - /// The base URL the HttpClient is tied to. - /// A factory function to create a handler chain. - /// An action that takes the newly created HttpClient and - /// configures it however needed before it is stored and/or returned. - /// An HttpClient configured to handle requests for the given URL. - HttpClient GetOrCreateForUrl(Uri baseUrl, - Func getHandlerChain = null, - Action configure = null); - - /// - /// If caching instances, remove any associated with the given URL. - /// - /// The base URL for which any cached HttpClient instances should - /// be purged. - void RemoveForUrl(Uri baseUrl); - - /// - /// Can this provider return an HttpClient instance for the given URL? - /// - /// The base URL for which we'd like to know if an HttpClient can be - /// provided. - /// True if this provider could return an HttpClient for the given URL, false - /// otherwise. - bool ExistsForUrl(Uri baseUrl); - } -} \ No newline at end of file diff --git a/Keen/IKeenHttpClient.cs b/Keen/IKeenHttpClient.cs deleted file mode 100644 index 812e07c..0000000 --- a/Keen/IKeenHttpClient.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System; -using System.Net.Http; -using System.Threading.Tasks; - - -namespace Keen.Core -{ - /// - /// Represents a type capable of performing HTTP operations destined for a Keen API endpoint. - /// This should augment and/or alter normal HttpClient behavior where appropriate taking into - /// consideration Keen-specific protocols. - /// - public interface IKeenHttpClient - { - /// - /// Create and send a GET request to the given relative resource using the given key for - /// authentication. - /// - /// The relative resource to GET. Must be properly formatted as a - /// relative Uri. - /// The key to use for authenticating this request. - /// The response message. - Task GetAsync(string resource, string authKey); - - /// - /// Create and send a GET request to the given relative resource using the given key for - /// authentication. - /// - /// The relative resource to GET. - /// The key to use for authenticating this request. - /// The response message. - Task GetAsync(Uri resource, string authKey); - - /// - /// Create and send a POST request with the given content to the given relative resource - /// using the given key for authentication. - /// - /// The relative resource to POST. Must be properly formatted as a - /// relative Uri. - /// The key to use for authenticating this request. - /// The POST body to send. - /// The response message. - Task PostAsync(string resource, string authKey, string content); - - /// - /// Create and send a POST request with the given content to the given relative resource - /// using the given key for authentication. - /// - /// The relative resource to POST. - /// The key to use for authenticating this request. - /// The POST body to send. - /// The response message. - Task PostAsync(Uri resource, string authKey, string content); - - /// - /// Create and send a DELETE request to the given relative resource using the given key for - /// authentication. - /// - /// The relative resource to DELETE. Must be properly formatted as a - /// relative Uri. - /// The key to use for authenticating this request. - /// The response message. - Task DeleteAsync(string resource, string authKey); - - /// - /// Create and send a DELETE request to the given relative resource using the given key for - /// authentication. - /// - /// The relative resource to DELETE. - /// The key to use for authenticating this request. - /// The response message. - Task DeleteAsync(Uri resource, string authKey); - - /// - /// Create and send a PUT request with the given content to the given relative resource - /// using the given key for authentication. - /// - /// The relative resource to PUT. Must be properly formatted as a - /// relative Uri. - /// The key to use for authenticating this request. - /// The PUT body to send. - /// >The response message. - Task PutAsync(string resource, string authKey, string content); - - /// - /// Create and send a PUT request with the given content to the given relative resource - /// using the given key for authentication. - /// - /// The relative resource to PUT. Must be properly formatted as a - /// relative Uri. - /// The key to use for authenticating this request. - /// The PUT body to send. - /// >The response message. - Task PutAsync(Uri resource, string authKey, string content); - } -} diff --git a/Keen/IKeenHttpClientProvider.cs b/Keen/IKeenHttpClientProvider.cs deleted file mode 100644 index 6212b74..0000000 --- a/Keen/IKeenHttpClientProvider.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; - - -namespace Keen.Core -{ - /// - /// An instance of this type can provide an to be used to perform - /// requests against a given URL. Implement to customize how other parts of the SDK dispatch - /// requests to a keen IO endpoint. - /// - public interface IKeenHttpClientProvider - { - /// - /// Given a base URL, return an IKeenHttpClient against which requests can be made. The - /// intent is that all requests using this IKeenHttpClient will be to resources relative to - /// this base URL. It is expected that this IKeenHttpClient is thread-safe. - /// - /// The base URL, e.g. https://api.keen.io/3.0/ - /// An IKeenHttpClient configured to handle requests to resources relative to the - /// given base URL. - IKeenHttpClient GetForUrl(Uri baseUrl); - } -} diff --git a/Keen/IProjectSettings.cs b/Keen/IProjectSettings.cs deleted file mode 100644 index 45ed7a3..0000000 --- a/Keen/IProjectSettings.cs +++ /dev/null @@ -1,39 +0,0 @@ - -namespace Keen.Core -{ - /// - /// Values required to access a Keen project - /// - public interface IProjectSettings - { - /// - /// The Keen.IO URL for this project. Usually this will be the - /// server address and API version. This should end with a '/'. - /// - /// - e.g. https://api.keen.io/3.0/ - /// - /// - string KeenUrl { get; } - - /// - /// The Project ID, identifying the data silo to be accessed. - /// - string ProjectId { get; } - - /// - /// The Master API key, required for getting a collection schema - /// or deleting the entire event collection. - /// - string MasterKey { get; } - - /// - /// The Write API key, required for inserting events. - /// - string WriteKey { get; } - - /// - /// The Read API key, used with query requests. - /// - string ReadKey { get; } - } -} diff --git a/Keen/Keen.csproj b/Keen/Keen.csproj deleted file mode 100644 index 5744708..0000000 --- a/Keen/Keen.csproj +++ /dev/null @@ -1,167 +0,0 @@ - - - - - 10.0 - Debug - AnyCPU - {36D156BF-8523-42EC-9AD6-7C3AC05D699F} - Library - Properties - Keen.Core - Keen - v4.0 - Profile344 - 512 - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 062c5b00 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - bin\Release\Keen.XML - - - bin\netfx\ - TRACE - bin\Release\Keen.XML - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - - - - - Properties\SharedAssemblyInfo.cs - - - Properties\SharedVersionInfo.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ..\packages\Microsoft.Bcl.Async.1.0.168\lib\portable-net40+sl4+win8+wp71+wpa81\Microsoft.Threading.Tasks.dll - True - - - ..\packages\Microsoft.Bcl.Async.1.0.168\lib\portable-net40+sl4+win8+wp71+wpa81\Microsoft.Threading.Tasks.Extensions.dll - True - - - ..\packages\Newtonsoft.Json.9.0.1\lib\portable-net40+sl5+wp80+win8+wpa81\Newtonsoft.Json.dll - True - - - ..\packages\PCLStorage.1.0.2\lib\portable-net45+sl5+wp8+wpa81+win8+monoandroid+monotouch+Xamarin.iOS+Xamarin.Mac\PCLStorage.dll - True - - - ..\packages\PCLStorage.1.0.2\lib\portable-net45+sl5+wp8+wpa81+win8+monoandroid+monotouch+Xamarin.iOS+Xamarin.Mac\PCLStorage.Abstractions.dll - True - - - ..\packages\Microsoft.Bcl.1.1.10\lib\portable-net40+sl5+win8+wp8+wpa81\System.IO.dll - True - - - ..\packages\Microsoft.Net.Http.2.2.29\lib\portable-net40+sl4+win8+wp71+wpa81\System.Net.Http.dll - True - - - ..\packages\Microsoft.Net.Http.2.2.29\lib\portable-net40+sl4+win8+wp71+wpa81\System.Net.Http.Extensions.dll - True - - - ..\packages\Microsoft.Net.Http.2.2.29\lib\portable-net40+sl4+win8+wp71+wpa81\System.Net.Http.Primitives.dll - True - - - ..\packages\Microsoft.Bcl.1.1.10\lib\portable-net40+sl5+win8+wp8+wpa81\System.Runtime.dll - True - - - ..\packages\Microsoft.Bcl.1.1.10\lib\portable-net40+sl5+win8+wp8+wpa81\System.Threading.Tasks.dll - True - - - - - - - - This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - \ No newline at end of file diff --git a/Keen/KeenClient.cs b/Keen/KeenClient.cs deleted file mode 100644 index b932e92..0000000 --- a/Keen/KeenClient.cs +++ /dev/null @@ -1,1150 +0,0 @@ -using Keen.Core.DataEnrichment; -using Keen.Core.EventCache; -using Keen.Core.Query; -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading.Tasks; -using Keen.Core.Dataset; - - -namespace Keen.Core -{ - /// - /// Keen.IO API access - /// - public class KeenClient - { - private readonly IProjectSettings _prjSettings; - private readonly IDictionary _globalProperties = new Dictionary(); - - /// - /// EventCollection provides direct access to the Keen.IO EventCollection API methods. - /// It is not normally necessary to use this property. - /// The default implementation can be overridden by setting a new implementation here. - /// - public IEventCollection EventCollection { get; set; } - - /// - /// Event provides direct access to the Keen.IO Event API methods. - /// It is not normally necessary to use this property. - /// The default implementation can be overridden by setting a new implementation here. - /// - public IEvent Event { get; set; } - - /// - /// EventCache provides a caching implementation allowing events to be cached locally - /// instead of being sent one at a time. It is not normally necessary to use this property. - /// The implementation is responsible for cache maintenance policy, such as trimming - /// old entries to avoid excessive cache size. - /// - public IEventCache EventCache { get; private set; } - - /// - /// Queries provides direct access to the Keen.IO Queries API methods. - /// It is not normally necessary to use this property. - /// The default implementation can be overridden by setting a new implementation here. - /// - public IQueries Queries { get; set; } - - public IDataset Datasets { get; set; } - - /// - /// Add a static global property. This property will be added to - /// every event. - /// - /// Property name - /// Property value. This may be a simple value, array, or object, - /// or an object that supports IDynamicPropertyValue returning one of those. - public void AddGlobalProperty(string property, object value) - { - // Verify that the property name is allowable, and that the value is populated. - KeenUtil.ValidatePropertyName(property); - if (value == null) - throw new KeenException("Global properties must have a non-null value."); - var dynProp = value as IDynamicPropertyValue; - if (dynProp != null) - // Execute the property once before it is needed to check the value - ExecDynamicPropertyValue(property, dynProp); - - _globalProperties.Add(property, value); - } - - private void ExecDynamicPropertyValue(string propName, IDynamicPropertyValue dynProp) - { - object result; - try - { - result = dynProp.Value(); - } - catch (Exception e) - { - throw new KeenException(string.Format("Dynamic property \"{0}\" execution failure", propName), e); - } - if (result == null) - throw new KeenException(string.Format("Dynamic property \"{0}\" execution returned null", propName)); - } - - private KeenClient(IProjectSettings prjSettings, - IEventCache eventCache, - IKeenHttpClientProvider keenHttpClientProvider) - { - // Preconditions - if (null == prjSettings) - throw new KeenException("An IProjectSettings instance is required."); - if (string.IsNullOrWhiteSpace(prjSettings.ProjectId)) - throw new KeenException("A Project ID is required."); - if ((string.IsNullOrWhiteSpace(prjSettings.MasterKey) - && string.IsNullOrWhiteSpace(prjSettings.WriteKey) - && string.IsNullOrWhiteSpace(prjSettings.ReadKey))) - throw new KeenException("An API key is required."); - if (string.IsNullOrWhiteSpace(prjSettings.KeenUrl)) - throw new KeenException("A URL for the server address is required."); - - _prjSettings = prjSettings; - - if (null != eventCache) - { - EventCache = eventCache; - } - - // Use the default provider if none was passed in. - keenHttpClientProvider = (keenHttpClientProvider ?? new KeenHttpClientProvider()); - - // These interfaces normally should not need to be set by client code, so the default - // implementation is set up here. These may be overridden by injecting an - // 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); - } - - /// - /// - /// - /// A ProjectSettings instance containing the ProjectId and API keys - public KeenClient(IProjectSettings prjSettings) - : this(prjSettings, null, null) - { - } - - /// - /// - /// - /// A ProjectSettings instance containing the ProjectId and API keys - /// An IEventCache instance providing a caching strategy - public KeenClient(IProjectSettings prjSettings, IEventCache eventCache) - : this(prjSettings, eventCache, null) - { - } - - public KeenClient(IProjectSettings prjSettings, - IKeenHttpClientProvider keenHttpClientProvider) - : this(prjSettings, null, keenHttpClientProvider) - { - } - - /// - /// Delete the specified collection. Deletion may be denied for collections with many events. - /// Master API key is required. - /// - /// Name of collection to delete. - public async Task DeleteCollectionAsync(string collection) - { - // Preconditions - KeenUtil.ValidateEventCollectionName(collection); - if (string.IsNullOrWhiteSpace(_prjSettings.MasterKey)) - throw new KeenException("Master API key is required for DeleteCollection"); - - await EventCollection.DeleteCollection(collection) - .ConfigureAwait(continueOnCapturedContext: false); - } - - /// - /// Delete the specified collection. Deletion may be denied for collections with many events. - /// Master API key is required. - /// - /// Name of collection to delete. - public void DeleteCollection(string collection) - { - try - { - DeleteCollectionAsync(collection).Wait(); - } - catch (AggregateException ex) - { - throw ex.TryUnwrap(); - } - } - - /// - /// Return schema information for all the event collections in this project. - /// - /// - public async Task GetSchemasAsync() - { - // Preconditions - if (string.IsNullOrWhiteSpace(_prjSettings.ReadKey)) - throw new KeenException("Read API key is required for GetSchemas"); - - return await Event.GetSchemas() - .ConfigureAwait(continueOnCapturedContext: false); - } - - /// - /// Return schema information for all the event collections in this project. - /// - /// - public JArray GetSchemas() - { - try - { - return GetSchemasAsync().Result; - } - catch (AggregateException ex) - { - throw ex.TryUnwrap(); - } - } - - /// - /// Retrieve the schema for the specified collection. This requires - /// a value for the project settings Master API key. - /// - /// - public async Task GetSchemaAsync(string collection) - { - // Preconditions - KeenUtil.ValidateEventCollectionName(collection); - if (string.IsNullOrWhiteSpace(_prjSettings.ReadKey)) - throw new KeenException("Read API key is required for GetSchema"); - - return await EventCollection.GetSchema(collection) - .ConfigureAwait(continueOnCapturedContext: false); - } - - /// - /// Retrieve the schema for the specified collection. This requires - /// a value for the project settings Master API key. - /// - /// - public dynamic GetSchema(string collection) - { - try - { - return GetSchemaAsync(collection).Result; - } - catch (AggregateException ex) - { - throw ex.TryUnwrap(); - } - } - - /// - /// Insert multiple events in a single request. - /// - /// Collection name - /// Collection of events to add - /// Optional collection of Data Enhancement Add-ons - public void AddEvents(string collection, IEnumerable eventsInfo, IEnumerable addOns = null) - { - try - { - AddEventsAsync(collection, eventsInfo, addOns).Wait(); - } - catch (AggregateException ex) - { - throw ex.TryUnwrap(); - } - } - - /// - /// Add a collection of events to the specified collection. Assumes that - /// objects in the collection have already been through AddEvent to receive - /// global properties. - /// - /// Collection name - /// Collection of events to add - /// Enumerable of any rejected events - private async Task> AddEventsBulkAsync(string collection, - IEnumerable eventsInfo) - { - if (null == eventsInfo) - throw new KeenException("AddEvents eventsInfo may not be null"); - if (!eventsInfo.Any()) - return new List(); - // Build a container object with a property to identify the collection - var jEvent = new JObject {{collection, JToken.FromObject(eventsInfo)}}; - - // Use the bulk interface to add events - return await Event.AddEvents(jEvent) - .ConfigureAwait(false); - } - - /// - /// Add a collection of events to the specified collection - /// - /// Collection name - /// Collection of events to add - /// Optional collection of Data Enhancement Add-ons - /// - public async Task AddEventsAsync(string collection, IEnumerable eventsInfo, IEnumerable addOns = null) - { - if (null == eventsInfo) - throw new KeenException("AddEvents eventsInfo may not be null"); - if (string.IsNullOrWhiteSpace(_prjSettings.WriteKey)) - throw new KeenException("Write API key is required for AddEvents"); - - var mainCache = EventCache; - var localCache = new List(); - - // prepare each object to add global properties and timestamp, then either - // add to the main cache if it exists, or if not to the local object list. - foreach (var e in eventsInfo) - { - // ReSharper disable once PossibleMultipleEnumeration - var jEvent = PrepareUserObject(e, addOns); - if (null != mainCache) - await mainCache.Add(new CachedEvent(collection, jEvent)) - .ConfigureAwait(false); - else - localCache.Add(jEvent); - } - - // if the local object list has data (caching is not enabled), go - // ahead and send it using the bulk interface. - if (localCache.Any()) - { - var errs = await AddEventsBulkAsync(collection, localCache) - .ConfigureAwait(false); - // ReSharper disable PossibleMultipleEnumeration - if (errs.Any()) - throw new KeenBulkException("One or more events was rejected during the bulk add operation", errs); - // ReSharper restore PossibleMultipleEnumeration - } - } - - /// - /// Add a single event to the specified collection. - /// - /// Collection name - /// The event to add. - /// Optional collection of Data Enhancement Add-ons - public async Task AddEventAsync(string collection, object eventInfo, IEnumerable addOns = null) - { - // Preconditions - KeenUtil.ValidateEventCollectionName(collection); - if (null == eventInfo) - throw new KeenException("Event data is required."); - if (string.IsNullOrWhiteSpace(_prjSettings.WriteKey)) - throw new KeenException("Write API key is required for AddEvent"); - - var jEvent = PrepareUserObject(eventInfo, addOns); - - // If an event cache has been provided, cache this event instead of sending it. - if (null != EventCache) - await EventCache.Add(new CachedEvent(collection, jEvent)) - .ConfigureAwait(false); - else - await EventCollection.AddEvent(collection, jEvent) - .ConfigureAwait(false); - } - - /// - /// Convert a user-supplied object to a JObject that can be sent to the Keen.IO API. - /// - /// This writes any global properties to the object and records the time. - /// - /// - /// Optional collection of Data Enhancement Add-ons - /// - private JObject PrepareUserObject(object eventInfo, IEnumerable addOns) - { - var jEvent = JObject.FromObject(eventInfo); - - // Add global properties to the event - foreach (var p in _globalProperties) - { - // If the property value is an IDynamicPropertyValue, - // exec the Value() to generate the property value. - var dynProp = p.Value as IDynamicPropertyValue; - if (dynProp == null) - { - KeenUtil.ValidatePropertyName(p.Key); - jEvent.Add(p.Key, JToken.FromObject(p.Value)); - } - else - { - var val = dynProp.Value(); - if (null == val) - throw new KeenException(string.Format("Dynamic property \"{0}\" returned a null value", p.Key)); - jEvent.Add(p.Key, JToken.FromObject(val)); - } - } - - // Ensure this event has a 'keen' object of the correct type - if (null == jEvent.Property("keen")) - jEvent.Add("keen", new JObject()); - else if (jEvent.Property("keen").Value.GetType() != typeof (JObject)) - throw new KeenException(string.Format("Value of property \"keen\" must be an object, is {0}", - jEvent.Property("keen").GetType())); - - - var keen = ((JObject) jEvent.Property("keen").Value); - if (addOns != null && addOns.Any()) - keen.Add("addons", JArray.FromObject(addOns)); - - // Set the keen.timestamp if it has not already been set - if (null == keen.Property("timestamp")) - keen.Add("timestamp", DateTime.Now); - - return jEvent; - } - - /// - /// Add a single event to the specified collection. - /// - /// Collection name - /// An object representing the event to be added. - /// Optional collection of Data Enhancement Add-ons - public void AddEvent(string collection, object eventInfo, IEnumerable addOns = null) - { - try - { - AddEventAsync(collection, eventInfo, addOns).Wait(); - } - catch (AggregateException ex) - { - throw ex.TryUnwrap(); - } - } - - /// - /// Submit all events found in the event cache. If any events are rejected by the server, - /// KeenCacheException will be thrown with a listing of the rejected events, each with - /// the error message it received. - /// - public void SendCachedEvents() - { - try - { - SendCachedEventsAsync().Wait(); - } - catch (AggregateException ex) - { - Debug.WriteLine(ex.TryUnwrap()); - throw ex.TryUnwrap(); - } - } - - /// - /// Submit all events found in the event cache. If any events are rejected by the server, - /// KeenCacheException will be thrown with a listing of the rejected events, each with - /// the error message it received. - /// - public async Task SendCachedEventsAsync() - { - if (null == EventCache) - throw new KeenException("Event caching is not enabled"); - - CachedEvent e; - var batches = new Dictionary>(); - var failedEvents = new List(); - - Func> getListFor = c => - { - if (batches.ContainsKey(c)) - return batches[c]; - - var l = new List(); - batches.Add(c, l); - return l; - }; - - // Take items from the cache and sort them by collection - while (null != (e = await EventCache.TryTake().ConfigureAwait(false))) - { - var batch = getListFor(e.Collection); - batch.Add(e); - - // If this collection has reached the maximum batch size, send it - if (batch.Count == KeenConstants.BulkBatchSize) - { - failedEvents.AddRange( - await AddEventsBulkAsync(e.Collection, batch.Select(n => n.Event)).ConfigureAwait(false)); - batch.Clear(); - } - } - - // Send the remainder of all the collections - foreach (var c in batches.Where(b => b.Value.Any())) - failedEvents.AddRange( - await AddEventsBulkAsync(c.Key, c.Value.Select(n => n.Event)).ConfigureAwait(false)); - - // if there where any failures, throw and include the errored items and details. - if (failedEvents.Any()) - throw new KeenBulkException("One or more cached events could not be submitted", failedEvents); - } - - /// - /// Retrieve a list of all the queries supported by the API. - /// - /// - public async Task>> GetQueries() - { - return await Queries.AvailableQueries(); - } - - /// - /// Call any Keen.IO API function with the specified parameters. - /// - /// - /// - /// - public async Task QueryAsync(string queryName, Dictionary parms) - { - return await Queries.Metric(queryName, parms); - } - - /// - /// Call any Keen.IO API function with the specified parameters. Refer to Keen API documentation for - /// details of request parameters and return type. Return type may be cast as dynamic. - /// - /// Query name, e.g., KeenConstants.QueryCount - /// Parameters for query, API keys are not required here. - /// - public JObject Query(string queryName, Dictionary parms) - { - try - { - return QueryAsync(queryName, parms).Result; - } - catch (AggregateException ex) - { - throw ex.TryUnwrap(); - } - } - - /// - /// Run a query returning a single value. - /// - /// Type of query to run. - /// Name of event collection to query. - /// Name of property to analyse. - /// Specifies window of time from which to select events for analysis. May be absolute or relative. - /// Filter to narrow down the events used in analysis. Optional, may be null. - /// The timezone to use when specifying a relative timeframe. Optional, may be blank. - /// - public async Task QueryAsync(QueryType queryType, string collection, string targetProperty, - IQueryTimeframe timeframe = null, IEnumerable filters = null, string timezone = "") - { - return - await - Queries.Metric(queryType, collection, targetProperty, timeframe, filters, timezone) - .ConfigureAwait(false); - } - - /// - /// Return a single value. - /// - /// Type of query to run. - /// Name of event collection to query. - /// Name of property to analyse. - /// Specifies window of time from which to select events for analysis. May be absolute or relative. - /// Filter to narrow down the events used in analysis. Optional, may be null. - /// The timezone to use when specifying a relative timeframe. Optional, may be blank. - /// - public string Query(QueryType queryType, string collection, string targetProperty, - IQueryTimeframe timeframe = null, IEnumerable filters = null, string timezone = "") - { - try - { - return QueryAsync(queryType, collection, targetProperty, timeframe, filters, timezone).Result; - } - catch (AggregateException ex) - { - throw ex.TryUnwrap(); - } - } - - /// - /// Returns values collected by group. - /// - /// Type of query to run. - /// Name of event collection to query. - /// Name of property to analyse. - /// Name of a collection field by which to group results. - /// Specifies window of time from which to select events for analysis. May be absolute or relative. - /// Filter to narrow down the events used in analysis. Optional, may be null. - /// The timezone to use when specifying a relative timeframe. Optional, may be blank. - /// - public async Task>> QueryGroupAsync(QueryType queryType, string collection, - string targetProperty, string groupBy, IQueryTimeframe timeframe = null, - IEnumerable filters = null, string timezone = "") - { - return - await - Queries.Metric(queryType, collection, targetProperty, groupBy, timeframe, filters, timezone) - .ConfigureAwait(false); - } - - /// - /// Returns values collected by group. - /// - /// Type of query to run. - /// Name of event collection to query. - /// Name of property to analyse. - /// Name of a collection field by which to group results. - /// Specifies window of time from which to select events for analysis. May be absolute or relative. - /// Filter to narrow down the events used in analysis. Optional, may be null. - /// The timezone to use when specifying a relative timeframe. Optional, may be blank. - /// - public IEnumerable> QueryGroup(QueryType queryType, string collection, - string targetProperty, string groupBy, IQueryTimeframe timeframe = null, - IEnumerable filters = null, string timezone = "") - { - try - { - return - QueryGroupAsync(queryType, collection, targetProperty, groupBy, timeframe, filters, timezone).Result; - } - catch (AggregateException ex) - { - throw ex.TryUnwrap(); - } - } - - /// - /// Return values collected by time interval. - /// - /// Type of query to run. - /// Name of event collection to query. - /// Name of property to analyse. - /// Specifies window of time from which to select events for analysis. May be absolute or relative. - /// The block size for partitioning the specified timeframe. Optional, may be null. - /// Filters to narrow down the events used in analysis. Optional, may be null. - /// The timezone to use when specifying a relative timeframe. Optional, may be blank. - /// - public async Task>> QueryIntervalAsync(QueryType queryType, - string collection, string targetProperty, IQueryTimeframe timeframe, QueryInterval interval = null, - IEnumerable filters = null, string timezone = "") - { - return - await - Queries.Metric(queryType, collection, targetProperty, timeframe, interval, filters, timezone) - .ConfigureAwait(false); - } - - /// - /// Returns values collected by time interval. - /// - /// Type of query to run. - /// Name of event collection to query. - /// Name of property to analyse. - /// Specifies window of time from which to select events for analysis. May be absolute or relative. - /// The block size for partitioning the specified timeframe. Optional, may be null. - /// Filters to narrow down the events used in analysis. Optional, may be null. - /// The timezone to use when specifying a relative timeframe. Optional, may be blank. - /// - public IEnumerable> QueryInterval(QueryType queryType, string collection, - string targetProperty, IQueryTimeframe timeframe, QueryInterval interval = null, - IEnumerable filters = null, string timezone = "") - { - try - { - return - QueryIntervalAsync(queryType, collection, targetProperty, timeframe, interval, filters, timezone) - .Result; - } - catch (AggregateException ex) - { - throw ex.TryUnwrap(); - } - } - - /// - /// Returns items collected by time interval and group. - /// - /// Type of query to run. - /// Name of event collection to query. - /// Name of property to analyse. - /// Name of field by which to group results. - /// Specifies window of time from which to select events for analysis. May be absolute or relative. - /// The block size for partitioning the specified timeframe. Optional, may be null. - /// Filters to narrow down the events used in analysis. Optional, may be null. - /// The timezone to use when specifying a relative timeframe. Optional, may be blank. - /// - public async Task>>>> QueryIntervalGroupAsync - (QueryType queryType, string collection, string targetProperty, string groupBy, IQueryTimeframe timeframe, - QueryInterval interval, IEnumerable filters = null, string timezone = "") - { - return - await - Queries.Metric(queryType, collection, targetProperty, groupBy, timeframe, interval, filters, - timezone).ConfigureAwait(false); - } - - /// - /// Returns items collected by time interval and group. - /// - /// Type of query to run. - /// Name of event collection to query. - /// Name of property to analyse. - /// Name of field by which to group results. - /// Specifies window of time from which to select events for analysis. May be absolute or relative. - /// The block size for partitioning the specified timeframe. Optional, may be null. - /// Filters to narrow down the events used in analysis. Optional, may be null. - /// The timezone to use when specifying a relative timeframe. Optional, may be blank. - /// - public IEnumerable>>> QueryIntervalGroup( - QueryType queryType, string collection, string targetProperty, string groupBy, IQueryTimeframe timeframe, - QueryInterval interval, IEnumerable filters = null, string timezone = "") - { - try - { - return - QueryIntervalGroupAsync(queryType, collection, targetProperty, groupBy, timeframe, interval, filters, - timezone).Result; - } - catch (AggregateException ex) - { - throw ex.TryUnwrap(); - } - } - - /// - /// Extract full-form event data with all property values. - /// - /// Name of event collection to query. - /// Specifies window of time from which to select events for analysis. May be absolute or relative. - /// Filter to narrow down the events used in analysis. Optional, may be null. - /// Request up to 100 of the most recent events added to a given collection. - /// If specified, email will be sent when the data is ready for download. Otherwise, it will be returned directly. - /// - public async Task> QueryExtractResourceAsync(string collection, - IQueryTimeframe timeframe = null, IEnumerable filters = null, int latest = 0, string email = "") - { - return await Queries.Extract(collection, timeframe, filters, latest, email).ConfigureAwait(false); - } - - /// - /// Extract full-form event data with all property values. - /// - /// Name of event collection to query. - /// Specifies window of time from which to select events for analysis. May be absolute or relative. - /// Filter to narrow down the events used in analysis. Optional, may be null. - /// Request up to 100 of the most recent events added to a given collection. - /// If specified, email will be sent when the data is ready for download. Otherwise, it will be returned directly. - /// - public IEnumerable QueryExtractResource(string collection, IQueryTimeframe timeframe = null, - IEnumerable filters = null, int latest = 0, string email = "") - { - try - { - return QueryExtractResourceAsync(collection, timeframe, filters, latest, email).Result; - } - catch (AggregateException ex) - { - throw ex.TryUnwrap(); - } - } - - /// - /// Funnels count relevant events in succession. See API documentation for details. - /// - /// Analysis steps for funnel. - /// Specifies window of time from which to select events for analysis. May be absolute or relative. - /// The timezone to use when specifying a relative timeframe. Optional, may be blank. - /// - public async Task QueryFunnelAsync(IEnumerable steps, - IQueryTimeframe timeframe = null, string timezone = "") - { - return await Queries.Funnel(steps, timeframe, timezone).ConfigureAwait(false); - } - - /// - /// Funnels count relevant events in succession. See API documentation for details. - /// - /// Analysis steps for funnel. - /// Specifies window of time from which to select events for analysis. May be absolute or relative. - /// The timezone to use when specifying a relative timeframe. Optional, may be blank. - /// - public FunnelResult QueryFunnel(IEnumerable steps, - IQueryTimeframe timeframe = null, string timezone = "") - { - try - { - return QueryFunnelAsync(steps, timeframe, timezone).Result; - } - catch (AggregateException ex) - { - throw ex.TryUnwrap(); - } - } - - /// - /// Run multiple types of analysis over the same data. - /// - /// Name of event collection to query. - /// Defines the multiple types of analyses to perform. - /// Specifies window of time from which to select events for analysis. May be absolute or relative. - /// Filter to narrow down the events used in analysis. Optional, may be null. - /// The timezone to use when specifying a relative timeframe. Optional, may be blank. - /// - public async Task> QueryMultiAnalysisAsync(string collection, - IEnumerable analysisParams, IQueryTimeframe timeframe = null, - IEnumerable filters = null, string timezone = "") - { - return - await - Queries.MultiAnalysis(collection, analysisParams, timeframe, filters, timezone) - .ConfigureAwait(false); - } - - /// - /// Run multiple types of analysis over the same data. - /// - /// Name of event collection to query. - /// Defines the multiple types of analyses to perform. - /// Specifies window of time from which to select events for analysis. May be absolute or relative. - /// Filter to narrow down the events used in analysis. Optional, may be null. - /// The timezone to use when specifying a relative timeframe. Optional, may be blank. - /// - public IDictionary QueryMultiAnalysis(string collection, - IEnumerable analysisParams, IQueryTimeframe timeframe = null, - IEnumerable filters = null, string timezone = "") - { - try - { - return QueryMultiAnalysisAsync(collection, analysisParams, timeframe, filters, timezone).Result; - } - catch (AggregateException ex) - { - throw ex.TryUnwrap(); - } - } - - /// - /// Run multiple types of analysis over the same data, - /// grouped by the specified field. - /// - /// Name of event collection to query. - /// Defines the multiple types of analyses to perform. - /// Specifies window of time from which to select events for analysis. May be absolute or relative. - /// Filter to narrow down the events used in analysis. Optional, may be null. - /// Name of a collection field by which to group results. - /// The timezone to use when specifying a relative timeframe. Optional, may be blank. - /// - public async Task>>> QueryMultiAnalysisGroupAsync( - string collection, IEnumerable analysisParams, IQueryTimeframe timeframe = null, - IEnumerable filters = null, string groupBy = "", string timezone = "") - { - return - await - Queries.MultiAnalysis(collection, analysisParams, timeframe, filters, groupBy, timezone) - .ConfigureAwait(false); - } - - /// - /// Run multiple types of analysis over the same data, - /// grouped by the specified field. - /// - /// Name of event collection to query. - /// Defines the multiple types of analyses to perform. - /// Specifies window of time from which to select events for analysis. May be absolute or relative. - /// Filter to narrow down the events used in analysis. Optional, may be null. - /// Name of a collection field by which to group results. - /// The timezone to use when specifying a relative timeframe. Optional, may be blank. - /// - public IEnumerable>> QueryMultiAnalysisGroup(string collection, - IEnumerable analysisParams, IQueryTimeframe timeframe = null, - IEnumerable filters = null, string groupBy = "", string timezone = "") - { - try - { - return - QueryMultiAnalysisGroupAsync(collection, analysisParams, timeframe, filters, groupBy, timezone) - .Result; - } - catch (AggregateException ex) - { - throw ex.TryUnwrap(); - } - } - - /// - /// Run multiple types of analysis over the same data. - /// Each item represents one interval. - /// - /// Name of event collection to query. - /// Defines the multiple types of analyses to perform. - /// Specifies window of time from which to select events for analysis. May be absolute or relative. - /// The block size for partitioning the specified timeframe. Optional, may be null. - /// Filters to narrow down the events used in analysis. Optional, may be null. - /// The timezone to use when specifying a relative timeframe. Optional, may be blank. - /// - public async Task>>> QueryMultiAnalysisIntervalAsync( - string collection, IEnumerable analysisParams, IQueryTimeframe timeframe = null, - QueryInterval interval = null, IEnumerable filters = null, string timezone = "") - { - return - await - Queries.MultiAnalysis(collection, analysisParams, timeframe, interval, filters, timezone) - .ConfigureAwait(false); - } - - /// - /// Run multiple types of analysis over the same data. - /// Each item represents one interval. - /// - /// Name of event collection to query. - /// Defines the multiple types of analyses to perform. - /// Specifies window of time from which to select events for analysis. May be absolute or relative. - /// The block size for partitioning the specified timeframe. Optional, may be null. - /// Filters to narrow down the events used in analysis. Optional, may be null. - /// The timezone to use when specifying a relative timeframe. Optional, may be blank. - /// - public IEnumerable>> QueryMultiAnalysisInterval( - string collection, IEnumerable analysisParams, IQueryTimeframe timeframe = null, - QueryInterval interval = null, IEnumerable filters = null, string timezone = "") - { - try - { - return - QueryMultiAnalysisIntervalAsync(collection, analysisParams, timeframe, interval, filters, timezone) - .Result; - } - catch (AggregateException ex) - { - throw ex.TryUnwrap(); - } - } - - /// - /// Run multiple types of analysis over the same data. - /// Each item contains information about the groupings in that interval. - /// - /// Name of event collection to query. - /// Defines the multiple types of analyses to perform. - /// Name of field by which to group results. - /// Specifies window of time from which to select events for analysis. May be absolute or relative. - /// The block size for partitioning the specified timeframe. Optional, may be null. - /// Filters to narrow down the events used in analysis. Optional, may be null. - /// The timezone to use when specifying a relative timeframe. Optional, may be blank. - /// - public async Task>>>>> - QueryMultiAnalysisIntervalGroupAsync(string collection, IEnumerable analysisParams, - IQueryTimeframe timeframe = null, QueryInterval interval = null, IEnumerable filters = null, - string groupBy = "", string timezone = "") - { - return - await - Queries.MultiAnalysis(collection, analysisParams, timeframe, interval, filters, groupBy, timezone) - .ConfigureAwait(false); - } - - /// - /// Run multiple types of analysis over the same data. - /// Each item contains information about the groupings in that interval. - /// - /// Name of event collection to query. - /// Defines the multiple types of analyses to perform. - /// Name of field by which to group results. - /// Specifies window of time from which to select events for analysis. May be absolute or relative. - /// The block size for partitioning the specified timeframe. Optional, may be null. - /// Filters to narrow down the events used in analysis. Optional, may be null. - /// The timezone to use when specifying a relative timeframe. Optional, may be blank. - /// - public IEnumerable>>>> - QueryMultiAnalysisIntervalGroup(string collection, IEnumerable analysisParams, - IQueryTimeframe timeframe = null, QueryInterval interval = null, IEnumerable filters = null, - string groupBy = "", string timezone = "") - { - try - { - return - QueryMultiAnalysisIntervalGroupAsync(collection, analysisParams, timeframe, interval, filters, - groupBy, timezone).Result; - } - catch (AggregateException ex) - { - throw ex.TryUnwrap(); - } - } - - /// - /// Get query results from a Cached Dataset. - /// - /// Name of cached dataset to query. - /// The string property value you want to retrieve results by. - /// Limits retrieval of results to a specific portion of the Cached Dataset - /// An instance of Newtonsoft.Json.Linq.JObject containing query results and metadata defining the cached dataset. - public async Task QueryDatasetAsync(string datasetName, string indexBy, string timeframe) - { - return await Datasets.GetResultsAsync(datasetName, indexBy, timeframe); - } - - /// - /// Get query results from a Cached Dataset. - /// - /// Name of cached dataset to query. - /// The string property value you want to retrieve results by. - /// Limits retrieval of results to a specific portion of the Cached Dataset - /// An instance of Newtonsoft.Json.Linq.JObject containing query results and metadata defining the cached dataset. - public JObject QueryDataset(string datasetName, string indexBy, string timeframe) - { - try - { - return QueryDatasetAsync(datasetName, indexBy, timeframe).Result; - } - catch (AggregateException ex) - { - throw ex.TryUnwrap(); - } - } - - /// - /// Get the definition of your cached dataset. - /// - /// Name of cached dataset to get the definition of. - /// An instance of Keen.Core.Dataset.DatasetDefinition containing metadata about your cached dataset. - public async Task GetDatasetDefinitionAsync(string datasetName) - { - return await Datasets.GetDefinitionAsync(datasetName); - } - - /// - /// Get the definition of your cached dataset. - /// - /// Name of cached dataset to get the definition of. - /// An instance of Keen.Core.Dataset.DatasetDefinition containing metadata about your cached dataset. - public DatasetDefinition GetDatasetDefinition(string datasetName) - { - try - { - return GetDatasetDefinitionAsync(datasetName).Result; - } - catch (AggregateException ex) - { - throw ex.TryUnwrap(); - } - } - - /// - /// Lists the first n cached dataset definitions in your project. - /// - /// How many cached dataset definitions to return at a time (1-100). Defaults to 10. - /// A cursor for use in pagination. afterName is the Cached Dataset name that defines your place in the list. - /// For instance, if you make a list request and receive 100 Cached Dataset definitions, - /// ending with dataset_foo you can use dataset_foo as your afterName to retrieve the next page of definitions. - /// Lists also return with helper “NextPageUrl” that uses AfterName, - /// so your subsequent call can fetch the next page of the list easily. - /// An instance of Keen.Core.Dataset.DatasetDefinitionCollection containing the total count, next page url and list of DatasetDefinitions. - public Task ListDatasetDefinitionsAsync(int limit = 10, string afterName = null) - { - return Datasets.ListDefinitionsAsync(limit, afterName); - } - - /// - /// Lists the first n cached dataset definitions in your project. - /// - /// How many cached dataset definitions to return at a time (1-100). Defaults to 10. - /// A cursor for use in pagination. afterName is the Cached Dataset name that defines your place in the list. - /// For instance, if you make a list request and receive 100 Cached Dataset definitions, - /// ending with dataset_foo you can use dataset_foo as your afterName to retrieve the next page of definitions. - /// Lists also return with helper “NextPageUrl” that uses AfterName, - /// so your subsequent call can fetch the next page of the list easily. - /// An instance of Keen.Core.Dataset.DatasetDefinitionCollection containing the total count, next page url and list of DatasetDefinitions. - public DatasetDefinitionCollection ListDatasetDefinitions(int limit = 10, string afterName = null) - { - try - { - return ListDatasetDefinitionsAsync(limit, afterName).Result; - } - catch(AggregateException ex) - { - throw ex.TryUnwrap(); - } - } - - /// - /// Lists all the dataset definitions in your project. - /// - /// A list of Keen.Core.Dataset.DatasetDefinition - public Task> ListAllDatasetDefinitionsAsync() - { - return Datasets.ListAllDefinitionsAsync(); - } - - /// - /// Lists all the dataset definitions in your project. - /// - /// A list of Keen.Core.Dataset.DatasetDefinition - public IEnumerable ListAllDatasetDefinitions() - { - try - { - return ListAllDatasetDefinitionsAsync().Result; - } - catch (AggregateException ex) - { - throw ex.TryUnwrap(); - } - } - - /// - /// Creates a new Cached Dataset - /// - /// An instance of Keen.Core.Dataset.DatasetDefinition. It must have DatasetName, DisplayName, IndexBy and Query populated. - /// An instance of Keen.Core.Dataset.DatasetDefinition populated more information. - public Task CreateDatasetAsync(DatasetDefinition dataset) - { - return Datasets.CreateDatasetAsync(dataset); - } - - /// - /// Creates a new Cached Dataset - /// - /// An instance of Keen.Core.Dataset.DatasetDefinition. It must have DatasetName, DisplayName, IndexBy and Query populated. - /// An instance of Keen.Core.Dataset.DatasetDefinition populated more information. - public DatasetDefinition CreateDataset(DatasetDefinition dataset) - { - try - { - return CreateDatasetAsync(dataset).Result; - } - catch (AggregateException ex) - { - throw ex.TryUnwrap(); - } - } - - /// - /// Delete a Cached Dataset - /// - /// The name of the dataset to be deleted. - public Task DeleteDatasetAsync(string datasetName) - { - return Datasets.DeleteDatasetAsync(datasetName); - } - - /// - /// Delete a Cached Dataset - /// - /// The name of the dataset to be deleted. - public void DeleteDataset(string datasetName) - { - try - { - DeleteDatasetAsync(datasetName).Wait(); - } - catch (AggregateException ex) - { - throw ex.TryUnwrap(); - } - } - } -} diff --git a/Keen/KeenConstants.cs b/Keen/KeenConstants.cs deleted file mode 100644 index 0b1c6a4..0000000 --- a/Keen/KeenConstants.cs +++ /dev/null @@ -1,102 +0,0 @@ - -namespace Keen.Core -{ - public class KeenConstants - { - private const string serverAddress = "https://api.keen.io"; - public static string ServerAddress { get { return serverAddress; } protected set { ;} } - - private const string eventsResource = "events"; - public static string EventsResource { get { return eventsResource; } protected set { ;} } - - private const string queriesResource = "queries"; - public static string QueriesResource { get { return queriesResource; } protected set { ;} } - - private const string queryCount = "count"; - public static string QueryCount { get { return queryCount; } protected set { ;} } - - private const string queryCountUnique = "count_unique"; - public static string QueryCountUnique { get { return queryCountUnique; } protected set { ;} } - - private const string queryMinimum = "minimum"; - public static string QueryMinimum { get { return queryMinimum; } protected set { ;} } - - private const string queryMaximum = "maximum"; - public static string QueryMaximum { get { return queryMaximum; } protected set { ;} } - - private const string queryAverage = "average"; - public static string QueryAverage { get { return queryAverage; } protected set { ;} } - - private const string querySum = "sum"; - public static string QuerySum { get { return querySum; } protected set { ;} } - - private const string querySelectUnique = "select_unique"; - public static string QuerySelectUnique { get { return querySelectUnique; } protected set { ;} } - - private const string queryExtraction = "extraction"; - public static string QueryExtraction { get { return queryExtraction; } protected set { ;} } - - private const string queryFunnel = "funnel"; - public static string QueryFunnel { get { return queryFunnel; } protected set { ;} } - - private const string queryMultiAnalysis = "multi_analysis"; - public static string QueryMultiAnalysis { get { return queryMultiAnalysis; } protected set { ;} } - - private const string queryParmEventCollection = "event_collection"; - public static string QueryParmEventCollection { get { return queryParmEventCollection; } protected set { ;} } - - private const string queryParmTargetProperty = "target_property"; - public static string QueryParmTargetProperty { get { return queryParmTargetProperty; } protected set { ;} } - - private const string queryParmTimeframe = "timeframe"; - public static string QueryParmTimeframe { get { return queryParmTimeframe; } protected set { ;} } - - private const string queryParmGroupBy = "group_by"; - public static string QueryParmGroupBy { get { return queryParmGroupBy; } protected set { ;} } - - private const string queryParmInterval = "interval"; - public static string QueryParmInterval { get { return queryParmInterval; } protected set { ;} } - - private const string queryParmTimezone = "timezone"; - public static string QueryParmTimezone { get { return queryParmTimezone; } protected set { ;} } - - private const string queryParmFilters = "filters"; - public static string QueryParmFilters { get { return queryParmFilters; } protected set { ;} } - - private const string queryParmEmail = "email"; - public static string QueryParmEmail { get { return queryParmEmail; } protected set { ;} } - - private const string queryParmLatest = "latest"; - public static string QueryParmLatest { get { return queryParmLatest; } protected set { ;} } - - private const string queryParmSteps = "steps"; - public static string QueryParmSteps { get { return queryParmSteps; } protected set { ;} } - - private const string queryParmAnalyses = "analyses"; - public static string QueryParmAnalyses { get { return queryParmAnalyses; } protected set { ;} } - - private const string apiVersion = "3.0"; - public static string ApiVersion { get { return apiVersion; } protected set { ;} } - - private const int bulkBatchSize = 1000; - public static int BulkBatchSize { get { return bulkBatchSize; } protected set { ;} } - - private const string datasetsResource = "datasets"; - public static string DatasetsResource { get { return datasetsResource; } protected set { ;} } - - private const string keenProjectId = "KEEN_PROJECT_ID"; - public static string KeenProjectId { get { return keenProjectId; } protected set {; } } - - private const string keenMasterKey = "KEEN_MASTER_KEY"; - public static string KeenMasterKey { get { return keenMasterKey; } protected set {; } } - - private const string keenWriteKey = "KEEN_WRITE_KEY"; - public static string KeenWriteKey { get { return keenWriteKey; } protected set {; } } - - private const string keenReadKey = "KEEN_READ_KEY"; - public static string KeenReadKey { get { return keenReadKey; } protected set {; } } - - private const string keenServerUrl = "KEEN_SERVER_URL"; - public static string KeenServerUrl { get { return keenServerUrl; } protected set {; } } - } -} diff --git a/Keen/KeenException.cs b/Keen/KeenException.cs deleted file mode 100644 index ed5e833..0000000 --- a/Keen/KeenException.cs +++ /dev/null @@ -1,85 +0,0 @@ -using Keen.Core.EventCache; -using System; -using System.Collections.Generic; - - -namespace Keen.Core -{ - public class KeenException : Exception - { - public KeenException() { } - public KeenException(string message) : base(message) { } - public KeenException(string message, Exception inner) : base(message, inner) { } - } - - public class KeenInvalidApiKeyException : KeenException - { - public KeenInvalidApiKeyException() { } - public KeenInvalidApiKeyException(string message) : base(message) { } - public KeenInvalidApiKeyException(string message, Exception inner) : base(message, inner) { } - } - - public class KeenResourceNotFoundException : KeenException - { - public KeenResourceNotFoundException() { } - public KeenResourceNotFoundException(string message) : base(message) { } - public KeenResourceNotFoundException(string message, Exception inner) : base(message, inner) { } - } - - public class KeenNamespaceTypeException : KeenException - { - public KeenNamespaceTypeException() { } - public KeenNamespaceTypeException(string message) : base(message) { } - public KeenNamespaceTypeException(string message, Exception inner) : base(message, inner) { } - } - - public class KeenInvalidEventException : KeenException - { - public KeenInvalidEventException() { } - public KeenInvalidEventException(string message) : base(message) { } - public KeenInvalidEventException(string message, Exception inner) : base(message, inner) { } - } - - public class KeenListsOfNonPrimitivesNotAllowedException : KeenException - { - public KeenListsOfNonPrimitivesNotAllowedException() { } - public KeenListsOfNonPrimitivesNotAllowedException(string message) : base(message) { } - public KeenListsOfNonPrimitivesNotAllowedException(string message, Exception inner) : base(message, inner) { } - } - - public class KeenInvalidBatchException : KeenException - { - public KeenInvalidBatchException() { } - public KeenInvalidBatchException(string message) : base(message) { } - public KeenInvalidBatchException(string message, Exception inner) : base(message, inner) { } - } - - public class KeenInternalServerErrorException : KeenException - { - public KeenInternalServerErrorException() { } - public KeenInternalServerErrorException(string message) : base(message) { } - public KeenInternalServerErrorException(string message, Exception inner) : base(message, inner) { } - } - - public class KeenInvalidKeenNamespacePropertyException: KeenException - { - public KeenInvalidKeenNamespacePropertyException() { } - public KeenInvalidKeenNamespacePropertyException(string message) : base(message) { } - public KeenInvalidKeenNamespacePropertyException(string message, Exception inner) : base(message, inner) { } - } - - public class KeenInvalidPropertyNameException: KeenException - { - public KeenInvalidPropertyNameException() { } - public KeenInvalidPropertyNameException(string message) : base(message) { } - public KeenInvalidPropertyNameException(string message, Exception inner) : base(message, inner) { } - } - - public class KeenBulkException : KeenException - { - private IEnumerable _failedEvents; - public IEnumerable FailedEvents { get { return _failedEvents; } protected set { ; } } - public KeenBulkException(IEnumerable failedEvents) { _failedEvents = failedEvents; } - public KeenBulkException(string message, IEnumerable failedEvents ) : base(message) { _failedEvents = failedEvents; } - } -} diff --git a/Keen/KeenHttpClient.cs b/Keen/KeenHttpClient.cs deleted file mode 100644 index 4ebccbf..0000000 --- a/Keen/KeenHttpClient.cs +++ /dev/null @@ -1,230 +0,0 @@ -using System; -using System.IO; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Text; -using System.Threading.Tasks; - - -namespace Keen.Core -{ - /// - /// Helps with performing HTTP operations destined for a Keen API endpoint. Helper methods in - /// this class will add appropriate headers and config to use the underlying HttpClient - /// in the way expected by the Keen IO API. This class should be long-lived and all public - /// methods are thread-safe, so ideal usage is to configure it once for a given base URL and - /// reuse it with relative resources to send requests for the duration of the app or module. - /// - internal class KeenHttpClient : IKeenHttpClient - { - private static readonly string JSON_CONTENT_TYPE = "application/json"; - private static readonly string AUTH_HEADER_KEY = "Authorization"; - - - // We don't destroy this manually. Whatever code provides the HttpClient directly or via an - // IHttpClientProvider should be sure to handle its lifetime. - private readonly HttpClient _httpClient = null; - - - internal KeenHttpClient(HttpClient httpClient) - { - _httpClient = httpClient; - } - - internal static string GetRelativeUrl(string projectId, string resource) - { - return $"projects/{projectId}/{resource}"; - } - - /// - /// Create and send a GET request to the given relative resource using the given key for - /// authentication. - /// - /// The relative resource to GET. Must be properly formatted as a - /// relative Uri. - /// The key to use for authenticating this request. - /// The response message. - public Task GetAsync(string resource, string authKey) - { - var url = new Uri(resource, UriKind.Relative); - - return GetAsync(url, authKey); - } - - /// - /// Create and send a GET request to the given relative resource using the given key for - /// authentication. - /// - /// The relative resource to GET. - /// The key to use for authenticating this request. - /// The response message. - public Task GetAsync(Uri resource, string authKey) - { - KeenHttpClient.RequireAuthKey(authKey); - - HttpRequestMessage get = CreateRequest(HttpMethod.Get, resource, authKey); - - return _httpClient.SendAsync(get); - } - - // TODO : Instead of (or in addition to) string, also accept HttpContent content and/or - // JObject content? - - /// - /// Create and send a POST request with the given content to the given relative resource - /// using the given key for authentication. - /// - /// The relative resource to POST. Must be properly formatted as a - /// relative Uri. - /// The key to use for authenticating this request. - /// The POST body to send. - /// The response message. - public Task PostAsync(string resource, string authKey, string content) - { - var url = new Uri(resource, UriKind.Relative); - - return PostAsync(url, authKey, content); - } - - /// - /// Create and send a POST request with the given content to the given relative resource - /// using the given key for authentication. - /// - /// The relative resource to POST. - /// The key to use for authenticating this request. - /// The POST body to send. - /// The response message. - public Task PostAsync(Uri resource, - string authKey, - string content) - { - return DispatchWithContentAsync(HttpMethod.Post, resource, authKey, content); - } - - /// - /// Create and send a DELETE request to the given relative resource using the given key for - /// authentication. - /// - /// The relative resource to DELETE. Must be properly formatted as a - /// relative Uri. - /// The key to use for authenticating this request. - /// The response message. - public Task DeleteAsync(string resource, string authKey) - { - var url = new Uri(resource, UriKind.Relative); - - return DeleteAsync(url, authKey); - } - - /// - /// Create and send a DELETE request to the given relative resource using the given key for - /// authentication. - /// - /// The relative resource to DELETE. - /// The key to use for authenticating this request. - /// The response message. - public Task DeleteAsync(Uri resource, string authKey) - { - KeenHttpClient.RequireAuthKey(authKey); - - HttpRequestMessage delete = CreateRequest(HttpMethod.Delete, resource, authKey); - - return _httpClient.SendAsync(delete); - } - - /// - /// Create and send a PUT request with the given content to the given relative resource - /// using the given key for authentication. - /// - /// The relative resource to PUT. Must be properly formatted as a - /// relative Uri. - /// The key to use for authenticating this request. - /// The PUT body to send. - /// The response message. - public Task PutAsync(string resource, string authKey, string content) - { - var url = new Uri(resource, UriKind.Relative); - - return PutAsync(url, authKey, content); - } - - /// - /// Create and send a PUT request with the given content to the given relative resource - /// using the given key for authentication. - /// - /// The relative resource to PUT. Must be properly formatted as a - /// relative Uri. - /// The key to use for authenticating this request. - /// The PUT body to send. - /// The response message. - public Task PutAsync(Uri resource, - string authKey, - string content) - { - return DispatchWithContentAsync(HttpMethod.Put, resource, authKey, content); - } - - private async Task DispatchWithContentAsync(HttpMethod httpMethod, - Uri resource, - string authKey, - string content) - { - KeenHttpClient.RequireAuthKey(authKey); - - if (string.IsNullOrWhiteSpace(content)) - { - // Technically, we can encode an empty string or whitespace, but why? For now - // we use GET for querying. If we ever need to POST with no content, we should - // reorganize the logic below to never create/set the content stream. - throw new ArgumentNullException(nameof(content), "Unexpected empty content."); - } - - // If we switch PCL profiles, instead use MediaTypeFormatters (or ObjectContent)?, - // like here?: https://msdn.microsoft.com/en-us/library/system.net.http.httpclientextensions.putasjsonasync(v=vs.118).aspx - using (var contentStream = - new StreamContent(new MemoryStream(Encoding.UTF8.GetBytes(content)))) - { - // TODO : Make sure this is the same as Add("content-type", "application/json") - contentStream.Headers.ContentType = - new MediaTypeHeaderValue(KeenHttpClient.JSON_CONTENT_TYPE); - - HttpRequestMessage request = CreateRequest(httpMethod, resource, authKey); - request.Content = contentStream; - - return await _httpClient.SendAsync(request).ConfigureAwait(false); - - // TODO : Should we do the KeenUtil.CheckApiErrorCode() here? - // TODO : Should we check the if (!responseMsg.IsSuccessStatusCode) here too? - // TODO : If we centralize error checking in this class we could have variations - // of these helpers that return string or JToken or JArray or JObject. It might - // also be nice for those options to optionally hand back the raw - // HttpResponseMessage in an out param if desired? - // TODO : Use CallerMemberNameAttribute to print error messages? - // http://stackoverflow.com/questions/3095696/how-do-i-get-the-calling-method-name-and-type-using-reflection?noredirect=1&lq=1 - } - } - - private static HttpRequestMessage CreateRequest(HttpMethod verb, - Uri resource, - string authKey) - { - var request = new HttpRequestMessage() - { - RequestUri = resource, - Method = verb - }; - - request.Headers.Add(KeenHttpClient.AUTH_HEADER_KEY, authKey); - - return request; - } - - private static void RequireAuthKey(string authKey) - { - if (string.IsNullOrWhiteSpace(authKey)) - { - throw new ArgumentNullException(nameof(authKey), "Auth key is required."); - } - } - } -} diff --git a/Keen/KeenHttpClientFactory.cs b/Keen/KeenHttpClientFactory.cs deleted file mode 100644 index 191029c..0000000 --- a/Keen/KeenHttpClientFactory.cs +++ /dev/null @@ -1,260 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; - - -namespace Keen.Core -{ - /// - /// A set of factory methods to help in creating see cref="IKeenHttpClient"/> instances. These - /// are useful when implementing see cref="IKeenHttpClientProvider"/> so that the constructed - /// instances have the right mix of default and custom configuration. - /// - public static class KeenHttpClientFactory - { - private static readonly IEnumerable> DEFAULT_HEADERS = - new[] { new KeyValuePair("Keen-Sdk", KeenUtil.GetSdkVersion()) }; - - - private class LoggingHttpHandler : DelegatingHandler - { - protected override Task SendAsync( - HttpRequestMessage request, - CancellationToken cancellationToken) - { - // TODO : Log stuff before and after request, then move to its own file. - - // Now dispatch to the inner handler via the base impl. - return base.SendAsync(request, cancellationToken); - } - } - - - private static HttpMessageHandler CreateHandlerChainInternal( - HttpClientHandler innerHandler, - IEnumerable handlers) - { - // NOTE : There is no WebProxy available to the PCL profile, so we have to create an - // IWebProxy implementation manually. Proxy is only supported on HttpClientHandler, and - // not directly on DelegatingHandler, so handle that too. Basically only support Proxy - // if client code does *not* give us an HttpClientHandler. Or else set the Proxy on - // their handler, but make sure it's not already set. - - // Example of how setting Proxy works in big .NET where the WebProxy class exists: - // - // new HttpClientHandler() - // { - // Proxy = WebProxy("http://localhost:8888", false), - // UseProxy = true - // }; - - - // TODO : Also, to support Proxy, we have to realize we'd be turning it on for a given - // HttpClientHandler already installed for the HttpClient in the cache for a given URL. - // Since modifications aren't allowed for HttpClients/*Handlers, we would replace the - // HttpClient, which would affect all future users of the cache requesting an - // HttpClient for that URL, when really we want an abstraction to keep that sort of - // setting tied to, which maybe is this KeenHttpClient? - - - HttpMessageHandler handlerChain = (innerHandler ?? new HttpClientHandler()); - - if (null == handlers) - { - return handlerChain; - } - - foreach (var handler in handlers.Reverse()) - { - if (null == handler) - { - throw new ArgumentNullException(nameof(handlers), - "One of the given DelegatingHandler params was null."); - } - - if (null != handler.InnerHandler) - { - throw new ArgumentException("Encountered a non-null InnerHandler in handler " + - "chain, which would be overwritten.", - nameof(handlers)); - } - - // This will throw if the given handler has already started any requests. - // Basically all properties on all HttpClient/*Handler variations call - // CheckDisposedOrStarted() in any setter, so the entire HttpClient is pretty much - // locked down once it starts getting used. - handler.InnerHandler = handlerChain; - handlerChain = handler; - } - - return handlerChain; - } - - private static IEnumerable CreateDefaultDelegatingHandlers() - { - // TODO : Put more custom handlers in here, like retry/failure/proxy/logging handlers. - - // Create these every time, since *Handlers can't have properties changed after they've - // started handling requests for an HttpClient. - return new[] { new LoggingHttpHandler() }; - } - - /// - /// Create the default handler pipeline with only Keen internal handlers installed. - /// - /// returns>The default handler chain. - public static HttpMessageHandler CreateDefaultHandlerChain() - { - return KeenHttpClientFactory.CreateHandlerChainInternal( - null, - KeenHttpClientFactory.CreateDefaultDelegatingHandlers()); - } - - /// - /// Create an HttpMessageHandler representing the handler pipeline. We will construct the - /// HTTP handler pipeline such that provided handlers are called in order for requests, and - /// receive responses in reverse order. Keen internal handlers will defer to the first - /// DelegatingHandler and the pipeline will terminate at our HttpClientHandler. - /// - /// Handlers to be chained in the pipeline. - /// returns>The entire handler chain. - public static HttpMessageHandler CreateHandlerChain(params DelegatingHandler[] handlers) - { - return KeenHttpClientFactory.CreateHandlerChain(null, handlers); - } - - /// - /// Create an HttpMessageHandler representing the handler pipeline. We will construct the - /// HTTP handler pipeline such that provided handlers are called in order for requests, and - /// receive responses in reverse order. Keen internal handlers will defer to the first - /// DelegatingHandler and the pipeline will terminate at our HttpClientHandler or to the - /// given HttpClientHandler if present, in case client code wants to do something like use - /// WebRequestHandler functionality or otherwise add custom behavior. - /// - /// Terminating HttpClientHandler. - /// Handlers to be chained in the pipeline. - /// The entire handler chain. - public static HttpMessageHandler CreateHandlerChain(HttpClientHandler innerHandler, - params DelegatingHandler[] handlers) - { - // We put our handlers first. Client code can look at the final state of the request - // this way. Overwriting built-in handler state is shooting oneself in the foot. - IEnumerable intermediateHandlers = - KeenHttpClientFactory.CreateDefaultDelegatingHandlers().Concat(handlers); - - return KeenHttpClientFactory.CreateHandlerChainInternal(innerHandler, - intermediateHandlers); - } - - // NOTE : BaseUrl should have a final slash or the last Uri part is discarded. Also, - // relative urls can *not* start with a slash. - - // Not exposed so that 3rd party code doesn't accidentally build a KeenHttpClient without - // our handlers installed, which wouldn't be ideal. - private static KeenHttpClient Create(Uri baseUrl, - IHttpClientProvider httpClientProvider, - Func getHandlerChain) - { - if (!baseUrl.IsAbsoluteUri) - { - throw new ArgumentException( - "The given base Url must be in the form of an absolute Uri.", - nameof(baseUrl)); - } - - // Delay actual creation of the handler chain by passing in a Func<> to create it. This - // way if HttpClient already exists, we won't bother creating/modifying handlers. - var httpClient = httpClientProvider.GetOrCreateForUrl( - baseUrl, - getHandlerChain, - KeenHttpClientFactory.DEFAULT_HEADERS); - - var newClient = new KeenHttpClient(httpClient); - - return newClient; - } - - /// - /// Construct an IKeenHttpClient for the given base URL, configured with an HttpClient that - /// is retrieved and/or stored in the given IHttpClientProvider. If necessary, the - /// HttpClient is created and configured with the default set of HTTP handlers. - /// - /// - /// - /// - /// The base URL for the constructed IKeenHttpClient. - /// The provider used to retrieve the HttpClient. - /// A new IKeenHttpClient for the given base URL. - public static IKeenHttpClient Create(Uri baseUrl, IHttpClientProvider httpClientProvider) - { - return KeenHttpClientFactory.Create( - baseUrl, - httpClientProvider, - () => KeenHttpClientFactory.CreateDefaultHandlerChain()); - } - - /// - /// Construct an IKeenHttpClient for the given base URL, configured with an HttpClient that - /// is retrieved and/or stored in the given IHttpClientProvider, and if necessary, - /// configured with the given HTTP handlers. - /// - /// - /// - /// - /// The base URL for the constructed IKeenHttpClient. - /// The provider used to retrieve the HttpClient. - /// HTTP handler terminating the handler chain. - /// Handlers to be chained in the pipeline. - /// A new IKeenHttpClient for the given base URL. - public static IKeenHttpClient Create(Uri baseUrl, - IHttpClientProvider httpClientProvider, - HttpClientHandler innerHandler, - params DelegatingHandler[] handlers) - { - return KeenHttpClientFactory.Create( - baseUrl, - httpClientProvider, - () => KeenHttpClientFactory.CreateHandlerChain(innerHandler, handlers)); - } - - /// - /// Construct an IKeenHttpClient for the given base URL, configured with an HttpClient that - /// is retrieved and/or stored in the given IHttpClientProvider, and if necessary, - /// configured with the given HTTP handlers in a lazy fashion only if construction is - /// necessary. Note that the given handler factory function could be called under a lock, - /// so care should be taken in multi-threaded scenarios. - /// - /// - /// - /// - /// The base URL for the constructed IKeenHttpClient. - /// The provider used to retrieve the HttpClient. - /// A factory function called if construction of the HttpClient - /// is necessary. It should return an optional HttpClientHandler to terminate the - /// handler chain, as well as an optional list of intermediate HTTP handlers to be - /// chained in the pipeline. - /// A new IKeenHttpClient for the given base URL. - public static IKeenHttpClient Create( - Uri baseUrl, - IHttpClientProvider httpClientProvider, - Func>> getHandlers) - { - Func getHandlerChain = () => - { - Tuple> handlers = - getHandlers?.Invoke(); - - return KeenHttpClientFactory.CreateHandlerChainInternal(handlers.Item1, - handlers.Item2); - }; - - return KeenHttpClientFactory.Create( - baseUrl, - httpClientProvider, - getHandlerChain); - } - } -} diff --git a/Keen/KeenHttpClientProvider.cs b/Keen/KeenHttpClientProvider.cs deleted file mode 100644 index 2703d8b..0000000 --- a/Keen/KeenHttpClientProvider.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; - - -namespace Keen.Core -{ - /// - /// An implementation of that uses the default - /// creation logic and relies on the - /// class as an . - /// - internal class KeenHttpClientProvider : IKeenHttpClientProvider - { - /// - /// Given a base URL, return an IKeenHttpClient against which requests can be made. - /// - /// The base URL, e.g. https://api.keen.io/3.0/ - /// An IKeenHttpClient configured to handle requests to resources relative to the - /// given base URL. - public IKeenHttpClient GetForUrl(Uri baseUrl) - { - var keenHttpClient = KeenHttpClientFactory.Create(baseUrl, HttpClientCache.Instance); - - return keenHttpClient; - } - } -} diff --git a/Keen/KeenUtil.cs b/Keen/KeenUtil.cs deleted file mode 100644 index 07005ca..0000000 --- a/Keen/KeenUtil.cs +++ /dev/null @@ -1,222 +0,0 @@ -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Reflection; -using System.Text.RegularExpressions; - - -namespace Keen.Core -{ - public static class KeenUtil - { - private static readonly string SdkVersion; - - static KeenUtil() - { - string version = GetAssemblyInformationalVersion(); - version = (string.IsNullOrWhiteSpace(version) ? "0.0.0" : version); - SdkVersion = string.Format(".net-{0}", version); - } - - /// - /// Retrieve a string representing the current version of the Keen IO SDK, as defined by - /// the AssemblyInformationalVersion. - /// - /// The SDK version string. - public static string GetSdkVersion() - { - return SdkVersion; - } - - private static string GetAssemblyInformationalVersion() - { - string assemblyInformationalVersion = string.Empty; - AssemblyInformationalVersionAttribute attribute = null; - - try - { - attribute = (AssemblyInformationalVersionAttribute)(Assembly.GetExecutingAssembly() - .GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false) - .FirstOrDefault()); - } - catch (Exception e) - { - Debug.WriteLine("Caught exception trying to read AssemblyInformationalVersion: {0}", e); - } - - if (null != attribute) - { - assemblyInformationalVersion = attribute.InformationalVersion; - } - - return assemblyInformationalVersion; - } - - private static HashSet validCollectionNames = new HashSet(); - - public static string ToSafeString(this object obj) - { - return (obj ?? string.Empty).ToString(); - } - - /// - /// Apply property name restrictions. Throws KeenException with an - /// explanation if a collection name is unacceptable. - /// - /// - public static void ValidatePropertyName(string property) - { - if (string.IsNullOrWhiteSpace(property)) - throw new KeenException("Property name may not be null or whitespace"); - - if (property.Length >= 256) - throw new KeenException("Property name must be less than 256 characters"); - - if (property.StartsWith("$")) - throw new KeenException("Property name may not start with \"$\""); - - if (property.Contains(".")) - throw new KeenException("Property name may not contain \".\""); - } - - /// - /// Apply the collection name restrictions. Throws KeenException with an - /// explanation if a collection name is unacceptable. - /// - /// - public static void ValidateEventCollectionName(string collection) - { - // Avoid cost of re-checking collection names that have already been validated. - // There is a race condition here, but it's harmless and does not justify the - // overhead of synchronization. - if (validCollectionNames.Contains(collection)) - return; - - if (null == collection) - throw new KeenException("Event collection name may not be null."); - if (string.IsNullOrWhiteSpace(collection)) - throw new KeenException("Event collection name may not be blank."); - if (collection.Length > 64) - throw new KeenException("Event collection name may not be longer than 64 characters."); - if (new Regex("[^\x00-\x7F]").Match(collection).Success) - throw new KeenException("Event collection name must contain only Ascii characters."); - if (collection.Contains("$")) - throw new KeenException("Event collection name may not contain \"$\"."); - if (collection.StartsWith("_")) - throw new KeenException("Event collection name may not begin with \"_\"."); - - validCollectionNames.Add(collection); - } - - /// - /// Check the 'error' field on a bulk insert operation response and return - /// the appropriate exception. - /// - /// Deserialized json response from a Keen API call. - public static Exception GetBulkApiError(JObject apiResponse) - { - var error = apiResponse.SelectToken("$.error"); - if (null == error) - return null; - - var errCode = error.SelectToken("$.name").ToString(); - var message = error.SelectToken("$.description").ToString(); - switch (errCode) - { - case "InvalidApiKeyError": - return new KeenInvalidApiKeyException(message); - - case "ResourceNotFoundError": - return new KeenResourceNotFoundException(message); - - case "NamespaceTypeError": - return new KeenNamespaceTypeException(message); - - case "InvalidEventError": - return new KeenInvalidEventException(message); - - case "ListsOfNonPrimitivesNotAllowedError": - return new KeenListsOfNonPrimitivesNotAllowedException(message); - - case "InvalidBatchError": - return new KeenInvalidBatchException(message); - - case "InternalServerError": - return new KeenInternalServerErrorException(message); - - case "InvalidKeenNamespaceProperty": - return new KeenInvalidKeenNamespacePropertyException(message); - - case "InvalidPropertyNameError": - return new KeenInvalidPropertyNameException(message); - - default: - Debug.WriteLine("Unhandled error_code \"{0}\" : \"{1}\"", errCode, message); - return new KeenException(errCode + " : " + message); - } - } - - /// - /// Check the 'error_code' field and throw the appropriate exception if non-null. - /// - /// Deserialized json response from a Keen API call. - public static void CheckApiErrorCode(dynamic apiResponse) - { - if (apiResponse is JArray) return; - - var errorCode = (string)apiResponse.SelectToken("$.error_code"); - - if (errorCode != null) - { - var message = (string)apiResponse.SelectToken("$.message"); - switch (errorCode) - { - case "InvalidApiKeyError": - throw new KeenInvalidApiKeyException(message); - - case "ResourceNotFoundError": - throw new KeenResourceNotFoundException(message); - - case "NamespaceTypeError": - throw new KeenNamespaceTypeException(message); - - case "InvalidEventError": - throw new KeenInvalidEventException(message); - - case "ListsOfNonPrimitivesNotAllowedError": - throw new KeenListsOfNonPrimitivesNotAllowedException(message); - - case "InvalidBatchError": - throw new KeenInvalidBatchException(message); - - case "InternalServerError": - throw new KeenInternalServerErrorException(message); - - case "InvalidKeenNamespaceProperty": - throw new KeenInvalidKeenNamespacePropertyException(message); - - default: - Debug.WriteLine("Unhandled error_code \"{0}\" : \"{1}\"", errorCode, message); - throw new KeenException(errorCode + " : " + message); - } - } - } - - /// - /// Flatten an AggregateException and if only one exception instance is found - /// in the innerexceptions, return it, otherwise return the original - /// AggregateException unchanged. - /// - /// - /// - internal static Exception TryUnwrap(this AggregateException ex) - { - if (ex.Flatten().InnerExceptions.Count == 1) - return ex.Flatten().InnerExceptions[0]; - else - return ex; - } - } -} diff --git a/Keen/ProjectSettingsProvider.cs b/Keen/ProjectSettingsProvider.cs deleted file mode 100644 index 9fe044c..0000000 --- a/Keen/ProjectSettingsProvider.cs +++ /dev/null @@ -1,62 +0,0 @@ - -namespace Keen.Core -{ - public class ProjectSettingsProvider : IProjectSettings - { - /// - /// The Keen.IO URL for this project. Usually this will be the - /// server address and API version. - /// - public string KeenUrl { get; protected set; } - - /// - /// The Project ID, identifying the data silo to be accessed. - /// - public string ProjectId { get; protected set; } - - /// - /// The Master API key, required for getting a collection schema - /// or deleting the entire event collection. - /// - public string MasterKey { get; protected set; } - - /// - /// The Write API key, required for inserting events. - /// - public string WriteKey { get; protected set; } - - /// - /// The Read API key, used with query requests. - /// - public string ReadKey { get; protected set; } - - /// - /// Obtains project setting values as constructor parameters - /// - /// - /// Keen project id, required - /// Master API key, required for certain operations, such as - /// getting schema or deleting collections - /// Write API key, required for inserting events - /// Read API key, required for performing queries - /// Base Keen.IO service URL - public ProjectSettingsProvider(string projectId, string masterKey = "", string writeKey = "", string readKey = "", string keenUrl = null) - { - KeenUrl = keenUrl ?? KeenConstants.ServerAddress + "/" + KeenConstants.ApiVersion + "/"; - ProjectId = projectId; - MasterKey = masterKey; - WriteKey = writeKey; - ReadKey = readKey; - } - - protected ProjectSettingsProvider() - { - } - - public override string ToString() - { - return string.Format("ProjectSettingsProviderEnv:{{\nKeenUrl:{0}; \nProjectId:{1}; \nMasterKey:{2}; \nWriteKey:{3}; \nReadKey:{4};\n}}", - KeenUrl, ProjectId, MasterKey, WriteKey, ReadKey); - } - } -} diff --git a/Keen/Properties/AssemblyInfo.cs b/Keen/Properties/AssemblyInfo.cs deleted file mode 100644 index 66bd3a7..0000000 --- a/Keen/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Reflection; -using System.Resources; -using System.Runtime.CompilerServices; - - -// Friendly name and description for this assembly. -[assembly: AssemblyTitle("Keen")] -[assembly: AssemblyDescription("Keen IO PCL")] - -[assembly: NeutralResourcesLanguage("en")] - -[assembly: InternalsVisibleTo("Keen.Net.Test")] - -// See also: SharedAssemblyInfo.cs and SharedVersionInfo.cs diff --git a/Keen/Query/FunnelResult.cs b/Keen/Query/FunnelResult.cs deleted file mode 100644 index 148799a..0000000 --- a/Keen/Query/FunnelResult.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Newtonsoft.Json; -using System.Collections.Generic; - - -namespace Keen.Core.Query -{ - public class FunnelResult - { - [JsonProperty(PropertyName = "actors", NullValueHandling = NullValueHandling.Ignore)] - public IEnumerable> Actors { get; set; } - - [JsonProperty(PropertyName = "steps", NullValueHandling = NullValueHandling.Ignore)] - public IEnumerable Steps { get; set; } - - [JsonProperty(PropertyName = "result", NullValueHandling = NullValueHandling.Ignore)] - public IEnumerable Result{ get; set; } - } -} diff --git a/Keen/Query/FunnelResultStep.cs b/Keen/Query/FunnelResultStep.cs deleted file mode 100644 index 4126a19..0000000 --- a/Keen/Query/FunnelResultStep.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Newtonsoft.Json; -using System.Collections.Generic; - - -namespace Keen.Core.Query -{ - public class FunnelResultStep - { - [JsonProperty(PropertyName = "with_actors")] - public bool WithActors { get; set; } - - [JsonProperty(PropertyName = "actor_property")] - public string ActorProperty { get; set; } - - [JsonProperty(PropertyName = "filters")] - public IEnumerable Filters { get; set; } - - [JsonProperty(PropertyName = "timeframe")] - [JsonConverter(typeof(TimeframeConverter))] - public IQueryTimeframe Timeframe { get; set; } - - [JsonProperty(PropertyName = "timezone")] - public string TimeZone { get; set; } - - [JsonProperty(PropertyName = "event_collection")] - public string EventCollection { get; set; } - - [JsonProperty(PropertyName = "optional")] - public bool Optional { get; set; } - - [JsonProperty(PropertyName = "inverted")] - public bool Inverted { get; set; } - } -} diff --git a/Keen/Query/FunnelStep.cs b/Keen/Query/FunnelStep.cs deleted file mode 100644 index 54f464d..0000000 --- a/Keen/Query/FunnelStep.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Newtonsoft.Json; -using System.Collections.Generic; - - -namespace Keen.Core.Query -{ - /// - /// Represents one step in a funnel query. See the Keen.IO API for details on how to perform a funnel query. - /// - public class FunnelStep - { - /// - /// The name of the event that defines the step. - /// - [JsonProperty(PropertyName = "event_collection")] - public string EventCollection { get; set; } - - /// - /// The name of the property that can be used as a unique identifier for a user (or any type of actor). - /// - [JsonProperty(PropertyName = "actor_property")] - public string ActorProperty { get; set; } - - /// - /// Filters are used to narrow the scope of events used in this step of the funnel. - /// - [JsonProperty(PropertyName = "filters", NullValueHandling = NullValueHandling.Ignore)] - public IEnumerable Filters { get; set; } - - /// - /// If set to true, events matching this step will be excluded from the funnel. - /// May not be applied to an initial step. - /// - [JsonProperty(PropertyName = "inverted", NullValueHandling = NullValueHandling.Ignore)] - public bool Inverted{ get; set; } - - /// - /// If set to true, filtering applied to this step won't apply to any steps after it. - /// May not be applied to an initial step. - /// - [JsonProperty(PropertyName = "optional", NullValueHandling = NullValueHandling.Ignore)] - public bool Optional { get; set; } - - /// - /// Window of time to use for the analysis. If not set, the timeframe from the funnel will be inherited, if available. - /// - [JsonProperty(PropertyName = "timeframe", NullValueHandling = NullValueHandling.Ignore)] - public IQueryTimeframe Timeframe { get; set; } - - /// - /// Offset from UTC in seconds. If not set, the timezone from the funnel will be inherited, if available. - /// - [JsonProperty(PropertyName = "timezone", NullValueHandling = NullValueHandling.Ignore)] - public string TimeZone { get; set; } - - /// - /// If set to true, a list of unique actor_properties will be returned for each step as the 'actors' - /// attribute alongside the 'results' attribute. - /// - [JsonProperty(PropertyName = "with_actors", NullValueHandling = NullValueHandling.Ignore)] - public bool WithActors { get; set; } - } -} diff --git a/Keen/Query/IQueries.cs b/Keen/Query/IQueries.cs deleted file mode 100644 index 184c763..0000000 --- a/Keen/Query/IQueries.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Newtonsoft.Json.Linq; -using System.Collections.Generic; -using System.Threading.Tasks; - - -namespace Keen.Core.Query -{ - public interface IQueries - { - /// - /// Returns a list of available queries and links to them. - /// - Task>> AvailableQueries(); - - Task Metric(string queryName, Dictionary parms); - - Task Metric(QueryType queryType, string collection, string targetProperty, IQueryTimeframe timeframe = null, IEnumerable filters = null, string timezone = ""); - Task>> Metric(QueryType queryType, string collection, string targetProperty, string groupBy, IQueryTimeframe timeframe = null, IEnumerable filters = null, string timezone = ""); - Task>> Metric(QueryType queryType, string collection, string targetProperty, IQueryTimeframe timeframe, QueryInterval interval, IEnumerable filters = null, string timezone = ""); - Task>>>> Metric(QueryType queryType, string collection, string targetProperty, string groupBy, IQueryTimeframe timeframe, QueryInterval interval, IEnumerable filters = null, string timezone = ""); - - Task> MultiAnalysis(string collection, IEnumerable analysisParams, IQueryTimeframe timeframe = null, IEnumerable filters = null, string timezone = ""); - Task>>> MultiAnalysis(string collection, IEnumerable analysisParams, IQueryTimeframe timeframe = null, IEnumerable filters = null, string groupby = "", string timezone = ""); - Task>>> MultiAnalysis(string collection, IEnumerable analysisParams, IQueryTimeframe timeframe = null, QueryInterval interval = null, IEnumerable filters = null, string timezone = ""); - Task>>>>> MultiAnalysis(string collection, IEnumerable analysisParams, IQueryTimeframe timeframe = null, QueryInterval interval = null, IEnumerable filters = null, string groupby = "", string timezone = ""); - - Task> Extract(string collection, IQueryTimeframe timeframe = null, IEnumerable filters = null, int latest = 0, string email = ""); - - Task Funnel(IEnumerable steps, IQueryTimeframe timeframe = null, string timeZone = "" ); - - } -} diff --git a/Keen/Query/IQueryTimeframe.cs b/Keen/Query/IQueryTimeframe.cs deleted file mode 100644 index 059e244..0000000 --- a/Keen/Query/IQueryTimeframe.cs +++ /dev/null @@ -1,7 +0,0 @@ - -namespace Keen.Core.Query -{ - public interface IQueryTimeframe - { - } -} diff --git a/Keen/Query/MultiAnalysisParam.cs b/Keen/Query/MultiAnalysisParam.cs deleted file mode 100644 index 0542faf..0000000 --- a/Keen/Query/MultiAnalysisParam.cs +++ /dev/null @@ -1,41 +0,0 @@ - -namespace Keen.Core.Query -{ - public sealed class MultiAnalysisParam - { - public sealed class Metric - { - private readonly string _value; - public readonly string TargetProperty; - internal Metric(string value) { _value = value; } - internal Metric(string value, string targetProperty) { _value = value; TargetProperty = targetProperty; } - public override string ToString() { return _value; } - public static implicit operator string(Metric value) { return value.ToString(); } - - public static Metric Count() { return new Metric("count"); } - public static Metric CountUnique(string targetProperty) { return new Metric("count_unique", targetProperty); } - public static Metric Sum(string targetProperty) { return new Metric("sum", targetProperty); } - public static Metric Average(string targetProperty) { return new Metric("average", targetProperty); } - public static Metric Minimum(string targetProperty) { return new Metric("minimum", targetProperty); } - public static Metric Maximum(string targetProperty) { return new Metric("maximum", targetProperty); } - public static Metric SelectUnique(string targetProperty) { return new Metric("select_unique", targetProperty); } - } - - public string Label { get; private set; } - public string Analysis { get; private set; } - public string TargetProperty { get; private set; } - - /// - /// MultiAnalysisParam defines one kind of analysis to run in a MultiAnalysis request. - /// - /// A user defined string that acts as a name for the analysis. - /// This will be returned in the results so the various analyses are easily identifiable. - /// The metric type. - public MultiAnalysisParam(string label, Metric analysis) - { - Label = label; - Analysis = analysis; - TargetProperty = analysis.TargetProperty; - } - } -} diff --git a/Keen/Query/Queries.cs b/Keen/Query/Queries.cs deleted file mode 100644 index 18e0d3a..0000000 --- a/Keen/Query/Queries.cs +++ /dev/null @@ -1,443 +0,0 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - - -namespace Keen.Core.Query -{ - /// - /// Queries implements the IQueries interface which represents the Keen.IO Query API methods. - /// - internal class Queries : IQueries - { - private readonly IKeenHttpClient _keenHttpClient; - private readonly string _queryRelativeUrl; - private readonly string _key; - - - internal Queries(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); - _queryRelativeUrl = KeenHttpClient.GetRelativeUrl(prjSettings.ProjectId, - KeenConstants.QueriesResource); - - // TODO : The Python SDK has changed to not automatically falling back, but rather - // throwing so that client devs learn to use the most appropriate key. So here we - // really could or should just demand the ReadKey. - _key = string.IsNullOrWhiteSpace(prjSettings.MasterKey) ? - prjSettings.ReadKey : prjSettings.MasterKey; - } - - private async Task KeenWebApiRequest(string operation = "", - Dictionary parms = null) - { - if (string.IsNullOrWhiteSpace(_key)) - { - throw new KeenException("An API ReadKey or MasterKey is required."); - } - - var parmVals = (parms == null) ? - "" : string.Join("&", from p in parms.Keys - where !string.IsNullOrEmpty(parms[p]) - select string.Format("{0}={1}", - p, - Uri.EscapeDataString(parms[p]))); - - var url = string.Format("{0}{1}{2}", - _queryRelativeUrl, - string.IsNullOrWhiteSpace(operation) ? "" : "/" + operation, - string.IsNullOrWhiteSpace(parmVals) ? "" : "?" + parmVals); - - var responseMsg = await _keenHttpClient.GetAsync(url, _key).ConfigureAwait(false); - - var responseString = await responseMsg - .Content - .ReadAsStringAsync() - .ConfigureAwait(false); - - var response = JObject.Parse(responseString); - - // error checking, throw an exception with information from the json - // response if available, then check the HTTP response. - KeenUtil.CheckApiErrorCode(response); - - if (!responseMsg.IsSuccessStatusCode) - { - throw new KeenException("Request failed with status: " + - responseMsg.StatusCode); - } - - return response; - } - - public async Task>> AvailableQueries() - { - var reply = await KeenWebApiRequest().ConfigureAwait(false); - return from j in reply.Children() - let p = j as JProperty - where p != null - select new KeyValuePair(p.Name, (string)p.Value); - } - - #region metric - - public async Task Metric(string queryName, Dictionary parms) - { - if (string.IsNullOrEmpty(queryName)) - throw new ArgumentNullException("queryName"); - if (null == parms) - throw new ArgumentNullException("parms"); - - return await KeenWebApiRequest(queryName, parms).ConfigureAwait(false); - } - - public async Task Metric(QueryType queryType, string collection, string targetProperty, IQueryTimeframe timeframe = null, IEnumerable filters = null, string timezone = "") - { - if (queryType == null) - throw new ArgumentNullException("queryType"); - if (string.IsNullOrWhiteSpace(collection)) - throw new ArgumentNullException("collection"); - if (string.IsNullOrWhiteSpace(targetProperty) && (queryType != QueryType.Count())) - throw new ArgumentNullException("targetProperty"); - - var parms = new Dictionary(); - parms.Add(KeenConstants.QueryParmEventCollection, collection); - parms.Add(KeenConstants.QueryParmTargetProperty, targetProperty); - parms.Add(KeenConstants.QueryParmTimeframe, timeframe.ToSafeString()); - parms.Add(KeenConstants.QueryParmTimezone, timezone); - parms.Add(KeenConstants.QueryParmFilters, filters == null ? "" : JArray.FromObject(filters).ToString()); - - var reply = await KeenWebApiRequest(queryType.ToString(), parms).ConfigureAwait(false); - - string result; - if (queryType == QueryType.SelectUnique()) - // This is to support SelectUnique which is the only query type with a list-type result. - result = string.Join(",", (reply.Value("result").Values())); - else - result = reply.Value("result"); - return result; - } - - public async Task>> Metric(QueryType queryType, string collection, string targetProperty, string groupby, IQueryTimeframe timeframe = null, IEnumerable filters = null, string timezone = "") - { - if (queryType == null) - throw new ArgumentNullException("queryType"); - if (string.IsNullOrWhiteSpace(collection)) - throw new ArgumentNullException("collection"); - if (string.IsNullOrWhiteSpace(targetProperty) && (queryType != QueryType.Count())) - throw new ArgumentNullException("targetProperty"); - if (string.IsNullOrWhiteSpace(groupby)) - throw new ArgumentNullException("groupby", "groupby field name must be specified for a groupby query"); - - var parms = new Dictionary(); - parms.Add(KeenConstants.QueryParmEventCollection, collection); - parms.Add(KeenConstants.QueryParmTargetProperty, targetProperty); - parms.Add(KeenConstants.QueryParmGroupBy, groupby); - parms.Add(KeenConstants.QueryParmTimeframe, timeframe.ToSafeString()); - parms.Add(KeenConstants.QueryParmTimezone, timezone); - parms.Add(KeenConstants.QueryParmFilters, filters == null ? "" : JArray.FromObject(filters).ToString()); - - var reply = await KeenWebApiRequest(queryType.ToString(), parms).ConfigureAwait(false); - - IEnumerable> result; - if (queryType == QueryType.SelectUnique()) - { - // This is to support SelectUnique which is the only query type with a list-type result. - result = from r in reply.Value("result") - let c = string.Join(",", r.Value("result").Values()) - let g = r.Value(groupby) - select new QueryGroupValue(c, g); - } - else - { - result = from r in reply.Value("result") - let c = r.Value("result") - let g = r.Value(groupby) - select new QueryGroupValue(c, g); - } - return result; - } - - public async Task>> Metric(QueryType queryType, string collection, string targetProperty, IQueryTimeframe timeframe, QueryInterval interval, IEnumerable filters = null, string timezone = "") - { - if (queryType == null) - throw new ArgumentNullException("queryType"); - if (string.IsNullOrWhiteSpace(collection)) - throw new ArgumentNullException("collection"); - if (string.IsNullOrWhiteSpace(targetProperty) && (queryType != QueryType.Count())) - throw new ArgumentNullException("targetProperty"); - if (null == timeframe) - throw new ArgumentException("timeframe", "Timeframe must be specified for a series query."); - if (null == interval) - throw new ArgumentNullException("interval", "interval must be specified for a series query"); - - var parms = new Dictionary(); - parms.Add(KeenConstants.QueryParmEventCollection, collection); - parms.Add(KeenConstants.QueryParmTargetProperty, targetProperty); - parms.Add(KeenConstants.QueryParmTimeframe, timeframe.ToSafeString()); - parms.Add(KeenConstants.QueryParmInterval, interval.ToSafeString()); - parms.Add(KeenConstants.QueryParmTimezone, timezone); - parms.Add(KeenConstants.QueryParmFilters, filters == null ? "" : JArray.FromObject(filters).ToString()); - - var reply = await KeenWebApiRequest(queryType.ToString(), parms).ConfigureAwait(false); - - IEnumerable> result; - if (queryType == QueryType.SelectUnique()) - { - // This is to support SelectUnique which is the only query type with a list-type result. - result = from i in reply.Value("result") - let v = string.Join(",", i.Value("value").Values()) - let t = i.Value("timeframe") - select new QueryIntervalValue(v, t.Value("start"), t.Value("end")); - } - else - { - result = from i in reply.Value("result") - let v = i.Value("value") - let t = i.Value("timeframe") - select new QueryIntervalValue(v, t.Value("start"), t.Value("end")); - } - - return result; - } - - public async Task>>>> Metric(QueryType queryType, string collection, string targetProperty, string groupby, IQueryTimeframe timeframe, QueryInterval interval, IEnumerable filters = null, string timezone = "") - { - if (queryType == null) - throw new ArgumentNullException("queryType"); - if (string.IsNullOrWhiteSpace(collection)) - throw new ArgumentNullException("collection"); - if (string.IsNullOrWhiteSpace(targetProperty) && (queryType != QueryType.Count())) - throw new ArgumentNullException("targetProperty"); - if (null == timeframe) - throw new ArgumentException("timeframe", "Timeframe must be specified for a series query."); - if (null == interval) - throw new ArgumentNullException("interval", "interval must be specified for a series query"); - if (string.IsNullOrWhiteSpace(groupby)) - throw new ArgumentNullException("groupby", "groupby field name must be specified for a goupby query"); - - var parms = new Dictionary(); - parms.Add(KeenConstants.QueryParmEventCollection, collection); - parms.Add(KeenConstants.QueryParmTargetProperty, targetProperty); - parms.Add(KeenConstants.QueryParmGroupBy, groupby); - parms.Add(KeenConstants.QueryParmTimeframe, timeframe.ToSafeString()); - parms.Add(KeenConstants.QueryParmInterval, interval.ToSafeString()); - parms.Add(KeenConstants.QueryParmTimezone, timezone); - parms.Add(KeenConstants.QueryParmFilters, filters == null ? "" : JArray.FromObject(filters).ToString()); - - var reply = await KeenWebApiRequest(queryType.ToString(), parms).ConfigureAwait(false); - - IEnumerable>>> result; - if (queryType == QueryType.SelectUnique()) - { - // This is to support SelectUnique which is the only query type with a list-type result. - result = from i in reply.Value("result") - let v = (from r in i.Value("value") - let c = string.Join(",", r.Value("result").Values()) - let g = r.Value(groupby) - select new QueryGroupValue(c, g)) - let t = i.Value("timeframe") - select new QueryIntervalValue>>(v, t.Value("start"), t.Value("end")); - } - else - { - result = from i in reply.Value("result") - let v = (from r in i.Value("value") - let c = r.Value("result") - let g = r.Value(groupby) - select new QueryGroupValue(c, g)) - let t = i.Value("timeframe") - select new QueryIntervalValue>>(v, t.Value("start"), t.Value("end")); - } - return result; - } - - #endregion metric - - public async Task> Extract(string collection, IQueryTimeframe timeframe = null, IEnumerable filters = null, int latest = 0, string email = "") - { - var parms = new Dictionary(); - parms.Add(KeenConstants.QueryParmEventCollection, collection); - parms.Add(KeenConstants.QueryParmTimeframe, timeframe.ToSafeString()); - parms.Add(KeenConstants.QueryParmFilters, filters == null ? "" : JArray.FromObject(filters).ToString()); - parms.Add(KeenConstants.QueryParmEmail, email); - parms.Add(KeenConstants.QueryParmLatest, latest > 0 ? latest.ToString() : ""); - - var reply = await KeenWebApiRequest(KeenConstants.QueryExtraction, parms).ConfigureAwait(false); - - return from i in reply.Value("result") select (dynamic)i; - } - - public async Task Funnel(IEnumerable steps, - IQueryTimeframe timeframe = null, string timezone = "") - { - var stepsJson = JArray.FromObject(steps).ToString(Formatting.None); - - var parms = new Dictionary(); - - if (null != timeframe) - { - parms.Add(KeenConstants.QueryParmTimeframe, timeframe.ToString()); - } - - parms.Add(KeenConstants.QueryParmTimezone, timezone); - parms.Add(KeenConstants.QueryParmSteps, stepsJson); - - var reply = await KeenWebApiRequest(KeenConstants.QueryFunnel, parms).ConfigureAwait(false); - var o = reply.ToObject(); - return o; - } - - string SerializeMultiAnalysisQueryParameter(IEnumerable analysisParams) - { - var jObs = analysisParams.Select(x => - new JProperty(x.Label, JObject.FromObject( - string.IsNullOrEmpty(x.TargetProperty) ? - (object)new { analysis_type = x.Analysis } : - new { analysis_type = x.Analysis, target_property = x.TargetProperty }))); - - return JsonConvert.SerializeObject( - new JObject(jObs), - Formatting.None, - new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); - } - - public async Task> MultiAnalysis(string collection, IEnumerable analysisParams, IQueryTimeframe timeframe = null, IEnumerable filters = null, string timezone = "") - { - var parms = new Dictionary(); - parms.Add(KeenConstants.QueryParmEventCollection, collection); - parms.Add(KeenConstants.QueryParmTimeframe, timeframe.ToSafeString()); - parms.Add(KeenConstants.QueryParmTimezone, timezone); - parms.Add(KeenConstants.QueryParmFilters, filters == null ? "" : JArray.FromObject(filters).ToString()); - parms.Add(KeenConstants.QueryParmAnalyses, SerializeMultiAnalysisQueryParameter(analysisParams)); - - var reply = await KeenWebApiRequest(KeenConstants.QueryMultiAnalysis, parms).ConfigureAwait(false); - - var result = new Dictionary(); - foreach (JProperty i in reply.Value("result").Children()) - result.Add(i.Name, (string)i.Value); - - return result; - } - - public async Task>>> MultiAnalysis(string collection, IEnumerable analysisParams, IQueryTimeframe timeframe = null, IEnumerable filters = null, string groupby = "", string timezone = "") - { - var parms = new Dictionary(); - parms.Add(KeenConstants.QueryParmEventCollection, collection); - parms.Add(KeenConstants.QueryParmTimeframe, timeframe.ToSafeString()); - parms.Add(KeenConstants.QueryParmTimezone, timezone); - parms.Add(KeenConstants.QueryParmFilters, filters == null ? "" : JArray.FromObject(filters).ToString()); - parms.Add(KeenConstants.QueryParmGroupBy, groupby); - parms.Add(KeenConstants.QueryParmAnalyses, SerializeMultiAnalysisQueryParameter(analysisParams)); - - var reply = await KeenWebApiRequest(KeenConstants.QueryMultiAnalysis, parms).ConfigureAwait(false); - - var result = new List>>(); - foreach (JObject i in reply.Value("result")) - { - var d = new Dictionary(); - string grpVal = ""; - foreach (JProperty p in i.Values()) - { - if (p.Name == groupby) - grpVal = (string)p.Value; - else - d.Add(p.Name, (string)p.Value); - } - var qg = new QueryGroupValue>(d, grpVal); - result.Add(qg); - } - - return result; - } - - public async Task>>> MultiAnalysis(string collection, IEnumerable analysisParams, IQueryTimeframe timeframe = null, QueryInterval interval = null, IEnumerable filters = null, string timezone = "") - { - var parms = new Dictionary(); - parms.Add(KeenConstants.QueryParmEventCollection, collection); - parms.Add(KeenConstants.QueryParmTimeframe, timeframe.ToSafeString()); - parms.Add(KeenConstants.QueryParmInterval, interval.ToSafeString()); - parms.Add(KeenConstants.QueryParmTimezone, timezone); - parms.Add(KeenConstants.QueryParmFilters, filters == null ? "" : JArray.FromObject(filters).ToString()); - parms.Add(KeenConstants.QueryParmAnalyses, SerializeMultiAnalysisQueryParameter(analysisParams)); - - var reply = await KeenWebApiRequest(KeenConstants.QueryMultiAnalysis, parms).ConfigureAwait(false); - - var result = new List>>(); - foreach (JObject i in reply.Value("result")) - { - var d = new Dictionary(); - foreach (JProperty p in i.Value("value").Values()) - d.Add(p.Name, (string)p.Value); - - var t = i.Value("timeframe"); - var qv = new QueryIntervalValue>(d, t.Value("start"), t.Value("end")); - result.Add(qv); - } - - return result; - } - - public async Task>>>>> MultiAnalysis(string collection, IEnumerable analysisParams, IQueryTimeframe timeframe = null, QueryInterval interval = null, IEnumerable filters = null, string groupby = "", string timezone = "") - { - var parms = new Dictionary(); - parms.Add(KeenConstants.QueryParmEventCollection, collection); - parms.Add(KeenConstants.QueryParmTimeframe, timeframe.ToSafeString()); - parms.Add(KeenConstants.QueryParmInterval, interval.ToSafeString()); - parms.Add(KeenConstants.QueryParmTimezone, timezone); - parms.Add(KeenConstants.QueryParmGroupBy, groupby); - parms.Add(KeenConstants.QueryParmFilters, filters == null ? "" : JArray.FromObject(filters).ToString()); - parms.Add(KeenConstants.QueryParmAnalyses, SerializeMultiAnalysisQueryParameter(analysisParams)); - - var reply = await KeenWebApiRequest(KeenConstants.QueryMultiAnalysis, parms).ConfigureAwait(false); - - var result = new List>>>>(); - foreach (JObject i in reply.Value("result")) - { - var qgl = new List>>(); - foreach (JObject o in i.Value("value")) - { - var d = new Dictionary(); - string grpVal = ""; - foreach (JProperty p in o.Values()) - { - if (p.Name == groupby) - grpVal = (string)p.Value; - else - d.Add(p.Name, (string)p.Value); - } - qgl.Add(new QueryGroupValue>(d, grpVal)); - } - - var t = i.Value("timeframe"); - var qv = new QueryIntervalValue>>>(qgl, t.Value("start"), t.Value("end")); - result.Add(qv); - } - return result; - } - } -} diff --git a/Keen/Query/QueryAbsoluteTimeframe.cs b/Keen/Query/QueryAbsoluteTimeframe.cs deleted file mode 100644 index 44f8759..0000000 --- a/Keen/Query/QueryAbsoluteTimeframe.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; - - -namespace Keen.Core.Query -{ - /// - /// A pair of dates representing a time interval. - /// - public sealed class QueryAbsoluteTimeframe : IQueryTimeframe - { - [JsonProperty(PropertyName = "start")] - public DateTime Start { get; private set; } - - [JsonProperty(PropertyName = "end")] - public DateTime End { get; private set; } - - public override string ToString() - { - return JObject.FromObject(this).ToString(Formatting.None); - } - - public QueryAbsoluteTimeframe(DateTime start, DateTime end) - { - if (start >= end) - throw new ArgumentException("Start date must be before stop date."); - - Start = start; - End = end; - } - } -} diff --git a/Keen/Query/QueryDefinition.cs b/Keen/Query/QueryDefinition.cs deleted file mode 100644 index 49edecb..0000000 --- a/Keen/Query/QueryDefinition.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Collections.Generic; - - -namespace Keen.Core.Query -{ - /// - /// Holds information describing the query that is cached within a cached dataset. - /// - public class QueryDefinition - { - /// - /// Unique id of the project to analyze. - /// - public string ProjectId { get; set; } - - /// - /// The type of analysis for this query (e.g. count, count_unique, sum etc.) - /// - public string AnalysisType { get; set; } - - /// - /// Specifies the name of the event collection to analyze. - /// - public string EventCollection { get; set; } - - /// - /// Refines the scope of events to be included in the analysis based on event property - /// values. - /// - public IEnumerable Filters { get; set; } - - /// - /// Limits analysis to a specific period of time when the events occurred. - /// - public string Timeframe { get; set; } - - /// - /// Assigns a timezone offset to relative timeframes. - /// - public string Timezone { get; set; } - - /// - /// Specifies the size of time interval by which to group results. Using this parameter - /// changes the response format. - /// - public string Interval { get; set; } - - /// - /// Specifies the names of properties by which to group results. Using this parameter - /// changes the response format. - /// - public IEnumerable GroupBy { get; set; } - } -} diff --git a/Keen/Query/QueryFilter.cs b/Keen/Query/QueryFilter.cs deleted file mode 100644 index 69ba3a1..0000000 --- a/Keen/Query/QueryFilter.cs +++ /dev/null @@ -1,163 +0,0 @@ -using Newtonsoft.Json; -using System; - - -namespace Keen.Core.Query -{ - /// - /// Represents a filter that can be applied to a query. - /// Because not all filter operators make sense for the different property data types, only certain operators are valid for each data type. - /// - public sealed class QueryFilter - { - public class OperatorConverter : JsonConverter - { - public override bool CanConvert(Type objectType) - { - return (objectType == typeof(FilterOperator)); - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - return new FilterOperator(reader.Value.ToString()); - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - writer.WriteValue(value.ToString()); - } - } - - [JsonConverter(typeof(OperatorConverter))] - public sealed class FilterOperator - { - private readonly string _value; - internal FilterOperator(string value) { _value = value; } - public override string ToString() { return _value; } - public static implicit operator string(FilterOperator value) { return value.ToString(); } - /// - /// Equal to. - /// Use with string, number, boolean - /// - public static FilterOperator Equals() { return new FilterOperator("eq"); } - - /// - /// Not equal to. - /// Use with string, number - /// - public static FilterOperator NotEqual() { return new FilterOperator("ne"); } - - /// - /// Less than. - /// Use with string, number - /// - public static FilterOperator LessThan() { return new FilterOperator("lt"); } - - /// - /// Less than or equal to. - /// Use with number - /// - public static FilterOperator LessThanOrEqual() { return new FilterOperator("lte"); } - - /// - /// Greater than. - /// Use with string, number - /// - public static FilterOperator GreaterThan() { return new FilterOperator("gt"); } - - /// - /// Greater than or equal to. - /// Use with number - /// - public static FilterOperator GreaterThanOrEqual() { return new FilterOperator("gte"); } - - /// - /// Whether a specific property exists on an event record. - /// The Value property must be set to "true" or "false" - /// Use with string, number, boolean - /// - public static FilterOperator Exists() { return new FilterOperator("exists"); } - - /// - /// Whether the property value is in a give set of values. - /// The Value property must be a JSON array of values, e.g.: "[1,2,4,5]" - /// Use with string, number, boolean - /// - public static FilterOperator In() { return new FilterOperator("in"); } - - /// - /// Whether the property value contains the give set of characters. - /// Use with strings - /// - public static FilterOperator Contains() { return new FilterOperator("contains"); } - - /// - /// Filter on events that do not contain the specified property value. - /// Use with strings - /// - public static FilterOperator NotContains() { return new FilterOperator("not_contains"); } - - /// - /// Used to select events within a certain radius of the provided geo coordinate. - /// Use with geo analysis - /// - public static FilterOperator Within() { return new FilterOperator("within"); } - } - - - public class GeoValue - { - [JsonProperty(PropertyName = "coordinates")] - public double[] Coordinates { get; private set; } - - [JsonProperty(PropertyName = "max_distance_miles")] - public double MaxDistanceMiles { get; private set; } - - public GeoValue(double longitude, double latitude, double maxDistanceMiles) - { - Coordinates = new [] { longitude, latitude }; - MaxDistanceMiles = maxDistanceMiles; - } - } - - /// - /// The name of the property on which to filter - /// - [JsonProperty(PropertyName = "property_name")] - public string PropertyName { get; set; } - - /// - /// The filter operator to use - /// - [JsonProperty(PropertyName = "operator")] - public FilterOperator Operator { get; set; } - - /// - /// The value to compare to the property specified in PropertyName - /// - [JsonProperty(PropertyName = "property_value")] - public object Value { get; set; } - - public QueryFilter() - { - - } - - public QueryFilter(string property, FilterOperator op, object value) - { - if (string.IsNullOrWhiteSpace(property)) - { - throw new ArgumentNullException(nameof(property), "Property name is required."); - } - - if (null == op) - { - throw new ArgumentNullException(nameof(op), "Filter operator is required."); - } - - PropertyName = property; - Operator = op; - Value = value; - } - } -} diff --git a/Keen/Query/QueryGroupValue.cs b/Keen/Query/QueryGroupValue.cs deleted file mode 100644 index 6c5d906..0000000 --- a/Keen/Query/QueryGroupValue.cs +++ /dev/null @@ -1,26 +0,0 @@ - -namespace Keen.Core.Query -{ - /// - /// Represents the values from a query performed with a groupby. - /// - /// - public sealed class QueryGroupValue - { - /// - /// The value for the group. Varies with the type of query performed. - /// - public T Value { get; private set; } - - /// - /// The value of the groupby field for this value. - /// - public string Group { get; private set; } - - public QueryGroupValue(T value, string group) - { - Value = value; - Group = group; - } - } -} diff --git a/Keen/Query/QueryInterval.cs b/Keen/Query/QueryInterval.cs deleted file mode 100644 index 418cd07..0000000 --- a/Keen/Query/QueryInterval.cs +++ /dev/null @@ -1,82 +0,0 @@ - -namespace Keen.Core.Query -{ - /// - /// Provides values for interval query parameter - /// - public sealed class QueryInterval - { - private readonly string _value; - private QueryInterval(string value) { _value = value; } - private QueryInterval(string value, int n) { _value = string.Format(value, n); } - public override string ToString() { return _value; } - public static implicit operator string(QueryInterval value) { return value.ToString(); } - - /// - /// breaks your timeframe into minute long chunks. - /// - public static QueryInterval Minutely() { return new QueryInterval("minutely"); } - - /// - /// breaks your timeframe into hour long chunks. - /// - public static QueryInterval Hourly() { return new QueryInterval("hourly"); } - - /// - /// breaks your timeframe into day long chunks. - /// - public static QueryInterval Daily() { return new QueryInterval("daily"); } - - /// - /// breaks your timeframe into week long chunks. - /// - public static QueryInterval Weekly() { return new QueryInterval("weekly"); } - - /// - /// breaks your timeframe into month long chunks. - /// - public static QueryInterval Monthly() { return new QueryInterval("monthly"); } - - /// - /// breaks your timeframe into year long chunks. - /// - public static QueryInterval Yearly() { return new QueryInterval("yearly"); } - - - /// - /// breaks your timeframe into chunks of the specified length - /// - /// chunk length - public static QueryInterval EveryNMinutes(int n) { return new QueryInterval("every_{0}_minutes", n); } - - /// - /// breaks your timeframe into chunks of the specified length - /// - /// chunk length - public static QueryInterval EveryNHours(int n) { return new QueryInterval("every_{0}_hours", n); } - - /// - /// breaks your timeframe into chunks of the specified length - /// - /// chunk length - public static QueryInterval EveryNDays(int n) { return new QueryInterval("every_{0}_days", n); } - - /// - /// breaks your timeframe into chunks of the specified length - /// - /// chunk length - public static QueryInterval EveryNWeeks(int n) { return new QueryInterval("every_{0}_weeks", n); } - - /// - /// breaks your timeframe into chunks of the specified length - /// - /// chunk length - public static QueryInterval EveryNMonths(int n) { return new QueryInterval("every_{0}_months", n); } - - /// - /// breaks your timeframe into chunks of the specified length - /// - /// chunk length - public static QueryInterval EveryNYears(int n) { return new QueryInterval("every_{0}_years", n); } - } -} diff --git a/Keen/Query/QueryIntervalValue.cs b/Keen/Query/QueryIntervalValue.cs deleted file mode 100644 index 035b790..0000000 --- a/Keen/Query/QueryIntervalValue.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; - - -namespace Keen.Core.Query -{ - /// - /// Represents a set of values from a query performed with an interval parameter. - /// - /// - public sealed class QueryIntervalValue - { - /// - /// The value for this interval. Varies with the type of query performed. - /// - public T Value { get; private set; } - - /// - /// Start time for this interval. - /// - public DateTime Start { get; private set; } - - /// - /// End time for this interval. - /// - public DateTime End { get; private set; } - - public QueryIntervalValue(T value, DateTime start, DateTime end) - { - Value = value; - Start = start; - End = end; - } - } -} diff --git a/Keen/Query/QueryRelativeTimeframe.cs b/Keen/Query/QueryRelativeTimeframe.cs deleted file mode 100644 index f1ea6fc..0000000 --- a/Keen/Query/QueryRelativeTimeframe.cs +++ /dev/null @@ -1,210 +0,0 @@ -using Newtonsoft.Json; - - -namespace Keen.Core.Query -{ - /// - /// Provides values for relative timeframe query parameter. - /// - [JsonConverter(typeof(TimeframeConverter))] - public sealed class QueryRelativeTimeframe : IQueryTimeframe - { - private readonly string _value; - - - private QueryRelativeTimeframe(string value) - { - _value = value; - } - - internal static QueryRelativeTimeframe Create(string value) - { - return new QueryRelativeTimeframe(value); - } - - - public override string ToString() { return _value; } - - - /// - /// Creates a timeframe starting from the beginning of the current minute until now. - /// - public static QueryRelativeTimeframe ThisMinute() { return Create("this_minute"); } - - /// - /// Creates a timeframe starting from the beginning of the current hour until now. - /// - public static QueryRelativeTimeframe ThisHour() { return Create("this_hour"); } - - /// - /// Creates a timeframe starting from the beginning of the current day until now. - /// - public static QueryRelativeTimeframe ThisDay() { return Create("this_day"); } - - /// - /// Creates a timeframe starting from the beginning of the current week until now. - /// - public static QueryRelativeTimeframe ThisWeek() { return Create("this_week"); } - - /// - /// Creates a timeframe starting from the beginning of the current month until now. - /// - public static QueryRelativeTimeframe ThisMonth() { return Create("this_month"); } - - /// - /// Creates a timeframe starting from the beginning of the current year until now. - /// - public static QueryRelativeTimeframe ThisYear() { return Create("this_year"); } - - /// - /// All of the current minute and the previous completed n-1 minutes. - /// - public static QueryRelativeTimeframe ThisNMinutes(int n) - { - return Create($"this_{n}_minutes"); - } - - /// - /// All of the current hour and the previous completed n-1 hours. - /// - public static QueryRelativeTimeframe ThisNHours(int n) - { - return Create($"this_{n}_hours"); - } - - /// - /// All of the current day and the previous completed n-1 days. - /// - public static QueryRelativeTimeframe ThisNDays(int n) - { - return Create($"this_{n}_days"); - } - - /// - /// All of the current week and the previous completed n-1 weeks. - /// - public static QueryRelativeTimeframe ThisNWeeks(int n) - { - return Create($"this_{n}_weeks"); - } - - /// - /// All the current month and previous completed n-1 months. - /// - public static QueryRelativeTimeframe ThisNMonths(int n) - { - return Create($"this_{n}_months"); - } - - /// - /// All the current year and previous completed n-1 years. - /// - public static QueryRelativeTimeframe ThisNYears(int n) - { - return Create($"this_{n}_years"); - } - - /// - /// Gives a start of n-minutes before the most recent complete minute and an end at the - /// most recent complete minute. - /// Example: If right now it is 7:15:30pm and I specify “previous_3_minutes”, the - /// timeframe would stretch from 7:12pm until 7:15pm. - /// - public static QueryRelativeTimeframe PreviousNMinutes(int n) - { - return Create($"previous_{n}_minutes"); - } - - /// - /// Gives a start of n-hours before the most recent complete hour and an end at the most - /// recent complete hour. - /// Example: If right now it is 7:15pm and I specify “previous_7_hours”, the - /// timeframe would stretch from noon until 7:00pm. - /// - public static QueryRelativeTimeframe PreviousNHours(int n) - { - return Create($"previous_{n}_hours"); - } - - /// - /// Gives a starting point of n-days before the most recent complete day and an end at the - /// most recent complete day. - /// Example: If right now is Friday at 9:00am and I specify a timeframe of - /// “previous_3_days”, the timeframe would stretch from Tuesday morning at 12:00am until - /// Thursday night at midnight. - /// - public static QueryRelativeTimeframe PreviousNDays(int n) - { - return Create($"previous_{n}_days"); - } - - /// - /// Gives a start of n-weeks before the most recent complete week and an end at the most - /// recent complete week. - /// Example: If right now is Monday and I specify a timeframe of “previous_2_weeks”, - /// the timeframe would stretch from three Sunday mornings ago at 12:00am until the most - /// recent Sunday at 12:00am. (yesterday morning) - /// - public static QueryRelativeTimeframe PreviousNWeeks(int n) - { - return Create($"previous_{n}_weeks"); - } - - /// - /// Gives a start of n-months before the most recent completed month and an end at the most - /// recent completed month. - /// Example: If right now is the 5th of the month and I specify a timeframe of - /// “previous_2_months”, the timeframe would stretch from the start of two months ago until - /// the end of last month. - /// - public static QueryRelativeTimeframe PreviousNMonths(int n) - { - return Create($"previous_{n}_months"); - } - - /// - /// Gives a start of n-years before the most recent completed year and an end at the most - /// recent completed year. - /// Example: If right now is the June 5th and I specify a timeframe of - /// “previous_2_years”, the timeframe would stretch from the start of two years ago until - /// the end of last year. - /// - public static QueryRelativeTimeframe PreviousNYears(int n) - { - return Create($"previous_{n}_years"); - } - - /// - /// convenience for “previous_1_minute” - /// - public static QueryRelativeTimeframe PreviousMinute() - { - return Create("previous_minute"); - } - - /// - /// convenience for “previous_1_hour” - /// - public static QueryRelativeTimeframe PreviousHour() { return Create("previous_hour"); } - - /// - /// convenience for “previous_1_day” - /// - public static QueryRelativeTimeframe Yesterday() { return Create("yesterday"); } - - /// - /// convenience for “previous_1_week” - /// - public static QueryRelativeTimeframe PreviousWeek() { return Create("previous_week"); } - - /// - /// convenience for “previous_1_months” - /// - public static QueryRelativeTimeframe PreviousMonth() { return Create("previous_month"); } - - /// - /// convenience for “previous_1_years” - /// - public static QueryRelativeTimeframe PreviousYear() { return Create("previous_year"); } - } -} diff --git a/Keen/Query/QueryType.cs b/Keen/Query/QueryType.cs deleted file mode 100644 index f9c1079..0000000 --- a/Keen/Query/QueryType.cs +++ /dev/null @@ -1,54 +0,0 @@ - -namespace Keen.Core.Query -{ - public sealed class QueryType - { - private readonly string _value; - private QueryType(string value) { _value = value; } - private QueryType(string value, int n) { _value = string.Format(value, n); } - public override string ToString() { return _value; } - public static implicit operator string(QueryType value) { return value.ToString(); } - - private static QueryType count = new QueryType("count"); - /// - /// Returns the number of resources in the event collection. Parameter targetProperty is ignored. - /// - public static QueryType Count() { return count; } - - private static QueryType countunique = new QueryType("count_unique"); - /// - /// Returns the number of unique resources in the event collection. - /// - public static QueryType CountUnique() { return countunique; } - - private static QueryType minimum = new QueryType("minimum"); - /// - /// Returns the minimum value for the target property in the event collection. - /// - public static QueryType Minimum() { return minimum; } - - private static QueryType maximum = new QueryType("maximum"); - /// - /// Returns the maximum value for the target property in the event collection. - /// - public static QueryType Maximum() { return maximum; } - - private static QueryType average = new QueryType("average"); - /// - /// Returns the average across all numeric values for the target property. - /// - public static QueryType Average() { return average; } - - private static QueryType sum = new QueryType("sum"); - /// - /// Returns the sum of all numeric resources in the event collection. - /// - public static QueryType Sum() { return sum; } - - private static QueryType selectunique = new QueryType("select_unique"); - /// - /// Returns a list of unique resources in the event collection. - /// - public static QueryType SelectUnique() { return selectunique; } - } -} diff --git a/Keen/Query/TimeframeConverter.cs b/Keen/Query/TimeframeConverter.cs deleted file mode 100644 index 820c60a..0000000 --- a/Keen/Query/TimeframeConverter.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; - - -namespace Keen.Core.Query -{ - /// - /// Used for converting IQueryTimeframe instances to/from JSON. - /// - internal class TimeframeConverter : JsonConverter - { - public override bool CanConvert(Type objectType) - { - return typeof(IQueryTimeframe).IsAssignableFrom(objectType); - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - // QueryAbsoluteTimeframe has fields with JsonProperty attributes, so shouldn't need - // custom converter support for writing JSON. - if (value is QueryAbsoluteTimeframe) - { - throw new ArgumentException("We don't expect TimeframeConverter to be used for " + - "absolute timeframes.", nameof(value)); - } - else if (value is QueryRelativeTimeframe) - { - writer.WriteValue(value.ToString()); - } - else - { - throw new ArgumentException("Type not supported by TimeframeConverter", - nameof(value)); - } - } - - public override object ReadJson(JsonReader reader, - Type objectType, - object existingValue, - JsonSerializer serializer) - { - var jsonToken = JToken.Load(reader); - - // If it's just a string, then it's a relative timeframe, otherwise parse as an - // absolute timeframe. - if (JTokenType.String == jsonToken.Type) - { - return QueryRelativeTimeframe.Create(jsonToken.Value()); - } - else - { - return jsonToken.ToObject(); - } - } - } -} diff --git a/Keen/app.config b/Keen/app.config deleted file mode 100644 index f2817e4..0000000 --- a/Keen/app.config +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Keen/packages.config b/Keen/packages.config deleted file mode 100644 index 052275e..0000000 --- a/Keen/packages.config +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/SharedAssemblyInfo.cs b/SharedAssemblyInfo.cs deleted file mode 100644 index 1deff95..0000000 --- a/SharedAssemblyInfo.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Reflection; - - -[assembly: AssemblyCompany("Keen IO")] -[assembly: AssemblyCopyright("Copyright © 2014 Keen IO")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyProduct("Keen IO .NET SDK")] - -// Add more configurations as neede so as to match the build env. -#if (DEBUG) -[assembly: AssemblyConfiguration("Debug")] -#else -[assembly: AssemblyConfiguration("Release")] -#endif - -[assembly: AssemblyCulture("")] \ No newline at end of file diff --git a/SharedVersionInfo.cs b/SharedVersionInfo.cs deleted file mode 100644 index f8d3a01..0000000 --- a/SharedVersionInfo.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Reflection; - - -// Version information for an assembly consists of the following four values: -// -// ... -// -// But, SemVer versioning is generally as such: -// -// ..[.Pre-Release+Metadata] -// -// Since we plan to use this version info for NuGet, we'll follow SemVer here. - -// AssemblyVersion can only contain numerical values, so no pre-release or metadata info like -// "-alpha123" can go in here. -[assembly: AssemblyVersion("0.3.17")] - -// AssemblyInformationalVersion can have more information in non-numerical format. Here is -// where we could/should put pre-release and/or metadata info if we want to release a version -// as "1.2.3-beta" or similar. -[assembly: AssemblyInformationalVersion("0.3.17")] - -// AssemblyFileVersion can and should differ in each assembly if we get into a situation where -// a given assembly needs to be rebuilt and we'd like to track that separately, but we don't -// intend to bump the SDK version nor the NuGet version. Leave it here until then. -[assembly: AssemblyFileVersion("0.3.17")] From 1e85c38d64dfab3dd317da9f7724f9bbf99e6624 Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 11 Nov 2017 03:14:04 -0500 Subject: [PATCH 3/9] Remove stale NUnit project file. We can add this back and update it if it seems useful for CI builds later. --- Keen.nunit | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 Keen.nunit diff --git a/Keen.nunit b/Keen.nunit deleted file mode 100644 index bef6382..0000000 --- a/Keen.nunit +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file From 713267bef6ae29ed8a1726e712f5f39118046e62 Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 11 Nov 2017 03:58:58 -0500 Subject: [PATCH 4/9] Bump some of the test dependency versions to use the latest and greatest. --- Keen.NetStandard.Test/Keen.NetStandard.Test.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Keen.NetStandard.Test/Keen.NetStandard.Test.csproj b/Keen.NetStandard.Test/Keen.NetStandard.Test.csproj index d94601c..6c196b5 100644 --- a/Keen.NetStandard.Test/Keen.NetStandard.Test.csproj +++ b/Keen.NetStandard.Test/Keen.NetStandard.Test.csproj @@ -6,10 +6,10 @@ - - - - + + + +