diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml new file mode 100644 index 0000000..6c0211b --- /dev/null +++ b/.github/workflows/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + - package-ecosystem: "nuget" + directory: "/" + schedule: + interval: "daily" + time: "03:00" + timezone: "UTC" + open-pull-requests-limit: 5 diff --git a/.gitignore b/.gitignore index 8a34f04..e898e06 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,7 @@ /Assets/Plugins/.signature.p7s /Assets/UnityXapiPublisher/Runtime/.vscode +reports +reports.meta +.clj-kondo +.lsp +.vscode diff --git a/Assemblies/Microsoft.Bcl.AsyncInterfaces.dll b/Assemblies/Microsoft.Bcl.AsyncInterfaces.dll index 39fd131..3a2d14a 100755 Binary files a/Assemblies/Microsoft.Bcl.AsyncInterfaces.dll and b/Assemblies/Microsoft.Bcl.AsyncInterfaces.dll differ diff --git a/Assemblies/RestSharp.dll b/Assemblies/RestSharp.dll index 2a83591..b0e421b 100755 Binary files a/Assemblies/RestSharp.dll and b/Assemblies/RestSharp.dll differ diff --git a/Assemblies/System.IO.Pipelines.dll b/Assemblies/System.IO.Pipelines.dll new file mode 100755 index 0000000..cfae953 Binary files /dev/null and b/Assemblies/System.IO.Pipelines.dll differ diff --git a/Assemblies/System.IO.Pipelines.dll.meta b/Assemblies/System.IO.Pipelines.dll.meta new file mode 100644 index 0000000..117be69 --- /dev/null +++ b/Assemblies/System.IO.Pipelines.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: f887835dab94a489a87e6d553e1f176b +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assemblies/System.Runtime.CompilerServices.Unsafe.dll b/Assemblies/System.Runtime.CompilerServices.Unsafe.dll index 51974c7..6e91f06 100755 Binary files a/Assemblies/System.Runtime.CompilerServices.Unsafe.dll and b/Assemblies/System.Runtime.CompilerServices.Unsafe.dll differ diff --git a/Assemblies/System.Text.Encodings.Web.dll b/Assemblies/System.Text.Encodings.Web.dll index 29ef2ae..de502b7 100755 Binary files a/Assemblies/System.Text.Encodings.Web.dll and b/Assemblies/System.Text.Encodings.Web.dll differ diff --git a/Assemblies/System.Text.Json.dll b/Assemblies/System.Text.Json.dll index 410e98a..b85d61e 100755 Binary files a/Assemblies/System.Text.Json.dll and b/Assemblies/System.Text.Json.dll differ diff --git a/NuGet.config b/NuGet.config index cc798bc..7761ba8 100644 --- a/NuGet.config +++ b/NuGet.config @@ -2,7 +2,7 @@ - + @@ -10,6 +10,6 @@ - + diff --git a/Runtime/LRS/Domain/Publisher.cs b/Runtime/LRS/Domain/Publisher.cs index 35b6a0b..092f884 100644 --- a/Runtime/LRS/Domain/Publisher.cs +++ b/Runtime/LRS/Domain/Publisher.cs @@ -32,7 +32,8 @@ public static void InvokeStatementSent(JsonObject statement) OnStatementSent?.Invoke(statement); } - private JsonObject formAgent() { + private JsonObject formAgent() + { bool hasEmail = PlayerPrefs.HasKey("LRSEmail"); bool hasAccount = PlayerPrefs.HasKey("LRSAccountId") && PlayerPrefs.HasKey("LRSHomepage"); if (hasEmail) @@ -78,8 +79,14 @@ private JsonObject formAgent() { private String gameId { get { return PlayerPrefs.GetString("LRSGameId"); } } private String gameDisplay { get { return PlayerPrefs.GetString("LRSGameDisplay"); } } - private bool hasCustomActivityEnv { get { return PlayerPrefs.HasKey("LRSActivityId") && - PlayerPrefs.HasKey("LRSActivityDefinition"); } } + private bool hasCustomActivityEnv + { + get + { + return PlayerPrefs.HasKey("LRSActivityId") && + PlayerPrefs.HasKey("LRSActivityDefinition"); + } + } private String customActivityId { get { return PlayerPrefs.GetString("LRSActivityId"); } } private String customActivityDefinition { get { return PlayerPrefs.GetString("LRSActivityDefinition"); } } @@ -114,13 +121,13 @@ private async Task GetLocation() } // otherwise, we go ahead and populate the cache by calling the API - return await Task.Run(async () => + var request = new RestRequest($"json/{ip}"); + location = await client.GetAsync(request); + if (location != null) { - location = await client.GetJsonAsync(string.Format("json/{0}", ip)); downloadCache.TryAdd("location", location); - - return location; - }); + } + return location; } private async Task FormBasicStatement(String verbId, @@ -152,7 +159,8 @@ private async Task FormBasicStatement(String verbId, }; // location - if (enableUserLocation) { + if (enableUserLocation) + { JsonObject loc = await this.locationTask; contextExtension["http://ip-api.com/location"] = loc; } @@ -161,7 +169,8 @@ private async Task FormBasicStatement(String verbId, String activityDefinition = gameDisplay; // if there's a custom ActivityID in the env, set activityId to that one. - if (hasCustomActivityEnv) { + if (hasCustomActivityEnv) + { activityId = customActivityId; activityDefinition = customActivityDefinition; } @@ -185,7 +194,7 @@ private async Task FormBasicStatement(String verbId, ["platform"] = gameId, ["extensions"] = contextExtension }, - ["timestamp"] = DateTime.UtcNow.ToString("o",CultureInfo.InvariantCulture) + ["timestamp"] = DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture) }; return statementFn(statement); @@ -203,14 +212,14 @@ private async Task FormBasicStatement(String verbId, String activityDescription, Func statementFn) { - Func setObjektFn = (s) => + Func setObjektFn = (s) => { s["object"]["id"] = activityID; s["object"]["definition"]["name"]["en-US"] = activityDescription; return statementFn(s); }; - JsonObject statement = await FormBasicStatement(verbId, + JsonObject statement = await FormBasicStatement(verbId, verbDisplay, user, gameId, @@ -240,7 +249,8 @@ private JsonObject FormActivity(String activityID, }; } - private void DebugStatements(string statement, RestResponse response) { + private void DebugStatements(string statement, RestResponse response) + { Debug.Log(statement); Debug.Log(response.Content); Debug.Log(response.ResponseStatus); @@ -280,7 +290,7 @@ public void SendCompletedStatement(String activityID, public async void SendStatement(String verbId, String verbDisplay) { - Func identity = (s) => + Func identity = (s) => { return s; }; @@ -300,7 +310,7 @@ public async void SendStatement(String verbId, public async void SendStatement(String verbId, String verbDisplay, - Func statementFn) + Func statementFn) { var statement = await FormBasicStatement(verbId, verbDisplay, @@ -321,7 +331,7 @@ public async void SendStatement(String verbId, String activityID, String activityDisplay) { - Func identity = (s) => + Func identity = (s) => { return s; }; @@ -345,7 +355,7 @@ public async void SendStatement(String verbId, String verbDisplay, String activityID, String activityDisplay, - Func statementFn) + Func statementFn) { var statement = await FormBasicStatement(verbId, verbDisplay, @@ -370,7 +380,7 @@ async void SendCustomStatement(String verbId, String gameDisplay, String registrationIdentifier) { - Func identity = (s) => + Func identity = (s) => { return s; }; diff --git a/Runtime/LRS/Sender.cs b/Runtime/LRS/Sender.cs index ba77092..d75773f 100644 --- a/Runtime/LRS/Sender.cs +++ b/Runtime/LRS/Sender.cs @@ -4,28 +4,33 @@ using System.Threading.Tasks; -namespace LRS { - public class Sender { +namespace LRS +{ + public class Sender + { public Sender(String LRSUrl, String LRSKey, - String LRSSecret) + String LRSSecret) { this.LRSUrl = LRSUrl; this.LRSKey = LRSKey; this.LRSSecret = LRSSecret; } - private String LRSUrl {set;get;} - private String LRSKey {set;get;} - private String LRSSecret {set;get;} + private String LRSUrl { set; get; } + private String LRSKey { set; get; } + private String LRSSecret { set; get; } - public async Task SendStatement(String statement) { - var client = new RestClient(LRSUrl) { - Authenticator = new HttpBasicAuthenticator(LRSKey,LRSSecret) + public async Task SendStatement(String statement) + { + var options = new RestClientOptions(LRSUrl) + { + Authenticator = new HttpBasicAuthenticator(LRSKey, LRSSecret) }; + var client = new RestClient(options); var request = new RestRequest("/xapi/statements", Method.Post); request.AddHeader("Accept", "application/json"); - request.AddStringBody(statement,DataFormat.Json); + request.AddStringBody(statement, DataFormat.Json); request.AddHeader("X-Experience-API-Version", "1.0.1"); return await client.ExecutePostAsync(request); } diff --git a/Tests/LRS/Domain/PublisherTest.cs b/Tests/LRS/Domain/PublisherTest.cs index 5a078f4..d96bb2f 100644 --- a/Tests/LRS/Domain/PublisherTest.cs +++ b/Tests/LRS/Domain/PublisherTest.cs @@ -39,7 +39,7 @@ public class PublisherTest string LRSAccountId = "123456"; string LRSHomepage = "https://www.yetanalytics.com"; - string LRSUsernameDisplay = "John Doe"; + string LRSUsernameDisplay = "John Doe"; string LRSGameId = "http://video.games/button-clicker"; string LRSGameDisplay = "Button Clicker"; string LRSActivityId = "http://video.games/button-clicker/level/1"; @@ -51,13 +51,13 @@ public class PublisherTest public void SetUp() { // set Account and homepage for a more anonymous and contained identity: - PlayerPrefs.SetString("LRSAccountId",LRSAccountId); - PlayerPrefs.SetString("LRSHomepage",LRSHomepage); + PlayerPrefs.SetString("LRSAccountId", LRSAccountId); + PlayerPrefs.SetString("LRSHomepage", LRSHomepage); // Or if you prefer, Email can be set the following way: //PlayerPrefs.SetString("LRSEmail","user@example.com"); - PlayerPrefs.SetString("LRSUsernameDisplay",LRSUsernameDisplay); + PlayerPrefs.SetString("LRSUsernameDisplay", LRSUsernameDisplay); // Game Identity Data // This sets the Platform of the statement under context.platform @@ -72,7 +72,7 @@ public void SetUp() // Session Identity Data - PlayerPrefs.SetString("LRSSessionIdentifier",LRSSessionIdentifier); + PlayerPrefs.SetString("LRSSessionIdentifier", LRSSessionIdentifier); // Location Data // Add this to the PlayerPrefs if you wish to get user location data @@ -83,19 +83,22 @@ public void SetUp() public void SendStartedStatementBehaves() { Hook hook = new Hook(); - Publisher publisher = new Publisher("http://example.com/LRSUrl","LRSKey","LRSSecret"); + Publisher publisher = new Publisher("http://example.com/LRSUrl", "LRSKey", "LRSSecret"); publisher.SendStartedStatement(); JsonObject statement = hook.results.First(); - Assert.AreEqual(LRSHomepage, statement["actor"]["account"]["homePage"].ToString()); - Assert.AreEqual(LRSAccountId, statement["actor"]["account"]["name"].ToString()); + Assert.AreEqual(statement["actor"]["account"]["homePage"].ToString(), LRSHomepage); + Assert.AreEqual(statement["actor"]["account"]["name"].ToString(), LRSAccountId); Assert.AreEqual("http://adlnet.gov/expapi/verbs/initialized", statement["verb"]["id"].ToString()); - Assert.AreEqual(LRSActivityId, statement["object"]["id"].ToString()); - Assert.AreEqual("", statement["object"]["definition"]["extensions"]["https://docs.unity3d.com/ScriptReference/XR.XRSettings.html"]["loadedDeviceName"].ToString()); - Assert.AreEqual("false", statement["object"]["definition"]["extensions"]["https://docs.unity3d.com/ScriptReference/XR.XRDisplaySubsystem.html"]["running"].ToString()); - Assert.AreEqual(LRSSessionIdentifier, statement["context"]["registration"].ToString()); - Assert.AreEqual(LRSGameId, statement["context"]["platform"].ToString()); - Assert.AreEqual("LinuxEditor", statement["context"]["extensions"]["https://docs.unity3d.com/ScriptReference/Application-platform.html"]["platform"].ToString()); + Assert.AreEqual(statement["object"]["id"].ToString(), + LRSActivityId); + Assert.AreEqual(statement["object"]["definition"]["extensions"]["https://docs.unity3d.com/ScriptReference/XR.XRSettings.html"]["loadedDeviceName"].ToString(), + ""); + Assert.AreEqual(statement["object"]["definition"]["extensions"]["https://docs.unity3d.com/ScriptReference/XR.XRDisplaySubsystem.html"]["running"].ToString(), + "false"); + Assert.AreEqual(statement["context"]["registration"].ToString(), LRSSessionIdentifier); + Assert.AreEqual(statement["context"]["platform"].ToString(), LRSGameId); + Assert.That(statement["context"]["extensions"]["https://docs.unity3d.com/ScriptReference/Application-platform.html"]["platform"].ToString(), Does.Match("^.+Editor$")); Assert.IsInstanceOf(statement["timestamp"].ToString()); } @@ -104,14 +107,14 @@ public void SendStatement_ModifiedVerbs() { string verbId = "http://video.games/verbs/quit"; string verbName = "Quit"; - + Hook hook = new Hook(); - Publisher publisher = new Publisher("http://example.com/LRSUrl","LRSKey","LRSSecret"); + Publisher publisher = new Publisher("http://example.com/LRSUrl", "LRSKey", "LRSSecret"); publisher.SendStatement(verbId, verbName); JsonObject statement = hook.results.First(); Assert.AreEqual(verbId, statement["verb"]["id"].ToString()); - Assert.AreEqual(verbName, statement["verb"]["display"]["en-US"].ToString()); + Assert.AreEqual(verbName, statement["verb"]["display"]["en-US"].ToString()); } [Test] @@ -121,17 +124,17 @@ public void SendStatement_ModifiedVerbsWithAdditionalChanges() string verbName = "Quit"; string publisherName = "ACME Games Corp."; - Func modifyFn = (statement) => + Func modifyFn = (statement) => { // modify the statement - statement["object"]["definition"]["extensions"]["https://video.games/publisher"] = new JsonObject{ ["name"] = publisherName }; + statement["object"]["definition"]["extensions"]["https://video.games/publisher"] = new JsonObject { ["name"] = publisherName }; // make sure to return for the callback return statement; }; - + Hook hook = new Hook(); - Publisher publisher = new Publisher("http://example.com/LRSUrl","LRSKey","LRSSecret"); + Publisher publisher = new Publisher("http://example.com/LRSUrl", "LRSKey", "LRSSecret"); publisher.SendStatement(verbId, verbName, modifyFn); JsonObject statement = hook.results.First(); @@ -147,16 +150,16 @@ public void SendStatement_ModifiedVerbsActivities() string verbName = "Quit"; string activityId = "http://video.games/pong"; string activityName = "Pong"; - + Hook hook = new Hook(); - Publisher publisher = new Publisher("http://example.com/LRSUrl","LRSKey","LRSSecret"); + Publisher publisher = new Publisher("http://example.com/LRSUrl", "LRSKey", "LRSSecret"); publisher.SendStatement(verbId, verbName, activityId, activityName); JsonObject statement = hook.results.First(); Assert.AreEqual(verbId, statement["verb"]["id"].ToString()); Assert.AreEqual(verbName, statement["verb"]["display"]["en-US"].ToString()); Assert.AreEqual(activityId, statement["object"]["id"].ToString()); - Assert.AreEqual(activityName, statement["object"]["definition"]["name"]["en-US"].ToString()); + Assert.AreEqual(activityName, statement["object"]["definition"]["name"]["en-US"].ToString()); } [Test] @@ -168,24 +171,24 @@ public void SendStatement_ModifiedVerbsActivitiesWithAdditionalChanges() string activityName = "Pong"; string publisherName = "ACME Games Corp."; - Func modifyFn = (statement) => + Func modifyFn = (statement) => { // modify the statement - statement["object"]["definition"]["extensions"]["https://video.games/publisher"] = new JsonObject{ ["name"] = publisherName }; + statement["object"]["definition"]["extensions"]["https://video.games/publisher"] = new JsonObject { ["name"] = publisherName }; // make sure to return for the callback return statement; }; - + Hook hook = new Hook(); - Publisher publisher = new Publisher("http://example.com/LRSUrl","LRSKey","LRSSecret"); + Publisher publisher = new Publisher("http://example.com/LRSUrl", "LRSKey", "LRSSecret"); publisher.SendStatement(verbId, verbName, activityId, activityName, modifyFn); JsonObject statement = hook.results.First(); Assert.AreEqual(verbId, statement["verb"]["id"].ToString()); Assert.AreEqual(verbName, statement["verb"]["display"]["en-US"].ToString()); Assert.AreEqual(activityId, statement["object"]["id"].ToString()); - Assert.AreEqual(activityName, statement["object"]["definition"]["name"]["en-US"].ToString()); + Assert.AreEqual(activityName, statement["object"]["definition"]["name"]["en-US"].ToString()); Assert.AreEqual(publisherName, statement["object"]["definition"]["extensions"]["https://video.games/publisher"]["name"].ToString()); } } \ No newline at end of file diff --git a/Tests/Tests.asmdef b/Tests/Tests.asmdef index 7cf673c..329c548 100644 --- a/Tests/Tests.asmdef +++ b/Tests/Tests.asmdef @@ -16,10 +16,11 @@ "nunit.framework.dll", "System.Text.Json.dll" ], - "autoReferenced": false, + "autoReferenced": true, "defineConstraints": [ "UNITY_INCLUDE_TESTS" ], "versionDefines": [], - "noEngineReferences": false + "noEngineReferences": false, + "testAssemblies": true } \ No newline at end of file diff --git a/nvd.sh b/nvd.sh new file mode 100755 index 0000000..63abbcc --- /dev/null +++ b/nvd.sh @@ -0,0 +1,8 @@ +docker run --rm \ + -v "$PWD:/src" -v "$PWD/reports:/reports" \ + owasp/dependency-check:latest \ + --project "unity-xapi-publisher" \ + --scan /src/packages.config \ + --format SARIF \ + --out /reports \ + --nvdApiKey "$NVD_API_KEY" \ No newline at end of file diff --git a/nvd.sh.meta b/nvd.sh.meta new file mode 100644 index 0000000..b91da8e --- /dev/null +++ b/nvd.sh.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0d0bf1d460ab44fe1b538c1c40f9d353 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/package.json b/package.json index a02bf60..974a625 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { "name": "com.yetanalytics.unity-xapi-publisher", - "version": "1.1.0", + "version": "1.1.1", "displayName": "Unity Xapi Publisher", "description": "This package gives you the tooling needed to send basic statements to an LRS", "unity": "2021.3", "unityRelease": "4f1", - "keywords": [ + "keywords": [ "XAPI", "integration", "data" diff --git a/packages.config b/packages.config index e275658..f2f65ff 100644 --- a/packages.config +++ b/packages.config @@ -1,8 +1,9 @@ - - - - - + + + + + +