From 66067308928b3906515acc6cd0c829d65832d7fb Mon Sep 17 00:00:00 2001 From: "SHAKIR, Muhammad" Date: Thu, 15 Aug 2024 16:48:08 +0100 Subject: [PATCH 1/2] Added delete project group endpoint --- .../ProjectGroup/DeleteProjectGroupCommand.cs | 10 ++++++ .../DeleteProjectGroupCommandHandler.cs | 32 +++++++++++++++++++ .../Controllers/ProjectGroupController.cs | 16 ++++++++++ 3 files changed, 58 insertions(+) create mode 100644 Dfe.Academies.Academisation.Service/Commands/ProjectGroup/DeleteProjectGroupCommand.cs create mode 100644 Dfe.Academies.Academisation.Service/Commands/ProjectGroup/DeleteProjectGroupCommandHandler.cs diff --git a/Dfe.Academies.Academisation.Service/Commands/ProjectGroup/DeleteProjectGroupCommand.cs b/Dfe.Academies.Academisation.Service/Commands/ProjectGroup/DeleteProjectGroupCommand.cs new file mode 100644 index 000000000..4b43328f8 --- /dev/null +++ b/Dfe.Academies.Academisation.Service/Commands/ProjectGroup/DeleteProjectGroupCommand.cs @@ -0,0 +1,10 @@ +using Dfe.Academies.Academisation.Core; +using MediatR; + +namespace Dfe.Academies.Academisation.Service.Commands.ProjectGroup +{ + public class DeleteProjectGroupCommand(string referenceNumber) : IRequest + { + public string GroupReferenceNumber { get; set; } = referenceNumber; + } +} diff --git a/Dfe.Academies.Academisation.Service/Commands/ProjectGroup/DeleteProjectGroupCommandHandler.cs b/Dfe.Academies.Academisation.Service/Commands/ProjectGroup/DeleteProjectGroupCommandHandler.cs new file mode 100644 index 000000000..4b544895a --- /dev/null +++ b/Dfe.Academies.Academisation.Service/Commands/ProjectGroup/DeleteProjectGroupCommandHandler.cs @@ -0,0 +1,32 @@ +using Dfe.Academies.Academisation.Core; +using Dfe.Academies.Academisation.Domain.ProjectGroupsAggregate; +using MediatR; +using Microsoft.Extensions.Logging; +using Dfe.Academies.Academisation.Domain.ApplicationAggregate; +using FluentValidation; +using Dfe.Academies.Academisation.IDomain.ProjectAggregate; + +namespace Dfe.Academies.Academisation.Service.Commands.ProjectGroup +{ + public class DeleteProjectGroupCommandHandler(IProjectGroupRepository projectGroupRepository, ILogger logger, IConversionProjectRepository conversionProjectRepository) : IRequestHandler + { + public async Task Handle(SetProjectGroupCommand message, CancellationToken cancellationToken) + { + logger.LogInformation("Setting project group with reference number:{value}", message.GroupReferenceNumber); + + var projectGroup = await projectGroupRepository.GetByReferenceNumberAsync(message.GroupReferenceNumber, cancellationToken); + if (projectGroup == null) + { + logger.LogError("Project group is not found with reference number:{value}", message.GroupReferenceNumber); + return new NotFoundCommandResult(); + } + + var conversionProjects = await conversionProjectRepository.GetProjectsByIdsAsync(message.ConversionProjectIds, cancellationToken).ConfigureAwait(false); + if (conversionProjects != null && conversionProjects.Any()) + { + + } + return new CommandSuccessResult(); + } + } +} diff --git a/Dfe.Academies.Academisation.WebApi/Controllers/ProjectGroupController.cs b/Dfe.Academies.Academisation.WebApi/Controllers/ProjectGroupController.cs index ff0acd2d6..2b3e9bb46 100644 --- a/Dfe.Academies.Academisation.WebApi/Controllers/ProjectGroupController.cs +++ b/Dfe.Academies.Academisation.WebApi/Controllers/ProjectGroupController.cs @@ -100,5 +100,21 @@ public async Task> GetProjectGrouptById( return Ok(project); } + + [HttpDelete("{referenceNumber:string}", Name = "DeleteProjectGroupByReference")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task> DeleteProjectGroupByReference(string referenceNumber, CancellationToken cancellationToken) + { + var result = await mediator.Send(new DeleteProjectGroupCommand(referenceNumber), cancellationToken).ConfigureAwait(false); + + return result switch + { + CommandSuccessResult => Ok(), + NotFoundCommandResult => NotFound(), + CommandValidationErrorResult validationErrorResult => new BadRequestObjectResult(validationErrorResult.ValidationErrors), + _ => new InternalServerErrorObjectResult("Error serving request") + }; + } } } From 079f37eeff16f9632191ceaae88784da41e72d6c Mon Sep 17 00:00:00 2001 From: "SHAKIR, Muhammad" Date: Fri, 16 Aug 2024 09:50:46 +0100 Subject: [PATCH 2/2] Added delete project group endpoint --- .../DeleteProjectGroupCommandHandlerTests.cs | 78 +++++++++++++++++++ .../DeleteProjectGroupCommandHandler.cs | 17 ++-- .../ProjectGroup/ProjectGroupTests.cs | 51 +++++++++++- .../Controllers/ProjectGroupController.cs | 5 +- 4 files changed, 136 insertions(+), 15 deletions(-) create mode 100644 Dfe.Academies.Academisation.Service.UnitTest/Commands/ProjectGroup/DeleteProjectGroupCommandHandlerTests.cs diff --git a/Dfe.Academies.Academisation.Service.UnitTest/Commands/ProjectGroup/DeleteProjectGroupCommandHandlerTests.cs b/Dfe.Academies.Academisation.Service.UnitTest/Commands/ProjectGroup/DeleteProjectGroupCommandHandlerTests.cs new file mode 100644 index 000000000..d51e22c30 --- /dev/null +++ b/Dfe.Academies.Academisation.Service.UnitTest/Commands/ProjectGroup/DeleteProjectGroupCommandHandlerTests.cs @@ -0,0 +1,78 @@ +using AutoFixture; +using Dfe.Academies.Academisation.Core; +using Dfe.Academies.Academisation.Domain.ProjectGroupsAggregate; +using Dfe.Academies.Academisation.Domain.SeedWork; +using Dfe.Academies.Academisation.Service.Commands.ProjectGroup; +using Microsoft.Extensions.Logging; +using Moq; +using Xunit; + +namespace Dfe.Academies.Academisation.Service.UnitTest.Commands.ProjectGroup +{ + public class DeleteProjectGroupCommandHandlerTests + { + private readonly MockRepository _mockRepository; + private readonly Mock _mockProjectGroupRepository; + private readonly Mock> _mocklogger; + private readonly Fixture _fixture = new(); + private readonly CancellationToken _cancellationToken; + private readonly DeleteProjectGroupCommandHandler _deleteProjectGroupCommandHandler; + + public DeleteProjectGroupCommandHandlerTests() + { + _mockRepository = new MockRepository(MockBehavior.Strict); + _cancellationToken = CancellationToken.None; + _mockProjectGroupRepository = _mockRepository.Create(); + _mocklogger = new Mock>(); + + var mockContext = new Mock(); + _mockProjectGroupRepository.Setup(x => x.UnitOfWork).Returns(mockContext.Object); + + _deleteProjectGroupCommandHandler = new DeleteProjectGroupCommandHandler( + _mockProjectGroupRepository.Object, + _mocklogger.Object); + } + + [Fact] + public async Task Handle_ProjectGroupDoesNotExists_ReturnsNotFoundCommandResult() + { + // Arrange + var request = new DeleteProjectGroupCommand("Reference"); + _mockProjectGroupRepository.Setup(x => x.GetByReferenceNumberAsync(request.GroupReferenceNumber, _cancellationToken)).ReturnsAsync((Domain.ProjectGroupsAggregate.ProjectGroup?)null); + + // Act + var result = await _deleteProjectGroupCommandHandler.Handle( + request, + _cancellationToken); + + // Assert + var notFoundCommandResult = Assert.IsType(result); + _mockProjectGroupRepository.Verify(x => x.Update(It.IsAny()), Times.Never()); + _mockProjectGroupRepository.Verify(x => x.GetByReferenceNumberAsync(request.GroupReferenceNumber, _cancellationToken), Times.Once()); + _mockProjectGroupRepository.Verify(x => x.UnitOfWork.SaveChangesAsync(It.Is(x => x == _cancellationToken)), Times.Never()); + } + + [Fact] + public async Task Handle_ValidRequestWithNoRemovedConversions_ReturnsSuccess() + { + // Arrange + var expectedProjectGroup = _fixture.Create(); + expectedProjectGroup.SetProjectReference(1); + var request = new DeleteProjectGroupCommand(expectedProjectGroup.ReferenceNumber!); + _mockProjectGroupRepository.Setup(x => x.Update(It.IsAny())); + _mockProjectGroupRepository.Setup(x => x.GetByReferenceNumberAsync(request.GroupReferenceNumber, _cancellationToken)).ReturnsAsync(expectedProjectGroup); + _mockProjectGroupRepository.Setup(x => x.Delete(expectedProjectGroup)); + + // Act + var result = await _deleteProjectGroupCommandHandler.Handle( + request, + _cancellationToken); + + // Assert + var commandSuccessResult = Assert.IsType(result); + _mockProjectGroupRepository.Verify(x => x.GetByReferenceNumberAsync(request.GroupReferenceNumber, _cancellationToken), Times.Once); + _mockProjectGroupRepository.Verify(x => x.Delete(expectedProjectGroup), Times.Once); + _mockProjectGroupRepository.Verify(x => x.UnitOfWork.SaveChangesAsync(_cancellationToken), Times.Once); + } + } +} diff --git a/Dfe.Academies.Academisation.Service/Commands/ProjectGroup/DeleteProjectGroupCommandHandler.cs b/Dfe.Academies.Academisation.Service/Commands/ProjectGroup/DeleteProjectGroupCommandHandler.cs index 4b544895a..2bf16d98d 100644 --- a/Dfe.Academies.Academisation.Service/Commands/ProjectGroup/DeleteProjectGroupCommandHandler.cs +++ b/Dfe.Academies.Academisation.Service/Commands/ProjectGroup/DeleteProjectGroupCommandHandler.cs @@ -2,15 +2,12 @@ using Dfe.Academies.Academisation.Domain.ProjectGroupsAggregate; using MediatR; using Microsoft.Extensions.Logging; -using Dfe.Academies.Academisation.Domain.ApplicationAggregate; -using FluentValidation; -using Dfe.Academies.Academisation.IDomain.ProjectAggregate; namespace Dfe.Academies.Academisation.Service.Commands.ProjectGroup { - public class DeleteProjectGroupCommandHandler(IProjectGroupRepository projectGroupRepository, ILogger logger, IConversionProjectRepository conversionProjectRepository) : IRequestHandler + public class DeleteProjectGroupCommandHandler(IProjectGroupRepository projectGroupRepository, ILogger logger) : IRequestHandler { - public async Task Handle(SetProjectGroupCommand message, CancellationToken cancellationToken) + public async Task Handle(DeleteProjectGroupCommand message, CancellationToken cancellationToken) { logger.LogInformation("Setting project group with reference number:{value}", message.GroupReferenceNumber); @@ -21,12 +18,10 @@ public async Task Handle(SetProjectGroupCommand message, Cancella return new NotFoundCommandResult(); } - var conversionProjects = await conversionProjectRepository.GetProjectsByIdsAsync(message.ConversionProjectIds, cancellationToken).ConfigureAwait(false); - if (conversionProjects != null && conversionProjects.Any()) - { - - } - return new CommandSuccessResult(); + projectGroupRepository.Delete(projectGroup); + await projectGroupRepository.UnitOfWork.SaveChangesAsync(cancellationToken); + + return new CommandSuccessResult(); } } } diff --git a/Dfe.Academies.Academisation.SubcutaneousTest/ProjectGroup/ProjectGroupTests.cs b/Dfe.Academies.Academisation.SubcutaneousTest/ProjectGroup/ProjectGroupTests.cs index 67cbe49a7..1ee986898 100644 --- a/Dfe.Academies.Academisation.SubcutaneousTest/ProjectGroup/ProjectGroupTests.cs +++ b/Dfe.Academies.Academisation.SubcutaneousTest/ProjectGroup/ProjectGroupTests.cs @@ -1,8 +1,12 @@ -using AutoFixture; +using System.Web.Http.Results; +using AutoFixture; +using Dfe.Academies.Academisation.Core; using Dfe.Academies.Academisation.Data; using Dfe.Academies.Academisation.IService.ServiceModels.ProjectGroup; using Dfe.Academies.Academisation.Service.Commands.ProjectGroup; using Dfe.Academies.Academisation.SubcutaneousTest.Utils; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; namespace Dfe.Academies.Academisation.SubcutaneousTest.ProjectGroup { @@ -34,5 +38,50 @@ public async Task CreateProjectGroup_ShouldCreateSuccessfully() Assert.NotEqual(response.Id, notExpectedId); Assert.Equal(response.TrustReferenceNumber, command.TrustReferenceNumber); } + + [Fact] + public async Task DeleteProjectGroup_ShouldDeleteSuccessfully() + { + // Arrange + var projectGroup = await CreateProjectGroup(); + + // Action + var httpResponseMessage = await _client.DeleteAsync($"project-group/{projectGroup.ReferenceNumber}", CancellationToken); + + Assert.True(httpResponseMessage.IsSuccessStatusCode); + var dbProjectGroup = await _context.ProjectGroups.AsNoTracking().FirstOrDefaultAsync(x => x.ReferenceNumber == projectGroup.ReferenceNumber); + Assert.Null(dbProjectGroup); + } + + [Fact] + public async Task DeleteProjectGroup_ShouldReturnNotFoundOnNotFindingGroup() + { + // Arrange + var referenceNumber = Fixture.Create()[..10]; + + // Action + var httpResponseMessage = await _client.DeleteAsync($"project-group/{referenceNumber}", CancellationToken); + + // Assert + Assert.False(httpResponseMessage.IsSuccessStatusCode); + Assert.Equal(httpResponseMessage!.StatusCode.GetHashCode(), System.Net.HttpStatusCode.NotFound.GetHashCode()); + } + + + private async Task CreateProjectGroup(bool assignUser = false) + { + var projectGroup = Domain.ProjectGroupsAggregate.ProjectGroup.Create(Fixture.Create()[..7], + Fixture.Create()[..7], + Fixture.Create()[..7], + DateTime.Now); + if (assignUser) + { + projectGroup.SetAssignedUser(Guid.NewGuid(), "Full Name", "First@email.com"); + } + projectGroup.SetProjectReference(1); + _context.ProjectGroups.Add(projectGroup); + await _context.SaveChangesAsync(); + return await _context.ProjectGroups.AsNoTracking().FirstAsync(x => x.TrustUkprn == projectGroup.TrustUkprn); + } } } diff --git a/Dfe.Academies.Academisation.WebApi/Controllers/ProjectGroupController.cs b/Dfe.Academies.Academisation.WebApi/Controllers/ProjectGroupController.cs index 2b3e9bb46..306096fea 100644 --- a/Dfe.Academies.Academisation.WebApi/Controllers/ProjectGroupController.cs +++ b/Dfe.Academies.Academisation.WebApi/Controllers/ProjectGroupController.cs @@ -101,18 +101,17 @@ public async Task> GetProjectGrouptById( return Ok(project); } - [HttpDelete("{referenceNumber:string}", Name = "DeleteProjectGroupByReference")] + [HttpDelete("{referenceNumber}", Name = "DeleteProjectGroupByReference")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task> DeleteProjectGroupByReference(string referenceNumber, CancellationToken cancellationToken) { - var result = await mediator.Send(new DeleteProjectGroupCommand(referenceNumber), cancellationToken).ConfigureAwait(false); + var result = await mediator.Send(new DeleteProjectGroupCommand(referenceNumber), cancellationToken); return result switch { CommandSuccessResult => Ok(), NotFoundCommandResult => NotFound(), - CommandValidationErrorResult validationErrorResult => new BadRequestObjectResult(validationErrorResult.ValidationErrors), _ => new InternalServerErrorObjectResult("Error serving request") }; }