From ff76d8f48bd1062c40fd5ef9bc97acfd91fb90e5 Mon Sep 17 00:00:00 2001 From: Phil-NHS Date: Fri, 5 Jul 2024 11:03:20 +0100 Subject: [PATCH 01/10] pulled from scorpius and added openapicontrollerbase --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 4119589ad..04baa838f 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,5 @@ obj /WebAPI/MigrationTool/LearningHub.Nhs.Migration.Staging.Database/LearningHub.Nhs.Migration.Staging.Database.dbmdl /WebAPI/MigrationTool/LearningHub.Nhs.Migration.Staging.Database/LearningHub.Nhs.Migration.Staging.Database.jfm /LearningHub.Nhs.WebUI.AutomatedUiTests/appsettings.Development.json +/OpenAPI/LearningHub.Nhs.OpenApi/appsettings.Development.json +/OpenAPI/LearningHub.Nhs.OpenApi/web.config From 1b3d60dcfbc87cdc376589d8e5c810601adbe621 Mon Sep 17 00:00:00 2001 From: Phil-NHS Date: Fri, 5 Jul 2024 13:21:17 +0100 Subject: [PATCH 02/10] current userid into controller --- .../Services/IResourceService.cs | 4 +- .../Services/ISearchService.cs | 2 +- .../Services/ResourceService.cs | 5 ++- .../Services/SearchService.cs | 6 +-- .../Controllers/OpenApiControllerBase.cs | 41 +++++++++++++++++++ .../Controllers/ResourceController.cs | 10 ++--- 6 files changed, 55 insertions(+), 13 deletions(-) create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi/Controllers/OpenApiControllerBase.cs diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/IResourceService.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/IResourceService.cs index 8b2e46dee..795ddbf42 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/IResourceService.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/IResourceService.cs @@ -15,13 +15,13 @@ public interface IResourceService /// /// The original resource reference id. /// The the resourceMetaDataViewModel corresponding to the resource reference. - Task GetResourceReferenceByOriginalId(int originalResourceReferenceId); + Task GetResourceReferenceByOriginalId(int originalResourceReferenceId, int? currentUserId); /// /// The get resources by Ids endpoint. /// /// The original resource reference Ids. /// The resourceReferenceMetaDataViewModel. - Task GetResourceReferencesByOriginalIds(List originalResourceReferenceIds); + Task GetResourceReferencesByOriginalIds(List originalResourceReferenceIds, int? currentUserId); } } diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/ISearchService.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/ISearchService.cs index 63155c5eb..c9b6e3031 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/ISearchService.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/Services/ISearchService.cs @@ -14,6 +14,6 @@ public interface ISearchService /// /// . /// . - Task Search(ResourceSearchRequest query); + Task Search(ResourceSearchRequest query, int? currentUserId); } } diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/ResourceService.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/ResourceService.cs index 1e3f4e563..ab154888e 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/ResourceService.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/ResourceService.cs @@ -47,8 +47,9 @@ public ResourceService(ILearningHubService learningHubService, IResourceReposito /// the get by id async. /// /// the id. + /// /// the resource. - public async Task GetResourceReferenceByOriginalId(int originalResourceReferenceId) + public async Task GetResourceReferenceByOriginalId(int originalResourceReferenceId, int? currentUserId) { var list = new List() { originalResourceReferenceId }; @@ -102,7 +103,7 @@ public async Task GetResourceReferencesByOrigina return new BulkResourceReferenceViewModel(matchedResources, unmatchedIds); } - private ResourceReferenceWithResourceDetailsViewModel GetResourceReferenceWithResourceDetailsViewModel(ResourceReference resourceReference) + private ResourceReferenceWithResourceDetailsViewModel GetResourceReferenceWithResourceDetailsViewModel(ResourceReference resourceReference, int? currentUserIdint? currentUserId) { var hasCurrentResourceVersion = resourceReference.Resource.CurrentResourceVersion != null; var hasRating = resourceReference.Resource.CurrentResourceVersion?.ResourceVersionRatingSummary != null; diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/SearchService.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/SearchService.cs index 656a8cb14..4c36da24a 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/SearchService.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/SearchService.cs @@ -57,7 +57,7 @@ public SearchService( } /// - public async Task Search(ResourceSearchRequest query) + public async Task Search(ResourceSearchRequest query, int? currentUserId) { var findwiseResultModel = await this.findwiseClient.Search(query); @@ -66,7 +66,7 @@ public async Task Search(ResourceSearchRequest query) return ResourceSearchResultModel.FailedWithStatus(findwiseResultModel.FindwiseRequestStatus); } - var resourceMetadataViewModels = await this.GetResourceMetadataViewModels(findwiseResultModel); + var resourceMetadataViewModels = await this.GetResourceMetadataViewModels(findwiseResultModel, currentUserId); var totalHits = findwiseResultModel.SearchResults?.Stats.TotalHits; @@ -77,7 +77,7 @@ public async Task Search(ResourceSearchRequest query) } private async Task> GetResourceMetadataViewModels( - FindwiseResultModel findwiseResultModel) + FindwiseResultModel findwiseResultModel, int? currentUserId) { var documentsFound = findwiseResultModel.SearchResults?.DocumentList.Documents?.ToList() ?? new List(); diff --git a/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/OpenApiControllerBase.cs b/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/OpenApiControllerBase.cs new file mode 100644 index 000000000..863a8bb22 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/OpenApiControllerBase.cs @@ -0,0 +1,41 @@ +namespace LearningHub.NHS.OpenAPI.Controllers +{ + using System.Net; + using System.Security.Claims; + using LearningHub.Nhs.OpenApi.Models.Exceptions; + using Microsoft.AspNetCore.Mvc; + + /// + /// The base class for API controllers. + /// + public abstract class OpenApiControllerBase : ControllerBase + { + /// + /// Gets the current user's ID. + /// + public int? CurrentUserId + { + get + { + if ((this.User?.Identity?.AuthenticationType ?? null) == "AuthenticationTypes.Federation") + { + int userId; + if (int.TryParse(User.FindFirst(ClaimTypes.NameIdentifier).Value, out userId)) + { + return userId; + } + else + { + // If parsing fails, return null - for apikey this will be the name + return null; + } + } + else + { + // When authorizing by ApiKey we do not have a user for example + return null; + } + } + } + } +} diff --git a/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/ResourceController.cs b/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/ResourceController.cs index 95591961f..a92463d20 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/ResourceController.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi/Controllers/ResourceController.cs @@ -21,7 +21,7 @@ namespace LearningHub.NHS.OpenAPI.Controllers /// [Route("Resource")] [Authorize] - public class ResourceController : Controller + public class ResourceController : OpenApiControllerBase { private const int MaxNumberOfReferenceIds = 1000; private readonly ISearchService searchService; @@ -75,7 +75,7 @@ await this.searchService.Search( offset, limit ?? this.findwiseConfig.DefaultItemLimitForSearch, catalogueId, - resourceTypes)); + resourceTypes), this.CurrentUserId); switch (resourceSearchResult.FindwiseRequestStatus) { @@ -109,7 +109,7 @@ await this.searchService.Search( [HttpGet("{originalResourceReferenceId}")] public async Task GetResourceReferenceByOriginalId(int originalResourceReferenceId) { - return await this.resourceService.GetResourceReferenceByOriginalId(originalResourceReferenceId); + return await this.resourceService.GetResourceReferenceByOriginalId(originalResourceReferenceId, this.CurrentUserId); } /// @@ -125,7 +125,7 @@ public async Task GetResourceReferencesByOrigina throw new HttpResponseException($"Too many resources requested. The maximum is {MaxNumberOfReferenceIds}", HttpStatusCode.BadRequest); } - return await this.resourceService.GetResourceReferencesByOriginalIds(resourceReferenceIds.ToList()); + return await this.resourceService.GetResourceReferencesByOriginalIds(resourceReferenceIds.ToList(), this.CurrentUserId); } /// @@ -148,7 +148,7 @@ public async Task GetResourceReferencesByOrigina throw new HttpResponseException($"Too many resources requested. The maximum is {MaxNumberOfReferenceIds}", HttpStatusCode.BadRequest); } - return await this.resourceService.GetResourceReferencesByOriginalIds(bulkResourceReferences.ResourceReferenceIds); + return await this.resourceService.GetResourceReferencesByOriginalIds(bulkResourceReferences.ResourceReferenceIds, this.CurrentUserId); } } } From 645c2b3e8e7b8af3cc910f73138f2a9502e9d845 Mon Sep 17 00:00:00 2001 From: Phil-NHS Date: Fri, 5 Jul 2024 14:07:36 +0100 Subject: [PATCH 03/10] update models --- .../LearningHub.Nhs.OpenApi.Models.csproj | 2 +- .../EntityFramework/LearningHubDbContext.cs | 12 ++++++------ .../EntityFramework/ServiceMappings.cs | 4 ++-- .../Map/Content/PageSectionDetailMap.cs | 4 +--- .../Map/LogMap.cs | 4 ---- .../Blocks/WholeSlideImageAnnotationMap.cs | 10 +++++----- .../Blocks/WholeSlideImageAnnotationMarkMap.cs | 14 +++++++------- ...arningHub.Nhs.OpenApi.Services.Interface.csproj | 2 +- .../LearningHub.Nhs.OpenApi.Services.csproj | 3 ++- .../Services/ResourceService.cs | 4 ++-- 10 files changed, 27 insertions(+), 32 deletions(-) diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Models/LearningHub.Nhs.OpenApi.Models.csproj b/OpenAPI/LearningHub.Nhs.OpenApi.Models/LearningHub.Nhs.OpenApi.Models.csproj index 0047f4483..d47af75fc 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Models/LearningHub.Nhs.OpenApi.Models.csproj +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Models/LearningHub.Nhs.OpenApi.Models.csproj @@ -16,7 +16,7 @@ - + diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/LearningHubDbContext.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/LearningHubDbContext.cs index 020a4f1f5..9cbbcc3f0 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/LearningHubDbContext.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/LearningHubDbContext.cs @@ -312,14 +312,14 @@ public LearningHubDbContext(LearningHubDbContextOptions options) public virtual DbSet DashboardResourceDto { get; set; } /// - /// Gets or sets the ScormContentDetailsViewModel. + /// Gets or sets the ExternalContentDetailsViewModel. /// - public virtual DbSet ScormContentDetailsViewModel { get; set; } + public virtual DbSet ExternalContentDetailsViewModel { get; set; } /// - /// Gets or sets the ScormContentServerViewModel. + /// Gets or sets the ContentServerViewModel. /// - public virtual DbSet ScormContentServerViewModel { get; set; } + public virtual DbSet ContentServerViewModel { get; set; } /// /// Gets or sets the DashboardCatalogueDto @@ -520,12 +520,12 @@ public LearningHubDbContext(LearningHubDbContextOptions options) /// /// Gets or sets the whole slide image annotation. /// - public virtual DbSet WholeSlideImageAnnotation { get; set; } + public virtual DbSet ImageAnnotation { get; set; } /// /// Gets or sets the whole slide image annotation mark. /// - public virtual DbSet WholeSlideImageAnnotationMark { get; set; } + public virtual DbSet ImageAnnotationMark { get; set; } /// /// Gets or sets the media block. diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/ServiceMappings.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/ServiceMappings.cs index 668d318b1..8db1cd876 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/ServiceMappings.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/ServiceMappings.cs @@ -110,8 +110,8 @@ public static void AddLearningHubMappings(this IServiceCollection services, ICon services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Content/PageSectionDetailMap.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Content/PageSectionDetailMap.cs index 1bdbb69c8..42d2e309f 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Content/PageSectionDetailMap.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Content/PageSectionDetailMap.cs @@ -17,8 +17,6 @@ protected override void InternalMap(EntityTypeBuilder entity) { entity.ToTable("PageSectionDetail", "content"); - entity.Property(e => e.AssetPositionId).HasDefaultValueSql("((2))"); - entity.Property(e => e.BackgroundColour).HasMaxLength(20); entity.Property(e => e.Description).HasMaxLength(512); @@ -31,7 +29,7 @@ protected override void InternalMap(EntityTypeBuilder entity) entity.Property(e => e.TextColour).HasMaxLength(20); - entity.Property(e => e.Title).HasMaxLength(128); + entity.Property(e => e.SectionTitle).HasMaxLength(128); entity.Property(e => e.DeletePending).IsRequired(false); diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/LogMap.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/LogMap.cs index 62ebffd0c..0b8bb7428 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/LogMap.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/LogMap.cs @@ -68,10 +68,6 @@ protected void InternalMap(EntityTypeBuilder modelBuilder) modelBuilder.Property(e => e.UserId) .HasColumnName("UserId"); - modelBuilder.HasOne(d => d.User) - .WithMany(p => p.Logs) - .HasForeignKey(d => d.UserId) - .OnDelete(DeleteBehavior.ClientSetNull); } } } diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Resources/Blocks/WholeSlideImageAnnotationMap.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Resources/Blocks/WholeSlideImageAnnotationMap.cs index 18a94369a..00f7f46cb 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Resources/Blocks/WholeSlideImageAnnotationMap.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Resources/Blocks/WholeSlideImageAnnotationMap.cs @@ -7,7 +7,7 @@ /// /// The whole slide image annotation map. /// - public class WholeSlideImageAnnotationMap : BaseEntityMap + public class ImageAnnotationMap : BaseEntityMap { /// /// The internal map. @@ -15,15 +15,15 @@ public class WholeSlideImageAnnotationMap : BaseEntityMap /// The model builder. /// - protected override void InternalMap(EntityTypeBuilder modelBuilder) + protected override void InternalMap(EntityTypeBuilder modelBuilder) { - modelBuilder.ToTable("WholeSlideImageAnnotation", "resources"); + modelBuilder.ToTable("ImageAnnotation", "resources"); modelBuilder.HasOne(a => a.WholeSlideImage) - .WithMany(i => i.WholeSlideImageAnnotations) + .WithMany(i => i.ImageAnnotations) .HasForeignKey(a => a.WholeSlideImageId) .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("FK_WholeSlideImageAnnotation_WholeSlideImageId"); + .HasConstraintName("FK_ImageAnnotation_WholeSlideImageId"); } } } diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Resources/Blocks/WholeSlideImageAnnotationMarkMap.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Resources/Blocks/WholeSlideImageAnnotationMarkMap.cs index 2720b23db..9db0a6db6 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Resources/Blocks/WholeSlideImageAnnotationMarkMap.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Resources/Blocks/WholeSlideImageAnnotationMarkMap.cs @@ -7,7 +7,7 @@ /// /// The whole slide image annotation map. /// - public class WholeSlideImageAnnotationMarkMap : BaseEntityMap + public class ImageAnnotationMarkMap : BaseEntityMap { /// /// The internal map. @@ -15,15 +15,15 @@ public class WholeSlideImageAnnotationMarkMap : BaseEntityMap /// The model builder. /// - protected override void InternalMap(EntityTypeBuilder modelBuilder) + protected override void InternalMap(EntityTypeBuilder modelBuilder) { - modelBuilder.ToTable("WholeSlideImageAnnotationMark", "resources"); + modelBuilder.ToTable("ImageAnnotationMark", "resources"); - modelBuilder.HasOne(a => a.WholeSlideImageAnnotation) - .WithMany(i => i.WholeSlideImageAnnotationMarks) - .HasForeignKey(a => a.WholeSlideImageAnnotationId) + modelBuilder.HasOne(a => a.ImageAnnotation) + .WithMany(i => i.ImageAnnotationMarks) + .HasForeignKey(a => a.ImageAnnotationId) .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("FK_WholeSlideImageAnnotationMark_WholeSlideImageAnnotationId"); + .HasConstraintName("FK_ImageAnnotationMark_ImageAnnotationId"); } } } diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/LearningHub.Nhs.OpenApi.Services.Interface.csproj b/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/LearningHub.Nhs.OpenApi.Services.Interface.csproj index 6372c89b6..8309db600 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/LearningHub.Nhs.OpenApi.Services.Interface.csproj +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/LearningHub.Nhs.OpenApi.Services.Interface.csproj @@ -16,7 +16,7 @@ - + diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Services/LearningHub.Nhs.OpenApi.Services.csproj b/OpenAPI/LearningHub.Nhs.OpenApi.Services/LearningHub.Nhs.OpenApi.Services.csproj index 2207dfdd4..a639e5f26 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Services/LearningHub.Nhs.OpenApi.Services.csproj +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Services/LearningHub.Nhs.OpenApi.Services.csproj @@ -1,4 +1,4 @@ - + net6.0 @@ -24,6 +24,7 @@ + diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/ResourceService.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/ResourceService.cs index ab154888e..d9d4fa40b 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/ResourceService.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/ResourceService.cs @@ -79,7 +79,7 @@ public async Task GetResourceRefe /// /// the resource reference ids. /// the resource. - public async Task GetResourceReferencesByOriginalIds(List originalResourceReferenceIds) + public async Task GetResourceReferencesByOriginalIds(List originalResourceReferenceIds, int? currentUserId) { var resourceReferences = await this.resourceRepository.GetResourceReferencesByOriginalResourceReferenceIds(originalResourceReferenceIds); var resourceReferencesList = resourceReferences.ToList(); @@ -103,7 +103,7 @@ public async Task GetResourceReferencesByOrigina return new BulkResourceReferenceViewModel(matchedResources, unmatchedIds); } - private ResourceReferenceWithResourceDetailsViewModel GetResourceReferenceWithResourceDetailsViewModel(ResourceReference resourceReference, int? currentUserIdint? currentUserId) + private ResourceReferenceWithResourceDetailsViewModel GetResourceReferenceWithResourceDetailsViewModel(ResourceReference resourceReference) { var hasCurrentResourceVersion = resourceReference.Resource.CurrentResourceVersion != null; var hasRating = resourceReference.Resource.CurrentResourceVersion?.ResourceVersionRatingSummary != null; From 7105627878cdd2d715d82baf13a1ae05e359aa15 Mon Sep 17 00:00:00 2001 From: Phil-NHS Date: Fri, 5 Jul 2024 14:47:24 +0100 Subject: [PATCH 04/10] Updating models --- .../LearningHub.Nhs.OpenApi.Models.csproj | 2 +- .../ViewModels/ResourceMetadataViewModel.cs | 13 +++++++- .../Repositories/IResourceRepository.cs | 9 +++++ .../EntityFramework/LearningHubDbContext.cs | 5 +++ .../Repositories/ResourceRepository.cs | 33 +++++++++++++++++++ ...gHub.Nhs.OpenApi.Services.Interface.csproj | 2 +- .../LearningHub.Nhs.OpenApi.Services.csproj | 2 +- .../Services/ResourceService.cs | 11 +++++++ .../Services/SearchService.cs | 18 ++++++++-- 9 files changed, 89 insertions(+), 6 deletions(-) diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Models/LearningHub.Nhs.OpenApi.Models.csproj b/OpenAPI/LearningHub.Nhs.OpenApi.Models/LearningHub.Nhs.OpenApi.Models.csproj index d47af75fc..2c87ba2cb 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Models/LearningHub.Nhs.OpenApi.Models.csproj +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Models/LearningHub.Nhs.OpenApi.Models.csproj @@ -16,7 +16,7 @@ - + diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceMetadataViewModel.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceMetadataViewModel.cs index 3675959a6..404ef92af 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceMetadataViewModel.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceMetadataViewModel.cs @@ -1,7 +1,9 @@ namespace LearningHub.Nhs.OpenApi.Models.ViewModels { + using LearningHub.Nhs.Models.Entities.Activity; using System.Collections.Generic; + /// /// Class. /// @@ -23,13 +25,15 @@ public ResourceMetadataViewModel() /// . /// . /// . + /// . public ResourceMetadataViewModel( int resourceId, string title, string description, List references, string resourceType, - decimal rating) + decimal rating, + List? userSummaryActivityStatuses) { this.ResourceId = resourceId; this.Title = title; @@ -37,6 +41,7 @@ public ResourceMetadataViewModel( this.References = references; this.ResourceType = resourceType; this.Rating = rating; + this.UserSummaryActivityStatuses = userSummaryActivityStatuses; } /// @@ -64,9 +69,15 @@ public ResourceMetadataViewModel( /// public string ResourceType { get; set; } + /// /// Gets or sets . /// public decimal Rating { get; set; } + + /// + /// Gets or sets . + /// + public List? UserSummaryActivityStatuses { get; set; } } } diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IResourceRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IResourceRepository.cs index 50eff71f6..d3a1856e4 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IResourceRepository.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories.Interface/Repositories/IResourceRepository.cs @@ -2,6 +2,7 @@ namespace LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories { using System.Collections.Generic; using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Activity; using LearningHub.Nhs.Models.Entities.Resource; /// @@ -23,5 +24,13 @@ public interface IResourceRepository /// Resource references. public Task> GetResourceReferencesByOriginalResourceReferenceIds( IEnumerable originalResourceReferenceIds); + + /// + /// Gets resource activity for resourceReferenceIds and userIds. + /// + /// . + /// + /// ResourceActivityDTO. + Task> GetResourceActivityPerResourceMajorVersion(IEnumerable? resourceReferenceIds, IEnumerable? userIds); } } diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/LearningHubDbContext.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/LearningHubDbContext.cs index 9cbbcc3f0..c1ebf721b 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/LearningHubDbContext.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/EntityFramework/LearningHubDbContext.cs @@ -295,6 +295,11 @@ public LearningHubDbContext(LearningHubDbContextOptions options) /// public virtual DbSet FileChunkDetail { get; set; } + /// + /// Gets or sets the ResourceActivityDto. These are not entities. They are returned from the [activity].[GetResourceActivityPerResourceMajorVersion] stored proc.. + /// + public virtual DbSet ResourceActivityDTO { get; set; } + /// /// Gets or sets the RecentlyAddedResources. These are not entities. They are returned from the [resources].[GetRecentlyAddedResources] stored proc.. /// diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/ResourceRepository.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/ResourceRepository.cs index a826c627e..7ffb3c6fa 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/ResourceRepository.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Repositories/ResourceRepository.cs @@ -1,11 +1,14 @@ namespace LearningHub.Nhs.OpenApi.Repositories.Repositories { + using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Activity; using LearningHub.Nhs.Models.Entities.Resource; using LearningHub.Nhs.OpenApi.Repositories.EntityFramework; using LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories; + using Microsoft.Data.SqlClient; using Microsoft.EntityFrameworkCore; /// @@ -69,5 +72,35 @@ public async Task> GetResourceReferencesByOrigina .ThenInclude(r => r.ResourceVersionRatingSummary) .ToListAsync(); } + + /// + /// + /// + /// . + /// A representing the result of the asynchronous operation. + public async Task> GetResourceActivityPerResourceMajorVersion( + IEnumerable? resourceReferenceIds, IEnumerable? userIds) + { + var resourceIdsParam = resourceReferenceIds != null + ? string.Join(",", resourceReferenceIds) + : null; + + var userIdsParam = userIds != null + ? string.Join(",", userIds) + : null; + + var resourceIdsParameter = new SqlParameter("@p0", resourceIdsParam ?? (object)DBNull.Value); + var userIdsParameter = new SqlParameter("@p1", userIdsParam ?? (object)DBNull.Value); + + List resourceActivityDTOs = await dbContext.ResourceActivityDTO + .FromSqlRaw( + "[activity].[GetResourceActivityPerResourceMajorVersion] @p0, @p1", + resourceIdsParameter, + userIdsParameter) + .AsNoTracking() + .ToListAsync(); + + return resourceActivityDTOs; + } } } diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/LearningHub.Nhs.OpenApi.Services.Interface.csproj b/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/LearningHub.Nhs.OpenApi.Services.Interface.csproj index 8309db600..108c74355 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/LearningHub.Nhs.OpenApi.Services.Interface.csproj +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Services.Interface/LearningHub.Nhs.OpenApi.Services.Interface.csproj @@ -16,7 +16,7 @@ - + diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Services/LearningHub.Nhs.OpenApi.Services.csproj b/OpenAPI/LearningHub.Nhs.OpenApi.Services/LearningHub.Nhs.OpenApi.Services.csproj index a639e5f26..ce9034d03 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Services/LearningHub.Nhs.OpenApi.Services.csproj +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Services/LearningHub.Nhs.OpenApi.Services.csproj @@ -24,7 +24,7 @@ - + diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/ResourceService.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/ResourceService.cs index d9d4fa40b..8d4f0826f 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/ResourceService.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/ResourceService.cs @@ -2,9 +2,11 @@ namespace LearningHub.Nhs.OpenApi.Services.Services { using System; using System.Collections.Generic; + using System.Data; using System.Linq; using System.Net; using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Activity; using LearningHub.Nhs.Models.Entities.Resource; using LearningHub.Nhs.OpenApi.Models.Exceptions; using LearningHub.Nhs.OpenApi.Models.ViewModels; @@ -51,6 +53,7 @@ public ResourceService(ILearningHubService learningHubService, IResourceReposito /// the resource. public async Task GetResourceReferenceByOriginalId(int originalResourceReferenceId, int? currentUserId) { + List resourceActivities = new List() { }; var list = new List() { originalResourceReferenceId }; var resourceReferences = await this.resourceRepository.GetResourceReferencesByOriginalResourceReferenceIds(list); @@ -65,6 +68,14 @@ public async Task GetResourceRefe throw new HttpResponseException("No matching resource reference", HttpStatusCode.NotFound); } + if (currentUserId.HasValue) + { + List resourceIds = new List() { resourceReference.ResourceId }; + List userIds = new List() { currentUserId.Value }; + + resourceActivities = (await this.resourceRepository.GetResourceActivityPerResourceMajorVersion(resourceIds, userIds))?.ToList() ?? new List() { }; + } + return this.GetResourceReferenceWithResourceDetailsViewModel(resourceReference); } catch (InvalidOperationException exception) diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/SearchService.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/SearchService.cs index 4c36da24a..1db1d142f 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/SearchService.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/SearchService.cs @@ -3,8 +3,11 @@ namespace LearningHub.Nhs.OpenApi.Services.Services using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; + using LearningHub.Nhs.Models.Entities.Activity; using LearningHub.Nhs.Models.Entities.Resource; + using LearningHub.Nhs.Models.Resource; using LearningHub.Nhs.Models.Search; + using LearningHub.Nhs.Models.ViewModels.Helpers; using LearningHub.Nhs.OpenApi.Models.ServiceModels.Findwise; using LearningHub.Nhs.OpenApi.Models.ServiceModels.Resource; using LearningHub.Nhs.OpenApi.Models.ViewModels; @@ -108,11 +111,19 @@ private async Task> GetResourceMetadataViewModel return resourceMetadataViewModels; } - private ResourceMetadataViewModel MapToViewModel(Resource resource) + private ResourceMetadataViewModel MapToViewModel(Resource resource, List resourceActivities) { var hasCurrentResourceVersion = resource.CurrentResourceVersion != null; var hasRating = resource.CurrentResourceVersion?.ResourceVersionRatingSummary != null; + List majorVersionIdActivityStatusDescription = new List() { }; + + if (resourceActivities != null && resourceActivities.Count != 0) + { + majorVersionIdActivityStatusDescription = ActivityStatusHelper.GetMajorVersionIdActivityStatusDescriptionLSPerResource(resource, resourceActivities) + .ToList(); + } + if (!hasCurrentResourceVersion) { this.logger.LogInformation( @@ -131,13 +142,16 @@ private ResourceMetadataViewModel MapToViewModel(Resource resource) this.logger.LogError($"Resource has unrecognised type: {resource.ResourceTypeEnum}"); } + return new ResourceMetadataViewModel( resource.Id, resource.CurrentResourceVersion?.Title ?? ResourceHelpers.NoResourceVersionText, resource.CurrentResourceVersion?.Description ?? string.Empty, resource.ResourceReference.Select(this.GetResourceReferenceViewModel).ToList(), resourceTypeNameOrEmpty, - resource.CurrentResourceVersion?.ResourceVersionRatingSummary?.AverageRating ?? 0.0m); + resource.CurrentResourceVersion?.ResourceVersionRatingSummary?.AverageRating ?? 0.0m, + majorVersionIdActivityStatusDescription + ); } private ResourceReferenceViewModel GetResourceReferenceViewModel( From bc43f7462d82356af2ec4f65d676788b79861486 Mon Sep 17 00:00:00 2001 From: Phil-NHS Date: Fri, 5 Jul 2024 16:13:19 +0100 Subject: [PATCH 05/10] End points major version --- .../ViewModels/ResourceMetadataViewModel.cs | 7 ++++ ...ceReferenceWithResourceDetailsViewModel.cs | 24 ++++++++++++-- .../Map/Resources/ResourceVersionMap.cs | 3 ++ .../Services/ResourceService.cs | 33 +++++++++++++++---- .../Services/SearchService.cs | 11 ++++++- 5 files changed, 69 insertions(+), 9 deletions(-) diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceMetadataViewModel.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceMetadataViewModel.cs index 404ef92af..89f19f50b 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceMetadataViewModel.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceMetadataViewModel.cs @@ -32,6 +32,7 @@ public ResourceMetadataViewModel( string description, List references, string resourceType, + int? majorVersion, decimal rating, List? userSummaryActivityStatuses) { @@ -40,6 +41,7 @@ public ResourceMetadataViewModel( this.Description = description; this.References = references; this.ResourceType = resourceType; + this.MajorVersion = majorVersion; this.Rating = rating; this.UserSummaryActivityStatuses = userSummaryActivityStatuses; } @@ -69,6 +71,11 @@ public ResourceMetadataViewModel( /// public string ResourceType { get; set; } + /// + /// Gets or sets . + /// + public int? MajorVersion { get; set; } + /// /// Gets or sets . diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceReferenceWithResourceDetailsViewModel.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceReferenceWithResourceDetailsViewModel.cs index cf31bdf54..26ee89e06 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceReferenceWithResourceDetailsViewModel.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceReferenceWithResourceDetailsViewModel.cs @@ -1,3 +1,6 @@ +using LearningHub.Nhs.Models.Entities.Activity; +using System.Collections.Generic; + namespace LearningHub.Nhs.OpenApi.Models.ViewModels { /// @@ -16,24 +19,28 @@ public class ResourceReferenceWithResourceDetailsViewModel /// . /// . /// . - public ResourceReferenceWithResourceDetailsViewModel( + public ResourceReferenceWithResourceDetailsViewModel( // qqqq may need major version id adding int resourceId, int refId, string title, string description, CatalogueViewModel catalogueViewModel, string resourceType, + int? majorVersion, decimal rating, - string link) + string link, + List? userSummaryActivityStatuses) { this.ResourceId = resourceId; this.RefId = refId; this.Title = title; this.Description = description; this.Catalogue = catalogueViewModel; + this.MajorVersion = majorVersion; this.ResourceType = resourceType; this.Rating = rating; this.Link = link; + this.UserSummaryActivityStatuses = userSummaryActivityStatuses; } /// @@ -66,14 +73,27 @@ public ResourceReferenceWithResourceDetailsViewModel( /// public string ResourceType { get; } + + /// + /// Gets . + /// + public int? MajorVersion { get; } + /// /// Gets . /// + /// + public decimal Rating { get; } /// /// Gets . /// public string Link { get; } + + /// + /// Gets . + /// + public List? UserSummaryActivityStatuses { get; } } } diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Resources/ResourceVersionMap.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Resources/ResourceVersionMap.cs index a87bee808..d96c3b4fe 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Resources/ResourceVersionMap.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Repositories/Map/Resources/ResourceVersionMap.cs @@ -39,6 +39,9 @@ protected override void InternalMap(EntityTypeBuilder modelBuil .OnDelete(DeleteBehavior.ClientSetNull) .HasConstraintName("FK_ResourceVersion_Resource"); + modelBuilder.Property(e => e.ResourceAccessibilityEnum).HasColumnName("ResourceAccessibilityId") + .HasConversion(); + modelBuilder.Property(e => e.VersionStatusEnum).HasColumnName("VersionStatusId") .HasConversion(); diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/ResourceService.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/ResourceService.cs index 8d4f0826f..3212b0b37 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/ResourceService.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/ResourceService.cs @@ -8,6 +8,7 @@ namespace LearningHub.Nhs.OpenApi.Services.Services using System.Threading.Tasks; using LearningHub.Nhs.Models.Entities.Activity; using LearningHub.Nhs.Models.Entities.Resource; + using LearningHub.Nhs.Models.ViewModels.Helpers; using LearningHub.Nhs.OpenApi.Models.Exceptions; using LearningHub.Nhs.OpenApi.Models.ViewModels; using LearningHub.Nhs.OpenApi.Repositories.Interface.Repositories; @@ -76,7 +77,7 @@ public async Task GetResourceRefe resourceActivities = (await this.resourceRepository.GetResourceActivityPerResourceMajorVersion(resourceIds, userIds))?.ToList() ?? new List() { }; } - return this.GetResourceReferenceWithResourceDetailsViewModel(resourceReference); + return this.GetResourceReferenceWithResourceDetailsViewModel(resourceReference, resourceActivities); } catch (InvalidOperationException exception) { @@ -92,6 +93,9 @@ public async Task GetResourceRefe /// the resource. public async Task GetResourceReferencesByOriginalIds(List originalResourceReferenceIds, int? currentUserId) { + List resourceActivities = new List() { }; + List majorVersionIdActivityStatusDescription = new List() { }; + var resourceReferences = await this.resourceRepository.GetResourceReferencesByOriginalResourceReferenceIds(originalResourceReferenceIds); var resourceReferencesList = resourceReferences.ToList(); var matchedIds = resourceReferencesList.Select(r => r.OriginalResourceReferenceId).ToList(); @@ -107,18 +111,33 @@ public async Task GetResourceReferencesByOrigina this.logger.LogWarning($"Multiple resource references found with OriginalResourceReferenceId {duplicateIds.First()}"); } - var matchedResources = resourceReferencesList - .Select(this.GetResourceReferenceWithResourceDetailsViewModel) - .ToList(); + if (currentUserId.HasValue) + { + List resourceIds = resourceReferencesList.Select(rrl => rrl.ResourceId).ToList(); + List userIds = new List() { currentUserId.Value }; + + resourceActivities = (await this.resourceRepository.GetResourceActivityPerResourceMajorVersion(resourceIds, userIds))?.ToList() ?? new List() { }; + } + + List matchedResources = resourceReferencesList + .Select(rr => this.GetResourceReferenceWithResourceDetailsViewModel(rr, resourceActivities.Where(ra => ra.ResourceId == rr.ResourceId).ToList())) + .ToList(); return new BulkResourceReferenceViewModel(matchedResources, unmatchedIds); } - private ResourceReferenceWithResourceDetailsViewModel GetResourceReferenceWithResourceDetailsViewModel(ResourceReference resourceReference) + private ResourceReferenceWithResourceDetailsViewModel GetResourceReferenceWithResourceDetailsViewModel(ResourceReference resourceReference, List resourceActivities) { var hasCurrentResourceVersion = resourceReference.Resource.CurrentResourceVersion != null; var hasRating = resourceReference.Resource.CurrentResourceVersion?.ResourceVersionRatingSummary != null; + List majorVersionIdActivityStatusDescription = new List() { }; + + if (resourceActivities != null && resourceActivities.Count != 0) + { + majorVersionIdActivityStatusDescription = ActivityStatusHelper.GetMajorVersionIdActivityStatusDescriptionLSPerResource(resourceReference.Resource, resourceActivities).ToList(); + } + if (resourceReference.Resource == null) { throw new Exception("No matching resource"); @@ -147,8 +166,10 @@ private ResourceReferenceWithResourceDetailsViewModel GetResourceReferenceWithRe resourceReference.Resource.CurrentResourceVersion?.Description ?? string.Empty, resourceReference.GetCatalogue(), resourceTypeNameOrEmpty, + resourceReference.Resource?.CurrentResourceVersion?.MajorVersion ?? 0, resourceReference.Resource?.CurrentResourceVersion?.ResourceVersionRatingSummary?.AverageRating ?? 0, - this.learningHubService.GetResourceLaunchUrl(resourceReference.OriginalResourceReferenceId)); + this.learningHubService.GetResourceLaunchUrl(resourceReference.OriginalResourceReferenceId), + majorVersionIdActivityStatusDescription); } } } diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/SearchService.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/SearchService.cs index 1db1d142f..c5ede76fb 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/SearchService.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Services/Services/SearchService.cs @@ -82,6 +82,7 @@ public async Task Search(ResourceSearchRequest query, private async Task> GetResourceMetadataViewModels( FindwiseResultModel findwiseResultModel, int? currentUserId) { + List resourceActivities = new List() { }; var documentsFound = findwiseResultModel.SearchResults?.DocumentList.Documents?.ToList() ?? new List(); var findwiseResourceIds = documentsFound.Select(d => int.Parse(d.Id)).ToList(); @@ -93,7 +94,7 @@ private async Task> GetResourceMetadataViewModel var resourcesFound = await this.resourceRepository.GetResourcesFromIds(findwiseResourceIds); - var resourceMetadataViewModels = resourcesFound.Select(this.MapToViewModel) + List resourceMetadataViewModels = resourcesFound.Select(resource => MapToViewModel(resource, resourceActivities.Where(x => x.ResourceId == resource.Id).ToList())) .OrderBySequence(findwiseResourceIds) .ToList(); @@ -108,6 +109,13 @@ private async Task> GetResourceMetadataViewModel unmatchedResourcesIdsString); } + if (currentUserId.HasValue) + { + List resourceIds = resourcesFound.Select(x => x.Id).ToList(); + List userIds = new List() { currentUserId.Value }; + + resourceActivities = (await this.resourceRepository.GetResourceActivityPerResourceMajorVersion(resourceIds, userIds))?.ToList() ?? new List() { }; + } return resourceMetadataViewModels; } @@ -149,6 +157,7 @@ private ResourceMetadataViewModel MapToViewModel(Resource resource, List Date: Fri, 5 Jul 2024 16:32:53 +0100 Subject: [PATCH 06/10] Not nullable should be empty list --- .../ViewModels/ResourceMetadataViewModel.cs | 4 ++-- .../ResourceReferenceWithResourceDetailsViewModel.cs | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceMetadataViewModel.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceMetadataViewModel.cs index 89f19f50b..a4c1fbf4c 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceMetadataViewModel.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceMetadataViewModel.cs @@ -34,7 +34,7 @@ public ResourceMetadataViewModel( string resourceType, int? majorVersion, decimal rating, - List? userSummaryActivityStatuses) + List userSummaryActivityStatuses) { this.ResourceId = resourceId; this.Title = title; @@ -85,6 +85,6 @@ public ResourceMetadataViewModel( /// /// Gets or sets . /// - public List? UserSummaryActivityStatuses { get; set; } + public List UserSummaryActivityStatuses { get; set; } } } diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceReferenceWithResourceDetailsViewModel.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceReferenceWithResourceDetailsViewModel.cs index 26ee89e06..41d1b197b 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceReferenceWithResourceDetailsViewModel.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Models/ViewModels/ResourceReferenceWithResourceDetailsViewModel.cs @@ -17,9 +17,11 @@ public class ResourceReferenceWithResourceDetailsViewModel /// . /// . /// . + /// /// . /// . - public ResourceReferenceWithResourceDetailsViewModel( // qqqq may need major version id adding + /// + public ResourceReferenceWithResourceDetailsViewModel( int resourceId, int refId, string title, @@ -29,7 +31,7 @@ public ResourceReferenceWithResourceDetailsViewModel( // qqqq may need major ver int? majorVersion, decimal rating, string link, - List? userSummaryActivityStatuses) + List userSummaryActivityStatuses) { this.ResourceId = resourceId; this.RefId = refId; @@ -94,6 +96,6 @@ public ResourceReferenceWithResourceDetailsViewModel( // qqqq may need major ver /// /// Gets . /// - public List? UserSummaryActivityStatuses { get; } + public List UserSummaryActivityStatuses { get; } } } From 66df1d0ce9a9296e6cead368989054024a242049 Mon Sep 17 00:00:00 2001 From: Phil-NHS Date: Fri, 5 Jul 2024 17:12:15 +0100 Subject: [PATCH 07/10] Updating tests --- .../Controllers/ResourceControllerTests.cs | 58 +++++++++++++++---- .../Services/Services/ResourceServiceTests.cs | 38 ++++++------ .../Services/Services/SearchServiceTests.cs | 16 ++--- 3 files changed, 78 insertions(+), 34 deletions(-) diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Controllers/ResourceControllerTests.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Controllers/ResourceControllerTests.cs index 49f901c72..acde1d3ef 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Controllers/ResourceControllerTests.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Controllers/ResourceControllerTests.cs @@ -18,8 +18,11 @@ namespace LearningHub.Nhs.OpenApi.Tests.Controllers using Moq; using Newtonsoft.Json; using Xunit; + using Microsoft.AspNetCore.Http; + using Microsoft.AspNetCore.Mvc; + using System.Security.Claims; - public sealed class ResourceControllerTests : IDisposable + public sealed class ResourceControllerTests { private readonly Mock searchService; private readonly Mock resourceService; @@ -87,6 +90,7 @@ await Assert.ThrowsAsync( public async Task SearchEndpointUsesDefaultLimitGivenInConfig() { // Given + int? currentUserId = null; //E.g if hitting endpoint with ApiKey auth this.GivenSearchServiceSucceedsButFindsNoItems(); this.GivenDefaultLimitForFindwiseSearchIs(12); this.resourceController = new ResourceController( @@ -99,7 +103,7 @@ public async Task SearchEndpointUsesDefaultLimitGivenInConfig() // Then this.searchService.Verify( - service => service.Search(It.Is(request => request.Limit == 12))); + service => service.Search(It.Is(request => request.Limit == 12), currentUserId)); } [Fact] @@ -177,6 +181,41 @@ await Assert.ThrowsAsync( exception.StatusCode.Should().Be(HttpStatusCode.BadRequest); } + [Fact] + public void CurrentUserIdSetByAuth() + { + // Arrange + ResourceController resourceController = new ResourceController( + this.searchService.Object, + this.resourceService.Object, + this.findwiseConfigOptions.Object + ); + + + // This Id is the development accountId + int currentUserId = 57541; + + // Create claims identity with the specified user id + var claims = new List + { + new Claim(ClaimTypes.NameIdentifier, currentUserId.ToString()), + }; + var identity = new ClaimsIdentity(claims, "AuthenticationTypes.Federation"); // Set the authentication type to "Federation" + + // Create claims principal with the claims identity + var claimsPrincipal = new ClaimsPrincipal(identity); + + // Create a mock HttpContext and set it to the ControllerContext + var httpContext = new DefaultHttpContext { User = claimsPrincipal }; + var controllerContext = new ControllerContext { HttpContext = httpContext }; + resourceController.ControllerContext = controllerContext; + + // Act + + // Assert that the CurrentUserId property of the resourceController matches the currentUserId + Assert.Equal(currentUserId, resourceController.CurrentUserId); + } + [Theory] [InlineData(1)] [InlineData(20)] @@ -184,6 +223,7 @@ await Assert.ThrowsAsync( public async Task SearchEndpointUsesPassedInLimitIfGiven(int limit) { // Given + int? currentUserId = null; //E.g if hitting endpoint with ApiKey auth this.GivenSearchServiceSucceedsButFindsNoItems(); this.GivenDefaultLimitForFindwiseSearchIs(20); this.resourceController = new ResourceController( @@ -196,12 +236,7 @@ public async Task SearchEndpointUsesPassedInLimitIfGiven(int limit) // Then this.searchService.Verify( - service => service.Search(It.Is(request => request.Limit == limit))); - } - - public void Dispose() - { - this.resourceController?.Dispose(); + service => service.Search(It.Is(request => request.Limit == limit), currentUserId)); } private void GivenDefaultLimitForFindwiseSearchIs(int limit) @@ -212,14 +247,17 @@ private void GivenDefaultLimitForFindwiseSearchIs(int limit) private void GivenSearchServiceFailsWithStatus(FindwiseRequestStatus status) { - this.searchService.Setup(ss => ss.Search(It.IsAny())).ReturnsAsync( + int? currentUserId = null; //E.g if hitting endpoint with ApiKey auth + this.searchService.Setup(ss => ss.Search(It.IsAny(), currentUserId)).ReturnsAsync( new ResourceSearchResultModel(new List(), status, 0)); } private void GivenSearchServiceSucceedsButFindsNoItems() { - this.searchService.Setup(ss => ss.Search(It.IsAny())).ReturnsAsync( + int? currentUserId = null; //E.g if hitting endpoint with ApiKey auth + this.searchService.Setup(ss => ss.Search(It.IsAny(), currentUserId)).ReturnsAsync( new ResourceSearchResultModel(new List(), FindwiseRequestStatus.Success, 0)); } + } } diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Services/Services/ResourceServiceTests.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Services/Services/ResourceServiceTests.cs index d6ae8f3db..2751d2254 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Services/Services/ResourceServiceTests.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Services/Services/ResourceServiceTests.cs @@ -22,9 +22,13 @@ public class ResourceServiceTests private readonly Mock learningHubService; private readonly ResourceService resourceService; private readonly Mock resourceRepository; + private readonly int currentUserId; public ResourceServiceTests() { + //This Id is the development accountId + this.currentUserId = 57541; + this.learningHubService = new Mock(); this.resourceRepository = new Mock(); this.resourceService = new ResourceService(this.learningHubService.Object, this.resourceRepository.Object, new NullLogger()); @@ -63,7 +67,7 @@ public async Task SingleResourceEndpointReturnsTheCorrectInformationIfThereIsAMa .ReturnsAsync(this.ResourceReferenceList.GetRange(0, 1)); // When - var x = await this.resourceService.GetResourceReferenceByOriginalId(1); + var x = await this.resourceService.GetResourceReferenceByOriginalId(1, null); // Then x.Rating.Should().Be(3); @@ -80,7 +84,7 @@ public async Task SingleResourceReturnsA404IfTheresNoResourceReferenceWithAMatch .ReturnsAsync(new List()); // When / Then - var exception = await Assert.ThrowsAsync(async () => await this.resourceService.GetResourceReferenceByOriginalId(999)); + var exception = await Assert.ThrowsAsync(async () => await this.resourceService.GetResourceReferenceByOriginalId(999, null)); exception.StatusCode.Should().Be(HttpStatusCode.NotFound); exception.ResponseBody.Should().Be("No matching resource reference"); } @@ -93,7 +97,7 @@ public async Task SingleResourceEndpointReturnsAResourceMetadataViewModelObjectW .ReturnsAsync(this.ResourceReferenceList.GetRange(1, 1)); // When - var x = await this.resourceService.GetResourceReferenceByOriginalId(2); + var x = await this.resourceService.GetResourceReferenceByOriginalId(2, null); // Then x.Title.Should().Be("No current resource version"); @@ -108,7 +112,7 @@ public async Task SingleResourceEndpointReturnsAMessageSayingNoCatalogueIfThereI .ReturnsAsync(this.ResourceReferenceList.GetRange(2, 1)); // When - var x = await this.resourceService.GetResourceReferenceByOriginalId(3); + var x = await this.resourceService.GetResourceReferenceByOriginalId(3, null); // Then x.Catalogue.Name.Should().Be("No catalogue for resource reference"); @@ -122,7 +126,7 @@ public async Task SingleResourceEndpointReturnsAMessageSayingNoCatalogueIfThereI .ReturnsAsync(this.ResourceReferenceList.GetRange(3, 1)); // When - var x = await this.resourceService.GetResourceReferenceByOriginalId(4); + var x = await this.resourceService.GetResourceReferenceByOriginalId(4, null); // Then x.Catalogue.Name.Should().Be("No catalogue for resource reference"); @@ -136,7 +140,7 @@ public async Task SingleResourceEndpointReturnsAMessageSayingNoCatalogueIfThereI .ReturnsAsync(this.ResourceReferenceList.GetRange(5, 1)); // When - var x = await this.resourceService.GetResourceReferenceByOriginalId(6); + var x = await this.resourceService.GetResourceReferenceByOriginalId(6, null); // Then x.Catalogue.Name.Should().Be("No catalogue for resource reference"); @@ -150,7 +154,7 @@ public async Task SingleResourceEndpointReturnsAZeroForRatingIfTheresNoRatingSum .ReturnsAsync(this.ResourceReferenceList.GetRange(7, 1)); // When - var x = await this.resourceService.GetResourceReferenceByOriginalId(8); + var x = await this.resourceService.GetResourceReferenceByOriginalId(8, null); // Then x.Catalogue.Name.Should().Be("catalogue3"); @@ -165,7 +169,7 @@ public async Task SingleResourceEndpointThrowsAnErrorAndReturnsABlankStringIfThe .ReturnsAsync(this.ResourceReferenceList.GetRange(8, 1)); // When - var x = await this.resourceService.GetResourceReferenceByOriginalId(9); + var x = await this.resourceService.GetResourceReferenceByOriginalId(9, null); // Then x.ResourceType.Should().Be(string.Empty); @@ -179,7 +183,7 @@ public async Task SingleResourceEndpointThrowsAnErrorIfThereIsMoreThanOneResourc .ReturnsAsync(this.ResourceReferenceList.GetRange(9, 2)); // When / Then - await Assert.ThrowsAsync(async () => await this.resourceService.GetResourceReferenceByOriginalId(10)); + await Assert.ThrowsAsync(async () => await this.resourceService.GetResourceReferenceByOriginalId(10, null)); } /*[Fact] @@ -198,7 +202,7 @@ public async Task BulkEndpointReturnsAllMatchingResources() .ReturnsAsync(this.ResourceReferenceList.GetRange(0, 2)); // When - var x = await this.resourceService.GetResourceReferencesByOriginalIds(idsToLookUp); + var x = await this.resourceService.GetResourceReferencesByOriginalIds(idsToLookUp, null); // Then x.ResourceReferences.Count.Should().Be(2); @@ -220,7 +224,7 @@ public async Task BulkEndpointReturnsA404IfThereAreNoMatchingResources() .ReturnsAsync(new List()); // When - var x = await this.resourceService.GetResourceReferencesByOriginalIds(idsToLookUp); + var x = await this.resourceService.GetResourceReferencesByOriginalIds(idsToLookUp, null); // Then x.UnmatchedResourceReferenceIds.Count.Should().Be(2); @@ -237,7 +241,7 @@ public async Task BulkEndpointReturnsResourcesWithIncompleteInformation() .ReturnsAsync(this.ResourceReferenceList.GetRange(0, 4)); // When - var x = await this.resourceService.GetResourceReferencesByOriginalIds(idsToLookUp); + var x = await this.resourceService.GetResourceReferencesByOriginalIds(idsToLookUp, null); // Then x.ResourceReferences.Count.Should().Be(4); @@ -257,7 +261,7 @@ public async Task BulkEndpointReturnsUnmatchedResourcesWithMatchedResources() .ReturnsAsync(this.ResourceReferenceList.GetRange(0, 1)); // When - var x = await this.resourceService.GetResourceReferencesByOriginalIds(idsToLookUp); + var x = await this.resourceService.GetResourceReferencesByOriginalIds(idsToLookUp, null); // Then x.ResourceReferences.Count.Should().Be(1); @@ -277,7 +281,7 @@ public async Task ResourceServiceReturnsTheOriginalResourceReferenceIdAsTheRefId .ReturnsAsync(this.ResourceReferenceList.GetRange(5, 2)); // When - var x = await this.resourceService.GetResourceReferencesByOriginalIds(list); + var x = await this.resourceService.GetResourceReferencesByOriginalIds(list, null); // Then x.ResourceReferences[0].RefId.Should().Be(6); @@ -293,7 +297,7 @@ public async Task ResourceServiceReturnsTheOriginalResourceReferenceIdAsTheRefId .ReturnsAsync(this.ResourceReferenceList.GetRange(5, 1)); // When - var x = await this.resourceService.GetResourceReferenceByOriginalId(6); + var x = await this.resourceService.GetResourceReferenceByOriginalId(6, null); // Then x.RefId.Should().Be(6); @@ -308,7 +312,7 @@ public async Task ResourceServiceReturnsThatARestrictedCatalogueIsRestricted() .ReturnsAsync(this.ResourceReferenceList.GetRange(8, 1)); // When - var x = await this.resourceService.GetResourceReferenceByOriginalId(9); + var x = await this.resourceService.GetResourceReferenceByOriginalId(9, null); // Then x.Catalogue.IsRestricted.Should().BeTrue(); @@ -323,7 +327,7 @@ public async Task ResourceServiceReturnsThatAnUnrestrictedCatalogueIsUnrestricte .ReturnsAsync(this.ResourceReferenceList.GetRange(7, 1)); // When - var x = await this.resourceService.GetResourceReferenceByOriginalId(8); + var x = await this.resourceService.GetResourceReferenceByOriginalId(8, null); // Then x.Catalogue.IsRestricted.Should().BeFalse(); diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Services/Services/SearchServiceTests.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Services/Services/SearchServiceTests.cs index d393ce59d..ee84c1c51 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Services/Services/SearchServiceTests.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Services/Services/SearchServiceTests.cs @@ -6,6 +6,7 @@ namespace LearningHub.Nhs.OpenApi.Tests.Services.Services using FizzWare.NBuilder; using FluentAssertions; using FluentAssertions.Execution; + using LearningHub.Nhs.Models.Entities.Activity; using LearningHub.Nhs.Models.Entities.Resource; using LearningHub.Nhs.Models.Enums; using LearningHub.Nhs.Models.Search; @@ -73,7 +74,7 @@ public async Task SearchPassesQueryOnToFindwise() .ReturnsAsync(FindwiseResultModel.Failure(FindwiseRequestStatus.Timeout)); // When - await this.searchService.Search(searchRequest); + await this.searchService.Search(searchRequest, null); // Then this.findwiseClient.Verify(fc => fc.Search(searchRequest)); @@ -90,7 +91,7 @@ public async Task SearchReturnsTotalHitsAndSearchResult() this.GivenFindwiseReturnsSuccessfulResponse(74, Enumerable.Range(1, 34)); // When - var searchResult = await this.searchService.Search(searchRequest); + var searchResult = await this.searchService.Search(searchRequest, null); // Then searchResult.Resources.Count.Should().Be(34); @@ -137,7 +138,7 @@ public async Task SearchResultsReturnExpectedValues() this.GivenFindwiseReturnsSuccessfulResponse(2, new[] { 1, 2, 3 }); // When - var searchResult = await this.searchService.Search(searchRequest); + var searchResult = await this.searchService.Search(searchRequest, null); // Then searchResult.Resources.Count.Should().Be(2); @@ -180,7 +181,7 @@ public async Task SearchReturnsResourcesInOrderMatchingFindwise() .ReturnsAsync(resources); // When - var searchResultModel = await this.searchService.Search(new ResourceSearchRequest("text", 0, 10)); + var searchResultModel = await this.searchService.Search(new ResourceSearchRequest("text", 0, 10), null); // Then searchResultModel.Resources.Select(r => r.ResourceId).Should().ContainInOrder(new[] { 1, 3, 2 }); @@ -194,7 +195,6 @@ public async Task SearchReplacesNullPropertiesOfResourceWithDefaultValues() { Builder.CreateNew() .With(r => r.Id = 1) - .With(r => r.CurrentResourceVersion = null) .With( r => r.ResourceReference = new[] { @@ -212,7 +212,7 @@ public async Task SearchReplacesNullPropertiesOfResourceWithDefaultValues() this.GivenFindwiseReturnsSuccessfulResponse(1, new[] { 1 }); // When - var searchResult = await this.searchService.Search(new ResourceSearchRequest("text", 0, 10)); + var searchResult = await this.searchService.Search(new ResourceSearchRequest("text", 0, 10), null); // Then using var scope = new AssertionScope(); @@ -233,7 +233,9 @@ public async Task SearchReplacesNullPropertiesOfResourceWithDefaultValues() string.Empty, expectedResourceReferences, "Article", - 0)); + 0, + 0, + new List(){ })); } private void GivenFindwiseReturnsSuccessfulResponse(int totalHits, IEnumerable resourceIds) From ebce59083ba576da06a0f6065477823e15e6c8d5 Mon Sep 17 00:00:00 2001 From: Phil-NHS Date: Mon, 8 Jul 2024 09:50:42 +0100 Subject: [PATCH 08/10] Add stored procedure GetResourceActivityPErResource + diff --git a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetResourceActivityPerResourceMajorVersion.sql b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetResourceActivityPerResourceMajorVersion.sql new file mode 100644 index 000000000..0c8fb4358 --- /dev/null +++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetResourceActivityPerResourceMajorVersion.sql @@ -0,0 +1,147 @@ + +------------------------------------------------------------------------------- +-- Author Phil T +-- Created 04-07-24 +-- Purpose Return resource activity for each major version for user + + +-- Description +/* + This procedure returns a single entry per resource Id, selecting the most important one for that major version. + This is so users can still have a resourceActivity history following a majorVersion change + + UserIds is nullable so that general resource activity can be searched for + ResourceIds is nullable so that all a users history can be searched for + + When determining the resourceActivity statusDescription in the front end resourceTypeId is also required for changing completed statuses to resourceType specific ones + + Currently if multiple rows meet the case criteria we retrieve the one with the highest Id which is also expected to be the ActivityEnd part of the activityStatus pair. + +*/ +-- Future Considerations +/* + Because the activityResource should come in pairs one with ActivityStart populated and one with ActivityEnd populated + it could be desireable to join via LaunchResourceActivityId and coalesce the data in future. + Or/And coalesce where the case returns multiple rows. + +*/ +-- Notes + -- resourceId is used not originalResourceId + + +------------------------------------------------------------------------------- + + + + -- Split the comma-separated list into a table of integers + DECLARE @ResourceIdTable TABLE (ResourceId INT); + + IF @ResourceIds IS NOT NULL AND @ResourceIds <> '' + BEGIN + INSERT INTO @ResourceIdTable (ResourceId) + SELECT CAST(value AS INT) + FROM STRING_SPLIT(@ResourceIds, ','); + END; + + -- Split the comma-separated list of UserIds into a table + DECLARE @UserIdTable TABLE (UserId INT); + + IF @UserIds IS NOT NULL AND @UserIds <> '' + BEGIN + INSERT INTO @UserIdTable (UserId) + SELECT CAST(value AS INT) + FROM STRING_SPLIT(@UserIds, ','); + END; + + WITH FilteredResourceActivities AS ( + SELECT + ars.[Id], + ars.[UserId], + ars.[LaunchResourceActivityId], + ars.[ResourceId], + ars.[ResourceVersionId], + ars.[MajorVersion], + ars.[MinorVersion], + ars.[NodePathId], + ars.[ActivityStatusId], + ars.[ActivityStart], + ars.[ActivityEnd], + ars.[DurationSeconds], + ars.[Score], + ars.[Deleted], + ars.[CreateUserID], + ars.[CreateDate], + ars.[AmendUserID], + ars.[AmendDate] + FROM + [activity].[resourceactivity] ars + WHERE + (@UserIds IS NULL OR ars.userId IN (SELECT UserId FROM @UserIdTable) OR NOT EXISTS (SELECT 1 FROM @UserIdTable)) + AND (@ResourceIds IS NULL OR @ResourceIds = '' OR ars.resourceId IN (SELECT ResourceId FROM @ResourceIdTable) OR NOT EXISTS (SELECT 1 FROM @ResourceIdTable)) + AND ars.Deleted = 0 + AND ars.ActivityStatusId NOT IN (1, 6, 2) -- These Ids are not in use - Launched, Downloaded, In Progress (stored as completed and incomplete then renamed in the application) + ), + RankedActivities AS ( + SELECT + ra.[Id], + ra.[UserId], + ra.[LaunchResourceActivityId], + ra.[ResourceId], + ra.[ResourceVersionId], + ra.[MajorVersion], + ra.[MinorVersion], + ra.[NodePathId], + ra.[ActivityStatusId], + ra.[ActivityStart], + ra.[ActivityEnd], + ra.[DurationSeconds], + ra.[Score], + ra.[Deleted], + ra.[CreateUserID], + ra.[CreateDate], + ra.[AmendUserID], + ra.[AmendDate], + ROW_NUMBER() OVER ( + PARTITION BY resourceId, userId, MajorVersion + ORDER BY + CASE + WHEN ActivityStatusId = 5 THEN 1 -- Passed + WHEN ActivityStatusId = 3 THEN 2 -- Completed + WHEN ActivityStatusId = 4 THEN 3 -- Failed + WHEN ActivityStatusId = 7 THEN 4 -- Incomplete + ELSE 5 -- shouldn't be any + END, + Id DESC -- we have two entries per interacting with a resource the start and the end, we are just returning the last entry made + -- there is the option of instead coalescing LaunchResourceActivityId, ActivityStart,ActivityEnd potentially via joining LaunchResourceActivityId and UserId + ) AS RowNum + FROM + FilteredResourceActivities ra + ) + SELECT + ra.[Id], + ra.[UserId], + ra.[LaunchResourceActivityId], + ra.[ResourceId], + ra.[ResourceVersionId], + ra.[MajorVersion], + ra.[MinorVersion], + ra.[NodePathId], + ra.[ActivityStatusId], + ra.[ActivityStart], + ra.[ActivityEnd], + ra.[DurationSeconds], + ra.[Score], + ra.[Deleted], + ra.[CreateUserID], + ra.[CreateDate], + ra.[AmendUserID], + ra.[AmendDate] + FROM + RankedActivities ra + WHERE + RowNum = 1 + order by MajorVersion desc; +END; +GO + + From 8f1765ba7c3197241f3043ae1b270fa4b33a3742 Mon Sep 17 00:00:00 2001 From: Phil-NHS Date: Mon, 8 Jul 2024 09:59:16 +0100 Subject: [PATCH 09/10] fix stored proc --- .../GetResourceActivityPerResourceMajorVersion.sql | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetResourceActivityPerResourceMajorVersion.sql b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetResourceActivityPerResourceMajorVersion.sql index 0c8fb4358..063110343 100644 --- a/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetResourceActivityPerResourceMajorVersion.sql +++ b/WebAPI/LearningHub.Nhs.Database/Stored Procedures/Activity/GetResourceActivityPerResourceMajorVersion.sql @@ -31,7 +31,12 @@ ------------------------------------------------------------------------------- - +-- Create the new stored procedure +CREATE PROCEDURE [activity].[GetResourceActivityPerResourceMajorVersion] + @ResourceIds VARCHAR(MAX) = NULL, + @UserIds VARCHAR(MAX) = NULL +AS +BEGIN -- Split the comma-separated list into a table of integers DECLARE @ResourceIdTable TABLE (ResourceId INT); From d3df951d3da5a28e90af3143294d6d488536fa53 Mon Sep 17 00:00:00 2001 From: Phil-NHS Date: Mon, 8 Jul 2024 13:03:48 +0100 Subject: [PATCH 10/10] Added tests --- .../Services/Services/ResourceServiceTests.cs | 59 ++++++++++++++++++- .../LearningHub.NHS.OpenAPI.csproj.user | 9 +++ 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 OpenAPI/LearningHub.Nhs.OpenApi/LearningHub.NHS.OpenAPI.csproj.user diff --git a/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Services/Services/ResourceServiceTests.cs b/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Services/Services/ResourceServiceTests.cs index 2751d2254..1f791a353 100644 --- a/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Services/Services/ResourceServiceTests.cs +++ b/OpenAPI/LearningHub.Nhs.OpenApi.Tests/Services/Services/ResourceServiceTests.cs @@ -2,10 +2,12 @@ namespace LearningHub.Nhs.OpenApi.Tests.Services.Services { using System; using System.Collections.Generic; + using System.Linq; using System.Net; using System.Threading.Tasks; using FizzWare.NBuilder; using FluentAssertions; + using LearningHub.Nhs.Models.Entities.Activity; using LearningHub.Nhs.Models.Entities.Resource; using LearningHub.Nhs.Models.Enums; using LearningHub.Nhs.OpenApi.Models.Exceptions; @@ -33,7 +35,14 @@ public ResourceServiceTests() this.resourceRepository = new Mock(); this.resourceService = new ResourceService(this.learningHubService.Object, this.resourceRepository.Object, new NullLogger()); } - + private List ResourceActivityDTOList => new List() + { + new ResourceActivityDTO{ ResourceId = 1, ActivityStatusId = 5, MajorVersion = 5 }, + new ResourceActivityDTO{ ResourceId = 1, ActivityStatusId = 7, MajorVersion = 4 }, + new ResourceActivityDTO{ ResourceId = 1, ActivityStatusId = 3, MajorVersion = 3 }, + new ResourceActivityDTO{ ResourceId = 1, ActivityStatusId = 7, MajorVersion = 2 }, + new ResourceActivityDTO{ ResourceId = 1, ActivityStatusId = 3, MajorVersion = 1 }, + }; private List ResourceList => new List() { ResourceTestHelper.CreateResourceWithDetails(id: 1, title: "title1", description: "description1", rating: 3m, resourceType: ResourceTypeEnum.Article), @@ -332,5 +341,53 @@ public async Task ResourceServiceReturnsThatAnUnrestrictedCatalogueIsUnrestricte // Then x.Catalogue.IsRestricted.Should().BeFalse(); } + + [Fact] + public async Task SingleResourceEndpointReturnsActivitySummaryWhenCurrentUserIdProvided() + { + // Given + this.resourceRepository.Setup(rr => rr.GetResourceReferencesByOriginalResourceReferenceIds(new List() { 1 })) + .ReturnsAsync(this.ResourceReferenceList.GetRange(0, 1)); + + this.resourceRepository.Setup(rr => rr.GetResourceActivityPerResourceMajorVersion(new List() { 1 }, new List() { currentUserId })) + .ReturnsAsync(this.ResourceActivityDTOList.ToList()); + + // When + var x = await this.resourceService.GetResourceReferenceByOriginalId(1, currentUserId); + + // Then + x.UserSummaryActivityStatuses.Should().NotBeNull(); + x.UserSummaryActivityStatuses[0].MajorVersionId.Should().Be(5); + x.UserSummaryActivityStatuses[1].MajorVersionId.Should().Be(4); + x.UserSummaryActivityStatuses[2].MajorVersionId.Should().Be(3); + x.UserSummaryActivityStatuses[3].MajorVersionId.Should().Be(2); + x.UserSummaryActivityStatuses[4].MajorVersionId.Should().Be(1); + + x.UserSummaryActivityStatuses[0].ActivityStatusDescription.Should().Be("Passed"); + x.UserSummaryActivityStatuses[1].ActivityStatusDescription.Should().Be("In progress"); + x.UserSummaryActivityStatuses[2].ActivityStatusDescription.Should().Be("Viewed"); + x.UserSummaryActivityStatuses[3].ActivityStatusDescription.Should().Be("In progress"); + x.UserSummaryActivityStatuses[4].ActivityStatusDescription.Should().Be("Viewed"); + + } + + [Fact] + public async Task SingleResourceEndpointReturnsEmptyActivitySummaryWhenNoCurrentUserIdProvided() + { + // Given + this.resourceRepository.Setup(rr => rr.GetResourceReferencesByOriginalResourceReferenceIds(new List() { 1 })) + .ReturnsAsync(this.ResourceReferenceList.GetRange(0, 1)); + + // This should not be hit + this.resourceRepository.Setup(rr => rr.GetResourceActivityPerResourceMajorVersion(new List() { 1 }, new List() { currentUserId })) + .ReturnsAsync(this.ResourceActivityDTOList.ToList()); + + // When + var x = await this.resourceService.GetResourceReferenceByOriginalId(1, null); + + // Then + x.UserSummaryActivityStatuses.Should().BeEmpty(); + + } } } diff --git a/OpenAPI/LearningHub.Nhs.OpenApi/LearningHub.NHS.OpenAPI.csproj.user b/OpenAPI/LearningHub.Nhs.OpenApi/LearningHub.NHS.OpenAPI.csproj.user new file mode 100644 index 000000000..b17387f00 --- /dev/null +++ b/OpenAPI/LearningHub.Nhs.OpenApi/LearningHub.NHS.OpenAPI.csproj.user @@ -0,0 +1,9 @@ + + + + ProjectDebugger + + + IIS Local + + \ No newline at end of file