Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions src/Application/Common/Models/Dtos/DepartmentDto.cs
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
using Application.Common.Mappings;
using Application.Common.Models.Dtos.Physical;
using AutoMapper;
using Domain.Entities;

namespace Application.Common.Models.Dtos;
Expand All@@ -8,5 +8,12 @@ public class DepartmentDto : IMapFrom<Department>
{
public Guid Id { get; set; }
public string Name { get; set; } = null!;
public RoomDto? Room { get; set; }
public Guid? RoomId { get; set; }

public void Mapping(Profile profile)
{
profile.CreateMap<Department, DepartmentDto>()
.ForMember(dest => dest.RoomId,
opt => opt.MapFrom(src => src.Room!.Id));
}
}
11 changes: 9 additions & 2 deletions src/Application/Common/Models/Dtos/Physical/RoomDto.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,9 +10,16 @@ public class RoomDto : IMapFrom<Room>
public Guid Id { get; set; }
public string Name { get; set; } = null!;
public string? Description { get; set; }
public StaffDto? Staff { get; set; }
public DepartmentDto Department { get; set; }
public Guid? StaffId { get; set; }
public DepartmentDto? Department { get; set; }
public int Capacity { get; set; }
public int NumberOfLockers { get; set; }
public bool IsAvailable { get; set; }

public void Mapping(Profile profile)
{
profile.CreateMap<Room, RoomDto>()
.ForMember(dest => dest.StaffId,
opt => opt.MapFrom(src => src.Staff!.Id));
}
}
4 changes: 2 additions & 2 deletions src/Application/Lockers/Commands/DisableLocker.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -26,12 +26,12 @@ public record Command : IRequest<LockerDto>
public Guid LockerId { get; init; }
}

public class RemoveLockerCommandHandler : IRequestHandler<Command, LockerDto>
public class CommandHandler : IRequestHandler<Command, LockerDto>
{
private readonly IApplicationDbContext _context;
private readonly IMapper _mapper;

public RemoveLockerCommandHandler(IApplicationDbContext context, IMapper mapper)
public CommandHandler(IApplicationDbContext context, IMapper mapper)
{
_context = context;
_mapper = mapper;
Expand Down
57 changes: 57 additions & 0 deletions src/Application/Lockers/Commands/RemoveLocker.cs
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,69 @@
using Application.Common.Exceptions;
using Application.Common.Interfaces;
using Application.Common.Models.Dtos.Physical;
using AutoMapper;
using FluentValidation;
using MediatR;
using Microsoft.EntityFrameworkCore;

namespace Application.Lockers.Commands;

public class RemoveLocker
{
public class Validator : AbstractValidator<Command>
{
public Validator()
{
RuleLevelCascadeMode = CascadeMode.Stop;

RuleFor(x => x.LockerId)
.NotEmpty().WithMessage("LockerId is required.");
}
}

public record Command : IRequest<LockerDto>
{
public Guid LockerId { get; init; }
}

public class CommandHandler : IRequestHandler<Command, LockerDto>
{
private readonly IApplicationDbContext _context;
private readonly IMapper _mapper;

public CommandHandler(IApplicationDbContext context, IMapper mapper)
{
_context = context;
_mapper = mapper;
}

public async Task<LockerDto> Handle(Command request, CancellationToken cancellationToken)
{
var locker = await _context.Lockers
.Include(x => x.Room)
.FirstOrDefaultAsync(x => x.Id.Equals(request.LockerId), cancellationToken);

if (locker is null)
{
throw new KeyNotFoundException("Locker does not exist.");
}

var canNotRemove = await _context.Documents
.CountAsync(x => x.Folder!.Locker.Id.Equals(request.LockerId), cancellationToken)
> 0;

if (canNotRemove)
{
throw new InvalidOperationException("Locker cannot be removed because it contains documents.");
}

var room = locker.Room;

var result = _context.Lockers.Remove(locker);
room.NumberOfLockers -= 1;
_context.Rooms.Update(room);
await _context.SaveChangesAsync(cancellationToken);
return _mapper.Map<LockerDto>(result.Entity);
}
}
}
2 changes: 2 additions & 0 deletions src/Application/Rooms/Commands/AddRoom.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -82,6 +82,8 @@ public async Task<RoomDto> Handle(Command request, CancellationToken cancellatio
NumberOfLockers = 0,
Capacity = request.Capacity,
Department = department,
DepartmentId = request.DepartmentId,
IsAvailable = true,
};
var result = await _context.Rooms.AddAsync(entity, cancellationToken);
await _context.SaveChangesAsync(cancellationToken);
Expand Down
4 changes: 2 additions & 2 deletions src/Application/Rooms/Commands/DisableRoom.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -26,12 +26,12 @@ public record Command : IRequest<RoomDto>
public Guid RoomId { get; init; }
}

public class DisableRoomCommandHandler : IRequestHandler<Command, RoomDto>
public class CommandHandler : IRequestHandler<Command, RoomDto>
{
private readonly IApplicationDbContext _context;
private readonly IMapper _mapper;

public DisableRoomCommandHandler(IApplicationDbContext context, IMapper mapper)
public CommandHandler(IApplicationDbContext context, IMapper mapper)
{
_context = context;
_mapper = mapper;
Expand Down
4 changes: 2 additions & 2 deletions src/Application/Rooms/Commands/RemoveRoom.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,12 +25,12 @@ public record Command : IRequest<RoomDto>
public Guid RoomId { get; init; }
}

public class RemoveRoomCommandHandler : IRequestHandler<Command, RoomDto>
public class CommandHandler : IRequestHandler<Command, RoomDto>
{
private readonly IApplicationDbContext _context;
private readonly IMapper _mapper;

public RemoveRoomCommandHandler(IApplicationDbContext context, IMapper mapper)
public CommandHandler(IApplicationDbContext context, IMapper mapper)
{
_context = context;
_mapper = mapper;
Expand Down
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
using Application.Lockers.Commands;
using Application.Rooms.Commands;
using Domain.Entities;
using Domain.Entities.Physical;
using FluentAssertions;
using Xunit;

namespace Application.Tests.Integration.Lockers.Commands;

public class RemoveLockerTests : BaseClassFixture
{
public RemoveLockerTests(CustomApiFactory apiFactory) : base(apiFactory)
{
}

[Fact]
public async Task ShouldRemoveLocker_WhenLockerHasNoDocuments()
{
// Arrange
var department = CreateDepartment();
var locker = CreateLocker();
var room = CreateRoom(department, locker);
await Add(room);

var command = new RemoveLocker.Command()
{
LockerId = locker.Id,
};

// Act
var result = await SendAsync(command);

// Assert
result.Id.Should().Be(locker.Id);
var removedLocker = await FindAsync<Locker>(locker.Id);
removedLocker.Should().BeNull();

// Cleanup
Remove(await FindAsync<Room>(room.Id));
Remove(await FindAsync<Department>(department.Id));
}

[Fact]
public async Task ShouldThrowConflictException_WhenRoomStillHasDocuments()
{
// Arrange
var department = CreateDepartment();
var documents = CreateNDocuments(1);
var folder = CreateFolder(documents);
var locker = CreateLocker(folder);
var room = CreateRoom(department, locker);
await AddAsync(room);

var command = new RemoveLocker.Command()
{
LockerId = locker.Id,
};

// Act
var action = async () => await SendAsync(command);

// Assert
await action.Should().ThrowAsync<InvalidOperationException>()
.WithMessage("Locker cannot be removed because it contains documents.");

// Cleanup
Remove(documents.First());
Remove(folder);
Remove(locker);
Remove(room);
Remove(await FindAsync<Department>(department.Id));
}

[Fact]
public async Task ShouldThrowKeyNotFoundException_WhenLockerDoesNotExist()
{
// Arrange
var command = new RemoveLocker.Command()
{
LockerId = Guid.NewGuid(),
};

// Act
var action = async () => await SendAsync(command);

// Assert
await action.Should().ThrowAsync<KeyNotFoundException>()
.WithMessage("Locker does not exist.");
}
}