From 7d6c303629558d7f2fd5ac09bfbfb0004700f93b Mon Sep 17 00:00:00 2001 From: Phil-NHS Date: Tue, 29 Jul 2025 16:14:16 +0100 Subject: [PATCH 1/2] TD-5862: Pulled in RC changes and Blazor Branch changes and added bff --- .../Configuration/WebSettings.cs | 5 + .../Helpers/IOpenApiFacade.cs | 59 + .../Helpers/OpenApiFacade.cs | 241 ++++ .../Interfaces/IOpenApiHttpClient.cs | 17 + .../ServiceCollectionExtension.cs | 9 + .../Services/BaseService.cs | 21 + .../Services/CatalogueService.cs | 10 +- .../Services/ContentService.cs | 50 +- .../Services/EventService.cs | 7 +- .../Services/HierarchyService.cs | 8 +- .../Services/OpenApiHttpClient.cs | 39 + .../Services/ProviderService.cs | 9 +- .../Services/ResourceService.cs | 29 +- .../Services/UserGroupService.cs | 28 +- .../Services/UserService.cs | 13 +- .../LearningHub.Nhs.AdminUI/appsettings.json | 1 + .../Interfaces/Http/IOpenAPIHttpClient.cs | 21 + .../Services/BaseService.cs | 19 + .../Services/ProviderService.cs | 11 +- .../Configuration/BFFPathValidationOptions.cs | 25 + .../Configuration/Settings.cs | 5 + .../Controllers/Api/BFFController.cs | 248 ++++ .../Controllers/SearchController.cs | 2 +- .../Helpers/IOpenApiFacade.cs | 59 + .../Helpers/OpenApiFacade.cs | 241 ++++ .../Services/ActivityService.cs | 5 +- .../Services/BoomarkService.cs | 15 +- LearningHub.Nhs.WebUI/Services/CardService.cs | 13 +- .../Services/CatalogueService.cs | 43 +- .../Services/ContentService.cs | 9 +- .../Services/ContributeService.cs | 55 +- .../Services/CountryService.cs | 4 +- .../Services/DashboardService.cs | 13 +- .../Services/DetectJsLogService.cs | 5 +- .../Services/GradeService.cs | 4 +- .../Services/HierarchyService.cs | 12 +- .../Services/InternalSystemService.cs | 5 +- .../Services/JobRoleService.cs | 4 +- .../Services/LocationService.cs | 4 +- .../Services/LoginWizardService.cs | 4 +- .../Services/MyLearningService.cs | 11 +- .../Services/NotificationService.cs | 17 +- .../Services/OpenApiHttpClient.cs | 41 + .../Services/PartialFileUploadService.cs | 4 +- .../Services/RatingService.cs | 5 +- .../Services/RegionService.cs | 5 +- .../Services/ResourceService.cs | 85 +- .../Services/RoadMapService.cs | 5 +- LearningHub.Nhs.WebUI/Services/RoleService.cs | 5 +- .../Services/SpecialtyService.cs | 4 +- .../Services/TermsAndConditionsService.cs | 4 +- .../Services/UserGroupService.cs | 8 +- LearningHub.Nhs.WebUI/Services/UserService.cs | 34 +- .../Startup/ServiceMappings.cs | 12 + .../Styles/nhsuk/common.scss | 172 +-- .../Styles/nhsuk/layout.scss | 38 +- .../Styles/nhsuk/nhsuk-overrides.scss | 672 +++++++++ LearningHub.Nhs.WebUI/Styles/nhsuk/nhsuk.scss | 486 +------ .../Views/Search/_SearchBar.cshtml | 4 +- .../NavigationItems/Searchbar.cshtml | 4 +- .../Shared/Tenant/LearningHub/_Layout.cshtml | 1 + LearningHub.Nhs.WebUI/appsettings.json | 196 +-- .../Configuration/AzureConfig.cs | 1 + .../Configuration/FindwiseConfig.cs | 10 + .../Configuration/LearningHubConfig.cs | 33 + .../IMediaResourcePlayedSegmentRepository.cs | 21 + .../Analytics/IEventRepository.cs | 18 + .../Repositories/Content/IPageRepository.cs | 43 + .../Content/IPageSectionDetailRepository.cs | 51 + .../Content/IPageSectionRepository.cs | 84 ++ .../Content/IVideoAssetRepository.cs | 25 + .../ICatalogueNodeVersionRepository.cs | 8 + .../Repositories/IEventLogRepository.cs | 22 + .../Repositories/IResourceRepository.cs | 11 + .../Repositories/IScopeRepository.cs | 25 + .../IUserGroupAttributeRepository.cs | 26 + .../Repositories/IUserGroupRepository.cs | 41 + .../IUserPasswordResetRequestsRepository.cs | 31 + .../Repositories/IUserProviderRepository.cs | 34 + .../IResourceVersionEventRepository.cs | 34 + .../EntityFramework/LearningHubDbContext.cs | 10 + .../EntityFramework/ServiceMappings.cs | 10 +- .../Map/Analytics/EventMap.cs | 3 +- .../Map/BaseEntityMap.cs | 4 +- .../Map/PasswordResetRequestsMap.cs | 23 + .../Map/UserProviderMap.cs | 41 + .../MediaResourcePlayedSegmentRepository.cs | 42 + .../Repositories/Analytics/EventRepository.cs | 43 + .../Repositories/Content/PageRepository.cs | 233 +++ .../Content/PageSectionDetailRepository.cs | 126 ++ .../Content/PageSectionRepository.cs | 546 +++++++ .../Content/VideoAssetRepository.cs | 45 + .../Repositories/EventLogRepository.cs | 80 ++ .../CatalogueNodeVersionRepository.cs | 26 +- .../Repositories/ResourceRepository.cs | 16 + .../ResourceVersionEventRepository.cs | 51 + .../Repositories/ScopeRepository.cs | 46 + .../UserGroupAttributeRepository.cs | 46 + .../Repositories/UserGroupRepository.cs | 99 ++ .../UserPasswordResetRequestsRepository.cs | 84 ++ .../Repositories/UserProviderRepository.cs | 88 ++ .../Startup.cs | 21 + .../Services/IBookmarkService.cs | 9 + .../Services/ICatalogueService.cs | 38 + .../Services/IDashboardService.cs | 38 + .../Services/IEventLogService.cs | 16 + .../Services/IEventService.cs | 35 + .../Services/IMyLearningService.cs | 49 + .../Services/IPageService.cs | 175 +++ .../Services/IResourceReferenceService.cs | 18 + .../Services/IResourceService.cs | 139 +- .../Services/ISearchService.cs | 131 ++ .../Services/IUserGroupService.cs | 148 +- .../Services/IUserLearningRecordService.cs | 24 + .../IUserPasswordResetRequestsService.cs | 26 + .../Services/IUserProviderService.cs | 19 + .../Extensions/PageExtension.cs | 92 ++ .../Helpers/BinaryFormatterHelper.cs | 21 + .../Services/BookmarkService.cs | 28 +- .../Services/CatalogueService.cs | 134 +- .../Services/DashboardService.cs | 140 ++ .../Services/EventLogService.cs | 45 + .../Services/EventService.cs | 99 ++ .../Services/MyLearningService.cs | 507 +++++++ .../Services/PageService.cs | 613 ++++++++ .../Services/ResourceReferenceService.cs | 30 + .../Services/ResourceService.cs | 1262 ++++++++++++++--- .../Services/SearchService.cs | 536 ++++++- .../Services/UserGroupService.cs | 906 +++++++++++- .../Services/UserLearningRecordService.cs | 129 ++ .../UserPasswordResetRequestsService.cs | 80 ++ .../Services/UserProfileService.cs | 4 +- .../Services/UserProviderService.cs | 52 + .../Startup.cs | 13 +- .../LearningHub.Nhs.OpenApi.Tests.csproj | 1 + .../Services/Services/ResourceServiceTests.cs | 11 +- .../Services/Services/SearchServiceTests.cs | 11 +- .../AuthorizeOrCallFromLHHandler.cs | 87 ++ .../AuthorizeOrCallFromLHRequirement.cs | 18 + .../Controllers/BookmarkController.cs | 21 +- .../Controllers/CatalogueController.cs | 52 +- .../Controllers/ContentController.cs | 331 +++++ .../Controllers/DashboardController.cs | 72 + .../Controllers/EventController.cs | 85 ++ .../Controllers/MyLearningController.cs | 86 ++ .../Controllers/ProviderController.cs | 37 +- .../Controllers/ResourceController.cs | 209 ++- .../Controllers/SearchController.cs | 356 +++++ .../Controllers/UserController.cs | 71 +- .../Controllers/UserGroupController.cs | 380 +++++ .../UserLearningRecordController.cs | 67 + .../Controllers/UserNotificationController.cs | 143 ++ .../LearningHub.NHS.OpenAPI.csproj | 71 +- OpenAPI/LearningHub.Nhs.OpenApi/Startup.cs | 15 +- .../SwaggerDefinitions/v1.3.0.json | 50 + .../LearningHub.Nhs.OpenApi/appsettings.json | 5 + 156 files changed, 11809 insertions(+), 1312 deletions(-) create mode 100644 AdminUI/LearningHub.Nhs.AdminUI/Helpers/IOpenApiFacade.cs create mode 100644 AdminUI/LearningHub.Nhs.AdminUI/Helpers/OpenApiFacade.cs create mode 100644 AdminUI/LearningHub.Nhs.AdminUI/Interfaces/IOpenApiHttpClient.cs create mode 100644 AdminUI/LearningHub.Nhs.AdminUI/Services/OpenApiHttpClient.cs create mode 100644 LearningHub.Nhs.Shared/Interfaces/Http/IOpenAPIHttpClient.cs create mode 100644 LearningHub.Nhs.WebUI/Configuration/BFFPathValidationOptions.cs create mode 100644 LearningHub.Nhs.WebUI/Controllers/Api/BFFController.cs create mode 100644 LearningHub.Nhs.WebUI/Helpers/IOpenApiFacade.cs create mode 100644 LearningHub.Nhs.WebUI/Helpers/OpenApiFacade.cs create mode 100644 LearningHub.Nhs.WebUI/Services/OpenApiHttpClient.cs create mode 100644 LearningHub.Nhs.WebUI/Styles/nhsuk/nhsuk-overrides.scss create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Activity/IMediaResourcePlayedSegmentRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Analytics/IEventRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Content/IPageRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Content/IPageSectionDetailRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Content/IPageSectionRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Content/IVideoAssetRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IEventLogRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IScopeRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IUserGroupAttributeRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IUserGroupRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IUserPasswordResetRequestsRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IUserProviderRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/Resources/IResourceVersionEventRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/PasswordResetRequestsMap.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/UserProviderMap.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Activity/MediaResourcePlayedSegmentRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Analytics/EventRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Content/PageRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Content/PageSectionDetailRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Content/PageSectionRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Content/VideoAssetRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/EventLogRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/Resources/ResourceVersionEventRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/ScopeRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/UserGroupAttributeRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/UserGroupRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/UserPasswordResetRequestsRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/UserProviderRepository.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/IDashboardService.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/IEventLogService.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/IEventService.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/IMyLearningService.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/IPageService.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/IResourceReferenceService.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/IUserLearningRecordService.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/IUserPasswordResetRequestsService.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/IUserProviderService.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Services/Extensions/PageExtension.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Services/Helpers/BinaryFormatterHelper.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/DashboardService.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/EventLogService.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/EventService.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/MyLearningService.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/PageService.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/ResourceReferenceService.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/UserLearningRecordService.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/UserPasswordResetRequestsService.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/UserProviderService.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi/Authentication/AuthorizeOrCallFromLHHandler.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi/Authentication/AuthorizeOrCallFromLHRequirement.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi/Controllers/ContentController.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi/Controllers/DashboardController.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi/Controllers/EventController.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi/Controllers/MyLearningController.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi/Controllers/UserGroupController.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi/Controllers/UserLearningRecordController.cs create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi/Controllers/UserNotificationController.cs diff --git a/AdminUI/LearningHub.Nhs.AdminUI/Configuration/WebSettings.cs b/AdminUI/LearningHub.Nhs.AdminUI/Configuration/WebSettings.cs index f8e3c1565..5c8ab554f 100644 --- a/AdminUI/LearningHub.Nhs.AdminUI/Configuration/WebSettings.cs +++ b/AdminUI/LearningHub.Nhs.AdminUI/Configuration/WebSettings.cs @@ -27,6 +27,11 @@ public class WebSettings /// public string LearningHubApiUrl { get; set; } + /// + /// Gets or sets the OpenApiUrl. + /// + public string OpenApiUrl { get; set; } + /// /// Gets or sets the user api url. /// diff --git a/AdminUI/LearningHub.Nhs.AdminUI/Helpers/IOpenApiFacade.cs b/AdminUI/LearningHub.Nhs.AdminUI/Helpers/IOpenApiFacade.cs new file mode 100644 index 000000000..2bcded72b --- /dev/null +++ b/AdminUI/LearningHub.Nhs.AdminUI/Helpers/IOpenApiFacade.cs @@ -0,0 +1,59 @@ +namespace LearningHub.Nhs.AdminUI.Helpers +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Common; + + /// + /// Defines the . + /// + public interface IOpenApiFacade + { + /// + /// The GetAsync. + /// + /// The type. + /// The url. + /// The . + Task GetAsync(string url) + where T : class, new(); + + /// + /// The PostAsync. + /// + /// The type. + /// The url. + /// The body. + /// The . + Task PostAsync(string url, T body) + where T : class, new(); + + /// + /// The PostAsync. + /// + /// The type. + /// . + /// The url. + /// The body. + /// The . + Task PostAsync(string url, TBody body) + where T : class, new() + where TBody : class, new(); + + /// + /// The PutAsync. + /// + /// The url. + /// The . + Task PutAsync(string url); + + /// + /// The PutAsync. + /// + /// . + /// The url. + /// The body. + /// The . + Task PutAsync(string url, T body) + where T : class, new(); + } +} diff --git a/AdminUI/LearningHub.Nhs.AdminUI/Helpers/OpenApiFacade.cs b/AdminUI/LearningHub.Nhs.AdminUI/Helpers/OpenApiFacade.cs new file mode 100644 index 000000000..6169d8e6b --- /dev/null +++ b/AdminUI/LearningHub.Nhs.AdminUI/Helpers/OpenApiFacade.cs @@ -0,0 +1,241 @@ +namespace LearningHub.Nhs.AdminUI.Helpers +{ + using System; + using System.Net.Http; + using System.Text; + using System.Threading.Tasks; + using LearningHub.Nhs.AdminUI.Interfaces; + using LearningHub.Nhs.Models.Common; + using Newtonsoft.Json; + + /// + /// Defines the . + /// + public class OpenApiFacade : IOpenApiFacade + { + /// + /// Defines the _client. + /// + private readonly IOpenApiHttpClient client; + + /// + /// Initializes a new instance of the class. + /// + /// The client. + public OpenApiFacade(IOpenApiHttpClient client) + { + this.client = client; + } + + /// + /// The GetAsync. + /// + /// . + /// The url. + /// The . + public async Task GetAsync(string url) + where T : class, new() + { + var client = await this.client.GetClientAsync(); + + var vm = new T(); + + var response = await client.GetAsync(url).ConfigureAwait(false); + + if (response.IsSuccessStatusCode) + { + var result = response.Content.ReadAsStringAsync().Result; + vm = JsonConvert.DeserializeObject(result); + + return vm; + } + else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized + || + response.StatusCode == System.Net.HttpStatusCode.Forbidden) + { + throw new Exception("AccessDenied"); + } + else + { + throw new Exception($"Exception HttpStatusCode={response.StatusCode}"); + } + } + + /// + /// The PostAsync. + /// + /// . + /// The url. + /// The body. + /// The . + public async Task PostAsync(string url, T body) + where T : class, new() + { + var client = await this.client.GetClientAsync(); + + var content = new StringContent(JsonConvert.SerializeObject(body), Encoding.UTF8, "application/json"); + var response = await client.PostAsync(url, content).ConfigureAwait(false); + + if (response.IsSuccessStatusCode) + { + return; + } + else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized + || + response.StatusCode == System.Net.HttpStatusCode.Forbidden) + { + throw new Exception("AccessDenied"); + } + else + { + throw new Exception($"Exception HttpStatusCode={response.StatusCode}"); + } + } + + /// + /// The PostAsync. + /// + /// The return type. + /// The type of body parameter. + /// The url. + /// The body. + /// The . + public async Task PostAsync(string url, TBody body) + where TBody : class, new() + where T : class, new() + { + var client = await this.client.GetClientAsync(); + + var vm = new T(); + var content = new StringContent(JsonConvert.SerializeObject(body), Encoding.UTF8, "application/json"); + var response = await client.PostAsync(url, content).ConfigureAwait(false); + + if (response.IsSuccessStatusCode) + { + var result = response.Content.ReadAsStringAsync().Result; + var apiResponse = JsonConvert.DeserializeObject(result); + if (apiResponse.Success) + { + return apiResponse; + } + else + { + string details = string.Empty; + if (apiResponse.ValidationResult != null) + { + if (apiResponse.ValidationResult.Details != null) + { + details = $"::ValidationResult: {string.Join(",", apiResponse.ValidationResult.Details)}"; + } + } + + throw new Exception($"PostAsync ApiResponse returned False: {details}"); + } + } + else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized + || + response.StatusCode == System.Net.HttpStatusCode.Forbidden) + { + throw new Exception("Access Denied"); + } + else + { + throw new Exception($"Exception HttpStatusCode={response.StatusCode}"); + } + } + + /// + /// The PutAsync. + /// + /// The url. + /// The . + public async Task PutAsync(string url) + { + var client = await this.client.GetClientAsync(); + + var response = await client.PutAsync(url, null).ConfigureAwait(false); + + if (response.IsSuccessStatusCode) + { + var result = response.Content.ReadAsStringAsync().Result; + var apiResponse = JsonConvert.DeserializeObject(result); + if (apiResponse.Success) + { + return apiResponse; + } + else + { + string details = string.Empty; + if (apiResponse.ValidationResult != null) + { + if (apiResponse.ValidationResult.Details != null) + { + details = $"::ValidationResult: {string.Join(",", apiResponse.ValidationResult.Details)}"; + } + } + + throw new Exception($"PutAsync ApiResponse returned False: {details}"); + } + } + else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized + || + response.StatusCode == System.Net.HttpStatusCode.Forbidden) + { + throw new Exception("Access Denied"); + } + else + { + throw new Exception($"Exception HttpStatusCode={response.StatusCode}"); + } + } + + /// + /// The PutAsync. + /// + /// . + /// The url. + /// The body. + /// The . + public async Task PutAsync(string url, T body) + where T : class, new() + { + var client = await this.client.GetClientAsync(); + + var content = new StringContent(JsonConvert.SerializeObject(body), Encoding.UTF8, "application/json"); + var response = await client.PutAsync(url, content).ConfigureAwait(false); + + if (response.IsSuccessStatusCode) + { + var result = response.Content.ReadAsStringAsync().Result; + var apiResponse = JsonConvert.DeserializeObject(result); + if (apiResponse.Success) + { + return apiResponse; + } + else + { + string details = string.Empty; + if (apiResponse.ValidationResult != null) + { + if (apiResponse.ValidationResult.Details != null) + { + details = $"::ValidationResult: {string.Join(",", apiResponse.ValidationResult.Details)}"; + } + } + + throw new Exception($"PutAsync ApiResponse returned False: {details}"); + } + } + else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized + || + response.StatusCode == System.Net.HttpStatusCode.Forbidden) + { + throw new Exception("Access Denied"); + } + else + { + throw new Exception($"Exception HttpStatusCode={response.StatusCode}"); + } + } + } +} diff --git a/AdminUI/LearningHub.Nhs.AdminUI/Interfaces/IOpenApiHttpClient.cs b/AdminUI/LearningHub.Nhs.AdminUI/Interfaces/IOpenApiHttpClient.cs new file mode 100644 index 000000000..34c488767 --- /dev/null +++ b/AdminUI/LearningHub.Nhs.AdminUI/Interfaces/IOpenApiHttpClient.cs @@ -0,0 +1,17 @@ +namespace LearningHub.Nhs.AdminUI.Interfaces +{ + using System.Net.Http; + using System.Threading.Tasks; + + /// + /// The OpenApiHttpClient interface. + /// + public interface IOpenApiHttpClient + { + /// + /// The get client. + /// + /// The . + Task GetClientAsync(); + } +} diff --git a/AdminUI/LearningHub.Nhs.AdminUI/ServiceCollectionExtension.cs b/AdminUI/LearningHub.Nhs.AdminUI/ServiceCollectionExtension.cs index 81867365f..1039a3673 100644 --- a/AdminUI/LearningHub.Nhs.AdminUI/ServiceCollectionExtension.cs +++ b/AdminUI/LearningHub.Nhs.AdminUI/ServiceCollectionExtension.cs @@ -86,6 +86,7 @@ public static void ConfigureServices(this IServiceCollection services, IConfigur services.AddSingleton(configuration); services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); @@ -132,11 +133,19 @@ public static void ConfigureServices(this IServiceCollection services, IConfigur ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator, }); + services.AddHttpClient() + .ConfigurePrimaryHttpMessageHandler( + () => new HttpClientHandler + { + ServerCertificateCustomValidationCallback = + HttpClientHandler.DangerousAcceptAnyServerCertificateValidator, + }); } else { services.AddHttpClient(); services.AddHttpClient(); + services.AddHttpClient(); } services.AddTransient(); diff --git a/AdminUI/LearningHub.Nhs.AdminUI/Services/BaseService.cs b/AdminUI/LearningHub.Nhs.AdminUI/Services/BaseService.cs index ab2a377e8..c164b5691 100644 --- a/AdminUI/LearningHub.Nhs.AdminUI/Services/BaseService.cs +++ b/AdminUI/LearningHub.Nhs.AdminUI/Services/BaseService.cs @@ -12,6 +12,11 @@ public class BaseService /// private ILearningHubHttpClient learningHubHttpClient; + /// + /// Defines the openApiHttpClient. + /// + private IOpenApiHttpClient openApiHttpClient; + /// /// Initializes a new instance of the class. /// @@ -21,9 +26,25 @@ protected BaseService(ILearningHubHttpClient learningHubHttpClient) this.learningHubHttpClient = learningHubHttpClient; } + /// + /// Initializes a new instance of the class. + /// + /// The learningHubHttpClient. + /// The openApiHttpClient. + protected BaseService(ILearningHubHttpClient learningHubHttpClient, IOpenApiHttpClient openApiHttpClient) + { + this.learningHubHttpClient = learningHubHttpClient; + this.openApiHttpClient = openApiHttpClient; + } + /// /// Gets the LearningHubHttpClient. /// protected ILearningHubHttpClient LearningHubHttpClient => this.learningHubHttpClient; + + /// + /// Gets the OpenApiHttpClient. + /// + protected IOpenApiHttpClient OpenApiHttpClient => this.openApiHttpClient; } } diff --git a/AdminUI/LearningHub.Nhs.AdminUI/Services/CatalogueService.cs b/AdminUI/LearningHub.Nhs.AdminUI/Services/CatalogueService.cs index 1fa44df2b..2ac0088d9 100644 --- a/AdminUI/LearningHub.Nhs.AdminUI/Services/CatalogueService.cs +++ b/AdminUI/LearningHub.Nhs.AdminUI/Services/CatalogueService.cs @@ -19,19 +19,21 @@ public class CatalogueService : BaseService, ICatalogueService /// /// Defines the _facade. /// - private readonly ILearningHubApiFacade facade; + private readonly IOpenApiFacade facade; /// /// Initializes a new instance of the class. /// /// The learningHubHttpClient. - /// The learningHubApiFacade. + /// The Open Api Http Client. + /// The openApiFacade. public CatalogueService( ILearningHubHttpClient learningHubHttpClient, - ILearningHubApiFacade learningHubApiFacade) + IOpenApiHttpClient openApiHttpClient, + IOpenApiFacade openApiFacade) : base(learningHubHttpClient) { - this.facade = learningHubApiFacade; + this.facade = openApiFacade; } /// diff --git a/AdminUI/LearningHub.Nhs.AdminUI/Services/ContentService.cs b/AdminUI/LearningHub.Nhs.AdminUI/Services/ContentService.cs index d49cd8422..ec983fffd 100644 --- a/AdminUI/LearningHub.Nhs.AdminUI/Services/ContentService.cs +++ b/AdminUI/LearningHub.Nhs.AdminUI/Services/ContentService.cs @@ -27,15 +27,17 @@ public class ContentService : BaseService, IContentService /// private readonly IFileService fileService; private readonly IAzureMediaService azureMediaService; + private readonly IOpenApiHttpClient openApiHttpClient; /// /// Initializes a new instance of the class. /// /// The learningHubHttpClient. + /// The openApiHttpClient. /// The fileService. /// azureMediaService. - public ContentService(ILearningHubHttpClient learningHubHttpClient, IFileService fileService, IAzureMediaService azureMediaService) - : base(learningHubHttpClient) + public ContentService(ILearningHubHttpClient learningHubHttpClient, IOpenApiHttpClient openApiHttpClient, IFileService fileService, IAzureMediaService azureMediaService) + : base(learningHubHttpClient, openApiHttpClient) { this.fileService = fileService; this.azureMediaService = azureMediaService; @@ -48,7 +50,7 @@ public ContentService(ILearningHubHttpClient learningHubHttpClient, IFileService /// The . public async Task DiscardAsync(int pageId) { - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"content/discard/{pageId}"; var response = await client.PutAsync(request, null).ConfigureAwait(false); if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized || @@ -68,7 +70,7 @@ public async Task GetPageByIdAsync(int id, bool includeHidden = f { PageViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = !includeHidden ? $"content/page/{id}" : $"content/page-all/{id}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -97,7 +99,7 @@ public async Task GetPageSectionDetailByIdAsync(int { PageSectionDetailViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"content/page-section-detail/{id}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -126,7 +128,7 @@ public async Task GetEditablePageSectionDetailByIdAs { PageSectionDetailViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"content/editable-page-section-detail/{pageSectionId}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -154,7 +156,7 @@ public async Task GetPagesAsync() { PageResultViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"content/pages"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -181,7 +183,7 @@ public async Task GetPagesAsync() /// The . public async Task PublishAsync(int pageId) { - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"content/publish/{pageId}"; var response = await client.PutAsync(request, null).ConfigureAwait(false); if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized || @@ -214,7 +216,7 @@ public async Task UpdatePageImageSectionDetailAsync(int pageId, PageImageSection var json = JsonConvert.SerializeObject(model); var stringContent = new StringContent(json, Encoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"content/page-image-section-detail/{pageId}"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -232,7 +234,7 @@ public async Task UpdatePageImageSectionDetailAsync(int pageId, PageImageSection /// The . public async Task ChangeOrderAsync(UpdatePageSectionOrderModel requestViewModel) { - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = "content/change-order/"; var content = new StringContent( JsonConvert.SerializeObject(requestViewModel), @@ -259,7 +261,7 @@ public async Task ChangeOrderAsync(UpdatePageSectionOrderModel requestViewModel) /// The . public async Task CloneAsync(int pageSectionId) { - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"content/clone/{pageSectionId}"; var response = await client.PutAsync(request, null).ConfigureAwait(false); if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized || @@ -276,7 +278,7 @@ public async Task CloneAsync(int pageSectionId) /// The . public async Task HideAsync(int pageSectionId) { - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"content/hide/{pageSectionId}"; var response = await client.PutAsync(request, null).ConfigureAwait(false); if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized || @@ -293,7 +295,7 @@ public async Task HideAsync(int pageSectionId) /// The . public async Task UnHideAsync(int pageSectionId) { - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"content/unhide/{pageSectionId}"; var response = await client.PutAsync(request, null).ConfigureAwait(false); if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized || @@ -310,7 +312,7 @@ public async Task UnHideAsync(int pageSectionId) /// The . public async Task DeleteAsync(int pageSectionId) { - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"content/delete/{pageSectionId}"; var response = await client.PutAsync(request, null).ConfigureAwait(false); if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized || @@ -376,7 +378,7 @@ public async Task> GetFileTypeAsync() { List fileTypeList = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetFileTypes"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -459,7 +461,7 @@ public async Task SaveAttributeFileDetailsAsync(FileCreateRequestViewModel var json = JsonConvert.SerializeObject(fileCreateRequestViewModel); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"content/save-attribute-file"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -493,7 +495,7 @@ public async Task SaveVideoAssetAsync(FileCreateRequestViewModel fileCreate var json = JsonConvert.SerializeObject(fileCreateRequestViewModel); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"content/save-video-asset"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -593,7 +595,7 @@ public async Task GetFileChunkDetail(int fileChunkDeta { FileChunkDetailViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetFileChunkDetail/{fileChunkDetailId}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -624,7 +626,7 @@ public async Task SaveFileChunkDetailsAsync(FileChunkDetailViewModel fileCh var json = JsonConvert.SerializeObject(fileChunkDetailCreateRequestViewModel); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/SaveFileChunkDetail"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -682,7 +684,7 @@ public async Task RegisterChunkedFileAsync(FileChunkRegisterMo public async Task DeleteFileChunkDetailAsync(int fileChunkDetailId) { ApiResponse apiResponse = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/DeleteFileChunkDetail/{fileChunkDetailId}"; var response = await client.DeleteAsync(request).ConfigureAwait(false); @@ -814,7 +816,7 @@ public async Task UpdateVideoAssetAsync(VideoAssetViewModel model) var json = JsonConvert.SerializeObject(model); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"content/update-video-asset"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -844,7 +846,7 @@ public async Task UpdateVideoAssetAsync(VideoAssetViewModel model) /// The . public async Task CreatePageSectionAsync(PageSectionViewModel requestViewModel) { - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = "content/create-page-section/"; var content = new StringContent( JsonConvert.SerializeObject(requestViewModel), @@ -874,7 +876,7 @@ public async Task CreatePageSectionAsync(PageSectionViewModel requestViewMo /// The . public async Task UpdatePageSectionDetailAsync(PageSectionDetailViewModel updateViewModel) { - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = "content/update-page-section-detail/"; var content = new StringContent( JsonConvert.SerializeObject(updateViewModel), @@ -899,7 +901,7 @@ public async Task GetPageSectionDetailVideoAssetById { PageSectionDetailViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"content/page-section-detail-video/{id}"; var response = await client.GetAsync(request).ConfigureAwait(false); diff --git a/AdminUI/LearningHub.Nhs.AdminUI/Services/EventService.cs b/AdminUI/LearningHub.Nhs.AdminUI/Services/EventService.cs index 000fce940..db61e47e3 100644 --- a/AdminUI/LearningHub.Nhs.AdminUI/Services/EventService.cs +++ b/AdminUI/LearningHub.Nhs.AdminUI/Services/EventService.cs @@ -18,8 +18,9 @@ public class EventService : BaseService, IEventService /// Initializes a new instance of the class. /// /// The learningHubHttpClient. - public EventService(ILearningHubHttpClient learningHubHttpClient) - : base(learningHubHttpClient) + /// The openApiHttpClient. + public EventService(ILearningHubHttpClient learningHubHttpClient, IOpenApiHttpClient openApiHttpClient) + : base(learningHubHttpClient, openApiHttpClient) { } @@ -30,7 +31,7 @@ public async Task Create(Event eventEntity) var json = JsonConvert.SerializeObject(eventEntity); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Event/Create"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); diff --git a/AdminUI/LearningHub.Nhs.AdminUI/Services/HierarchyService.cs b/AdminUI/LearningHub.Nhs.AdminUI/Services/HierarchyService.cs index b408cb637..17b37999b 100644 --- a/AdminUI/LearningHub.Nhs.AdminUI/Services/HierarchyService.cs +++ b/AdminUI/LearningHub.Nhs.AdminUI/Services/HierarchyService.cs @@ -16,21 +16,21 @@ public class HierarchyService : BaseService, IHierarchyService /// /// Defines the _facade. /// - private readonly ILearningHubApiFacade facade; + private readonly IOpenApiFacade facade; /// /// Initializes a new instance of the class. /// /// The learning hub http client. - /// The learningHubApiFacade. + /// The openApiFacade. /// The logger. public HierarchyService( ILearningHubHttpClient learningHubHttpClient, - ILearningHubApiFacade learningHubApiFacade, + IOpenApiFacade openApiFacade, ILogger logger) : base(learningHubHttpClient) { - this.facade = learningHubApiFacade; + this.facade = openApiFacade; } /// diff --git a/AdminUI/LearningHub.Nhs.AdminUI/Services/OpenApiHttpClient.cs b/AdminUI/LearningHub.Nhs.AdminUI/Services/OpenApiHttpClient.cs new file mode 100644 index 000000000..5e8463cad --- /dev/null +++ b/AdminUI/LearningHub.Nhs.AdminUI/Services/OpenApiHttpClient.cs @@ -0,0 +1,39 @@ +namespace LearningHub.Nhs.AdminUI.Services +{ + using System.Net.Http; + using LearningHub.Nhs.AdminUI.Configuration; + using LearningHub.Nhs.AdminUI.Interfaces; + using LearningHub.Nhs.Caching; + using Microsoft.AspNetCore.Http; + using Microsoft.Extensions.Logging; + using Microsoft.Extensions.Options; + + /// + /// The open api http client. + /// + public class OpenApiHttpClient : BaseHttpClient, IOpenApiHttpClient + { + /// + /// Initializes a new instance of the class. + /// + /// The http context accessor. + /// The web settings. + /// The http client. + /// The logger. + /// The cache service. + public OpenApiHttpClient( + IHttpContextAccessor httpContextAccessor, + IOptions webSettings, + HttpClient client, + ILogger logger, + ICacheService cacheService) + : base(httpContextAccessor, webSettings.Value, client, logger, cacheService) + { + } + + /// + /// Gets the open api url. + /// + public override string ApiUrl => this.WebSettings.OpenApiUrl; + } +} diff --git a/AdminUI/LearningHub.Nhs.AdminUI/Services/ProviderService.cs b/AdminUI/LearningHub.Nhs.AdminUI/Services/ProviderService.cs index eeb6976f9..cfdc4050b 100644 --- a/AdminUI/LearningHub.Nhs.AdminUI/Services/ProviderService.cs +++ b/AdminUI/LearningHub.Nhs.AdminUI/Services/ProviderService.cs @@ -24,19 +24,20 @@ public class ProviderService : BaseService, IProviderService /// /// Defines the _facade. /// - private readonly ILearningHubApiFacade facade; + private readonly IOpenApiFacade facade; /// /// Initializes a new instance of the class. /// /// The cache service. /// Learning hub http client. - /// The learningHubApiFacade. - public ProviderService(ICacheService cacheService, ILearningHubHttpClient learningHubHttpClient, ILearningHubApiFacade learningHubApiFacade) + /// open api http client. + /// The openApiFacade. + public ProviderService(ICacheService cacheService, ILearningHubHttpClient learningHubHttpClient, IOpenApiHttpClient openApiHttpClient, IOpenApiFacade openApiFacade) : base(learningHubHttpClient) { this.cacheService = cacheService; - this.facade = learningHubApiFacade; + this.facade = openApiFacade; } /// diff --git a/AdminUI/LearningHub.Nhs.AdminUI/Services/ResourceService.cs b/AdminUI/LearningHub.Nhs.AdminUI/Services/ResourceService.cs index 4c6e3fe46..038681c64 100644 --- a/AdminUI/LearningHub.Nhs.AdminUI/Services/ResourceService.cs +++ b/AdminUI/LearningHub.Nhs.AdminUI/Services/ResourceService.cs @@ -24,8 +24,9 @@ public class ResourceService : BaseService, IResourceService /// Initializes a new instance of the class. /// /// The learningHubHttpClient. - public ResourceService(ILearningHubHttpClient learningHubHttpClient) - : base(learningHubHttpClient) + /// The openApiHttpClient. + public ResourceService(ILearningHubHttpClient learningHubHttpClient, IOpenApiHttpClient openApiHttpClient) + : base(learningHubHttpClient, openApiHttpClient) { } @@ -50,7 +51,7 @@ public async Task> GetResourc var filter = JsonConvert.SerializeObject(pagingRequestModel.Filter); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var json = JsonConvert.SerializeObject(pagingRequestModel); var stringContent = new StringContent(json, Encoding.UTF8, "application/json"); @@ -82,7 +83,7 @@ public async Task> GetResourceVersionEventsA { List viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetResourceVersionEvents/{resourceVersionId.ToString()}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -111,7 +112,7 @@ public async Task GetResourceVersionVa { ResourceVersionValidationResultViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetResourceVersionValidationResult/{resourceVersionId.ToString()}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -140,7 +141,7 @@ public async Task GetResourceVersionDevIdDetailsA { ResourceVersionDevIdViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetResourceVersionDevIdDetails/{resourceVersionId.ToString()}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -167,7 +168,7 @@ public async Task GetResourceVersionDevIdDetailsA /// The . public async Task DoesDevIdExistsAsync(string devId) { - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/DoesDevIdExists/{devId}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -198,7 +199,7 @@ public async Task UpdateDevIdDetailsAsync(ResourceVersionDevIdViewModel model) var json = JsonConvert.SerializeObject(model); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/UpdateDevId"; var response = await client.PutAsync(request, stringContent).ConfigureAwait(false); @@ -223,7 +224,7 @@ public async Task GetResourceVersionExtendedVi { ResourceVersionExtendedViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetResourceVersionExtendedViewModel/{resourceVersionId.ToString()}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -252,7 +253,7 @@ public async Task> GetResourceVersionsAsync(int r { List viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetResourceVersions/{resourceId.ToString()}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -283,7 +284,7 @@ public async Task RevertToDraft(int resourceVersion var json = JsonConvert.SerializeObject(new { resourceVersionId }); var stringContent = new StringContent(resourceVersionId.ToString(), Encoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/RevertToDraft"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -335,7 +336,7 @@ public async Task TransferResourceOwnership(int res var json = JsonConvert.SerializeObject(vm); var stringContent = new StringContent(json, Encoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/TransferResourceOwnership"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -382,7 +383,7 @@ public async Task UnpublishResourceVersionAsync(int var json = JsonConvert.SerializeObject(vm); var stringContent = new StringContent(json, Encoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/UnpublishResourceVersion"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -433,7 +434,7 @@ public async Task CreateResourceVersionEvent(int re var json = JsonConvert.SerializeObject(vm); var stringContent = new StringContent(json, Encoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/CreateResourceVersionEvent"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); diff --git a/AdminUI/LearningHub.Nhs.AdminUI/Services/UserGroupService.cs b/AdminUI/LearningHub.Nhs.AdminUI/Services/UserGroupService.cs index 45dff59de..0a941c761 100644 --- a/AdminUI/LearningHub.Nhs.AdminUI/Services/UserGroupService.cs +++ b/AdminUI/LearningHub.Nhs.AdminUI/Services/UserGroupService.cs @@ -32,15 +32,17 @@ public class UserGroupService : BaseService, IUserGroupService /// Initializes a new instance of the class. /// /// The learningHubHttpClient. + /// The Open Api Http Client. /// The http context accessor. /// The cacheService. /// The roleService. public UserGroupService( ILearningHubHttpClient learningHubHttpClient, + IOpenApiHttpClient openApiHttpClient, ICacheService cacheService, IRoleService roleService, IHttpContextAccessor contextAccessor) - : base(learningHubHttpClient) + : base(learningHubHttpClient, openApiHttpClient) { this.contextAccessor = contextAccessor; this.cacheService = cacheService; @@ -112,7 +114,7 @@ public async Task GetUserGroupAdminDetailbyIdAsyn { UserGroupAdminDetailViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"UserGroup/GetUserGroupAdminDetailById/{id}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -141,7 +143,7 @@ public async Task> GetUserGroupAdminRoleDetailByIdA { List viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"UserGroup/GetUserGroupAdminRoleDetailById/{id}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -171,7 +173,7 @@ public async Task CreateUserGroup(UserGroupAdminDet var json = JsonConvert.SerializeObject(userGroup); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"UserGroup/CreateUserGroup"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -214,7 +216,7 @@ public async Task UpdateUserGroup(UserGroupAdminDet var json = JsonConvert.SerializeObject(userGroup); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"UserGroup/UpdateUserGroup"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -258,7 +260,7 @@ public async Task DeleteUserGroup(int userGroupId) var json = JsonConvert.SerializeObject(userGroup); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"UserGroup/DeleteUserGroup"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -308,7 +310,7 @@ public async Task AddUsersToUserGroup(int userGroup var json = JsonConvert.SerializeObject(userUserGroups); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"UserGroup/AddUserUserGroups"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -359,7 +361,7 @@ public async Task AddUserGroupsToCatalogue(int cata var json = JsonConvert.SerializeObject(roleUserGroups); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"UserGroup/AddRoleUserGroups"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -403,7 +405,7 @@ public async Task DeleteUserUserGroup(int userUserG var json = JsonConvert.SerializeObject(userGroup); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"UserGroup/DeleteUserUserGroup"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -447,7 +449,7 @@ public async Task DeleteRoleUserGroup(int roleUserG var json = JsonConvert.SerializeObject(roleUserGroupUpdate); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"UserGroup/DeleteRoleUserGroup"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -502,7 +504,7 @@ public async Task> GetUserUserGroupPageAs var filter = JsonConvert.SerializeObject(pagingRequestModel.Filter); var presetFilter = JsonConvert.SerializeObject(pagingRequestModel.PresetFilter); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"UserGroup/GetUserUserGroupAdminFilteredPage" + $"/{pagingRequestModel.Page}" @@ -551,7 +553,7 @@ public async Task> GetRoleUserGroupPageAs var filter = JsonConvert.SerializeObject(pagingRequestModel.Filter); var presetFilter = JsonConvert.SerializeObject(pagingRequestModel.PresetFilter); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"UserGroup/GetRoleUserGroupAdminFilteredPage" + $"/{pagingRequestModel.Page}" @@ -598,7 +600,7 @@ private async Task> FetchRoleUserGroupDetailAsync() { List viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"UserGroup/GetUserGroupRoleDetail"; var response = await client.GetAsync(request).ConfigureAwait(false); diff --git a/AdminUI/LearningHub.Nhs.AdminUI/Services/UserService.cs b/AdminUI/LearningHub.Nhs.AdminUI/Services/UserService.cs index 7c64fd2c0..c84dbc2f2 100644 --- a/AdminUI/LearningHub.Nhs.AdminUI/Services/UserService.cs +++ b/AdminUI/LearningHub.Nhs.AdminUI/Services/UserService.cs @@ -31,10 +31,11 @@ public class UserService : BaseService, IUserService /// Initializes a new instance of the class. /// /// The learningHubHttpClient. + /// The openApiHttpClient . /// The CacheService . /// The userApiHttpClient . - public UserService(ILearningHubHttpClient learningHubHttpClient, ICacheService cacheService, IUserApiHttpClient userApiHttpClient) - : base(learningHubHttpClient) + public UserService(ILearningHubHttpClient learningHubHttpClient, IOpenApiHttpClient openApiHttpClient, ICacheService cacheService, IUserApiHttpClient userApiHttpClient) + : base(learningHubHttpClient, openApiHttpClient) { this.cacheService = cacheService; this.userApiHttpClient = userApiHttpClient; @@ -166,7 +167,7 @@ public async Task GetEmailAddressRegistrationStatusAsyn var filter = JsonConvert.SerializeObject(pagingRequestModel.Filter); var presetFilter = JsonConvert.SerializeObject(pagingRequestModel.PresetFilter); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"User/GetLHUserAdminBasicFilteredPage" + $"/{pagingRequestModel.Page}" @@ -274,7 +275,7 @@ public async Task> GetUserCon var modelString = JsonConvert.SerializeObject(pagingRequestModel); var content = new StringContent(modelString, Encoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetResourceAdminSearchFilteredPage"; @@ -459,7 +460,7 @@ public async Task AddUserGroupsToUser(int userId, s var json = JsonConvert.SerializeObject(userUserGroups); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"UserGroup/AddUserUserGroups"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -565,7 +566,7 @@ public async Task> GetUserLearni var filter = JsonConvert.SerializeObject(pagingRequestModel.Filter); var presetFilter = JsonConvert.SerializeObject(pagingRequestModel.PresetFilter); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"UserLearningRecord/GetUserLearningRecords" + $"/{pagingRequestModel.Page}" diff --git a/AdminUI/LearningHub.Nhs.AdminUI/appsettings.json b/AdminUI/LearningHub.Nhs.AdminUI/appsettings.json index b3a5c0f9c..95a04b8bc 100644 --- a/AdminUI/LearningHub.Nhs.AdminUI/appsettings.json +++ b/AdminUI/LearningHub.Nhs.AdminUI/appsettings.json @@ -17,6 +17,7 @@ "ELfhHubUrl": "", "LearningHubApiUrl": "", "UserApiUrl": "", + "OpenApiUrl": "", "LearningHubAdminUrl": "", "LogConfigDir": "D:\\learningHub\\NLog", "AuthenticationServiceUrl": "", diff --git a/LearningHub.Nhs.Shared/Interfaces/Http/IOpenAPIHttpClient.cs b/LearningHub.Nhs.Shared/Interfaces/Http/IOpenAPIHttpClient.cs new file mode 100644 index 000000000..d20a3eab2 --- /dev/null +++ b/LearningHub.Nhs.Shared/Interfaces/Http/IOpenAPIHttpClient.cs @@ -0,0 +1,21 @@ +namespace LearningHub.Nhs.Shared.Interfaces.Http +{ + /// + /// Marker interface for the Open API HttpClient. + /// + /// + /// Inherits from to enable + /// dependency injection of a specific implementation configured with + /// a user-related API endpoint or settings. + /// + /// + /// + /// This interface is currently empty and used solely to differentiate + /// implementations that connect to different endpoints via configuration. + /// It may be extended in the future with user-specific functionality or properties. + /// + /// + public interface IOpenApiHttpClient : IAPIHttpClient + { + } +} diff --git a/LearningHub.Nhs.Shared/Services/BaseService.cs b/LearningHub.Nhs.Shared/Services/BaseService.cs index 7078f9838..f0a1c07ed 100644 --- a/LearningHub.Nhs.Shared/Services/BaseService.cs +++ b/LearningHub.Nhs.Shared/Services/BaseService.cs @@ -11,6 +11,7 @@ public abstract class BaseService { private readonly ILogger logger; private readonly ILearningHubHttpClient learningHubHttpClient; + private readonly IOpenApiHttpClient openApiHttpClient; /// /// Initializes a new instance of the class. @@ -23,11 +24,29 @@ protected BaseService(ILearningHubHttpClient learningHubHttpClient, ILogger l this.logger = logger; } + /// + /// Initializes a new instance of the class. + /// + /// The learningHubHttpClient. + /// The openApiHttpClient. + /// The logger. + protected BaseService(ILearningHubHttpClient learningHubHttpClient, IOpenApiHttpClient openApiHttpClient, ILogger logger) + { + this.learningHubHttpClient = learningHubHttpClient; + this.openApiHttpClient = openApiHttpClient; + this.logger = logger; + } + /// /// Gets the LearningHubHttpClient. /// protected ILearningHubHttpClient LearningHubHttpClient => this.learningHubHttpClient; + /// + /// Gets the OpenApiHttpClient. + /// + protected IOpenApiHttpClient OpenApiHttpClient => this.openApiHttpClient; + /// /// Gets the Logger. /// diff --git a/LearningHub.Nhs.Shared/Services/ProviderService.cs b/LearningHub.Nhs.Shared/Services/ProviderService.cs index c2c31b4b1..a4d56ed47 100644 --- a/LearningHub.Nhs.Shared/Services/ProviderService.cs +++ b/LearningHub.Nhs.Shared/Services/ProviderService.cs @@ -23,9 +23,10 @@ public class ProviderService : BaseService, IProviderService /// /// The cache service. /// Learning hub http client. + /// The Open Api Http Client. /// Logger. - public ProviderService(ICacheService cacheService, ILearningHubHttpClient learningHubHttpClient, ILogger logger) - : base(learningHubHttpClient, logger) + public ProviderService(ICacheService cacheService, ILearningHubHttpClient learningHubHttpClient, IOpenApiHttpClient openApiHttpClient, ILogger logger) + : base(learningHubHttpClient, openApiHttpClient, logger) { this.cacheService = cacheService; } @@ -48,7 +49,7 @@ public async Task> GetProvidersForUserAsync(int userId) { List viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Provider/GetProvidersByUserId/{userId}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -77,7 +78,7 @@ public async Task> GetProvidersForResourceAsync(int reso { List viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Provider/GetProvidersByResource/{resourceVersionId}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -112,7 +113,7 @@ private async Task> GetAllProviders() { List viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Provider/all"; var response = await client.GetAsync(request).ConfigureAwait(false); diff --git a/LearningHub.Nhs.WebUI/Configuration/BFFPathValidationOptions.cs b/LearningHub.Nhs.WebUI/Configuration/BFFPathValidationOptions.cs new file mode 100644 index 000000000..6da85a403 --- /dev/null +++ b/LearningHub.Nhs.WebUI/Configuration/BFFPathValidationOptions.cs @@ -0,0 +1,25 @@ +namespace LearningHub.Nhs.WebUI.Configuration +{ + using System.Collections.Generic; + + /// + /// Configuration options for validating BFF paths. + /// + public class BFFPathValidationOptions + { + /// + /// Gets the section name for BFF path validation options. + /// + public const string SectionName = "BFFPathValidation"; + + /// + /// Gets or sets fine tuning. Allows for fine tuning of what paths the BFF can be used to access and what not to. + /// + public List AllowedPathPrefixes { get; set; } = new List(); + + /// + /// Gets or sets fine tuning of what paths the BFF can be used to access and what not to. + /// + public List BlockedPathSegments { get; set; } = new List(); + } +} diff --git a/LearningHub.Nhs.WebUI/Configuration/Settings.cs b/LearningHub.Nhs.WebUI/Configuration/Settings.cs index 3e6116565..e16e7e231 100644 --- a/LearningHub.Nhs.WebUI/Configuration/Settings.cs +++ b/LearningHub.Nhs.WebUI/Configuration/Settings.cs @@ -32,6 +32,11 @@ public Settings() /// public string LearningHubApiUrl { get; set; } + /// + /// Gets or sets the OpenApiUrl. + /// + public string OpenApiUrl { get; set; } + /// /// Gets or sets the UserApiUrl. /// diff --git a/LearningHub.Nhs.WebUI/Controllers/Api/BFFController.cs b/LearningHub.Nhs.WebUI/Controllers/Api/BFFController.cs new file mode 100644 index 000000000..029fcde57 --- /dev/null +++ b/LearningHub.Nhs.WebUI/Controllers/Api/BFFController.cs @@ -0,0 +1,248 @@ +namespace LearningHub.Nhs.WebUI.Controllers.Api +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Net.Http; + using System.Net.Http.Headers; + using System.Threading.Tasks; + using LearningHub.Nhs.Shared.Interfaces.Http; + using LearningHub.Nhs.WebUI.Configuration; + using Microsoft.AspNetCore.Authorization; + using Microsoft.AspNetCore.Mvc; + using Microsoft.Extensions.Logging; + using Microsoft.Extensions.Options; + + /// + /// This controller allows proxying of requests to different APIs using same site cookie authentication. + /// It uses the http clients registered in the DI container. + /// The BFF (Backend for Frontend) pattern is used to simplify client-side code and centralize API access. + /// Unauthorized requests will be redirected to the login page so 302s are expected when unauthorized, and redirecting for using a Blazor island component for example may not be desireable so these responses need to be handled by the caller. + /// This controller is designed to be used with a clientside calls i.e. Blazor utilizing the BFF pattern, which enables same site cookie authentication and avoid the necessity of storing tokens in client storage + /// The bff prefix is followed by the API name (e.g. "learninghub", "userapi") and the path to the specific endpoint to enable easy routing to different APIs. + /// See confluence for more details on the BFF pattern and how to use this controller. + /// + [Authorize] + [Route("bff/{apiName}/{**path}")] + [ApiController] + public class BFFController : BaseApiController + { + private readonly IOptions bffPathValidationOptions; + + /// + /// The list of API clients that can be used to proxy requests. + /// + private List apiClients; + + /// + /// Initializes a new instance of the class. + /// + /// The logger instance used for logging. + /// The HTTP client for the Learning Hub API. + /// The HTTP client for the User API. + /// The HTTP client for the Open API. + /// The application settings. + /// The options for validating BFF paths. + public BFFController( + ILogger logger, + ILearningHubHttpClient learningHubClient, + IUserApiHttpClient userAPIClient, + IOpenApiHttpClient openAPIClient, + IOptions bffPathValidationOptions) + : base(logger) + { + // qqqq should i be using the httpfactory - i think no + // services.AddHttpClient(); + // services.AddHttpClient(); + // services.AddHttpClient(); + // services.AddHttpClient(); + this.apiClients = new List() + { + learningHubClient, + userAPIClient, + openAPIClient, + }; + + this.bffPathValidationOptions = bffPathValidationOptions; + } + + /// + /// Takes an API name and a path, and proxies the request to the appropriate API provided that api is part of the client list and the path is allowed and not blocked. + /// + /// The name of the API to which the request should be proxied. + /// The path of the endpoint within the specified API. + /// An representing the result of the proxied request. + [HttpGet] + [HttpPost] + [HttpPut] + [HttpDelete] + [HttpPatch] + public async Task ProxyRequest(string apiName, string path) + { + // qqqq + // we want to do this - oh but it wont be in domain! + // it will be https://lh-web.dev.local/bff/ the api name so we need to set the name the same ... it doesnt need to be the same but may aswell + // the other local ones are https://lh-web.dev.local/api/ + // https://lh-openapi.dev.local/ + // "LearningHubUrl": "https://lh-web.dev.local/", + // "ELfhHubUrl": "https://test-portal.e-lfhtech.org.uk ", + // "LearningHubApiUrl": "https://lh-api.dev.local/api/", + // "UserApiUrl": "https://lh-userapi.dev.local/api/", + // "LearningHubAdminUrl": "https://lh-admin.dev.local/", + string sanitizedPath = path?.Trim('/').ToLowerInvariant() ?? string.Empty; + string sanitizedApiName = apiName?.Trim('/').ToLowerInvariant() ?? string.Empty; + + IAPIHttpClient apiClient; + try + { + // qqqq qqqq !!!! need single end point to test + // ApiUrl = "https://lh-api.dev.local/api/" + // "lh-api.dev.local" + apiClient = this.apiClients.Single(x => + { + try + { + var uri = new Uri(x.ApiUrl); + return uri.Host.ToLowerInvariant() == sanitizedApiName; + } + catch + { + return false; + } + }); + } + catch (Exception e) + { + return this.BadRequest($"Unknown API alias: {sanitizedApiName}"); + } + + // api/catalogue/getlatestcatalogueaccessrequest/500 qqqq + if (!this.IsPathAllowed(sanitizedPath)) + { + return this.Forbid("This path is not allowed via BFF proxy."); + } + + var client = await apiClient.GetClientAsync(); + string targetUrl = $"{apiClient.ApiUrl.TrimEnd('/')}/{path}"; + + // Add query parameters from the original request + if (this.Request.QueryString.HasValue) + { + targetUrl += this.Request.QueryString.Value; + } + + // No headers added becaue all security is handled by host httpclients via baseclient + // Copy body if necessary (for POST, PUT, PATCH, etc.) + var method = new HttpMethod(this.Request.Method); + var requestMessage = new HttpRequestMessage(method, targetUrl); + + if (this.Request.ContentLength > 0 && + !string.Equals(this.Request.Method, "GET", StringComparison.OrdinalIgnoreCase) && + !string.Equals(this.Request.Method, "HEAD", StringComparison.OrdinalIgnoreCase)) + { + requestMessage.Content = new StreamContent(this.Request.Body); + if (!string.IsNullOrEmpty(this.Request.ContentType)) + { + requestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue(this.Request.ContentType); + } + } + + try + { + var response = await client.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead); + + // Handle redirects with token preservation + // if we are redirected the client may not handle it as it isnt the token holder so we need to continue using the bff until we get the outcome + // qqqq we would avoid hitting authorization because we dont want to redirect the component to a page its the mvc that would want redirecting, the mvc page to another mvc page. So we may never need this + if (response.StatusCode == System.Net.HttpStatusCode.Redirect || + response.StatusCode == System.Net.HttpStatusCode.Found || + response.StatusCode == System.Net.HttpStatusCode.TemporaryRedirect || + response.StatusCode == System.Net.HttpStatusCode.PermanentRedirect) + { + return await this.HandleRedirect(response, apiClient); + } + + var content = await response.Content.ReadAsStringAsync(); + var contentType = response.Content.Headers.ContentType?.MediaType ?? "application/json"; + + return new ContentResult + { + Content = content, + ContentType = contentType, + StatusCode = (int)response.StatusCode, + }; + } + catch (HttpRequestException ex) + { + this.Logger.LogError(ex, "Error proxying request to {TargetUrl}", targetUrl); + return this.StatusCode(500, "An error occurred while processing the request."); + } + } + + // qqqq make sure this gets tested + private async Task HandleRedirect(HttpResponseMessage response, IAPIHttpClient apiClient) + { + var location = response.Headers.Location?.ToString(); + + if (string.IsNullOrEmpty(location)) + { + return this.StatusCode((int)response.StatusCode, "Redirect location not found"); + } + + // Check if the redirect location is relative or absolute + string redirectUrl; + if (Uri.IsWellFormedUriString(location, UriKind.Absolute)) + { + redirectUrl = location; + } + else + { + // Handle relative redirects + var baseUri = new Uri(apiClient.ApiUrl); + redirectUrl = new Uri(baseUri, location).ToString(); + } + + // Create a new request for the redirect + var redirectRequest = new HttpRequestMessage(HttpMethod.Get, redirectUrl); + + // Add authentication token to the redirect request (apiClient handles this) + // No additional headers needed - apiClient is already configured + try + { + var client = await apiClient.GetClientAsync(); + var redirectResponse = await client.SendAsync(redirectRequest); + var content = await redirectResponse.Content.ReadAsStringAsync(); + var contentType = redirectResponse.Content.Headers.ContentType?.MediaType ?? "application/json"; + + return new ContentResult + { + Content = content, + ContentType = contentType, + StatusCode = (int)redirectResponse.StatusCode, + }; + } + catch (HttpRequestException ex) + { + this.Logger.LogError(ex, "Error following redirect to {RedirectUrl}", redirectUrl); + return this.StatusCode(500, "An error occurred while following the redirect."); + } + } + + /// + /// Validates the path against allowed and blocked segments. + /// + private bool IsPathAllowed(string path) + { + var normalizedPath = path?.Trim('/').ToLowerInvariant() ?? string.Empty; + + // Check blacklist first + if (this.bffPathValidationOptions.Value.BlockedPathSegments.Any(blocked => normalizedPath.Contains(blocked.ToLowerInvariant()))) + { + return false; + } + + // Check whitelist + return this.bffPathValidationOptions.Value.AllowedPathPrefixes.Any(prefix => normalizedPath.StartsWith(prefix.ToLowerInvariant())); + } + } +} diff --git a/LearningHub.Nhs.WebUI/Controllers/SearchController.cs b/LearningHub.Nhs.WebUI/Controllers/SearchController.cs index 9d92a4277..cada3e014 100644 --- a/LearningHub.Nhs.WebUI/Controllers/SearchController.cs +++ b/LearningHub.Nhs.WebUI/Controllers/SearchController.cs @@ -83,7 +83,7 @@ public async Task Index(SearchRequestViewModel search, bool noSor if (filterApplied) { - await this.searchService.RegisterSearchEventsAsync(search, SearchFormActionTypeEnum.ApplyFilter, searchResult.ResourceSearchResult.TotalHits); + await this.searchService.RegisterSearchEventsAsync(search, SearchFormActionTypeEnum.ApplyFilter, searchResult.ResourceSearchResult?.TotalHits ?? 0); } if (noSortFilterError) diff --git a/LearningHub.Nhs.WebUI/Helpers/IOpenApiFacade.cs b/LearningHub.Nhs.WebUI/Helpers/IOpenApiFacade.cs new file mode 100644 index 000000000..a603461c7 --- /dev/null +++ b/LearningHub.Nhs.WebUI/Helpers/IOpenApiFacade.cs @@ -0,0 +1,59 @@ +namespace LearningHub.Nhs.WebUI.Helpers +{ + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Common; + + /// + /// Defines the . + /// + public interface IOpenApiFacade + { + /// + /// The GetAsync. + /// + /// The type. + /// The url. + /// The . + Task GetAsync(string url) + where T : class, new(); + + /// + /// The PostAsync. + /// + /// The type. + /// The url. + /// The body. + /// The . + Task PostAsync(string url, T body) + where T : class, new(); + + /// + /// The PostAsync. + /// + /// The type. + /// . + /// The url. + /// The body. + /// The . + Task PostAsync(string url, TBody body) + where T : class, new() + where TBody : class, new(); + + /// + /// The PutAsync. + /// + /// The url. + /// The . + Task PutAsync(string url); + + /// + /// The PutAsync. + /// + /// . + /// The url. + /// The body. + /// The . + Task PutAsync(string url, T body) + where T : class, new(); + } +} diff --git a/LearningHub.Nhs.WebUI/Helpers/OpenApiFacade.cs b/LearningHub.Nhs.WebUI/Helpers/OpenApiFacade.cs new file mode 100644 index 000000000..3627bc4a8 --- /dev/null +++ b/LearningHub.Nhs.WebUI/Helpers/OpenApiFacade.cs @@ -0,0 +1,241 @@ +namespace LearningHub.Nhs.WebUI.Helpers +{ + using System; + using System.Net.Http; + using System.Text; + using System.Threading.Tasks; + using LearningHub.Nhs.Models.Common; + using LearningHub.Nhs.Shared.Interfaces.Http; + using Newtonsoft.Json; + + /// + /// Defines the . + /// + public class OpenApiFacade : IOpenApiFacade + { + /// + /// Defines the _client. + /// + private readonly IOpenApiHttpClient client; + + /// + /// Initializes a new instance of the class. + /// + /// The client. + public OpenApiFacade(IOpenApiHttpClient client) + { + this.client = client; + } + + /// + /// The GetAsync. + /// + /// . + /// The url. + /// The . + public async Task GetAsync(string url) + where T : class, new() + { + var client = await this.client.GetClientAsync(); + + var vm = new T(); + + var response = await client.GetAsync(url).ConfigureAwait(false); + + if (response.IsSuccessStatusCode) + { + var result = response.Content.ReadAsStringAsync().Result; + vm = JsonConvert.DeserializeObject(result); + + return vm; + } + else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized + || + response.StatusCode == System.Net.HttpStatusCode.Forbidden) + { + throw new Exception("AccessDenied"); + } + else + { + throw new Exception($"Exception HttpStatusCode={response.StatusCode}"); + } + } + + /// + /// The PostAsync. + /// + /// . + /// The url. + /// The body. + /// The . + public async Task PostAsync(string url, T body) + where T : class, new() + { + var client = await this.client.GetClientAsync(); + + var content = new StringContent(JsonConvert.SerializeObject(body), Encoding.UTF8, "application/json"); + var response = await client.PostAsync(url, content).ConfigureAwait(false); + + if (response.IsSuccessStatusCode) + { + return; + } + else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized + || + response.StatusCode == System.Net.HttpStatusCode.Forbidden) + { + throw new Exception("AccessDenied"); + } + else + { + throw new Exception($"Exception HttpStatusCode={response.StatusCode}"); + } + } + + /// + /// The PostAsync. + /// + /// The return type. + /// The type of body parameter. + /// The url. + /// The body. + /// The . + public async Task PostAsync(string url, TBody body) + where TBody : class, new() + where T : class, new() + { + var client = await this.client.GetClientAsync(); + + var vm = new T(); + var content = new StringContent(JsonConvert.SerializeObject(body), Encoding.UTF8, "application/json"); + var response = await client.PostAsync(url, content).ConfigureAwait(false); + + if (response.IsSuccessStatusCode) + { + var result = response.Content.ReadAsStringAsync().Result; + var apiResponse = JsonConvert.DeserializeObject(result); + if (apiResponse.Success) + { + return apiResponse; + } + else + { + string details = string.Empty; + if (apiResponse.ValidationResult != null) + { + if (apiResponse.ValidationResult.Details != null) + { + details = $"::ValidationResult: {string.Join(",", apiResponse.ValidationResult.Details)}"; + } + } + + throw new Exception($"PostAsync ApiResponse returned False: {details}"); + } + } + else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized + || + response.StatusCode == System.Net.HttpStatusCode.Forbidden) + { + throw new Exception("Access Denied"); + } + else + { + throw new Exception($"Exception HttpStatusCode={response.StatusCode}"); + } + } + + /// + /// The PutAsync. + /// + /// The url. + /// The . + public async Task PutAsync(string url) + { + var client = await this.client.GetClientAsync(); + + var response = await client.PutAsync(url, null).ConfigureAwait(false); + + if (response.IsSuccessStatusCode) + { + var result = response.Content.ReadAsStringAsync().Result; + var apiResponse = JsonConvert.DeserializeObject(result); + if (apiResponse.Success) + { + return apiResponse; + } + else + { + string details = string.Empty; + if (apiResponse.ValidationResult != null) + { + if (apiResponse.ValidationResult.Details != null) + { + details = $"::ValidationResult: {string.Join(",", apiResponse.ValidationResult.Details)}"; + } + } + + throw new Exception($"PutAsync ApiResponse returned False: {details}"); + } + } + else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized + || + response.StatusCode == System.Net.HttpStatusCode.Forbidden) + { + throw new Exception("Access Denied"); + } + else + { + throw new Exception($"Exception HttpStatusCode={response.StatusCode}"); + } + } + + /// + /// The PutAsync. + /// + /// . + /// The url. + /// The body. + /// The . + public async Task PutAsync(string url, T body) + where T : class, new() + { + var client = await this.client.GetClientAsync(); + + var content = new StringContent(JsonConvert.SerializeObject(body), Encoding.UTF8, "application/json"); + var response = await client.PutAsync(url, content).ConfigureAwait(false); + + if (response.IsSuccessStatusCode) + { + var result = response.Content.ReadAsStringAsync().Result; + var apiResponse = JsonConvert.DeserializeObject(result); + if (apiResponse.Success) + { + return apiResponse; + } + else + { + string details = string.Empty; + if (apiResponse.ValidationResult != null) + { + if (apiResponse.ValidationResult.Details != null) + { + details = $"::ValidationResult: {string.Join(",", apiResponse.ValidationResult.Details)}"; + } + } + + throw new Exception($"PutAsync ApiResponse returned False: {details}"); + } + } + else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized + || + response.StatusCode == System.Net.HttpStatusCode.Forbidden) + { + throw new Exception("Access Denied"); + } + else + { + throw new Exception($"Exception HttpStatusCode={response.StatusCode}"); + } + } + } +} diff --git a/LearningHub.Nhs.WebUI/Services/ActivityService.cs b/LearningHub.Nhs.WebUI/Services/ActivityService.cs index f3e24b67e..963c246ac 100644 --- a/LearningHub.Nhs.WebUI/Services/ActivityService.cs +++ b/LearningHub.Nhs.WebUI/Services/ActivityService.cs @@ -23,9 +23,10 @@ public class ActivityService : BaseService, IActivityService /// Initializes a new instance of the class. /// /// Learning hub http client. + /// OpenApiHttpClient http client. /// Logger. - public ActivityService(ILearningHubHttpClient learningHubHttpClient, ILogger logger) - : base(learningHubHttpClient, logger) + public ActivityService(ILearningHubHttpClient learningHubHttpClient, IOpenApiHttpClient openApiHttpClient, ILogger logger) + : base(learningHubHttpClient, openApiHttpClient, logger) { } diff --git a/LearningHub.Nhs.WebUI/Services/BoomarkService.cs b/LearningHub.Nhs.WebUI/Services/BoomarkService.cs index 9428b3b95..e18d467a1 100644 --- a/LearningHub.Nhs.WebUI/Services/BoomarkService.cs +++ b/LearningHub.Nhs.WebUI/Services/BoomarkService.cs @@ -21,16 +21,17 @@ public class BoomarkService : BaseService, IBookmarkService /// Initializes a new instance of the class. /// /// The learningHubHttpClient. + /// The openApiHttpClient. /// The logger. - public BoomarkService(ILearningHubHttpClient learningHubHttpClient, ILogger logger) - : base(learningHubHttpClient, logger) + public BoomarkService(ILearningHubHttpClient learningHubHttpClient, IOpenApiHttpClient openApiHttpClient, ILogger logger) + : base(learningHubHttpClient, openApiHttpClient, logger) { } /// public async Task Create(UserBookmarkViewModel bookmarkViewModel) { - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = "bookmark/Create"; var content = new StringContent( JsonConvert.SerializeObject(bookmarkViewModel), @@ -58,7 +59,7 @@ public async Task Create(UserBookmarkViewModel bookmarkViewModel) /// public async Task Edit(UserBookmarkViewModel bookmarkViewModel) { - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = "bookmark/Edit"; var content = new StringContent( JsonConvert.SerializeObject(bookmarkViewModel), @@ -86,7 +87,7 @@ public async Task Edit(UserBookmarkViewModel bookmarkViewModel) /// public async Task DeleteFolder(int bookmarkId) { - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"bookmark/deletefolder/{bookmarkId}"; var response = await client.DeleteAsync(request).ConfigureAwait(false); if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized @@ -100,7 +101,7 @@ public async Task DeleteFolder(int bookmarkId) /// public async Task> GetAllByParent(int? parentId, bool? all = false) { - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"bookmark/GetAllByParent/{parentId}?all={all}"; var response = await client.GetAsync(request).ConfigureAwait(false); if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized @@ -122,7 +123,7 @@ public async Task> GetAllByParent(int? parent /// public async Task Toggle(UserBookmarkViewModel bookmarkViewModel) { - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = "bookmark/toggle"; var content = new StringContent( JsonConvert.SerializeObject(bookmarkViewModel), diff --git a/LearningHub.Nhs.WebUI/Services/CardService.cs b/LearningHub.Nhs.WebUI/Services/CardService.cs index e87c2a2ff..f641a921c 100644 --- a/LearningHub.Nhs.WebUI/Services/CardService.cs +++ b/LearningHub.Nhs.WebUI/Services/CardService.cs @@ -21,9 +21,10 @@ public class CardService : BaseService, ICardService /// Initializes a new instance of the class. /// /// The Learning Hub Http Client. + /// The Open Api Http Client. /// The logger. - public CardService(ILearningHubHttpClient learningHubHttpClient, ILogger logger) - : base(learningHubHttpClient, logger) + public CardService(ILearningHubHttpClient learningHubHttpClient, IOpenApiHttpClient openApiHttpClient, ILogger logger) + : base(learningHubHttpClient, openApiHttpClient, logger) { } @@ -36,7 +37,7 @@ public async Task GetMyContributionsTotalsAsync( { MyContributionsTotalsViewModel totals = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetMyContributionsTotals/{catalogueId.ToString()}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -65,7 +66,7 @@ public async Task> GetContributionsAsync( { List myContributionCards = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var json = JsonConvert.SerializeObject(resourceContributionsRequestViewModel); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); @@ -96,7 +97,7 @@ public async Task GetMyResourceViewModelAsync() { MyResourceViewModel myresourcecards = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetMyResourceViewModel"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -125,7 +126,7 @@ public async Task GetResourceCardExtendedViewMode { ResourceCardExtendedViewModel resourceCardExtendedViewModel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/ResourceCardExtendedViewModel/{id.ToString()}"; var response = await client.GetAsync(request).ConfigureAwait(false); diff --git a/LearningHub.Nhs.WebUI/Services/CatalogueService.cs b/LearningHub.Nhs.WebUI/Services/CatalogueService.cs index b711e7c2b..08885adc1 100644 --- a/LearningHub.Nhs.WebUI/Services/CatalogueService.cs +++ b/LearningHub.Nhs.WebUI/Services/CatalogueService.cs @@ -27,10 +27,11 @@ public class CatalogueService : BaseService, ICatalogueService /// Initializes a new instance of the class. /// /// The learning hub http client. + /// The Open Api Http Client. /// The logger. /// The cacheService. - public CatalogueService(ILearningHubHttpClient learningHubHttpClient, ILogger logger, ICacheService cacheService) - : base(learningHubHttpClient, logger) + public CatalogueService(ILearningHubHttpClient learningHubHttpClient, IOpenApiHttpClient openApiHttpClient, ILogger logger, ICacheService cacheService) + : base(learningHubHttpClient, openApiHttpClient, logger) { this.cacheService = cacheService; } @@ -43,7 +44,7 @@ public async Task> GetCataloguesForUserAsync() { List viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Catalogue/GetForCurrentUser"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -72,7 +73,7 @@ public async Task GetCatalogueAsync(string reference) { CatalogueViewModel viewmodel = new CatalogueViewModel { }; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"catalogue/catalogue/{reference}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -100,7 +101,7 @@ public async Task GetCatalogueAsync(int catalogueNodeVersion { CatalogueViewModel viewmodel = new CatalogueViewModel { }; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"catalogue/catalogues/{catalogueNodeVersionId}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -128,7 +129,7 @@ public async Task GetCatalogueRecordedAsync(string reference { CatalogueViewModel viewmodel = new CatalogueViewModel { }; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"catalogue/catalogue-recorded/{reference}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -156,7 +157,7 @@ public async Task GetResourcesAsync(Catalogu { CatalogueResourceResponseViewModel viewmodel = new CatalogueResourceResponseViewModel { }; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var json = JsonConvert.SerializeObject(requestViewModel); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); @@ -187,7 +188,7 @@ public async Task CanCurrentUserEditCatalogue(int catalogueId) { var request = $"Catalogue/CanCurrentUserEditCatalogue/{catalogueId}"; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var response = await client.GetAsync(request).ConfigureAwait(false); var catalogueIsEditable = false; @@ -215,7 +216,7 @@ public async Task AccessDetailsAsync(string ref { var request = $"Catalogue/AccessDetails/{reference}"; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var response = await client.GetAsync(request).ConfigureAwait(false); var accessDetails = new CatalogueAccessDetailsViewModel(); @@ -243,7 +244,7 @@ public async Task GetLatestCatalogueAccessReque { var request = $"Catalogue/GetLatestCatalogueAccessRequest/{catalogueNodeId}"; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var response = await client.GetAsync(request).ConfigureAwait(false); var car = new CatalogueAccessRequestViewModel(); @@ -277,7 +278,7 @@ public async Task RequestAccessAsync(string referen { var request = $"Catalogue/RequestAccess/{reference}/{accessType}"; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var content = new StringContent(JsonConvert.SerializeObject(vm), Encoding.UTF8, "application/json"); var response = await client.PostAsync(request, content).ConfigureAwait(false); var catalogueAccessRequested = false; @@ -310,7 +311,7 @@ public async Task InviteUserAsync(RestrictedCatalog { var request = $"Catalogue/InviteUser"; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var content = new StringContent(JsonConvert.SerializeObject(vm), Encoding.UTF8, "application/json"); var response = await client.PostAsync(request, content).ConfigureAwait(false); var catalogueAccessRequested = false; @@ -346,7 +347,7 @@ public async Task> GetRestricted var json = JsonConvert.SerializeObject(requestModel); var stringContent = new StringContent(json, Encoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Catalogue/GetRestrictedCatalogueAccessRequests"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -379,7 +380,7 @@ public async Task GetRestrictedCatalogueSum { var viewmodel = new RestrictedCatalogueSummaryViewModel(); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"catalogue/GetRestrictedCatalogueSummary/{catalogueNodeId}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -414,7 +415,7 @@ public async Task GetRestrictedCatalogueUsers var json = JsonConvert.SerializeObject(requestModel); var stringContent = new StringContent(json, Encoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Catalogue/GetRestrictedCatalogueUsers"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -448,7 +449,7 @@ public async Task AcceptAccessRequestAsync(int acce { var request = $"Catalogue/AcceptAccessRequest/{accessRequestId}"; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var content = new StringContent(JsonConvert.SerializeObject(new { })); var response = await client.PostAsync(request, content).ConfigureAwait(false); var catalogueAccessRequested = new LearningHubValidationResult(); @@ -480,7 +481,7 @@ public async Task RejectAccessRequestAsync(int acce { var request = $"Catalogue/RejectAccessRequest/{accessRequestId}"; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var content = new StringContent(JsonConvert.SerializeObject(new CatalogueAccessRejectionViewModel { RejectionReason = rejectionReason }), Encoding.UTF8, "application/json"); var response = await client.PostAsync(request, content).ConfigureAwait(false); var vr = new LearningHubValidationResult(); @@ -509,7 +510,7 @@ public async Task DismissAccessRequestAsync(int cat { var request = $"Catalogue/DismissAccessRequest/{catalogueNodeId}"; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var content = new StringContent(JsonConvert.SerializeObject(new { })); var response = await client.PostAsync(request, content).ConfigureAwait(false); var vr = new LearningHubValidationResult(false); @@ -538,7 +539,7 @@ public async Task GetCatalogueAccessRequestAsyn { var request = $"Catalogue/AccessRequest/{catalogueAccessRequestId}"; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var response = await client.GetAsync(request).ConfigureAwait(false); var catalogueAccessRequest = new CatalogueAccessRequestViewModel(); @@ -572,7 +573,7 @@ public async Task RemoveUserFromRestrictedAccessUse var json = JsonConvert.SerializeObject(userGroup); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"UserGroup/DeleteUserUserGroup"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -613,7 +614,7 @@ public async Task RemoveUserFromRestrictedAccessUse public async Task GetAllCatalogueAsync(string filterChar) { AllCatalogueResponseViewModel viewmodel = new AllCatalogueResponseViewModel { }; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"catalogue/allcatalogues/{filterChar}"; var response = await client.GetAsync(request).ConfigureAwait(false); diff --git a/LearningHub.Nhs.WebUI/Services/ContentService.cs b/LearningHub.Nhs.WebUI/Services/ContentService.cs index 281564950..b46d4ae4d 100644 --- a/LearningHub.Nhs.WebUI/Services/ContentService.cs +++ b/LearningHub.Nhs.WebUI/Services/ContentService.cs @@ -20,10 +20,11 @@ public class ContentService : BaseService, IContentService /// Initializes a new instance of the class. /// /// Learning hub http client. + /// The Open Api Http Client. /// Logger. /// azureMediaService. - public ContentService(ILearningHubHttpClient learningHubHttpClient, ILogger logger, IAzureMediaService azureMediaService) - : base(learningHubHttpClient, logger) + public ContentService(ILearningHubHttpClient learningHubHttpClient, IOpenApiHttpClient openApiHttpClient, ILogger logger, IAzureMediaService azureMediaService) + : base(learningHubHttpClient, openApiHttpClient, logger) { this.azureMediaService = azureMediaService; } @@ -38,7 +39,7 @@ public async Task GetPageByIdAsync(int id, bool preview) { PageViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var response = await client.GetAsync($"content/page/{id}?publishedOnly={!preview}&preview={preview}").ConfigureAwait(false); @@ -65,7 +66,7 @@ public async Task GetPageSectionDetailVideoAssetById { PageSectionDetailViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var response = await client.GetAsync($"content/page-section-detail-video/{id}").ConfigureAwait(false); diff --git a/LearningHub.Nhs.WebUI/Services/ContributeService.cs b/LearningHub.Nhs.WebUI/Services/ContributeService.cs index dd69c37b4..ec5e73894 100644 --- a/LearningHub.Nhs.WebUI/Services/ContributeService.cs +++ b/LearningHub.Nhs.WebUI/Services/ContributeService.cs @@ -41,9 +41,10 @@ public class ContributeService : BaseService, IContributeServ /// Azure media service. /// MKIO media service. /// Learning hub http client. + /// The Open Api Http Client. /// Logger. - public ContributeService(IFileService fileService, IResourceService resourceService, IAzureMediaService azureMediaService, ILearningHubHttpClient learningHubHttpClient, ILogger logger, IAzureMediaService mediaService) - : base(learningHubHttpClient, logger) + public ContributeService(IFileService fileService, IResourceService resourceService, IAzureMediaService azureMediaService, ILearningHubHttpClient learningHubHttpClient, IOpenApiHttpClient openApiHttpClient, ILogger logger, IAzureMediaService mediaService) + : base(learningHubHttpClient, openApiHttpClient, logger) { this.fileService = fileService; this.resourceService = resourceService; @@ -64,7 +65,7 @@ public async Task CreateNewResourceVersionAsync(int var json = JsonConvert.SerializeObject(model); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/CreateNewResourceVersion"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -98,7 +99,7 @@ public async Task CreateResourceAuthorAsync(ResourceAuthorViewModel resourc var json = JsonConvert.SerializeObject(resourceAuthorViewModel); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/AddResourceVersionAuthor"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -132,7 +133,7 @@ public async Task CreateResourceKeywordAsync(ResourceKeywordViewModel resou var json = JsonConvert.SerializeObject(resourceKeywordViewModel); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/AddResourceVersionKeyword"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -165,7 +166,7 @@ public async Task DeleteArticleFileAsync(FileDeleteRequestModel model) var json = JsonConvert.SerializeObject(model); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/DeleteArticleFile"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -195,7 +196,7 @@ public async Task DeleteArticleFileAsync(FileDeleteRequestModel model) /// The . public async Task DeleteFileChunkDetailAsync(int fileChunkDetailId) { - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/DeleteFileChunkDetail/{fileChunkDetailId}"; var response = await client.DeleteAsync(request).ConfigureAwait(false); @@ -226,7 +227,7 @@ public async Task DeleteResourceAttributeFileAsync(FileDeleteRequestModel var json = JsonConvert.SerializeObject(model); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/DeleteResourceAttributeFile"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -259,7 +260,7 @@ public async Task DeleteResourceAuthorAsync(AuthorDeleteRequestModel model var json = JsonConvert.SerializeObject(model); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/DeleteResourceVersionAuthor"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -292,7 +293,7 @@ public async Task DeleteResourceKeywordAsync(KeywordDeleteRequestModel mod var json = JsonConvert.SerializeObject(model); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/DeleteResourceVersionKeyword"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -322,7 +323,7 @@ public async Task DeleteResourceKeywordAsync(KeywordDeleteRequestModel mod /// The . public async Task DeleteResourceVersionAsync(int resourceVersionId) { - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/DeleteResourceVersion/{resourceVersionId}"; var response = await client.DeleteAsync(request).ConfigureAwait(false); @@ -358,7 +359,7 @@ public async Task GetFileChunkDetail(int fileChunkDeta { FileChunkDetailViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetFileChunkDetail/{fileChunkDetailId}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -801,7 +802,7 @@ public async Task SaveArticleAttachedFileDetailsAsync(FileCreateRequestView var json = JsonConvert.SerializeObject(fileCreateRequestViewModel); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/SaveArticleAttachedFileDetails"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -835,7 +836,7 @@ public async Task SaveArticleDetailAsync(ArticleUpdateRequestViewModel arti var json = JsonConvert.SerializeObject(articleViewModel); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/UpdateArticleDetail"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -869,7 +870,7 @@ public async Task SaveFileChunkDetailsAsync(FileChunkDetailViewModel fileCh var json = JsonConvert.SerializeObject(fileChunkDetailCreateRequestViewModel); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/SaveFileChunkDetail"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -903,7 +904,7 @@ public async Task SaveFileDetailsAsync(FileCreateRequestViewModel fileCreat var json = JsonConvert.SerializeObject(fileCreateRequestViewModel); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/SaveFileDetails"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -937,7 +938,7 @@ public async Task SaveGenericFileDetailAsync(GenericFileUpdateRequestViewMo var json = JsonConvert.SerializeObject(genericFileViewModel); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/UpdateGenericFileDetail"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -971,7 +972,7 @@ public async Task SaveScormDetailAsync(ScormUpdateRequestViewModel scormUpd var json = JsonConvert.SerializeObject(scormUpdateRequestViewModel); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/UpdateScormDetail"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -1005,7 +1006,7 @@ public async Task SaveHtmlDetailAsync(HtmlResourceUpdateRequestViewModel ht var json = JsonConvert.SerializeObject(htmlResource); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/UpdateHtmlDetail"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -1039,7 +1040,7 @@ public async Task SaveImageDetailAsync(ImageUpdateRequestViewModel imageVie var json = JsonConvert.SerializeObject(imageViewModel); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/UpdateImageDetail"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -1073,7 +1074,7 @@ public async Task SaveResourceAttributeFileDetailsAsync(FileCreateRequestVi var json = JsonConvert.SerializeObject(fileCreateRequestViewModel); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/SaveResourceAttributeFileDetails"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -1130,7 +1131,7 @@ public async Task SaveWeblinkDetailAsync(WebLinkViewModel weblinkViewModel) var json = JsonConvert.SerializeObject(weblinkViewModel); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/UpdateWeblinkDetail"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -1164,7 +1165,7 @@ public async Task SaveCaseDetailAsync(CaseViewModel caseViewModel) var json = JsonConvert.SerializeObject(caseViewModel); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/UpdateCaseDetail"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -1198,7 +1199,7 @@ public async Task SaveAssessmentDetailAsync(AssessmentViewModel assessmentV var json = JsonConvert.SerializeObject(assessmentViewModel); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/UpdateAssessmentDetail"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -1232,7 +1233,7 @@ public async Task SubmitResourceVersionForPublishAs var json = JsonConvert.SerializeObject(publishViewModel); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/SubmitResourceVersionForPublish"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -1337,7 +1338,7 @@ private async Task CreateResourceAsync(ResourceDetailViewModel resourceDeta var json = JsonConvert.SerializeObject(resourceDetailViewModel); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/CreateResource"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -1370,7 +1371,7 @@ private async Task UpdateResourceVersionAsync(ResourceDetailViewModel resourceDe var json = JsonConvert.SerializeObject(resourceDetailViewModel); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/UpdateResourceVersion"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); diff --git a/LearningHub.Nhs.WebUI/Services/CountryService.cs b/LearningHub.Nhs.WebUI/Services/CountryService.cs index b16737317..ccbe6536a 100644 --- a/LearningHub.Nhs.WebUI/Services/CountryService.cs +++ b/LearningHub.Nhs.WebUI/Services/CountryService.cs @@ -22,13 +22,15 @@ public class CountryService : BaseService, ICountryService /// Initializes a new instance of the class. /// /// Learning hub http client. + /// The Open Api Http Client. /// User api http client. /// Logger. public CountryService( ILearningHubHttpClient learningHubHttpClient, + IOpenApiHttpClient openApiHttpClient, IUserApiHttpClient userApiHttpClient, ILogger logger) - : base(learningHubHttpClient, logger) + : base(learningHubHttpClient, openApiHttpClient, logger) { this.userApiHttpClient = userApiHttpClient; } diff --git a/LearningHub.Nhs.WebUI/Services/DashboardService.cs b/LearningHub.Nhs.WebUI/Services/DashboardService.cs index 226e0392a..639eb7017 100644 --- a/LearningHub.Nhs.WebUI/Services/DashboardService.cs +++ b/LearningHub.Nhs.WebUI/Services/DashboardService.cs @@ -28,10 +28,11 @@ public class DashboardService : BaseService, IDashboardService /// Initializes a new instance of the class. /// /// learningHubHttpClient. + /// The Open Api Http Client. /// logger. /// MoodleHttpClient. - public DashboardService(ILearningHubHttpClient learningHubHttpClient, ILogger logger, IMoodleHttpClient moodleHttpClient) - : base(learningHubHttpClient, logger) + public DashboardService(ILearningHubHttpClient learningHubHttpClient, IOpenApiHttpClient openApiHttpClient, ILogger logger, IMoodleHttpClient moodleHttpClient) + : base(learningHubHttpClient, openApiHttpClient, logger) { this.moodleHttpClient = moodleHttpClient; } @@ -46,7 +47,7 @@ public async Task GetMyAccessLearningsAsyn { DashboardMyLearningResponseViewModel viewmodel = new DashboardMyLearningResponseViewModel { }; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"dashboard/myaccesslearning/{dashboardType}/{pageNumber}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -75,7 +76,7 @@ public async Task GetCataloguesAsync(string { DashboardCatalogueResponseViewModel viewmodel = new DashboardCatalogueResponseViewModel { }; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"dashboard/catalogues/{dashboardType}/{pageNumber}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -104,7 +105,7 @@ public async Task GetResourcesAsync(string d { DashboardResourceResponseViewModel viewmodel = new DashboardResourceResponseViewModel { }; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"dashboard/resources/{dashboardType}/{pageNumber}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -151,7 +152,7 @@ public async Task RecordDashBoardEventAsync(DashboardEventViewModel dashboardEve }; var content = new System.Net.Http.StringContent(JsonConvert.SerializeObject(eventEntity), Encoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"event/Create"; var response = await client.PostAsync(request, content).ConfigureAwait(false); diff --git a/LearningHub.Nhs.WebUI/Services/DetectJsLogService.cs b/LearningHub.Nhs.WebUI/Services/DetectJsLogService.cs index 8cd0c6dd1..cbee0894d 100644 --- a/LearningHub.Nhs.WebUI/Services/DetectJsLogService.cs +++ b/LearningHub.Nhs.WebUI/Services/DetectJsLogService.cs @@ -16,9 +16,10 @@ public class DetectJsLogService : BaseService, IDetectJsLogS /// Initializes a new instance of the class. /// /// Learning hub http client. + /// The Open Api Http Client. /// Logger. - public DetectJsLogService(ILearningHubHttpClient learningHubHttpClient, ILogger logger) - : base(learningHubHttpClient, logger) + public DetectJsLogService(ILearningHubHttpClient learningHubHttpClient, IOpenApiHttpClient openApiHttpClient, ILogger logger) + : base(learningHubHttpClient, openApiHttpClient, logger) { } diff --git a/LearningHub.Nhs.WebUI/Services/GradeService.cs b/LearningHub.Nhs.WebUI/Services/GradeService.cs index 3baabdd83..0394b9543 100644 --- a/LearningHub.Nhs.WebUI/Services/GradeService.cs +++ b/LearningHub.Nhs.WebUI/Services/GradeService.cs @@ -21,13 +21,15 @@ public class GradeService : BaseService, IGradeService /// Initializes a new instance of the class. /// /// Learning hub http client. + /// The Open Api Http Client. /// User api http client. /// Logger. public GradeService( ILearningHubHttpClient learningHubHttpClient, + IOpenApiHttpClient openApiHttpClient, IUserApiHttpClient userApiHttpClient, ILogger logger) - : base(learningHubHttpClient, logger) + : base(learningHubHttpClient, openApiHttpClient, logger) { this.userApiHttpClient = userApiHttpClient; } diff --git a/LearningHub.Nhs.WebUI/Services/HierarchyService.cs b/LearningHub.Nhs.WebUI/Services/HierarchyService.cs index 6df3f2090..d68e29447 100644 --- a/LearningHub.Nhs.WebUI/Services/HierarchyService.cs +++ b/LearningHub.Nhs.WebUI/Services/HierarchyService.cs @@ -18,21 +18,23 @@ public class HierarchyService : BaseService, IHierarchyService /// /// Defines the _facade. /// - private readonly ILearningHubApiFacade facade; + private readonly IOpenApiFacade facade; /// /// Initializes a new instance of the class. /// /// The learning hub http client. - /// The learningHubApiFacade. + /// The Open Api Http Client. + /// The openApiFacade. /// The logger. public HierarchyService( ILearningHubHttpClient learningHubHttpClient, - ILearningHubApiFacade learningHubApiFacade, + IOpenApiHttpClient openApiHttpClient, + IOpenApiFacade openApiFacade, ILogger logger) - : base(learningHubHttpClient, logger) + : base(learningHubHttpClient, openApiHttpClient, logger) { - this.facade = learningHubApiFacade; + this.facade = openApiFacade; } /// diff --git a/LearningHub.Nhs.WebUI/Services/InternalSystemService.cs b/LearningHub.Nhs.WebUI/Services/InternalSystemService.cs index 6105f0512..67fcb25ea 100644 --- a/LearningHub.Nhs.WebUI/Services/InternalSystemService.cs +++ b/LearningHub.Nhs.WebUI/Services/InternalSystemService.cs @@ -18,9 +18,10 @@ public class InternalSystemService : BaseService, IIntern /// Initializes a new instance of the class. /// /// Learning hub http client. + /// The Open Api Http Client. /// Logger. - public InternalSystemService(ILearningHubHttpClient learningHubHttpClient, ILogger logger) - : base(learningHubHttpClient, logger) + public InternalSystemService(ILearningHubHttpClient learningHubHttpClient, IOpenApiHttpClient openApiHttpClient, ILogger logger) + : base(learningHubHttpClient, openApiHttpClient, logger) { } diff --git a/LearningHub.Nhs.WebUI/Services/JobRoleService.cs b/LearningHub.Nhs.WebUI/Services/JobRoleService.cs index 97433bab8..7b9c3a674 100644 --- a/LearningHub.Nhs.WebUI/Services/JobRoleService.cs +++ b/LearningHub.Nhs.WebUI/Services/JobRoleService.cs @@ -24,13 +24,15 @@ public class JobRoleService : BaseService, IJobRoleService /// Initializes a new instance of the class. /// /// Learning hub http client. + /// The Open Api Http Client. /// User api http client. /// Logger. public JobRoleService( ILearningHubHttpClient learningHubHttpClient, + IOpenApiHttpClient openApiHttpClient, IUserApiHttpClient userApiHttpClient, ILogger logger) - : base(learningHubHttpClient, logger) + : base(learningHubHttpClient, openApiHttpClient, logger) { this.userApiHttpClient = userApiHttpClient; } diff --git a/LearningHub.Nhs.WebUI/Services/LocationService.cs b/LearningHub.Nhs.WebUI/Services/LocationService.cs index 29d9be912..578532520 100644 --- a/LearningHub.Nhs.WebUI/Services/LocationService.cs +++ b/LearningHub.Nhs.WebUI/Services/LocationService.cs @@ -21,13 +21,15 @@ public class LocationService : BaseService, ILocationService /// Initializes a new instance of the class. /// /// Learning hub http client. + /// The Open Api Http Client. /// User api http client. /// Logger. public LocationService( ILearningHubHttpClient learningHubHttpClient, + IOpenApiHttpClient openApiHttpClient, IUserApiHttpClient userApiHttpClient, ILogger logger) - : base(learningHubHttpClient, logger) + : base(learningHubHttpClient, openApiHttpClient, logger) { this.userApiHttpClient = userApiHttpClient; } diff --git a/LearningHub.Nhs.WebUI/Services/LoginWizardService.cs b/LearningHub.Nhs.WebUI/Services/LoginWizardService.cs index 433f5a7ba..c06f530c4 100644 --- a/LearningHub.Nhs.WebUI/Services/LoginWizardService.cs +++ b/LearningHub.Nhs.WebUI/Services/LoginWizardService.cs @@ -26,13 +26,15 @@ public class LoginWizardService : BaseService, ILoginWizardS /// Initializes a new instance of the class. /// /// Learning hub http client. + /// The Open Api Http Client. /// User api http client. /// Logger. public LoginWizardService( ILearningHubHttpClient learningHubHttpClient, + IOpenApiHttpClient openApiHttpClient, IUserApiHttpClient userApiHttpClient, ILogger logger) - : base(learningHubHttpClient, logger) + : base(learningHubHttpClient, openApiHttpClient, logger) { this.userApiHttpClient = userApiHttpClient; } diff --git a/LearningHub.Nhs.WebUI/Services/MyLearningService.cs b/LearningHub.Nhs.WebUI/Services/MyLearningService.cs index 4d51dcbe5..7aa1d65ad 100644 --- a/LearningHub.Nhs.WebUI/Services/MyLearningService.cs +++ b/LearningHub.Nhs.WebUI/Services/MyLearningService.cs @@ -22,9 +22,10 @@ public class MyLearningService : BaseService, IMyLearningServ /// Initializes a new instance of the class. /// /// The learningHubHttpClient. + /// The Open Api Http Client. /// The logger. - public MyLearningService(ILearningHubHttpClient learningHubHttpClient, ILogger logger) - : base(learningHubHttpClient, logger) + public MyLearningService(ILearningHubHttpClient learningHubHttpClient, IOpenApiHttpClient openApiHttpClient, ILogger logger) + : base(learningHubHttpClient, openApiHttpClient, logger) { } @@ -40,7 +41,7 @@ public async Task GetActivityDetailed(MyLearningReq var json = JsonConvert.SerializeObject(requestModel); var stringContent = new StringContent(json, Encoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"MyLearning/GetActivityDetailed"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -69,7 +70,7 @@ public async Task GetActivityDetailed(MyLearningReq public async Task> GetPlayedSegments(int resourceId, int majorVersion) { List viewModel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"MyLearning/GetPlayedSegments/{resourceId}/{majorVersion}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -100,7 +101,7 @@ public async Task> GetPlayedSegments(int resourceId public async Task> GetResourceCertificateDetails(int resourceReferenceId, int? majorVersion = 0, int? minorVersion = 0, int? userId = 0) { Tuple viewModel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"MyLearning/GetResourceCertificateDetails/{resourceReferenceId}/{majorVersion}/{minorVersion}/{userId}"; var response = await client.GetAsync(request).ConfigureAwait(false); diff --git a/LearningHub.Nhs.WebUI/Services/NotificationService.cs b/LearningHub.Nhs.WebUI/Services/NotificationService.cs index 4ce1afb09..14eaaf300 100644 --- a/LearningHub.Nhs.WebUI/Services/NotificationService.cs +++ b/LearningHub.Nhs.WebUI/Services/NotificationService.cs @@ -24,9 +24,10 @@ public class NotificationService : BaseService, INotificati /// Initializes a new instance of the class. /// /// Learning hub http client. + /// The Open Api Http Client. /// Logger. - public NotificationService(ILearningHubHttpClient learningHubHttpClient, ILogger logger) - : base(learningHubHttpClient, logger) + public NotificationService(ILearningHubHttpClient learningHubHttpClient, IOpenApiHttpClient openApiHttpClient, ILogger logger) + : base(learningHubHttpClient, openApiHttpClient, logger) { } @@ -97,7 +98,7 @@ public async Task> GetPagedAsync(Pagin { PagedResultSet viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var content = new StringContent(JsonConvert.SerializeObject(pagingRequestModel), Encoding.UTF8, "application/json"); @@ -126,7 +127,7 @@ public async Task GetUserNotificationIdAsync(int id) { UserNotification viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"UserNotification/GetById/{id}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -155,7 +156,7 @@ public async Task GetUserNotificationDetailsAsync(int id) { UserNotification viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"UserNotification/GetByIdAndUserId/{id}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -184,7 +185,7 @@ public async Task GetUserUnreadNotificationCountAsync(int userid) { int count = 0; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"UserNotification/GetUserUnreadNotificationCount/{userid}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -214,7 +215,7 @@ private async Task PutAsync(UserNotification notification) var json = JsonConvert.SerializeObject(notification); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"UserNotification/PutAsync"; var response = await client.PutAsync(request, stringContent).ConfigureAwait(false); @@ -247,7 +248,7 @@ private async Task PostAsync(UserNotification notification) var json = JsonConvert.SerializeObject(notification); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"UserNotification/PostAsync"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); diff --git a/LearningHub.Nhs.WebUI/Services/OpenApiHttpClient.cs b/LearningHub.Nhs.WebUI/Services/OpenApiHttpClient.cs new file mode 100644 index 000000000..502025b0b --- /dev/null +++ b/LearningHub.Nhs.WebUI/Services/OpenApiHttpClient.cs @@ -0,0 +1,41 @@ +namespace LearningHub.Nhs.WebUI.Services +{ + using System.Net.Http; + using LearningHub.Nhs.Caching; + using LearningHub.Nhs.Shared.Interfaces.Http; + using LearningHub.Nhs.WebUI.Configuration; + using Microsoft.AspNetCore.Http; + using Microsoft.Extensions.Logging; + using Microsoft.Extensions.Options; + + /// + /// The open api http client. + /// + public class OpenApiHttpClient : BaseHttpClient, IOpenApiHttpClient + { + /// + /// Initializes a new instance of the class. + /// + /// The http context accessor. + /// The web settings. + /// The auth config. + /// The http client. + /// The logger. + /// The cache service. + public OpenApiHttpClient( + IHttpContextAccessor httpContextAccessor, + IOptions webSettings, + LearningHubAuthServiceConfig authConfig, + HttpClient client, + ILogger logger, + ICacheService cacheService) + : base(httpContextAccessor, webSettings.Value, authConfig, client, logger, cacheService) + { + } + + /// + /// Gets the open api url. + /// + public override string ApiUrl => this.WebSettings.OpenApiUrl; + } +} diff --git a/LearningHub.Nhs.WebUI/Services/PartialFileUploadService.cs b/LearningHub.Nhs.WebUI/Services/PartialFileUploadService.cs index 59230f71b..b97309e65 100644 --- a/LearningHub.Nhs.WebUI/Services/PartialFileUploadService.cs +++ b/LearningHub.Nhs.WebUI/Services/PartialFileUploadService.cs @@ -36,12 +36,14 @@ public class PartialFileUploadService : BaseService, I /// /// The Settings. /// The learning hub http client. + /// The Open Api Http Client. /// The logger. public PartialFileUploadService( IOptions settings, ILearningHubHttpClient learningHubHttpClient, + IOpenApiHttpClient openApiHttpClient, ILogger logger) - : base(learningHubHttpClient, logger) + : base(learningHubHttpClient, openApiHttpClient, logger) { this.settings = settings.Value; } diff --git a/LearningHub.Nhs.WebUI/Services/RatingService.cs b/LearningHub.Nhs.WebUI/Services/RatingService.cs index 724256728..eedb5cf67 100644 --- a/LearningHub.Nhs.WebUI/Services/RatingService.cs +++ b/LearningHub.Nhs.WebUI/Services/RatingService.cs @@ -21,9 +21,10 @@ public class RatingService : BaseService, IRatingService /// Initializes a new instance of the class. /// /// Learning hub http client. + /// The Open Api Http Client. /// Logger. - public RatingService(ILearningHubHttpClient learningHubHttpClient, ILogger logger) - : base(learningHubHttpClient, logger) + public RatingService(ILearningHubHttpClient learningHubHttpClient, IOpenApiHttpClient openApiHttpClient, ILogger logger) + : base(learningHubHttpClient, openApiHttpClient, logger) { } diff --git a/LearningHub.Nhs.WebUI/Services/RegionService.cs b/LearningHub.Nhs.WebUI/Services/RegionService.cs index f0211252f..e12ae5f33 100644 --- a/LearningHub.Nhs.WebUI/Services/RegionService.cs +++ b/LearningHub.Nhs.WebUI/Services/RegionService.cs @@ -21,10 +21,11 @@ public class RegionService : BaseService, IRegionService /// Initializes a new instance of the class. /// /// Learning hub http client. + /// The Open Api Http Client. /// Logger. /// User Api http client. - public RegionService(ILearningHubHttpClient learningHubHttpClient, ILogger logger, IUserApiHttpClient userApiHttpClient) - : base(learningHubHttpClient, logger) + public RegionService(ILearningHubHttpClient learningHubHttpClient, IOpenApiHttpClient openApiHttpClient, ILogger logger, IUserApiHttpClient userApiHttpClient) + : base(learningHubHttpClient, openApiHttpClient, logger) { this.userApiHttpClient = userApiHttpClient; } diff --git a/LearningHub.Nhs.WebUI/Services/ResourceService.cs b/LearningHub.Nhs.WebUI/Services/ResourceService.cs index 6577ba801..eb3370b7f 100644 --- a/LearningHub.Nhs.WebUI/Services/ResourceService.cs +++ b/LearningHub.Nhs.WebUI/Services/ResourceService.cs @@ -36,11 +36,12 @@ public class ResourceService : BaseService, IResourceService /// Initializes a new instance of the class. /// /// Learning hub http client. + /// The Open Api Http Client. /// Azure media services. /// Logger. /// Settings. - public ResourceService(ILearningHubHttpClient learningHubHttpClient, IAzureMediaService azureMediaService, ILogger logger, IOptions settings) - : base(learningHubHttpClient, logger) + public ResourceService(ILearningHubHttpClient learningHubHttpClient, IOpenApiHttpClient openApiHttpClient, IAzureMediaService azureMediaService, ILogger logger, IOptions settings) + : base(learningHubHttpClient, openApiHttpClient, logger) { this.settings = settings.Value; this.azureMediaService = azureMediaService; @@ -57,7 +58,7 @@ public async Task AcceptSensitiveContentAsync(int r var stringContent = new StringContent(resourceVersionId.ToString(), Encoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/AcceptSensitiveContent"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -89,7 +90,7 @@ public async Task GetArticleDetailsByIdAsync(int resourceVersi { ArticleViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetArticleDetails/{resourceVersionId}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -118,7 +119,7 @@ public async Task GetAudioDetailsByIdAsync(int resourceVersionId { AudioViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetAudioDetails/{resourceVersionId}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -147,7 +148,7 @@ public async Task GetByIdAsync(int id) { ResourceHeaderViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetResourceHeaderViewModelAsync/{id}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -175,7 +176,7 @@ public async Task> GetFileTypeAsync() { List fileTypeList = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetFileTypes"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -204,7 +205,7 @@ public async Task GetGenericFileDetailsByIdAsync(int resou { GenericFileViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetGenericFileDetails/{resourceVersionId}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -233,7 +234,7 @@ public async Task GetHtmlDetailsByIdAsync(int resourceVer { HtmlResourceViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetHtmlDetails/{resourceVersionId}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -262,7 +263,7 @@ public async Task GetScormDetailsByIdAsync(int resourceVersionId { ScormViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetScormDetails/{resourceVersionId}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -290,7 +291,7 @@ public async Task GetExternalContentDetailsAsyn { ExternalContentDetailsViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetExternalContentDetailsById/{resourceVersionId}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -319,7 +320,7 @@ public async Task RecordExternalReferenceUserAgreementAsync(ExternalRefere var json = JsonConvert.SerializeObject(model); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/RecordExternalReferenceUserAgreement"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -351,7 +352,7 @@ public async Task GetImageDetailsByIdAsync(int resourceVersionId { ImageViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetImageDetails/{resourceVersionId}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -380,7 +381,7 @@ public async Task GetInformationByIdAsync(int id) { ResourceInformationViewModel resourceInfo = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetResourceInformationViewModelAsync/{id}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -409,7 +410,7 @@ public async Task GetItemByIdAsync(int id) { ResourceItemViewModel resourceItem = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetResourceItemViewModelAsync/{id}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -455,7 +456,7 @@ public async Task> GetLicencesAsync() { List licences = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetResourceLicences"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -484,7 +485,7 @@ public async Task GetLocationsByIdAsync(int id) { CatalogueLocationsViewModel resourceLocations = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetCatalogueLocations/{id}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -513,7 +514,7 @@ public async Task GetResourceVersionAsync(int resourceV { ResourceDetailViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetResourceVersion/{resourceVersionId}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -542,7 +543,7 @@ public async Task GetResourceVersionViewModelAsync(int { ResourceVersionViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetResourceVersionViewModel/{resourceVersionId.ToString()}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -571,7 +572,7 @@ public async Task GetResourceVersionExtendedAs { ResourceVersionExtendedViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetResourceVersionExtendedViewModel/{resourceVersionId.ToString()}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -611,7 +612,7 @@ public async Task GetVideoDetailsByIdAsync(int resourceVersionId { VideoViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetVideoDetails/{resourceVersionId}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -650,7 +651,7 @@ public async Task GetWeblinkDetailsByIdAsync(int resourceVersi { WebLinkViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetWeblinkDetails/{resourceVersionId}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -679,7 +680,7 @@ public async Task GetCaseDetailsByIdAsync(int resourceVersionId) { CaseViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetCaseDetails/{resourceVersionId.ToString()}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -708,7 +709,7 @@ public async Task GetAssessmentDetailsByIdAsync(int resourc { AssessmentViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetAssessmentDetails/{resourceVersionId.ToString()}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -737,7 +738,7 @@ public async Task GetAssessmentContent(int resourceVersionI { AssessmentViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetAssessmentContent/{resourceVersionId.ToString()}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -766,7 +767,7 @@ public async Task GetAssessmentProgressByResourceVe { AssessmentProgressViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetAssessmentProgress/resource/{resourceVersionId.ToString()}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -795,7 +796,7 @@ public async Task GetAssessmentProgressByActivity(i { AssessmentProgressViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetAssessmentProgress/activity/{assessmentResourceActivityId.ToString()}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -824,7 +825,7 @@ public async Task> GetFileStatusDetailsAsync(int[] fileIds) { List viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); string queryString = string.Join("&", fileIds.Select(fileId => $"fileIds={fileId}")); var request = $"Resource/GetFileStatusDetails?{queryString}"; @@ -857,7 +858,7 @@ public async Task UnpublishResourceVersionAsync(int var json = JsonConvert.SerializeObject(rv); var stringContent = new StringContent(json, Encoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/UnpublishResourceVersion"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -886,7 +887,7 @@ public async Task UnpublishResourceVersionAsync(int /// The . public async Task UserHasPublishedResourcesAsync() { - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/HasPublishedResources"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -918,7 +919,7 @@ public async Task DuplicateResourceAsync(DuplicateR var json = JsonConvert.SerializeObject(model); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/DuplicateResource"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -965,7 +966,7 @@ public async Task DuplicateBlocksAsync(DuplicateBlo var json = JsonConvert.SerializeObject(model); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/DuplicateBlocks"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -1009,7 +1010,7 @@ public async Task> GetMyContri { var json = JsonConvert.SerializeObject(model); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var response = await client.PostAsync("Resource/GetMyContributions", stringContent).ConfigureAwait(false); @@ -1032,7 +1033,7 @@ public async Task> GetMyContri /// The . public async Task> GetAllPublishedResourceAsync() { - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var response = await client.GetAsync("Resource/GetAllPublishedResource").ConfigureAwait(false); @@ -1058,7 +1059,7 @@ public async Task GetResourceVersionVa { ResourceVersionValidationResultViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetResourceVersionValidationResult/{resourceVersionId.ToString()}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -1087,7 +1088,7 @@ public async Task GetResourceVersionExtendedVi { ResourceVersionExtendedViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetResourceVersionExtendedViewModel/{resourceVersionId.ToString()}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -1118,7 +1119,7 @@ public async Task RevertToDraft(int resourceVersion var json = JsonConvert.SerializeObject(new { resourceVersionId }); var stringContent = new StringContent(resourceVersionId.ToString(), Encoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/RevertToDraft"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -1162,7 +1163,7 @@ public async Task CreateResourceVersionProviderAsyn var json = JsonConvert.SerializeObject(model); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/AddResourceProvider"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -1208,7 +1209,7 @@ public async Task CreateResourceVersionValidationResultAsync(ResourceVersionVali var json = JsonConvert.SerializeObject(validationResultViewModel); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/CreateResourceVersionValidationResult"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -1239,7 +1240,7 @@ public async Task DeleteResourceVersionProviderAsyn var json = JsonConvert.SerializeObject(model); var stringContent = new StringContent(json, UnicodeEncoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/DeleteResourceProvider"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -1280,7 +1281,7 @@ public async Task DeleteResourceVersionProviderAsyn public async Task DeleteAllResourceVersionProviderAsync(int resourceVersionId) { var content = new StringContent(JsonConvert.SerializeObject(new { })); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/DeleteAllResourceProvider/{resourceVersionId}"; var response = await client.PostAsync(request, content).ConfigureAwait(false); @@ -1322,7 +1323,7 @@ public async Task> GetObsoleteResourceFile(int resourceVersionId, b { List filePaths = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Resource/GetObsoleteResourceFile/{resourceVersionId}/{deletedResource}"; var response = await client.GetAsync(request).ConfigureAwait(false); diff --git a/LearningHub.Nhs.WebUI/Services/RoadMapService.cs b/LearningHub.Nhs.WebUI/Services/RoadMapService.cs index 33210c4fb..5a16366ea 100644 --- a/LearningHub.Nhs.WebUI/Services/RoadMapService.cs +++ b/LearningHub.Nhs.WebUI/Services/RoadMapService.cs @@ -18,9 +18,10 @@ public class RoadMapService : BaseService, IRoadMapService /// Initializes a new instance of the class. /// /// Learing hub http client. + /// The Open Api Http Client. /// Logger. - public RoadMapService(ILearningHubHttpClient learningHubHttpClient, ILogger logger) - : base(learningHubHttpClient, logger) + public RoadMapService(ILearningHubHttpClient learningHubHttpClient, IOpenApiHttpClient openApiHttpClient, ILogger logger) + : base(learningHubHttpClient, openApiHttpClient, logger) { } diff --git a/LearningHub.Nhs.WebUI/Services/RoleService.cs b/LearningHub.Nhs.WebUI/Services/RoleService.cs index 32b0af0d8..bb75e4753 100644 --- a/LearningHub.Nhs.WebUI/Services/RoleService.cs +++ b/LearningHub.Nhs.WebUI/Services/RoleService.cs @@ -23,9 +23,10 @@ public class RoleService : BaseService, IRoleService /// /// The cache service. /// Learning hub http client. + /// The Open Api Http Client. /// Logger. - public RoleService(ICacheService cacheService, ILearningHubHttpClient learningHubHttpClient, ILogger logger) - : base(learningHubHttpClient, logger) + public RoleService(ICacheService cacheService, ILearningHubHttpClient learningHubHttpClient, IOpenApiHttpClient openApiHttpClient, ILogger logger) + : base(learningHubHttpClient, openApiHttpClient, logger) { this.cacheService = cacheService; } diff --git a/LearningHub.Nhs.WebUI/Services/SpecialtyService.cs b/LearningHub.Nhs.WebUI/Services/SpecialtyService.cs index 46410724c..c8853e9f5 100644 --- a/LearningHub.Nhs.WebUI/Services/SpecialtyService.cs +++ b/LearningHub.Nhs.WebUI/Services/SpecialtyService.cs @@ -21,13 +21,15 @@ public class SpecialtyService : BaseService, ISpecialtyService /// Initializes a new instance of the class. /// /// Learning hub http client. + /// The Open Api Http Client. /// User api http client. /// . public SpecialtyService( ILearningHubHttpClient learningHubHttpClient, + IOpenApiHttpClient openApiHttpClient, IUserApiHttpClient userApiHttpClient, ILogger logger) - : base(learningHubHttpClient, logger) + : base(learningHubHttpClient, openApiHttpClient, logger) { this.userApiHttpClient = userApiHttpClient; } diff --git a/LearningHub.Nhs.WebUI/Services/TermsAndConditionsService.cs b/LearningHub.Nhs.WebUI/Services/TermsAndConditionsService.cs index 24c6ddd65..b7bb5c93a 100644 --- a/LearningHub.Nhs.WebUI/Services/TermsAndConditionsService.cs +++ b/LearningHub.Nhs.WebUI/Services/TermsAndConditionsService.cs @@ -24,13 +24,15 @@ public class TermsAndConditionsService : BaseService, /// Initializes a new instance of the class. /// /// Learning hub http client. + /// The Open Api Http Client. /// User api http client. /// Logger. public TermsAndConditionsService( ILearningHubHttpClient learningHubHttpClient, + IOpenApiHttpClient openApiHttpClient, IUserApiHttpClient userApiHttpClient, ILogger logger) - : base(learningHubHttpClient, logger) + : base(learningHubHttpClient, openApiHttpClient, logger) { this.userApiHttpClient = userApiHttpClient; } diff --git a/LearningHub.Nhs.WebUI/Services/UserGroupService.cs b/LearningHub.Nhs.WebUI/Services/UserGroupService.cs index bfc465966..ac0ac9bb3 100644 --- a/LearningHub.Nhs.WebUI/Services/UserGroupService.cs +++ b/LearningHub.Nhs.WebUI/Services/UserGroupService.cs @@ -28,17 +28,19 @@ public class UserGroupService : BaseService, IUserGroupService /// Initializes a new instance of the class. /// /// The learning hub http client. + /// The Open Api Http Client. /// The logger. /// The http context accessor. /// The cacheService. /// roleService. public UserGroupService( ILearningHubHttpClient learningHubHttpClient, + IOpenApiHttpClient openApiHttpClient, ILogger logger, IHttpContextAccessor contextAccessor, ICacheService cacheService, IRoleService roleService) - : base(learningHubHttpClient, logger) + : base(learningHubHttpClient, openApiHttpClient, logger) { this.contextAccessor = contextAccessor; this.cacheService = cacheService; @@ -91,7 +93,7 @@ private async Task> FetchRoleUserGroupDetailAsync() { List viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"UserGroup/GetUserGroupRoleDetail"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -115,7 +117,7 @@ private async Task> FetchRoleUserGroupDetailForUser { List viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"UserGroup/GetUserGroupRoleDetailByUserId/{userId}"; var response = await client.GetAsync(request).ConfigureAwait(false); diff --git a/LearningHub.Nhs.WebUI/Services/UserService.cs b/LearningHub.Nhs.WebUI/Services/UserService.cs index d9addf3fa..799c91b7b 100644 --- a/LearningHub.Nhs.WebUI/Services/UserService.cs +++ b/LearningHub.Nhs.WebUI/Services/UserService.cs @@ -56,6 +56,7 @@ public class UserService : BaseService, IUserService /// Initializes a new instance of the class. /// /// The learningHubHttpClient. + /// The Open Api Http Client. /// The userApiHttpClient. /// The logger. /// The settings. @@ -68,6 +69,7 @@ public class UserService : BaseService, IUserService /// The login wizard service service. public UserService( ILearningHubHttpClient learningHubHttpClient, + IOpenApiHttpClient openApiHttpClient, IUserApiHttpClient userApiHttpClient, ILogger logger, IOptions settings, @@ -78,7 +80,7 @@ public UserService( ILocationService locationService, IGradeService gradeService, ILoginWizardService loginWizardService) - : base(learningHubHttpClient, logger) + : base(learningHubHttpClient, openApiHttpClient, logger) { this.userApiHttpClient = userApiHttpClient; this.settings = settings.Value; @@ -96,7 +98,7 @@ public async Task> GetActiveContentAsync() { List viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = "User/GetActiveContent"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -336,7 +338,7 @@ public async Task GetCurrentUserProfileAsync() { UserProfile viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = "User/GetCurrentUserProfile"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -361,7 +363,7 @@ public async Task GetUserProfileAsync(int userId) { UserProfile viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"User/GetUserProfile/{userId}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -389,7 +391,7 @@ public async Task CreateUserProfileAsync(UserProfil var json = JsonConvert.SerializeObject(userProfile); var stringContent = new StringContent(json, Encoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"User/CreateUserProfile"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -424,7 +426,7 @@ public async Task UpdateUserProfileAsync(UserProfil var json = JsonConvert.SerializeObject(userProfile); var stringContent = new StringContent(json, Encoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"User/UpdateUserProfile"; var response = await client.PutAsync(request, stringContent).ConfigureAwait(false); @@ -625,7 +627,7 @@ public async Task GetLHUserByUserIdAsync(int id) { UserLHBasicViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"User/GetByUserId/{id}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -1096,7 +1098,7 @@ public async Task UpdateUserAsync(UserUpdateViewMod var json = JsonConvert.SerializeObject(userUpdateViewModel); var stringContent = new StringContent(json, Encoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"User/UpdateUser"; @@ -1132,7 +1134,7 @@ public async Task CreateUserAsync(UserCreateViewMod var json = JsonConvert.SerializeObject(newLhUser); var stringContent = new StringContent(json, Encoding.UTF8, "application/json"); - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"User/CreateUser"; var response = await client.PostAsync(request, stringContent).ConfigureAwait(false); @@ -1568,7 +1570,7 @@ public async Task ValidateEmailChangeTokenAsyn { EmailChangeValidationTokenResult tokenResult = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"User/ValidateEmailChangeToken/{Uri.EscapeUriString(token.EncodeParameter())}/{Uri.EscapeUriString(loctoken.EncodeParameter())}/{isUserRoleUpgrade}"; @@ -1597,7 +1599,7 @@ public async Task GetLastIssuedEmailChangeV { EmailChangeValidationTokenViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = "User/GetLastIssuedEmailChangeValidationToken"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -1622,7 +1624,7 @@ public async Task RegenerateEmailChangeVali { EmailChangeValidationTokenViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"User/ReGenerateEmailChangeValidationToken/{newPrimaryEmail}/{isUserRoleUpgrade}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -1647,7 +1649,7 @@ public async Task CanRequestPasswordResetAsync(string emailAddress, int pa { bool status = false; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"User/CanRequestPasswordReset/{emailAddress}/{passwordRequestLimitingPeriod}/{passwordRequestLimit}"; var response = await client.GetAsync(request).ConfigureAwait(false); @@ -1672,7 +1674,7 @@ public async Task GenerateEmailChangeValida { EmailChangeValidationTokenViewModel viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"User/GenerateEmailChangeValidationTokenAndSendEmail/{emailAddress}/{isUserRoleUpgrade}"; @@ -1696,7 +1698,7 @@ public async Task GenerateEmailChangeValida /// public async Task CancelEmailChangeValidationTokenAsync() { - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"User/CancelEmailChangeValidationToken"; @@ -1846,7 +1848,7 @@ public async Task> GetProvidersByUserIdAsync(int userId) { List viewmodel = null; - var client = await this.LearningHubHttpClient.GetClientAsync(); + var client = await this.OpenApiHttpClient.GetClientAsync(); var request = $"Provider/GetProvidersByUserId/{userId}"; var response = await client.GetAsync(request).ConfigureAwait(false); diff --git a/LearningHub.Nhs.WebUI/Startup/ServiceMappings.cs b/LearningHub.Nhs.WebUI/Startup/ServiceMappings.cs index ad98a2b59..50d52eaae 100644 --- a/LearningHub.Nhs.WebUI/Startup/ServiceMappings.cs +++ b/LearningHub.Nhs.WebUI/Startup/ServiceMappings.cs @@ -8,6 +8,7 @@ using LearningHub.Nhs.Shared.Interfaces.Http; using LearningHub.Nhs.Shared.Interfaces.Services; using LearningHub.Nhs.Shared.Services; + using LearningHub.Nhs.WebUI.Configuration; using LearningHub.Nhs.WebUI.Filters; using LearningHub.Nhs.WebUI.Helpers; using LearningHub.Nhs.WebUI.Interfaces; @@ -42,6 +43,14 @@ public static void AddLearningHubMappings(this IServiceCollection services, ICon HttpClientHandler.DangerousAcceptAnyServerCertificateValidator, }); + services.AddHttpClient() + .ConfigurePrimaryHttpMessageHandler( + () => new HttpClientHandler + { + ServerCertificateCustomValidationCallback = + HttpClientHandler.DangerousAcceptAnyServerCertificateValidator, + }); + services.AddHttpClient() .ConfigurePrimaryHttpMessageHandler( () => new HttpClientHandler @@ -68,6 +77,7 @@ public static void AddLearningHubMappings(this IServiceCollection services, ICon else { services.AddHttpClient(); + services.AddHttpClient(); services.AddHttpClient(); services.AddHttpClient(); services.AddHttpClient(); @@ -75,6 +85,7 @@ public static void AddLearningHubMappings(this IServiceCollection services, ICon // Config services.Configure(configuration.GetSection("OpenAthensScopes")); + services.Configure(configuration.GetSection(BFFPathValidationOptions.SectionName)); // Learning Hub Services services.AddTransient(); @@ -106,6 +117,7 @@ public static void AddLearningHubMappings(this IServiceCollection services, ICon services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); diff --git a/LearningHub.Nhs.WebUI/Styles/nhsuk/common.scss b/LearningHub.Nhs.WebUI/Styles/nhsuk/common.scss index 1c18cc3cc..e7e49e651 100644 --- a/LearningHub.Nhs.WebUI/Styles/nhsuk/common.scss +++ b/LearningHub.Nhs.WebUI/Styles/nhsuk/common.scss @@ -1,80 +1,6 @@ @use "../abstracts/all" as *; @use "nhsuk" as *; -.nhsuk-u-font-style-italic { - font-style: italic; -} - -.nhsuk-button--red { - background-color: $nhsuk-red !important; - - &:hover { - background-color: $nhsuk-red-hover !important; - border-color: $nhsuk-red-hover !important; - } -} - -.nhsuk-button--beta-login { - background-color: $nhsuk-blue; - box-shadow: 0 4px 0 #002f5c; -} - -.nhsuk-button--beta-login:hover { - background-color: $nhsuk-btn-blue-hover; -} - -// By default, the text on reverse (white) buttons turns white once clicked, rendering them invisible. -.nhsuk-button--reverse:visited { - color: $nhsuk-black -} - -.nhsuk-back-link { - padding: px2rem(20) 0; - margin-bottom: 0; -} - -.nhsuk-radios__divider { - text-align: left; - width: unset; -} - -.nhsuk-radios__item label { - font-family: $font-stack; -} - -/* Conditional radio buttons - Note: The nhsuk-radios__conditional element needs to be a SIBLING of the radio button input element - otherwise the CSS selector won't work. See Views/Bookmark/Move.cshtml for a usage example. - The NHSUK component (nhsuk-radios__conditional) requires JavaScript to work. These tweaks allow it to work without. -*/ -.nhsuk-radios__conditional { - display: none; - margin-left: -22px; - margin-top: 8px; -} - -.nhsuk-radios__input:checked ~ .nhsuk-radios__conditional { - display: block !important; -} - -/* jquery unbobtrusive validation style over */ -.nhsuk-error-summary__list li { - color: $nhsuk-red; -} - -.nhsuk-form-group.input-validation-error { - @extend .nhsuk-form-group--error; -} - -.nhsuk-input.input-validation-error { - @extend .nhsuk-input--error -} - -.nhsuk-input:focus { - border: 2px solid #212b32; - box-shadow: inset 0 0 0 2px; - outline: 4px solid #ffeb3b; /* 1 */ - outline-offset: 0; -} .display--hide { display: none !important; @@ -120,92 +46,6 @@ } -.nhsuk-bg-light-blue { - background-color: $nhsuk-light-blue-color; -} - -.nhsuk-bg-pale-blue { - background-color: $nhsuk-pale-blue-color; -} - -.nhsuk-bg-white { - background-color: $color_nhsuk-white; -} - -.nhsuk-width-container.search-width-container { - max-width: px2rem(752); - margin: 0 auto; - padding-left: px2rem(68); - padding-right: px2rem(68); -} - -#maincontent { - - button[class^='nhsuk-search__submit'] span.nhsuk-u-visually-hidden { - color: $color_nhsuk-grey-1; - background-color: $color_nhsuk-white; - } -} - -form label.nhsuk-u-visually-hidden { - color: $color_nhsuk-grey-1; - background-color: $color_nhsuk-white; -} - -/* One third column layout that switches to full width at the small desktop breakpoint (990px) instead of mobile. */ -.nhsuk-grid-column-one-third-small-desktop { - @extend .nhsuk-grid-column-one-third; - width: 33.3333333333% !important; - - @media (max-width: px2rem(990)) { - width: 100% !important; - } -} - - -/* Tweaks to styling for single card view. */ -@media(min-width: 768px) and (max-width: 990px) { - .nhsuk-card-group .nhsuk-grid-column-one-third-small-desktop { - max-width: 600px !important; - } - - .nhsuk-card-group--centred { - justify-content: center !important; - } -} - -.nhsuk-card-banner-container { - padding-bottom: 42.86%; - position: relative; -} - -.nhsuk-card-banner { - height: 100%; - object-fit: cover; - object-position: left; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; -} - -.nhsuk-card-banner-empty { - background-color: $nhsuk-pale-blue-color; - border-bottom: 1px solid #d8dde0; -} - -.nhsuk-error-message.error-message--margin-bottom-1 { - margin-bottom: nhsuk-spacing(1); -} - -.nhsuk-u-margin-bottom-2point5 { - margin-bottom: 12px; - - @media(max-width: 641px) { - margin-bottom: 10px; - } -} .word-break__break-word { word-break: break-word; @@ -216,9 +56,7 @@ form label.nhsuk-u-visually-hidden { justify-content: space-between; } -.nhsuk-button--no-shrink { - white-space: nowrap; -} + .modal-footer--buttons { justify-content: space-between !important; @@ -233,14 +71,6 @@ form label.nhsuk-u-visually-hidden { box-shadow: 0 0 0 0.2rem $nhsuk-yellow !important; background-color: $govuk-focus-highlight-yellow; } -/*Add a background color to the radio button when focused */ -.nhsuk-radios__input:focus + .radioButton { - box-shadow: 0 0 0 3px $nhsuk-yellow; -} -/*Add a background color to the radio button when focused */ -.nhsuk-checkboxes__input:focus + .checkmark { - box-shadow: 0 0 0 4px $nhsuk-yellow; -} .accessible-link:focus { outline: none; diff --git a/LearningHub.Nhs.WebUI/Styles/nhsuk/layout.scss b/LearningHub.Nhs.WebUI/Styles/nhsuk/layout.scss index f8e150eeb..25b84b7d4 100644 --- a/LearningHub.Nhs.WebUI/Styles/nhsuk/layout.scss +++ b/LearningHub.Nhs.WebUI/Styles/nhsuk/layout.scss @@ -40,21 +40,6 @@ body { font-size: px2rem(16); } - -#header-dropdown-menu-control { - opacity: 0; - position: absolute; -} - -#header-dropdown-menu-control:checked ~ .nhsuk-header__navigation:not(.js-show) { - display: block -} - -#header-mobile-search-control { - display: none; -} - - button[data-toggle="modal"] { color: #005eb8; padding: 0; @@ -125,37 +110,16 @@ li.autosuggestion-option:last-of-type { top: 100%; } } - /* tablet */ @media (max-width: px2rem(768)) { - .autosuggestion-menu { top: 100%; } - - #header-mobile-search-control { - display: block; - opacity: 0; - position: absolute; - } - } /* mobile */ @media (max-width: px2rem(640)) { - - #header-mobile-search-control { - display: block; - opacity: 0; - position: absolute; - } - - #header-mobile-search-control:checked ~ .nhsuk-header__search .nhsuk-header__search-wrap { - display: block; - } - - .autosuggestion-menu { top: 100%; } -} \ No newline at end of file +} diff --git a/LearningHub.Nhs.WebUI/Styles/nhsuk/nhsuk-overrides.scss b/LearningHub.Nhs.WebUI/Styles/nhsuk/nhsuk-overrides.scss new file mode 100644 index 000000000..22c319751 --- /dev/null +++ b/LearningHub.Nhs.WebUI/Styles/nhsuk/nhsuk-overrides.scss @@ -0,0 +1,672 @@ +@use "../abstracts/all" as *; +@use "nhsuk" as *; + +.nhsuk-u-font-style-italic { + font-style: italic; +} + +.nhsuk-button--red { + background-color: $nhsuk-red !important; + + &:hover { + background-color: $nhsuk-red-hover !important; + border-color: $nhsuk-red-hover !important; + } +} + +.nhsuk-button--beta-login { + background-color: $nhsuk-blue; + box-shadow: 0 4px 0 #002f5c; +} + +.nhsuk-button--beta-login:hover { + background-color: $nhsuk-btn-blue-hover; +} + +// By default, the text on reverse (white) buttons turns white once clicked, rendering them invisible. +.nhsuk-button--reverse:visited { + color: $nhsuk-black +} + +.nhsuk-back-link { + padding: px2rem(20) 0; + margin-bottom: 0; +} + +.nhsuk-radios__divider { + text-align: left; + width: unset; +} + +.nhsuk-radios__item label { + font-family: $font-stack; +} + +/* Conditional radio buttons - Note: The nhsuk-radios__conditional element needs to be a SIBLING of the radio button input element + otherwise the CSS selector won't work. See Views/Bookmark/Move.cshtml for a usage example. + The NHSUK component (nhsuk-radios__conditional) requires JavaScript to work. These tweaks allow it to work without. +*/ +.nhsuk-radios__conditional { + display: none; + margin-left: -22px; + margin-top: 8px; +} + +.nhsuk-radios__input:checked ~ .nhsuk-radios__conditional { + display: block !important; +} + +/* jquery unbobtrusive validation style over */ +.nhsuk-error-summary__list li { + color: $nhsuk-red; +} + +.nhsuk-form-group.input-validation-error { + @extend .nhsuk-form-group--error; +} + +.nhsuk-input.input-validation-error { + @extend .nhsuk-input--error +} + +.nhsuk-input:focus { + border: 2px solid #212b32; + box-shadow: inset 0 0 0 2px; + outline: 4px solid #ffeb3b; /* 1 */ + outline-offset: 0; +} + +.nhsuk-bg-light-blue { + background-color: $nhsuk-light-blue-color; +} + +.nhsuk-bg-pale-blue { + background-color: $nhsuk-pale-blue-color; +} + +.nhsuk-bg-white { + background-color: $color_nhsuk-white; +} + +.nhsuk-width-container.search-width-container { + max-width: px2rem(752); + margin: 0 auto; + padding-left: px2rem(68); + padding-right: px2rem(68); +} + +#maincontent { + button[class^='nhsuk-search__submit'] span.nhsuk-u-visually-hidden { + color: $color_nhsuk-grey-1; + background-color: $color_nhsuk-white; + } +} + +form label.nhsuk-u-visually-hidden { + color: $color_nhsuk-grey-1; + background-color: $color_nhsuk-white; +} + +/* One third column layout that switches to full width at the small desktop breakpoint (990px) instead of mobile. */ +.nhsuk-grid-column-one-third-small-desktop { + @extend .nhsuk-grid-column-one-third; + width: 33.3333333333% !important; + + @media (max-width: px2rem(990)) { + width: 100% !important; + } +} + + +/* Tweaks to styling for single card view. */ +@media(min-width: 768px) and (max-width: 990px) { + .nhsuk-card-group .nhsuk-grid-column-one-third-small-desktop { + max-width: 600px !important; + } + + .nhsuk-card-group--centred { + justify-content: center !important; + } +} + +.nhsuk-card-banner-container { + padding-bottom: 42.86%; + position: relative; +} + +.nhsuk-card-banner { + height: 100%; + object-fit: cover; + object-position: left; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; +} + +.nhsuk-card-banner-empty { + background-color: $nhsuk-pale-blue-color; + border-bottom: 1px solid #d8dde0; +} + +.nhsuk-error-message.error-message--margin-bottom-1 { + margin-bottom: nhsuk-spacing(1); +} + +.nhsuk-u-margin-bottom-2point5 { + margin-bottom: 12px; + + @media(max-width: 641px) { + margin-bottom: 10px; + } +} + +.nhsuk-button--no-shrink { + white-space: nowrap; +} + +/*Add a background color to the radio button when focused */ +.nhsuk-radios__input:focus + .radioButton { + box-shadow: 0 0 0 3px $nhsuk-yellow; +} +/*Add a background color to the radio button when focused */ +.nhsuk-checkboxes__input:focus + .checkmark { + box-shadow: 0 0 0 4px $nhsuk-yellow; +} + + + +// Below taken from layout.scss +// Overrides largely due to +// - Highly customised header +// - Use of full browser width hero images and full width colour bands +// - Beta banner +// +// Header customisation not needed with current frontend package +// - FGC 19/06/25 + + +.nhsuk-width-container.app-width-container { + max-width: px2rem(1208); + margin: 0 auto; + padding-left: px2rem(32); + padding-right: px2rem(32); +} + +// Header items +.nhsuk-header { + padding: 0 px2rem(32); +} + +.nhsuk-header .nhsuk-width-container.app-width-container { + max-width: px2rem(1144); + margin: 0 auto; +} + +.nhsuk-width-container.app-width-container.beta-banner { + padding: px2rem(8) px2rem(32); + max-width: px2rem(1208); + margin: 0 auto; +} + +.nhsuk-header .nhsuk-header__container::after { + content: none; +} + +.nhsuk-header__navigation.app-width-container { + max-width: px2rem(1144); +} + +.nhsuk-header__container.app-width-container { + display: flex; + justify-content: space-between; + gap: 0 px2rem(24); + padding: px2rem(16) 0; +} + +.nhsuk-header__logo { + flex: 1 0 0; +} + +.nhsuk-header__logo .nhsuk-header__link--service { + display: inline-flex; +} + +.nhsuk-header__service-name { + font-size: px2rem(19); +} + +.nhsuk-header__notification-dot { + position: absolute; + top: px2rem(8); + right: px2rem(-10); + font-size: px2rem(11); + line-height: px2rem(18); + font-weight: 900; + background: $nhsuk-error-color; + color: white; + min-width: px2rem(18); + height: px2rem(18); + text-align: center; + border-radius: px2rem(9); + padding: px2rem(1) px2rem(3) 0; +} + +.nhsuk-header__menu { + display: none; +} + +.nhsuk-header__search .nhsuk-search__input { + width: px2rem(260); +} + +.nhsuk-header__search { + .nhsuk-search__input { + width: px2rem(260); + + &::-moz-placeholder { + opacity: 1; + } + } + + #search > label.nhsuk-u-visually-hidden { + background-color: $nhsuk-white; + } +} + +#header-dropdown-menu-control { + opacity: 0; + position: absolute; +} + + +#header-dropdown-menu-control:checked ~ .nhsuk-header__navigation:not(.js-show) { + display: block +} + +#header-mobile-search-control { + display: none; +} + +.nhsuk-header__break { + display: none; +} + +.nhsuk-header__mobile-only-nav { + display: none; +} + +.nhsuk-header__mobile-break { + display: none; +} + +.nhsuk-header__navigation-item--current { + a { + font-weight: bold; + } +} + +.nhsuk-account__login { + // also a header item + font-size: px2rem(14); + float: right; + position: relative; + z-index: 2; + display: flex; + justify-content: space-between; + align-items: center; + gap: px2rem(24); +} + +.nhsuk-account__login--link, +.nhsuk-account__login--link:visited, +.nhsuk-account__login--link:hover { + // all header items + color: #fff; +} + + +// End of header items + + +.nhsuk-width-container.app-width-container--full { + // used to allow placement of hero (full width) images + margin: 0; + max-width: none +} + +.app-main-wrapper--no-padding { + // allowing hero image to touch headers + padding: 0 +} + +.nhsuk-footer { + padding: px2rem(48) 0; +} + + + +/* large desktop */ +@media (min-width: px2rem(990)) { + // entirely headers + + .nhsuk-header__navigation-item--current a { + border-bottom: 4px solid $nhsuk-grey-lighter; + font-weight: normal; + } + + .nhsuk-header__navigation-link { + position: relative; + } + + .nhsuk-header__navigation-item:last-child { + margin-right: 16px; + } +} + +/* small desktop */ +@media (max-width: px2rem(989)) { + + //entirely headers + + .nhsuk-header__container { + flex-wrap: wrap; + } + + .nhsuk-header { + padding: 0; + } + + .nhsuk-header__container.app-width-container { + flex-wrap: wrap; + gap: 0 0; + padding: px2rem(16) px2rem(32); + } + + .nhsuk-header__link--service { + align-items: center; + -ms-flex-align: center; + margin-bottom: 0; + width: auto; + } + + .nhsuk-header__service-name { + padding-left: px2rem(16); + } + + .nhsuk-header__logo { + order: 0; + } + + .nhsuk-account__login { + // also part of the header + order: 1; + margin-left: auto; + margin-right: 0px; + } + + .nhsuk-header__break { + display: block; + width: 100%; + height: px2rem(24); + order: 2 + } + + .nhsuk-header__search { + order: 3; + flex-grow: 1; + margin-left: 0; + margin-right: px2rem(24); + } + + .nhsuk-header__menu { + display: block; + position: relative; + order: 4; + flex: 0 0 px2rem(74); + } + + .nhsuk-header__navigation-list .nhsuk-header__navigation-item, + .nhsuk-header__navigation .nhsuk-header__navigation-title { + border-top: 1px solid $color_nhsuk-grey-4; + } + + .nhsuk-header__menu-toggle { + text-align: center; + margin: 0; + right: 0; + font-weight: 600; + } + + .nhsuk-header__search-form { + display: flex; + } + + .nhsuk-header__search .nhsuk-search__input { + flex: 1 0 0; + } + + #header-dropdown-menu-control:checked ~ .nhsuk-header__navigation:not(.js-show) { + display: block; + } + + .nhsuk-header__notification-dot { + position: absolute; + top: px2rem(15); + left: px2rem(115); + font-size: px2rem(11); + line-height: px2rem(18); + font-weight: 900; + background: $nhsuk-error-color; + color: $nhsuk-white; + min-width: px2rem(18); + width: fit-content; + height: px2rem(18); + text-align: center; + border-radius: px2rem(9); + } + + .nhsuk-header__navigation-item--current .nhsuk-header__notification-dot { + left: px2rem(125); + } + + .nhsuk-header__menu-notification-dot { + position: absolute; + top: px2rem(-5); + right: px2rem(-6); + background: $nhsuk-error-color; + width: px2rem(12); + height: px2rem(12); + border-radius: px2rem(6); + box-shadow: 0 0 0 2px white; + z-index: 10; + } +} + +@media (max-width: px2rem(768)) { + .nhsuk-width-container.app-width-container, + .nhsuk-width-container.app-width-container.beta-banner { + padding-left: px2rem(16); + padding-right: px2rem(16); + } + + // entirely headers from this point + + .nhsuk-back-link { + padding: 0.5rem 0; + } + + .nhsuk-header__menu .nhsuk-header__not-mobile { + display: none; + } + + .nhsuk-header__not-mobile { + display: none; + } + + .nhsuk-header__mobile-only-nav { + display: flex; + order: 1; + justify-content: space-around; + gap: 0 px2rem(16); + align-items: flex-start; + flex-wrap: wrap; + width: px2rem(166); + } + + .nhsuk-header__mobile-only-nav .nhsuk-header__menu { + margin-right: px2rem(12); + } + + .nhsuk-header__mobile-only-nav .nhsuk-header__search-toggle { + margin-left: px2rem(12); + } + + .nhsuk-header__break { + display: none; + } + + .nhsuk-header__mobile-break { + display: block; + width: 100%; + height: 0; + } + + .nhsuk-header__link--service { + flex-direction: column; + align-items: flex-start; + } + + .nhsuk-header__notification-dot { + top: px2rem(13); + left: px2rem(100); + } + + .nhsuk-header__service-name { + padding: px2rem(12) 0 0; + } + + .nhsuk-header__search-toggle { + position: relative; + height: px2rem(40); + order: 2; + padding: px2rem(7) px2rem(10) 0; + margin: 0 + } + + .nhsuk-header__search .nhsuk-search__submit { + padding-top: nhsuk-spacing(1); + } + + .nhsuk-header__menu { + order: 3; + } + + .nhsuk-header__search { + order: 4; + width: 100%; + flex-grow: 1; + margin: px2rem(16) px2rem(-16) 0; + border-bottom: 1px solid $color_nhsuk-grey-4; + } +} + +@media (max-width: px2rem(640)) { + + //entirely headers + + .nhsuk-header__not-mobile { + display: none; + } + + .nhsuk-header__logo { + max-width: none; + } + + .nhsuk-header__mobile-only-nav { + display: flex; + order: 1; + justify-content: space-around; + gap: 0 px2rem(16); + align-items: flex-start; + flex-wrap: wrap; + width: px2rem(166); + } + + .nhsuk-header__pre-login .nhsuk-header__mobile-only-nav { + align-items: center; + justify-content: flex-end; + padding-bottom: px2rem(16); + } + + .nhsuk-header__mobile-only-nav .nhsuk-header__menu { + margin-right: px2rem(12); + } + + .nhsuk-header__mobile-only-nav .nhsuk-header__search-toggle { + margin-left: px2rem(12); + } + + .nhsuk-header__break { + display: none; + } + + .nhsuk-header__mobile-break { + display: block; + width: 100%; + height: 0; + } + + .nhsuk-header__link--service { + flex-direction: column; + align-items: flex-start; + } + + .nhsuk-header__notification-dot { + top: px2rem(13); + left: px2rem(100); + } + + .nhsuk-header__service-name { + padding: px2rem(12) 0 0; + } + + .nhsuk-header__search-toggle { + position: relative; + height: px2rem(40); + order: 2; + padding: px2rem(7) px2rem(10) 0; + margin: 0 + } + + .nhsuk-header__search .nhsuk-search__submit { + padding-top: nhsuk-spacing(1); + } + + .nhsuk-header__menu { + order: 3; + } + + .nhsuk-header__search { + order: 4; + width: 100%; + flex-grow: 1; + margin: px2rem(16) px2rem(-16) 0; + border-bottom: 1px solid $color_nhsuk-grey-4; + } + + #header-mobile-search-control { + display: block; + opacity: 0; + position: absolute; + } + + #header-mobile-search-control:checked ~ .nhsuk-header__search .nhsuk-header__search-wrap { + display: block; + } + + .nhsuk-width-container.nhsuk-header__container.app-width-container { + padding-bottom: 0; + } +} diff --git a/LearningHub.Nhs.WebUI/Styles/nhsuk/nhsuk.scss b/LearningHub.Nhs.WebUI/Styles/nhsuk/nhsuk.scss index 40c7e1095..7254e0901 100644 --- a/LearningHub.Nhs.WebUI/Styles/nhsuk/nhsuk.scss +++ b/LearningHub.Nhs.WebUI/Styles/nhsuk/nhsuk.scss @@ -1,485 +1 @@ -@import "../../node_modules/nhsuk-frontend/packages/nhsuk"; - -// Overrides largely due to -// - Highly customised header -// - Use of full browser width hero images and full width colour bands -// - Beta banner -// -// Header customisation not needed with current frontend package -// -// - FGC 19/06/25 - - -.nhsuk-width-container.app-width-container { - max-width: px2rem(1208); - margin: 0 auto; - padding-left: px2rem(32); - padding-right: px2rem(32); -} - -// Header items - .nhsuk-header { - padding: 0 px2rem(32); - } - - .nhsuk-header .nhsuk-width-container.app-width-container { - max-width: px2rem(1144); - margin: 0 auto; - } - - .nhsuk-width-container.app-width-container.beta-banner { - padding: px2rem(8) px2rem(32); - max-width: px2rem(1208); - margin: 0 auto; - } - - .nhsuk-header .nhsuk-header__container::after { - content: none; - } - - .nhsuk-header__navigation.app-width-container { - max-width: px2rem(1144); - } - - .nhsuk-header__container.app-width-container { - display: flex; - justify-content: space-between; - gap: 0 px2rem(24); - padding: px2rem(16) 0; - } - - .nhsuk-header__logo { - flex: 1 0 0; - } - - .nhsuk-header__logo .nhsuk-header__link--service { - display: inline-flex; - } - - .nhsuk-header__service-name { - font-size: px2rem(19); - } - - .nhsuk-header__notification-dot { - position: absolute; - top: px2rem(8); - right: px2rem(-10); - font-size: px2rem(11); - line-height: px2rem(18); - font-weight: 900; - background: $nhsuk-error-color; - color: white; - min-width: px2rem(18); - height: px2rem(18); - text-align: center; - border-radius: px2rem(9); - padding: px2rem(1) px2rem(3) 0; - } - - .nhsuk-header__menu { - display: none; - } - - .nhsuk-header__search .nhsuk-search__input { - width: px2rem(260); - } - - .nhsuk-header__search { - .nhsuk-search__input { - width: px2rem(260); - - &::-moz-placeholder { - opacity: 1; - } - } - - #search > label.nhsuk-u-visually-hidden { - background-color: $nhsuk-white; - } - } - - .nhsuk-header__break { - display: none; - } - - .nhsuk-header__mobile-only-nav { - display: none; - } - - .nhsuk-header__mobile-break { - display: none; - } - - .nhsuk-header__navigation-item--current { - a { - font-weight: bold; - } - } - - .nhsuk-account__login { - // also a header item - font-size: px2rem(14); - float: right; - position: relative; - z-index: 2; - display: flex; - justify-content: space-between; - align-items: center; - gap: px2rem(24); - } - - .nhsuk-account__login--link, - .nhsuk-account__login--link:visited, - .nhsuk-account__login--link:hover { - // all header items - color: #fff; - } - - -// End of header items - - -.app-width-container--full { - // used to allow placement of hero (full width) images - margin: 0; - max-width: none -} - -.app-main-wrapper--no-padding { - // allowing hero image to touch headers - padding: 0 -} - -.nhsuk-footer { - padding: px2rem(48) 0; -} - - - -/* large desktop */ -@media (min-width: px2rem(990)) { - // entirely headers - - .nhsuk-header__navigation-item--current a { - border-bottom: 4px solid $nhsuk-grey-lighter; - font-weight: normal; - } - - .nhsuk-header__navigation-link { - position: relative; - } - - .nhsuk-header__navigation-item:last-child { - margin-right: 16px; - } -} - -/* small desktop */ -@media (max-width: px2rem(989)) { - - //entirely headers - - .nhsuk-header__container { - flex-wrap: wrap; - } - - .nhsuk-header { - padding: 0; - } - - .nhsuk-header__container.app-width-container { - flex-wrap: wrap; - gap: 0 0; - padding: px2rem(16) px2rem(32); - } - - .nhsuk-header__link--service { - align-items: center; - -ms-flex-align: center; - margin-bottom: 0; - width: auto; - } - - .nhsuk-header__service-name { - padding-left: px2rem(16); - } - - .nhsuk-header__logo { - order: 0; - } - - .nhsuk-header__break { - display: block; - width: 100%; - height: px2rem(24); - order: 2 - } - - .nhsuk-header__search { - order: 3; - flex-grow: 1; - margin-left: 0; - margin-right: px2rem(24); - } - - .nhsuk-header__menu { - display: block; - position: relative; - order: 4; - flex: 0 0 px2rem(74); - } - - .nhsuk-header__navigation-list .nhsuk-header__navigation-item, - .nhsuk-header__navigation .nhsuk-header__navigation-title { - border-top: 1px solid $color_nhsuk-grey-4; - } - - .nhsuk-header__menu-toggle { - text-align: center; - margin: 0; - right: 0; - font-weight: 600; - } - - .nhsuk-header__search-form { - display: flex; - } - - .nhsuk-header__search .nhsuk-search__input { - flex: 1 0 0; - } - - #header-dropdown-menu-control:checked ~ .nhsuk-header__navigation:not(.js-show) { - display: block; - } - - .nhsuk-header__notification-dot { - position: absolute; - top: px2rem(15); - left: px2rem(115); - font-size: px2rem(11); - line-height: px2rem(18); - font-weight: 900; - background: $nhsuk-error-color; - color: $nhsuk-white; - min-width: px2rem(18); - width: fit-content; - height: px2rem(18); - text-align: center; - border-radius: px2rem(9); - } - - .nhsuk-header__navigation-item--current .nhsuk-header__notification-dot { - left: px2rem(125); - } - - .nhsuk-header__menu-notification-dot { - position: absolute; - top: px2rem(-5); - right: px2rem(-6); - background: $nhsuk-error-color; - width: px2rem(12); - height: px2rem(12); - border-radius: px2rem(6); - box-shadow: 0 0 0 2px white; - z-index: 10; - } - - .nhsuk-account__login { - // also part of the header - order: 1; - margin-left: auto; - margin-right: 0px; - } - -} - -@media (max-width: px2rem(768)) { - .nhsuk-width-container.app-width-container, - .nhsuk-width-container.app-width-container.beta-banner { - padding-left: px2rem(16); - padding-right: px2rem(16); - } - - // entirely headers from this point - - .nhsuk-back-link { - padding: 0.5rem 0; - } - - .nhsuk-header__menu .nhsuk-header__not-mobile { - display: none; - } - - .nhsuk-header__not-mobile { - display: none; - } - - .nhsuk-header__mobile-only-nav { - display: flex; - order: 1; - justify-content: space-around; - gap: 0 px2rem(16); - align-items: flex-start; - flex-wrap: wrap; - width: px2rem(166); - } - - .nhsuk-header__mobile-only-nav .nhsuk-header__menu { - margin-right: px2rem(12); - } - - .nhsuk-header__mobile-only-nav .nhsuk-header__search-toggle { - margin-left: px2rem(12); - } - - .nhsuk-header__break { - display: none; - } - - .nhsuk-header__mobile-break { - display: block; - width: 100%; - height: 0; - } - - .nhsuk-header__link--service { - flex-direction: column; - align-items: flex-start; - } - - .nhsuk-header__notification-dot { - top: px2rem(13); - left: px2rem(100); - } - - .nhsuk-header__service-name { - padding: px2rem(12) 0 0; - } - - .nhsuk-header__search-toggle { - position: relative; - height: px2rem(40); - order: 2; - padding: px2rem(7) px2rem(10) 0; - margin: 0 - } - - .nhsuk-header__search .nhsuk-search__submit { - padding-top: nhsuk-spacing(1); - } - - .nhsuk-header__menu { - order: 3; - } - - .nhsuk-header__search { - order: 4; - width: 100%; - flex-grow: 1; - margin: px2rem(16) px2rem(-16) 0; - border-bottom: 1px solid $color_nhsuk-grey-4; - } - - #header-mobile-search-control:checked ~ .nhsuk-header__search .nhsuk-header__search-wrap { - display: block; - } - - .nhsuk-width-container.nhsuk-header__container.app-width-container { - padding-bottom: 0; - } - -} - -@media (max-width: px2rem(640)) { - - //entirely headers - - .nhsuk-header__not-mobile { - display: none; - } - - .nhsuk-header__logo { - max-width: none; - } - - .nhsuk-header__mobile-only-nav { - display: flex; - order: 1; - justify-content: space-around; - gap: 0 px2rem(16); - align-items: flex-start; - flex-wrap: wrap; - width: px2rem(166); - } - - .nhsuk-header__pre-login .nhsuk-header__mobile-only-nav { - align-items: center; - justify-content: flex-end; - padding-bottom: px2rem(16); - } - - .nhsuk-header__mobile-only-nav .nhsuk-header__menu { - margin-right: px2rem(12); - } - - .nhsuk-header__mobile-only-nav .nhsuk-header__search-toggle { - margin-left: px2rem(12); - } - - .nhsuk-header__break { - display: none; - } - - .nhsuk-header__mobile-break { - display: block; - width: 100%; - height: 0; - } - - .nhsuk-header__link--service { - flex-direction: column; - align-items: flex-start; - } - - .nhsuk-header__notification-dot { - top: px2rem(13); - left: px2rem(100); - } - - .nhsuk-header__service-name { - padding: px2rem(12) 0 0; - } - - .nhsuk-header__search-toggle { - position: relative; - height: px2rem(40); - order: 2; - padding: px2rem(7) px2rem(10) 0; - margin: 0 - } - - .nhsuk-header__search .nhsuk-search__submit { - padding-top: nhsuk-spacing(1); - } - - .nhsuk-header__menu { - order: 3; - } - - .nhsuk-header__search { - order: 4; - width: 100%; - flex-grow: 1; - margin: px2rem(16) px2rem(-16) 0; - border-bottom: 1px solid $color_nhsuk-grey-4; - } - - #header-mobile-search-control:checked ~ .nhsuk-header__search .nhsuk-header__search-wrap { - display: block; - } - - .nhsuk-width-container.nhsuk-header__container.app-width-container { - padding-bottom: 0; - } -} \ No newline at end of file +@import "../../node_modules/nhsuk-frontend/packages/nhsuk"; \ No newline at end of file diff --git a/LearningHub.Nhs.WebUI/Views/Search/_SearchBar.cshtml b/LearningHub.Nhs.WebUI/Views/Search/_SearchBar.cshtml index a0139e3eb..da4a475e9 100644 --- a/LearningHub.Nhs.WebUI/Views/Search/_SearchBar.cshtml +++ b/LearningHub.Nhs.WebUI/Views/Search/_SearchBar.cshtml @@ -48,8 +48,8 @@ function autocomplete(input, minLength) { if (input != null) { input.addEventListener("input", function () { - var val = this.value; - if (val.length < minLength) { + var val = this.value.trimStart(); + if (val.length < minLength || val.trim() === "") { closeAllLists(); return false; } diff --git a/LearningHub.Nhs.WebUI/Views/Shared/Components/NavigationItems/Searchbar.cshtml b/LearningHub.Nhs.WebUI/Views/Shared/Components/NavigationItems/Searchbar.cshtml index 8f7f8a738..f3a69f8e6 100644 --- a/LearningHub.Nhs.WebUI/Views/Shared/Components/NavigationItems/Searchbar.cshtml +++ b/LearningHub.Nhs.WebUI/Views/Shared/Components/NavigationItems/Searchbar.cshtml @@ -72,8 +72,8 @@ function autocomplete(input, minLength) { if (input != null) { input.addEventListener("input", function () { - var val = this.value; - if (val.length < minLength) { + var val = this.value.trimStart(); + if (val.length < minLength || val.trim() === "") { closeAllLists(); return false; } diff --git a/LearningHub.Nhs.WebUI/Views/Shared/Tenant/LearningHub/_Layout.cshtml b/LearningHub.Nhs.WebUI/Views/Shared/Tenant/LearningHub/_Layout.cshtml index c16fa837c..6b679b67e 100644 --- a/LearningHub.Nhs.WebUI/Views/Shared/Tenant/LearningHub/_Layout.cshtml +++ b/LearningHub.Nhs.WebUI/Views/Shared/Tenant/LearningHub/_Layout.cshtml @@ -21,6 +21,7 @@ +