From 76409a19c8118b30e72780b26c5b4b33b3a55c85 Mon Sep 17 00:00:00 2001 From: binon Date: Tue, 20 Feb 2024 17:05:22 +0000 Subject: [PATCH 01/62] TD-2899 - New project accessibilty unit test --- .gitignore | 1 + .../AccessibilityTestsBase.cs | 67 +++++++++ .../BasicAccessibilityTests.cs | 39 +++++ ...rningHub.Nhs.WebUI.AutomatedUiTests.csproj | 55 +++++++ .../SeleniumServerFactory.cs | 138 ++++++++++++++++++ .../TestFixtures/AccessibilityTestsFixture.cs | 55 +++++++ .../AuthenticatedAccessibilityTestsFixture.cs | 37 +++++ .../MyAccountAccessibiltyTests.cs | 63 ++++++++ .../TestHelpers/ConfigurationHelper.cs | 26 ++++ .../TestHelpers/DriverHelper.cs | 111 ++++++++++++++ .../TestHelpers/LoginHelper.cs | 54 +++++++ .../appsettings.json | 7 + LearningHub.Nhs.WebUI.sln | 10 ++ 13 files changed, 663 insertions(+) create mode 100644 LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/AccessibilityTestsBase.cs create mode 100644 LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/BasicAccessibilityTests.cs create mode 100644 LearningHub.Nhs.WebUI.AutomatedUiTests/LearningHub.Nhs.WebUI.AutomatedUiTests.csproj create mode 100644 LearningHub.Nhs.WebUI.AutomatedUiTests/SeleniumServerFactory.cs create mode 100644 LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/AccessibilityTestsFixture.cs create mode 100644 LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/AuthenticatedAccessibilityTestsFixture.cs create mode 100644 LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/MyAccountAccessibiltyTests.cs create mode 100644 LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/ConfigurationHelper.cs create mode 100644 LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/DriverHelper.cs create mode 100644 LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/LoginHelper.cs create mode 100644 LearningHub.Nhs.WebUI.AutomatedUiTests/appsettings.json diff --git a/.gitignore b/.gitignore index 0afa44753..4119589ad 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,4 @@ obj /WebAPI/LearningHub.Nhs.Database/LearningHub.Nhs.Database.jfm /WebAPI/MigrationTool/LearningHub.Nhs.Migration.Staging.Database/LearningHub.Nhs.Migration.Staging.Database.dbmdl /WebAPI/MigrationTool/LearningHub.Nhs.Migration.Staging.Database/LearningHub.Nhs.Migration.Staging.Database.jfm +/LearningHub.Nhs.WebUI.AutomatedUiTests/appsettings.Development.json diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/AccessibilityTestsBase.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/AccessibilityTestsBase.cs new file mode 100644 index 000000000..2560b4326 --- /dev/null +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/AccessibilityTestsBase.cs @@ -0,0 +1,67 @@ +// +// Copyright (c) HEE.nhs.uk. +// + +namespace LearningHub.Nhs.WebUI.AutomatedUiTests.AccessibilityTests +{ + using FluentAssertions; + using LearningHub.Nhs.WebUI.AutomatedUiTests.TestFixtures; + using LearningHub.Nhs.WebUI.Startup; + using OpenQA.Selenium; + using Selenium.Axe; + using Xunit; + + /// + /// Accessibility Tests Base. + /// + [Collection("Selenium test collection")] + public class AccessibilityTestsBase + { + /// + /// Gets the base URL for the tests. + /// +#pragma warning disable SA1401 // Fields should be private + internal readonly string BaseUrl; +#pragma warning restore SA1401 // Fields should be private + + /// + /// Gets the WebDriver used for the tests. + /// +#pragma warning disable SA1401 // Fields should be private + internal readonly IWebDriver Driver; +#pragma warning restore SA1401 // Fields should be private + + /// + /// Initializes a new instance of the class. + /// + /// fixture. + public AccessibilityTestsBase(AccessibilityTestsFixture fixture) + { + this.BaseUrl = fixture.BaseUrl; + this.Driver = fixture.Driver; + } + + /// + /// Analyze Page Heading And Accessibility. + /// + /// Page Title. + public void AnalyzePageHeadingAndAccessibility(string pageTitle) + { + this.ValidatePageHeading(pageTitle); + + // then + var axeResult = new AxeBuilder(this.Driver).Analyze(); + axeResult.Violations.Should().BeEmpty(); + } + + /// + /// ValidatePageHeading. + /// + /// Page Title. + public void ValidatePageHeading(string pageTitle) + { + var h1Element = this.Driver.FindElement(By.TagName("h1")); + h1Element.Text.Should().BeEquivalentTo(pageTitle); + } + } +} diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/BasicAccessibilityTests.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/BasicAccessibilityTests.cs new file mode 100644 index 000000000..37c3da9a6 --- /dev/null +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/BasicAccessibilityTests.cs @@ -0,0 +1,39 @@ +// +// Copyright (c) HEE.nhs.uk. +// + +namespace LearningHub.Nhs.WebUI.AutomatedUiTests.AccessibilityTests +{ + using LearningHub.Nhs.WebUI.AutomatedUiTests.TestFixtures; + using Xunit; + + /// + /// BasicAccessibilityTests. + /// + public class BasicAccessibilityTests : AccessibilityTestsBase, IClassFixture> + { + /// + /// Initializes a new instance of the class. + /// BasicAccessibilityTests. + /// + /// fixture. + public BasicAccessibilityTests(AccessibilityTestsFixture fixture) + : base(fixture) + { + } + + [Theory] + [InlineData("/Home/Index", "A platform for learning and sharing resources")] + +#pragma warning disable SA1600 // Elements should be documented + public void PageHasNoAccessibilityErrors(string url, string pageTitle) +#pragma warning restore SA1600 // Elements should be documented + { + // when + this.Driver.Navigate().GoToUrl(this.BaseUrl + url); + + // then + this.AnalyzePageHeadingAndAccessibility(pageTitle); + } + } +} diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/LearningHub.Nhs.WebUI.AutomatedUiTests.csproj b/LearningHub.Nhs.WebUI.AutomatedUiTests/LearningHub.Nhs.WebUI.AutomatedUiTests.csproj new file mode 100644 index 000000000..719ae88fb --- /dev/null +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/LearningHub.Nhs.WebUI.AutomatedUiTests.csproj @@ -0,0 +1,55 @@ + + + + net6.0 + enable + enable + + false + + True + + + + + + + + + + PreserveNewest + true + PreserveNewest + + + PreserveNewest + true + PreserveNewest + + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/SeleniumServerFactory.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/SeleniumServerFactory.cs new file mode 100644 index 000000000..e01df94f3 --- /dev/null +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/SeleniumServerFactory.cs @@ -0,0 +1,138 @@ +// +// Copyright (c) HEE.nhs.uk. +// + +namespace LearningHub.Nhs.WebUI.AutomatedUiTests +{ + using System; + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using LearningHub.Nhs.WebUI.AutomatedUiTests.TestHelpers; + using LearningHub.Nhs.WebUI.Startup; + using Microsoft.AspNetCore; + using Microsoft.AspNetCore.Hosting; + using Microsoft.AspNetCore.Hosting.Server.Features; + using Microsoft.AspNetCore.Mvc.Testing; + using Microsoft.AspNetCore.TestHost; + using Microsoft.Extensions.Configuration; + using Microsoft.Extensions.Configuration.Json; + using Serilog; + + /// + /// SeleniumServerFactory. + /// + /// TStartup. + public class SeleniumServerFactory : WebApplicationFactory + where TStartup : class + { + /// + /// Root Uri. + /// +#pragma warning disable SA1401 // Fields should be private + public string RootUri; +#pragma warning restore SA1401 // Fields should be private + private IWebHost host; + + /// + /// Initializes a new instance of the class. + /// + public SeleniumServerFactory() + { + IConfiguration configuration = ConfigurationHelper.GetConfiguration(); + this.RootUri = configuration["LearningHubWebUiUrl"]; + + // We are consuming from IIS express + // this.CreateServer(this.CreateWebHostBuilder()); + // this.CreateClient(); + } + + /// + /// Create Server. + /// + /// builder. + /// TestServer. + protected sealed override TestServer CreateServer(IWebHostBuilder builder) + { + // base.ConfigureWebHost(builder); + + // Real TCP port + var host = builder + .UseStartup() + .UseSerilog() + .ConfigureAppConfiguration(configBuilder => + { + configBuilder.AddConfiguration(GetConfigForUiTests()); + }) + .Build(); + + host.Start(); + var rootUri = host.ServerFeatures.Get().Addresses.First(); + + // Fake Server to satisfy the return type + return new TestServer( + new WebHostBuilder() + .UseStartup() + .UseSerilog() + .ConfigureAppConfiguration( + configBuilder => { configBuilder.AddConfiguration(GetConfigForUiTests()); })); + } + + /// + /// Create Web Host Builder. + /// + /// CreateDefaultBuilder. + protected sealed override IWebHostBuilder CreateWebHostBuilder() + { + return WebHost.CreateDefaultBuilder(Array.Empty()) + .UseStartup() + .UseSerilog() + .UseUrls("http://127.0.0.1:0") + .ConfigureAppConfiguration( + configBuilder => + { + var jsonConfigSources = configBuilder.Sources + .Where(source => source.GetType() == typeof(JsonConfigurationSource)) + .ToList(); + + foreach (var jsonConfigSource in jsonConfigSources) + { + configBuilder.Sources.Remove(jsonConfigSource); + } + + configBuilder.AddConfiguration(GetConfigForUiTests()); + }); + } + + /// + /// Dispose implementation. + /// + /// disposing. + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (disposing) + { + this.host?.Dispose(); + } + } + + /// + /// Get Config ForUi Tests. + /// + /// ConfigurationBuilder. + private static IConfigurationRoot GetConfigForUiTests() + { + return new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.Development.json") + .AddEnvironmentVariables("DlsRefactor_") + .AddUserSecrets(typeof(Program).Assembly) + .Build(); + } + } +} diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/AccessibilityTestsFixture.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/AccessibilityTestsFixture.cs new file mode 100644 index 000000000..8238b893d --- /dev/null +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/AccessibilityTestsFixture.cs @@ -0,0 +1,55 @@ +// +// Copyright (c) HEE.nhs.uk. +// + +namespace LearningHub.Nhs.WebUI.AutomatedUiTests.TestFixtures +{ + using System; + using System.Data.SqlClient; + using LearningHub.Nhs.WebUI.AutomatedUiTests.TestHelpers; + using OpenQA.Selenium; + + /// + /// Represents a fixture for accessibility tests. + /// + /// The type of the startup class. + public class AccessibilityTestsFixture : IDisposable + where TStartup : class + { + /// + /// Gets the base URL for the tests. + /// +#pragma warning disable SA1401 // Fields should be private + internal readonly string BaseUrl; +#pragma warning restore SA1401 // Fields should be private + + /// + /// Gets the WebDriver used for the tests. + /// +#pragma warning disable SA1401 // Fields should be private + internal readonly IWebDriver Driver; +#pragma warning restore SA1401 // Fields should be private + + private readonly SeleniumServerFactory factory; + + /// + /// Initializes a new instance of the class. + /// + public AccessibilityTestsFixture() + { + this.factory = new SeleniumServerFactory(); + this.BaseUrl = this.factory.RootUri; + this.Driver = DriverHelper.CreateHeadlessChromeDriver(); + } + + /// + /// Dispose. + /// + public void Dispose() + { + this.Driver.Quit(); + this.Driver.Dispose(); + this.factory.Dispose(); + } + } +} diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/AuthenticatedAccessibilityTestsFixture.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/AuthenticatedAccessibilityTestsFixture.cs new file mode 100644 index 000000000..8fc395da5 --- /dev/null +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/AuthenticatedAccessibilityTestsFixture.cs @@ -0,0 +1,37 @@ +// +// Copyright (c) HEE.nhs.uk. +// + +namespace LearningHub.Nhs.WebUI.AutomatedUiTests.TestFixtures +{ + using LearningHub.Nhs.WebUI.AutomatedUiTests.TestHelpers; + using Microsoft.Extensions.Configuration; + + /// + /// AuthenticatedAccessibilityTestsFixture. + /// + /// TStartup. + public class AuthenticatedAccessibilityTestsFixture : AccessibilityTestsFixture + where TStartup : class + { + /// + /// Initializes a new instance of the class. + /// + public AuthenticatedAccessibilityTestsFixture() + { + IConfiguration configuration = ConfigurationHelper.GetConfiguration(); + string adminUsername = configuration["AdminUser:Username"]; + string adminPassword = configuration["AdminUser:Password"]; + this.Driver.LogUserInAsAdmin(this.BaseUrl, adminUsername, adminPassword); + } + + /// + /// Dispose. + /// + public new void Dispose() + { + this.Driver.LogOutUser(this.BaseUrl); + base.Dispose(); + } + } +} diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/MyAccountAccessibiltyTests.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/MyAccountAccessibiltyTests.cs new file mode 100644 index 000000000..3d80d035a --- /dev/null +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/MyAccountAccessibiltyTests.cs @@ -0,0 +1,63 @@ +// +// Copyright (c) HEE.nhs.uk. +// + +namespace LearningHub.Nhs.WebUI.AutomatedUiTests.TestFixtures +{ + using FluentAssertions; + using LearningHub.Nhs.WebUI.AutomatedUiTests.AccessibilityTests; + using LearningHub.Nhs.WebUI.AutomatedUiTests.TestHelpers; + using Selenium.Axe; + using Xunit; + + /// + /// MyAccountAccessibiltyTests. + /// + public class MyAccountAccessibiltyTests : AccessibilityTestsBase, + IClassFixture> + { + /// + /// Initializes a new instance of the class. + /// MyAccountAccessibiltyTests. + /// + /// fixture. + public MyAccountAccessibiltyTests(AuthenticatedAccessibilityTestsFixture fixture) + : base(fixture) + { + } + + /// + /// MyAccountPageHasNoAccessibilityErrors. + /// + [Fact] + public void MyAccountPageHasNoAccessibilityErrors() + { + // given + // this.Driver.LogUserInAsAdmin(this.BaseUrl); + const string myaccountsUrl = "/myaccount"; + + // when + this.Driver.Navigate().GoToUrl(this.BaseUrl + myaccountsUrl); + var result = new AxeBuilder(this.Driver).Analyze(); + + // then + CheckAccessibilityResult(result); + } + + private static void CheckAccessibilityResult(AxeResult result) + { + // Expect axe violations caused by having an aria-expanded attribute on two + // radio inputs and one checkbox input. + // The targets #course-filter-type-1, #course-filter-type-2 and #EndDate are + // nhs-tested components so ignore this violation. + result.Violations.Should().HaveCount(5); + + var violation = result.Violations[0]; + + violation.Id.Should().Be("landmark-contentinfo-is-top-level"); + violation.Nodes.Should().HaveCount(1); + violation.Nodes[0].Target.Should().HaveCount(1); + violation.Nodes[0].Target[0].Selector.Should().Be("footer > footer"); + } + } +} diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/ConfigurationHelper.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/ConfigurationHelper.cs new file mode 100644 index 000000000..675564e76 --- /dev/null +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/ConfigurationHelper.cs @@ -0,0 +1,26 @@ +// +// Copyright (c) HEE.nhs.uk. +// + +namespace LearningHub.Nhs.WebUI.AutomatedUiTests.TestHelpers +{ + using Microsoft.Extensions.Configuration; + + /// + /// ConfigurationHelper. + /// + public static class ConfigurationHelper + { + /// + /// GetConfiguration. + /// + /// IConfiguration. + public static IConfiguration GetConfiguration() + { + return new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.Development.json") + .Build(); + } + } +} diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/DriverHelper.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/DriverHelper.cs new file mode 100644 index 000000000..b1a23d3fb --- /dev/null +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/DriverHelper.cs @@ -0,0 +1,111 @@ +// +// Copyright (c) HEE.nhs.uk. +// + +namespace LearningHub.Nhs.WebUI.AutomatedUiTests.TestHelpers +{ + using OpenQA.Selenium; + using OpenQA.Selenium.Chrome; + using OpenQA.Selenium.Support.UI; + + /// + /// Driver Helper. + /// + public static class DriverHelper + { + /// + /// Create Headless ChromeDriver. + /// + /// Chrome Driver. + public static ChromeDriver CreateHeadlessChromeDriver() + { + var chromeOptions = new ChromeOptions(); + + // chromeOptions.AddArgument("--headless"); + return new ChromeDriver(chromeOptions); + } + + /// + /// Fill Text Input. + /// + /// WebDriver. + /// inputId. + /// inputText. + public static void FillTextInput(this IWebDriver driver, string inputId, string inputText) + { + var answer = driver.FindElement(By.Id(inputId)); + answer.Clear(); + answer.SendKeys(inputText); + } + + /// + /// ClickButtonByText. + /// + /// WebDriver. + /// text. + public static void ClickButtonByText(this IWebDriver driver, string text) + { + var addButton = driver.FindElement(By.XPath($"//button[.='{text}']")); + addButton.Click(); + } + + /// + /// ClickLinkContainingText. + /// + /// WebDriver. + /// text. + public static void ClickLinkContainingText(this IWebDriver driver, string text) + { + var foundLink = driver.FindElement(By.XPath($"//a[contains(., '{text}')]")); + foundLink.Click(); + } + + /// + /// SelectDropdownItemValue. + /// + /// WebDriver. + /// dropdownId. + /// selectedValue. + public static void SelectDropdownItemValue(this IWebDriver driver, string dropdownId, string selectedValue) + { + var dropdown = new SelectElement(driver.FindElement(By.Id(dropdownId))); + dropdown.SelectByValue(selectedValue); + } + + /// + /// SetCheckboxState. + /// + /// WebDriver. + /// inputId. + /// checkState. + public static void SetCheckboxState(this IWebDriver driver, string inputId, bool checkState) + { + var answer = driver.FindElement(By.Id(inputId)); + if (answer.Selected != checkState) + { + answer.Click(); + } + } + + /// + /// Submit Form. + /// + /// WebDriver. + public static void SubmitForm(this IWebDriver driver) + { + var selectPromptForm = driver.FindElement(By.TagName("form")); + selectPromptForm.Submit(); + } + + /// + /// Select Radio Option By Id. + /// + /// WebDriver. + /// radio Id. + public static void SelectRadioOptionById(this IWebDriver driver, string radioId) + { + var radioInput = driver.FindElement(By.Id(radioId)); + radioInput.Click(); + } + } +} diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/LoginHelper.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/LoginHelper.cs new file mode 100644 index 000000000..31aa5087f --- /dev/null +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/LoginHelper.cs @@ -0,0 +1,54 @@ +// +// Copyright (c) HEE.nhs.uk. +// +namespace LearningHub.Nhs.WebUI.AutomatedUiTests.TestHelpers +{ + using OpenQA.Selenium; + using OpenQA.Selenium.Support.UI; + + /// + /// LoginHelper. + /// + public static class LoginHelper + { + /// + /// Get LogUserInAsAdmin. + /// + /// WebDriver. + /// baseUrl. + /// adminName. + /// adminPassword. + public static void LogUserInAsAdmin(this IWebDriver driver, string baseUrl, string adminName, string adminPassword) + { + driver.Navigate().GoToUrl(baseUrl + "/Login"); + var username = driver.FindElement(By.Id("Username")); + username.SendKeys(adminName); + + var password = driver.FindElement(By.Id("Password")); + password.SendKeys(adminPassword); + + var submitButton = driver.FindElement(By.TagName("form")); + submitButton.Submit(); + } + + /// + /// LogOutUser. + /// + /// WebDriver. + /// baseUrl. + public static void LogOutUser(this IWebDriver driver, string baseUrl) + { + driver.Navigate().GoToUrl(baseUrl); + + // Wait for the element to be present on the page + WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10)); + IWebElement logoutLink = driver.FindElement(By.XPath("//a[@class='nhsuk-account__login--link' and @href='/Home/Logout']")); + + // Perform an action on the element (e.g., click) + logoutLink.Click(); + + // var submitButton = driver.FindElement(By.TagName("form")); + // submitButton.Submit(); + } + } +} diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/appsettings.json b/LearningHub.Nhs.WebUI.AutomatedUiTests/appsettings.json new file mode 100644 index 000000000..223c0c93f --- /dev/null +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/appsettings.json @@ -0,0 +1,7 @@ +{ + "LearningHubWebUiUrl": "", + "AdminUser": { + "Username": "", + "Password": "" + } +} \ No newline at end of file diff --git a/LearningHub.Nhs.WebUI.sln b/LearningHub.Nhs.WebUI.sln index 9cce69d55..5aea6885f 100644 --- a/LearningHub.Nhs.WebUI.sln +++ b/LearningHub.Nhs.WebUI.sln @@ -79,6 +79,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LearningHub.Nhs.ReportApi.S EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LearningHub.Nhs.ReportApi.Shared", "ReportAPI\LearningHub.Nhs.ReportApi.Shared\LearningHub.Nhs.ReportApi.Shared.csproj", "{6167F037-166C-4C5A-81BE-55618E77D4E8}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LearningHub.Nhs.WebUI.AutomatedUiTests", "LearningHub.Nhs.WebUI.AutomatedUiTests\LearningHub.Nhs.WebUI.AutomatedUiTests.csproj", "{A84EC50B-2B01-4819-A2B1-BD867B7595CA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -335,6 +337,14 @@ Global {6167F037-166C-4C5A-81BE-55618E77D4E8}.Release|Any CPU.Build.0 = Release|Any CPU {6167F037-166C-4C5A-81BE-55618E77D4E8}.Release|x64.ActiveCfg = Release|Any CPU {6167F037-166C-4C5A-81BE-55618E77D4E8}.Release|x64.Build.0 = Release|Any CPU + {A84EC50B-2B01-4819-A2B1-BD867B7595CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A84EC50B-2B01-4819-A2B1-BD867B7595CA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A84EC50B-2B01-4819-A2B1-BD867B7595CA}.Debug|x64.ActiveCfg = Debug|Any CPU + {A84EC50B-2B01-4819-A2B1-BD867B7595CA}.Debug|x64.Build.0 = Debug|Any CPU + {A84EC50B-2B01-4819-A2B1-BD867B7595CA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A84EC50B-2B01-4819-A2B1-BD867B7595CA}.Release|Any CPU.Build.0 = Release|Any CPU + {A84EC50B-2B01-4819-A2B1-BD867B7595CA}.Release|x64.ActiveCfg = Release|Any CPU + {A84EC50B-2B01-4819-A2B1-BD867B7595CA}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 21d1a689c8d1a36f98a583d1c85a3ca7b8123a87 Mon Sep 17 00:00:00 2001 From: binon Date: Tue, 27 Feb 2024 12:37:28 +0000 Subject: [PATCH 02/62] Refactored some of the code --- .../AccessibilityTestsBase.cs | 2 +- .../BasicAccessibilityTests.cs | 6 +- .../MyAccountAccessibiltyTests.cs | 21 ++-- .../SeleniumServerFactory.cs | 96 +------------------ .../TestFixtures/AccessibilityTestsFixture.cs | 13 +-- .../AuthenticatedAccessibilityTestsFixture.cs | 5 +- .../TestHelpers/LoginHelper.cs | 42 ++++++-- 7 files changed, 58 insertions(+), 127 deletions(-) rename LearningHub.Nhs.WebUI.AutomatedUiTests/{TestFixtures => AccessibilityTests}/MyAccountAccessibiltyTests.cs (69%) diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/AccessibilityTestsBase.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/AccessibilityTestsBase.cs index 2560b4326..28a445e13 100644 --- a/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/AccessibilityTestsBase.cs +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/AccessibilityTestsBase.cs @@ -35,7 +35,7 @@ public class AccessibilityTestsBase /// Initializes a new instance of the class. /// /// fixture. - public AccessibilityTestsBase(AccessibilityTestsFixture fixture) + public AccessibilityTestsBase(AccessibilityTestsFixture fixture) { this.BaseUrl = fixture.BaseUrl; this.Driver = fixture.Driver; diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/BasicAccessibilityTests.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/BasicAccessibilityTests.cs index 37c3da9a6..7a4352353 100644 --- a/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/BasicAccessibilityTests.cs +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/BasicAccessibilityTests.cs @@ -10,14 +10,14 @@ namespace LearningHub.Nhs.WebUI.AutomatedUiTests.AccessibilityTests /// /// BasicAccessibilityTests. /// - public class BasicAccessibilityTests : AccessibilityTestsBase, IClassFixture> + public class BasicAccessibilityTests : AccessibilityTestsBase, IClassFixture { /// /// Initializes a new instance of the class. /// BasicAccessibilityTests. /// /// fixture. - public BasicAccessibilityTests(AccessibilityTestsFixture fixture) + public BasicAccessibilityTests(AccessibilityTestsFixture fixture) : base(fixture) { } @@ -34,6 +34,8 @@ public void PageHasNoAccessibilityErrors(string url, string pageTitle) // then this.AnalyzePageHeadingAndAccessibility(pageTitle); + + this.Driver.Dispose(); } } } diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/MyAccountAccessibiltyTests.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/MyAccountAccessibiltyTests.cs similarity index 69% rename from LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/MyAccountAccessibiltyTests.cs rename to LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/MyAccountAccessibiltyTests.cs index 3d80d035a..530ec67d1 100644 --- a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/MyAccountAccessibiltyTests.cs +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/MyAccountAccessibiltyTests.cs @@ -2,10 +2,10 @@ // Copyright (c) HEE.nhs.uk. // -namespace LearningHub.Nhs.WebUI.AutomatedUiTests.TestFixtures +namespace LearningHub.Nhs.WebUI.AutomatedUiTests.AccessibilityTests { using FluentAssertions; - using LearningHub.Nhs.WebUI.AutomatedUiTests.AccessibilityTests; + using LearningHub.Nhs.WebUI.AutomatedUiTests.TestFixtures; using LearningHub.Nhs.WebUI.AutomatedUiTests.TestHelpers; using Selenium.Axe; using Xunit; @@ -14,26 +14,26 @@ namespace LearningHub.Nhs.WebUI.AutomatedUiTests.TestFixtures /// MyAccountAccessibiltyTests. /// public class MyAccountAccessibiltyTests : AccessibilityTestsBase, - IClassFixture> + IClassFixture { /// /// Initializes a new instance of the class. /// MyAccountAccessibiltyTests. /// /// fixture. - public MyAccountAccessibiltyTests(AuthenticatedAccessibilityTestsFixture fixture) + public MyAccountAccessibiltyTests(AuthenticatedAccessibilityTestsFixture fixture) : base(fixture) { } /// - /// MyAccountPageHasNoAccessibilityErrors. + /// MyAccount Page Has Accessibility Errors. /// [Fact] - public void MyAccountPageHasNoAccessibilityErrors() + public void MyAccountPageHasAccessibilityErrors() { // given - // this.Driver.LogUserInAsAdmin(this.BaseUrl); + // this.Driver.LogUserInAsAdmin(this.BaseUrl); const string myaccountsUrl = "/myaccount"; // when @@ -42,14 +42,13 @@ public void MyAccountPageHasNoAccessibilityErrors() // then CheckAccessibilityResult(result); + + this.Driver.LogOutUser(this.BaseUrl); } private static void CheckAccessibilityResult(AxeResult result) { - // Expect axe violations caused by having an aria-expanded attribute on two - // radio inputs and one checkbox input. - // The targets #course-filter-type-1, #course-filter-type-2 and #EndDate are - // nhs-tested components so ignore this violation. + // Expect axe violation result.Violations.Should().HaveCount(5); var violation = result.Violations[0]; diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/SeleniumServerFactory.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/SeleniumServerFactory.cs index e01df94f3..bf1274c84 100644 --- a/LearningHub.Nhs.WebUI.AutomatedUiTests/SeleniumServerFactory.cs +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/SeleniumServerFactory.cs @@ -26,113 +26,21 @@ namespace LearningHub.Nhs.WebUI.AutomatedUiTests /// /// SeleniumServerFactory. /// - /// TStartup. - public class SeleniumServerFactory : WebApplicationFactory - where TStartup : class + public class SeleniumServerFactory { /// /// Root Uri. /// #pragma warning disable SA1401 // Fields should be private public string RootUri; -#pragma warning restore SA1401 // Fields should be private - private IWebHost host; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public SeleniumServerFactory() { IConfiguration configuration = ConfigurationHelper.GetConfiguration(); this.RootUri = configuration["LearningHubWebUiUrl"]; - - // We are consuming from IIS express - // this.CreateServer(this.CreateWebHostBuilder()); - // this.CreateClient(); - } - - /// - /// Create Server. - /// - /// builder. - /// TestServer. - protected sealed override TestServer CreateServer(IWebHostBuilder builder) - { - // base.ConfigureWebHost(builder); - - // Real TCP port - var host = builder - .UseStartup() - .UseSerilog() - .ConfigureAppConfiguration(configBuilder => - { - configBuilder.AddConfiguration(GetConfigForUiTests()); - }) - .Build(); - - host.Start(); - var rootUri = host.ServerFeatures.Get().Addresses.First(); - - // Fake Server to satisfy the return type - return new TestServer( - new WebHostBuilder() - .UseStartup() - .UseSerilog() - .ConfigureAppConfiguration( - configBuilder => { configBuilder.AddConfiguration(GetConfigForUiTests()); })); - } - - /// - /// Create Web Host Builder. - /// - /// CreateDefaultBuilder. - protected sealed override IWebHostBuilder CreateWebHostBuilder() - { - return WebHost.CreateDefaultBuilder(Array.Empty()) - .UseStartup() - .UseSerilog() - .UseUrls("http://127.0.0.1:0") - .ConfigureAppConfiguration( - configBuilder => - { - var jsonConfigSources = configBuilder.Sources - .Where(source => source.GetType() == typeof(JsonConfigurationSource)) - .ToList(); - - foreach (var jsonConfigSource in jsonConfigSources) - { - configBuilder.Sources.Remove(jsonConfigSource); - } - - configBuilder.AddConfiguration(GetConfigForUiTests()); - }); - } - - /// - /// Dispose implementation. - /// - /// disposing. - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (disposing) - { - this.host?.Dispose(); - } - } - - /// - /// Get Config ForUi Tests. - /// - /// ConfigurationBuilder. - private static IConfigurationRoot GetConfigForUiTests() - { - return new ConfigurationBuilder() - .SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile("appsettings.Development.json") - .AddEnvironmentVariables("DlsRefactor_") - .AddUserSecrets(typeof(Program).Assembly) - .Build(); } } } diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/AccessibilityTestsFixture.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/AccessibilityTestsFixture.cs index 8238b893d..eac8a6d68 100644 --- a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/AccessibilityTestsFixture.cs +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/AccessibilityTestsFixture.cs @@ -4,17 +4,13 @@ namespace LearningHub.Nhs.WebUI.AutomatedUiTests.TestFixtures { - using System; - using System.Data.SqlClient; using LearningHub.Nhs.WebUI.AutomatedUiTests.TestHelpers; using OpenQA.Selenium; /// /// Represents a fixture for accessibility tests. /// - /// The type of the startup class. - public class AccessibilityTestsFixture : IDisposable - where TStartup : class + public class AccessibilityTestsFixture { /// /// Gets the base URL for the tests. @@ -30,14 +26,14 @@ public class AccessibilityTestsFixture : IDisposable internal readonly IWebDriver Driver; #pragma warning restore SA1401 // Fields should be private - private readonly SeleniumServerFactory factory; + private readonly SeleniumServerFactory factory; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public AccessibilityTestsFixture() { - this.factory = new SeleniumServerFactory(); + this.factory = new SeleniumServerFactory(); this.BaseUrl = this.factory.RootUri; this.Driver = DriverHelper.CreateHeadlessChromeDriver(); } @@ -49,7 +45,6 @@ public void Dispose() { this.Driver.Quit(); this.Driver.Dispose(); - this.factory.Dispose(); } } } diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/AuthenticatedAccessibilityTestsFixture.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/AuthenticatedAccessibilityTestsFixture.cs index 8fc395da5..6cb3a442f 100644 --- a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/AuthenticatedAccessibilityTestsFixture.cs +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/AuthenticatedAccessibilityTestsFixture.cs @@ -11,11 +11,10 @@ namespace LearningHub.Nhs.WebUI.AutomatedUiTests.TestFixtures /// AuthenticatedAccessibilityTestsFixture. /// /// TStartup. - public class AuthenticatedAccessibilityTestsFixture : AccessibilityTestsFixture - where TStartup : class + public class AuthenticatedAccessibilityTestsFixture : AccessibilityTestsFixture { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public AuthenticatedAccessibilityTestsFixture() { diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/LoginHelper.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/LoginHelper.cs index 31aa5087f..84846b6d7 100644 --- a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/LoginHelper.cs +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/LoginHelper.cs @@ -40,15 +40,43 @@ public static void LogOutUser(this IWebDriver driver, string baseUrl) { driver.Navigate().GoToUrl(baseUrl); - // Wait for the element to be present on the page - WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10)); - IWebElement logoutLink = driver.FindElement(By.XPath("//a[@class='nhsuk-account__login--link' and @href='/Home/Logout']")); + try + { + // Maximum time to wait for the element in seconds + int maxWaitTimeInSeconds = 10; - // Perform an action on the element (e.g., click) - logoutLink.Click(); + // Find the element using XPath + IWebElement logoutLink = null; - // var submitButton = driver.FindElement(By.TagName("form")); - // submitButton.Submit(); + for (int i = 0; i < maxWaitTimeInSeconds; i++) + { + try + { + logoutLink = driver.FindElement(By.XPath("//a[@class='nhsuk-account__login--link' and @href='/Home/Logout']")); + if (logoutLink.Displayed) + { + break; // Exit the loop if element is found and displayed + } + } + catch (NoSuchElementException) + { + // Element not found yet, wait for a second and try again + Thread.Sleep(1000); + } + } + + // Check if the element is found and displayed + if (logoutLink != null && logoutLink.Displayed) + { + // Perform an action on the element (e.g., click) + logoutLink.Click(); + } + } + finally + { + // Close the browser window + driver.Quit(); + } } } } From bfc8d58f43795c0c38d85daf1adf41ff5f56a519 Mon Sep 17 00:00:00 2001 From: binon Date: Tue, 27 Feb 2024 14:18:18 +0000 Subject: [PATCH 03/62] Td-2899 Minor updated chrome driver configuration --- .../AccessibilityTests/MyAccountAccessibiltyTests.cs | 2 +- .../TestHelpers/DriverHelper.cs | 2 +- .../TestHelpers/LoginHelper.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/MyAccountAccessibiltyTests.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/MyAccountAccessibiltyTests.cs index 530ec67d1..ca2f2127e 100644 --- a/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/MyAccountAccessibiltyTests.cs +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/MyAccountAccessibiltyTests.cs @@ -33,7 +33,6 @@ public MyAccountAccessibiltyTests(AuthenticatedAccessibilityTestsFixture fixture public void MyAccountPageHasAccessibilityErrors() { // given - // this.Driver.LogUserInAsAdmin(this.BaseUrl); const string myaccountsUrl = "/myaccount"; // when @@ -43,6 +42,7 @@ public void MyAccountPageHasAccessibilityErrors() // then CheckAccessibilityResult(result); + // Dispose driver this.Driver.LogOutUser(this.BaseUrl); } diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/DriverHelper.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/DriverHelper.cs index b1a23d3fb..cf1e771f6 100644 --- a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/DriverHelper.cs +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/DriverHelper.cs @@ -21,7 +21,7 @@ public static ChromeDriver CreateHeadlessChromeDriver() { var chromeOptions = new ChromeOptions(); - // chromeOptions.AddArgument("--headless"); + chromeOptions.AddArgument("--headless"); return new ChromeDriver(chromeOptions); } diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/LoginHelper.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/LoginHelper.cs index 84846b6d7..ebdabb30b 100644 --- a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/LoginHelper.cs +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/LoginHelper.cs @@ -52,7 +52,7 @@ public static void LogOutUser(this IWebDriver driver, string baseUrl) { try { - logoutLink = driver.FindElement(By.XPath("//a[@class='nhsuk-account__login--link' and @href='/Home/Logout']")); + logoutLink = driver.FindElement(By.CssSelector("a.nhsuk-account__login--link[href='/Home/Logout']")); if (logoutLink.Displayed) { break; // Exit the loop if element is found and displayed From 95d550f50dd3bc91ecfba9f0212b87e30d18c3e4 Mon Sep 17 00:00:00 2001 From: binon Date: Wed, 28 Feb 2024 13:13:53 +0000 Subject: [PATCH 04/62] TD-2899, Merged changes from CI, removed copyright and suppressed warning (globally) --- .../AccessibilityTests/AccessibilityTestsBase.cs | 8 -------- .../AccessibilityTests/BasicAccessibilityTests.cs | 8 +------- .../MyAccountAccessibiltyTests.cs | 10 +++------- .../GlobalSuppressions.cs | 13 +++++++++++++ .../SeleniumServerFactory.cs | 7 +------ .../TestFixtures/AccessibilityTestsFixture.cs | 8 -------- .../AuthenticatedAccessibilityTestsFixture.cs | 4 ---- .../TestHelpers/ConfigurationHelper.cs | 6 +----- .../TestHelpers/DriverHelper.cs | 6 +----- .../TestHelpers/LoginHelper.cs | 5 +---- 10 files changed, 21 insertions(+), 54 deletions(-) create mode 100644 LearningHub.Nhs.WebUI.AutomatedUiTests/GlobalSuppressions.cs diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/AccessibilityTestsBase.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/AccessibilityTestsBase.cs index 28a445e13..c8409d921 100644 --- a/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/AccessibilityTestsBase.cs +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/AccessibilityTestsBase.cs @@ -1,7 +1,3 @@ -// -// Copyright (c) HEE.nhs.uk. -// - namespace LearningHub.Nhs.WebUI.AutomatedUiTests.AccessibilityTests { using FluentAssertions; @@ -20,16 +16,12 @@ public class AccessibilityTestsBase /// /// Gets the base URL for the tests. /// -#pragma warning disable SA1401 // Fields should be private internal readonly string BaseUrl; -#pragma warning restore SA1401 // Fields should be private /// /// Gets the WebDriver used for the tests. /// -#pragma warning disable SA1401 // Fields should be private internal readonly IWebDriver Driver; -#pragma warning restore SA1401 // Fields should be private /// /// Initializes a new instance of the class. diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/BasicAccessibilityTests.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/BasicAccessibilityTests.cs index 7a4352353..eac9ac28a 100644 --- a/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/BasicAccessibilityTests.cs +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/BasicAccessibilityTests.cs @@ -1,8 +1,4 @@ -// -// Copyright (c) HEE.nhs.uk. -// - -namespace LearningHub.Nhs.WebUI.AutomatedUiTests.AccessibilityTests +namespace LearningHub.Nhs.WebUI.AutomatedUiTests.AccessibilityTests { using LearningHub.Nhs.WebUI.AutomatedUiTests.TestFixtures; using Xunit; @@ -25,9 +21,7 @@ public BasicAccessibilityTests(AccessibilityTestsFixture fixture) [Theory] [InlineData("/Home/Index", "A platform for learning and sharing resources")] -#pragma warning disable SA1600 // Elements should be documented public void PageHasNoAccessibilityErrors(string url, string pageTitle) -#pragma warning restore SA1600 // Elements should be documented { // when this.Driver.Navigate().GoToUrl(this.BaseUrl + url); diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/MyAccountAccessibiltyTests.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/MyAccountAccessibiltyTests.cs index ca2f2127e..7990f0fa8 100644 --- a/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/MyAccountAccessibiltyTests.cs +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/AccessibilityTests/MyAccountAccessibiltyTests.cs @@ -1,8 +1,4 @@ -// -// Copyright (c) HEE.nhs.uk. -// - -namespace LearningHub.Nhs.WebUI.AutomatedUiTests.AccessibilityTests +namespace LearningHub.Nhs.WebUI.AutomatedUiTests.AccessibilityTests { using FluentAssertions; using LearningHub.Nhs.WebUI.AutomatedUiTests.TestFixtures; @@ -49,9 +45,9 @@ public void MyAccountPageHasAccessibilityErrors() private static void CheckAccessibilityResult(AxeResult result) { // Expect axe violation - result.Violations.Should().HaveCount(5); + result.Violations.Should().HaveCount(6); - var violation = result.Violations[0]; + var violation = result.Violations[1]; violation.Id.Should().Be("landmark-contentinfo-is-top-level"); violation.Nodes.Should().HaveCount(1); diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/GlobalSuppressions.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/GlobalSuppressions.cs new file mode 100644 index 000000000..01866c145 --- /dev/null +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/GlobalSuppressions.cs @@ -0,0 +1,13 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "Allowed", Scope = "member", Target = "~F:LearningHub.Nhs.WebUI.AutomatedUiTests.SeleniumServerFactory.RootUri")] +[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "Allowed", Scope = "member", Target = "~F:LearningHub.Nhs.WebUI.AutomatedUiTests.TestFixtures.AccessibilityTestsFixture.Driver")] +[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "Allowed", Scope = "member", Target = "~F:LearningHub.Nhs.WebUI.AutomatedUiTests.TestFixtures.AccessibilityTestsFixture.BaseUrl")] +[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "Allowed", Scope = "member", Target = "~F:LearningHub.Nhs.WebUI.AutomatedUiTests.AccessibilityTests.AccessibilityTestsBase.BaseUrl")] +[assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "Allowed", Scope = "member", Target = "~F:LearningHub.Nhs.WebUI.AutomatedUiTests.AccessibilityTests.AccessibilityTestsBase.Driver")] +[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Regions allowed", Scope = "member", Target = "~M:LearningHub.Nhs.WebUI.AutomatedUiTests.AccessibilityTests.BasicAccessibilityTests.PageHasNoAccessibilityErrors(System.String,System.String)")] diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/SeleniumServerFactory.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/SeleniumServerFactory.cs index bf1274c84..19edcdaef 100644 --- a/LearningHub.Nhs.WebUI.AutomatedUiTests/SeleniumServerFactory.cs +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/SeleniumServerFactory.cs @@ -1,8 +1,4 @@ -// -// Copyright (c) HEE.nhs.uk. -// - -namespace LearningHub.Nhs.WebUI.AutomatedUiTests +namespace LearningHub.Nhs.WebUI.AutomatedUiTests { using System; using System; @@ -31,7 +27,6 @@ public class SeleniumServerFactory /// /// Root Uri. /// -#pragma warning disable SA1401 // Fields should be private public string RootUri; /// diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/AccessibilityTestsFixture.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/AccessibilityTestsFixture.cs index eac8a6d68..9ad27df2a 100644 --- a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/AccessibilityTestsFixture.cs +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/AccessibilityTestsFixture.cs @@ -1,7 +1,3 @@ -// -// Copyright (c) HEE.nhs.uk. -// - namespace LearningHub.Nhs.WebUI.AutomatedUiTests.TestFixtures { using LearningHub.Nhs.WebUI.AutomatedUiTests.TestHelpers; @@ -15,16 +11,12 @@ public class AccessibilityTestsFixture /// /// Gets the base URL for the tests. /// -#pragma warning disable SA1401 // Fields should be private internal readonly string BaseUrl; -#pragma warning restore SA1401 // Fields should be private /// /// Gets the WebDriver used for the tests. /// -#pragma warning disable SA1401 // Fields should be private internal readonly IWebDriver Driver; -#pragma warning restore SA1401 // Fields should be private private readonly SeleniumServerFactory factory; diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/AuthenticatedAccessibilityTestsFixture.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/AuthenticatedAccessibilityTestsFixture.cs index 6cb3a442f..130e05036 100644 --- a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/AuthenticatedAccessibilityTestsFixture.cs +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestFixtures/AuthenticatedAccessibilityTestsFixture.cs @@ -1,7 +1,3 @@ -// -// Copyright (c) HEE.nhs.uk. -// - namespace LearningHub.Nhs.WebUI.AutomatedUiTests.TestFixtures { using LearningHub.Nhs.WebUI.AutomatedUiTests.TestHelpers; diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/ConfigurationHelper.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/ConfigurationHelper.cs index 675564e76..660575162 100644 --- a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/ConfigurationHelper.cs +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/ConfigurationHelper.cs @@ -1,8 +1,4 @@ -// -// Copyright (c) HEE.nhs.uk. -// - -namespace LearningHub.Nhs.WebUI.AutomatedUiTests.TestHelpers +namespace LearningHub.Nhs.WebUI.AutomatedUiTests.TestHelpers { using Microsoft.Extensions.Configuration; diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/DriverHelper.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/DriverHelper.cs index cf1e771f6..9d467b946 100644 --- a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/DriverHelper.cs +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/DriverHelper.cs @@ -1,8 +1,4 @@ -// -// Copyright (c) HEE.nhs.uk. -// - -namespace LearningHub.Nhs.WebUI.AutomatedUiTests.TestHelpers +namespace LearningHub.Nhs.WebUI.AutomatedUiTests.TestHelpers { using OpenQA.Selenium; using OpenQA.Selenium.Chrome; diff --git a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/LoginHelper.cs b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/LoginHelper.cs index ebdabb30b..8f9786d27 100644 --- a/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/LoginHelper.cs +++ b/LearningHub.Nhs.WebUI.AutomatedUiTests/TestHelpers/LoginHelper.cs @@ -1,7 +1,4 @@ -// -// Copyright (c) HEE.nhs.uk. -// -namespace LearningHub.Nhs.WebUI.AutomatedUiTests.TestHelpers +namespace LearningHub.Nhs.WebUI.AutomatedUiTests.TestHelpers { using OpenQA.Selenium; using OpenQA.Selenium.Support.UI; From 34bbb27ec513b060c17ec17fcd17b2df9efc2e0a Mon Sep 17 00:00:00 2001 From: OluwatobiAwe Date: Thu, 16 Jan 2025 09:31:58 +0000 Subject: [PATCH 05/62] P1 endpoints for TD-5118 --- .../Configuration/AzureBlobSettings.cs | 18 + .../Configuration/AzureConfig.cs | 13 + .../Configuration/LearningHubConfig.cs | 5 + .../ICatalogueNodeVersionRepository.cs | 141 +++ .../Hierarchy/INodeResourceRepository.cs | 53 ++ .../Repositories/IBookmarkRepository.cs | 27 + .../Repositories/IGenericRepository.cs | 43 + .../Repositories/IProviderRepository.cs | 50 ++ .../Repositories/IResourceRepository.cs | 13 +- .../Repositories/IRoleUserGroupRepository.cs | 67 ++ .../Repositories/ITimezoneOffsetManager.cs | 22 + .../Resources/IBlockCollectionRepository.cs | 44 + .../ICaseResourceVersionRepository.cs | 18 + .../Repositories/Resources/IFileRepository.cs | 18 + .../Resources/IFileTypeRepository.cs | 18 + .../IGenericFileResourceVersionRepository.cs | 35 + .../IHtmlResourceVersionRepository.cs | 34 + .../Resources/IResourceVersionRepository.cs | 303 +++++++ .../IScormResourceVersionRepository.cs | 50 ++ .../IWebLinkResourceVersionRepository.cs | 26 + .../EntityFramework/LearningHubDbContext.cs | 26 + .../Helpers/DateTimeHelper.cs | 26 + .../Helpers/TextHelper.cs | 37 + .../Repositories/BookmarkRepository.cs | 57 ++ .../Repositories/GenericRepository.cs | 177 ++++ .../CatalogueNodeVersionRepository.cs | 393 +++++++++ .../Hierarchy/NodeResourceRepository.cs | 161 ++++ .../Repositories/MultipleResultSets.cs | 132 +++ .../Repositories/ProviderRepository.cs | 91 ++ .../Repositories/ResourceRepository.cs | 21 +- .../Resources/BlockCollectionRepository.cs | 443 ++++++++++ .../CaseResourceVersionRepository.cs | 35 + .../Repositories/Resources/FileRepository.cs | 35 + .../Resources/FileTypeRepository.cs | 35 + .../GenericFileResourceVersionRepository.cs | 62 ++ .../HtmlResourceVersionRepository.cs | 63 ++ .../Resources/ResourceVersionRepository.cs | 804 ++++++++++++++++++ .../ScormResourceVersionRepository.cs | 94 ++ .../WebLinkResourceVersionRepository.cs | 38 + .../Repositories/RoleUserGroupRepository.cs | 135 +++ .../Repositories/TimezoneOffsetManager.cs | 58 ++ .../Services/IBookmarkService.cs | 8 + .../Services/ICatalogueService.cs | 30 + .../Services/IFileTypeService.cs | 32 + .../Services/ILearningHubService.cs | 7 + .../Services/IProviderService.cs | 46 + .../Services/IResourceService.cs | 59 ++ .../Services/ISearchService.cs | 1 + .../LearningHub.Nhs.OpenApi.Services.csproj | 1 + .../Services/BookmarkService.cs | 25 +- .../Services/CatalogueService.cs | 257 +++++- .../Services/FileTypeService.cs | 98 +++ .../Services/LearningHubService.cs | 6 + .../Services/ProviderService.cs | 72 ++ .../Services/ResourceService.cs | 281 +++++- .../Services/SearchService.cs | 11 +- .../Configuration/ConfigurationExtensions.cs | 7 + .../Controllers/BookmarkController.cs | 14 + .../Controllers/CatalogueController.cs | 47 +- .../Controllers/ProviderController.cs | 67 ++ .../Controllers/ResourceController.cs | 108 ++- .../LearningHub.NHS.OpenAPI.csproj.user | 2 + .../LearningHub.Nhs.OpenApi/appsettings.json | 7 + 63 files changed, 5090 insertions(+), 17 deletions(-) create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/AzureBlobSettings.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/AzureConfig.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/ICatalogueNodeVersionRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/INodeResourceRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IBookmarkRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IGenericRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IProviderRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IRoleUserGroupRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/ITimezoneOffsetManager.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IBlockCollectionRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/ICaseResourceVersionRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IFileRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IFileTypeRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IGenericFileResourceVersionRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IHtmlResourceVersionRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceVersionRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IScormResourceVersionRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IWebLinkResourceVersionRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Helpers/DateTimeHelper.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Helpers/TextHelper.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/BookmarkRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/GenericRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Hierarchy/CatalogueNodeVersionRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Hierarchy/NodeResourceRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/MultipleResultSets.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/ProviderRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Resources/BlockCollectionRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Resources/CaseResourceVersionRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Resources/FileRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Resources/FileTypeRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Resources/GenericFileResourceVersionRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Resources/HtmlResourceVersionRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Resources/ResourceVersionRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Resources/ScormResourceVersionRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Resources/WebLinkResourceVersionRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/RoleUserGroupRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/TimezoneOffsetManager.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/IFileTypeService.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/IProviderService.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/FileTypeService.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/ProviderService.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi/Controllers/ProviderController.cs diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/AzureBlobSettings.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/AzureBlobSettings.cs new file mode 100644 index 000000000..12d6da133 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/AzureBlobSettings.cs @@ -0,0 +1,18 @@ +namespace LearningHub.Nhs.OpenApi.Models.Configuration +{ + /// + /// The AzureBlobSettings. + /// + public class AzureBlobSettings + { + /// + /// Gets or sets the connectionString. + /// + public string ConnectionString { get; set; } = null!; + + /// + /// Gets or sets the catalogue collection id. + /// + public string UploadContainer { get; set; } = null!; + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/AzureConfig.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/AzureConfig.cs new file mode 100644 index 000000000..a63513ff1 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/AzureConfig.cs @@ -0,0 +1,13 @@ +namespace LearningHub.Nhs.OpenApi.Models.Configuration +{ + /// + /// The AzureConfig. + /// + public class AzureConfig + { + /// + /// Gets or sets the azure blob settings. + /// + public AzureBlobSettings AzureBlobSettings { get; set; } = null!; + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/LearningHubConfig.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/LearningHubConfig.cs index 9e2086ee3..c1d37d814 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/LearningHubConfig.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Models/Configuration/LearningHubConfig.cs @@ -14,5 +14,10 @@ public class LearningHubConfig /// Gets or sets . /// public string LaunchResourceEndpoint { get; set; } = null!; + + /// + /// Gets or sets . + /// + public string ContentServerUrl { get; set; } = null!; } } diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/ICatalogueNodeVersionRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/ICatalogueNodeVersionRepository.cs new file mode 100644 index 000000000..e1137c520 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/ICatalogueNodeVersionRepository.cs @@ -0,0 +1,141 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Hierarchy +{ + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Catalogue; + using LearningHub.Nhs.Models.Dashboard; + using LearningHub.Nhs.Models.Entities.Hierarchy; + + /// + /// The ICatalogueNodeVersionRepository. + /// + public interface ICatalogueNodeVersionRepository : IGenericRepository + { + /// + /// The get catalogues. + /// + /// The catalogue ids. + /// A representing the result of the asynchronous operation. + Task> GetCatalogues(List catalogueIds); + + /// + /// The get catalogues. + /// + /// The . + Task> GetPublishedCatalogues(); + + /// + /// The get catalogues for user. + /// + /// The userId. + /// The . + IQueryable GetPublishedCataloguesForUserAsync(int userId); + + /// + /// The get basic catalogue. + /// + /// The catalogueNodeId. + /// The . + CatalogueNodeVersion GetBasicCatalogue(int catalogueNodeId); + + /// + /// The UpdateCatalogueAsync. + /// + /// The userId. + /// The catalogue view model. + /// The task. + Task UpdateCatalogueAsync(int userId, CatalogueViewModel vm); + + /// + /// The UpdateCatalogueOwnerAsync. + /// + /// The userId. + /// The catalogue owner view model. + /// The task. + Task UpdateCatalogueOwnerAsync(int userId, CatalogueOwnerViewModel vm); + + /// + /// The CreateCatalogueAsync. + /// + /// The userId. + /// The catalogue view model. + /// The catalogueNodeVersionId. + Task CreateCatalogueAsync(int userId, CatalogueViewModel vm); + + /// + /// Get Catlogue by reference. + /// + /// The reference. + /// The CatalogueViewModel. + Task GetCatalogueAsync(string reference); + + /// + /// Get list of Restricted Catalogue AccessRequests for the supplied request. + /// + /// The restrictedCatalogueAccessRequestsRequestViewModel. + /// A RestrictedCatalogueAccessRequestsViewModel. + List GetRestrictedCatalogueAccessRequests(RestrictedCatalogueAccessRequestsRequestViewModel restrictedCatalogueAccessRequestsRequestViewModel); + + /// + /// Get list of RestrictedCatalogueUsersRequestViewModel for the supplied request. + /// + /// The restrictedCatalogueUsersRequestViewModel. + /// A RestrictedCatalogueUsersViewModel. + RestrictedCatalogueUsersViewModel GetRestrictedCatalogueUsers(RestrictedCatalogueUsersRequestViewModel restrictedCatalogueUsersRequestViewModel); + + /// + /// The ShowCatalogue. + /// + /// The UserId. + /// The NodeId. + /// The Task. + Task ShowCatalogue(int userId, int nodeId); + + /// + /// Get Restricted Catalogue Summary for the supplied catalogue node id. + /// + /// The catalogueNodeId. + /// A RestrictedCatalogueUsersViewModel. + RestrictedCatalogueSummaryViewModel GetRestrictedCatalogueSummary(int catalogueNodeId); + + /// + /// Gets catalogues for dashboard based on type. + /// + /// The dashboard type. + /// The page Number. + /// The userId. + /// Catalogue totals and records. + (int TotalCount, List Catalogues) GetCatalogues(string dashboardType, int pageNumber, int userId); + + /// + /// Check if a Catalogue with a specific name exists or not. + /// + /// The catalogue name. + /// True if the catalogue exists, otherwise false. + Task ExistsAsync(string name); + + /// + /// Gets the Node Id for a particular catalogue name. + /// + /// The catalogue name. + /// The catalogue's node id. + Task GetNodeIdByCatalogueName(string catalogueName); + + /// + /// Gets the catalogues count in alphabet list. + /// + /// The userId. + /// The catalogues alphabet count list. + List GetAllCataloguesAlphaCount(int userId); + + /// + /// Gets catalogues based on filter character. + /// + /// The pageSize. + /// The filterChar. + /// The userId. + /// The catalogues. + Task> GetAllCataloguesAsync(int pageSize, string filterChar, int userId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/INodeResourceRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/INodeResourceRepository.cs new file mode 100644 index 000000000..dce087b47 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Hierarchy/INodeResourceRepository.cs @@ -0,0 +1,53 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Hierarchy +{ + using System.Collections.Generic; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Catalogue; + using LearningHub.Nhs.Models.Entities.Hierarchy; + using LearningHub.Nhs.Models.Enums; + using LearningHub.Nhs.Models.Hierarchy; + + /// + /// The NodeResourceRepository interface. + /// + public interface INodeResourceRepository : IGenericRepository + { + /// + /// The get by resource id async. + /// + /// The resource id. + /// The . + Task> GetByResourceIdAsync(int resourceId); + + /// + /// The get catalogue locations for resource. + /// + /// The resource id. + /// The CatalogueLocationViewModel list. + List GetCatalogueLocationsForResource(int resourceId); + + /// + /// GetResourcesAsync. + /// + /// nodeId. + /// catalogueOrder. + /// offset. + /// A representing the result of the asynchronous operation. + Task GetResourcesAsync(int nodeId, CatalogueOrder catalogueOrder, int offset); + + /// + /// Get All published resources id. + /// + /// The . + Task> GetAllPublishedResourceAsync(); + + /// + /// Creates or updates the NodeResource record for a draft resource in a node. + /// + /// The nodeId. + /// The resourceId. + /// The userId. + /// The . + Task CreateOrUpdateAsync(int nodeId, int resourceId, int userId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IBookmarkRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IBookmarkRepository.cs new file mode 100644 index 000000000..1244736c5 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IBookmarkRepository.cs @@ -0,0 +1,27 @@ +namespace LearningHub.Nhs.Repository.Interface +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities; + using LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories; + + /// + /// The IRoadmapRepository. + /// + public interface IBookmarkRepository : IGenericRepository + { + /// + /// The GetById. + /// + /// The bookmarkId. + /// The . + Task GetById(int bookmarkId); + + /// + /// DeleteFolder. + /// + /// bookmarkId. + /// userId. + /// A representing the result of the asynchronous operation. + Task DeleteFolder(int bookmarkId, int userId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IGenericRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IGenericRepository.cs new file mode 100644 index 000000000..25969ea32 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IGenericRepository.cs @@ -0,0 +1,43 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories +{ + using System.Linq; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities; + + /// + /// The GenericRepository interface. + /// + /// Imput type. + public interface IGenericRepository + where TEntity : EntityBase + { + /// + /// The get all. + /// + /// The . + IQueryable GetAll(); + + /// + /// The create async. + /// + /// The user id. + /// The entity. + /// The . + Task CreateAsync(int userId, TEntity entity); + + /// + /// The update async. + /// + /// The user id. + /// The entity. + /// The . + Task UpdateAsync(int userId, TEntity entity); + + /// + /// The update. + /// + /// The user id. + /// The entity. + void Update(int userId, TEntity entity); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IProviderRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IProviderRepository.cs new file mode 100644 index 000000000..2c797be4f --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IProviderRepository.cs @@ -0,0 +1,50 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories +{ + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities; + using LearningHub.Nhs.Models.Entities.Resource; + + /// + /// The ProviderRepository interface. + /// + public interface IProviderRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + + /// + /// The get by id async. + /// + /// The id. + /// The include children. + /// The . + Task GetByIdAsync(int id, bool includeChildren); + + /// + /// The get by user id async. + /// + /// The user id. + /// The . + IQueryable GetProvidersByUserIdAsync(int userId); + + /// + /// The get by resource version id async. + /// + /// The resource version id. + /// The . + IQueryable GetProvidersByResourceIdAsync(int resourceVersionId); + + /// + /// The get by node version id async. + /// + /// The node version id. + /// The . + IQueryable GetProvidersByCatalogueIdAsync(int nodeVersionId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IResourceRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IResourceRepository.cs index 52b3dee08..ed16ae016 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IResourceRepository.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IResourceRepository.cs @@ -15,14 +15,14 @@ public interface IResourceRepository /// /// . /// Resources with details. - public Task> GetResourcesFromIds(IEnumerable resourceIds); + Task> GetResourcesFromIds(IEnumerable resourceIds); /// /// Gets resource references by their original resource reference Ids. /// /// . /// Resource references. - public Task> GetResourceReferencesByOriginalResourceReferenceIds( + Task> GetResourceReferencesByOriginalResourceReferenceIds( IEnumerable originalResourceReferenceIds); /// @@ -37,6 +37,13 @@ public Task> GetResourceReferencesByOriginalResou /// GetAchievedCertificatedResourceIds /// /// . - public Task> GetAchievedCertificatedResourceIds(int currentUserId); + Task> GetAchievedCertificatedResourceIds(int currentUserId); + + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); } } diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IRoleUserGroupRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IRoleUserGroupRepository.cs new file mode 100644 index 000000000..7fd1426a1 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IRoleUserGroupRepository.cs @@ -0,0 +1,67 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories +{ + using System.Collections.Generic; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities; + using LearningHub.Nhs.Models.User; + + /// + /// The IRoleUserGroupRepository interface. + /// + public interface IRoleUserGroupRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + + /// + /// The get by catalogueNodeId async. + /// + /// The role id. + /// The userGroup id. + /// The scope id. + /// The . + Task GetByRoleIdUserGroupIdScopeIdAsync(int roleId, int userGroupId, int scopeId); + + /// + /// The get by role id and catalogue id. + /// + /// The role id. + /// The catalogue node id. + /// The . + Task> GetByRoleIdCatalogueId(int roleId, int catalogueNodeId); + + /// + /// The get by role id and catalogue id that has users. + /// + /// The role id. + /// The catalogue node id. + /// The . + Task> GetByRoleIdCatalogueIdWithUsers(int roleId, int catalogueNodeId); + + /// + /// Get list of RoleUserGroupViewModel for a supplied User Group. + /// + /// The userGroupId. + /// A list of RoleUserGroupViewModel. + Task> GetRoleUserGroupViewModelsByUserGroupId(int userGroupId); + + /// + /// Get list of RoleUserGroupViewModel for a supplied User Group. + /// + /// The userGroupId. + /// A list of RoleUserGroupViewModel. + Task> GetRoleUserGroupViewModelsByUserId(int userId); + + /// + /// The get all for search. + /// + /// The catalogueNodeId. + /// The userId. + /// The . + Task> GetAllforSearch(int catalogueNodeId, int userId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/ITimezoneOffsetManager.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/ITimezoneOffsetManager.cs new file mode 100644 index 000000000..15f4b8bf1 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/ITimezoneOffsetManager.cs @@ -0,0 +1,22 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories +{ + using System; + + /// + /// The TimezoneOffsetManager interface. + /// + public interface ITimezoneOffsetManager + { + /// + /// Gets User Timezone Offset. + /// + int? UserTimezoneOffset { get; } + + /// + /// Converts a UTC DateTimeOffset to the timezone of the current user. + /// + /// The UTC DateTimeOffset to convert. + /// . + DateTimeOffset ConvertToUserTimezone(DateTimeOffset utcDate); + } +} \ No newline at end of file diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IBlockCollectionRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IBlockCollectionRepository.cs new file mode 100644 index 000000000..33762762b --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IBlockCollectionRepository.cs @@ -0,0 +1,44 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource.Blocks; + using LearningHub.Nhs.Models.Enums; + + /// + /// The BlockRepository interface. + /// + public interface IBlockCollectionRepository : IGenericRepository + { + /// + /// Delete the Block Collection. + /// + /// The User Id. + /// The Block Collection Id. + /// The . + Task DeleteBlockCollection(int userId, int blockCollectionId); + + /// + /// Gets the Block Collection. + /// + /// The Block Collection Id. + /// The . + Task GetBlockCollection(int? blockCollectionId); + + /// + /// Gets the Question blocks for a particular blockCollectionId. + /// + /// The Block Collection Id. + /// The . + Task> GetQuestionBlocks(int blockCollectionId); + + /// + /// Gets the Case, AssessmentContent, AssessmentGuidance Block Collections (including child Blocks, TextBlocks, WholeSlideImageBlocks and Files) except that of the provided resource version. + /// + /// The excluded ResourceVersion Id. + /// The resource type. + /// The . + Task GetResourceBlockCollectionsFileAsync(int excludeResourceVersionId, ResourceTypeEnum resourceTypeEnum); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/ICaseResourceVersionRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/ICaseResourceVersionRepository.cs new file mode 100644 index 000000000..af9c600bc --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/ICaseResourceVersionRepository.cs @@ -0,0 +1,18 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + + /// + /// The CaseResourceVersionRepository interface. + /// + public interface ICaseResourceVersionRepository : IGenericRepository + { + /// + /// The get by Resource Version Id async. + /// + /// The Resource Version Id. + /// The . + Task GetByResourceVersionIdAsync(int resourceVersionId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IFileRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IFileRepository.cs new file mode 100644 index 000000000..7baf4ed9d --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IFileRepository.cs @@ -0,0 +1,18 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + + /// + /// The FileRepository interface. + /// + public interface IFileRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IFileTypeRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IFileTypeRepository.cs new file mode 100644 index 000000000..33ca0ec23 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IFileTypeRepository.cs @@ -0,0 +1,18 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + + /// + /// The FileTypeRepository interface. + /// + public interface IFileTypeRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IGenericFileResourceVersionRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IGenericFileResourceVersionRepository.cs new file mode 100644 index 000000000..e4e449b9d --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IGenericFileResourceVersionRepository.cs @@ -0,0 +1,35 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + using LearningHub.Nhs.Models.Resource; + + /// + /// The GenericFileResourceVersionRepository interface. + /// + public interface IGenericFileResourceVersionRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + + /// + /// The get by resource version id async. + /// + /// The id. + /// Allows deleted items to be returned. + /// The . + Task GetByResourceVersionIdAsync(int id, bool includeDeleted = false); + + /// + /// The get by resource version id. + /// + /// The id. + /// Allows deleted items to be returned. + /// The generic file resource version. + GenericFileResourceVersion GetByResourceVersionId(int id, bool includeDeleted = false); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IHtmlResourceVersionRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IHtmlResourceVersionRepository.cs new file mode 100644 index 000000000..3def3f0b9 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IHtmlResourceVersionRepository.cs @@ -0,0 +1,34 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + + /// + /// The GenericFileResourceVersionRepository interface. + /// + public interface IHtmlResourceVersionRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + + /// + /// The get by resource version id async. + /// + /// The id. + /// Allows deleted items to be returned. + /// The . + Task GetByResourceVersionIdAsync(int id, bool includeDeleted = false); + + /// + /// The get by resource version id. + /// + /// The id. + /// Allows deleted items to be returned. + /// The html resource version. + HtmlResourceVersion GetByResourceVersionId(int id, bool includeDeleted = false); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceVersionRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceVersionRepository.cs new file mode 100644 index 000000000..b2e296682 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceVersionRepository.cs @@ -0,0 +1,303 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Dashboard; + using LearningHub.Nhs.Models.Entities.Resource; + using LearningHub.Nhs.Models.Enums; + using LearningHub.Nhs.Models.Resource; + using LearningHub.Nhs.Models.Resource.ResourceDisplay; + + /// + /// The ResourceVersionRepository interface. + /// + public interface IResourceVersionRepository : IGenericRepository + { + /// + /// The get all admin search. + /// + /// The userId. + /// The . + IQueryable GetAllAdminSearch(int userId); + + /// + /// The get by id async. + /// + /// The id. + /// The include events. + /// The . + Task GetByIdAsync(int id, bool includeEvents); + + /// + /// The get basic by id async. + /// + /// The id. + /// The . + Task GetBasicByIdAsync(int id); + + /// + /// The contribution totals. + /// + /// The user id. + /// The catalogue id. + /// The MyContributionsTotalsViewModel. + MyContributionsTotalsViewModel GetMyContributionTotals(int userId, int catalogueId); + + /// + /// Get resource cards. + /// + /// The include events. + /// The . + Task> GetResourceCards(bool includeEvents); + + /// + /// The get current for resource async. + /// + /// The resource Id. + /// The . + Task GetCurrentForResourceAsync(int resourceId); + + /// + /// The get current resource for resourceid async. + /// + /// The resource Id. + /// The . + Task GetCurrentResourceDetailsAsync(int resourceId); + + /// + /// The get resource version details by id async. + /// + /// The resourceVersionId. + /// The . + Task GetByResourceVersionByIdAsync(int resourceVersionId); + + /// + /// The check dev id already exists in the table async. + /// + /// The devId. + /// The . + Task DoesDevIdExistsAync(string devId); + + /// + /// The get current published for resource async. + /// + /// The resource id. + /// The . + Task GetCurrentPublicationForResourceAsync(int resourceId); + + /// + /// The get current for resource reference id async. + /// + /// The resource reference id. + /// The . + Task GetCurrentForResourceReferenceIdAsync(int resourceReferenceId); + + /// + /// The get current resource for resource reference id async. + /// + /// The resource reference id. + /// The . + Task GetCurrentResourceForResourceReferenceIdAsync(int resourceReferenceId); + + /// + /// The get current published for resource reference id async. + /// + /// The resource reference id. + /// The . + Task GetCurrentPublicationForResourceReferenceIdAsync(int resourceReferenceId); + + /// + /// The set resource type. + /// + /// The resource version id. + /// The resource type enum. + /// The user id. + void SetResourceType(int resourceVersionId, ResourceTypeEnum resourceTypeEnum, int userId); + + /// + /// Gets the resource type of a ResourceVersion. + /// + /// The resourceVersionId. + /// The . + Task GetResourceType(int resourceVersionId); + + /// + /// The publish. + /// + /// The resource version id. + /// The is major revision. + /// The notes. + /// The publication date. Set to null if not giving the resource a publication date in the past. This parameter is intended for use by the migration tool. + /// The user id. + /// The publication id. + int Publish(int resourceVersionId, bool isMajorRevision, string notes, DateTimeOffset? publicationDate, int userId); + + /// + /// The unpublish. + /// + /// The resource version id. + /// The details. + /// The user id. + void Unpublish(int resourceVersionId, string details, int userId); + + /// + /// The revert to draft. + /// + /// The resource version id. + /// The user id. + void RevertToDraft(int resourceVersionId, int userId); + + /// + /// Delete a resource version. + /// + /// The resource version id. + /// The user id. + void Delete(int resourceVersionId, int userId); + + /// + /// The get resource versions. + /// + /// The resource id. + /// The . + Task> GetResourceVersionsAsync(int resourceId); + + ///// + ///// The create activity. + ///// + ///// + ///// The user id. + ///// + ///// + ///// The resource version id. + ///// + ///// + ///// The activity status enum. + ///// + ///// + ///// The activity start. + ///// + ///// + ///// The activity end. + ///// + ////void CreateActivity( + // int userId, + // int resourceVersionId, + // ActivityStatusEnum activityStatusEnum, + // DateTimeOffset activityStart, + // DateTimeOffset activityEnd); + + /// + /// Check if a user has completed the activity corresponding to the resource version. + /// + /// The user id. + /// The resource version id. + /// A boolean. + Task HasUserCompletedActivity(int userId, int resourceVersionId); + + /// + /// Return whether a version at a specific status currently exists. + /// + /// The resource id. + /// The resource version status id. + /// Whether the resource version exists or not. + Task DoesVersionExist(int resourceId, VersionStatusEnum draft); + + /// + /// Return whether a version at a specific status currently exists. + /// + /// The resource id. + /// The user id. + /// Returns the new resource version id. + Task CreateNextVersionAsync(int resourceId, int userId); + + /// + /// Duplicates a resource version. + /// + /// The resource id. + /// The user id. + /// Returns the new resource version id. + Task CreateDuplicateVersionAsync(int resourceId, int userId); + + /// + /// The publishing. + /// + /// The resource version id. + /// The user id. + void Publishing(int resourceVersionId, int userId); + + /// + /// "Failed to publish". + /// + /// The resource version id. + /// The user id. + void FailedToPublish(int resourceVersionId, int userId); + + /// + /// The submit publishing. + /// + /// The resource version id. + /// The user id. + void SubmitForPublishing(int resourceVersionId, int userId); + + /// + /// Create resource version event. + /// + /// resourceVersionId. + /// resourceVersionEventType. + /// details. + /// user id. + void CreateResourceVersionEvent(int resourceVersionId, ResourceVersionEventTypeEnum resourceVersionEventType, string details, int userId); + + /// + /// Get Contributions. + /// + /// The userId. + /// The ResourceContributionsRequestViewModel. + /// A list of contributed resources. + List GetContributions(int userId, ResourceContributionsRequestViewModel resourceContributionsRequestViewModel); + + /// + /// The GetResourceVersionForIdList. + /// + /// List of resource version ids. + /// The resource version list. + Task> GetResourceVersionsForSearchSubmission(List resourceVersionIds); + + /// + /// Gets resources for dashboard based on type. + /// + /// The dashboard type. + /// The page Number. + /// The userId. + /// resources. + (int resourceCount, List resources) GetResources(string dashboardType, int pageNumber, int userId); + + /// + /// Copy the blocks from source to destination. + /// + /// The user id. + /// The source block collection id. + /// The destination block collection id. + /// The blocks to be duplicated. + /// The . + Task FractionalDuplication(int userId, int sourceBlockCollectionId, int destinationBlockCollectionId, List blocks); + + /// + /// Gets external content details. + /// + /// resourceVersionId. + /// userId. + /// A representing the result of the asynchronous operation. + Task GetExternalContentDetails(int resourceVersionId, int userId); + + /// + /// To update dev id details. + /// + /// The userId. + /// The resourceVersionDevIdViewModel. + /// The . + Task UpdateDevIdAsync(int userId, ResourceVersionDevIdViewModel resourceVersionDevIdViewModel); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IScormResourceVersionRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IScormResourceVersionRepository.cs new file mode 100644 index 000000000..2fefbe739 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IScormResourceVersionRepository.cs @@ -0,0 +1,50 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Collections.Generic; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + using LearningHub.Nhs.Models.Resource; + + /// + /// The ScormResourceVersionRepository interface. + /// + public interface IScormResourceVersionRepository : IGenericRepository + { + /// + /// The get by id async. + /// + /// The id. + /// The . + Task GetByIdAsync(int id); + + /// + /// The get by resource version id async. + /// + /// The id. + /// The includeDeleted. + /// The . + Task GetByResourceVersionIdAsync(int id, bool includeDeleted = false); + + /// + /// Gets the content details for a particular Learning Hub external reference (guid). + /// + /// The external reference (guid). + /// A ContentServerViewModel. + Task GetContentServerDetailsByLHExternalReference(string externalReference); + + /// + /// Gets the SCORM content details for a particular historic external URL. These historic URLs have to be supported to + /// allow historic ESR links on migrated resources to continue to work. + /// + /// The external Url. + /// A ContentServerViewModel. + Task GetScormContentServerDetailsByHistoricExternalUrl(string externalUrl); + + /// + /// GetExternalReferenceByResourceId. + /// + /// Resource id. + /// External Reference. + Task> GetExternalReferenceByResourceId(int resourceId); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IWebLinkResourceVersionRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IWebLinkResourceVersionRepository.cs new file mode 100644 index 000000000..7eea30ee3 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IWebLinkResourceVersionRepository.cs @@ -0,0 +1,26 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + + /// + /// The WebLinkResourceVersionRepository interface. + /// + public interface IWebLinkResourceVersionRepository + { + /// + /// The get by resource version id async. + /// + /// The id. + /// The . + Task GetByResourceVersionIdAsync(int id); + + /// + /// The update async. + /// + /// The user id. + /// The web link resource version. + /// The . + Task UpdateAsync(int userId, WebLinkResourceVersion webLinkResourceVersion); + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/LearningHubDbContext.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/LearningHubDbContext.cs index c1ebf721b..be0e6556f 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/LearningHubDbContext.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/LearningHubDbContext.cs @@ -602,6 +602,32 @@ public LearningHubDbContext(LearningHubDbContextOptions options) /// public virtual DbSet VideoAsset { get; set; } + + /// + /// Gets or sets UserBookmark. + /// + public virtual DbSet UserBookmark { get; set; } + + /// + /// Gets or sets the AllCatalogueAlphabet. + /// + public virtual DbSet AllCatalogueAlphabetModel { get; set; } + + /// + /// Gets or sets the AllCatalogueAlphabet. + /// + public virtual DbSet AllCatalogueViewModel { get; set; } + + /// + /// Gets or sets Provider. + /// + public virtual DbSet Provider { get; set; } + + /// + /// Gets or sets the html resource version. + /// + public virtual DbSet HtmlResourceVersion { get; set; } + /// /// The on model creating. /// diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Helpers/DateTimeHelper.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Helpers/DateTimeHelper.cs new file mode 100644 index 000000000..7133033da --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Helpers/DateTimeHelper.cs @@ -0,0 +1,26 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Helpers +{ + using System; + + /// + /// Extension methods for DateTime class. + /// + public static class DateTimeHelper + { + /// + /// Returns the date of the first day of the week, based on the day of week passed in. + /// + /// The datetime. + /// The first day of the week. + /// The date of the first day of the week. + public static DateTime FirstDateInWeek(this DateTime dt, DayOfWeek weekStartDay) + { + while (dt.DayOfWeek != weekStartDay) + { + dt = dt.AddDays(-1); + } + + return dt; + } + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Helpers/TextHelper.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Helpers/TextHelper.cs new file mode 100644 index 000000000..e14f17754 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Helpers/TextHelper.cs @@ -0,0 +1,37 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Helpers +{ + /// + /// The text helper. + /// + public static class TextHelper + { + /// + /// The Combine Date Components method. + /// + /// The authoed year. + /// The authored month. + /// The authored day of month. + /// The formatted date. + public static string CombineDateComponents(int? authoredYear, int? authoredMonth, int? authoredDayOfMonth) + { + string returnText = string.Empty; + if (authoredYear > 0) + { + returnText = authoredYear.ToString(); + + if (authoredMonth > 0) + { + string[] monthName = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + returnText = monthName[(int)authoredMonth - 1] + " " + returnText; + + if (authoredDayOfMonth > 0) + { + returnText = authoredDayOfMonth.ToString() + " " + returnText; + } + } + } + + return returnText; + } + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/BookmarkRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/BookmarkRepository.cs new file mode 100644 index 000000000..375a401b3 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/BookmarkRepository.cs @@ -0,0 +1,57 @@ +namespace LearningHub.Nhs.Repository +{ + using System.Linq; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities; + using LearningHub.Nhs.OpenApi.Repositories.EntityFramework; + using LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories; + using LearningHub.Nhs.OpenApi.Repositories.Repositories; + using LearningHub.Nhs.Repository.Interface; + using Microsoft.EntityFrameworkCore; + + /// + /// The BookmarkRepository. + /// + public class BookmarkRepository : GenericRepository, IBookmarkRepository + { + /// + /// Initializes a new instance of the class. + /// + /// The dbContext. + /// The Timezone offset manager. + public BookmarkRepository(LearningHubDbContext dbContext, ITimezoneOffsetManager tzOffsetManager) + : base(dbContext, tzOffsetManager) + { + } + + /// + /// The GetById. + /// + /// The bookmarkId. + /// The . + public async Task GetById(int bookmarkId) + { + return await this.DbContext.UserBookmark.SingleAsync(ub => ub.Id == bookmarkId); + } + + /// + /// DeleteFolder. + /// + /// bookmarkId. + /// userId. + /// A representing the result of the asynchronous operation. + public async Task DeleteFolder(int bookmarkId, int userId) + { + var bookmarks = this.DbContext.UserBookmark.Where(ub => ub.Id == bookmarkId || ub.ParentId == bookmarkId); + + foreach (var bookmark in bookmarks) + { + bookmark.ParentId = null; + bookmark.Deleted = true; + this.SetAuditFieldsForUpdate(userId, bookmark); + } + + await this.DbContext.SaveChangesAsync(); + } + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/GenericRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/GenericRepository.cs new file mode 100644 index 000000000..cff356d60 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/GenericRepository.cs @@ -0,0 +1,177 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Repositories +{ + using System; + using System.Linq; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities; + using LearningHub.Nhs.OpenApi.Repositories.EntityFramework; + using LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories; + using Microsoft.EntityFrameworkCore; + + /// + /// The generic repository. + /// + /// Input type. + public class GenericRepository : IGenericRepository + where TEntity : EntityBase + { + /// + /// Initializes a new instance of the class. + /// + /// The db context. + /// The Timezone offset manager. + public GenericRepository(LearningHubDbContext dbContext, ITimezoneOffsetManager tzOffsetManager) + { + DbContext = dbContext; + TimezoneOffsetManager = tzOffsetManager; + } + + /// + /// Gets the db context. + /// + protected LearningHubDbContext DbContext { get; } + + /// + /// Gets the TimezoneOffset manager. + /// + protected ITimezoneOffsetManager TimezoneOffsetManager { get; } + + /// + /// The get all. + /// + /// The . + public IQueryable GetAll() + { + return DbContext.Set().AsNoTracking(); + } + + /// + /// The create async. + /// + /// The user id. + /// The entity. + /// The . + public virtual async Task CreateAsync(int userId, TEntity entity) + { + await DbContext.Set().AddAsync(entity); + SetAuditFieldsForCreate(userId, entity); + try + { + await DbContext.SaveChangesAsync(); + } + catch (Exception) + { + throw; + } + + DbContext.Entry(entity).State = EntityState.Detached; + + return entity.Id; + } + + /// + /// The update async. + /// + /// The user id. + /// The entity. + /// The . + public virtual async Task UpdateAsync(int userId, TEntity entity) + { + DbContext.Set().Update(entity); + + SetAuditFieldsForUpdate(userId, entity); + + await DbContext.SaveChangesAsync(); + + DbContext.Entry(entity).State = EntityState.Detached; + } + + /// + /// The update. + /// + /// The user id. + /// The entity. + public virtual void Update(int userId, TEntity entity) + { + DbContext.Set().Update(entity); + + SetAuditFieldsForUpdate(userId, entity); + + DbContext.SaveChanges(); + + DbContext.Entry(entity).State = EntityState.Detached; + } + + /// + /// The set audit fields for create. + /// + /// The user id. + /// The entity. + public void SetAuditFieldsForCreate(int userId, EntityBase entity) + { + var amendDate = GetAmendDate(); + + entity.Deleted = false; + entity.CreateUserId = userId; + entity.CreateDate = amendDate; + entity.AmendUserId = userId; + entity.AmendDate = amendDate; + } + + /// + /// The set audit fields for create or delete. + /// + /// The user id. + /// The entity. + /// Flag used to specify the journey. + public void SetAuditFieldsForCreateOrDelete(int userId, EntityBase entity, bool isCreate) + { + if (isCreate) + { + SetAuditFieldsForCreate(userId, entity); + } + else + { + SetAuditFieldsForDelete(userId, entity); + } + } + + /// + /// The set audit fields for update. + /// + /// The user id. + /// The entity. + public void SetAuditFieldsForUpdate(int userId, EntityBase entity) + { + entity.AmendUserId = userId; + entity.AmendDate = GetAmendDate(); + DbContext.Entry(entity).Property("CreateUserId").IsModified = false; + DbContext.Entry(entity).Property("CreateDate").IsModified = false; + if (entity.GetType() == typeof(User)) + { + DbContext.Entry(entity).Property("VersionStartTime").IsModified = false; + DbContext.Entry(entity).Property("VersionEndTime").IsModified = false; + } + } + + /// + /// The set audit fields for delete. + /// + /// The user id. + /// The entity. + public void SetAuditFieldsForDelete(int userId, EntityBase entity) + { + entity.Deleted = true; + entity.AmendUserId = userId; + entity.AmendDate = GetAmendDate(); + DbContext.Entry(entity).Property("CreateUserId").IsModified = false; + DbContext.Entry(entity).Property("CreateDate").IsModified = false; + } + + private DateTimeOffset GetAmendDate() + { + var tzOffset = TimezoneOffsetManager.UserTimezoneOffset; + return tzOffset.HasValue ? new DateTimeOffset(DateTime.UtcNow.AddMinutes(tzOffset.Value).Ticks, TimeSpan.FromMinutes(tzOffset.Value)) : DateTimeOffset.Now; + } + } +} \ No newline at end of file diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Hierarchy/CatalogueNodeVersionRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Hierarchy/CatalogueNodeVersionRepository.cs new file mode 100644 index 000000000..ee2dd0f50 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Hierarchy/CatalogueNodeVersionRepository.cs @@ -0,0 +1,393 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Repositories.Hierarchy +{ + using System; + using System.Collections.Generic; + using System.Data; + using System.Linq; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Catalogue; + using LearningHub.Nhs.Models.Dashboard; + using LearningHub.Nhs.Models.Entities.Hierarchy; + using LearningHub.Nhs.Models.Enums; + using LearningHub.Nhs.OpenApi.Repositories.EntityFramework; + using LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories; + using LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Hierarchy; + using Microsoft.Data.SqlClient; + using Microsoft.EntityFrameworkCore; + + /// + /// The CatalogueNodeVersionRepository. + /// + public class CatalogueNodeVersionRepository : GenericRepository, ICatalogueNodeVersionRepository + { + /// + /// Initializes a new instance of the class. + /// + /// The dbContext. + /// The Timezone offset manager. + public CatalogueNodeVersionRepository(LearningHubDbContext dbContext, ITimezoneOffsetManager tzOffsetManager) + : base(dbContext, tzOffsetManager) + { + } + + /// + /// The get basic catalogue. + /// + /// The catalogueNodeId. + /// The . + public CatalogueNodeVersion GetBasicCatalogue(int catalogueNodeId) + { + var catalogue = DbContext.CatalogueNodeVersion.AsNoTracking() + .Include(cnv => cnv.NodeVersion.Node) + .FirstOrDefault(cnv => cnv.NodeVersion.VersionStatusEnum == VersionStatusEnum.Published + && cnv.NodeVersion.NodeId == catalogueNodeId); + + return catalogue; + } + + /// + /// The get catalogues for id list. + /// + /// The catalogues ids. + /// The . + public async Task> GetCatalogues(List catalogueIds) + { + return await DbContext.CatalogueNodeVersion.AsNoTracking() + .Include(r => r.NodeVersion).AsNoTracking() + .Include(r => r.NodeVersion.Node).AsNoTracking() + .Where(r => !r.Deleted + && catalogueIds.Any(catalogueId => catalogueId == r.NodeVersion.NodeId) + && r.NodeVersion.Node.CurrentNodeVersionId == r.NodeVersionId + && r.NodeVersion.VersionStatusEnum == VersionStatusEnum.Published).ToListAsync(); + } + + /// + /// The get catalogues. + /// + /// The . + public async Task> GetPublishedCatalogues() + { + return await DbContext.CatalogueNodeVersion.AsNoTracking() + .Include(r => r.NodeVersion).AsNoTracking() + .Include(r => r.NodeVersion.Node).AsNoTracking() + .Where(r => !r.Deleted + && r.NodeVersion.Node.CurrentNodeVersionId == r.NodeVersionId + && r.NodeVersion.VersionStatusEnum == VersionStatusEnum.Published).ToListAsync(); + } + + /// + /// The get published catalogues for user. + /// + /// The user id. + /// The . + public IQueryable GetPublishedCataloguesForUserAsync(int userId) + { + var communityCatalogue = DbContext.CatalogueNodeVersion.AsNoTracking() + .Include(cnv => cnv.NodeVersion.Node) + .Where(cnv => cnv.NodeVersion.VersionStatusEnum == VersionStatusEnum.Published + && cnv.NodeVersion.NodeId == 1 /* Community Catalogue */); + + var cataloguesForUser = from cnv in DbContext.CatalogueNodeVersion.Include(cnv => cnv.NodeVersion.Node).AsNoTracking() + join nv in DbContext.NodeVersion.Where(cnv => cnv.VersionStatusEnum == VersionStatusEnum.Published && !cnv.Deleted) // .Include(nv => nv.Node) + on cnv.NodeVersionId equals nv.Id + join s in DbContext.Scope.Where(x => !x.Deleted) + on nv.NodeId equals s.CatalogueNodeId + join rug in DbContext.RoleUserGroup.Where(r => r.RoleId == (int)RoleEnum.Editor && !r.Deleted) + on s.Id equals rug.ScopeId + join uug in DbContext.UserUserGroup.Where(u => u.UserId == userId && !u.Deleted) + on rug.UserGroupId equals uug.UserGroupId + join n in DbContext.Node.Where(x => !x.Deleted) + on nv.Id equals n.CurrentNodeVersionId + select cnv; + + var returnedCatalogues = communityCatalogue.Union(cataloguesForUser).Distinct() + .OrderBy(cnv => cnv.NodeVersion.NodeId != 1) + .ThenBy(cnv => cnv.Name); + + return returnedCatalogues; + } + + /// + /// The CreateCatalogueAsync. + /// + /// The userId. + /// The catalogue view model. + /// The catalogueNodeVersionId. + public async Task CreateCatalogueAsync(int userId, CatalogueViewModel vm) + { + try + { + var param0 = new SqlParameter("@p0", SqlDbType.Int) { Value = (int)vm.Status }; + var param1 = new SqlParameter("@p1", SqlDbType.VarChar) { Value = vm.Name }; + var param2 = new SqlParameter("@p2", SqlDbType.VarChar) { Value = vm.Url }; + var param3 = string.IsNullOrEmpty(vm.BadgeUrl) + ? new SqlParameter("@p3", SqlDbType.VarChar) { Value = DBNull.Value } + : new SqlParameter("@p3", SqlDbType.VarChar) { Value = vm.BadgeUrl }; + var param4 = string.IsNullOrEmpty(vm.CertificateUrl) + ? new SqlParameter("@p4", SqlDbType.VarChar) { Value = DBNull.Value } + : new SqlParameter("@p4", SqlDbType.VarChar) { Value = vm.CertificateUrl }; + var param5 = string.IsNullOrEmpty(vm.CardImageUrl) + ? new SqlParameter("@p5", SqlDbType.VarChar) { Value = DBNull.Value } + : new SqlParameter("@p5", SqlDbType.VarChar) { Value = vm.CardImageUrl }; + var param6 = string.IsNullOrEmpty(vm.BannerUrl) + ? new SqlParameter("@p6", SqlDbType.VarChar) { Value = DBNull.Value } + : new SqlParameter("@p6", SqlDbType.VarChar) { Value = vm.BannerUrl }; + var param7 = new SqlParameter("@p7", SqlDbType.Int) { Value = (int)vm.ResourceOrder }; + var param8 = new SqlParameter("@p8", SqlDbType.VarChar) { Value = vm.Description }; + var param9 = new SqlParameter("@p9", SqlDbType.Int) { Value = userId }; + var param10 = new SqlParameter("@p10", SqlDbType.Bit) { Value = vm.Hidden }; + var param11 = new SqlParameter("@p11", SqlDbType.VarChar) { Value = string.Join(",", vm.Keywords) }; + var param12 = new SqlParameter("@p12", SqlDbType.Bit) { Value = vm.RestrictedAccess }; + var param13 = new SqlParameter("@p13", SqlDbType.Int) { Value = TimezoneOffsetManager.UserTimezoneOffset ?? (object)DBNull.Value }; + var param14 = new SqlParameter("@p14", SqlDbType.Int) { Value = vm.CatalogueNodeVersionProvider.ProviderId }; + var param15 = new SqlParameter("@p15", SqlDbType.Int) { Direction = ParameterDirection.Output }; + + await DbContext.Database.ExecuteSqlRawAsync("hierarchy.CatalogueCreate @p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11, @p12, @p13, @p14, @p15 output", param0, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13, param14, param15); + + int catalogueNodeVersionId = (int)param15.Value; + + return catalogueNodeVersionId; + } + catch (Exception ex) + { + throw new Exception(ex.Message); + } + } + + /// + /// The UpdateCatalogueAsync. + /// + /// The userId. + /// The catalogue view model. + /// The task. + public async Task UpdateCatalogueAsync(int userId, CatalogueViewModel vm) + { + try + { + var param0 = new SqlParameter("@p0", SqlDbType.Int) { Value = (int)vm.Status }; + var param1 = new SqlParameter("@p1", SqlDbType.VarChar) { Value = vm.Name }; + var param2 = string.IsNullOrEmpty(vm.BadgeUrl) + ? new SqlParameter("@p2", SqlDbType.VarChar) { Value = DBNull.Value } + : new SqlParameter("@p2", SqlDbType.VarChar) { Value = vm.BadgeUrl }; + var param3 = string.IsNullOrEmpty(vm.CardImageUrl) + ? new SqlParameter("@p3", SqlDbType.VarChar) { Value = DBNull.Value } + : new SqlParameter("@p3", SqlDbType.VarChar) { Value = vm.CardImageUrl }; + var param4 = string.IsNullOrEmpty(vm.BannerUrl) + ? new SqlParameter("@p4", SqlDbType.VarChar) { Value = DBNull.Value } + : new SqlParameter("@p4", SqlDbType.VarChar) { Value = vm.BannerUrl }; + var param5 = string.IsNullOrEmpty(vm.CertificateUrl) + ? new SqlParameter("@p5", SqlDbType.VarChar) { Value = DBNull.Value } + : new SqlParameter("@p5", SqlDbType.VarChar) { Value = vm.CertificateUrl }; + var param6 = new SqlParameter("@p6", SqlDbType.Int) { Value = (int)vm.ResourceOrder }; + var param7 = new SqlParameter("@p7", SqlDbType.VarChar) { Value = vm.Description }; + var param8 = new SqlParameter("@p8", SqlDbType.Int) { Value = userId }; + var param9 = new SqlParameter("@p9", SqlDbType.VarChar) { Value = string.Join(",", vm.Keywords) }; + var param10 = new SqlParameter("@p10", SqlDbType.Int) { Value = vm.CatalogueNodeVersionId }; + var param11 = new SqlParameter("@p11", SqlDbType.Bit) { Value = vm.RestrictedAccess }; + var param12 = new SqlParameter("@p12", SqlDbType.Int) { Value = TimezoneOffsetManager.UserTimezoneOffset ?? (object)DBNull.Value }; + var param13 = new SqlParameter("@p13", SqlDbType.Int) { Value = vm.CatalogueNodeVersionProvider.ProviderId }; + + await DbContext.Database.ExecuteSqlRawAsync("hierarchy.CatalogueUpdate @p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11, @p12, @p13", param0, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11, param12, param13); + } + catch (Exception ex) + { + throw new Exception(ex.Message); + } + } + + /// + /// The UpdateCatalogueOwnerAsync. + /// + /// The userId. + /// The catalogue owner view model. + /// The task. + public async Task UpdateCatalogueOwnerAsync(int userId, CatalogueOwnerViewModel vm) + { + try + { + var param0 = new SqlParameter("@p0", SqlDbType.VarChar) { Value = vm.OwnerName }; + var param1 = new SqlParameter("@p1", SqlDbType.VarChar) { Value = vm.OwnerEmailAddress }; + var param2 = new SqlParameter("@p2", SqlDbType.VarChar) { Value = vm.Notes }; + var param3 = new SqlParameter("@p3", SqlDbType.Int) { Value = userId }; + var param4 = new SqlParameter("@p4", SqlDbType.Int) { Value = vm.CatalogueNodeVersionId }; + var param5 = new SqlParameter("@p5", SqlDbType.Int) { Value = TimezoneOffsetManager.UserTimezoneOffset ?? (object)DBNull.Value }; + + await DbContext.Database.ExecuteSqlRawAsync("hierarchy.CatalogueOwnerUpdate @p0, @p1, @p2, @p3, @p4, @p5", param0, param1, param2, param3, param4, param5); + } + catch (Exception ex) + { + throw new Exception(ex.Message); + } + } + + /// + /// Get Catlogue by reference. + /// + /// The reference. + /// The CatalogueViewModel. + public async Task GetCatalogueAsync(string reference) + { + return await (from cnv in DbContext.CatalogueNodeVersion.AsNoTracking() + join nv in DbContext.NodeVersion.AsNoTracking() on cnv.NodeVersionId equals nv.Id + join n in DbContext.Node.AsNoTracking() on cnv.NodeVersionId equals n.CurrentNodeVersionId + where cnv.Url != null && cnv.Url.ToLower() == reference.ToLower() && cnv.Deleted == false && nv.VersionStatusEnum == VersionStatusEnum.Published + select new CatalogueViewModel + { + Id = cnv.Id, + NodeVersionId = cnv.NodeVersionId, + BadgeUrl = cnv.BadgeUrl, + BannerUrl = cnv.BannerUrl, + CertificateUrl = cnv.CertificateUrl, + Name = cnv.Name, + Description = cnv.Description, + Notes = cnv.Notes, + Url = cnv.Url, + NodeId = n.Id, + CatalogueNodeVersionId = cnv.NodeVersionId, + ResourceOrder = cnv.Order, + RestrictedAccess = cnv.RestrictedAccess, + Hidden = n.Hidden, + }).SingleOrDefaultAsync(); + } + + /// + /// Get list of Restricted Catalogue AccessRequests for the supplied request. + /// + /// The restrictedCatalogueAccessRequestsRequestViewModel. + /// A RestrictedCatalogueAccessRequestsViewModel. + public List GetRestrictedCatalogueAccessRequests(RestrictedCatalogueAccessRequestsRequestViewModel restrictedCatalogueAccessRequestsRequestViewModel) + { + var param0 = new SqlParameter("@p0", SqlDbType.Int) { Value = restrictedCatalogueAccessRequestsRequestViewModel.CatalogueNodeId }; + var param1 = new SqlParameter("@p1", SqlDbType.Bit) { Value = restrictedCatalogueAccessRequestsRequestViewModel.IncludeNew }; + var param2 = new SqlParameter("@p2", SqlDbType.Bit) { Value = restrictedCatalogueAccessRequestsRequestViewModel.IncludeApproved }; + var param3 = new SqlParameter("@p3", SqlDbType.Bit) { Value = restrictedCatalogueAccessRequestsRequestViewModel.IncludeDenied }; + + var restrictedCatalogueAccessRequests = DbContext.RestrictedCatalogueAccessRequestViewModel.FromSqlRaw("hub.RestrictedCatalogueGetAccessRequests @p0, @p1, @p2, @p3", param0, param1, param2, param3).ToList(); + + return restrictedCatalogueAccessRequests; + } + + /// + /// Get list of Restricted Catalogue Users for the supplied request. + /// + /// The restrictedCatalogueUsersRequestViewModel. + /// A RestrictedCatalogueUsersViewModel. + public RestrictedCatalogueUsersViewModel GetRestrictedCatalogueUsers(RestrictedCatalogueUsersRequestViewModel restrictedCatalogueUsersRequestViewModel) + { + var param0 = new SqlParameter("@p0", SqlDbType.Int) { Value = restrictedCatalogueUsersRequestViewModel.CatalogueNodeId }; + var param1 = new SqlParameter("@p1", SqlDbType.NVarChar) { Value = string.IsNullOrEmpty(restrictedCatalogueUsersRequestViewModel.EmailAddressFilter) ? string.Empty : restrictedCatalogueUsersRequestViewModel.EmailAddressFilter }; + var param2 = new SqlParameter("@p2", SqlDbType.Bit) { Value = restrictedCatalogueUsersRequestViewModel.IncludeCatalogueAdmins }; + var param3 = new SqlParameter("@p3", SqlDbType.Bit) { Value = restrictedCatalogueUsersRequestViewModel.IncludePlatformAdmins }; + var param4 = new SqlParameter("@p4", SqlDbType.Int) { Value = restrictedCatalogueUsersRequestViewModel.Skip }; + var param5 = new SqlParameter("@p5", SqlDbType.Int) { Value = restrictedCatalogueUsersRequestViewModel.Take }; + var param6 = new SqlParameter("@p6", SqlDbType.Int) { Direction = ParameterDirection.Output }; + + var restrictedCatalogueUsers = DbContext.RestrictedCatalogueUserViewModel.FromSqlRaw("hub.RestrictedCatalogueGetUsers @p0, @p1, @p2, @p3, @p4, @p5, @p6 output", param0, param1, param2, param3, param4, param5, param6).ToList(); + + var vm = new RestrictedCatalogueUsersViewModel(); + vm.RestrictedCatalogueUsers = restrictedCatalogueUsers; + vm.UserCount = (int)param6.Value; + + return vm; + } + + /// + /// Get Restricted Catalogue Summary for the supplied catalogue node id. + /// + /// The catalogueNodeId. + /// A RestrictedCatalogueUsersViewModel. + public RestrictedCatalogueSummaryViewModel GetRestrictedCatalogueSummary(int catalogueNodeId) + { + var param0 = new SqlParameter("@p0", SqlDbType.Int) { Value = catalogueNodeId }; + + var vm = DbContext.RestrictedCatalogueSummaryViewModel.FromSqlRaw("hub.RestrictedCatalogueGetSummary @p0", param0).AsEnumerable().FirstOrDefault(); + + return vm; + } + + /// + /// Gets catalogues for dashboard based on type. + /// + /// The dashboard type. + /// The page Number. + /// The userId. + /// resources. + public (int TotalCount, List Catalogues) GetCatalogues(string dashboardType, int pageNumber, int userId) + { + var param0 = new SqlParameter("@DashboardType", SqlDbType.NVarChar, 30) { Value = dashboardType }; + var param1 = new SqlParameter("@UserId", SqlDbType.Int) { Value = userId }; + var param2 = new SqlParameter("@pageNumber", SqlDbType.Int) { Value = pageNumber }; + var param3 = new SqlParameter("@TotalRecords", SqlDbType.Int) { Direction = ParameterDirection.Output }; + + var dashboardCatalogues = DbContext.DashboardCatalogueDto.FromSqlRaw("[hierarchy].[GetDashboardCatalogues] @DashboardType, @UserId, @pageNumber, @TotalRecords OUTPUT", param0, param1, param2, param3).ToList(); + + return (TotalCount: (int)param3.Value, Catalogues: dashboardCatalogues); + } + + /// + public async Task ShowCatalogue(int userId, int nodeId) + { + var param0 = new SqlParameter("@NodeId", SqlDbType.Int) { Value = nodeId }; + var param1 = new SqlParameter("@UserId", SqlDbType.Int) { Value = userId }; + var param2 = new SqlParameter("@UserTimezoneOffset", SqlDbType.Int) { Value = TimezoneOffsetManager.UserTimezoneOffset ?? (object)DBNull.Value }; + + await DbContext.Database.ExecuteSqlRawAsync("[hierarchy].[CatalogueShow] @NodeId, @UserId, @UserTimezoneOffset", param0, param1, param2); + } + + /// + /// Check if a Catalogue with a specific name exists or not. + /// + /// The catalogue name. + /// True if the catalogue exists, otherwise false. + public async Task ExistsAsync(string name) + { + return await DbContext.CatalogueNodeVersion.AnyAsync(x => x.Name.Equals(name) && !x.Deleted); + } + + /// + /// Gets the Node Id for a particular catalogue name. + /// + /// The catalogue name. + /// The catalogue's node id. + public async Task GetNodeIdByCatalogueName(string catalogueName) + { + return await (from cnv in DbContext.CatalogueNodeVersion.AsNoTracking() + join nv in DbContext.NodeVersion.AsNoTracking() on cnv.NodeVersionId equals nv.Id + where cnv.Name == catalogueName && cnv.Deleted == false + select nv.NodeId).FirstOrDefaultAsync(); + } + + /// + /// Gets catalogues count based on alphabets. + /// + /// The userId. + /// resources. + public List GetAllCataloguesAlphaCount(int userId) + { + var param0 = new SqlParameter("@userId", SqlDbType.Int) { Value = userId }; + + var result = DbContext.AllCatalogueAlphabetModel.FromSqlRaw("[hierarchy].[GetCataloguesCount] @userid", param0) + .AsNoTracking().ToList(); + return result; + } + + /// + /// Gets catalogues based on filter character. + /// + /// The pageSize. + /// The filterChar. + /// The userId. + /// resources. + public async Task> GetAllCataloguesAsync(int pageSize, string filterChar, int userId) + { + var param0 = new SqlParameter("@userId", SqlDbType.Int) { Value = userId }; + var param1 = new SqlParameter("@filterChar", SqlDbType.NVarChar, 10) { Value = filterChar.Trim() }; + var param2 = new SqlParameter("@OffsetRows", SqlDbType.Int) { Value = 0 }; + var param3 = new SqlParameter("@fetchRows", SqlDbType.Int) { Value = pageSize }; + + var result = await DbContext.AllCatalogueViewModel.FromSqlRaw("[hierarchy].[GetCatalogues] @userId, @filterChar, @OffsetRows, @fetchRows", param0, param1, param2, param3) + .AsNoTracking().ToListAsync(); + return result; + } + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Hierarchy/NodeResourceRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Hierarchy/NodeResourceRepository.cs new file mode 100644 index 000000000..e881cb3dc --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Hierarchy/NodeResourceRepository.cs @@ -0,0 +1,161 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Repositories.Hierarchy +{ + using System; + using System.Collections.Generic; + using System.Data; + using System.Linq; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Catalogue; + using LearningHub.Nhs.Models.Entities.Hierarchy; + using LearningHub.Nhs.Models.Enums; + using LearningHub.Nhs.Models.Hierarchy; + using LearningHub.Nhs.OpenApi.Repositories.EntityFramework; + using LearningHub.Nhs.OpenApi.Repositories.Helpers; + using LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories; + using LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Hierarchy; + using Microsoft.Data.SqlClient; + using Microsoft.EntityFrameworkCore; + + /// + /// The node resource repository. + /// + public class NodeResourceRepository : GenericRepository, INodeResourceRepository + { + /// + /// Initializes a new instance of the class. + /// + /// The db context. + /// The Timezone offset manager. + public NodeResourceRepository(LearningHubDbContext dbContext, ITimezoneOffsetManager tzOffsetManager) + : base(dbContext, tzOffsetManager) + { + } + + /// + /// The get by resource id async. + /// + /// The resource id. + /// The . + public async Task> GetByResourceIdAsync(int resourceId) + { + return await DbContext.NodeResource.AsNoTracking().Where(r => r.ResourceId == resourceId && !r.Deleted) + .Include(n => n.Node) + .ThenInclude(n => n.NodePaths) + .ThenInclude(n => n.NodePathNode) + .ThenInclude(n => n.Node) + .ToListAsync(); + } + + /// + /// Gets the Id of the node where a resource is currently located (IT1 - one resource is located in one node). + /// + /// The resource id. + /// The node id. + public async Task GetNodeIdByResourceId(int resourceId) + { + return await DbContext.NodeResource.AsNoTracking().Where(r => r.ResourceId == resourceId && !r.Deleted) + .Select(x => x.NodeId).SingleAsync(); + } + + /// + /// The get catalogue locations for resource. + /// + /// The resource id. + /// The CatalogueLocationViewModel list. + public List GetCatalogueLocationsForResource(int resourceId) + { + var param0 = new SqlParameter("@p0", SqlDbType.Int) { Value = resourceId }; + + var vms = DbContext.CatalogueLocationViewModel.FromSqlRaw("hierarchy.CatalogueLocationsForResource @p0", param0).ToList(); + + return vms; + } + + /// + /// GetResourcesAsync. + /// + /// nodeId. + /// catalogueOrder. + /// offset. + /// CatalogueResourceViewModel. + public async Task GetResourcesAsync(int nodeId, CatalogueOrder catalogueOrder, int offset) + { + var resourceViewModels = (from nr in DbContext.NodeResource.AsNoTracking() + join rr in DbContext.ResourceReference.AsNoTracking() on nr.ResourceId equals rr.ResourceId + join np in DbContext.NodePath.AsNoTracking() on rr.NodePathId equals np.Id + join r in DbContext.Resource.AsNoTracking() on nr.ResourceId equals r.Id + join rv in DbContext.ResourceVersion.AsNoTracking() on r.CurrentResourceVersionId equals rv.Id + join grv in DbContext.GenericFileResourceVersion.AsNoTracking() on rv.Id equals grv.ResourceVersionId into genericjoin + from grv in genericjoin.DefaultIfEmpty() + where nr.NodeId == nodeId && np.NodeId == nodeId && rv.VersionStatusEnum == VersionStatusEnum.Published && rv.Deleted == false && nr.VersionStatusEnum == VersionStatusEnum.Published && nr.Deleted == false + select new CatalogueResourceViewModel + { + Type = r.ResourceTypeEnum.ToString(), + ResourceId = nr.ResourceId.ToString(), + ResourceVersionId = r.CurrentResourceVersionId.ToString(), + ResourceReferenceId = rr.Id.ToString(), + Title = rv.Title, + Description = rv.Description, + AuthoredBy = rv.ResourceVersionAuthor.FirstOrDefault().AuthorName, + Organisation = rv.ResourceVersionAuthor.FirstOrDefault().Organisation, + CreatedOn = rv.CreateDate.Date, + AuthoredDateText = r.ResourceTypeEnum != ResourceTypeEnum.GenericFile ? string.Empty : TextHelper.CombineDateComponents(grv.AuthoredYear, grv.AuthoredMonth, grv.AuthoredDayOfMonth), + }).AsQueryable(); + + if (resourceViewModels != null) + { + var totalResources = await resourceViewModels.CountAsync(); + if (catalogueOrder == CatalogueOrder.AlphabeticalAscending) + { + resourceViewModels = resourceViewModels.OrderBy(r => r.Title).Take(offset + 10); + } + else + { + resourceViewModels = resourceViewModels.OrderByDescending(r => r.CreatedOn).Take(offset + 10); + } + + return new CatalogueResourceResponseViewModel + { + NodeId = nodeId, + TotalResources = totalResources, + CatalogueResources = resourceViewModels?.ToList(), + }; + } + + return null; + } + + /// + /// Get All published resources id. + /// + /// The . + public async Task> GetAllPublishedResourceAsync() + { + return await (from nr in DbContext.NodeResource.AsNoTracking() + join rr in DbContext.ResourceReference.AsNoTracking() on nr.ResourceId equals rr.ResourceId + join np in DbContext.NodePath.AsNoTracking() on rr.NodePathId equals np.Id + join r in DbContext.Resource.AsNoTracking() on nr.ResourceId equals r.Id + join rv in DbContext.ResourceVersion.AsNoTracking() on r.CurrentResourceVersionId equals rv.Id + where nr.NodeId == np.NodeId && rv.VersionStatusEnum == VersionStatusEnum.Published && nr.VersionStatusEnum == VersionStatusEnum.Published + select rr.Id) + .ToListAsync(); + } + + /// + /// Creates or updates the NodeResource record for a draft resource in a node. + /// + /// The nodeId. + /// The resourceId. + /// The userId. + /// The . + public async Task CreateOrUpdateAsync(int nodeId, int resourceId, int userId) + { + var param0 = new SqlParameter("@p0", SqlDbType.Int) { Value = nodeId }; + var param1 = new SqlParameter("@p1", SqlDbType.Int) { Value = resourceId }; + var param2 = new SqlParameter("@p2", SqlDbType.Int) { Value = userId }; + var param3 = new SqlParameter("@p3", SqlDbType.Int) { Value = TimezoneOffsetManager.UserTimezoneOffset ?? (object)DBNull.Value }; + + return await DbContext.Database.ExecuteSqlRawAsync("hierarchy.NodeResourceCreateOrUpdate @p0, @p1, @p2, @p3", param0, param1, param2, param3); + } + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/MultipleResultSets.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/MultipleResultSets.cs new file mode 100644 index 000000000..9889db659 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/MultipleResultSets.cs @@ -0,0 +1,132 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Repositories +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Data; + using System.Data.Common; + using System.Linq; + using System.Reflection; + using Microsoft.Data.SqlClient; + using Microsoft.EntityFrameworkCore; + + /// + /// MultipleResultSets. + /// + public static class MultipleResultSets + { + /// + /// MultipleResults extension. + /// + /// DbContext. + /// Store Proc. + /// MultipleResultSetWrapper. + public static MultipleResultSetWrapper MultipleResults(this DbContext db, SqlCommand command) + { + return new MultipleResultSetWrapper(db, command); + } + + /// + /// MultipleResultSetWrapper. + /// + public class MultipleResultSetWrapper + { + private readonly DbContext dbContext; + private readonly SqlCommand command; + private List> resultSets; + + /// + /// Initializes a new instance of the class. + /// + /// DbContext. + /// Store Proc. + public MultipleResultSetWrapper(DbContext dbContext, SqlCommand storedProcedure) + { + this.dbContext = dbContext; + command = storedProcedure; + resultSets = new List>(); + } + + /// + /// With result. + /// + /// Type. + /// MultipleResultSetWrapper. + public MultipleResultSetWrapper With() + { + resultSets.Add(reader => MapToList(reader)); + + return this; + } + + /// + /// Execute. + /// + /// IEnumerable list. + public List Execute() + { + var results = new List(); + + var connection = dbContext.Database.GetDbConnection(); + + if (connection.State != ConnectionState.Open) + { + connection.Open(); + } + + command.Connection = (SqlConnection)connection; + using (var reader = command.ExecuteReader()) + { + foreach (var resultSet in resultSets) + { + results.Add(resultSet(reader)); + reader.NextResult(); + } + } + + return results; + } + + /// + /// Retrieves the column values from the stored procedure and maps them to 's properties. + /// + /// Type. + /// DbDataReader. + /// Array of type. + private static List MapToList(DbDataReader dr) + { + var objList = new List(); + var props = typeof(T).GetRuntimeProperties().ToList(); + + var colMapping = dr.GetColumnSchema() + .Where(x => props.Any(y => y.Name.ToLower() == x.ColumnName.ToLower())) + .ToDictionary(key => key.ColumnName.ToLower()); + + if (dr.HasRows) + { + while (dr.Read()) + { + T obj = Activator.CreateInstance(); + foreach (var prop in props) + { + if (colMapping.ContainsKey(prop.Name.ToLower())) + { + var column = colMapping[prop.Name.ToLower()]; + + if (column?.ColumnOrdinal != null) + { + var val = dr.GetValue(column.ColumnOrdinal.Value); + prop.SetValue(obj, val == DBNull.Value ? null : val); + } + } + } + + objList.Add(obj); + } + } + + return objList; + } + } + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/ProviderRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/ProviderRepository.cs new file mode 100644 index 000000000..e81581930 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/ProviderRepository.cs @@ -0,0 +1,91 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Repositories +{ + using System.Data; + using System.Linq; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities; + using LearningHub.Nhs.Models.Entities.Hierarchy; + using LearningHub.Nhs.Models.Entities.Resource; + using LearningHub.Nhs.OpenApi.Repositories.EntityFramework; + using LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories; + using Microsoft.EntityFrameworkCore; + + /// + /// The provider repository. + /// + public class ProviderRepository : GenericRepository, IProviderRepository + { + /// + /// Initializes a new instance of the class. + /// + /// The db context. + /// The Timezone offset manager. + public ProviderRepository(LearningHubDbContext dbContext, ITimezoneOffsetManager tzOffsetManager) + : base(dbContext, tzOffsetManager) + { + } + + /// + public async Task GetByIdAsync(int id) + { + return await DbContext.Provider.AsNoTracking().FirstOrDefaultAsync(n => n.Id == id && !n.Deleted); + } + + /// + public async Task GetByIdAsync(int id, bool includeChildren) + { + if (includeChildren) + { + return await DbContext.Provider + .Where(r => !r.Deleted) + .Include(r => r.UserProvider) + .ThenInclude(pr => pr.User) + .AsNoTracking() + .FirstOrDefaultAsync(n => n.Id == id); + } + else + { + return await DbContext.Provider.AsNoTracking().FirstOrDefaultAsync(n => n.Id == id && !n.Deleted); + } + } + + /// + /// The get by user id async. + /// + /// The user id. + /// The . + public IQueryable GetProvidersByUserIdAsync(int userId) + { + return DbContext.Set() + .Include(up => up.Provider) + .Where(up => up.UserId == userId && !up.Deleted).AsNoTracking() + .Select(up => up.Provider); + } + + /// + /// The get by resource version id async. + /// + /// The resource version id. + /// The . + public IQueryable GetProvidersByResourceIdAsync(int resourceVersionId) + { + return DbContext.Set() + .Include(up => up.Provider) + .Where(up => up.ResourceVersionId == resourceVersionId && !up.Deleted).AsNoTracking() + .Select(up => up.Provider); + } + + /// + /// The get by node version id async. + /// + /// The node version id. + /// The . + public IQueryable GetProvidersByCatalogueIdAsync(int nodeVersionId) + { + return DbContext.Set() + .Include(up => up.Provider) + .Where(up => up.CatalogueNodeVersionId == nodeVersionId && !up.Deleted).AsNoTracking() + .Select(up => up.Provider); + } + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/ResourceRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/ResourceRepository.cs index 79b7a239b..1ba6a10af 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/ResourceRepository.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/ResourceRepository.cs @@ -90,11 +90,11 @@ public async Task> GetAchievedCertificatedResourceIds(int currentUserI return achievedCertificatedResourceIds; } - /// - /// - /// - /// . - /// A representing the result of the asynchronous operation. + // + // + // + // . + // A representing the result of the asynchronous operation. public async Task> GetResourceActivityPerResourceMajorVersion( IEnumerable? resourceIds, IEnumerable? userIds) { @@ -119,5 +119,16 @@ public async Task> GetResourceActivityPerResour return resourceActivityDTOs; } + + /// + /// The get by id async. + /// + /// The id. + /// The . + public async Task GetByIdAsync(int id) + { + return await this.dbContext.Resource.AsNoTracking().FirstOrDefaultAsync(r => r.Id == id && !r.Deleted); + } + } } diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Resources/BlockCollectionRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Resources/BlockCollectionRepository.cs new file mode 100644 index 000000000..35b7432f1 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Resources/BlockCollectionRepository.cs @@ -0,0 +1,443 @@ +namespace LearningHub.Nhs.OpenApi.Repositories.Repositories.Resources +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Data; + using System.Linq; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Resource; + using LearningHub.Nhs.Models.Entities.Resource.Blocks; + using LearningHub.Nhs.Models.Enums; + using LearningHub.Nhs.Models.Resource.Blocks; + using LearningHub.Nhs.OpenApi.Repositories.EntityFramework; + using LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories; + using LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories.Resources; + using Microsoft.Data.SqlClient; + using Microsoft.EntityFrameworkCore; + + /// + /// The block collection repository. + /// + public class BlockCollectionRepository : GenericRepository, IBlockCollectionRepository + { + /// + /// Initializes a new instance of the class. + /// + /// The db context. + /// The Timezone offset manager. + public BlockCollectionRepository(LearningHubDbContext dbContext, ITimezoneOffsetManager tzOffsetManager) + : base(dbContext, tzOffsetManager) + { + } + + /// + /// The create async. + /// + /// The user id. + /// The block collection. + /// The . + public override async Task CreateAsync(int userId, BlockCollection blockCollection) + { + SetAuditFieldsOnChildren(userId, blockCollection, isCreate: true); + + return await base.CreateAsync(userId, blockCollection); + } + + /// + /// Delete the Block Collection. + /// + /// The User Id. + /// The Block Collection Id. + /// The . + public async Task DeleteBlockCollection(int userId, int blockCollectionId) + { + BlockCollection blockCollection = DbContext.BlockCollection + .Include(b => b.Blocks) + .ThenInclude(b => b.QuestionBlock) + .ThenInclude(b => b.Answers) + .Include(bc => bc.Blocks) + .ThenInclude(b => b.ImageCarouselBlock) + .FirstOrDefault(bc => bc.Id == blockCollectionId); + + if (blockCollection == null) + { + return; + } + + var questionBlockCollection = blockCollection.Blocks + .Where(b => b.QuestionBlock != null) + .SelectMany(b => + { + var qb = b.QuestionBlock; + return qb.Answers + .Where(t => t.BlockCollectionId.HasValue) + .Select(a => a.BlockCollectionId.Value) + .Concat(new[] { qb.QuestionBlockCollectionId, qb.FeedbackBlockCollectionId }); + }); + + var imageBlockCollection = blockCollection.Blocks + .Where(b => b.ImageCarouselBlock != null) + .Select(b => b.ImageCarouselBlock.ImageBlockCollectionId); + + var collectionIds = new[] { blockCollectionId }.Concat(imageBlockCollection.Concat(questionBlockCollection)); + + foreach (var id in collectionIds) + { + await DbContext.Database.ExecuteSqlRawAsync("resources.BlockCollectionDelete @p0", new SqlParameter("@p0", SqlDbType.Int) { Value = id }); + } + } + + /// + /// Gets the Block Collection (including child Blocks, TextBlocks, WholeSlideImageBlocks and Files). + /// + /// The Block Collection Id. + /// The . + public async Task GetBlockCollection(int? blockCollectionId) + { + if (!blockCollectionId.HasValue) + { + return null; + } + + var command = new SqlCommand + { + CommandText = "[resources].[BlockCollectionGet]", + CommandType = CommandType.StoredProcedure, + Parameters = { new SqlParameter("@BlockCollectionId", SqlDbType.Int) { Value = blockCollectionId } }, + }; + + var results = DbContext.MultipleResults(command) + .With() + .With() + .With() + .With() + .With() + .With() + .With() + .With() + .With() + .With() + .With() + .With() + .With() + .With() + .With() + .With() + .With() + .With