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
153 changes: 153 additions & 0 deletions API/Controllers/BorrowingController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
using Application.Commands.BorrowingCommands.BorrowBookCopy;
using Application.Commands.BorrowingCommands.DeleteBorrowing;
using Application.Commands.BorrowingCommands.ReturnBookCopy;
using Application.Dtos.BorrowingDtos;
using Application.Queries.BorrowingQueries.GetAllBorrowings;
using Application.Queries.BorrowingQueries.GetBorrowingById;
using Application.Queries.BorrowingQueries.GetUserBorrowings;
using MediatR;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
using System.Linq.Expressions;

namespace API.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class BorrowingController(IMediator mediator) : ControllerBase
{
private readonly IMediator _mediator = mediator;


[HttpGet]
[Route("GetAll")]
[SwaggerOperation(Description = "Gets all borrowings from collection")]
[SwaggerResponse(200, "Successfully retrieved all borrowings")]
[SwaggerResponse(404, "Borrowings not found.")]
public async Task<IActionResult> GetAllBorrowings()
{
try
{
var operationResult = await _mediator.Send(new GetAllBorrowingsQuery());
if (operationResult.IsSuccess)
{
return Ok(operationResult.Data);
}
return BadRequest(new { message = operationResult.Message, errors = operationResult.ErrorMessage });
}
catch (Exception ex)
{
return StatusCode(500, ex.Message);
}
}

[HttpGet("GetById{id}")]
[SwaggerOperation(Description = "Gets borrowing by Id from collection")]
[SwaggerResponse(200, "Successfully retrieved Borrowing")]
[SwaggerResponse(404, "Borrowing not found.")]
public async Task<IActionResult> GetBorrowingById(int id)
{
try
{
var operationResult = await _mediator.Send(new GetBorrowingByIdQuery(id));
if (operationResult.IsSuccess)
{
return Ok(operationResult.Data);
}
return BadRequest(new { message = operationResult.Message, errors = operationResult.ErrorMessage });
}
catch (Exception ex)
{
return StatusCode(500, ex.Message);
}
}

[HttpGet("GetUserBorrowings{id}")]
[SwaggerOperation(Description = "Gets user borrowing by userId from collection")]
[SwaggerResponse(200, "Successfully retrieved users Borrowings")]
[SwaggerResponse(404, "Borrowings not found.")]
public async Task<IActionResult> GetBorrowingById(Guid id)
{
try
{
var operationResult = await _mediator.Send(new GetUserBorrowingsQuery(id));
if (operationResult.IsSuccess)
{
return Ok(operationResult.Data);
}
return BadRequest(new { message = operationResult.Message, errors = operationResult.ErrorMessage });
}
catch (Exception ex)
{
return StatusCode(500, ex.Message);
}
}

[HttpPost]
[Route("BorrowBook")]
[SwaggerOperation(Description = "User borrows a book from the database")]
[SwaggerResponse(201, "Book copy successfully borrowed")]
[SwaggerResponse(400, "Invalid input data")]
public async Task<IActionResult> AddBorrowing([FromBody] BorrowBookDto dto)
{
try
{
var operationResult = await _mediator.Send(new BorrowBookCopyCommand(dto));
if (operationResult.IsSuccess)
{
return Ok(operationResult.Data);
}
return BadRequest(new { message = operationResult.Message, errors = operationResult.ErrorMessage });
}
catch (Exception ex)
{
return StatusCode(500, ex.Message);
}
}

[HttpPut]
[Route("ReturnBookCopy{borrowingId}")]
[SwaggerOperation(Description = "Returns book")]
[SwaggerResponse(201, "BookCopy successfully returned")]
[SwaggerResponse(400, "Invalid input data")]
public async Task<IActionResult> ReturnBook(int borrowingId)
{
try
{
var operationResult = await _mediator.Send(new ReturnBookCopyCommand(borrowingId));
if (operationResult.IsSuccess)
{
return StatusCode(201, operationResult.Data);
}
return BadRequest(new { message = operationResult.Message, errors = operationResult.ErrorMessage });
}
catch (Exception ex)
{
return StatusCode(500, ex.Message);
}
}

[HttpDelete]
[Route("Delete/{id}")]
[SwaggerOperation(Description = "Deletes borrowing from collection")]
[SwaggerResponse(201, "Successfully deleted borrowing.")]
[SwaggerResponse(400, "Invalid input data")]
public async Task<IActionResult> DeleteBorrowing(int id)
{
try
{
var operationResult = await _mediator.Send(new DeleteBorrowingCommand(id));
if (operationResult.IsSuccess)
{
return StatusCode(201, operationResult.Data);
}
return BadRequest(new { message = operationResult.Message, errors = operationResult.ErrorMessage });
}
catch (Exception ex)
{
return StatusCode(500, ex.Message);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using Application.Dtos.BorrowingDtos;
using Application.Models;
using MediatR;

namespace Application.Commands.BorrowingCommands.BorrowBookCopy
{
public class BorrowBookCopyCommand(BorrowBookDto dto) : IRequest<OperationResult<GetBorrowingDto>>
{
public BorrowBookDto Dto { get; set; } = dto;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using Application.Dtos.BorrowingDtos;
using Application.Interfaces.RepositoryInterfaces;
using Application.Models;
using AutoMapper;
using Domain.Entities.Core;
using Domain.Entities.Locations;
using Domain.Entities.Transactions;
using MediatR;

namespace Application.Commands.BorrowingCommands.BorrowBookCopy
{
internal class BorrowBookCopyCommandHandler : IRequestHandler<BorrowBookCopyCommand, OperationResult<GetBorrowingDto>>
{
private readonly IGenericRepository<Borrowing, int> _borrowingRepository;
private readonly IGenericRepository<BookCopy, Guid> _bookRepository;
private readonly IGenericRepository<User, string> _userRepository;
private readonly IMapper _mapper;
public BorrowBookCopyCommandHandler(IGenericRepository<Borrowing, int> borrowingRepository, IMapper mapper,
IGenericRepository<BookCopy, Guid> bookRepository, IGenericRepository<User, string> userRepository)
{
_borrowingRepository = borrowingRepository;
_bookRepository = bookRepository;
_userRepository = userRepository;
_mapper = mapper;
}

public async Task<OperationResult<GetBorrowingDto>> Handle(BorrowBookCopyCommand request, CancellationToken cancellationToken)
{
try
{
// Retrieve the book copy
var bookCopy = await _bookRepository.GetByIdAsync(request.Dto.CopyId);
if (bookCopy == null || bookCopy.Status != "Available")
{
return OperationResult<GetBorrowingDto>.Failure("Book copy is not available");
}

// Retrieve the user
var user = await _userRepository.GetByIdAsync(request.Dto.UserId.ToString());
if (user == null)
{
return OperationResult<GetBorrowingDto>.Failure("User not found");
}

// Update the bookCopy status
bookCopy.Status = "Borrowed";
var succesfulUpdate = await _bookRepository.UpdateAsync(bookCopy);
if (succesfulUpdate is null)
{
return OperationResult<GetBorrowingDto>.Failure("Operation failed");
}

// Create a new borrowing record
Borrowing borrowing = new()
{
Status = "Active",
BorrowDate = DateTime.Now,
DueDate = DateTime.Now.AddDays(14),
UserId = request.Dto.UserId.ToString(),
CopyId = request.Dto.CopyId,
User = user,
};

var successfulUpdate = await _bookRepository.UpdateAsync(bookCopy);
if (successfulUpdate is null)
{
return OperationResult<GetBorrowingDto>.Failure("Operation failed");
}

var successfulCreation = await _borrowingRepository.AddAsync(borrowing);
if (successfulCreation is null)
{
return OperationResult<GetBorrowingDto>.Failure("Operation failed");
}

var mappedBorrowing = _mapper.Map<GetBorrowingDto>(successfulCreation);
return OperationResult<GetBorrowingDto>.Success(mappedBorrowing);
}

catch (Exception ex)
{
throw new ApplicationException("An error occurred.", ex);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Application.Models;
using MediatR;

namespace Application.Commands.BorrowingCommands.DeleteBorrowing
{
public class DeleteBorrowingCommand(int id) : IRequest<OperationResult<bool>>
{
public int Id { get; set; } = id;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Application.Interfaces.RepositoryInterfaces;
using Application.Models;
using Domain.Entities.Transactions;
using MediatR;

namespace Application.Commands.BorrowingCommands.DeleteBorrowing
{
internal class DeleteBorrowingCommandHandler(IGenericRepository<Borrowing, int> repository) : IRequestHandler<DeleteBorrowingCommand, OperationResult<bool>>
{

public async Task<OperationResult<bool>> Handle(DeleteBorrowingCommand request, CancellationToken cancellationToken)
{
try
{
var successfulDeletion = await repository.DeleteAsync(request.Id);
if (successfulDeletion is false)
{
return OperationResult<bool>.Failure("Operation failed");
}
else
{
return OperationResult<bool>.Success(successfulDeletion);
}
}
catch (Exception e)
{
return OperationResult<bool>.Failure(e.Message);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using Application.Dtos.BorrowingDtos;
using Application.Models;
using MediatR;

namespace Application.Commands.BorrowingCommands.ReturnBookCopy
{
public class ReturnBookCopyCommand(int borrowId) : IRequest<OperationResult<GetBorrowingDto>>
{
public int BorrowId { get; set; } = borrowId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using Application.Dtos.BorrowingDtos;
using Application.Interfaces.RepositoryInterfaces;
using Application.Models;
using AutoMapper;
using Domain.Entities.Core;
using Domain.Entities.Locations;
using Domain.Entities.Transactions;
using MediatR;

namespace Application.Commands.BorrowingCommands.ReturnBookCopy
{
internal class ReturnBookCopyCommandHandler(IGenericRepository<Borrowing, int> borrowingRepository, IGenericRepository<BookCopy, Guid> bookRepository, IMapper mapper) : IRequestHandler<ReturnBookCopyCommand, OperationResult<GetBorrowingDto>>
{
private readonly IGenericRepository<BookCopy, Guid> _bookRepository = bookRepository;
private readonly IGenericRepository<Borrowing, int> _borrowingRepository = borrowingRepository;
private readonly IMapper _mapper = mapper;

public async Task<OperationResult<GetBorrowingDto>> Handle(ReturnBookCopyCommand request, CancellationToken cancellationToken)
{
try
{
// Get the borrowing record
var borrowing = await _borrowingRepository.GetByIdAsync(request.BorrowId);
if (borrowing is null)
{
return OperationResult<GetBorrowingDto>.Failure("Borrowing record not found");
}

// Get the book copy
var bookCopy = await _bookRepository.GetByIdAsync(borrowing.CopyId);
if (bookCopy is null)
{
return OperationResult<GetBorrowingDto>.Failure("Book copy not found");
}

// Update the borrowing record
borrowing.Status = "Returned";
borrowing.ReturnDate = DateTime.Now;

var successfulUpdate = await _borrowingRepository.UpdateAsync(borrowing);
if (successfulUpdate is null)
{
return OperationResult<GetBorrowingDto>.Failure("Operation failed");
}

// Update the book copy status
bookCopy.Status = "Available";
var successfulBookCopyUpdate = await _bookRepository.UpdateAsync(bookCopy);
if (successfulBookCopyUpdate is null)
{
return OperationResult<GetBorrowingDto>.Failure("Operation failed");
}

var mappedBorrowing = _mapper.Map<GetBorrowingDto>(successfulUpdate);
return OperationResult<GetBorrowingDto>.Success(mappedBorrowing);
}
catch (Exception e)
{
return OperationResult<GetBorrowingDto>.Failure(e.Message);
}
}
}
}
1 change: 1 addition & 0 deletions Application/DependencyInjection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public static IServiceCollection AddApplication(this IServiceCollection services
services.AddAutoMapper(typeof(ReviewMappingProfiles).Assembly);
services.AddAutoMapper(typeof(PublisherMappingProfiles).Assembly);
services.AddAutoMapper(typeof(ReservationMappingProfiles).Assembly);
services.AddAutoMapper(typeof(BorrowingMappingProfiles).Assembly);

// Register validators
services.AddValidatorsFromAssemblyContaining<AddAuthorCommandValidator>();
Expand Down
9 changes: 9 additions & 0 deletions Application/Dtos/BorrowingDtos/BorrowBookDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

namespace Application.Dtos.BorrowingDtos
{
public class BorrowBookDto
{
public Guid UserId { get; set; }
public Guid CopyId { get; set; }
}
}
Loading
Loading