Skip to content

Feature/Menu#5

Merged
apaegs merged 31 commits into
mainfrom
feature/menu
Jan 13, 2026
Merged

Feature/Menu#5
apaegs merged 31 commits into
mainfrom
feature/menu

Conversation

@apaegs

@apaegs apaegs commented Jan 9, 2026

Copy link
Copy Markdown

Move CLI-menu logic to separate class Menu.java and add CLI menu structure for booking system

Summary by CodeRabbit

  • Refactor

    • Replaced inline CLI flow with a dedicated menu-driven interface and removed legacy CLI scaffolding.
  • New Features

    • Added an interactive menu to create bookings, view bookings, view guests for a booking, and cancel bookings with prompts and formatted displays.
  • Bug Fixes

    • Strengthened validation for dates, guest counts and room capacity; improved pricing calculation and availability checks.
  • Style

    • Minor formatting and spacing cleanups.
  • Chores

    • Adjusted service/repository interfaces to support the menu-driven flow.

✏️ Tip: You can customize this high-level summary in your review settings.

…cture for booking system

Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>
@coderabbitai

coderabbitai Bot commented Jan 9, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@apaegs has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 18 minutes and 31 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 4815b43 and 45a3fc4.

📒 Files selected for processing (2)
  • src/main/java/org/example/BookingService.java
  • src/main/java/org/example/Menu.java
📝 Walkthrough

Walkthrough

Replaced App's inline CLI with a new Menu class and delegated startup to Menu.start(). Updated BookingService and BookingRepository method signatures and validation/price logic (guest-count types, constructor dependency swap, internal priceClass). Removed legacy CLI helper methods from App.

Changes

Cohort / File(s) Summary
App wiring
src/main/java/org/example/App.java
Removed Scanner-based CLI helpers and legacy print/create/remove methods; minor formatting; now constructs repositories and BookingService then instantiates Menu(...).start().
New CLI: Menu
src/main/java/org/example/Menu.java
Added Menu class with constructor Menu(GuestRepository, BookingRepository, BookingService) and start() loop implementing create/view/cancel booking flows, input validation, printing helpers, guest creation, room availability checks, booking creation and cancellation. Review input validation, date parsing, and repository/service interactions.
BookingService
src/main/java/org/example/BookingService.java
Constructor now accepts BookingRepository (replacing GuestRepository); added priceClass BigDecimal field (initialized to 300); calculateTotalPrice now takes int numberOfGuests and uses internal priceClass; validateValues made private with changed signature and updated validation rules.
BookingRepository API
src/main/java/org/example/BookingRepository.java
Changed guest-count parameter types longint in getEmptyRooms and create; added getMaxGuests(); imported Optional. Confirm all callers updated to use int and capacity logic reviewed.
Misc / formatting
src/main/java/org/example/*.java
Minor spacing/formatting normalizations (try-with-resources, property spacing, etc.).

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant App
  participant Menu
  participant GuestRepository
  participant BookingRepository
  participant BookingService

  User->>App: start application
  App->>Menu: new Menu(GuestRepo, BookingRepo, BookingService)
  App->>Menu: menu.start()
  loop user actions
    User->>Menu: select option
    alt create booking
      Menu->>BookingService: calculateTotalPrice(numGuests,start,end)
      BookingService-->>Menu: totalPrice / -1 on invalid
      Menu->>GuestRepository: findByEmail / create guest(s)
      GuestRepository-->>Menu: guest(s)
      Menu->>BookingRepository: getEmptyRooms(start,end,guests)
      BookingRepository-->>Menu: rooms
      Menu->>BookingRepository: create(emails,start,end,guests,totalPrice)
      BookingRepository-->>Menu: success/failure
      Menu-->>User: result
    else view/cancel bookings
      Menu->>BookingRepository: findAll()/findByGuest()/findGuestsByBooking()/delete(id)
      BookingRepository-->>Menu: results/confirmation
      Menu-->>User: display result
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hopped through code with a tail so bright,

Moved menus to burrows and set inputs right.
Guests and rooms, totals counted with care,
I nudged a few methods and tidied the lair.
Carrots, bookings, and a merry byte.

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.26% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Feature/Menu' is vague and generic, using a branch naming convention rather than describing the actual change. It does not convey meaningful information about the specific functionality being added. Use a descriptive title that summarizes the main change, such as 'Extract CLI menu logic into separate Menu class' or 'Add Menu-driven CLI structure for booking operations'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @src/main/java/org/example/App.java:
- Around line 14-16: App.java currently hard-codes DB connection details via the
.jdbcUrl, .jdbcUsername and .jdbcPassword builder calls; replace these
hard-coded values with configurable sources (prefer environment variables or a
configuration/properties file) by reading
System.getenv("DB_URL")/System.getenv("DB_USER")/System.getenv("DB_PASS") or a
loaded Properties object and passing those values into the
.jdbcUrl/.jdbcUsername/.jdbcPassword calls, providing sensible fallbacks or
failing fast with clear errors if values are missing.
🧹 Nitpick comments (2)
src/main/java/org/example/App.java (1)

8-8: Remove unused import.

The java.awt.* import is not used anywhere in this file.

♻️ Proposed fix
-import java.awt.*;
 import java.util.Scanner;
src/main/java/org/example/Menu.java (1)

7-7: Consider adding error handling for Scanner operations.

While the Scanner correctly wraps System.in and is intentionally not closed, the start() method should handle potential NoSuchElementException or other Scanner exceptions to prevent unexpected crashes.

💡 Example error handling
 public void start() {
     while (true) {
-        System.out.println("\n=== Booking System ===");
-        System.out.println("1) Create booking");
-        System.out.println("2) View all bookings");
-        System.out.println("3) View all bookings by guest");
-        System.out.println("4) View all guests by booking");
-        System.out.println("5) Cancel booking");
-        System.out.println("0) Exit");
-        System.out.print("Choose: ");
-
-        String choice = scanner.nextLine();
+        try {
+            System.out.println("\n=== Booking System ===");
+            System.out.println("1) Create booking");
+            System.out.println("2) View all bookings");
+            System.out.println("3) View all bookings by guest");
+            System.out.println("4) View all guests by booking");
+            System.out.println("5) Cancel booking");
+            System.out.println("0) Exit");
+            System.out.print("Choose: ");
+
+            String choice = scanner.nextLine();
+            // ... rest of switch logic
+        } catch (Exception e) {
+            System.err.println("Error reading input: " + e.getMessage());
+        }
+    }
+}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8828238 and ce0b8a1.

📒 Files selected for processing (2)
  • src/main/java/org/example/App.java
  • src/main/java/org/example/Menu.java
🔇 Additional comments (4)
src/main/java/org/example/App.java (1)

27-28: Clean delegation to Menu class.

The refactoring successfully separates CLI concerns into the Menu class, improving maintainability and single responsibility.

src/main/java/org/example/Menu.java (3)

12-15: Good use of dependency injection.

The constructor properly injects repository dependencies, enabling testability and loose coupling.


17-43: Clean menu structure with proper exit handling.

The infinite loop with a clear exit point (case "0") and switch expressions provide a clean, modern menu implementation.


46-66: Stub methods appropriately marked with TODOs.

All placeholder methods are clearly marked with TODO comments describing their intended functionality. This provides good guidance for future implementation.

Comment on lines 14 to 16
.jdbcUrl("jdbc:mysql://localhost:3306/booking_db")
.jdbcUsername("root")
.jdbcPassword("root")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Externalize database credentials and configuration.

Hard-coded database credentials ("root"/"root") and connection URLs pose security and portability risks. Consider externalizing these to environment variables or a configuration file.

💡 Example approach using environment variables
-            .jdbcUrl("jdbc:mysql://localhost:3306/booking_db")
-            .jdbcUsername("root")
-            .jdbcPassword("root")
+            .jdbcUrl(System.getenv("DB_URL"))
+            .jdbcUsername(System.getenv("DB_USER"))
+            .jdbcPassword(System.getenv("DB_PASSWORD"))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.jdbcUrl("jdbc:mysql://localhost:3306/booking_db")
.jdbcUsername("root")
.jdbcPassword("root")
.jdbcUrl(System.getenv("DB_URL"))
.jdbcUsername(System.getenv("DB_USER"))
.jdbcPassword(System.getenv("DB_PASSWORD"))
🤖 Prompt for AI Agents
In @src/main/java/org/example/App.java around lines 14 - 16, App.java currently
hard-codes DB connection details via the .jdbcUrl, .jdbcUsername and
.jdbcPassword builder calls; replace these hard-coded values with configurable
sources (prefer environment variables or a configuration/properties file) by
reading
System.getenv("DB_URL")/System.getenv("DB_USER")/System.getenv("DB_PASS") or a
loaded Properties object and passing those values into the
.jdbcUrl/.jdbcUsername/.jdbcPassword calls, providing sensible fallbacks or
failing fast with clear errors if values are missing.

Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @src/main/java/org/example/Menu.java:
- Around line 7-15: Menu currently holds a Scanner (field scanner) that is never
closed; implement AutoCloseable on the Menu class and add a close() method that
closes the Scanner instance (scanner.close()), then update usages (e.g., where
Menu is constructed and started in App.java) to use try-with-resources so Menu
is auto-closed; ensure the Menu constructor and start() semantics remain
unchanged but the class signature implements AutoCloseable and the scanner field
is closed in close().
🧹 Nitpick comments (2)
src/main/java/org/example/Menu.java (2)

17-48: Consider more specific exception handling.

The catch block handles all exceptions generically, which keeps the menu running but might mask programming errors. For a CLI application this is acceptable, but you could differentiate between expected input errors and unexpected runtime exceptions.

♻️ Optional: More granular exception handling
         while (true) {
             try {
                 System.out.println("\n=== Booking System ===");
                 System.out.println("1) Create booking");
                 System.out.println("2) View all bookings");
                 System.out.println("3) View all bookings by guest");
                 System.out.println("4) View all guests by booking");
                 System.out.println("5) Cancel booking");
                 System.out.println("0) Exit");
                 System.out.print("Choose: ");
 
                 String choice = scanner.nextLine();
 
                 switch (choice) {
                     case "1" -> createBooking();
                     case "2" -> printBookings();
                     case "3" -> printBookingsByGuest();
                     case "4" -> printGuestsByBooking();
                     case "5" -> removeBooking();
                     case "0" -> {
                         System.out.println("Goodbye!");
                         return;
                     }
                     default -> System.out.println("Invalid choice");
                 }
+            } catch (java.util.NoSuchElementException | IllegalStateException e) {
+                System.err.println("Error reading input: " + e.getMessage());
-            } catch (Exception e) {
-                System.err.println("Error reading input: " + e.getMessage());
+            } catch (Exception e) {
+                System.err.println("Unexpected error: " + e.getMessage());
+                e.printStackTrace();
             }
 
         }

51-71: Stub methods are appropriately marked for future implementation.

The placeholder methods with //todo comments clearly outline the intended functionality. The structure provides a good foundation for implementing the booking operations.

Do you want me to help generate implementation code for any of these methods, or open issues to track each implementation task?

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ce0b8a1 and 0363d67.

📒 Files selected for processing (2)
  • src/main/java/org/example/App.java
  • src/main/java/org/example/Menu.java
🔇 Additional comments (2)
src/main/java/org/example/App.java (1)

15-28: LGTM! Clean refactoring to Menu-driven architecture.

The extraction of CLI logic into the Menu class is well-executed. The EntityManagerFactory is properly managed with try-with-resources, and the Menu receives the necessary repository dependencies.

src/main/java/org/example/Menu.java (1)

1-73: Verification confirmed: removed App methods are not called elsewhere.

No references to the removed App methods (printBookings, printBookingsByGuest, printGuestsByBooking, createBooking, removeBooking) exist in the codebase. App.java correctly instantiates Menu and delegates to menu.start().

Comment on lines +7 to +15
private final Scanner scanner = new Scanner(System.in);

private final GuestRepository guestRepo;
private final BookingRepository bookingRepo;

public Menu(GuestRepository guestRepo, BookingRepository bookingRepo) {
this.guestRepo = guestRepo;
this.bookingRepo = bookingRepo;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Scanner resource leak - implement AutoCloseable.

The Scanner is never closed, leaking the System.in resource when the menu exits. Menu should implement AutoCloseable and close the Scanner, or App.java should call a cleanup method.

🔒 Proposed fix: Implement AutoCloseable
-public class Menu {
+public class Menu implements AutoCloseable {
 
     private final Scanner scanner = new Scanner(System.in);
 
     private final GuestRepository guestRepo;
     private final BookingRepository bookingRepo;
 
     public Menu(GuestRepository guestRepo, BookingRepository bookingRepo) {
         this.guestRepo = guestRepo;
         this.bookingRepo = bookingRepo;
     }
+
+    @Override
+    public void close() {
+        scanner.close();
+    }

Then update App.java to use try-with-resources:

try (EntityManagerFactory emf = cfg.createEntityManagerFactory();
     Menu menu = new Menu(new GuestRepository(emf), 
                          new BookingRepository(emf, new GuestRepository(emf)))) {
    menu.start();
}

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In @src/main/java/org/example/Menu.java around lines 7 - 15, Menu currently
holds a Scanner (field scanner) that is never closed; implement AutoCloseable on
the Menu class and add a close() method that closes the Scanner instance
(scanner.close()), then update usages (e.g., where Menu is constructed and
started in App.java) to use try-with-resources so Menu is auto-closed; ensure
the Menu constructor and start() semantics remain unchanged but the class
signature implements AutoCloseable and the scanner field is closed in close().

apaegs and others added 5 commits January 9, 2026 15:31
Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>
Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>
Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>
Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>
Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>
coderabbitai[bot]

This comment was marked as resolved.

apaegs and others added 9 commits January 12, 2026 11:22
Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>
Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>
Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>
Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>
Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>
…in Menu.java

Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>
Co-authored-by: Elias Lennheimer Lennheimer.elias@gmail.com
Co-authored-by: Martin Karlsson Martin.Karlsson@iths.se
Co-authored-by: Rickard Ankar rickard.ankar@iths.se
# Conflicts:
#	src/main/java/org/example/App.java
Implement priceCalculation()

Co-authored-by: Elias Lennheimer Lennheimer.elias@gmail.com
Co-authored-by: Martin Karlsson Martin.Karlsson@iths.se
Co-authored-by: Rickard Ankar rickard.ankar@iths.se

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/java/org/example/BookingRepository.java (1)

104-127: getFirst() will throw NoSuchElementException if no rooms are available.

Line 106 calls getEmptyRooms(...).getFirst() without checking if the list is empty. If no rooms match the criteria, this will throw an exception. The caller (Menu.createBooking) does check getEmptyRooms first (line 165), but this method doesn't enforce that contract.

🐛 Suggested defensive fix
     public boolean create(List<String> emailList, LocalDate startDate, LocalDate endDate, int numberOfGuests, BigDecimal totalPrice){
         return emf.callInTransaction(em -> {
-            Room room = getEmptyRooms(startDate, endDate, numberOfGuests).getFirst();
+            List<Room> emptyRooms = getEmptyRooms(startDate, endDate, numberOfGuests);
+            if (emptyRooms.isEmpty()) {
+                return false;
+            }
+            Room room = emptyRooms.getFirst();
🤖 Fix all issues with AI agents
In @src/main/java/org/example/Menu.java:
- Around line 87-92: The guest existence check prints a message but does not
stop execution, so bookingRepo.getBookingInfoByGuest(email) will run for a
non-existent guest; update the control flow in the method containing
guestRepo.guestExist(email) (the block that assigns var bookings) to return (or
otherwise exit/skip the subsequent logic) immediately after printing the "No
guest found" message when guestRepo.guestExist(email) is false, ensuring
bookingRepo.getBookingInfoByGuest(email) is only called for existing guests.
- Line 170: The print statement in Menu (the System.out.println that builds the
guest label using variable i) suffers from operator precedence so `"guest #" + i
+ 1` concatenates i as a string then adds 1; fix it by grouping the arithmetic
or formatting the value—e.g., build the string as "guest #" + (i + 1) or use
String.format("guest #%d", i + 1) where the current System.out.println occurs so
guest numbering prints correctly.
- Around line 162-196: The bookingRepo.create(...) call is executed even when no
rooms are available, causing an exception; wrap the booking creation logic so
that bookingRepo.create(emails, start, end, guests, totalPrice) is only invoked
inside the existing if (!bookingRepo.getEmptyRooms(start, end,
guests).isEmpty()) block after emails is populated, and add an else branch that
prints a clear "No rooms available" (or similar) message and returns/aborts the
booking flow; ensure the emails list and related guest prompt loop remain
scoped/used only when the room-availability condition is true.
🧹 Nitpick comments (6)
src/main/java/org/example/BookingService.java (3)

10-18: Unused emf field.

The emf field is injected via the constructor but never used within the class. Consider removing it if it's not needed, or document its intended future use.

♻️ Suggested fix
 public class BookingService {
-    private EntityManagerFactory emf;
     private BookingRepository bookingRepository;
     private BigDecimal priceClass;

-    public BookingService (EntityManagerFactory emf, BookingRepository bookingRepository) {
-        this.emf = emf;
+    public BookingService (BookingRepository bookingRepository) {
         this.bookingRepository = bookingRepository;
         priceClass = BigDecimal.valueOf(300);
     }

Note: This would require updating App.java as well.


38-52: Validation method has side effects (console output).

validateValues prints error messages directly to System.out, coupling validation logic with presentation. This makes the service harder to test and reuse. Consider returning a validation result object or throwing exceptions, letting the caller handle user messaging.


27-36: Consider using Optional instead of sentinel value -1.

Returning -1 to indicate validation failure is error-prone and requires callers to remember to check for it. An Optional<BigDecimal> or a dedicated result type would be more explicit and type-safe.

src/main/java/org/example/Menu.java (3)

58-75: Consider early return for empty bookings.

The empty check prints a message but still continues to the forEach. While harmless (empty list iteration does nothing), an early return would be clearer.

♻️ Suggested improvement
         // Validate that bookings is not empty
         if (bookings.isEmpty()) {
             System.out.println("No bookings found.");
+            return;
         }

137-153: Unhandled parse exceptions for user input.

LocalDate.parse() (lines 139, 142) and Integer.parseInt() (line 151) can throw exceptions on malformed input. While the outer catch in start() handles this, users will see cryptic messages. Consider adding input validation with user-friendly error messages.


217-232: Inverted boolean semantics in validateBookingId.

The method returns true when validation fails, which is counterintuitive. Callers use if (validateBookingId(bookingId)) return;, which works but is confusing. Consider renaming to isInvalidBookingId() or inverting the logic.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9bf0eeb and 73570d5.

📒 Files selected for processing (4)
  • src/main/java/org/example/App.java
  • src/main/java/org/example/BookingRepository.java
  • src/main/java/org/example/BookingService.java
  • src/main/java/org/example/Menu.java
🔇 Additional comments (4)
src/main/java/org/example/BookingRepository.java (2)

143-153: LGTM!

The getMaxGuests method correctly handles the case where no rooms exist by using Optional.ofNullable and returning 0 as a safe default.


20-40: LGTM!

The parameter type change from long to int is appropriate for guest counts and consistent with the rest of the codebase.

src/main/java/org/example/Menu.java (1)

24-55: LGTM!

The main menu loop structure is clear, with proper exit handling and exception recovery for CLI robustness.

src/main/java/org/example/App.java (1)

21-28: LGTM!

Clean separation of concerns - App handles configuration and dependency wiring, then delegates CLI control to Menu. The wiring matches the updated BookingService constructor signature.

Comment thread src/main/java/org/example/Menu.java
Comment thread src/main/java/org/example/Menu.java Outdated
Comment thread src/main/java/org/example/Menu.java Outdated
Co-authored-by: Elias Lennheimer Lennheimer.elias@gmail.com
Co-authored-by: Martin Karlsson Martin.Karlsson@iths.se
Co-authored-by: Rickard Ankar rickard.ankar@iths.se
Co-authored-by: Elias Lennheimer Lennheimer.elias@gmail.com
Co-authored-by: Martin Karlsson Martin.Karlsson@iths.se
Co-authored-by: Rickard Ankar rickard.ankar@iths.se
Co-authored-by: Elias Lennheimer Lennheimer.elias@gmail.com
Co-authored-by: Martin Karlsson Martin.Karlsson@iths.se
Co-authored-by: Rickard Ankar rickard.ankar@iths.se
Co-authored-by: Elias Lennheimer Lennheimer.elias@gmail.com
Co-authored-by: Martin Karlsson Martin.Karlsson@iths.se
Co-authored-by: Rickard Ankar rickard.ankar@iths.se
Co-authored-by: Elias Lennheimer Lennheimer.elias@gmail.com
Co-authored-by: Martin Karlsson Martin.Karlsson@iths.se
Co-authored-by: Rickard Ankar rickard.ankar@iths.se
Co-authored-by: Elias Lennheimer Lennheimer.elias@gmail.com
Co-authored-by: Martin Karlsson Martin.Karlsson@iths.se
Co-authored-by: Rickard Ankar rickard.ankar@iths.se

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @src/main/java/org/example/Menu.java:
- Around line 96-108: The check for an empty bookings list prints "No bookings
found." but doesn't return, so the method continues to the bookings.forEach
call; add an early return after the System.out.println("No bookings found.") so
the method exits immediately when bookings.isEmpty() is true (matching the
behavior of printBookings()), keeping the rest of the method (the
bookings.forEach that prints each Booking b with b.id(), b.roomNumber(),
b.startDate(), b.endDate(), b.totalPrice()) unchanged.
- Around line 139-155: User input parsing can throw DateTimeParseException and
NumberFormatException and the current return on invalid date exits the method
instead of re-prompting; wrap the LocalDate.parse calls and Integer.parseInt
call in try/catch blocks (catch DateTimeParseException and
NumberFormatException), print a friendly error and continue the loop rather than
returning, and only call bookingService.calculateTotalPrice when parsing
succeeds; adjust the do/while loop to use continue to restart on invalid input
and ensure totalPrice is only used after successful parsing so the loop
condition (totalPrice.signum()) is safe.
🧹 Nitpick comments (2)
src/main/java/org/example/Menu.java (2)

186-189: Redundant condition.

The else if (guestRepo.guestExist(email)) check on line 187 is always true at this point since the if on line 176 already established the guest exists when this branch is reached.

Proposed simplification
                         if (isValidGuestNames(firstName, lastName)) {
                             guestRepo.create(firstName, lastName, email);
                             guestExists = true;
                         }

-                    } else if (guestRepo.guestExist(email)){
+                    } else {
+                        // Guest already exists
                         guestExists = true;
                     }

195-198: Condition !email.isEmpty() is always true here.

At line 195, email is guaranteed to be non-empty because the inner while (!guestExists) loop only exits after a non-empty email is processed (checked at line 175). This condition can be removed for clarity.

Proposed simplification
-            if (!email.isEmpty()) {
-                bookingRepo.create(emails, start, end, guests, totalPrice);
-                System.out.println("Booking created.");
-            }
+            bookingRepo.create(emails, start, end, guests, totalPrice);
+            System.out.println("Booking created.");
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 80d757e and 5cd9e76.

📒 Files selected for processing (1)
  • src/main/java/org/example/Menu.java
🔇 Additional comments (6)
src/main/java/org/example/Menu.java (6)

10-22: LGTM! Clean dependency injection.

The constructor properly injects dependencies as final fields. The Scanner wrapping System.in is intentionally left unclosed since closing it would close System.in globally, which is the correct approach for CLI applications.


24-55: LGTM! Well-structured menu loop.

The menu loop is clean with proper exit handling. The broad exception catch provides resilience for a CLI application while still logging errors.


58-76: LGTM!

Clean implementation with proper empty-state handling.


111-132: LGTM!

Good validation flow and proper empty-state handling with early return.


208-221: LGTM!

Proper validation and exception handling for the cancellation flow.


223-246: LGTM!

Validation helpers are straightforward. Minor inconsistency: isInvalidBookingId is static while isValidGuestNames is not (though neither uses instance state). Consider making them consistent, but this is a nitpick.

Comment thread src/main/java/org/example/Menu.java
Co-authored-by: Elias Lennheimer Lennheimer.elias@gmail.com
Co-authored-by: Martin Karlsson Martin.Karlsson@iths.se
Co-authored-by: Rickard Ankar rickard.ankar@iths.se
Co-authored-by: Elias Lennheimer Lennheimer.elias@gmail.com
Co-authored-by: Martin Karlsson Martin.Karlsson@iths.se
Co-authored-by: Rickard Ankar rickard.ankar@iths.se
Co-authored-by: Elias Lennheimer Lennheimer.elias@gmail.com
Co-authored-by: Martin Karlsson Martin.Karlsson@iths.se
Co-authored-by: Rickard Ankar rickard.ankar@iths.se

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In @src/main/java/org/example/Menu.java:
- Around line 179-182: The code lowers guest names by calling .toLowerCase() on
input when assigning firstName and lastName; instead preserve user casing or
apply title-case: remove the .toLowerCase() calls on the scanner results (keep
.trim()) or replace them with a capitalization routine that uppercases the first
character and lowercases the rest before assigning to firstName and lastName;
update any downstream uses that assumed lowercase if necessary (look for the
variables firstName and lastName in Menu.java).
- Around line 142-157: The loop can call
bookingService.calculateTotalPrice(guests, start, end) with null start/end when
LocalDate.parse or Integer.parse fails; in the catch block for
NumberFormatException | DateTimeParseException, skip the calculation and prompt
again by using continue (or otherwise re-check that start, end and guests are
non-null/valid before calling bookingService.calculateTotalPrice), so modify the
do/while to continue on parse errors and only call
bookingService.calculateTotalPrice when start and end are successfully parsed
and guests is a valid positive integer.
- Around line 159-169: Move the room-availability check so it runs before
printing or confirming the total price: call bookingRepo.getEmptyRooms(start,
end, guests) immediately after date/guest input and before the
System.out.println that displays totalPrice and before prompting the scanner for
confirmation, returning or informing the user if the list is empty; also change
the rounding behavior on totalPrice from RoundingMode.CEILING to
RoundingMode.HALF_UP to avoid always rounding up (update the
totalPrice.setScale(...) call accordingly).
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5cd9e76 and 158ffc9.

📒 Files selected for processing (1)
  • src/main/java/org/example/Menu.java
🔇 Additional comments (5)
src/main/java/org/example/Menu.java (5)

11-24: LGTM - Clean dependency injection setup.

Constructor injection with final fields is the right pattern here. The Scanner(System.in) is intentionally not closed since closing it would also close System.in, which would break subsequent input operations.


26-57: LGTM - Well-structured menu loop.

The switch expression provides clean dispatch, and the broad exception handler is acceptable for a CLI application to prevent crashes from unexpected input errors.


60-112: LGTM - Good validation and output handling.

Both methods properly check for empty results before iterating and provide clear user feedback. The email validation in printBookingsByGuest() correctly checks existence before querying bookings.


114-135: LGTM - Clean implementation.

Proper validation delegation and clear error messaging for edge cases.


208-246: LGTM - Clean validation helpers and error handling.

The isInvalidBookingId() method consolidates validation logic well, and removeBooking() properly handles repository exceptions with user-friendly error messages.

Comment thread src/main/java/org/example/Menu.java Outdated
Comment on lines +179 to +182
System.out.print("First name: ");
String firstName = scanner.nextLine().trim().toLowerCase();
System.out.print("Last name: ");
String lastName = scanner.nextLine().trim().toLowerCase();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Lowercasing guest names may be undesirable.

Converting firstName and lastName to lowercase results in names like "john smith" instead of "John Smith". Consider preserving the case or applying proper title case instead.

Suggested fix: preserve original case or capitalize
                             System.out.print("First name: ");
-                            String firstName = scanner.nextLine().trim().toLowerCase();
+                            String firstName = scanner.nextLine().trim();
                             System.out.print("Last name: ");
-                            String lastName = scanner.nextLine().trim().toLowerCase();
+                            String lastName = scanner.nextLine().trim();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
System.out.print("First name: ");
String firstName = scanner.nextLine().trim().toLowerCase();
System.out.print("Last name: ");
String lastName = scanner.nextLine().trim().toLowerCase();
System.out.print("First name: ");
String firstName = scanner.nextLine().trim();
System.out.print("Last name: ");
String lastName = scanner.nextLine().trim();
🤖 Prompt for AI Agents
In @src/main/java/org/example/Menu.java around lines 179 - 182, The code lowers
guest names by calling .toLowerCase() on input when assigning firstName and
lastName; instead preserve user casing or apply title-case: remove the
.toLowerCase() calls on the scanner results (keep .trim()) or replace them with
a capitalization routine that uppercases the first character and lowercases the
rest before assigning to firstName and lastName; update any downstream uses that
assumed lowercase if necessary (look for the variables firstName and lastName in
Menu.java).

Co-authored-by: Elias Lennheimer Lennheimer.elias@gmail.com
Co-authored-by: Martin Karlsson Martin.Karlsson@iths.se
Co-authored-by: Rickard Ankar rickard.ankar@iths.se

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @src/main/java/org/example/Menu.java:
- Around line 142-162: The catch block handling NumberFormatException |
DateTimeParseException must stop the current loop iteration so null/invalid
values aren't passed to bookingRepo.getEmptyRooms(start, end, guests); update
the try/catch in the do/while around LocalDate.parse and Integer.parseInt to add
a continue after printing the error message so the loop restarts when parsing
fails; ensure you reference the existing symbols LocalDate.parse,
bookingRepo.getEmptyRooms(start, end, guests), and the variables
start/end/guests when making this change.
🧹 Nitpick comments (2)
src/main/java/org/example/Menu.java (2)

174-205: Redundant room availability check.

The room availability check at line 174 duplicates the check already performed at line 156. Since significant user interaction occurs between these checks, consider either:

  1. Removing the redundant check if concurrency isn't a concern, or
  2. Keeping only the check at line 174 (right before booking creation) and removing line 156-159 to avoid the TOCTOU gap.
♻️ Option 1: Remove redundant check (simpler)
         List<String> emails = new ArrayList<>();
         String email = "";
 
-        if (!bookingRepo.getEmptyRooms(start, end, guests).isEmpty()) {
-            for (int i = 0; i < guests; i++) {
+        for (int i = 0; i < guests; i++) {
             // ... guest collection logic
-            }
+        }
 
-            bookingRepo.create(emails, start, end, guests, totalPrice);
-            System.out.println("Booking created.");
+        if (bookingRepo.getEmptyRooms(start, end, guests).isEmpty()) {
+            System.out.println("No rooms available anymore.");
+            return;
+        }
 
-        }
+        bookingRepo.create(emails, start, end, guests, totalPrice);
+        System.out.println("Booking created.");

242-248: Consider making this method static for consistency.

This method doesn't use any instance state, similar to isInvalidBookingId. Making it static would be consistent with the other validation helper.

♻️ Suggested change
-    private boolean isValidGuestNames(String firstName, String lastName){
+    private static boolean isValidGuestNames(String firstName, String lastName) {
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 158ffc9 and e11762a.

📒 Files selected for processing (1)
  • src/main/java/org/example/Menu.java
🔇 Additional comments (7)
src/main/java/org/example/Menu.java (7)

11-24: LGTM!

Constructor properly uses dependency injection for repositories and service. The Scanner wrapping System.in is correctly kept as a field without explicit closing, since closing it would close System.in and break further input in the CLI application.


26-57: LGTM!

The menu loop structure is clean with proper exit handling. The broad exception catch is acceptable for a CLI application to prevent unexpected crashes while still logging errors.


60-78: LGTM!

Clean implementation with proper handling of empty bookings list before iterating.


80-112: LGTM!

Proper validation sequence: checks for empty email, verifies guest existence, then handles empty booking results gracefully.


114-135: LGTM!

Clean implementation with proper validation and empty result handling.


210-223: LGTM!

Proper validation and exception handling for the cancellation flow.


225-240: LGTM!

Clear validation logic with appropriate error messages for both empty and non-numeric booking IDs.

Comment thread src/main/java/org/example/Menu.java
Co-authored-by: Elias Lennheimer Lennheimer.elias@gmail.com
Co-authored-by: Martin Karlsson Martin.Karlsson@iths.se
Co-authored-by: Rickard Ankar rickard.ankar@iths.se
Co-authored-by: Elias Lennheimer Lennheimer.elias@gmail.com
Co-authored-by: Martin Karlsson Martin.Karlsson@iths.se
Co-authored-by: Rickard Ankar rickard.ankar@iths.se

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/main/java/org/example/Menu.java (3)

167-171: Consider explicit confirmation handling.

Currently, only "n" cancels the booking; any other input (including typos like "yes", "ok", or accidental enters) proceeds with the booking. For a more defensive approach, require explicit "y" confirmation.

Suggested fix
     System.out.println("Do you want to continue? (Y/N)");
     String input = scanner.nextLine().toLowerCase();
-    if (input.equals("n")) {
+    if (!input.equals("y")) {
+        System.out.println("Booking cancelled.");
         return;
     }

176-176: Redundant room availability check.

This check duplicates line 158. If no rooms were available, the method would have returned at line 160. However, in a concurrent environment, this re-check provides a safeguard against rooms being booked between the price display and booking creation. Consider whether this is intentional (and if so, add a comment) or can be removed.


187-189: Guest names are lowercased—is this intentional?

Converting firstName and lastName to lowercase may not align with user expectations, as names typically preserve their original casing (e.g., "John Doe" vs "john doe").

Suggested fix
                         System.out.print("First name: ");
-                        String firstName = scanner.nextLine().trim().toLowerCase();
+                        String firstName = scanner.nextLine().trim();
                         System.out.print("Last name: ");
-                        String lastName = scanner.nextLine().trim().toLowerCase();
+                        String lastName = scanner.nextLine().trim();
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e11762a and c1bd9ea.

📒 Files selected for processing (1)
  • src/main/java/org/example/Menu.java
🔇 Additional comments (8)
src/main/java/org/example/Menu.java (8)

12-25: LGTM!

The class structure and dependency injection via constructor are clean. The INVALID sentinel constant is a reasonable approach for tracking uninitialized state.


27-58: LGTM!

The main menu loop is well-structured with clear options and proper exit handling. The switch expression syntax is clean.


61-79: LGTM!

Clean implementation with proper empty-check before iteration.


81-113: LGTM!

Good validation sequence: empty check → guest existence → bookings existence. Clear error messages for each case.


115-136: LGTM!

Proper validation using the helper method and clear output formatting.


212-225: LGTM!

Proper validation and error handling for the cancel operation.


227-242: LGTM!

Validation logic is correct. Minor note: this is static while isValidGuestNames is an instance method—consider making them consistent, though it's not critical.


244-250: LGTM!

Simple and effective validation. The upstream trim() calls ensure whitespace-only inputs are properly rejected.

Comment thread src/main/java/org/example/Menu.java Outdated
Comment on lines +143 to +164
do {
try {
System.out.print("Start date (YYYY-MM-DD): ");
start = LocalDate.parse(scanner.nextLine());

System.out.print("End date (YYYY-MM-DD): ");
end = LocalDate.parse(scanner.nextLine());

System.out.print("Number of guests: ");
guests = Integer.parseInt(scanner.nextLine());
} catch (NumberFormatException | DateTimeParseException e) {
System.out.println("Invalid Date format/Number of guests needs to be a whole number.");
continue;
}

if (bookingRepo.getEmptyRooms(start, end, guests).isEmpty()) {
System.out.println("No rooms available for the selected dates and guest count.");
return;
}

totalPrice = bookingService.calculateTotalPrice(guests, start, end);
} while (Objects.requireNonNull(totalPrice).signum() != 1);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Potential infinite loop if price calculation returns zero or negative.

The do-while condition Objects.requireNonNull(totalPrice).signum() != 1 loops until the price is positive. However, if calculateTotalPrice consistently returns a non-positive value (e.g., due to invalid business rules or edge cases), the user has no way to exit this loop—there's no prompt or break mechanism.

Suggested fix
         } catch (NumberFormatException | DateTimeParseException e) {
             System.out.println("Invalid Date format/Number of guests needs to be a whole number.");
             continue;
         }

         if (bookingRepo.getEmptyRooms(start, end, guests).isEmpty()) {
             System.out.println("No rooms available for the selected dates and guest count.");
             return;
         }

         totalPrice = bookingService.calculateTotalPrice(guests, start, end);
-    } while (Objects.requireNonNull(totalPrice).signum() != 1);
+        if (totalPrice == null || totalPrice.signum() != 1) {
+            System.out.println("Could not calculate a valid price. Please try different dates or guest count.");
+            System.out.print("Try again? (Y/N): ");
+            if (scanner.nextLine().trim().equalsIgnoreCase("n")) {
+                return;
+            }
+        }
+    } while (totalPrice == null || totalPrice.signum() != 1);

Co-authored-by: Elias Lennheimer Lennheimer.elias@gmail.com
Co-authored-by: Martin Karlsson Martin.Karlsson@iths.se
Co-authored-by: Rickard Ankar rickard.ankar@iths.se

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In @src/main/java/org/example/Menu.java:
- Around line 188-190: The code currently forces guest names to lowercase by
calling .toLowerCase() on scanner input; remove the .toLowerCase() calls so
firstName and lastName are assigned with scanner.nextLine().trim() to preserve
user casing, or alternatively apply a proper capitalization routine (e.g.,
title-case each word) before storing; update the two assignments for firstName
and lastName (the lines currently using .toLowerCase()) accordingly and ensure
any downstream usage expects the preserved/formatted casing.
- Around line 167-172: The confirmation logic in Menu.java currently treats
anything other than "n" as confirmation; update the block that reads from
scanner (the variable scanner) to explicitly accept "y" (and "Y") as
confirmation, treat "n" (and "N") as cancellation, and loop to reprompt on any
other invalid input until a valid response is provided; replace the current
single-read check (input.equals("n")) with a small loop that normalizes input
(toLowerCase or equalsIgnoreCase) and only proceeds or returns when "y" or "n"
is entered.
- Around line 163-164: The do-while loop using
Objects.requireNonNull(totalPrice).signum() != 1 in Menu (around totalPrice and
bookingService.calculateTotalPrice) can trap the user in an infinite loop when
calculateTotalPrice returns zero/negative; change the flow to explicitly handle
three cases after calling bookingService.calculateTotalPrice: if the result is
null or negative treat it as a service error (log/show a clear message and
either break/return to the main menu or throw a handled exception), if the
result is zero treat it as a valid but free booking (inform the user and
continue), and if positive proceed; also add a user-visible escape path or retry
limit so the UI does not loop forever.
🧹 Nitpick comments (5)
src/main/java/org/example/Menu.java (5)

21-25: Constructor lacks null validation for dependencies.

Consider adding null checks or using Objects.requireNonNull() for the injected dependencies to fail fast if misconfigured.

♻️ Optional improvement
 public Menu(GuestRepository guestRepo, BookingRepository bookingRepo, BookingService bookingService) {
-    this.guestRepo = guestRepo;
-    this.bookingRepo = bookingRepo;
-    this.bookingService = bookingService;
+    this.guestRepo = Objects.requireNonNull(guestRepo, "guestRepo cannot be null");
+    this.bookingRepo = Objects.requireNonNull(bookingRepo, "bookingRepo cannot be null");
+    this.bookingService = Objects.requireNonNull(bookingService, "bookingService cannot be null");
 }

53-55: Overly broad exception handling.

Catching Exception is too broad and may mask programming errors. Consider catching only expected exceptions like NoSuchElementException from the scanner.


83-89: Consider trimming input and checking for blank strings.

Using isEmpty() won't catch whitespace-only input. Consider using isBlank() (Java 11+) or trimming the input first for consistency with other methods in this class.

♻️ Suggested improvement
     System.out.print("Guest email: ");
-    String email = scanner.nextLine();
+    String email = scanner.nextLine().trim();

     // Validate if email is not empty
-    if (email.isEmpty()) {
+    if (email.isBlank()) {
         System.out.println("Email cannot be empty");
         return;
     }

213-226: Consider adding confirmation before cancelling booking.

The booking is cancelled immediately without user confirmation. For destructive operations, a Y/N prompt (similar to the one in createBooking) would prevent accidental cancellations.


245-251: Inconsistent method modifiers and validation approach.

isInvalidBookingId is static while isValidGuestNames is an instance method, though neither uses instance state. Also, isValidGuestNames uses isEmpty() which doesn't catch whitespace-only names. Consider using isBlank() for consistency.

♻️ Suggested fix
-    private boolean isValidGuestNames(String firstName, String lastName){
-        if (firstName.isEmpty() || lastName.isEmpty()) {
+    private static boolean isValidGuestNames(String firstName, String lastName) {
+        if (firstName.isBlank() || lastName.isBlank()) {
             System.out.println("First and last name can't be blank.");
             return false;
         }
         return true;
     }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c1bd9ea and f63c365.

📒 Files selected for processing (1)
  • src/main/java/org/example/Menu.java
🔇 Additional comments (2)
src/main/java/org/example/Menu.java (2)

61-79: LGTM!

The method correctly handles the empty bookings case and uses a clean lambda expression for output formatting.


115-136: LGTM!

The method properly validates the booking ID and handles the case where no guests are found.

Comment thread src/main/java/org/example/Menu.java Outdated
Comment on lines +163 to +164
totalPrice = bookingService.calculateTotalPrice(guests, start, end);
} while (Objects.requireNonNull(totalPrice).signum() != 1);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Confusing loop condition and potential infinite loop.

The do-while condition Objects.requireNonNull(totalPrice).signum() != 1 will loop indefinitely if calculateTotalPrice returns zero or a negative value. If this indicates a service-layer error rather than invalid user input, the user has no way to exit. Consider clarifying when a non-positive price can occur and providing an escape path.

🐛 Suggested fix
             totalPrice = bookingService.calculateTotalPrice(guests, start, end);
-        } while (Objects.requireNonNull(totalPrice).signum() != 1);
+            if (totalPrice == null || totalPrice.signum() != 1) {
+                System.out.println("Could not calculate a valid price. Please try again or enter 'q' to cancel.");
+                String retry = scanner.nextLine().trim().toLowerCase();
+                if (retry.equals("q")) {
+                    return;
+                }
+            }
+        } while (totalPrice == null || totalPrice.signum() != 1);
🤖 Prompt for AI Agents
In @src/main/java/org/example/Menu.java around lines 163 - 164, The do-while
loop using Objects.requireNonNull(totalPrice).signum() != 1 in Menu (around
totalPrice and bookingService.calculateTotalPrice) can trap the user in an
infinite loop when calculateTotalPrice returns zero/negative; change the flow to
explicitly handle three cases after calling bookingService.calculateTotalPrice:
if the result is null or negative treat it as a service error (log/show a clear
message and either break/return to the main menu or throw a handled exception),
if the result is zero treat it as a valid but free booking (inform the user and
continue), and if positive proceed; also add a user-visible escape path or retry
limit so the UI does not loop forever.

Comment on lines +167 to +172
System.out.println("Do you want to continue? (Y/N)");
String input = scanner.nextLine().toLowerCase();
if (input.equals("n")) {
System.out.println("Booking cancelled.");
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Confirmation prompt accepts any input except "n" as affirmative.

Only "n" cancels the booking; any other input (including typos like "no", "N", or accidental keypresses) proceeds. Consider explicitly checking for "y" and reprompting on invalid input.

💡 Suggested improvement
     System.out.println("Do you want to continue? (Y/N)");
-    String input = scanner.nextLine().toLowerCase();
-    if (input.equals("n")) {
-        System.out.println("Booking cancelled.");
-        return;
+    String input = scanner.nextLine().trim().toLowerCase();
+    while (!input.equals("y") && !input.equals("n")) {
+        System.out.println("Please enter Y or N:");
+        input = scanner.nextLine().trim().toLowerCase();
+    }
+    if (input.equals("n")) {
+        System.out.println("Booking cancelled.");
+        return;
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
System.out.println("Do you want to continue? (Y/N)");
String input = scanner.nextLine().toLowerCase();
if (input.equals("n")) {
System.out.println("Booking cancelled.");
return;
}
System.out.println("Do you want to continue? (Y/N)");
String input = scanner.nextLine().trim().toLowerCase();
while (!input.equals("y") && !input.equals("n")) {
System.out.println("Please enter Y or N:");
input = scanner.nextLine().trim().toLowerCase();
}
if (input.equals("n")) {
System.out.println("Booking cancelled.");
return;
}
🤖 Prompt for AI Agents
In @src/main/java/org/example/Menu.java around lines 167 - 172, The confirmation
logic in Menu.java currently treats anything other than "n" as confirmation;
update the block that reads from scanner (the variable scanner) to explicitly
accept "y" (and "Y") as confirmation, treat "n" (and "N") as cancellation, and
loop to reprompt on any other invalid input until a valid response is provided;
replace the current single-read check (input.equals("n")) with a small loop that
normalizes input (toLowerCase or equalsIgnoreCase) and only proceeds or returns
when "y" or "n" is entered.

Comment on lines +177 to +208
if (!bookingRepo.getEmptyRooms(start, end, guests).isEmpty()) {
for (int i = 0; i < guests; i++) {
boolean guestExists = false;
while (!guestExists) {
// verify not empty
System.out.println("Enter details for guest #" + (i + 1));
System.out.print("Email: ");
email = scanner.nextLine().trim().toLowerCase();
if (!email.isEmpty()) {
if (!guestRepo.guestExist(email)) {
System.out.print("First name: ");
String firstName = scanner.nextLine().trim().toLowerCase();
System.out.print("Last name: ");
String lastName = scanner.nextLine().trim().toLowerCase();

if (isValidGuestNames(firstName, lastName)) {
guestRepo.create(firstName, lastName, email);
guestExists = true;
}

} else{
guestExists = true;
}
}
}
emails.add(email);
}

bookingRepo.create(emails, start, end, guests, totalPrice);
System.out.println("Booking created.");

}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n src/main/java/org/example/Menu.java | sed -n '150,215p'

Repository: ithsjava25/project-jpa-grupp-6-g

Length of output: 3215


🏁 Script executed:

find . -type f -name "*BookingRepository*" | head -5

Repository: ithsjava25/project-jpa-grupp-6-g

Length of output: 126


🏁 Script executed:

rg -n "getEmptyRooms|bookingRepo\.create|@Transactional|synchronized" --type=java -A 3 -B 3

Repository: ithsjava25/project-jpa-grupp-6-g

Length of output: 2953


🏁 Script executed:

cat -n src/main/java/org/example/BookingRepository.java | sed -n '104,130p'

Repository: ithsjava25/project-jpa-grupp-6-g

Length of output: 1362


🏁 Script executed:

wc -l src/main/java/org/example/BookingRepository.java

Repository: ithsjava25/project-jpa-grupp-6-g

Length of output: 128


🏁 Script executed:

cat -n src/main/java/org/example/BookingRepository.java

Repository: ithsjava25/project-jpa-grupp-6-g

Length of output: 6764


Remove redundant availability check at line 177; implement atomic room booking.

The availability check at line 177 is redundant—if no rooms were available at line 158, the method returns early at line 160, making the second check always true. More critically, the time gap between the first check (line 177) and booking creation (line 205) allows a concurrent user to book the same room after the initial availability check passes (TOCTOU race condition). While BookingRepository.create() re-checks availability at line 106 inside a transaction, the separate callInTransaction() call for getEmptyRooms() may not provide sufficient isolation from concurrent bookings made by other threads.

Remove the redundant outer if at line 177, and refactor BookingRepository.create() to perform room selection and booking atomically within a single transaction (e.g., using JPA locking or a single unified query that both selects and reserves the room).

Comment on lines +188 to +190
String firstName = scanner.nextLine().trim().toLowerCase();
System.out.print("Last name: ");
String lastName = scanner.nextLine().trim().toLowerCase();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Guest names are converted to lowercase.

Converting first and last names to lowercase is unusual and may not match user expectations (e.g., "John Smith" becomes "john smith"). Consider preserving the original casing or using proper case formatting.

🤖 Prompt for AI Agents
In @src/main/java/org/example/Menu.java around lines 188 - 190, The code
currently forces guest names to lowercase by calling .toLowerCase() on scanner
input; remove the .toLowerCase() calls so firstName and lastName are assigned
with scanner.nextLine().trim() to preserve user casing, or alternatively apply a
proper capitalization routine (e.g., title-case each word) before storing;
update the two assignments for firstName and lastName (the lines currently using
.toLowerCase()) accordingly and ensure any downstream usage expects the
preserved/formatted casing.

…e of faulty business logic

Co-authored-by: Elias Lennheimer Lennheimer.elias@gmail.com
Co-authored-by: Martin Karlsson Martin.Karlsson@iths.se
Co-authored-by: Rickard Ankar rickard.ankar@iths.se

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @src/main/java/org/example/BookingService.java:
- Around line 48-49: Update the user-facing error string in BookingService where
the start/end date check occurs: replace the current System.out.println("Start
date must from today and before end date.") with a grammatically correct message
such as "Start date must be today or later and before end date." so the output
tied to the startDate/isAfter(endDate) || startDate.isBefore(LocalDate.now())
condition reads clearly.

In @src/main/java/org/example/Menu.java:
- Around line 138-164: The loop can NPE because totalPrice is null when the
catch does a continue; initialize totalPrice to a non-positive BigDecimal (e.g.,
BigDecimal.ZERO or BigDecimal.valueOf(-1)) at declaration in createBooking so
Objects.requireNonNull(totalPrice).signum() is safe, or alternatively set
totalPrice = BigDecimal.ZERO inside the catch before continue; adjust the
totalPrice initialization and/or catch block so totalPrice is never null before
the while condition that checks totalPrice.signum(), while keeping the existing
calls to bookingRepo.getEmptyRooms(...) and
bookingService.calculateTotalPrice(...).
🧹 Nitpick comments (2)
src/main/java/org/example/Menu.java (2)

186-195: Names are lowercased, losing proper casing.

Converting first and last names to lowercase (lines 188, 190) will store names as "john" instead of "John". This is unusual for proper nouns and may not match user expectations when displayed.

Consider preserving casing or applying proper title case
                         System.out.print("First name: ");
-                            String firstName = scanner.nextLine().trim().toLowerCase();
+                            String firstName = scanner.nextLine().trim();
                             System.out.print("Last name: ");
-                            String lastName = scanner.nextLine().trim().toLowerCase();
+                            String lastName = scanner.nextLine().trim();

177-208: Room availability may change between price confirmation and booking creation.

The second getEmptyRooms check on line 177 is redundant after the earlier check on line 158. More critically, there's a potential time-of-check to time-of-use (TOCTOU) issue where rooms could be booked between the user confirming the price (line 169) and the actual booking creation (line 205).

Consider handling this at the repository level with proper transaction isolation, or at minimum, handle the case where bookingRepo.create fails due to no available rooms.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f63c365 and 4815b43.

📒 Files selected for processing (2)
  • src/main/java/org/example/BookingService.java
  • src/main/java/org/example/Menu.java
🔇 Additional comments (10)
src/main/java/org/example/BookingService.java (2)

10-18: LGTM!

Constructor and field initialization are properly structured. Dependency injection of BookingRepository is appropriate for the validation requirements.


27-38: LGTM!

The price calculation logic is correct. The defensive RuntimeException on lines 33-34 is appropriate as a fail-fast mechanism since a non-positive price should be impossible after successful validation.

src/main/java/org/example/Menu.java (8)

12-25: LGTM!

Clean dependency injection setup with final fields. The class structure is well-organized for CLI menu handling.


27-58: LGTM!

The main menu loop is well-structured with proper exception handling. Using string-based switch for menu choices is appropriate for CLI input.


61-79: LGTM!

Proper empty-check before iterating bookings with clear formatting.


81-113: LGTM!

Good validation sequence: empty check → guest existence → bookings retrieval. Clean separation of concerns.


115-136: LGTM!

Properly validates booking ID before querying and handles the empty result case.


213-226: LGTM!

Clean implementation with proper validation and error handling for the remove operation.


228-243: LGTM!

Proper validation helper that checks both empty string and numeric format. The naming convention isInvalidBookingId returning true for invalid cases is clear.


245-251: LGTM!

Simple validation helper for guest names.

Comment thread src/main/java/org/example/BookingService.java Outdated
Comment thread src/main/java/org/example/Menu.java Outdated
Fix possible NullPointerException in createBooking()

Co-authored-by: Elias Lennheimer Lennheimer.elias@gmail.com
Co-authored-by: Martin Karlsson Martin.Karlsson@iths.se
Co-authored-by: Rickard Ankar rickard.ankar@iths.se
@apaegs
apaegs merged commit e09ad6f into main Jan 13, 2026
2 checks passed
@apaegs
apaegs deleted the feature/menu branch January 13, 2026 13:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant