diff --git a/java/roomBooking/src/main/java/com/uva/roomBooking/Controllers/BookingController.java b/java/roomBooking/src/main/java/com/uva/roomBooking/Controllers/BookingController.java index 84376955ee8c7f04ac4a241f55dbdda053f20761..b3b78e3543d2dd533c1423d27c5e2e291552a729 100644 --- a/java/roomBooking/src/main/java/com/uva/roomBooking/Controllers/BookingController.java +++ b/java/roomBooking/src/main/java/com/uva/roomBooking/Controllers/BookingController.java @@ -2,7 +2,12 @@ package com.uva.roomBooking.Controllers; import com.uva.roomBooking.Models.Booking; +import com.uva.roomBooking.Models.Room; +import com.uva.roomBooking.Models.User; import com.uva.roomBooking.Repositories.BookingRepository; +import com.uva.roomBooking.Repositories.RoomRepository; +import com.uva.roomBooking.Repositories.UserRepository; + import org.springframework.web.bind.annotation.*; import java.util.List; @@ -13,9 +18,14 @@ import java.util.List; public class BookingController { private final BookingRepository bookingRepository; + private final UserRepository userRepository; + private final RoomRepository roomRepository; - public BookingController(BookingRepository bookingRepository) { + public BookingController(BookingRepository bookingRepository, UserRepository userRepository, + RoomRepository roomRepository) { this.bookingRepository = bookingRepository; + this.userRepository = userRepository; + this.roomRepository = roomRepository; } @GetMapping @@ -25,6 +35,10 @@ public class BookingController { @PostMapping public Booking createBooking(@RequestBody Booking booking) { + User user = userRepository.findById(booking.getUserId().getId()).orElseThrow(); + Room room = roomRepository.findById(booking.getRoomId().getId()).orElseThrow(); + booking.setUserId(user); + booking.setRoomId(room); return bookingRepository.save(booking); } diff --git a/java/roomBooking/src/main/java/com/uva/roomBooking/Controllers/HotelController.java b/java/roomBooking/src/main/java/com/uva/roomBooking/Controllers/HotelController.java index f492154189492766ede1c685a9866161c84fa333..949b175f65224b8ae3399cc8ba14b26c9638b1cc 100644 --- a/java/roomBooking/src/main/java/com/uva/roomBooking/Controllers/HotelController.java +++ b/java/roomBooking/src/main/java/com/uva/roomBooking/Controllers/HotelController.java @@ -1,7 +1,6 @@ package com.uva.roomBooking.Controllers; import java.util.List; -import java.util.Optional; import java.time.LocalDate; import com.uva.roomBooking.Exceptions.HotelNotFoundException; @@ -28,81 +27,91 @@ public class HotelController { // Obtener todos los hoteles @GetMapping - public ResponseEntity<List<Hotel>> getAllHotels() { - List<Hotel> hotels = hotelRepository.findAll(); - return new ResponseEntity<>(hotels, HttpStatus.OK); + public List<Hotel> getAllHotels() { + return hotelRepository.findAll(); } // Añadir un hotel con sus habitaciones @PostMapping public ResponseEntity<Hotel> addHotel(@RequestBody Hotel hotel) { - if (hotel.getRooms() != null) { - hotel.getRooms().forEach(room -> room.setHotel(hotel)); // Aseguramos la relación bidireccional - } Hotel savedHotel = hotelRepository.save(hotel); return new ResponseEntity<>(savedHotel, HttpStatus.CREATED); } // Obtener un hotel por su ID @GetMapping("/{id}") - public ResponseEntity<Hotel> getHotelById(@PathVariable int id) { + public Hotel getHotelById(@PathVariable int id) { return hotelRepository.findById(id) - .map(value -> new ResponseEntity<>(value, HttpStatus.OK)) - .orElseThrow(() -> new HotelNotFoundException(id)); + .orElseThrow(() -> new HotelNotFoundException(id)); } // Borrar un hotel junto con sus habitaciones (borrado en cascada) @DeleteMapping("/{id}") public ResponseEntity<Void> deleteHotel(@PathVariable Integer id) { - Optional<Hotel> target = hotelRepository.findById(id); - if (target.isPresent()) { - // Borramos habitaciones asociadas - roomRepository.deleteAllByHotelId(id); - hotelRepository.deleteById(id); - return new ResponseEntity<>(HttpStatus.NO_CONTENT); - } - throw new HotelNotFoundException(id); // Lanzamos excepción personalizada + Hotel target = hotelRepository.findById(id).orElseThrow(() -> new HotelNotFoundException(id)); + hotelRepository.delete(target); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); } // Obtener habitaciones de un hotel según disponibilidad y fechas @GetMapping("/{hotelId}/rooms") public ResponseEntity<List<Room>> getRoomsFromHotel( - @PathVariable int hotelId, - @RequestParam(required = false) LocalDate date1, - @RequestParam(required = false) LocalDate date2) { + @PathVariable(required = true) int hotelId, + @RequestParam(required = false) LocalDate date1, + @RequestParam(required = false) LocalDate date2) { List<Room> rooms; if (date1 != null && date2 != null) { + if (!date1.isBefore(date2)) { + // TODO cambiar por una excepción adecuada + throw new IllegalArgumentException("date1 should be before than date2"); + } rooms = roomRepository.findAvailableRoomsByHotelAndDates(hotelId, date1, date2); } else { - rooms = roomRepository.findByHotelId(hotelId); + rooms = roomRepository.findAllByHotelId(hotelId); } return new ResponseEntity<>(rooms, HttpStatus.OK); } // Actualizar disponibilidad de habitaciones de un hotel - @PatchMapping("/{hotelId}/rooms") + @PatchMapping("/{hotelId}/rooms/") + // TODO incluir tratamiento por id de habitación public ResponseEntity<List<Room>> updateRoomAvailabilityFromHotel( - @PathVariable int hotelId, @RequestBody List<Room> updatedRooms) { + @PathVariable int hotelId, @RequestBody List<Room> updatedRooms) { - List<Room> existingRooms = roomRepository.findByHotelId(hotelId); + List<Room> existingRooms = roomRepository.findAllByHotelId(hotelId); for (Room updatedRoom : updatedRooms) { existingRooms.stream() - .filter(room -> Integer.valueOf(room.getId()).equals(updatedRoom.getId())) // Conversión a Integer - .findFirst() - .ifPresent(room -> room.setAvailable(updatedRoom.isAvailable())); + .filter(room -> room.getId() == updatedRoom.getId()) // Conversión a Integer + .findFirst() + .ifPresent(room -> room.setAvailable(updatedRoom.isAvailable())); } roomRepository.saveAll(existingRooms); return new ResponseEntity<>(existingRooms, HttpStatus.OK); } + // Mi propuesta para el anterior + // // Actualizar disponibilidad de habitaciones de un hotel + // @PatchMapping("/{hotelId}/rooms/{roomId}") + // // TODO incluir tratamiento por id de habitación + // public ResponseEntity<Room> updateRoomAvailabilityFromHotel( + // @PathVariable int hotelId, @PathVariable int roomId, @RequestBody Map<String, + // Boolean> roomPatch) { + // Room target = roomRepository.findByIdAndHotelId(roomId, hotelId) + // .orElseThrow(() -> new IllegalArgumentException("No founds")); + // if (roomPatch.containsKey("available")) { + // throw new InvalidRequestException("no available field in body request"); + // } + // target.setAvailable(roomPatch.get("available")); + // roomRepository.save(target); + // return new ResponseEntity<>(target, HttpStatus.OK); + // } + // Obtener los detalles de una habitación específica en un hotel @GetMapping("/{hotelId}/rooms/{roomId}") - public ResponseEntity<Room> getRoomByIdFromHotel( - @PathVariable int hotelId, @PathVariable int roomId) { - - Optional<Room> room = roomRepository.findByHotelIdAndRoomId(hotelId, roomId); - return room.map(value -> new ResponseEntity<>(value, HttpStatus.OK)) - .orElseThrow(() -> new HotelNotFoundException(hotelId)); // Lanzamos excepción personalizada + public Room getRoomByIdFromHotel( + @PathVariable int hotelId, @PathVariable int roomId) { + return roomRepository.findByIdAndHotelId(roomId, hotelId) + .orElseThrow(() -> new HotelNotFoundException(hotelId)); } } diff --git a/java/roomBooking/src/main/java/com/uva/roomBooking/Models/Booking.java b/java/roomBooking/src/main/java/com/uva/roomBooking/Models/Booking.java index 5b6dc29e1fddae995f4ef68a480ef4fedc45102d..c2b36c948f72392174dcf1ebdcb7c4face3303ef 100644 --- a/java/roomBooking/src/main/java/com/uva/roomBooking/Models/Booking.java +++ b/java/roomBooking/src/main/java/com/uva/roomBooking/Models/Booking.java @@ -22,66 +22,67 @@ public class Booking { @GeneratedValue(strategy = GenerationType.IDENTITY) @Basic(optional = false) private int id; + // TODO revisar si lo de cascade es estrictamente necesario @JoinColumn(name = "user_id", referencedColumnName = "id") - @ManyToOne(optional = false, fetch = FetchType.LAZY, cascade = CascadeType.MERGE) + @ManyToOne(optional = false, fetch = FetchType.EAGER, cascade = CascadeType.MERGE) private User userId; @JoinColumn(name = "room_id", referencedColumnName = "id") - @ManyToOne(optional = false, fetch = FetchType.LAZY) - private Room roomID; + @ManyToOne(optional = false, fetch = FetchType.EAGER, cascade = CascadeType.MERGE) + private Room roomId; @Column(name = "start_date", nullable = false) - private Date startDate; + private LocalDate startDate; @Column(name = "end_date", nullable = false) - private Date endDate; + private LocalDate endDate; + public Booking() { + } - public Booking (int id, User userId, Room roomID, Date startDate, Date endDate) { + public Booking(int id, User userId, Room roomID, LocalDate startDate, LocalDate endDate) { this.id = id; this.userId = userId; - this.roomID = roomID; + this.roomId = roomID; this.startDate = startDate; this.endDate = endDate; } - public void setId (int id) { + public void setId(int id) { this.id = id; } - public int getId () { + public int getId() { return this.id; } - public void setUser (User userId) { + public void setUserId(User userId) { this.userId = userId; } - public User getUser () { + public User getUserId() { return this.userId; } - public void setRoom (Room roomID) { - this.roomID = roomID; + public void setRoomId(Room roomID) { + this.roomId = roomID; } - public Room getRoom () { - return this.roomID; + public Room getRoomId() { + return this.roomId; } - public void setStartDate (Date startDate) { + public void setStartDate(LocalDate startDate) { this.startDate = startDate; } - public Date getStartDate () { + public LocalDate getStartDate() { return this.startDate; } - public void setEndDate (Date endDate) { + public void setEndDate(LocalDate endDate) { this.endDate = endDate; } - public Date getEndDate () { + public LocalDate getEndDate() { return this.endDate; } - } - diff --git a/java/roomBooking/src/main/java/com/uva/roomBooking/Models/Room.java b/java/roomBooking/src/main/java/com/uva/roomBooking/Models/Room.java index 7c47003f020e696a157343c2ad02ff32866bd103..7919e3643add4b380472b348e7100fb8e6742975 100644 --- a/java/roomBooking/src/main/java/com/uva/roomBooking/Models/Room.java +++ b/java/roomBooking/src/main/java/com/uva/roomBooking/Models/Room.java @@ -40,7 +40,7 @@ public class Room { @Column(name = "available", nullable = false) private boolean available; @JsonIgnore - @OneToMany(mappedBy = "roomID", fetch = FetchType.EAGER, cascade = CascadeType.MERGE) + @OneToMany(mappedBy = "roomId", fetch = FetchType.EAGER, cascade = CascadeType.MERGE) private List<Booking> bookings; public Room() { diff --git a/java/roomBooking/src/main/java/com/uva/roomBooking/Repositories/RoomRepository.java b/java/roomBooking/src/main/java/com/uva/roomBooking/Repositories/RoomRepository.java index 966448f697544de8c9f4f66ffa5b05d66dd2bd26..71b439ff3db90f48caa5ed5fbea6df89d44bd02e 100644 --- a/java/roomBooking/src/main/java/com/uva/roomBooking/Repositories/RoomRepository.java +++ b/java/roomBooking/src/main/java/com/uva/roomBooking/Repositories/RoomRepository.java @@ -2,9 +2,7 @@ package com.uva.roomBooking.Repositories; import com.uva.roomBooking.Models.Room; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; -import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; import java.util.List; @@ -12,29 +10,23 @@ import java.util.Optional; public interface RoomRepository extends JpaRepository<Room, Integer> { - // Encontrar todas las habitaciones de un hotel - List<Room> findByHotelId(int hotelId); + Optional<Room> findByIdAndHotelId(int id, int hotelId); - // Borrar todas las habitaciones asociadas a un hotel - @Transactional - @Modifying - void deleteAllByHotelId(int hotelId); + // Encontrar todas las habitaciones de un hotel + List<Room> findAllByHotelId(int hotelId); // Encontrar habitaciones disponibles de un hotel en un rango de fechas + // TODO revisar los límites en las fechas @Query(""" - SELECT r FROM Room r - WHERE r.hotel.id = :hotelId - AND r.available = true - AND NOT EXISTS ( - SELECT b FROM Booking b - WHERE b.roomID.id = r.id - AND (b.startDate <= :endDate AND b.endDate >= :startDate) - ) -""") + SELECT r FROM Room r + WHERE r.hotel.id = ?1 + AND r.available = true + AND NOT EXISTS ( + SELECT b FROM Booking b + WHERE b.roomId.id = r.id + AND (b.startDate < ?3 AND b.endDate > ?2) + ) + """) List<Room> findAvailableRoomsByHotelAndDates( - int hotelId, LocalDate startDate, LocalDate endDate); - - // Encontrar una habitación específica por hotel y su ID - @Query("SELECT r FROM Room r WHERE r.hotel.id = :hotelId AND r.id = :roomId") - Optional<Room> findByHotelIdAndRoomId(int hotelId, int roomId); + int hotelId, LocalDate startDate, LocalDate endDate); } diff --git a/java/roomBooking/src/main/resources/application.properties b/java/roomBooking/src/main/resources/application.properties index 4331f85f147bc141240304d915c1820829a7768f..e111b6dcf5ce04ef32bd59b8d7c15b7fc35dc407 100644 --- a/java/roomBooking/src/main/resources/application.properties +++ b/java/roomBooking/src/main/resources/application.properties @@ -4,4 +4,7 @@ spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/RoomsBooking?createDatabaseIfNotExist=true spring.datasource.username=user spring.datasource.password=password -spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver \ No newline at end of file +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver + +# Usar esto para alternar entre las exposición del room repository ya que no es necesario su uso pero por defecto, al no cubrir su ruta, se expone +spring.data.rest.base-path=false \ No newline at end of file