Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

53 edit bookings #152

Merged
merged 11 commits into from
Jan 17, 2023
58 changes: 57 additions & 1 deletion src/deskstar-backend/Deskstar/Controllers/BookingController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ public IActionResult CreateBooking([FromBody] BookingRequest bookingRequest)
[HttpDelete("{bookingId}")]
[Authorize]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[Produces("application/json")]
Expand All @@ -240,7 +241,62 @@ public IActionResult DeleteBooking(string bookingId)
{
"User not found" => NotFound(e.Message),
"Booking not found" => NotFound(e.Message),
"You are not allowed to delete this booking" => BadRequest(e.Message),
"You are not allowed to delete this booking" => Forbid(e.Message),
_ => Problem(statusCode: 500)
};
}
}

/// <summary>
/// Updates a Booking for Token-User
/// </summary>
/// <returns>Updated Booking in JSON Format</returns>
/// <remarks>
/// Sample request:
/// Put /bookings/{bookingId} with JWT Token
/// </remarks>
///
/// <response code="200">Returns the updated booking</response>
/// <response code="404">User not found</response>
/// <response code="404">Booking not found</response>
/// <response code="403">User is not allowed to update this booking</response>
/// <response code="409">Desk is not available at that time</response>
/// <response code="400">Bad Request</response>
[HttpPut("{bookingId}")]
[Authorize]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[Produces("application/json")]
public IActionResult UpdateBooking(string bookingId, [FromBody] UpdateBookingRequest updateBookingRequest)
{
if (updateBookingRequest.StartTime.Equals(DateTime.MinValue) || updateBookingRequest.EndTime.Equals(DateTime.MinValue))
{
return BadRequest("Required fields are missing");
}

var userId = RequestInteractions.ExtractIdFromRequest(Request);

// ToDo: require Frontend to Use Universaltime
updateBookingRequest.StartTime = updateBookingRequest.StartTime.ToLocalTime();
updateBookingRequest.EndTime = updateBookingRequest.EndTime.ToLocalTime();

try
{
var booking = _bookingUsecases.UpdateBooking(userId, new Guid(bookingId), updateBookingRequest);
return Ok();
}
catch (Exception e)
{
_logger.LogError(e, e.Message);
return e.Message switch
{
"User not found" => NotFound(e.Message),
"Booking not found" => NotFound(e.Message),
"You are not allowed to update this booking" => Forbid(e.Message),
"Time slot not available" => Conflict(e.Message),
_ => Problem(statusCode: 500)
};
}
Expand Down
10 changes: 10 additions & 0 deletions src/deskstar-backend/Deskstar/Models/UpdateBookingRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.ComponentModel.DataAnnotations;

namespace Deskstar.Models;
public class UpdateBookingRequest
{
[Required]
public DateTime StartTime { get; set; }
[Required]
public DateTime EndTime { get; set; }
}
36 changes: 36 additions & 0 deletions src/deskstar-backend/Deskstar/Usecases/BookingUsecases.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public interface IBookingUsecases
public Booking CreateBooking(Guid userId, BookingRequest bookingRequest);
int CountValidBookings(Guid userId, string direction, DateTime start, DateTime end);
public Booking DeleteBooking(Guid userId, Guid bookingId);
public Booking UpdateBooking(Guid userId, Guid bookingId, UpdateBookingRequest updateBookingRequest);
}

public class BookingUsecases : IBookingUsecases
Expand Down Expand Up @@ -138,4 +139,39 @@ public Booking DeleteBooking(Guid userId, Guid bookingId)
_context.SaveChanges();
return booking;
}

public Booking UpdateBooking(Guid userId, Guid bookingId, UpdateBookingRequest updateBookingRequest)
{
var user = _context.Users.FirstOrDefault(u => u.UserId == userId);
if (user == null)
{
throw new ArgumentException("User not found");
}

var booking = _context.Bookings.FirstOrDefault(b => b.BookingId == bookingId);
if (booking == null)
{
throw new ArgumentException("Booking not found");
}

if (booking.UserId != userId)
{
throw new ArgumentException("You are not allowed to update this booking");
}

var bookings = _context.Bookings.Where(b => b.DeskId == booking.DeskId && b.BookingId != bookingId);
var timeSlotAvailable = bookings.All(b => b.StartTime >= updateBookingRequest.EndTime || b.EndTime <= updateBookingRequest.StartTime);
if (!timeSlotAvailable)
{
throw new ArgumentException("Time slot not available");
}

booking.StartTime = updateBookingRequest.StartTime;
booking.EndTime = updateBookingRequest.EndTime;
booking.Timestamp = DateTime.Now;

_context.Bookings.Update(booking);
_context.SaveChanges();
return booking;
}
}
235 changes: 235 additions & 0 deletions src/deskstar-backend/Teststar.Tests/Tests/BookingUsecasesTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,241 @@ public void CreateBooking_WhenAvailable_ShouldReturnABooking()
db.Database.EnsureDeleted();
}

[Test]
public void UpdateBooking_WhenUserNotExists_ShouldThrowAnArgumentException()
{
//setup
using var db = new DataContext();

var deskId = Guid.NewGuid();
SetupMockData(db, deskId: deskId);

//arrange
var logger = new Mock<ILogger<BookingUsecases>>();
var usecases = new BookingUsecases(logger.Object, db);

var updateBookingRequest = new UpdateBookingRequest
{
StartTime = DateTime.Now,
EndTime = DateTime.Now
};
//act
try
{
var result = usecases.UpdateBooking(Guid.NewGuid(), Guid.NewGuid(), updateBookingRequest);

//assert
Assert.Fail();
}
catch (ArgumentException e)
{
Assert.That(e.Message, Is.EqualTo("User not found"));
}

//cleanup
db.Database.EnsureDeleted();
}

[Test]
public void UpdateBooking_WhenBookingNotExists_ShouldThrowAnArgumentException()
{
//setup
using var db = new DataContext();

var userId = Guid.NewGuid();
SetupMockData(db, userId: userId);

//arrange
var logger = new Mock<ILogger<BookingUsecases>>();
var usecases = new BookingUsecases(logger.Object, db);

var updateBookingRequest = new UpdateBookingRequest
{
StartTime = DateTime.Now,
EndTime = DateTime.Now
};
//act
try
{
var result = usecases.UpdateBooking(userId, Guid.NewGuid(), updateBookingRequest);

//assert
Assert.Fail();
}
catch (ArgumentException e)
{
Assert.That(e.Message, Is.EqualTo("Booking not found"));
}

//cleanup
db.Database.EnsureDeleted();
}

[Test]
public void UpdateBooking_WhenBookingNotFromUser_ShouldThrowAnArgumentException()
{
//setup
using var db = new DataContext();

var userId1 = Guid.NewGuid();
var userId2 = Guid.NewGuid();
var deskId = Guid.NewGuid();
var bookingId = Guid.NewGuid();

SetupMockData(db, userId: userId1, deskId: deskId);
SetupMockData(db, userId: userId2);

var firstBooking = new Booking
{
BookingId = bookingId,
DeskId = deskId,
UserId = userId1,
Timestamp = DateTime.Now,
StartTime = DateTime.Now,
EndTime = DateTime.Now
};
db.Add(firstBooking);
db.SaveChanges();

//arrange
var logger = new Mock<ILogger<BookingUsecases>>();
var usecases = new BookingUsecases(logger.Object, db);

var updateBookingRequest = new UpdateBookingRequest
{
StartTime = DateTime.Now,
EndTime = DateTime.Now
};

//act
try
{
var result = usecases.UpdateBooking(userId2, bookingId, updateBookingRequest);

//assert
Assert.Fail();
}
catch (ArgumentException e)
{
Assert.That(e.Message, Is.EqualTo("You are not allowed to update this booking"));
}

//cleanup
db.Database.EnsureDeleted();
}

[Test]
public void UpdateBooking_WhenTimeslotBooked_ShouldThrowAnArgumentException()
{
//setup
using var db = new DataContext();

var userId = Guid.NewGuid();
var deskId = Guid.NewGuid();
var bookingId = Guid.NewGuid();

SetupMockData(db, userId: userId, deskId: deskId);
var fbStart = DateTime.Now.Add(TimeSpan.FromHours(1));
var fbEnd = DateTime.Now.Add(TimeSpan.FromHours(2));

var firstBooking = new Booking
{
BookingId = bookingId,
DeskId = deskId,
UserId = userId,
Timestamp = DateTime.Now,
StartTime = DateTime.Now,
EndTime = DateTime.Now
};
var secondBooking = new Booking
{
BookingId = Guid.NewGuid(),
DeskId = deskId,
UserId = userId,
Timestamp = DateTime.Now,
StartTime = fbStart,
EndTime = fbEnd
};
db.Add(firstBooking);
db.Add(secondBooking);
db.SaveChanges();

//arrange
var logger = new Mock<ILogger<BookingUsecases>>();
var usecases = new BookingUsecases(logger.Object, db);

var updateBookingRequest = new UpdateBookingRequest
{
StartTime = fbStart,
EndTime = fbEnd
};
//act
try
{
var result = usecases.UpdateBooking(userId, bookingId, updateBookingRequest);

//assert
Assert.Fail();
}
catch (ArgumentException e)
{
Assert.That(e.Message, Is.EqualTo("Time slot not available"));
}

//cleanup
db.Database.EnsureDeleted();
}

[Test]
public void UpdateBooking_WhenAvailable_ShouldReturnABooking()
{
//setup
using var db = new DataContext();

var userId = Guid.NewGuid();
var deskId = Guid.NewGuid();
var bookingId = Guid.NewGuid();

SetupMockData(db, userId: userId, deskId: deskId);
var fbStart = DateTime.Now.Add(TimeSpan.FromHours(1));
var fbEnd = DateTime.Now.Add(TimeSpan.FromHours(2));

var firstBooking = new Booking
{
BookingId = bookingId,
DeskId = deskId,
UserId = userId,
Timestamp = DateTime.Now,
StartTime = fbStart,
EndTime = fbEnd
};
db.Add(firstBooking);
db.SaveChanges();

//arrange
var logger = new Mock<ILogger<BookingUsecases>>();
var usecases = new BookingUsecases(logger.Object, db);

var updateBookingRequest = new UpdateBookingRequest
{
StartTime = DateTime.Now,
EndTime = DateTime.Now
};
//act
var result = usecases.UpdateBooking(userId, bookingId, updateBookingRequest);

//assert
Assert.That(result, Is.Not.Null);
Assert.That(result.BookingId, Is.EqualTo(bookingId));
Assert.That(result.UserId, Is.EqualTo(userId));
Assert.That(result.DeskId, Is.EqualTo(deskId));
Assert.That(result.StartTime, Is.EqualTo(updateBookingRequest.StartTime));
Assert.That(result.EndTime, Is.EqualTo(updateBookingRequest.EndTime));

//cleanup
db.Database.EnsureDeleted();
}

[Test]
public void DeleteBooking_WhenUserNotExists_ShouldThrowAnArgumentException()
{
Expand Down
Loading