From 1e5fbf4d7a50ff672ef1a9ab335bb4ada23a230d Mon Sep 17 00:00:00 2001 From: Jonathan Bloxsom Date: Tue, 2 Nov 2021 10:56:52 +0000 Subject: [PATCH 1/8] HEEDLS-408 tracker GetObjectiveArray endpoint --- .../TutorialContentDataService.cs | 21 ++++++++ .../Enums/TrackerEndpointAction.cs | 5 +- .../Models/Objective.cs | 21 ++++++++ .../Tracker/TrackerEndpointQueryParams.cs | 2 + .../Services/TrackerActionService.cs | 48 +++++++++++++++++++ .../Services/TrackerService.cs | 5 +- DigitalLearningSolutions.Web/Startup.cs | 1 + 7 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 DigitalLearningSolutions.Data/Models/Objective.cs create mode 100644 DigitalLearningSolutions.Data/Services/TrackerActionService.cs diff --git a/DigitalLearningSolutions.Data/DataServices/TutorialContentDataService.cs b/DigitalLearningSolutions.Data/DataServices/TutorialContentDataService.cs index 3674827ba6..0f3c8aa1fd 100644 --- a/DigitalLearningSolutions.Data/DataServices/TutorialContentDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/TutorialContentDataService.cs @@ -21,6 +21,7 @@ int tutorialId IEnumerable GetTutorialsBySectionId(int sectionId, int customisationId); IEnumerable GetTutorialIdsForCourse(int customisationId); void UpdateOrInsertCustomisationTutorialStatuses(int tutorialId, int customisationId, bool diagnosticEnabled, bool learningEnabled); + IEnumerable GetObjectivesBySectionId(int sectionId, int customisationId); } public class TutorialContentDataService : ITutorialContentDataService @@ -338,5 +339,25 @@ INSERT INTO CustomisationTutorials (CustomisationID, TutorialID, [Status], DiagS new { customisationId, tutorialId, learningEnabled, diagnosticEnabled } ); } + + public IEnumerable GetObjectivesBySectionId(int sectionId, int customisationId) + { + return connection.Query( + @"SELECT + CASE + WHEN tu.OriginalTutorialID > 0 THEN tu.OriginalTutorialID + ELSE tu.TutorialID + END AS TutorialID, + tu.CMIInteractionsIDs AS Interactions, + ct.DiagAssessOutOf AS Possible + FROM dbo.Tutorials AS tu + LEFT JOIN dbo.CustomisationTutorials AS ct + ON ct.TutorialID = tu.TutorialID + WHERE tu.SectionID = @sectionId + AND ct.CustomisationID = @customisationId + AND tu.ArchivedDate IS NULL", + new { sectionId, customisationId } + ); + } } } diff --git a/DigitalLearningSolutions.Data/Enums/TrackerEndpointAction.cs b/DigitalLearningSolutions.Data/Enums/TrackerEndpointAction.cs index 3577d05f5c..403a7e751d 100644 --- a/DigitalLearningSolutions.Data/Enums/TrackerEndpointAction.cs +++ b/DigitalLearningSolutions.Data/Enums/TrackerEndpointAction.cs @@ -1,4 +1,7 @@ namespace DigitalLearningSolutions.Data.Enums { - public enum TrackerEndpointAction { } + public enum TrackerEndpointAction + { + GetObjectiveArray + } } diff --git a/DigitalLearningSolutions.Data/Models/Objective.cs b/DigitalLearningSolutions.Data/Models/Objective.cs new file mode 100644 index 0000000000..15e175af5a --- /dev/null +++ b/DigitalLearningSolutions.Data/Models/Objective.cs @@ -0,0 +1,21 @@ +namespace DigitalLearningSolutions.Data.Models +{ + using System.Collections.Generic; + using System.Linq; + + public class Objective + { + public Objective(int tutorialId, string interactions, int possible) + { + TutorialId = tutorialId; + Interactions = interactions.Split(',').Select(int.Parse); + Possible = possible; + MyScore = 0; + } + + public int TutorialId { get; set; } + public IEnumerable Interactions { get; set; } + public int Possible { get; set; } + public int MyScore { get; set; } + } +} diff --git a/DigitalLearningSolutions.Data/Models/Tracker/TrackerEndpointQueryParams.cs b/DigitalLearningSolutions.Data/Models/Tracker/TrackerEndpointQueryParams.cs index 8693d07308..9d2d363212 100644 --- a/DigitalLearningSolutions.Data/Models/Tracker/TrackerEndpointQueryParams.cs +++ b/DigitalLearningSolutions.Data/Models/Tracker/TrackerEndpointQueryParams.cs @@ -3,5 +3,7 @@ public class TrackerEndpointQueryParams { public string? Action { get; set; } + public int? CustomisationId { get; set; } + public int? SectionId { get; set; } } } diff --git a/DigitalLearningSolutions.Data/Services/TrackerActionService.cs b/DigitalLearningSolutions.Data/Services/TrackerActionService.cs new file mode 100644 index 0000000000..c702e1e4f4 --- /dev/null +++ b/DigitalLearningSolutions.Data/Services/TrackerActionService.cs @@ -0,0 +1,48 @@ +namespace DigitalLearningSolutions.Data.Services +{ + using System.Collections.Generic; + using System.Linq; + using DigitalLearningSolutions.Data.DataServices; + using Newtonsoft.Json; + using Newtonsoft.Json.Serialization; + + public interface ITrackerActionService + { + string GetObjectiveArray(int? customisationId, int? sectionId); + } + + public class TrackerActionService : ITrackerActionService + { + private readonly ITutorialContentDataService tutorialContentDataService; + + private readonly JsonSerializerSettings settings; + + public TrackerActionService(ITutorialContentDataService tutorialContentDataService) + { + this.tutorialContentDataService = tutorialContentDataService; + settings = new JsonSerializerSettings { ContractResolver = new LowercaseContractResolver() }; + } + + public string GetObjectiveArray(int? customisationId, int? sectionId) + { + if (!customisationId.HasValue || !sectionId.HasValue) + { + return JsonConvert.SerializeObject(new {}); + } + + var objectives = tutorialContentDataService.GetObjectivesBySectionId(sectionId.Value, customisationId.Value).ToList(); + + object objectiveArrayObject = objectives.Any() ? (object)new { objectives } : new {}; + + return JsonConvert.SerializeObject(objectiveArrayObject, settings); + } + + private class LowercaseContractResolver : DefaultContractResolver + { + protected override string ResolvePropertyName(string propertyName) + { + return propertyName.ToLower(); + } + } + } +} diff --git a/DigitalLearningSolutions.Data/Services/TrackerService.cs b/DigitalLearningSolutions.Data/Services/TrackerService.cs index c175eb59c0..498d8f29bb 100644 --- a/DigitalLearningSolutions.Data/Services/TrackerService.cs +++ b/DigitalLearningSolutions.Data/Services/TrackerService.cs @@ -13,10 +13,12 @@ public interface ITrackerService public class TrackerService : ITrackerService { private readonly ILogger logger; + private readonly ITrackerActionService trackerActionService; - public TrackerService(ILogger logger) + public TrackerService(ILogger logger, ITrackerActionService trackerActionService) { this.logger = logger; + this.trackerActionService = trackerActionService; } public string ProcessQuery(TrackerEndpointQueryParams query) @@ -32,6 +34,7 @@ public string ProcessQuery(TrackerEndpointQueryParams query) { return action switch { + TrackerEndpointAction.GetObjectiveArray => trackerActionService.GetObjectiveArray(query.CustomisationId, query.SectionId), _ => throw new ArgumentOutOfRangeException() }; } diff --git a/DigitalLearningSolutions.Web/Startup.cs b/DigitalLearningSolutions.Web/Startup.cs index 2067c122de..f09827dd8e 100644 --- a/DigitalLearningSolutions.Web/Startup.cs +++ b/DigitalLearningSolutions.Web/Startup.cs @@ -192,6 +192,7 @@ private static void RegisterServices(IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); From 71867f9a62ae3bb3086ad21567e8da81fe936ac2 Mon Sep 17 00:00:00 2001 From: Jonathan Bloxsom Date: Tue, 2 Nov 2021 10:59:40 +0000 Subject: [PATCH 2/8] HEEDLS-408 tests --- .../ObjectiveTests.cs | 28 +++++++ .../Services/TrackerActionServiceTests.cs | 75 +++++++++++++++++++ .../Services/TrackerServiceTests.cs | 19 ++++- 3 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 DigitalLearningSolutions.Data.Tests/DataServices/TutorialContentDataServiceTests/ObjectiveTests.cs create mode 100644 DigitalLearningSolutions.Data.Tests/Services/TrackerActionServiceTests.cs diff --git a/DigitalLearningSolutions.Data.Tests/DataServices/TutorialContentDataServiceTests/ObjectiveTests.cs b/DigitalLearningSolutions.Data.Tests/DataServices/TutorialContentDataServiceTests/ObjectiveTests.cs new file mode 100644 index 0000000000..961f845e5b --- /dev/null +++ b/DigitalLearningSolutions.Data.Tests/DataServices/TutorialContentDataServiceTests/ObjectiveTests.cs @@ -0,0 +1,28 @@ +namespace DigitalLearningSolutions.Data.Tests.DataServices.TutorialContentDataServiceTests +{ + using System; + using System.Linq; + using FluentAssertions; + using FluentAssertions.Execution; + using NUnit.Framework; + + internal partial class TutorialContentDataServiceTests + { + [Test] + public void GetObjectivesBySectionId_returns_objectives_correctly() + { + // When + var result = tutorialContentDataService.GetObjectivesBySectionId(SectionId, CustomisationId).ToList(); + + // Then + using (new AssertionScope()) + { + result.Count.Should().Be(4); + result.First().TutorialId.Should().Be(49); + result.First().Interactions.Should().BeEquivalentTo(new []{1,2,3,4}); + result.First().Possible.Should().Be(255); + result.First().MyScore.Should().Be(120); + } + } + } +} diff --git a/DigitalLearningSolutions.Data.Tests/Services/TrackerActionServiceTests.cs b/DigitalLearningSolutions.Data.Tests/Services/TrackerActionServiceTests.cs new file mode 100644 index 0000000000..c4cb5991c1 --- /dev/null +++ b/DigitalLearningSolutions.Data.Tests/Services/TrackerActionServiceTests.cs @@ -0,0 +1,75 @@ +namespace DigitalLearningSolutions.Data.Tests.Services +{ + using System.Collections.Generic; + using DigitalLearningSolutions.Data.DataServices; + using DigitalLearningSolutions.Data.Models; + using DigitalLearningSolutions.Data.Services; + using FakeItEasy; + using FluentAssertions; + using NUnit.Framework; + + public class TrackerActionServiceTests + { + private ITrackerActionService trackerActionService = null!; + + private ITutorialContentDataService dataService = null!; + + private const string EmptyObjectJson = "{}"; + + [SetUp] + public void Setup() + { + dataService = A.Fake(); + + trackerActionService = new TrackerActionService(dataService); + } + + [Test] + public void GetObjectiveArray_returns_results_in_specified_json_format() + { + // given + A.CallTo(() => dataService.GetObjectivesBySectionId(A._, A._)) + .Returns(new[] { new Objective(1, "6,7,8", 4), new Objective(2, "17,18,19", 0) }); + var expectedJson = + "{\"objectives\":[{\"tutorialid\":1,\"interactions\":[6,7,8],\"possible\":4,\"myscore\":0}," + + "{\"tutorialid\":2,\"interactions\":[17,18,19],\"possible\":0,\"myscore\":0}]}"; + + // when + var result = trackerActionService.GetObjectiveArray(1, 1); + + // then + result.Should().Be(expectedJson); + } + + [Test] + public void GetObjectiveArray_returns_empty_object_json_if_no_results_found() + { + // given + A.CallTo(() => dataService.GetObjectivesBySectionId(A._, A._)) + .Returns(new List()); + + // when + var result = trackerActionService.GetObjectiveArray(1, 1); + + // then + result.Should().Be(EmptyObjectJson); + } + + [Test] + [TestCase(null, null)] + [TestCase(1, null)] + [TestCase(null, 1)] + public void GetObjectiveArray_returns_empty_object_json_if_parameter_missing(int? customisationId, int? sectionId) + { + // given + A.CallTo(() => dataService.GetObjectivesBySectionId(A._, A._)) + .Returns(new[] { new Objective(1, "1", 9) }); + + // when + var result = trackerActionService.GetObjectiveArray(customisationId, sectionId); + + // then + result.Should().Be(EmptyObjectJson); + } + } +} diff --git a/DigitalLearningSolutions.Data.Tests/Services/TrackerServiceTests.cs b/DigitalLearningSolutions.Data.Tests/Services/TrackerServiceTests.cs index d1b73cdfdf..68ee42762e 100644 --- a/DigitalLearningSolutions.Data.Tests/Services/TrackerServiceTests.cs +++ b/DigitalLearningSolutions.Data.Tests/Services/TrackerServiceTests.cs @@ -11,14 +11,16 @@ public class TrackerServiceTests { private ILogger logger = null!; + private ITrackerActionService actionService = null!; private ITrackerService trackerService = null!; [SetUp] public void Setup() { logger = A.Fake>(); + actionService = A.Fake(); - trackerService = new TrackerService(logger); + trackerService = new TrackerService(logger, actionService); } [Test] @@ -46,5 +48,20 @@ public void ProcessQuery_with_unknown_action_returns_InvalidAction_response() // Then result.Should().Be(TrackerEndpointErrorResponse.InvalidAction); } + + [Test] + public void + ProcessQuery_with_GetObjectiveArray_action_passes_query_params_and_returns_appropriate_service_response() + { + // Given + var query = new TrackerEndpointQueryParams { Action = "GetObjectiveArray", CustomisationId = 1, SectionId = 1 }; + A.CallTo(() => actionService.GetObjectiveArray(1, 1)).Returns("GOA result"); + + // When + var result = trackerService.ProcessQuery(query); + + // Then + result.Should().Be("GOA result"); + } } } From 1b92a080482c572e14681ea6fadadadf4cec2f40 Mon Sep 17 00:00:00 2001 From: Jonathan Bloxsom Date: Tue, 2 Nov 2021 12:46:14 +0000 Subject: [PATCH 3/8] HEEDLS-408 fixes --- .../TutorialContentDataServiceTests/ObjectiveTests.cs | 11 +++++------ .../DataServices/TutorialContentDataService.cs | 4 ++-- DigitalLearningSolutions.Data/Models/Objective.cs | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/DigitalLearningSolutions.Data.Tests/DataServices/TutorialContentDataServiceTests/ObjectiveTests.cs b/DigitalLearningSolutions.Data.Tests/DataServices/TutorialContentDataServiceTests/ObjectiveTests.cs index 961f845e5b..b6e14a64cc 100644 --- a/DigitalLearningSolutions.Data.Tests/DataServices/TutorialContentDataServiceTests/ObjectiveTests.cs +++ b/DigitalLearningSolutions.Data.Tests/DataServices/TutorialContentDataServiceTests/ObjectiveTests.cs @@ -1,6 +1,5 @@ namespace DigitalLearningSolutions.Data.Tests.DataServices.TutorialContentDataServiceTests { - using System; using System.Linq; using FluentAssertions; using FluentAssertions.Execution; @@ -12,16 +11,16 @@ internal partial class TutorialContentDataServiceTests public void GetObjectivesBySectionId_returns_objectives_correctly() { // When - var result = tutorialContentDataService.GetObjectivesBySectionId(SectionId, CustomisationId).ToList(); + var result = tutorialContentDataService.GetObjectivesBySectionId(248, 22062).ToList(); // Then using (new AssertionScope()) { result.Count.Should().Be(4); - result.First().TutorialId.Should().Be(49); - result.First().Interactions.Should().BeEquivalentTo(new []{1,2,3,4}); - result.First().Possible.Should().Be(255); - result.First().MyScore.Should().Be(120); + result.First().TutorialId.Should().Be(1137); + result.First().Interactions.Should().BeEquivalentTo(new []{0,1,2,3}); + result.First().Possible.Should().Be(4); + result.First().MyScore.Should().Be(0); } } } diff --git a/DigitalLearningSolutions.Data/DataServices/TutorialContentDataService.cs b/DigitalLearningSolutions.Data/DataServices/TutorialContentDataService.cs index 0f3c8aa1fd..5ea78c2a6c 100644 --- a/DigitalLearningSolutions.Data/DataServices/TutorialContentDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/TutorialContentDataService.cs @@ -348,8 +348,8 @@ public IEnumerable GetObjectivesBySectionId(int sectionId, int custom WHEN tu.OriginalTutorialID > 0 THEN tu.OriginalTutorialID ELSE tu.TutorialID END AS TutorialID, - tu.CMIInteractionsIDs AS Interactions, - ct.DiagAssessOutOf AS Possible + tu.CMIInteractionIDs AS Interactions, + tu.DiagAssessOutOf AS Possible FROM dbo.Tutorials AS tu LEFT JOIN dbo.CustomisationTutorials AS ct ON ct.TutorialID = tu.TutorialID diff --git a/DigitalLearningSolutions.Data/Models/Objective.cs b/DigitalLearningSolutions.Data/Models/Objective.cs index 15e175af5a..873c01e89f 100644 --- a/DigitalLearningSolutions.Data/Models/Objective.cs +++ b/DigitalLearningSolutions.Data/Models/Objective.cs @@ -8,7 +8,7 @@ public class Objective public Objective(int tutorialId, string interactions, int possible) { TutorialId = tutorialId; - Interactions = interactions.Split(',').Select(int.Parse); + Interactions = string.IsNullOrEmpty(interactions) ? new List() : interactions.Split(',').Select(int.Parse); Possible = possible; MyScore = 0; } From 0862257cbdfb4ce0431c4b6e8a75a91cfb132f0a Mon Sep 17 00:00:00 2001 From: Jonathan Bloxsom Date: Tue, 2 Nov 2021 14:29:56 +0000 Subject: [PATCH 4/8] HEEDLS-408 cleanup code --- .../TutorialContentDataServiceTests/ObjectiveTests.cs | 2 +- .../Services/TrackerActionServiceTests.cs | 10 ++++++---- .../Services/TrackerServiceTests.cs | 9 +++++---- .../DataServices/TutorialContentDataService.cs | 9 ++++++++- .../Enums/TrackerEndpointAction.cs | 2 +- DigitalLearningSolutions.Data/Models/Objective.cs | 4 +++- .../Services/TrackerActionService.cs | 11 +++++------ .../Services/TrackerService.cs | 7 +++++-- DigitalLearningSolutions.Web/Startup.cs | 2 +- 9 files changed, 35 insertions(+), 21 deletions(-) diff --git a/DigitalLearningSolutions.Data.Tests/DataServices/TutorialContentDataServiceTests/ObjectiveTests.cs b/DigitalLearningSolutions.Data.Tests/DataServices/TutorialContentDataServiceTests/ObjectiveTests.cs index b6e14a64cc..cbcf7c282c 100644 --- a/DigitalLearningSolutions.Data.Tests/DataServices/TutorialContentDataServiceTests/ObjectiveTests.cs +++ b/DigitalLearningSolutions.Data.Tests/DataServices/TutorialContentDataServiceTests/ObjectiveTests.cs @@ -18,7 +18,7 @@ public void GetObjectivesBySectionId_returns_objectives_correctly() { result.Count.Should().Be(4); result.First().TutorialId.Should().Be(1137); - result.First().Interactions.Should().BeEquivalentTo(new []{0,1,2,3}); + result.First().Interactions.Should().BeEquivalentTo(new[] { 0, 1, 2, 3 }); result.First().Possible.Should().Be(4); result.First().MyScore.Should().Be(0); } diff --git a/DigitalLearningSolutions.Data.Tests/Services/TrackerActionServiceTests.cs b/DigitalLearningSolutions.Data.Tests/Services/TrackerActionServiceTests.cs index c4cb5991c1..007830d185 100644 --- a/DigitalLearningSolutions.Data.Tests/Services/TrackerActionServiceTests.cs +++ b/DigitalLearningSolutions.Data.Tests/Services/TrackerActionServiceTests.cs @@ -10,11 +10,10 @@ public class TrackerActionServiceTests { - private ITrackerActionService trackerActionService = null!; + private const string EmptyObjectJson = "{}"; private ITutorialContentDataService dataService = null!; - - private const string EmptyObjectJson = "{}"; + private ITrackerActionService trackerActionService = null!; [SetUp] public void Setup() @@ -59,7 +58,10 @@ public void GetObjectiveArray_returns_empty_object_json_if_no_results_found() [TestCase(null, null)] [TestCase(1, null)] [TestCase(null, 1)] - public void GetObjectiveArray_returns_empty_object_json_if_parameter_missing(int? customisationId, int? sectionId) + public void GetObjectiveArray_returns_empty_object_json_if_parameter_missing( + int? customisationId, + int? sectionId + ) { // given A.CallTo(() => dataService.GetObjectivesBySectionId(A._, A._)) diff --git a/DigitalLearningSolutions.Data.Tests/Services/TrackerServiceTests.cs b/DigitalLearningSolutions.Data.Tests/Services/TrackerServiceTests.cs index 68ee42762e..4f2a0adcb2 100644 --- a/DigitalLearningSolutions.Data.Tests/Services/TrackerServiceTests.cs +++ b/DigitalLearningSolutions.Data.Tests/Services/TrackerServiceTests.cs @@ -10,14 +10,14 @@ public class TrackerServiceTests { - private ILogger logger = null!; private ITrackerActionService actionService = null!; + private ILogger logger = null!; private ITrackerService trackerService = null!; [SetUp] public void Setup() { - logger = A.Fake>(); + logger = A.Fake>(); actionService = A.Fake(); trackerService = new TrackerService(logger, actionService); @@ -40,7 +40,7 @@ public void ProcessQuery_with_null_action_returns_NullAction_response() public void ProcessQuery_with_unknown_action_returns_InvalidAction_response() { // Given - var query = new TrackerEndpointQueryParams{Action = "InvalidAction"}; + var query = new TrackerEndpointQueryParams { Action = "InvalidAction" }; // When var result = trackerService.ProcessQuery(query); @@ -54,7 +54,8 @@ public void ProcessQuery_with_GetObjectiveArray_action_passes_query_params_and_returns_appropriate_service_response() { // Given - var query = new TrackerEndpointQueryParams { Action = "GetObjectiveArray", CustomisationId = 1, SectionId = 1 }; + var query = new TrackerEndpointQueryParams + { Action = "GetObjectiveArray", CustomisationId = 1, SectionId = 1 }; A.CallTo(() => actionService.GetObjectiveArray(1, 1)).Returns("GOA result"); // When diff --git a/DigitalLearningSolutions.Data/DataServices/TutorialContentDataService.cs b/DigitalLearningSolutions.Data/DataServices/TutorialContentDataService.cs index 5ea78c2a6c..ef75881ccd 100644 --- a/DigitalLearningSolutions.Data/DataServices/TutorialContentDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/TutorialContentDataService.cs @@ -20,7 +20,14 @@ int tutorialId TutorialVideo? GetTutorialVideo(int customisationId, int sectionId, int tutorialId); IEnumerable GetTutorialsBySectionId(int sectionId, int customisationId); IEnumerable GetTutorialIdsForCourse(int customisationId); - void UpdateOrInsertCustomisationTutorialStatuses(int tutorialId, int customisationId, bool diagnosticEnabled, bool learningEnabled); + + void UpdateOrInsertCustomisationTutorialStatuses( + int tutorialId, + int customisationId, + bool diagnosticEnabled, + bool learningEnabled + ); + IEnumerable GetObjectivesBySectionId(int sectionId, int customisationId); } diff --git a/DigitalLearningSolutions.Data/Enums/TrackerEndpointAction.cs b/DigitalLearningSolutions.Data/Enums/TrackerEndpointAction.cs index 403a7e751d..602b7f7526 100644 --- a/DigitalLearningSolutions.Data/Enums/TrackerEndpointAction.cs +++ b/DigitalLearningSolutions.Data/Enums/TrackerEndpointAction.cs @@ -2,6 +2,6 @@ { public enum TrackerEndpointAction { - GetObjectiveArray + GetObjectiveArray, } } diff --git a/DigitalLearningSolutions.Data/Models/Objective.cs b/DigitalLearningSolutions.Data/Models/Objective.cs index 873c01e89f..65cead0736 100644 --- a/DigitalLearningSolutions.Data/Models/Objective.cs +++ b/DigitalLearningSolutions.Data/Models/Objective.cs @@ -8,7 +8,9 @@ public class Objective public Objective(int tutorialId, string interactions, int possible) { TutorialId = tutorialId; - Interactions = string.IsNullOrEmpty(interactions) ? new List() : interactions.Split(',').Select(int.Parse); + Interactions = string.IsNullOrEmpty(interactions) + ? new List() + : interactions.Split(',').Select(int.Parse); Possible = possible; MyScore = 0; } diff --git a/DigitalLearningSolutions.Data/Services/TrackerActionService.cs b/DigitalLearningSolutions.Data/Services/TrackerActionService.cs index c702e1e4f4..75cafc13df 100644 --- a/DigitalLearningSolutions.Data/Services/TrackerActionService.cs +++ b/DigitalLearningSolutions.Data/Services/TrackerActionService.cs @@ -1,6 +1,5 @@ namespace DigitalLearningSolutions.Data.Services { - using System.Collections.Generic; using System.Linq; using DigitalLearningSolutions.Data.DataServices; using Newtonsoft.Json; @@ -13,9 +12,8 @@ public interface ITrackerActionService public class TrackerActionService : ITrackerActionService { - private readonly ITutorialContentDataService tutorialContentDataService; - private readonly JsonSerializerSettings settings; + private readonly ITutorialContentDataService tutorialContentDataService; public TrackerActionService(ITutorialContentDataService tutorialContentDataService) { @@ -27,12 +25,13 @@ public string GetObjectiveArray(int? customisationId, int? sectionId) { if (!customisationId.HasValue || !sectionId.HasValue) { - return JsonConvert.SerializeObject(new {}); + return JsonConvert.SerializeObject(new { }); } - var objectives = tutorialContentDataService.GetObjectivesBySectionId(sectionId.Value, customisationId.Value).ToList(); + var objectives = tutorialContentDataService.GetObjectivesBySectionId(sectionId.Value, customisationId.Value) + .ToList(); - object objectiveArrayObject = objectives.Any() ? (object)new { objectives } : new {}; + object objectiveArrayObject = objectives.Any() ? (object)new { objectives } : new { }; return JsonConvert.SerializeObject(objectiveArrayObject, settings); } diff --git a/DigitalLearningSolutions.Data/Services/TrackerService.cs b/DigitalLearningSolutions.Data/Services/TrackerService.cs index 498d8f29bb..19c2015d76 100644 --- a/DigitalLearningSolutions.Data/Services/TrackerService.cs +++ b/DigitalLearningSolutions.Data/Services/TrackerService.cs @@ -34,8 +34,11 @@ public string ProcessQuery(TrackerEndpointQueryParams query) { return action switch { - TrackerEndpointAction.GetObjectiveArray => trackerActionService.GetObjectiveArray(query.CustomisationId, query.SectionId), - _ => throw new ArgumentOutOfRangeException() + TrackerEndpointAction.GetObjectiveArray => trackerActionService.GetObjectiveArray( + query.CustomisationId, + query.SectionId + ), + _ => throw new ArgumentOutOfRangeException(), }; } diff --git a/DigitalLearningSolutions.Web/Startup.cs b/DigitalLearningSolutions.Web/Startup.cs index f09827dd8e..df383f3b7b 100644 --- a/DigitalLearningSolutions.Web/Startup.cs +++ b/DigitalLearningSolutions.Web/Startup.cs @@ -263,7 +263,7 @@ public void Configure(IApplicationBuilder app, IMigrationRunner migrationRunner, app.UseForwardedHeaders( new ForwardedHeadersOptions { - ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto + ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto, } ); From ab8c6cfab494b5577be4b4f0e1f05429a34ec531 Mon Sep 17 00:00:00 2001 From: Jonathan Bloxsom Date: Fri, 12 Nov 2021 10:21:23 +0000 Subject: [PATCH 5/8] HEEDLS-408 markups --- .../TutorialContentDataServiceTests/ObjectiveTests.cs | 5 +++-- .../Services/TrackerActionServiceTests.cs | 6 +++--- .../DataServices/TutorialContentDataService.cs | 4 ++-- .../Services/TrackerActionService.cs | 3 ++- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/DigitalLearningSolutions.Data.Tests/DataServices/TutorialContentDataServiceTests/ObjectiveTests.cs b/DigitalLearningSolutions.Data.Tests/DataServices/TutorialContentDataServiceTests/ObjectiveTests.cs index cbcf7c282c..4682ae5b84 100644 --- a/DigitalLearningSolutions.Data.Tests/DataServices/TutorialContentDataServiceTests/ObjectiveTests.cs +++ b/DigitalLearningSolutions.Data.Tests/DataServices/TutorialContentDataServiceTests/ObjectiveTests.cs @@ -8,10 +8,11 @@ internal partial class TutorialContentDataServiceTests { [Test] - public void GetObjectivesBySectionId_returns_objectives_correctly() + public void GetNonArchivedObjectivesBySectionAndCustomisationId_returns_objectives_correctly() { // When - var result = tutorialContentDataService.GetObjectivesBySectionId(248, 22062).ToList(); + var result = tutorialContentDataService.GetNonArchivedObjectivesBySectionAndCustomisationId(248, 22062) + .ToList(); // Then using (new AssertionScope()) diff --git a/DigitalLearningSolutions.Data.Tests/Services/TrackerActionServiceTests.cs b/DigitalLearningSolutions.Data.Tests/Services/TrackerActionServiceTests.cs index 007830d185..7a2b661340 100644 --- a/DigitalLearningSolutions.Data.Tests/Services/TrackerActionServiceTests.cs +++ b/DigitalLearningSolutions.Data.Tests/Services/TrackerActionServiceTests.cs @@ -27,7 +27,7 @@ public void Setup() public void GetObjectiveArray_returns_results_in_specified_json_format() { // given - A.CallTo(() => dataService.GetObjectivesBySectionId(A._, A._)) + A.CallTo(() => dataService.GetNonArchivedObjectivesBySectionAndCustomisationId(A._, A._)) .Returns(new[] { new Objective(1, "6,7,8", 4), new Objective(2, "17,18,19", 0) }); var expectedJson = "{\"objectives\":[{\"tutorialid\":1,\"interactions\":[6,7,8],\"possible\":4,\"myscore\":0}," + @@ -44,7 +44,7 @@ public void GetObjectiveArray_returns_results_in_specified_json_format() public void GetObjectiveArray_returns_empty_object_json_if_no_results_found() { // given - A.CallTo(() => dataService.GetObjectivesBySectionId(A._, A._)) + A.CallTo(() => dataService.GetNonArchivedObjectivesBySectionAndCustomisationId(A._, A._)) .Returns(new List()); // when @@ -64,7 +64,7 @@ public void GetObjectiveArray_returns_empty_object_json_if_parameter_missing( ) { // given - A.CallTo(() => dataService.GetObjectivesBySectionId(A._, A._)) + A.CallTo(() => dataService.GetNonArchivedObjectivesBySectionAndCustomisationId(A._, A._)) .Returns(new[] { new Objective(1, "1", 9) }); // when diff --git a/DigitalLearningSolutions.Data/DataServices/TutorialContentDataService.cs b/DigitalLearningSolutions.Data/DataServices/TutorialContentDataService.cs index ef75881ccd..70207c3db0 100644 --- a/DigitalLearningSolutions.Data/DataServices/TutorialContentDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/TutorialContentDataService.cs @@ -28,7 +28,7 @@ void UpdateOrInsertCustomisationTutorialStatuses( bool learningEnabled ); - IEnumerable GetObjectivesBySectionId(int sectionId, int customisationId); + IEnumerable GetNonArchivedObjectivesBySectionAndCustomisationId(int sectionId, int customisationId); } public class TutorialContentDataService : ITutorialContentDataService @@ -347,7 +347,7 @@ INSERT INTO CustomisationTutorials (CustomisationID, TutorialID, [Status], DiagS ); } - public IEnumerable GetObjectivesBySectionId(int sectionId, int customisationId) + public IEnumerable GetNonArchivedObjectivesBySectionAndCustomisationId(int sectionId, int customisationId) { return connection.Query( @"SELECT diff --git a/DigitalLearningSolutions.Data/Services/TrackerActionService.cs b/DigitalLearningSolutions.Data/Services/TrackerActionService.cs index 75cafc13df..356597c5f9 100644 --- a/DigitalLearningSolutions.Data/Services/TrackerActionService.cs +++ b/DigitalLearningSolutions.Data/Services/TrackerActionService.cs @@ -28,7 +28,8 @@ public string GetObjectiveArray(int? customisationId, int? sectionId) return JsonConvert.SerializeObject(new { }); } - var objectives = tutorialContentDataService.GetObjectivesBySectionId(sectionId.Value, customisationId.Value) + var objectives = tutorialContentDataService + .GetNonArchivedObjectivesBySectionAndCustomisationId(sectionId.Value, customisationId.Value) .ToList(); object objectiveArrayObject = objectives.Any() ? (object)new { objectives } : new { }; From fcecc3150eb250fbb354cd4bfc206a93edafc7df Mon Sep 17 00:00:00 2001 From: Jonathan Bloxsom Date: Fri, 12 Nov 2021 17:43:55 +0000 Subject: [PATCH 6/8] HEEDLS-408 review markups 1 --- .../ObjectiveTests.cs | 34 +++++++++----- .../TutorialContentDataServiceTests.cs | 12 +++-- .../Services/TrackerActionServiceTests.cs | 18 +++----- .../Services/TrackerServiceTests.cs | 46 +++++++++++++++++-- .../TutorialContentDataService.cs | 3 ++ .../Mappers/EnumerableIntHandler.cs | 21 +++++++++ .../Models/Tracker/GetObjectiveArrayData.cs | 14 ++++++ .../Tracker/ITrackerEndpointDataModel.cs | 10 ++++ .../Models/{ => Tracker}/Objective.cs | 9 ++-- .../Services/TrackerActionService.cs | 21 ++------- .../Services/TrackerService.cs | 26 ++++++++++- 11 files changed, 160 insertions(+), 54 deletions(-) create mode 100644 DigitalLearningSolutions.Data/Mappers/EnumerableIntHandler.cs create mode 100644 DigitalLearningSolutions.Data/Models/Tracker/GetObjectiveArrayData.cs create mode 100644 DigitalLearningSolutions.Data/Models/Tracker/ITrackerEndpointDataModel.cs rename DigitalLearningSolutions.Data/Models/{ => Tracker}/Objective.cs (53%) diff --git a/DigitalLearningSolutions.Data.Tests/DataServices/TutorialContentDataServiceTests/ObjectiveTests.cs b/DigitalLearningSolutions.Data.Tests/DataServices/TutorialContentDataServiceTests/ObjectiveTests.cs index 4682ae5b84..08d9347dd3 100644 --- a/DigitalLearningSolutions.Data.Tests/DataServices/TutorialContentDataServiceTests/ObjectiveTests.cs +++ b/DigitalLearningSolutions.Data.Tests/DataServices/TutorialContentDataServiceTests/ObjectiveTests.cs @@ -1,6 +1,8 @@ namespace DigitalLearningSolutions.Data.Tests.DataServices.TutorialContentDataServiceTests { using System.Linq; + using System.Transactions; + using Dapper; using FluentAssertions; using FluentAssertions.Execution; using NUnit.Framework; @@ -10,18 +12,28 @@ internal partial class TutorialContentDataServiceTests [Test] public void GetNonArchivedObjectivesBySectionAndCustomisationId_returns_objectives_correctly() { - // When - var result = tutorialContentDataService.GetNonArchivedObjectivesBySectionAndCustomisationId(248, 22062) - .ToList(); - - // Then - using (new AssertionScope()) + using (new TransactionScope()) { - result.Count.Should().Be(4); - result.First().TutorialId.Should().Be(1137); - result.First().Interactions.Should().BeEquivalentTo(new[] { 0, 1, 2, 3 }); - result.First().Possible.Should().Be(4); - result.First().MyScore.Should().Be(0); + connection.Execute("UPDATE Tutorials SET OriginalTutorialID = 1 WHERE TutorialID = 1137"); + + // When + var result = tutorialContentDataService.GetNonArchivedObjectivesBySectionAndCustomisationId(248, 22062) + .ToList(); + + // Then + using (new AssertionScope()) + { + result.Count.Should().Be(4); + result.First().TutorialId.Should().Be(1); + result.First().Interactions.Should().BeEquivalentTo(new[] { 0, 1, 2, 3 }); + result.First().Possible.Should().Be(4); + result.First().MyScore.Should().Be(0); + + result.Last().TutorialId.Should().Be(1257); + result.Last().Interactions.Should().BeEquivalentTo(new[] { 8, 9, 10 }); + result.Last().Possible.Should().Be(3); + result.Last().MyScore.Should().Be(0); + } } } } diff --git a/DigitalLearningSolutions.Data.Tests/DataServices/TutorialContentDataServiceTests/TutorialContentDataServiceTests.cs b/DigitalLearningSolutions.Data.Tests/DataServices/TutorialContentDataServiceTests/TutorialContentDataServiceTests.cs index 966294f26f..172eab9e32 100644 --- a/DigitalLearningSolutions.Data.Tests/DataServices/TutorialContentDataServiceTests/TutorialContentDataServiceTests.cs +++ b/DigitalLearningSolutions.Data.Tests/DataServices/TutorialContentDataServiceTests/TutorialContentDataServiceTests.cs @@ -2,19 +2,21 @@ { using DigitalLearningSolutions.Data.DataServices; using DigitalLearningSolutions.Data.Tests.TestHelpers; + using Microsoft.Data.SqlClient; using NUnit.Framework; internal partial class TutorialContentDataServiceTests { - private TutorialContentDataService tutorialContentDataService; - private TutorialContentTestHelper tutorialContentTestHelper; - private SectionContentTestHelper sectionContentTestHelper; - private CourseContentTestHelper courseContentTestHelper; + private SqlConnection connection = null!; + private TutorialContentDataService tutorialContentDataService = null!; + private TutorialContentTestHelper tutorialContentTestHelper = null!; + private SectionContentTestHelper sectionContentTestHelper = null!; + private CourseContentTestHelper courseContentTestHelper = null!; [SetUp] public void Setup() { - var connection = ServiceTestHelper.GetDatabaseConnection(); + connection = ServiceTestHelper.GetDatabaseConnection(); tutorialContentDataService = new TutorialContentDataService(connection); tutorialContentTestHelper = new TutorialContentTestHelper(connection); sectionContentTestHelper = new SectionContentTestHelper(connection); diff --git a/DigitalLearningSolutions.Data.Tests/Services/TrackerActionServiceTests.cs b/DigitalLearningSolutions.Data.Tests/Services/TrackerActionServiceTests.cs index 7a2b661340..ded004214f 100644 --- a/DigitalLearningSolutions.Data.Tests/Services/TrackerActionServiceTests.cs +++ b/DigitalLearningSolutions.Data.Tests/Services/TrackerActionServiceTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using DigitalLearningSolutions.Data.DataServices; using DigitalLearningSolutions.Data.Models; + using DigitalLearningSolutions.Data.Models.Tracker; using DigitalLearningSolutions.Data.Services; using FakeItEasy; using FluentAssertions; @@ -10,8 +11,6 @@ public class TrackerActionServiceTests { - private const string EmptyObjectJson = "{}"; - private ITutorialContentDataService dataService = null!; private ITrackerActionService trackerActionService = null!; @@ -28,16 +27,13 @@ public void GetObjectiveArray_returns_results_in_specified_json_format() { // given A.CallTo(() => dataService.GetNonArchivedObjectivesBySectionAndCustomisationId(A._, A._)) - .Returns(new[] { new Objective(1, "6,7,8", 4), new Objective(2, "17,18,19", 0) }); - var expectedJson = - "{\"objectives\":[{\"tutorialid\":1,\"interactions\":[6,7,8],\"possible\":4,\"myscore\":0}," + - "{\"tutorialid\":2,\"interactions\":[17,18,19],\"possible\":0,\"myscore\":0}]}"; + .Returns(new[] { new Objective(1, new List { 6, 7, 8 }, 4), new Objective(2, new List { 17, 18, 19 }, 0) }); // when var result = trackerActionService.GetObjectiveArray(1, 1); // then - result.Should().Be(expectedJson); + result.Should().BeEquivalentTo(new GetObjectiveArrayData(new[] { new Objective(1, new List { 6, 7, 8 }, 4), new Objective(2, new List { 17, 18, 19 }, 0) })); } [Test] @@ -51,27 +47,27 @@ public void GetObjectiveArray_returns_empty_object_json_if_no_results_found() var result = trackerActionService.GetObjectiveArray(1, 1); // then - result.Should().Be(EmptyObjectJson); + result.Should().Be(null); } [Test] [TestCase(null, null)] [TestCase(1, null)] [TestCase(null, 1)] - public void GetObjectiveArray_returns_empty_object_json_if_parameter_missing( + public void GetObjectiveArray_returns_null_if_parameter_missing( int? customisationId, int? sectionId ) { // given A.CallTo(() => dataService.GetNonArchivedObjectivesBySectionAndCustomisationId(A._, A._)) - .Returns(new[] { new Objective(1, "1", 9) }); + .Returns(new[] { new Objective(1, new List{1}, 9) }); // when var result = trackerActionService.GetObjectiveArray(customisationId, sectionId); // then - result.Should().Be(EmptyObjectJson); + result.Should().Be(null); } } } diff --git a/DigitalLearningSolutions.Data.Tests/Services/TrackerServiceTests.cs b/DigitalLearningSolutions.Data.Tests/Services/TrackerServiceTests.cs index 4f2a0adcb2..34c2994d94 100644 --- a/DigitalLearningSolutions.Data.Tests/Services/TrackerServiceTests.cs +++ b/DigitalLearningSolutions.Data.Tests/Services/TrackerServiceTests.cs @@ -1,6 +1,8 @@ namespace DigitalLearningSolutions.Data.Tests.Services { + using System.Collections.Generic; using DigitalLearningSolutions.Data.Enums; + using DigitalLearningSolutions.Data.Models; using DigitalLearningSolutions.Data.Models.Tracker; using DigitalLearningSolutions.Data.Services; using FakeItEasy; @@ -50,19 +52,55 @@ public void ProcessQuery_with_unknown_action_returns_InvalidAction_response() } [Test] - public void - ProcessQuery_with_GetObjectiveArray_action_passes_query_params_and_returns_appropriate_service_response() + public void ProcessQuery_with_GetObjectiveArray_action_passes_query_params() { // Given var query = new TrackerEndpointQueryParams { Action = "GetObjectiveArray", CustomisationId = 1, SectionId = 1 }; - A.CallTo(() => actionService.GetObjectiveArray(1, 1)).Returns("GOA result"); + + // When + trackerService.ProcessQuery(query); + + // Then + A.CallTo(() => actionService.GetObjectiveArray(1, 1)).MustHaveHappenedOnceExactly(); + } + + [Test] + public void ProcessQuery_with_GetObjectiveArray_action_correctly_serialises_contentful_response() + { + // Given + var dataToReturn = new GetObjectiveArrayData(new[] { new Objective(1, new List{6,7,8}, 4), new Objective(2, new List{17,18,19}, 0) }); + var expectedJson = + "{\"objectives\":[{\"tutorialid\":1,\"interactions\":[6,7,8],\"possible\":4,\"myscore\":0}," + + "{\"tutorialid\":2,\"interactions\":[17,18,19],\"possible\":0,\"myscore\":0}]}"; + + var query = new TrackerEndpointQueryParams + { Action = "GetObjectiveArray", CustomisationId = 1, SectionId = 1 }; + A.CallTo(() => actionService.GetObjectiveArray(1, 1)).Returns(dataToReturn); + + // When + var result = trackerService.ProcessQuery(query); + + // Then + result.Should().Be(expectedJson); + } + + [Test] + public void ProcessQuery_with_GetObjectiveArray_action_correctly_serialises_null_response() + { + // Given + GetObjectiveArrayData? dataToReturn = null; + var expectedJson = "{}"; + + var query = new TrackerEndpointQueryParams + { Action = "GetObjectiveArray", CustomisationId = 1, SectionId = 1 }; + A.CallTo(() => actionService.GetObjectiveArray(1, 1)).Returns(dataToReturn); // When var result = trackerService.ProcessQuery(query); // Then - result.Should().Be("GOA result"); + result.Should().Be(expectedJson); } } } diff --git a/DigitalLearningSolutions.Data/DataServices/TutorialContentDataService.cs b/DigitalLearningSolutions.Data/DataServices/TutorialContentDataService.cs index 70207c3db0..5f0b095aae 100644 --- a/DigitalLearningSolutions.Data/DataServices/TutorialContentDataService.cs +++ b/DigitalLearningSolutions.Data/DataServices/TutorialContentDataService.cs @@ -4,7 +4,9 @@ using System.Data; using Dapper; using DigitalLearningSolutions.Data.Exceptions; + using DigitalLearningSolutions.Data.Mappers; using DigitalLearningSolutions.Data.Models; + using DigitalLearningSolutions.Data.Models.Tracker; using DigitalLearningSolutions.Data.Models.TutorialContent; public interface ITutorialContentDataService @@ -38,6 +40,7 @@ public class TutorialContentDataService : ITutorialContentDataService public TutorialContentDataService(IDbConnection connection) { this.connection = connection; + SqlMapper.AddTypeHandler(new EnumerableIntHandler()); } public TutorialInformation? GetTutorialInformation( diff --git a/DigitalLearningSolutions.Data/Mappers/EnumerableIntHandler.cs b/DigitalLearningSolutions.Data/Mappers/EnumerableIntHandler.cs new file mode 100644 index 0000000000..189a191489 --- /dev/null +++ b/DigitalLearningSolutions.Data/Mappers/EnumerableIntHandler.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; + +namespace DigitalLearningSolutions.Data.Mappers +{ + using System.Data; + using System.Linq; + using Dapper; + + public class EnumerableIntHandler : SqlMapper.TypeHandler> + { + public override void SetValue(IDbDataParameter parameter, IEnumerable value) + { + throw new System.NotImplementedException(); + } + + public override IEnumerable Parse(object value) + { + return value.ToString()?.Split(',').Select(int.Parse) ?? new List(); + } + } +} diff --git a/DigitalLearningSolutions.Data/Models/Tracker/GetObjectiveArrayData.cs b/DigitalLearningSolutions.Data/Models/Tracker/GetObjectiveArrayData.cs new file mode 100644 index 0000000000..d6b231f674 --- /dev/null +++ b/DigitalLearningSolutions.Data/Models/Tracker/GetObjectiveArrayData.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace DigitalLearningSolutions.Data.Models.Tracker +{ + public class GetObjectiveArrayData : ITrackerEndpointDataModel + { + public GetObjectiveArrayData(IEnumerable objectives) + { + Objectives = objectives; + } + + public IEnumerable Objectives { get; set; } + } +} diff --git a/DigitalLearningSolutions.Data/Models/Tracker/ITrackerEndpointDataModel.cs b/DigitalLearningSolutions.Data/Models/Tracker/ITrackerEndpointDataModel.cs new file mode 100644 index 0000000000..477544abb9 --- /dev/null +++ b/DigitalLearningSolutions.Data/Models/Tracker/ITrackerEndpointDataModel.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace DigitalLearningSolutions.Data.Models.Tracker +{ + interface ITrackerEndpointDataModel + { + } +} diff --git a/DigitalLearningSolutions.Data/Models/Objective.cs b/DigitalLearningSolutions.Data/Models/Tracker/Objective.cs similarity index 53% rename from DigitalLearningSolutions.Data/Models/Objective.cs rename to DigitalLearningSolutions.Data/Models/Tracker/Objective.cs index 65cead0736..a63c079770 100644 --- a/DigitalLearningSolutions.Data/Models/Objective.cs +++ b/DigitalLearningSolutions.Data/Models/Tracker/Objective.cs @@ -1,16 +1,13 @@ -namespace DigitalLearningSolutions.Data.Models +namespace DigitalLearningSolutions.Data.Models.Tracker { using System.Collections.Generic; - using System.Linq; public class Objective { - public Objective(int tutorialId, string interactions, int possible) + public Objective(int tutorialId, IEnumerable interactions, int possible) { TutorialId = tutorialId; - Interactions = string.IsNullOrEmpty(interactions) - ? new List() - : interactions.Split(',').Select(int.Parse); + Interactions = interactions; Possible = possible; MyScore = 0; } diff --git a/DigitalLearningSolutions.Data/Services/TrackerActionService.cs b/DigitalLearningSolutions.Data/Services/TrackerActionService.cs index 356597c5f9..f651d3d69e 100644 --- a/DigitalLearningSolutions.Data/Services/TrackerActionService.cs +++ b/DigitalLearningSolutions.Data/Services/TrackerActionService.cs @@ -2,47 +2,36 @@ { using System.Linq; using DigitalLearningSolutions.Data.DataServices; + using DigitalLearningSolutions.Data.Models.Tracker; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; public interface ITrackerActionService { - string GetObjectiveArray(int? customisationId, int? sectionId); + GetObjectiveArrayData? GetObjectiveArray(int? customisationId, int? sectionId); } public class TrackerActionService : ITrackerActionService { - private readonly JsonSerializerSettings settings; private readonly ITutorialContentDataService tutorialContentDataService; public TrackerActionService(ITutorialContentDataService tutorialContentDataService) { this.tutorialContentDataService = tutorialContentDataService; - settings = new JsonSerializerSettings { ContractResolver = new LowercaseContractResolver() }; } - public string GetObjectiveArray(int? customisationId, int? sectionId) + public GetObjectiveArrayData? GetObjectiveArray(int? customisationId, int? sectionId) { if (!customisationId.HasValue || !sectionId.HasValue) { - return JsonConvert.SerializeObject(new { }); + return null; } var objectives = tutorialContentDataService .GetNonArchivedObjectivesBySectionAndCustomisationId(sectionId.Value, customisationId.Value) .ToList(); - object objectiveArrayObject = objectives.Any() ? (object)new { objectives } : new { }; - - return JsonConvert.SerializeObject(objectiveArrayObject, settings); - } - - private class LowercaseContractResolver : DefaultContractResolver - { - protected override string ResolvePropertyName(string propertyName) - { - return propertyName.ToLower(); - } + return objectives.Any() ? new GetObjectiveArrayData(objectives) : null; } } } diff --git a/DigitalLearningSolutions.Data/Services/TrackerService.cs b/DigitalLearningSolutions.Data/Services/TrackerService.cs index 19c2015d76..e98797c3bf 100644 --- a/DigitalLearningSolutions.Data/Services/TrackerService.cs +++ b/DigitalLearningSolutions.Data/Services/TrackerService.cs @@ -4,6 +4,8 @@ using DigitalLearningSolutions.Data.Enums; using DigitalLearningSolutions.Data.Models.Tracker; using Microsoft.Extensions.Logging; + using Newtonsoft.Json; + using Newtonsoft.Json.Serialization; public interface ITrackerService { @@ -14,11 +16,13 @@ public class TrackerService : ITrackerService { private readonly ILogger logger; private readonly ITrackerActionService trackerActionService; + private readonly JsonSerializerSettings settings; public TrackerService(ILogger logger, ITrackerActionService trackerActionService) { this.logger = logger; this.trackerActionService = trackerActionService; + settings = new JsonSerializerSettings { ContractResolver = new LowercaseContractResolver() }; } public string ProcessQuery(TrackerEndpointQueryParams query) @@ -32,7 +36,7 @@ public string ProcessQuery(TrackerEndpointQueryParams query) { if (Enum.TryParse(query.Action, true, out var action)) { - return action switch + var actionDataResult = action switch { TrackerEndpointAction.GetObjectiveArray => trackerActionService.GetObjectiveArray( query.CustomisationId, @@ -40,6 +44,8 @@ public string ProcessQuery(TrackerEndpointQueryParams query) ), _ => throw new ArgumentOutOfRangeException(), }; + + return ConvertToJsonString(actionDataResult); } return TrackerEndpointErrorResponse.InvalidAction; @@ -50,5 +56,23 @@ public string ProcessQuery(TrackerEndpointQueryParams query) return TrackerEndpointErrorResponse.UnexpectedException; } } + + private string ConvertToJsonString(ITrackerEndpointDataModel? foo) + { + if (foo == null) + { + return JsonConvert.SerializeObject(new { }); + } + + return JsonConvert.SerializeObject(foo, settings); + } + + private class LowercaseContractResolver : DefaultContractResolver + { + protected override string ResolvePropertyName(string propertyName) + { + return propertyName.ToLower(); + } + } } } From feb4f5ecfe0d63f95432f6cc79fe84a2dd846583 Mon Sep 17 00:00:00 2001 From: Jonathan Bloxsom Date: Tue, 16 Nov 2021 13:42:09 +0000 Subject: [PATCH 7/8] HEEDLS-408 SetValue code for int enumerable handler --- .../Mappers/EnumerableIntHandler.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/DigitalLearningSolutions.Data/Mappers/EnumerableIntHandler.cs b/DigitalLearningSolutions.Data/Mappers/EnumerableIntHandler.cs index 189a191489..dbd9666c89 100644 --- a/DigitalLearningSolutions.Data/Mappers/EnumerableIntHandler.cs +++ b/DigitalLearningSolutions.Data/Mappers/EnumerableIntHandler.cs @@ -1,16 +1,16 @@ -using System.Collections.Generic; - -namespace DigitalLearningSolutions.Data.Mappers +namespace DigitalLearningSolutions.Data.Mappers { using System.Data; using System.Linq; + using System.Collections.Generic; using Dapper; public class EnumerableIntHandler : SqlMapper.TypeHandler> { public override void SetValue(IDbDataParameter parameter, IEnumerable value) { - throw new System.NotImplementedException(); + parameter.DbType = DbType.String; + parameter.Value = string.Join(",", value); } public override IEnumerable Parse(object value) From cfa4f45d580c351225137baaa499724967f00a67 Mon Sep 17 00:00:00 2001 From: Jonathan Bloxsom Date: Fri, 19 Nov 2021 15:27:37 +0000 Subject: [PATCH 8/8] HEEDLS-408 review markups --- .../Services/TrackerActionServiceTests.cs | 21 +++++++++++++++---- .../Services/TrackerServiceTests.cs | 17 ++++++++------- .../Tracker/ITrackerEndpointDataModel.cs | 6 +----- ...eArrayData.cs => TrackerObjectiveArray.cs} | 4 ++-- .../Services/TrackerActionService.cs | 6 +++--- .../Services/TrackerService.cs | 12 ++++++----- 6 files changed, 40 insertions(+), 26 deletions(-) rename DigitalLearningSolutions.Data/Models/Tracker/{GetObjectiveArrayData.cs => TrackerObjectiveArray.cs} (62%) diff --git a/DigitalLearningSolutions.Data.Tests/Services/TrackerActionServiceTests.cs b/DigitalLearningSolutions.Data.Tests/Services/TrackerActionServiceTests.cs index ded004214f..b9cc687a0b 100644 --- a/DigitalLearningSolutions.Data.Tests/Services/TrackerActionServiceTests.cs +++ b/DigitalLearningSolutions.Data.Tests/Services/TrackerActionServiceTests.cs @@ -2,7 +2,6 @@ { using System.Collections.Generic; using DigitalLearningSolutions.Data.DataServices; - using DigitalLearningSolutions.Data.Models; using DigitalLearningSolutions.Data.Models.Tracker; using DigitalLearningSolutions.Data.Services; using FakeItEasy; @@ -27,13 +26,27 @@ public void GetObjectiveArray_returns_results_in_specified_json_format() { // given A.CallTo(() => dataService.GetNonArchivedObjectivesBySectionAndCustomisationId(A._, A._)) - .Returns(new[] { new Objective(1, new List { 6, 7, 8 }, 4), new Objective(2, new List { 17, 18, 19 }, 0) }); + .Returns( + new[] + { + new Objective(1, new List { 6, 7, 8 }, 4), + new Objective(2, new List { 17, 18, 19 }, 0), + } + ); // when var result = trackerActionService.GetObjectiveArray(1, 1); // then - result.Should().BeEquivalentTo(new GetObjectiveArrayData(new[] { new Objective(1, new List { 6, 7, 8 }, 4), new Objective(2, new List { 17, 18, 19 }, 0) })); + result.Should().BeEquivalentTo( + new TrackerObjectiveArray( + new[] + { + new Objective(1, new List { 6, 7, 8 }, 4), + new Objective(2, new List { 17, 18, 19 }, 0), + } + ) + ); } [Test] @@ -61,7 +74,7 @@ public void GetObjectiveArray_returns_null_if_parameter_missing( { // given A.CallTo(() => dataService.GetNonArchivedObjectivesBySectionAndCustomisationId(A._, A._)) - .Returns(new[] { new Objective(1, new List{1}, 9) }); + .Returns(new[] { new Objective(1, new List { 1 }, 9) }); // when var result = trackerActionService.GetObjectiveArray(customisationId, sectionId); diff --git a/DigitalLearningSolutions.Data.Tests/Services/TrackerServiceTests.cs b/DigitalLearningSolutions.Data.Tests/Services/TrackerServiceTests.cs index 34c2994d94..9aa6c33a82 100644 --- a/DigitalLearningSolutions.Data.Tests/Services/TrackerServiceTests.cs +++ b/DigitalLearningSolutions.Data.Tests/Services/TrackerServiceTests.cs @@ -2,7 +2,6 @@ { using System.Collections.Generic; using DigitalLearningSolutions.Data.Enums; - using DigitalLearningSolutions.Data.Models; using DigitalLearningSolutions.Data.Models.Tracker; using DigitalLearningSolutions.Data.Services; using FakeItEasy; @@ -56,20 +55,25 @@ public void ProcessQuery_with_GetObjectiveArray_action_passes_query_params() { // Given var query = new TrackerEndpointQueryParams - { Action = "GetObjectiveArray", CustomisationId = 1, SectionId = 1 }; + { Action = "GetObjectiveArray", CustomisationId = 1, SectionId = 2 }; // When trackerService.ProcessQuery(query); // Then - A.CallTo(() => actionService.GetObjectiveArray(1, 1)).MustHaveHappenedOnceExactly(); + A.CallTo(() => actionService.GetObjectiveArray(1, 2)).MustHaveHappenedOnceExactly(); } [Test] public void ProcessQuery_with_GetObjectiveArray_action_correctly_serialises_contentful_response() { // Given - var dataToReturn = new GetObjectiveArrayData(new[] { new Objective(1, new List{6,7,8}, 4), new Objective(2, new List{17,18,19}, 0) }); + var dataToReturn = new TrackerObjectiveArray( + new[] + { + new Objective(1, new List { 6, 7, 8 }, 4), new Objective(2, new List { 17, 18, 19 }, 0), + } + ); var expectedJson = "{\"objectives\":[{\"tutorialid\":1,\"interactions\":[6,7,8],\"possible\":4,\"myscore\":0}," + "{\"tutorialid\":2,\"interactions\":[17,18,19],\"possible\":0,\"myscore\":0}]}"; @@ -89,8 +93,7 @@ public void ProcessQuery_with_GetObjectiveArray_action_correctly_serialises_cont public void ProcessQuery_with_GetObjectiveArray_action_correctly_serialises_null_response() { // Given - GetObjectiveArrayData? dataToReturn = null; - var expectedJson = "{}"; + TrackerObjectiveArray? dataToReturn = null; var query = new TrackerEndpointQueryParams { Action = "GetObjectiveArray", CustomisationId = 1, SectionId = 1 }; @@ -100,7 +103,7 @@ public void ProcessQuery_with_GetObjectiveArray_action_correctly_serialises_null var result = trackerService.ProcessQuery(query); // Then - result.Should().Be(expectedJson); + result.Should().Be("{}"); } } } diff --git a/DigitalLearningSolutions.Data/Models/Tracker/ITrackerEndpointDataModel.cs b/DigitalLearningSolutions.Data/Models/Tracker/ITrackerEndpointDataModel.cs index 477544abb9..2bb49d5597 100644 --- a/DigitalLearningSolutions.Data/Models/Tracker/ITrackerEndpointDataModel.cs +++ b/DigitalLearningSolutions.Data/Models/Tracker/ITrackerEndpointDataModel.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace DigitalLearningSolutions.Data.Models.Tracker +namespace DigitalLearningSolutions.Data.Models.Tracker { interface ITrackerEndpointDataModel { diff --git a/DigitalLearningSolutions.Data/Models/Tracker/GetObjectiveArrayData.cs b/DigitalLearningSolutions.Data/Models/Tracker/TrackerObjectiveArray.cs similarity index 62% rename from DigitalLearningSolutions.Data/Models/Tracker/GetObjectiveArrayData.cs rename to DigitalLearningSolutions.Data/Models/Tracker/TrackerObjectiveArray.cs index d6b231f674..141b8c9790 100644 --- a/DigitalLearningSolutions.Data/Models/Tracker/GetObjectiveArrayData.cs +++ b/DigitalLearningSolutions.Data/Models/Tracker/TrackerObjectiveArray.cs @@ -2,9 +2,9 @@ namespace DigitalLearningSolutions.Data.Models.Tracker { - public class GetObjectiveArrayData : ITrackerEndpointDataModel + public class TrackerObjectiveArray : ITrackerEndpointDataModel { - public GetObjectiveArrayData(IEnumerable objectives) + public TrackerObjectiveArray(IEnumerable objectives) { Objectives = objectives; } diff --git a/DigitalLearningSolutions.Data/Services/TrackerActionService.cs b/DigitalLearningSolutions.Data/Services/TrackerActionService.cs index f651d3d69e..131a16c7cd 100644 --- a/DigitalLearningSolutions.Data/Services/TrackerActionService.cs +++ b/DigitalLearningSolutions.Data/Services/TrackerActionService.cs @@ -8,7 +8,7 @@ public interface ITrackerActionService { - GetObjectiveArrayData? GetObjectiveArray(int? customisationId, int? sectionId); + TrackerObjectiveArray? GetObjectiveArray(int? customisationId, int? sectionId); } public class TrackerActionService : ITrackerActionService @@ -20,7 +20,7 @@ public TrackerActionService(ITutorialContentDataService tutorialContentDataServi this.tutorialContentDataService = tutorialContentDataService; } - public GetObjectiveArrayData? GetObjectiveArray(int? customisationId, int? sectionId) + public TrackerObjectiveArray? GetObjectiveArray(int? customisationId, int? sectionId) { if (!customisationId.HasValue || !sectionId.HasValue) { @@ -31,7 +31,7 @@ public TrackerActionService(ITutorialContentDataService tutorialContentDataServi .GetNonArchivedObjectivesBySectionAndCustomisationId(sectionId.Value, customisationId.Value) .ToList(); - return objectives.Any() ? new GetObjectiveArrayData(objectives) : null; + return objectives.Any() ? new TrackerObjectiveArray(objectives) : null; } } } diff --git a/DigitalLearningSolutions.Data/Services/TrackerService.cs b/DigitalLearningSolutions.Data/Services/TrackerService.cs index e98797c3bf..8ae1afc631 100644 --- a/DigitalLearningSolutions.Data/Services/TrackerService.cs +++ b/DigitalLearningSolutions.Data/Services/TrackerService.cs @@ -15,14 +15,16 @@ public interface ITrackerService public class TrackerService : ITrackerService { private readonly ILogger logger; + + private readonly JsonSerializerSettings settings = new JsonSerializerSettings + { ContractResolver = new LowercaseContractResolver() }; + private readonly ITrackerActionService trackerActionService; - private readonly JsonSerializerSettings settings; public TrackerService(ILogger logger, ITrackerActionService trackerActionService) { this.logger = logger; this.trackerActionService = trackerActionService; - settings = new JsonSerializerSettings { ContractResolver = new LowercaseContractResolver() }; } public string ProcessQuery(TrackerEndpointQueryParams query) @@ -57,14 +59,14 @@ public string ProcessQuery(TrackerEndpointQueryParams query) } } - private string ConvertToJsonString(ITrackerEndpointDataModel? foo) + private string ConvertToJsonString(ITrackerEndpointDataModel? data) { - if (foo == null) + if (data == null) { return JsonConvert.SerializeObject(new { }); } - return JsonConvert.SerializeObject(foo, settings); + return JsonConvert.SerializeObject(data, settings); } private class LowercaseContractResolver : DefaultContractResolver