From 8d3565275758280476c20f8a9d1e105da8dcd032 Mon Sep 17 00:00:00 2001 From: Dominic NEED Date: Mon, 15 Apr 2024 15:02:50 +0100 Subject: [PATCH 1/7] Respository search on transfers projects --- .../Repositories/TransferProjectRepository.cs | 88 +++++++++++++++++++ .../Controllers/TransferProjectController.cs | 39 ++++---- 2 files changed, 107 insertions(+), 20 deletions(-) diff --git a/Dfe.Academies.Academisation.Data/Repositories/TransferProjectRepository.cs b/Dfe.Academies.Academisation.Data/Repositories/TransferProjectRepository.cs index ac666510..5d164287 100644 --- a/Dfe.Academies.Academisation.Data/Repositories/TransferProjectRepository.cs +++ b/Dfe.Academies.Academisation.Data/Repositories/TransferProjectRepository.cs @@ -19,7 +19,95 @@ public TransferProjectRepository(AcademisationContext context) : base(context) { return await DefaultIncludes().SingleOrDefaultAsync(x => x.Urn == urn); } + public async Task<(IEnumerable, int totalcount)> SearchProjects(IEnumerable? states, string? title, IEnumerable? deliveryOfficers, int page, int count, int? urn, IEnumerable? regions = default, IEnumerable? applicationReferences = default) + { + IQueryable queryable = this.dbSet; + + // Region & Local Authority isn't on Transfers right now + //queryable = FilterByRegion(regions, queryable); + //queryable = FilterByLocalAuthority(localAuthorities, queryable); + queryable = FilterByStatus(states, queryable); + queryable = FilterByUrn(urn, queryable); + queryable = FilterByKeyword(title, queryable); + queryable = FilterByDeliveryOfficer(deliveryOfficers, queryable); + + var totalProjects = queryable.Count(); + var projects = await queryable + .OrderByDescending(acp => acp.CreatedOn) + .Skip((page - 1) * count) + .Take(count).ToListAsync(); + + return (projects, totalProjects); + } + private static IQueryable FilterByDeliveryOfficer(IEnumerable? deliveryOfficers, IQueryable queryable) + { + if (deliveryOfficers != null && deliveryOfficers.Any()) + { + var lowerCaseDeliveryOfficers = deliveryOfficers.Select(officer => officer.ToLower()); + + if (lowerCaseDeliveryOfficers.Contains("not assigned")) + { + // Query by unassigned or assigned delivery officer + queryable = queryable.Where(p => + (!string.IsNullOrEmpty(p.AssignedUserFullName) && lowerCaseDeliveryOfficers.Contains(p.AssignedUserFullName.ToLower())) + || string.IsNullOrEmpty(p.AssignedUserFullName)); + } + else + { + // Query by assigned delivery officer only + queryable = queryable.Where(p => + !string.IsNullOrEmpty(p.AssignedUserFullName) && lowerCaseDeliveryOfficers.Contains(p.AssignedUserFullName.ToLower())); + } + } + + return queryable; + } + private static IQueryable FilterByKeyword(string? title, IQueryable queryable) + { + if (!string.IsNullOrWhiteSpace(title)) + { + + queryable = FilterIncomingTrust(title, queryable); + queryable = FilterOutgoingTrust(title, queryable); + queryable = FilterURN(title, queryable); + + } + return queryable; + } + + private static IQueryable FilterIncomingTrust(string title, IQueryable queryable) + { + return queryable.Where(p => + p.TransferringAcademies.Any(x => + x.IncomingTrustName != null && + x.IncomingTrustName.Contains(title, StringComparison.CurrentCultureIgnoreCase) + ) + ); + } + private static IQueryable FilterOutgoingTrust(string title, IQueryable queryable) + { + return queryable.Where(p => p.OutgoingTrustName != null && p.OutgoingTrustName.Contains(title, StringComparison.CurrentCultureIgnoreCase)); + } + private static IQueryable FilterURN(string title, IQueryable queryable) + { + return queryable.Where(p => p.Urn.ToString().Contains(title, StringComparison.CurrentCultureIgnoreCase)); + } + private static IQueryable FilterByUrn(int? urn, IQueryable queryable) + { + if (urn.HasValue) queryable = queryable.Where(p => p.Urn == urn); + + return queryable; + } + private static IQueryable FilterByStatus(IEnumerable? states, IQueryable queryable) + { + if (states != null && states!.Any()) + { + queryable = queryable.Where(p => states.Contains(p.Status!.ToLower())); + } + + return queryable; + } public async Task> GetAllTransferProjects() { try diff --git a/Dfe.Academies.Academisation.WebApi/Controllers/TransferProjectController.cs b/Dfe.Academies.Academisation.WebApi/Controllers/TransferProjectController.cs index 1472a24c..31c67e3e 100644 --- a/Dfe.Academies.Academisation.WebApi/Controllers/TransferProjectController.cs +++ b/Dfe.Academies.Academisation.WebApi/Controllers/TransferProjectController.cs @@ -1,13 +1,11 @@ using Dfe.Academies.Academisation.Core; +using Dfe.Academies.Academisation.IService.Query; using Dfe.Academies.Academisation.IService.ServiceModels.TransferProject; +using Dfe.Academies.Academisation.Service.Commands.Application; using Dfe.Academies.Academisation.Service.Commands.TransferProject; using MediatR; using Microsoft.AspNetCore.Mvc; using TramsDataApi.RequestModels.AcademyTransferProject; -using Dfe.Academies.Academisation.IService.Query; -using Dfe.Academies.Academisation.Service.Commands.Application; -using System; -using Dfe.Academies.Academisation.IService.ServiceModels.Application; namespace Dfe.Academies.Academisation.WebApi.Controllers { @@ -20,7 +18,7 @@ public class TransferProjectController : ControllerBase private readonly IMediator _mediator; private readonly ILogger _logger; private readonly ITransferProjectQueryService _transferProjectQueryService; - + public TransferProjectController(IMediator mediator, ITransferProjectQueryService transferProjectQueryService, ILogger logger) @@ -257,24 +255,25 @@ public async Task> GetByUrn(int urn [HttpGet("GetTransferProjects", Name = "GetTransferProjects")] public async Task> GetTransferProjects( - [FromQuery] string? title, - [FromQuery] int page = 1, - [FromQuery] int count = 50, - [FromQuery] int? urn = null) + [FromQuery] string? title, + [FromQuery] int page = 1, + [FromQuery] int count = 50, + [FromQuery] int? urn = null) { - - _logger.LogInformation($"Attempting to retrieve {count} Academy Transfer Projects filtered by: urn: {urn} title: {title}", count, urn, title); - PagedResultResponse result = - await _transferProjectQueryService.GetTransferProjects(page, count, urn,title); + _logger.LogInformation($"Attempting to retrieve {count} Academy Transfer Projects filtered by: urn: {urn} title: {title}", count, urn, title); - if (result.Results.Any()) - { - IEnumerable projectIds = result.Results.Select(p => p.ProjectUrn); - _logger.LogInformation($"Returning {count} Academy Transfer Projects with Id(s): {projectIds}", result.Results.Count(), string.Join(',', projectIds)); - } + PagedResultResponse result = + await _transferProjectQueryService.GetTransferProjects(page, count, urn, title); + + if (result.Results.Any()) + { + IEnumerable projectIds = result.Results.Select(p => p.ProjectUrn); + _logger.LogInformation($"Returning {count} Academy Transfer Projects with Id(s): {projectIds}", result.Results.Count(), string.Join(',', projectIds)); + } + + return Ok(result); + } - return Ok(result); - } } } From 85b552ba6215749987aa5039e3770c2275c5e9f2 Mon Sep 17 00:00:00 2001 From: Dominic NEED Date: Mon, 15 Apr 2024 15:14:12 +0100 Subject: [PATCH 2/7] Service layer for searching transfer projects --- .../Repositories/TransferProjectRepository.cs | 5 ++--- .../ITransferProjectRepository.cs | 1 + .../Queries/TransferProjectQueryService.cs | 15 ++++++++++++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Dfe.Academies.Academisation.Data/Repositories/TransferProjectRepository.cs b/Dfe.Academies.Academisation.Data/Repositories/TransferProjectRepository.cs index 5d164287..1bd682e2 100644 --- a/Dfe.Academies.Academisation.Data/Repositories/TransferProjectRepository.cs +++ b/Dfe.Academies.Academisation.Data/Repositories/TransferProjectRepository.cs @@ -19,15 +19,14 @@ public TransferProjectRepository(AcademisationContext context) : base(context) { return await DefaultIncludes().SingleOrDefaultAsync(x => x.Urn == urn); } - public async Task<(IEnumerable, int totalcount)> SearchProjects(IEnumerable? states, string? title, IEnumerable? deliveryOfficers, int page, int count, int? urn, IEnumerable? regions = default, IEnumerable? applicationReferences = default) + public async Task<(IEnumerable, int totalcount)> SearchProjects(IEnumerable? states, string? title, IEnumerable? deliveryOfficers, int page, int count) { IQueryable queryable = this.dbSet; // Region & Local Authority isn't on Transfers right now //queryable = FilterByRegion(regions, queryable); - //queryable = FilterByLocalAuthority(localAuthorities, queryable); + //queryable = FilterByLocalAuthority(localAuthorities, queryable); queryable = FilterByStatus(states, queryable); - queryable = FilterByUrn(urn, queryable); queryable = FilterByKeyword(title, queryable); queryable = FilterByDeliveryOfficer(deliveryOfficers, queryable); diff --git a/Dfe.Academies.Academisation.Domain/TransferProjectAggregate/ITransferProjectRepository.cs b/Dfe.Academies.Academisation.Domain/TransferProjectAggregate/ITransferProjectRepository.cs index d7a1e16f..770785c5 100644 --- a/Dfe.Academies.Academisation.Domain/TransferProjectAggregate/ITransferProjectRepository.cs +++ b/Dfe.Academies.Academisation.Domain/TransferProjectAggregate/ITransferProjectRepository.cs @@ -7,5 +7,6 @@ public interface ITransferProjectRepository : IRepository, IGen { public Task GetByUrn(int urn); public Task> GetAllTransferProjects(); + public async Task<(IEnumerable, int totalcount)> SearchProjects(IEnumerable? states, string? title, IEnumerable? deliveryOfficers, int page, int count); } } diff --git a/Dfe.Academies.Academisation.Service/Queries/TransferProjectQueryService.cs b/Dfe.Academies.Academisation.Service/Queries/TransferProjectQueryService.cs index 85e9b0c3..56996760 100644 --- a/Dfe.Academies.Academisation.Service/Queries/TransferProjectQueryService.cs +++ b/Dfe.Academies.Academisation.Service/Queries/TransferProjectQueryService.cs @@ -2,8 +2,10 @@ using Dfe.Academies.Academisation.IData.ConversionAdvisoryBoardDecisionAggregate; using Dfe.Academies.Academisation.IDomain.TransferProjectAggregate; using Dfe.Academies.Academisation.IService.Query; +using Dfe.Academies.Academisation.IService.ServiceModels.Legacy.ProjectAggregate; using Dfe.Academies.Academisation.IService.ServiceModels.TransferProject; using Dfe.Academies.Academisation.Service.Extensions; +using Dfe.Academies.Academisation.Service.Factories; using Dfe.Academies.Academisation.Service.Mappers.TransferProject; namespace Dfe.Academies.Academisation.Service.Queries @@ -62,7 +64,18 @@ public async Task> Ge return await Task.FromResult(new PagedResultResponse(projects, recordTotal)); } - + public async Task?> GetProjects(IEnumerable? states, string? title, IEnumerable? deliveryOfficers, int page, int count, IEnumerable? regions, IEnumerable? localAuthorities, IEnumerable? advisoryBoardDates) + { + var (projects, totalCount) = await _transferProjectRepository.SearchProjects(states, title, deliveryOfficers, page, count); + IEnumerable data = AcademyTransferProjectSummaryResponse(projects); + var pageResponse = PagingResponseFactory.Create("transfer-projects/projects", page, count, totalCount, + new Dictionary { + {"states", states}, + }); + + return new PagedDataResponse(data, + pageResponse); + } public async Task> GetExportedTransferProjects(string? title) { IEnumerable transferProjects = (await _transferProjectRepository.GetAllTransferProjects()).ToList(); From b3870e64a4d5d158008cc24476dee0ea4659aa9f Mon Sep 17 00:00:00 2001 From: Dominic NEED Date: Mon, 15 Apr 2024 15:19:55 +0100 Subject: [PATCH 3/7] Added Controller endpoint for searching --- .../ITransferProjectRepository.cs | 2 +- .../Query/ITransferProjectQueryService.cs | 4 +-- .../Queries/TransferProjectQueryService.cs | 2 +- .../Controllers/TransferProjectController.cs | 25 ++++++++++++++++++- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/Dfe.Academies.Academisation.Domain/TransferProjectAggregate/ITransferProjectRepository.cs b/Dfe.Academies.Academisation.Domain/TransferProjectAggregate/ITransferProjectRepository.cs index 770785c5..68cd1a68 100644 --- a/Dfe.Academies.Academisation.Domain/TransferProjectAggregate/ITransferProjectRepository.cs +++ b/Dfe.Academies.Academisation.Domain/TransferProjectAggregate/ITransferProjectRepository.cs @@ -7,6 +7,6 @@ public interface ITransferProjectRepository : IRepository, IGen { public Task GetByUrn(int urn); public Task> GetAllTransferProjects(); - public async Task<(IEnumerable, int totalcount)> SearchProjects(IEnumerable? states, string? title, IEnumerable? deliveryOfficers, int page, int count); + Task<(IEnumerable, int totalcount)> SearchProjects(IEnumerable? states, string? title, IEnumerable? deliveryOfficers, int page, int count); } } diff --git a/Dfe.Academies.Academisation.IService/Query/ITransferProjectQueryService.cs b/Dfe.Academies.Academisation.IService/Query/ITransferProjectQueryService.cs index 201c0a79..c75f483f 100644 --- a/Dfe.Academies.Academisation.IService/Query/ITransferProjectQueryService.cs +++ b/Dfe.Academies.Academisation.IService/Query/ITransferProjectQueryService.cs @@ -1,5 +1,4 @@ -using Dfe.Academies.Academisation.IDomain.TransferProjectAggregate; -using Dfe.Academies.Academisation.IService.ServiceModels.Legacy.ProjectAggregate; +using Dfe.Academies.Academisation.IService.ServiceModels.Legacy.ProjectAggregate; using Dfe.Academies.Academisation.IService.ServiceModels.TransferProject; namespace Dfe.Academies.Academisation.IService.Query @@ -10,6 +9,7 @@ public interface ITransferProjectQueryService Task GetById(int id); Task> GetTransferProjects(int page, int count, int? urn, string title); + Task?> GetProjects(IEnumerable? states, string? title, IEnumerable? deliveryOfficers, int page, int count); Task> GetExportedTransferProjects(string? title); } diff --git a/Dfe.Academies.Academisation.Service/Queries/TransferProjectQueryService.cs b/Dfe.Academies.Academisation.Service/Queries/TransferProjectQueryService.cs index 56996760..2f600e31 100644 --- a/Dfe.Academies.Academisation.Service/Queries/TransferProjectQueryService.cs +++ b/Dfe.Academies.Academisation.Service/Queries/TransferProjectQueryService.cs @@ -64,7 +64,7 @@ public async Task> Ge return await Task.FromResult(new PagedResultResponse(projects, recordTotal)); } - public async Task?> GetProjects(IEnumerable? states, string? title, IEnumerable? deliveryOfficers, int page, int count, IEnumerable? regions, IEnumerable? localAuthorities, IEnumerable? advisoryBoardDates) + public async Task?> GetProjects(IEnumerable? states, string? title, IEnumerable? deliveryOfficers, int page, int count) { var (projects, totalCount) = await _transferProjectRepository.SearchProjects(states, title, deliveryOfficers, page, count); IEnumerable data = AcademyTransferProjectSummaryResponse(projects); diff --git a/Dfe.Academies.Academisation.WebApi/Controllers/TransferProjectController.cs b/Dfe.Academies.Academisation.WebApi/Controllers/TransferProjectController.cs index 31c67e3e..9255c171 100644 --- a/Dfe.Academies.Academisation.WebApi/Controllers/TransferProjectController.cs +++ b/Dfe.Academies.Academisation.WebApi/Controllers/TransferProjectController.cs @@ -1,5 +1,7 @@ using Dfe.Academies.Academisation.Core; +using Dfe.Academies.Academisation.Data.ProjectAggregate; using Dfe.Academies.Academisation.IService.Query; +using Dfe.Academies.Academisation.IService.ServiceModels.Legacy.ProjectAggregate; using Dfe.Academies.Academisation.IService.ServiceModels.TransferProject; using Dfe.Academies.Academisation.Service.Commands.Application; using Dfe.Academies.Academisation.Service.Commands.TransferProject; @@ -274,6 +276,27 @@ public async Task> GetTransferProje return Ok(result); } - + /// + /// Retrieve all projects matching specified filter conditions + /// + /// describing filtering requirements for the request + /// URN of a specific project to retrieve + /// + /// Filters are cumulative (AND logic), applied in the following order: by Region, by Status, by URN, by School, by + /// Delivery Officer. + /// + /// One or more projects matching the specified filter criteria were found + /// No projects matched the specified search criteria + [HttpPost("projects", Name = "GetProjects")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task>> GetProjects( + GetAcademyConversionSearchModel? searchModel) + { + PagedDataResponse? result = + await _transferProjectQueryService.GetProjects(searchModel!.StatusQueryString, searchModel.TitleFilter, + searchModel.DeliveryOfficerQueryString, searchModel.Page, searchModel.Count); + return result is null ? NotFound() : Ok(result); + } } } From 3b33706fa5c78779aeeee2a40a5f5ee510eda62d Mon Sep 17 00:00:00 2001 From: Dominic NEED Date: Mon, 15 Apr 2024 15:23:56 +0100 Subject: [PATCH 4/7] Renamed endpoint to align with others in controller --- .../Controllers/TransferProjectController.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Dfe.Academies.Academisation.WebApi/Controllers/TransferProjectController.cs b/Dfe.Academies.Academisation.WebApi/Controllers/TransferProjectController.cs index 9255c171..c0cc2dc6 100644 --- a/Dfe.Academies.Academisation.WebApi/Controllers/TransferProjectController.cs +++ b/Dfe.Academies.Academisation.WebApi/Controllers/TransferProjectController.cs @@ -279,15 +279,14 @@ public async Task> GetTransferProje /// /// Retrieve all projects matching specified filter conditions /// - /// describing filtering requirements for the request - /// URN of a specific project to retrieve + /// describing filtering requirements for the request /// /// Filters are cumulative (AND logic), applied in the following order: by Region, by Status, by URN, by School, by /// Delivery Officer. /// /// One or more projects matching the specified filter criteria were found /// No projects matched the specified search criteria - [HttpPost("projects", Name = "GetProjects")] + [HttpPost("GetTransferProjects", Name = "GetTransferProjects")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task>> GetProjects( From 2c4c844c69a5321a1d65c20c568b10481384d27c Mon Sep 17 00:00:00 2001 From: Dominic NEED Date: Mon, 15 Apr 2024 15:25:03 +0100 Subject: [PATCH 5/7] Updated search model to be used by both Conversions and Transfers --- ...earchModel.cs => GetProjectSearchModel.cs} | 4 ++-- .../ApplicationSubmitTests.cs | 2 +- .../LegacyProjectListGetTests.cs | 6 +++--- .../ConversionProjectController.cs | 21 ++----------------- .../Controllers/ProjectController.cs | 4 ++-- .../Controllers/TransferProjectController.cs | 4 ++-- 6 files changed, 12 insertions(+), 29 deletions(-) rename Dfe.Academies.Academisation.Data/ProjectAggregate/{GetAcademyConversionSearchModel.cs => GetProjectSearchModel.cs} (87%) diff --git a/Dfe.Academies.Academisation.Data/ProjectAggregate/GetAcademyConversionSearchModel.cs b/Dfe.Academies.Academisation.Data/ProjectAggregate/GetProjectSearchModel.cs similarity index 87% rename from Dfe.Academies.Academisation.Data/ProjectAggregate/GetAcademyConversionSearchModel.cs rename to Dfe.Academies.Academisation.Data/ProjectAggregate/GetProjectSearchModel.cs index 48f27bdb..10fa17f2 100644 --- a/Dfe.Academies.Academisation.Data/ProjectAggregate/GetAcademyConversionSearchModel.cs +++ b/Dfe.Academies.Academisation.Data/ProjectAggregate/GetProjectSearchModel.cs @@ -1,8 +1,8 @@ namespace Dfe.Academies.Academisation.Data.ProjectAggregate { - public class GetAcademyConversionSearchModel + public class GetProjectSearchModel { - public GetAcademyConversionSearchModel(int page, int count, string? titleFilter, + public GetProjectSearchModel(int page, int count, string? titleFilter, IEnumerable? deliveryOfficerQueryString, IEnumerable? regionQueryString, IEnumerable? statusQueryString, IEnumerable? applicationReferences) { diff --git a/Dfe.Academies.Academisation.SubcutaneousTest/ApplicationAggregate/ApplicationSubmitTests.cs b/Dfe.Academies.Academisation.SubcutaneousTest/ApplicationAggregate/ApplicationSubmitTests.cs index 35f8fd5c..8c7aa052 100644 --- a/Dfe.Academies.Academisation.SubcutaneousTest/ApplicationAggregate/ApplicationSubmitTests.cs +++ b/Dfe.Academies.Academisation.SubcutaneousTest/ApplicationAggregate/ApplicationSubmitTests.cs @@ -199,7 +199,7 @@ public async Task FormAMatApplicationExists___ApplicationIsSubmitted_And_Project var projectController = new ProjectController( Mock.Of(), new ConversionProjectQueryService(new ConversionProjectRepository(_context, null), new FormAMatProjectRepository(_context)), Mock.Of()); - var projectResults = await projectController.GetProjects(new GetAcademyConversionSearchModel(1, 3, null, null, null, null, new[] { $"A2B_{createdPayload.ApplicationReference!}" })); + var projectResults = await projectController.GetProjects(new GetProjectSearchModel(1, 3, null, null, null, null, new[] { $"A2B_{createdPayload.ApplicationReference!}" })); (_, PagedDataResponse projects) = DfeAssert.OkObjectResult(projectResults); diff --git a/Dfe.Academies.Academisation.SubcutaneousTest/ProjectAggregate/LegacyProjectListGetTests.cs b/Dfe.Academies.Academisation.SubcutaneousTest/ProjectAggregate/LegacyProjectListGetTests.cs index 45077dbd..884c0f8a 100644 --- a/Dfe.Academies.Academisation.SubcutaneousTest/ProjectAggregate/LegacyProjectListGetTests.cs +++ b/Dfe.Academies.Academisation.SubcutaneousTest/ProjectAggregate/LegacyProjectListGetTests.cs @@ -38,7 +38,7 @@ public async Task ProjectsExists___ProjectListReturned() await _context.Projects.AddAsync(project3); await _context.SaveChangesAsync(); - GetAcademyConversionSearchModel searchModel = new GetAcademyConversionSearchModel(1, 3, null, null, null, null, null); + GetProjectSearchModel searchModel = new GetProjectSearchModel(1, 3, null, null, null, null, null); // act var result = await _subject.GetProjects(searchModel); @@ -61,7 +61,7 @@ public async Task ProjectsExists_ProjectListReturned_WithRegionFilter() await _context.SaveChangesAsync(); string[] regions = { project1.Details.Region!.ToLower(), project2.Details.Region!.ToLower() }; - GetAcademyConversionSearchModel searchModel = new GetAcademyConversionSearchModel(1, 3, null, null, regions, null, null); + GetProjectSearchModel searchModel = new GetProjectSearchModel(1, 3, null, null, regions, null, null); // act var result = await _subject.GetProjects(searchModel); @@ -84,7 +84,7 @@ public async Task ProjectsExists_ProjectListReturned_WithApplicationIdFilter() await _context.SaveChangesAsync(); string[] applicationReferences = { project1.Details.ApplicationReferenceNumber!, project2.Details.ApplicationReferenceNumber! }; - GetAcademyConversionSearchModel searchModel = new GetAcademyConversionSearchModel(1, 3, null, null, null, null, applicationReferences); + GetProjectSearchModel searchModel = new GetProjectSearchModel(1, 3, null, null, null, null, applicationReferences); // act var result = await _subject.GetProjects(searchModel); diff --git a/Dfe.Academies.Academisation.WebApi/Controllers/ConversionProjectController.cs b/Dfe.Academies.Academisation.WebApi/Controllers/ConversionProjectController.cs index b07bd055..a04af03f 100644 --- a/Dfe.Academies.Academisation.WebApi/Controllers/ConversionProjectController.cs +++ b/Dfe.Academies.Academisation.WebApi/Controllers/ConversionProjectController.cs @@ -144,7 +144,7 @@ public async Task SetIncomingTrust(int id, SetIncomingTrustCommand /// /// Retrieve all projects matching specified filter conditions /// - /// describing filtering requirements for the request + /// describing filtering requirements for the request /// URN of a specific project to retrieve /// /// Filters are cumulative (AND logic), applied in the following order: by Region, by Status, by URN, by School, by @@ -169,7 +169,7 @@ await _conversionProjectQueryService.GetProjectsV2(searchModel!.StatusQueryStrin /// /// Retrieve all form a mat projects matching specified filter conditions /// - /// describing filtering requirements for the request + /// describing filtering requirements for the request /// URN of a specific project to retrieve /// /// Filters are cumulative (AND logic), applied in the following order: by Region, by Status, by URN, by School, by @@ -303,22 +303,5 @@ public async Task SetFormAMatProjectReference( _ => throw new NotImplementedException() }; } - - [HttpDelete("{id:int}/Delete", Name = "DeleteProject")] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task DeleteAProjectById(int id, CancellationToken cancellationToken) - { - SetDeletedAtCommand request = new SetDeletedAtCommand(id); - - CommandResult result = await _mediator.Send(request); - - return result switch - { - CommandSuccessResult => Ok(), - NotFoundCommandResult => NotFound(), - _ => throw new NotImplementedException() - }; - } } } diff --git a/Dfe.Academies.Academisation.WebApi/Controllers/ProjectController.cs b/Dfe.Academies.Academisation.WebApi/Controllers/ProjectController.cs index 5b52213a..fc94cd3d 100644 --- a/Dfe.Academies.Academisation.WebApi/Controllers/ProjectController.cs +++ b/Dfe.Academies.Academisation.WebApi/Controllers/ProjectController.cs @@ -38,7 +38,7 @@ public ProjectController( /// /// Retrieve all projects matching specified filter conditions /// - /// describing filtering requirements for the request + /// describing filtering requirements for the request /// URN of a specific project to retrieve /// /// Filters are cumulative (AND logic), applied in the following order: by Region, by Status, by URN, by School, by @@ -50,7 +50,7 @@ public ProjectController( [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task>> GetProjects( - GetAcademyConversionSearchModel? searchModel, + GetProjectSearchModel? searchModel, [FromQuery] int? urn = null) { PagedDataResponse? result = diff --git a/Dfe.Academies.Academisation.WebApi/Controllers/TransferProjectController.cs b/Dfe.Academies.Academisation.WebApi/Controllers/TransferProjectController.cs index c0cc2dc6..7510f977 100644 --- a/Dfe.Academies.Academisation.WebApi/Controllers/TransferProjectController.cs +++ b/Dfe.Academies.Academisation.WebApi/Controllers/TransferProjectController.cs @@ -281,7 +281,7 @@ public async Task> GetTransferProje /// /// describing filtering requirements for the request /// - /// Filters are cumulative (AND logic), applied in the following order: by Region, by Status, by URN, by School, by + /// Filters are cumulative (AND logic), applied in the following order: by Status, by URN, by /// Delivery Officer. /// /// One or more projects matching the specified filter criteria were found @@ -290,7 +290,7 @@ public async Task> GetTransferProje [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task>> GetProjects( - GetAcademyConversionSearchModel? searchModel) + GetProjectSearchModel? searchModel) { PagedDataResponse? result = await _transferProjectQueryService.GetProjects(searchModel!.StatusQueryString, searchModel.TitleFilter, From 26c13d8d513761151d6a32b73258c302860460ca Mon Sep 17 00:00:00 2001 From: Dominic NEED Date: Wed, 17 Apr 2024 10:24:08 +0100 Subject: [PATCH 6/7] Added test for the query service --- .../TransferProjectQueryServiceTests.cs | 108 +++++++++++++----- 1 file changed, 80 insertions(+), 28 deletions(-) diff --git a/Dfe.Academies.Academisation.Service.UnitTest/Queries/TransferProjectQueryServiceTests.cs b/Dfe.Academies.Academisation.Service.UnitTest/Queries/TransferProjectQueryServiceTests.cs index 2ac4c76b..fbba7db0 100644 --- a/Dfe.Academies.Academisation.Service.UnitTest/Queries/TransferProjectQueryServiceTests.cs +++ b/Dfe.Academies.Academisation.Service.UnitTest/Queries/TransferProjectQueryServiceTests.cs @@ -1,18 +1,17 @@ -using Xunit; -using Moq; -using FluentAssertions; -using Dfe.Academies.Academisation.Domain.TransferProjectAggregate; -using Dfe.Academies.Academisation.Service.Mappers.TransferProject; -using Dfe.Academies.Academisation.Service.Queries; +using Dfe.Academies.Academisation.Domain.TransferProjectAggregate; +using Dfe.Academies.Academisation.IData.ConversionAdvisoryBoardDecisionAggregate; +using Dfe.Academies.Academisation.IDomain.ConversionAdvisoryBoardDecisionAggregate; using Dfe.Academies.Academisation.IDomain.TransferProjectAggregate; using Dfe.Academies.Academisation.IService.Query; using Dfe.Academies.Academisation.IService.ServiceModels.TransferProject; -using Dfe.Academies.Academisation.IData.ConversionAdvisoryBoardDecisionAggregate; -using Dfe.Academies.Academisation.IDomain.ConversionAdvisoryBoardDecisionAggregate; -using Dfe.Academies.Academisation.IService.ServiceModels.Academies; +using Dfe.Academies.Academisation.Service.Mappers.TransferProject; +using Dfe.Academies.Academisation.Service.Queries; using Dfe.Academies.Academisation.Service.UnitTest.Mocks; -using Microsoft.Extensions.DependencyInjection; using Dfe.Academies.Contracts.V4.Establishments; +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; +using Moq; +using Xunit; namespace Dfe.Academies.Academisation.Service.UnitTest.Queries { @@ -35,15 +34,15 @@ public async Task GetByUrn_ShouldReturnExpectedResponse() var dummyUrn = 1; - // Create a TransferProject - ITransferProject dummyTransferProject = TransferProject.Create( - "dummyOutgoingTrustUkprn", - "out trust", - "dummyIncomingTrustUkprn", - "in trust", - new List { "dummyUkprn1", "dummyUkprn2" }, - DateTime.Now - ); + // Create a TransferProject + ITransferProject dummyTransferProject = TransferProject.Create( + "dummyOutgoingTrustUkprn", + "out trust", + "dummyIncomingTrustUkprn", + "in trust", + new List { "dummyUkprn1", "dummyUkprn2" }, + DateTime.Now + ); // Mock the setup to return the dummy project mockRepository.Setup(repo => repo.GetByUrn(It.IsAny())).Returns(Task.FromResult((ITransferProject?)dummyTransferProject)); @@ -68,15 +67,15 @@ public async Task GetById_ShouldReturnExpectedResponse() var dummyId = 1; - // Create a TransferProject - var dummyTransferProject = TransferProject.Create( - "dummyOutgoingTrustUkprn", - "out trust", - "dummyIncomingTrustUkprn", - "in trust", - new List { "dummyUkprn1", "dummyUkprn2" }, - DateTime.Now - ); + // Create a TransferProject + var dummyTransferProject = TransferProject.Create( + "dummyOutgoingTrustUkprn", + "out trust", + "dummyIncomingTrustUkprn", + "in trust", + new List { "dummyUkprn1", "dummyUkprn2" }, + DateTime.Now + ); // Mock the setup to return the dummy project mockRepository.Setup(repo => repo.GetById(It.IsAny())).Returns(Task.FromResult(dummyTransferProject)); @@ -246,5 +245,58 @@ private static ExportedTransferProjectModel GetDummyTransferProjectModel( Urn = "0" }; } + + + [Fact] + public async Task GetProjects_ReturnsFilteredProjects() + { + // Arrange + var mockRepository = new Mock(); + var service = new TransferProjectQueryService(mockRepository.Object, _establishmentRepo, _advisoryBoardDecisionGetDataByProjectIdQuery); + + var states = new List { "Active", "Completed" }; + var title = "Project X"; + var deliveryOfficers = new List { "Officer A" }; + var page = 1; + var count = 10; + + // Sample data setup + var dummyProjects = new List + { + TransferProject.Create("outUkprn1", "Out Trust 1", "inUkprn1", "In Trust 1", new List { "ukprn1" }, DateTime.UtcNow), + TransferProject.Create("outUkprn2", "Out Trust 2", "inUkprn2", "In Trust 2", new List { "ukprn2" }, DateTime.UtcNow) + }; + + // Expected data setup + var expectedData = dummyProjects.Select(p => new AcademyTransferProjectSummaryResponse + { + ProjectUrn = p.Urn.ToString(), + ProjectReference = p.ProjectReference, + OutgoingTrustUkprn = p.OutgoingTrustUkprn, + OutgoingTrustName = p.OutgoingTrustName, + Status = p.Status, + TransferringAcademies = p.TransferringAcademies.Select(a => new TransferringAcademiesResponse + { + IncomingTrustName = a.IncomingTrustName, + IncomingTrustUkprn = a.IncomingTrustUkprn, + OutgoingAcademyUkprn = a.OutgoingAcademyUkprn, + }).ToList(), + AssignedUser = null, + IsFormAMat = false + }); + + mockRepository.Setup(repo => repo.SearchProjects(states, title, deliveryOfficers, page, count)) + .ReturnsAsync((dummyProjects, 2)); // 2 represents the total count of items + + // Act + var result = await service.GetProjects(states, title, deliveryOfficers, page, count); + + // Assert + result.Should().NotBeNull(); + result!.Data.Should().BeEquivalentTo(expectedData); + result.Paging.Page.Should().Be(page); + result.Paging.RecordCount.Should().Be(2); + } + } } From f4cbe5a0a42ce1807ec9b98bce64791743312756 Mon Sep 17 00:00:00 2001 From: Dominic NEED Date: Wed, 17 Apr 2024 11:41:59 +0100 Subject: [PATCH 7/7] Updated test to account for new isfam param --- .../TransferProject.cs | 23 +++++----- .../TransferProjectQueryServiceTests.cs | 44 +++++++++---------- 2 files changed, 34 insertions(+), 33 deletions(-) diff --git a/Dfe.Academies.Academisation.Domain/TransferProjectAggregate/TransferProject.cs b/Dfe.Academies.Academisation.Domain/TransferProjectAggregate/TransferProject.cs index 61050bc9..658e4c9a 100644 --- a/Dfe.Academies.Academisation.Domain/TransferProjectAggregate/TransferProject.cs +++ b/Dfe.Academies.Academisation.Domain/TransferProjectAggregate/TransferProject.cs @@ -34,7 +34,7 @@ protected TransferProject() { } public string? ProjectReference { get; private set; } public string OutgoingTrustUkprn { get; private set; } - public string? OutgoingTrustName{ get; private set; } + public string? OutgoingTrustName { get; private set; } public string? WhoInitiatedTheTransfer { get; private set; } @@ -94,7 +94,7 @@ protected TransferProject() { } public IReadOnlyCollection TransferringAcademies => _transferringAcademies; IReadOnlyCollection ITransferProject.IntendedTransferBenefits => _intendedTransferBenefits; - IReadOnlyCollection ITransferProject.TransferringAcademies => + IReadOnlyCollection ITransferProject.TransferringAcademies => _transferringAcademies; public DateTime? CreatedOn { get; private set; } @@ -133,7 +133,7 @@ public void AssignUser(Guid userId, string userEmail, string userFullName) } public void SetFeatures(string whoInitiatedTheTransfer, List specificReasonsForTransfer, string transferType, bool? isCompleted) - { + { WhoInitiatedTheTransfer = whoInitiatedTheTransfer; _specificReasonsForTransfer = specificReasonsForTransfer; TypeOfTransfer = transferType; @@ -166,12 +166,12 @@ public void SetTransferringAcademiesSchoolData(string transferringAcademyUkprn, ); } - public void SetBenefitsAndRisks(bool? anyRisks, bool? equalitiesImpactAssessmentConsidered, - List selectedBenefits, string? otherBenefitValue, - bool? highProfileShouldBeConsidered, string? highProfileFurtherSpecification, - bool? complexLandAndBuildingShouldBeConsidered, string? complexLandAndBuildingFurtherSpecification, - bool? financeAndDebtShouldBeConsidered, string? financeAndDebtFurtherSpecification, - bool? otherRisksShouldBeConsidered, string? otherRisksFurtherSpecification, + public void SetBenefitsAndRisks(bool? anyRisks, bool? equalitiesImpactAssessmentConsidered, + List selectedBenefits, string? otherBenefitValue, + bool? highProfileShouldBeConsidered, string? highProfileFurtherSpecification, + bool? complexLandAndBuildingShouldBeConsidered, string? complexLandAndBuildingFurtherSpecification, + bool? financeAndDebtShouldBeConsidered, string? financeAndDebtFurtherSpecification, + bool? otherRisksShouldBeConsidered, string? otherRisksFurtherSpecification, bool? isCompleted) { AnyRisks = anyRisks; @@ -182,7 +182,7 @@ public void SetBenefitsAndRisks(bool? anyRisks, bool? equalitiesImpactAssessment OtherBenefitValue = otherBenefitValue; HighProfileShouldBeConsidered = highProfileShouldBeConsidered; - HighProfileFurtherSpecification = highProfileFurtherSpecification; + HighProfileFurtherSpecification = highProfileFurtherSpecification; ComplexLandAndBuildingShouldBeConsidered = complexLandAndBuildingShouldBeConsidered; ComplexLandAndBuildingFurtherSpecification = complexLandAndBuildingFurtherSpecification; FinanceAndDebtShouldBeConsidered = financeAndDebtShouldBeConsidered; @@ -219,7 +219,8 @@ public void SetAcademyIncomingTrustName(int academyId, string incomingTrustName) var transferringAcademy = TransferringAcademies.SingleOrDefault(x => x.Id == academyId); - if (transferringAcademy != null) { + if (transferringAcademy != null) + { transferringAcademy.SetIncomingTrustName(incomingTrustName); diff --git a/Dfe.Academies.Academisation.Service.UnitTest/Queries/TransferProjectQueryServiceTests.cs b/Dfe.Academies.Academisation.Service.UnitTest/Queries/TransferProjectQueryServiceTests.cs index d79f83ec..238412ec 100644 --- a/Dfe.Academies.Academisation.Service.UnitTest/Queries/TransferProjectQueryServiceTests.cs +++ b/Dfe.Academies.Academisation.Service.UnitTest/Queries/TransferProjectQueryServiceTests.cs @@ -35,16 +35,16 @@ public async Task GetByUrn_ShouldReturnExpectedResponse() var dummyUrn = 1; - // Create a TransferProject - ITransferProject dummyTransferProject = TransferProject.Create( - "dummyOutgoingTrustUkprn", - "out trust", - "dummyIncomingTrustUkprn", - "in trust", - new List { "dummyUkprn1", "dummyUkprn2" }, - false, - DateTime.Now - ); + // Create a TransferProject + ITransferProject dummyTransferProject = TransferProject.Create( + "dummyOutgoingTrustUkprn", + "out trust", + "dummyIncomingTrustUkprn", + "in trust", + new List { "dummyUkprn1", "dummyUkprn2" }, + false, + DateTime.Now + ); // Mock the setup to return the dummy project @@ -71,16 +71,16 @@ public async Task GetById_ShouldReturnExpectedResponse() var dummyId = 1; - // Create a TransferProject - var dummyTransferProject = TransferProject.Create( - "dummyOutgoingTrustUkprn", - "out trust", - "dummyIncomingTrustUkprn", - "in trust", - new List { "dummyUkprn1", "dummyUkprn2" }, - false, - DateTime.Now - ); + // Create a TransferProject + var dummyTransferProject = TransferProject.Create( + "dummyOutgoingTrustUkprn", + "out trust", + "dummyIncomingTrustUkprn", + "in trust", + new List { "dummyUkprn1", "dummyUkprn2" }, + false, + DateTime.Now + ); // Mock the setup to return the dummy project mockRepository.Setup(repo => repo.GetById(It.IsAny())).Returns(Task.FromResult(dummyTransferProject)); @@ -268,8 +268,8 @@ public async Task GetProjects_ReturnsFilteredProjects() // Sample data setup var dummyProjects = new List { - TransferProject.Create("outUkprn1", "Out Trust 1", "inUkprn1", "In Trust 1", new List { "ukprn1" }, DateTime.UtcNow), - TransferProject.Create("outUkprn2", "Out Trust 2", "inUkprn2", "In Trust 2", new List { "ukprn2" }, DateTime.UtcNow) + TransferProject.Create("outUkprn1", "Out Trust 1", "inUkprn1", "In Trust 1", new List { "ukprn1" },false, DateTime.UtcNow), + TransferProject.Create("outUkprn2", "Out Trust 2", "inUkprn2", "In Trust 2", new List { "ukprn2" },false, DateTime.UtcNow) }; // Expected data setup