From 4a52c30731d6ed88539cbe99314d8e5fac359760 Mon Sep 17 00:00:00 2001 From: Mouaz Abdelsamad <ma03081@surrey.ac.uk> Date: Mon, 15 Apr 2024 23:28:25 +0100 Subject: [PATCH] Add Endpoints for flight history and upcoming flights --- .../Clients/FlightServiceClient.cs | 9 +++- .../Clients/IFlightServiceClient.cs | 8 ++- .../Controllers/BookingController.cs | 39 +++++++++++++++ BookingMicroservice/Models/Booking.cs | 6 --- BookingMicroservice/Models/BookingClass.cs | 8 +++ BookingMicroservice/Models/Flight.cs | 12 +++++ .../Models/FlightBookingInfo.cs | 14 ++++++ .../Models/FlightIdCollection.cs | 8 +++ .../Models/FlightResponseWrapper.cs | 10 ++++ .../Services/IReservationComplianceService.cs | 3 +- .../Services/ReservationComplianceService.cs | 49 +++++++++++++++++++ .../Controllers/FlightController.cs | 10 ++++ .../Models/FlightIdCollection.cs | 7 +++ FlightMicroservice/Services/FlightService.cs | 8 +++ FlightMicroservice/Services/IFlightService.cs | 1 + .../BookingService/BookingServiceClient.cs | 11 +++++ .../BookingService/IBookingServiceClient.cs | 5 +- GatewayAPI/Controllers/BookingController.cs | 14 ++++++ 18 files changed, 212 insertions(+), 10 deletions(-) create mode 100644 BookingMicroservice/Models/BookingClass.cs create mode 100644 BookingMicroservice/Models/Flight.cs create mode 100644 BookingMicroservice/Models/FlightBookingInfo.cs create mode 100644 BookingMicroservice/Models/FlightIdCollection.cs create mode 100644 BookingMicroservice/Models/FlightResponseWrapper.cs create mode 100644 FlightMicroservice/Models/FlightIdCollection.cs diff --git a/BookingMicroservice/Clients/FlightServiceClient.cs b/BookingMicroservice/Clients/FlightServiceClient.cs index 4d54fd5..a67d1e3 100644 --- a/BookingMicroservice/Clients/FlightServiceClient.cs +++ b/BookingMicroservice/Clients/FlightServiceClient.cs @@ -1,4 +1,6 @@ -namespace BookingMicroservice.Clients +using BookingMicroservice.Models; + +namespace BookingMicroservice.Clients { public class FlightServiceClient : IFlightServiceClient { @@ -27,5 +29,10 @@ return await httpClient.PutAsync($"{SEAT_API_PATH}/{seatId}", null); } + public async Task<HttpResponseMessage> GetFlightsByIdAsync(FlightIdCollection flightIdCollection) + { + return await httpClient.PostAsJsonAsync($"{FLIGHT_API_PATH}/byIds", flightIdCollection); + } + } } diff --git a/BookingMicroservice/Clients/IFlightServiceClient.cs b/BookingMicroservice/Clients/IFlightServiceClient.cs index c3e9dcb..e8fbca1 100644 --- a/BookingMicroservice/Clients/IFlightServiceClient.cs +++ b/BookingMicroservice/Clients/IFlightServiceClient.cs @@ -1,9 +1,15 @@ -namespace BookingMicroservice.Clients +using BookingMicroservice.Migrations; +using BookingMicroservice.Models; +using BookingMicroservice.Services; +using static System.Runtime.InteropServices.JavaScript.JSType; + +namespace BookingMicroservice.Clients { public interface IFlightServiceClient { Task<HttpResponseMessage> GetFlightCapacityAsync(int flightId, int classType); Task<HttpResponseMessage> IsSeatAvailableAsync(int seatId); Task<HttpResponseMessage> BookSeatAsync(int seatId); + Task<HttpResponseMessage> GetFlightsByIdAsync(FlightIdCollection flightIdCollection); } } diff --git a/BookingMicroservice/Controllers/BookingController.cs b/BookingMicroservice/Controllers/BookingController.cs index de800b6..a8aabde 100644 --- a/BookingMicroservice/Controllers/BookingController.cs +++ b/BookingMicroservice/Controllers/BookingController.cs @@ -87,5 +87,44 @@ namespace BookingMicroservice.Controllers return Ok(new { BookedSeat = false, Message = exception.Message }); } } + + [Authorize] + [HttpGet("upcoming")] + public async Task<IActionResult> GetUpcomingFlightBookings() + { + string? userIdValue = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + if (!int.TryParse(userIdValue, out int userId)) + return BadRequest("Unable to get User Id from Token"); + + try + { + IEnumerable<FlightBookingInfo> flights = await reservationComplianceService.TryGetUpcomingFlightsAsync(userId); + return Ok(flights); + } + catch(InvalidOperationException exception) + { + return BadRequest(exception.Message); + } + } + + [Authorize] + [HttpGet("history")] + public async Task<IActionResult> GetPreviousFlightBookings() + { + string? userIdValue = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + if (!int.TryParse(userIdValue, out int userId)) + return BadRequest("Unable to get User Id from Token"); + + try + { + IEnumerable<FlightBookingInfo> flights = await reservationComplianceService.TryGetPreviousFlightsAsync(userId); + return Ok(flights); + } + catch (InvalidOperationException exception) + { + return BadRequest(exception.Message); + } + } + } } diff --git a/BookingMicroservice/Models/Booking.cs b/BookingMicroservice/Models/Booking.cs index 61eaa3e..d9c9ac6 100644 --- a/BookingMicroservice/Models/Booking.cs +++ b/BookingMicroservice/Models/Booking.cs @@ -23,10 +23,4 @@ SeatId = seatId; } } - - public enum BookingClass - { - BUSINESS = 0, - ECONOMY = 1 - } } diff --git a/BookingMicroservice/Models/BookingClass.cs b/BookingMicroservice/Models/BookingClass.cs new file mode 100644 index 0000000..80b20fb --- /dev/null +++ b/BookingMicroservice/Models/BookingClass.cs @@ -0,0 +1,8 @@ +namespace BookingMicroservice.Models +{ + public enum BookingClass + { + BUSINESS = 0, + ECONOMY = 1 + } +} diff --git a/BookingMicroservice/Models/Flight.cs b/BookingMicroservice/Models/Flight.cs new file mode 100644 index 0000000..441926b --- /dev/null +++ b/BookingMicroservice/Models/Flight.cs @@ -0,0 +1,12 @@ +namespace BookingMicroservice.Models +{ + public class Flight + { + public int Id { get; set; } + public int AirlineId { get; set; } + public string Origin { get; set; } + public string Destination { get; set; } + public DateTime DepartureTime { get; set; } + public DateTime ArrivalTime { get; set; } + } +} diff --git a/BookingMicroservice/Models/FlightBookingInfo.cs b/BookingMicroservice/Models/FlightBookingInfo.cs new file mode 100644 index 0000000..32c31dc --- /dev/null +++ b/BookingMicroservice/Models/FlightBookingInfo.cs @@ -0,0 +1,14 @@ +namespace BookingMicroservice.Models +{ + public class FlightBookingInfo + { + public Flight Flight { get; set; } + public Booking Booking { get; set; } + + public FlightBookingInfo(Flight flight, Booking booking) + { + Flight = flight; + Booking = booking; + } + } +} diff --git a/BookingMicroservice/Models/FlightIdCollection.cs b/BookingMicroservice/Models/FlightIdCollection.cs new file mode 100644 index 0000000..bf39f59 --- /dev/null +++ b/BookingMicroservice/Models/FlightIdCollection.cs @@ -0,0 +1,8 @@ +namespace BookingMicroservice.Models +{ + public class FlightIdCollection + { + public required List<int> FlightIds { get; set; } + + } +} diff --git a/BookingMicroservice/Models/FlightResponseWrapper.cs b/BookingMicroservice/Models/FlightResponseWrapper.cs new file mode 100644 index 0000000..07b804e --- /dev/null +++ b/BookingMicroservice/Models/FlightResponseWrapper.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace BookingMicroservice.Models +{ + public class FlightResponseWrapper + { + [JsonPropertyName("$values")] + public List<Flight> Values { get; set; } + } +} diff --git a/BookingMicroservice/Services/IReservationComplianceService.cs b/BookingMicroservice/Services/IReservationComplianceService.cs index 7f82720..a7ad5fe 100644 --- a/BookingMicroservice/Services/IReservationComplianceService.cs +++ b/BookingMicroservice/Services/IReservationComplianceService.cs @@ -6,6 +6,7 @@ namespace BookingMicroservice.Services { Task<Booking?> TryCreateBookingAsync(int flightId, int userId, BookingClass bookingClass, int? seatId); Task TryBookSeatAsync(int bookingId, int seatId); - + Task<IEnumerable<FlightBookingInfo>> TryGetUpcomingFlightsAsync(int userId); + Task<IEnumerable<FlightBookingInfo>> TryGetPreviousFlightsAsync(int userId); } } diff --git a/BookingMicroservice/Services/ReservationComplianceService.cs b/BookingMicroservice/Services/ReservationComplianceService.cs index df3f6a6..ab80ec3 100644 --- a/BookingMicroservice/Services/ReservationComplianceService.cs +++ b/BookingMicroservice/Services/ReservationComplianceService.cs @@ -1,6 +1,8 @@ using BookingMicroservice.Clients; using BookingMicroservice.Exceptions; using BookingMicroservice.Models; +using System.Linq; +using System.Text.Json; namespace BookingMicroservice.Services { @@ -76,5 +78,52 @@ namespace BookingMicroservice.Services bookingService.UpdateBooking(bookingId, seatId); } + public async Task<IEnumerable<FlightBookingInfo>> TryGetUpcomingFlightsAsync(int userId) + { + return await GetFlightsAsync(userId, true); + } + + public async Task<IEnumerable<FlightBookingInfo>> TryGetPreviousFlightsAsync(int userId) + { + return await GetFlightsAsync(userId, false); + } + + private async Task<IEnumerable<FlightBookingInfo>> GetFlightsAsync(int userId, bool isUpcoming) + { + List<Booking> bookings = bookingService.GetBookings(userId: userId); + List<int> flightIds = bookings.Select(booking => booking.FlightId).ToList(); + FlightIdCollection flightIdCollection = new FlightIdCollection { FlightIds = flightIds }; + + HttpResponseMessage flightsResponse = await flightServiceClient.GetFlightsByIdAsync(flightIdCollection); + if (!flightsResponse.IsSuccessStatusCode) + throw new InvalidOperationException("Could not retrieve flights."); + + string flightsString = await flightsResponse.Content.ReadAsStringAsync(); + FlightResponseWrapper? flightResponse = JsonSerializer.Deserialize<FlightResponseWrapper>(flightsString, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); + if (flightResponse?.Values == null) + throw new InvalidOperationException("Deserialization of flights failed."); + + List<Flight> flights = flightResponse.Values; + + DateTime comparisonTime = DateTime.UtcNow; + IEnumerable<Flight> filteredFlights = isUpcoming + ? flights.Where(flight => flight.DepartureTime > comparisonTime) + : flights.Where(flight => flight.DepartureTime <= comparisonTime); + + List<FlightBookingInfo> result = filteredFlights.Select(flight => + { + Booking? matchingBooking = bookings.FirstOrDefault(booking => booking.FlightId == flight.Id); + if (matchingBooking == null) + throw new InvalidOperationException($"No booking found for flight ID {flight.Id}."); + + return new FlightBookingInfo(flight, matchingBooking); + + }).ToList(); + + return result; + } + + } + } diff --git a/FlightMicroservice/Controllers/FlightController.cs b/FlightMicroservice/Controllers/FlightController.cs index 2bd55a2..5ab973f 100644 --- a/FlightMicroservice/Controllers/FlightController.cs +++ b/FlightMicroservice/Controllers/FlightController.cs @@ -78,6 +78,16 @@ namespace FlightMicroservice.Controllers return Ok(seats); } + [HttpPost("byIds")] + public IActionResult GetFlightsByIds([FromBody] FlightIdCollection flightIdCollection) + { + List<Flight> flights = flightService.GetFlightsByIds(flightIdCollection.FlightIds); + if (flights == null) + return NotFound(); + + return Ok(flights); + } + } } diff --git a/FlightMicroservice/Models/FlightIdCollection.cs b/FlightMicroservice/Models/FlightIdCollection.cs new file mode 100644 index 0000000..03b09b6 --- /dev/null +++ b/FlightMicroservice/Models/FlightIdCollection.cs @@ -0,0 +1,7 @@ +namespace FlightMicroservice.Models +{ + public class FlightIdCollection + { + public required List<int> FlightIds { get; set; } + } +} diff --git a/FlightMicroservice/Services/FlightService.cs b/FlightMicroservice/Services/FlightService.cs index c849e71..5122bf6 100644 --- a/FlightMicroservice/Services/FlightService.cs +++ b/FlightMicroservice/Services/FlightService.cs @@ -80,6 +80,14 @@ namespace FlightMicroservice.Services .ToList(); } + public List<Flight> GetFlightsByIds(List<int> ids) + { + return dbContext.Flights + .Where(flight => ids.Contains(flight.Id)) + .ToList(); + } + + private void InitializeSeatsForFlight(Flight flight) { int businessRows = flight.BusinessCapacity / Flight.BUSINESS_SEATS_PER_ROW; diff --git a/FlightMicroservice/Services/IFlightService.cs b/FlightMicroservice/Services/IFlightService.cs index 4dad71b..b9fe6f0 100644 --- a/FlightMicroservice/Services/IFlightService.cs +++ b/FlightMicroservice/Services/IFlightService.cs @@ -9,5 +9,6 @@ namespace FlightMicroservice.Services Flight CreateFlight(int airlineId, string origin, string destination, DateTime departureTime, DateTime arrivalTime, int economyCapacity, int businessCapacity, decimal economyPrice, decimal businessPrice); bool RemoveFlight(int flightId); List<Seat> GetSeatsByFlightId(int flightId); + List<Flight> GetFlightsByIds(List<int> ids); } } diff --git a/GatewayAPI/Clients/BookingService/BookingServiceClient.cs b/GatewayAPI/Clients/BookingService/BookingServiceClient.cs index dab63de..be1534f 100644 --- a/GatewayAPI/Clients/BookingService/BookingServiceClient.cs +++ b/GatewayAPI/Clients/BookingService/BookingServiceClient.cs @@ -47,5 +47,16 @@ namespace GatewayAPI.Clients.BookingService } + public Task<HttpResponseMessage> GetUpcomingFlightBookingsAsync() + { + return httpClient.GetAsync($"{API_PATH}/upcoming"); + } + + public Task<HttpResponseMessage> GetPreviousFlightBookingsAsync() + { + return httpClient.GetAsync($"{API_PATH}/history"); + } + + } } diff --git a/GatewayAPI/Clients/BookingService/IBookingServiceClient.cs b/GatewayAPI/Clients/BookingService/IBookingServiceClient.cs index 9b4d6b2..be2db11 100644 --- a/GatewayAPI/Clients/BookingService/IBookingServiceClient.cs +++ b/GatewayAPI/Clients/BookingService/IBookingServiceClient.cs @@ -7,5 +7,8 @@ namespace GatewayAPI.Clients.BookingService Task<HttpResponseMessage> GetBookingAsync(int id); Task<HttpResponseMessage> GetBookingsAsync(int? flightId = null, int? userId = null, int? bookingClass = null); Task<HttpResponseMessage> MakeBookingAsync(BookingCreation bookingModel); - Task<HttpResponseMessage> UpdateBookingAsync(int bookindId, BookingUpdate bookingModel); } + Task<HttpResponseMessage> UpdateBookingAsync(int bookindId, BookingUpdate bookingModel); + Task<HttpResponseMessage> GetUpcomingFlightBookingsAsync(); + Task<HttpResponseMessage> GetPreviousFlightBookingsAsync(); + } } diff --git a/GatewayAPI/Controllers/BookingController.cs b/GatewayAPI/Controllers/BookingController.cs index 3828149..b6ce381 100644 --- a/GatewayAPI/Controllers/BookingController.cs +++ b/GatewayAPI/Controllers/BookingController.cs @@ -43,5 +43,19 @@ namespace GatewayAPI.Controllers HttpResponseMessage response = await bookingServiceClient.UpdateBookingAsync(id, bookingUpdateModel); return new HttpResponseMessageResult(response); } + + [HttpGet("upcoming")] + public async Task<IActionResult> GetUpcomingFlightBookings() + { + HttpResponseMessage response = await bookingServiceClient.GetUpcomingFlightBookingsAsync(); + return new HttpResponseMessageResult(response); + } + + [HttpGet("history")] + public async Task<IActionResult> GetPreviousFlightBookings() + { + HttpResponseMessage response = await bookingServiceClient.GetPreviousFlightBookingsAsync(); + return new HttpResponseMessageResult(response); + } } } -- GitLab