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