Skip to content

Commit

Permalink
refactor bookingRequest into service and improve error handling + fro…
Browse files Browse the repository at this point in the history
…nt-end refactor (#114)

Also, an overhaul of the front-end booking request code. I started by trying to simplify components here but ended quite similar to what we had before... 🙈 I hope you don't mind @wigsnes ❤️ 

I just wanted to mention that the save button has been removed. 
Instead, clicking a period in the calendar books it right away. And it can be toggled back and forth as much as you want. Relying on the state in the back-end instead of local.

A loading state has been added to the button as well when toggling its state.
  • Loading branch information
SjoenH authored Apr 20, 2024
1 parent 5e33226 commit 6a41ab2
Show file tree
Hide file tree
Showing 41 changed files with 1,286 additions and 1,030 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
using kabinizer_api.Dtos.BookingRequest;
using kabinizer_api.Model;
using kabinizer_api.Services;
using kabinizer_api.Services.BookingRequest;
using kabinizer_api.Services.Export;
using kabinizer_data;
using kabinizer_data.Entities;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
Expand All @@ -12,71 +11,124 @@ namespace kabinizer_api.Controllers;

[Route("api/[controller]")]
[ApiController]
public class BookingRequestController(EntityContext entityContext, ITokenService tokenService) : ControllerBase
public class BookingRequestController(
EntityContext entityContext,
BookingRequestService bookingRequestService
) : ControllerBase
{
[HttpGet]
public IEnumerable<BookingRequest> GetBookingRequests()
[HttpGet("{id:guid}")]
[ProducesResponseType(200, Type = typeof(BookingRequestDto))]
[ProducesResponseType(404, Type = typeof(string))]
public async Task<ActionResult<BookingRequestDto>> GetBookingRequest(Guid id)
{
var currentUserId = tokenService.GetUserId();
return entityContext.BookingRequests
.Include(br => br.User)
.Include(br => br.Period)
.Where(b => b.UserId == currentUserId)
.AsEnumerable().Select(BookingRequest.FromModel);
try
{
var bookingRequest = await bookingRequestService.GetBookingRequest(id);
if (bookingRequest == null)
{
return NotFound("Booking request does not exist or does not belong to the current user");
}

return Ok(BookingRequestDto.FromModel(bookingRequest));
}
catch (Exception ex)
{
return NotFound(ex.Message);
}
}

[HttpPost]
public void AddBookingRequests([Required] IEnumerable<CreateBookingRequestDto> requests)
[HttpGet]
[ProducesResponseType(200, Type = typeof(IEnumerable<BookingRequestDto>))]
[ProducesResponseType(404, Type = typeof(string))]
public async Task<ActionResult<IEnumerable<BookingRequestDto>>> GetBookingRequests()
{
var currentUserId = tokenService.GetUserId();
var periodIds = requests.Select(e => e.PeriodId).ToList();

var periods = entityContext.Periods
.Where(p => periodIds.Contains(p.Id))
.Include(p => p.Draw)
.ToList();

if (periods.Count != periodIds.Count)
try
{
throw new Exception("One or more periods do not exist");
var bookingRequests = await bookingRequestService.GetBookingRequests();
return Ok(bookingRequests.Select(BookingRequestDto.FromModel));
}

var periodsWithoutDraw = periods.Where(p => p.Draw == null).ToList();
if (periodsWithoutDraw.Count != 0)
catch (Exception ex)
{
throw new Exception("One or more periods are not part of a draw");
return NotFound(ex.Message);
}

var periodsWithEndedDraw = periods.Where(p => p.Draw?.DeadlineEnd > DateTime.Now).ToList();
if (periodsWithEndedDraw.Count != 0)
}

[HttpGet("user/{userId:guid}")]
[ProducesResponseType(200, Type = typeof(IEnumerable<BookingRequestDto>))]
[ProducesResponseType(404, Type = typeof(string))]
public async Task<ActionResult<IEnumerable<BookingRequestDto>>> GetBookingRequestsByUser(Guid userId)
{
try
{
throw new Exception("Cannot make a booking request for a draw that has ended");
var bookingRequests = await bookingRequestService.GetBookingRequestsByUser(userId);
return Ok(bookingRequests.Select(BookingRequestDto.FromModel));
}
catch (Exception ex)
{
return NotFound(ex.Message);
}
}

[HttpGet("period/{periodId:guid}")]
[ProducesResponseType(200, Type = typeof(IEnumerable<BookingRequestDto>))]
[ProducesResponseType(404, Type = typeof(string))]
public async Task<ActionResult<IEnumerable<BookingRequestDto>>> GetBookingRequestsByPeriod(Guid periodId)
{
try
{
var bookingRequests = await bookingRequestService.GetBookingRequestsByPeriod(periodId);
if (bookingRequests.Count == 0)
{
return NotFound("No booking requests found for the period");
}

return Ok(bookingRequests.Select(BookingRequestDto.FromModel));
}
catch (Exception ex)
{
return NotFound(ex.Message);
}

var bookingRequestEntities = periodIds.Select(id => new BookingRequestEntity(currentUserId, id));
entityContext.BookingRequests.AddRange(bookingRequestEntities);
entityContext.SaveChanges();
}

[HttpDelete]
public bool DeleteBookingRequests([Required] IEnumerable<Guid> requests)
[HttpPost]
[ProducesResponseType(200, Type = typeof(BookingRequestDto))]
[ProducesResponseType(400, Type = typeof(string))]
public async Task<ActionResult<IEnumerable<BookingRequestDto>>> AddBookingRequests([Required] IEnumerable<CreateBookingRequestDto> requests)
{
var currentUserId = tokenService.GetUserId();
try
{
var bookingRequest = await bookingRequestService.AddBookingRequest(requests.First());
if (bookingRequest == null)
{
return BadRequest("Booking request could not be added");
}

foreach (Guid requestId in requests)
return Ok(BookingRequestDto.FromModel(bookingRequest));
}
catch (Exception e)
{
BookingRequestEntity entityToRemove = entityContext.BookingRequests.Single(br => br.Id == requestId);
return BadRequest(e.Message);
}
}

if (entityToRemove.UserId != currentUserId)
[HttpDelete]
[ProducesResponseType(200, Type = typeof(string))]
[ProducesResponseType(400, Type = typeof(string))]
public async Task<IActionResult> DeleteBookingRequests([Required] IEnumerable<Guid> requests)
{
try
{
foreach (Guid request in requests)
{
throw new Exception("You cannot remove a booking request for another user");
await bookingRequestService.DeleteBookingRequest(request);
}

entityContext.BookingRequests.Remove(entityToRemove);
return Ok("Booking requests deleted");
}
catch (Exception e)
{
return BadRequest(e.Message);
}

entityContext.SaveChanges();
return true;
}

[HttpGet]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using kabinizer_data.Entities;

namespace kabinizer_api.Dtos.BookingRequest;

public class BookingRequestDto
{
public Guid Id { get; set; }
public Guid UserId { get; set; }
public Guid PeriodId { get; set; }
public DateTime CreatedDate { get; set; }
public Guid CreatedBy { get; set; }
public DateTime? UpdatedDate { get; set; }
public Guid? UpdatedBy { get; set; }

public static BookingRequestDto FromModel(BookingRequestEntity e)
{
return new BookingRequestDto
{
Id = e.Id,
UserId = e.UserId,
PeriodId = e.PeriodId,
CreatedDate = e.CreatedDate,
CreatedBy = e.CreatedBy,
UpdatedDate = e.UpdatedDate,
UpdatedBy = e.UpdatedBy
};
}
}
4 changes: 2 additions & 2 deletions kabinizer-back-end/kabinizer-api/Model/Period.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

namespace kabinizer_api.Model;

public record Period(Guid Id, DateTime PeriodStart, DateTime PeriodEnd, string Title, Guid DrawId)
public record Period(Guid Id, DateTime PeriodStart, DateTime PeriodEnd, string? Title, Guid DrawId)
{
public Period(DateTime periodStart, DateTime periodEnd, string title, Guid DrawId)
public Period(DateTime periodStart, DateTime periodEnd, string? title, Guid DrawId)
: this(Guid.NewGuid(), periodStart, periodEnd, title, DrawId)
{
}
Expand Down
2 changes: 1 addition & 1 deletion kabinizer-back-end/kabinizer-api/Model/User.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace kabinizer_api.Model;

public record User(Guid Id, string Name)
public record User(Guid Id, string? Name)
{
public static User FromEntity(UserEntity u)
{
Expand Down
3 changes: 3 additions & 0 deletions kabinizer-back-end/kabinizer-api/Program.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
using kabinizer_api.Services;
using kabinizer_api.Services.BookingRequest;
using kabinizer_api.Services.Draw;
using kabinizer_data;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using Microsoft.Identity.Web;
using Microsoft.OpenApi.Models;
using System.Text.Json.Serialization;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors();
builder.Services.AddScoped<DrawService>();
builder.Services.AddScoped<PeriodService>();
builder.Services.AddScoped<BookingRequestService>();

// Add services to the container.
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
using kabinizer_api.Dtos.BookingRequest;
using kabinizer_data;
using kabinizer_data.Entities;
using Microsoft.EntityFrameworkCore;

namespace kabinizer_api.Services.BookingRequest;

public class BookingRequestService(EntityContext entityContext, ITokenService tokenService)
{
private async Task<BookingRequestEntity?> GetBookingRequestById(Guid bookingRequestId)
{
return await entityContext.BookingRequests
.Include(br => br.User)
.Include(br => br.Period)
.FirstOrDefaultAsync(b => b.Id == bookingRequestId && b.UserId == tokenService.GetUserId())
?? throw new InvalidOperationException("No matching booking request found.");
}

public async Task<BookingRequestEntity?> GetBookingRequest(Guid bookingRequestId)
{
BookingRequestEntity? id = await GetBookingRequestById(bookingRequestId);
if (id == null)
{
throw new Exception("Booking request does not exist or does not belong to the current user");
}

return id;
}

public async Task<IEnumerable<BookingRequestEntity>> GetBookingRequests()
{
var bookingRequestEntities = await entityContext.BookingRequests
.Include(br => br.User)
.Include(br => br.Period)
.Where(b => b.UserId == tokenService.GetUserId())
.ToListAsync();
if (bookingRequestEntities == null)
{
throw new Exception("No booking requests found for the current user");
}

return bookingRequestEntities;
}

public async Task<BookingRequestEntity?> AddBookingRequest(CreateBookingRequestDto request)
{
var period = await entityContext.Periods
.Include(p => p.Draw)
.FirstOrDefaultAsync(p => p.Draw != null && p.Id == request.PeriodId && p.Draw.DeadlineEnd >= DateTime.Now);

if (period == null)
{
throw new Exception("Period does not exist, is not part of a draw, or the draw has ended");
}

var user = await entityContext.Users.FirstOrDefaultAsync(u => u.Id == tokenService.GetUserId());
if (user == null)
{
throw new Exception("User does not exist");
}

var bookingRequest = new BookingRequestEntity
{
PeriodId = period.Id,
UserId = user.Id,
CreatedDate = DateTime.Now,
CreatedBy = user.Id,
User = user,
Period = period
};
entityContext.BookingRequests.Add(bookingRequest);
await entityContext.SaveChangesAsync();
return bookingRequest;
}

public async Task DeleteBookingRequest(Guid bookingRequestId)
{
var bookingRequest = await entityContext.BookingRequests.FirstOrDefaultAsync(b => b.Id == bookingRequestId);
if (bookingRequest == null)
{
throw new Exception(
$"Booking request with id {bookingRequestId} does not exist or does not belong to the current user");
}

entityContext.BookingRequests.Remove(bookingRequest);
await entityContext.SaveChangesAsync();
}

public async Task<List<BookingRequestEntity>> GetBookingRequestsByUser(Guid userId)
{
var bookingRequests = await entityContext.BookingRequests
.Include(br => br.User)
.Include(br => br.Period)
.Where(b => b.UserId == userId)
.ToListAsync();
if (bookingRequests == null)
{
throw new Exception("No booking requests found for the user");
}

return bookingRequests;
}

public async Task<List<BookingRequestEntity>> GetBookingRequestsByPeriod(Guid periodId)
{
var bookingRequests = await entityContext.BookingRequests
.Include(br => br.User)
.Include(br => br.Period)
.Where(b => b.PeriodId == periodId)
.ToListAsync();

if (bookingRequests == null)
{
throw new Exception("No booking requests found for the period");
}

return bookingRequests;
}
}
Loading

0 comments on commit 6a41ab2

Please sign in to comment.