diff --git a/LearnositySDK/Request/Init.cs b/LearnositySDK/Request/Init.cs old mode 100755 new mode 100644 index 87695c1..28ea379 --- a/LearnositySDK/Request/Init.cs +++ b/LearnositySDK/Request/Init.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Text; using LearnositySDK.Utils; using System.Web; +using System.Runtime.InteropServices; namespace LearnositySDK.Request { @@ -77,6 +77,11 @@ public class Init /// private string algorithm; + /// + /// Determines if telemetry is enabled; default is true + /// + private static bool telemetryEnabled = true; + /// /// Instantiate this class with all security and request data. It will be used to create a signature. /// @@ -153,6 +158,7 @@ private void Initialize(string service, JsonObject securityPacket, string secret //try //{ this.validate(this.service, ref this.securityPacket, this.secret, this.requestPacket, this.action); + this.addTelemetryData(); this.requestString = this.generateRequestString(); this.setServiceOptions(); this.securityPacket.set("signature", this.generateSignature()); @@ -180,7 +186,7 @@ private string generateRequestString() { throw new Exception("Invalid data, please check you request packet."); } - + return request; } @@ -191,7 +197,7 @@ private string generateRequestString() /// - the `action` value if passed /// /// A signature hash for the request authentication - private string generateSignature() + public string generateSignature() { List signatureList = new List(); string temp = null; @@ -256,7 +262,7 @@ public string generate() { return this.generateData(output); } - else if(this.service == "assess") + else if (this.service == "assess") { output = this.generateAssess(output); } @@ -277,7 +283,7 @@ public string generate() // do nothing break; } - + return output.toJson(); } @@ -291,7 +297,7 @@ private string generateData(JsonObject output) StringBuilder sb = new StringBuilder(); JsonObject obj = null; string str = ""; - + obj = output.getJsonObject("security"); if (!Tools.empty(obj)) { @@ -380,7 +386,7 @@ private void setServiceOptions() case "assess": // fall-through case "questions": - + this.signRequestData = false; if (this.service == "assess" && Tools.array_key_exists("questionsApiActivity", this.requestPacket)) @@ -456,18 +462,21 @@ private void setServiceOptions() this.signRequestData = false; JsonObject requestPackageUsers = this.requestPacket.getJsonObject("users"); - if (requestPackageUsers != null) { - string[] users = requestPackageUsers.getValuesArray(); - if (users != null && users.Length > 0) { + if (requestPackageUsers != null) + { + string[] users = requestPackageUsers.getValuesArray(); + if (users != null && users.Length > 0) + { hashedUsers = new JsonObject(); - for (int i = 0; i < users.Length; i++) { + for (int i = 0; i < users.Length; i++) + { string user_id = users[i]; hashedUsers.set(user_id, Tools.hash(this.algorithm, user_id + this.secret)); } this.requestPacket.set("users", hashedUsers); } } - + break; default: // do nothing @@ -518,5 +527,111 @@ public void validate(string service, ref JsonObject securityPacket, string secre throw new Exception("The `secret` argument must be a valid string"); } } + + /// + /// Adds a meta field with SDK information in it to the requestPacket + /// + public void addTelemetryData() + { + if (this.isTelemetryEnabled()) + { + JsonObject meta; + if (this.requestPacket.getJsonObject("meta") != null) + { + meta = this.requestPacket.getJsonObject("meta"); + } + else + { + meta = new JsonObject(); + } + + meta.set("sdk", this.getSdkMeta()); + this.requestPacket.set("meta", meta); + } + } + + public bool isTelemetryEnabled() + { + return telemetryEnabled; + } + + public JsonObject getSdkMeta() + { + JsonObject sdkMeta = new JsonObject(); + sdkMeta.set("version", this.getSdkVersion()); + sdkMeta.set("lang", "asp.net"); + sdkMeta.set("lang_version", this.getLanguageVersion()); + sdkMeta.set("platform", this.getPlatform()); + sdkMeta.set("platform_version", this.getPlatformVersion()); + + return sdkMeta; + } + + /// + /// Returns the version number of ASP.NET + /// + /// + public string getLanguageVersion() + { + return Environment.Version.ToString(); + } + + /// + /// Returns the name of the platform the code is running on + /// + /// + public string getPlatform() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return "darwin"; + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return "win"; + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return "linux"; + } + + return Environment.OSVersion.Platform.ToString(); + } + + /// + /// Returns the version number of the platform the code is running on + /// + /// + public string getPlatformVersion() + { + var os = Environment.OSVersion; + return string.Concat(os.Version.Major, '.', os.Version.Minor); + } + + /// + /// Extracts the version number of this SDK from the project meta data + /// + /// The version number + public string getSdkVersion() + { + return GetType().Assembly.GetName().Version.ToString(); + } + + /// + /// We use telemetry to enable better support and feature planning. + /// It will not interfere with any usage. + /// However, it is not advised to disable it. + /// + public static void disableTelemetry() + { + telemetryEnabled = false; + } + + public static void enableTelemetry() + { + telemetryEnabled = true; + } } } diff --git a/LearnositySDKIntegrationTests/LearnositySDKIntegrationTests.cs b/LearnositySDKIntegrationTests/LearnositySDKIntegrationTests.cs index a01bc3c..0b33c63 100644 --- a/LearnositySDKIntegrationTests/LearnositySDKIntegrationTests.cs +++ b/LearnositySDKIntegrationTests/LearnositySDKIntegrationTests.cs @@ -1,5 +1,6 @@ using System; using Xunit; +using System.IO; using LearnositySDK.Utils; using LearnositySDK.Request; @@ -69,5 +70,147 @@ private static string BuildDataAPIBaseUrl(string env, string region, string vers return "https://data" + regionDomain + envDomain + ".learnosity.com/" + versionPath; } + + [Fact] + public void InitGeneratesExactSameSignature() + { + string action = "get"; + + JsonObject security = this.generateSecurityObject(); + + JsonObject request = new JsonObject(); + request.set("limit", 100); + + Init.disableTelemetry(); + Init init = new Init("data", security, this.consumerSecret, request, action); + + // Assert signature is still the same + Assert.Equal( + init.generateSignature(), + "e1eae0b86148df69173cb3b824275ea73c9c93967f7d17d6957fcdd299c8a4fe" + ); + + // Assert telemetry is turned off + Assert.False(init.isTelemetryEnabled()); + Init.enableTelemetry(); + } + + [Fact] + public void InitGenerateBuildsNonEmptyRequest() + { + string action = "get"; + + JsonObject security = this.generateSecurityObject(); + + JsonObject request = new JsonObject(); + request.set("page", 1); + + Init init = new Init("data", security, this.consumerSecret, request, action); + string generatedString = init.generate(); + + // Assert generated string is not empty + Assert.NotEmpty(generatedString); + + // Assert telemetry is turned on + Assert.True(init.isTelemetryEnabled()); + } + + [Fact] + public void EnabledTelemetryAddsSdkField() + { + string action = "get"; + JsonObject security = this.generateSecurityObject(); + + JsonObject request = new JsonObject(); + request.set("page", 1); + + Init init = new Init("data", security, this.consumerSecret, request, action); + string generatedString = init.generate(); + + Assert.Contains("meta", generatedString); + Assert.Contains("sdk", generatedString); + } + + [Fact] + public void EnabledTelemetryPreservesOtherMetaProps() + { + string action = "get"; + JsonObject security = this.generateSecurityObject(); + + JsonObject metaField = new JsonObject(); + metaField.set("test_key_string", "test-string"); + metaField.set("test_key_integer", 12345); + + JsonObject request = new JsonObject(); + request.set("page", 1); + request.set("meta", metaField); + + Init init = new Init("data", security, this.consumerSecret, request, action); + string generatedString = init.generate(); + + Assert.Contains("meta", generatedString); + Assert.Contains("sdk", generatedString); + Assert.Contains("test_key_string", generatedString); + Assert.Contains("test_key_integer", generatedString); + } + + [Fact] + public void DisabledTelemetryPreservesEmptyProps() + { + string action = "get"; + JsonObject security = this.generateSecurityObject(); + + Init.disableTelemetry(); + + JsonObject request = new JsonObject(); + request.set("page", 1); + + Init init = new Init("data", security, this.consumerSecret, request, action); + string generatedString = init.generate(); + + Assert.DoesNotContain("meta", generatedString); + Assert.DoesNotContain("sdk", generatedString); + + Init.enableTelemetry(); + } + + [Fact] + public void DisabledTelemetryPreservesFilledProps() + { + string action = "get"; + JsonObject security = this.generateSecurityObject(); + + Init.disableTelemetry(); + + JsonObject metaField = new JsonObject(); + metaField.set("test_key_string", "test-string"); + metaField.set("test_key_integer", 12345); + + JsonObject request = new JsonObject(); + request.set("page", 1); + request.set("meta", metaField); + + Init init = new Init("data", security, this.consumerSecret, request, action); + string generatedString = init.generate(); + + Assert.Contains("meta", generatedString); + Assert.DoesNotContain("sdk", generatedString); + Assert.Contains("test_key_string", generatedString); + Assert.Contains("test_key_integer", generatedString); + + Init.enableTelemetry(); + } + + private JsonObject generateSecurityObject() + { + string timestamp = "20140626-0528"; + + JsonObject security = new JsonObject(); + security.set("consumer_key", this.consumerKey); + security.set("domain", this.domain); + security.set("timestamp", timestamp); + + return security; + } } }