diff --git a/dotnet/AipsCore/Application/Models/Whiteboard/Command/DeleteWhiteboard/DeleteWhiteboardCommand.cs b/dotnet/AipsCore/Application/Models/Whiteboard/Command/DeleteWhiteboard/DeleteWhiteboardCommand.cs new file mode 100644 index 0000000..af6ccab --- /dev/null +++ b/dotnet/AipsCore/Application/Models/Whiteboard/Command/DeleteWhiteboard/DeleteWhiteboardCommand.cs @@ -0,0 +1,5 @@ +using AipsCore.Application.Abstract.Command; + +namespace AipsCore.Application.Models.Whiteboard.Command.DeleteWhiteboard; + +public record DeleteWhiteboardCommand(string WhiteboardId) : ICommand; \ No newline at end of file diff --git a/dotnet/AipsCore/Application/Models/Whiteboard/Command/DeleteWhiteboard/DeleteWhiteboardCommandHandler.cs b/dotnet/AipsCore/Application/Models/Whiteboard/Command/DeleteWhiteboard/DeleteWhiteboardCommandHandler.cs new file mode 100644 index 0000000..f6e3d16 --- /dev/null +++ b/dotnet/AipsCore/Application/Models/Whiteboard/Command/DeleteWhiteboard/DeleteWhiteboardCommandHandler.cs @@ -0,0 +1,48 @@ +using AipsCore.Application.Abstract.Command; +using AipsCore.Application.Abstract.UserContext; +using AipsCore.Domain.Abstract; +using AipsCore.Domain.Abstract.Validation; +using AipsCore.Domain.Common.Validation; +using AipsCore.Domain.Models.Whiteboard.External; +using AipsCore.Domain.Models.Whiteboard.Validation; +using AipsCore.Domain.Models.Whiteboard.ValueObjects; + +namespace AipsCore.Application.Models.Whiteboard.Command.DeleteWhiteboard; + +public class DeleteWhiteboardCommandHandler : ICommandHandler +{ + private readonly IUserContext _userContext; + private readonly IWhiteboardRepository _whiteboardRepository; + private readonly IUnitOfWork _unitOfWork; + + public DeleteWhiteboardCommandHandler( + IUserContext userContext, + IWhiteboardRepository whiteboardRepository, + IUnitOfWork unitOfWork) + { + _userContext = userContext; + _whiteboardRepository = whiteboardRepository; + _unitOfWork = unitOfWork; + } + + public async Task Handle(DeleteWhiteboardCommand command, CancellationToken cancellationToken = default) + { + var whiteboardId = new WhiteboardId(command.WhiteboardId); + var userId = _userContext.GetCurrentUserId(); + + var whiteboard = await _whiteboardRepository.GetByIdAsync(whiteboardId, cancellationToken); + + if (whiteboard is null) + { + throw new ValidationException(WhiteboardErrors.NotFound(whiteboardId)); + } + + if (!whiteboard.IsOwnedBy(userId)) + { + throw new ValidationException(WhiteboardErrors.OnlyOwnerCanDeleteWhiteboard(userId)); + } + + await _whiteboardRepository.SoftDeleteAsync(whiteboardId, cancellationToken); + await _unitOfWork.SaveChangesAsync(cancellationToken); + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/Whiteboard/External/IWhiteboardRepository.cs b/dotnet/AipsCore/Domain/Models/Whiteboard/External/IWhiteboardRepository.cs index 0fcfe36..1dc3125 100644 --- a/dotnet/AipsCore/Domain/Models/Whiteboard/External/IWhiteboardRepository.cs +++ b/dotnet/AipsCore/Domain/Models/Whiteboard/External/IWhiteboardRepository.cs @@ -6,4 +6,5 @@ namespace AipsCore.Domain.Models.Whiteboard.External; public interface IWhiteboardRepository : IAbstractRepository { Task WhiteboardCodeExists(WhiteboardCode whiteboardCode); + Task SoftDeleteAsync(WhiteboardId id, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/Whiteboard/Validation/WhiteboardErrors.cs b/dotnet/AipsCore/Domain/Models/Whiteboard/Validation/WhiteboardErrors.cs index 262e72b..ad1070e 100644 --- a/dotnet/AipsCore/Domain/Models/Whiteboard/Validation/WhiteboardErrors.cs +++ b/dotnet/AipsCore/Domain/Models/Whiteboard/Validation/WhiteboardErrors.cs @@ -38,4 +38,12 @@ public class WhiteboardErrors : AbstractErrors return CreateValidationError(code, message); } + + public static ValidationError OnlyOwnerCanDeleteWhiteboard(UserId currentUserId) + { + string code = "only_owner_can_delete_whiteboard"; + string message = $"Only owner of whiteboard can delete the whiteboard. Current user id: '{currentUserId}' is not the owner."; + + return CreateValidationError(code, message); + } } \ No newline at end of file diff --git a/dotnet/AipsCore/Domain/Models/Whiteboard/Whiteboard.IsOwnedBy.cs b/dotnet/AipsCore/Domain/Models/Whiteboard/Whiteboard.IsOwnedBy.cs new file mode 100644 index 0000000..dcbdfca --- /dev/null +++ b/dotnet/AipsCore/Domain/Models/Whiteboard/Whiteboard.IsOwnedBy.cs @@ -0,0 +1,11 @@ +using AipsCore.Domain.Models.User.ValueObjects; + +namespace AipsCore.Domain.Models.Whiteboard; + +public partial class Whiteboard +{ + public bool IsOwnedBy(UserId userId) + { + return WhiteboardOwnerId.IdValue == userId.IdValue; + } +} \ No newline at end of file diff --git a/dotnet/AipsCore/Infrastructure/Persistence/Whiteboard/WhiteboardRepository.cs b/dotnet/AipsCore/Infrastructure/Persistence/Whiteboard/WhiteboardRepository.cs index d606cee..29518f9 100644 --- a/dotnet/AipsCore/Infrastructure/Persistence/Whiteboard/WhiteboardRepository.cs +++ b/dotnet/AipsCore/Infrastructure/Persistence/Whiteboard/WhiteboardRepository.cs @@ -1,3 +1,4 @@ +using AipsCore.Domain.Models.Whiteboard.Enums; using AipsCore.Domain.Models.Whiteboard.External; using AipsCore.Domain.Models.Whiteboard.ValueObjects; using AipsCore.Infrastructure.Persistence.Abstract; @@ -61,4 +62,16 @@ public class WhiteboardRepository { return await Context.Whiteboards.AnyAsync(w => w.Code == whiteboardCode.CodeValue); } + + public async Task SoftDeleteAsync(WhiteboardId id, CancellationToken cancellationToken = default) + { + var entity = await Context.Whiteboards.FindAsync([new Guid(id.IdValue)], cancellationToken); + + if (entity != null) + { + entity.State = WhiteboardState.Deleted; + entity.DeletedAt = DateTime.UtcNow; + Context.Whiteboards.Update(entity); + } + } } \ No newline at end of file diff --git a/dotnet/AipsWebApi/Controllers/WhiteboardController.cs b/dotnet/AipsWebApi/Controllers/WhiteboardController.cs index 0bbc8d3..fd418ae 100644 --- a/dotnet/AipsWebApi/Controllers/WhiteboardController.cs +++ b/dotnet/AipsWebApi/Controllers/WhiteboardController.cs @@ -1,5 +1,6 @@ using AipsCore.Application.Abstract; using AipsCore.Application.Models.Whiteboard.Command.CreateWhiteboard; +using AipsCore.Application.Models.Whiteboard.Command.DeleteWhiteboard; using AipsCore.Application.Models.Whiteboard.Query.GetRecentWhiteboards; using AipsCore.Application.Models.Whiteboard.Query.GetWhiteboard; using AipsCore.Application.Models.Whiteboard.Query.GetWhiteboardHistory; @@ -41,6 +42,15 @@ public class WhiteboardController : ControllerBase return Ok(whiteboard); } + [Authorize] + [HttpDelete("{whiteboardId}")] + public async Task DeleteWhiteboard([FromRoute] string whiteboardId, CancellationToken cancellationToken) + { + await _dispatcher.Execute(new DeleteWhiteboardCommand(whiteboardId), cancellationToken); + return Ok(); + } + + [Authorize] [HttpGet("history")] public async Task>> GetWhiteboardHistory(CancellationToken cancellationToken)