Skip to content

Erika solution#22

Open
eafalkens wants to merge 3 commits intomainfrom
erika-solution
Open

Erika solution#22
eafalkens wants to merge 3 commits intomainfrom
erika-solution

Conversation

@eafalkens
Copy link

@eafalkens eafalkens commented Dec 14, 2025

Summary by CodeRabbit

  • Documentation

    • Added a "Review Assignment Due Date" badge to the README.
  • New Features

    • Interactive CLI: user login/authentication and a main menu for operations.
    • Mission features: list missions, fetch mission by ID, and count/analyze missions by year.
    • Account management: create accounts, update passwords, and delete accounts.

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

@coderabbitai
Copy link

coderabbitai bot commented Dec 14, 2025

Walkthrough

Added an interactive CLI to Main.java with username/password login, a menu for mission queries and account CRUD operations backed by JDBC; README.md gained a review-due-date badge. (50 words)

Changes

Cohort / File(s) Summary
Documentation
README.md
Added a "Review Assignment Due Date" badge image/link at the top.
CLI & JDBC DB logic
src/main/java/com/example/Main.java
Replaced explicit JDBC imports with java.sql.*, added java.util.Scanner, and implemented an interactive CLI in run() that performs login validation and a menu for: listing missions, fetching mission by ID, counting missions by year, and account CRUD. Added private helpers: validateLogin, listMoonMissions, getMissionById, countMissionsByYear, createAccount, updateAccountPassword, deleteAccount, plus cap3 formatting helper and associated SQL/error handling.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI as Main.run()
    participant DB as Database (JDBC)

    User->>CLI: Enter username/password
    CLI->>DB: validateLogin (prepared statement query)
    DB-->>CLI: login result (success/failure)
    alt success
        CLI->>User: Show main menu
        User->>CLI: Select action (list/get/count/create/update/delete)
        CLI->>DB: perform chosen DB operation (query/update)
        DB-->>CLI: result (rows / update count / error)
        CLI->>User: Display result, prompt to continue
    else failure
        CLI->>User: Inform failure, offer retry/exit
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Review JDBC usage for proper use of prepared statements vs. string concatenation to avoid SQL injection.
  • Verify correct closing of Connection, PreparedStatement/Statement, and ResultSet (try-with-resources).
  • Check authentication logic and secure handling of credentials (no logging, no plaintext persistence).
  • Validate input parsing, numeric conversions, and consistent error handling across helper methods.
  • Confirm transactional behavior where multiple DB operations may be required (and isolation/commit handling).

Poem

🐰 I hopped into the terminal bright,
A username, a password — a tiny light,
Menus and missions, accounts to tend,
Seven helpers prance and mend,
CLI joy — a rabbit's coding delight!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'Erika solution' is vague and generic, providing no meaningful information about the actual changes in the pull request. Use a descriptive title that summarizes the main change, such as 'Add interactive CLI with login validation and account/mission management' or 'Refactor Main.java with interactive menu system and database operations'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

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/com/example/Main.java (1)

16-105: Integration tests will hang due to extra "Press Enter to continue" prompt. The test harness provides exact input sequences via ByteArrayInputStream (lines 100–103 consume input the tests don't provide), causing scanner.nextLine() to block indefinitely and trigger the 10-second timeout at line 281 of CliAppIT.java.

Additionally, after an invalid login, choosing to continue (anything other than "0") bypasses authentication entirely and grants access to the main menu without re-prompting for credentials. The if (!isValid) block at lines 45–53 only returns if the user enters "0"; any other input falls through to the main menu with no reauthentication check.

Both issues must be fixed: loop the login prompt until valid credentials are provided, and remove the pause after menu actions (or gate it behind a flag) so tests receive only the inputs they expect.

🧹 Nitpick comments (1)
src/main/java/com/example/Main.java (1)

3-5: Wildcard JDBC import is OK, but keep an eye on hidden ambiguity. If you later add types like Date (java.util vs java.sql), wildcard imports can make this harder to spot.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e629e94 and 9746599.

📒 Files selected for processing (2)
  • README.md (1 hunks)
  • src/main/java/com/example/Main.java (3 hunks)
🔇 Additional comments (2)
README.md (1)

1-1: Badge addition looks fine.

src/main/java/com/example/Main.java (1)

141-165: DB operations look reasonable (prepared statements where needed). The basic JDBC usage and error handling won’t crash the CLI for bad inputs.

Also applies to: 167-183, 225-273

Comment on lines +107 to +123
private boolean validateLogin(String username, String password,
String jdbcUrl, String dbUser, String dbPass) {
String sql = "SELECT 1 FROM account WHERE name = ? AND password = ?";
try (Connection conn = DriverManager.getConnection(jdbcUrl, dbUser, dbPass);
PreparedStatement stmt = conn.prepareStatement(sql)) {

stmt.setString(1, username);
stmt.setString(2, password);

ResultSet rs = stmt.executeQuery();
return rs.next();

} catch (SQLException e) {
System.err.println("Login error: " + e.getMessage());
return false;
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Close ResultSet explicitly (minor), and avoid printing raw SQL errors that may include sensitive info.

-            ResultSet rs = stmt.executeQuery();
-            return rs.next();
+            try (ResultSet rs = stmt.executeQuery()) {
+                return rs.next();
+            }
 
         } catch (SQLException e) {
-            System.err.println("Login error: " + e.getMessage());
+            System.err.println("Login error");
             return false;
         }
🤖 Prompt for AI Agents
In src/main/java/com/example/Main.java around lines 107 to 123, the code doesn't
close the ResultSet explicitly and prints raw SQL error via System.err which may
leak sensitive info; change the try-with-resources to include the ResultSet (or
wrap the executeQuery call in its own try-with-resources) so rs is closed,
replace System.err prints with a proper logger call that emits a generic error
message (e.g., "Login failed") and send the SQLException details to a secure
debug/trace log rather than standard error or user-facing output, and ensure no
sensitive SQL or credential data is included in the logged messages.

@eafalkens eafalkens closed this Dec 14, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (2)
src/main/java/com/example/Main.java (2)

107-124: Don’t print raw SQL exception messages on login (info leak) — this repeats a previously-raised concern; log a generic message to the user and keep details for debug logs only.

-        } catch (SQLException e) {
-            System.err.println("Login error: " + e.getMessage());
+        } catch (SQLException e) {
+            System.err.println("Login failed");
             return false;
         }

168-184: Close ResultSet in countMissionsByYear (currently not in try-with-resources)

-            stmt.setInt(1, Integer.parseInt(year));
-            ResultSet rs = stmt.executeQuery();
-
-            if (rs.next()) {
-                int count = rs.getInt("count");
-                System.out.println("Number of missions in " + year + ": " + count);
-            }
+            stmt.setInt(1, Integer.parseInt(year));
+            try (ResultSet rs = stmt.executeQuery()) {
+                if (rs.next()) {
+                    int count = rs.getInt("count");
+                    System.out.println("Number of missions in " + year + ": " + count);
+                }
+            }
🧹 Nitpick comments (3)
src/main/java/com/example/Main.java (3)

3-5: Avoid java.sql.* wildcard import — it hurts readability and can mask name collisions.


16-31: Redundant connection open/close in run() (unused connection) — either reuse it for the session or remove this “connectivity check” to avoid extra connections.

-        try (Connection connection = DriverManager.getConnection(jdbcUrl, dbUser, dbPass)) {
-        } catch (SQLException e) {
-            throw new RuntimeException(e);
-        }
+        // Optional: keep a connectivity check, but prefer reusing a single Connection for the CLI session.

225-250: Consider basic input validation for updateAccountPassword (empty password, password policy) to prevent accidental lockouts.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9746599 and 1ee0491.

📒 Files selected for processing (1)
  • src/main/java/com/example/Main.java (3 hunks)
🔇 Additional comments (3)
src/main/java/com/example/Main.java (3)

126-140: LGTM: resources closed correctly in listMoonMissions (Connection/Statement/ResultSet all in try-with-resources).


252-272: LGTM: deleteAccount uses prepared statement and checks affected rows.


302-310: LGTM: cap3() matches expected mixed-case username convention and handles null/blank safely.

Comment on lines +33 to +105
Scanner scanner = new Scanner(System.in);

System.out.println("Username:");
String username = scanner.nextLine();

System.out.println("Password:");
String password = scanner.nextLine();

boolean isValid = validateLogin(username, password, jdbcUrl, dbUser, dbPass);

if (!isValid) {
System.out.println("Invalid username or password");
// Testarna förväntar sig att man kan välja 0 för att avsluta
System.out.print("Press 0 to exit or any key to continue...");
String choice = scanner.nextLine();
if ("0".equals(choice)) {
return;
}
} else {
System.out.println("Login successful!");
}

// Huvudmeny
boolean running = true;
while (running) {
System.out.println("1) List moon missions");
System.out.println("2) Get a moon mission by mission_id");
System.out.println("3) Count missions for a given year");
System.out.println("4) Create an account");
System.out.println("5) Update an account password");
System.out.println("6) Delete an account");
System.out.println("0) Exit");

System.out.print("Enter choice: ");
String choice = scanner.nextLine();

switch (choice) {
case "1":
listMoonMissions(jdbcUrl, dbUser, dbPass);
break;
case "2":
System.out.print("Enter mission ID: ");
String missionId = scanner.nextLine();
getMissionById(jdbcUrl, dbUser, dbPass, missionId);
break;
case "3":
System.out.print("Enter year: ");
String year = scanner.nextLine();
countMissionsByYear(jdbcUrl, dbUser, dbPass, year);
break;
case "4":
createAccount(jdbcUrl, dbUser, dbPass, scanner);
break;
case "5":
updateAccountPassword(jdbcUrl, dbUser, dbPass, scanner);
break;
case "6":
deleteAccount(jdbcUrl, dbUser, dbPass, scanner);
break;
case "0":
System.out.println("Exiting...");
running = false;
break;
default:
System.out.println("Invalid choice");
}

if (running && !choice.equals("0")) {
System.out.println("\nPress Enter to continue...");
scanner.nextLine();
}
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don’t allow main menu access after failed login — currently, a user can fail auth and still proceed into CRUD/query operations by choosing “any key to continue”.

-        System.out.println("Username:");
-        String username = scanner.nextLine();
-
-        System.out.println("Password:");
-        String password = scanner.nextLine();
-
-        boolean isValid = validateLogin(username, password, jdbcUrl, dbUser, dbPass);
-
-        if (!isValid) {
-            System.out.println("Invalid username or password");
-            // Testarna förväntar sig att man kan välja 0 för att avsluta
-            System.out.print("Press 0 to exit or any key to continue...");
-            String choice = scanner.nextLine();
-            if ("0".equals(choice)) {
-                return;
-            }
-        } else {
-            System.out.println("Login successful!");
-        }
+        boolean isValid = false;
+        while (!isValid) {
+            System.out.println("Username:");
+            String username = scanner.nextLine();
+
+            System.out.println("Password:");
+            String password = scanner.nextLine();
+
+            isValid = validateLogin(username, password, jdbcUrl, dbUser, dbPass);
+            if (!isValid) {
+                System.out.println("Invalid username or password");
+                // Testarna förväntar sig att man kan välja 0 för att avsluta
+                System.out.print("Press 0 to exit or any key to try again...");
+                String choice = scanner.nextLine();
+                if ("0".equals(choice)) return;
+            }
+        }
+        System.out.println("Login successful!");
📝 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
Scanner scanner = new Scanner(System.in);
System.out.println("Username:");
String username = scanner.nextLine();
System.out.println("Password:");
String password = scanner.nextLine();
boolean isValid = validateLogin(username, password, jdbcUrl, dbUser, dbPass);
if (!isValid) {
System.out.println("Invalid username or password");
// Testarna förväntar sig att man kan välja 0 för att avsluta
System.out.print("Press 0 to exit or any key to continue...");
String choice = scanner.nextLine();
if ("0".equals(choice)) {
return;
}
} else {
System.out.println("Login successful!");
}
// Huvudmeny
boolean running = true;
while (running) {
System.out.println("1) List moon missions");
System.out.println("2) Get a moon mission by mission_id");
System.out.println("3) Count missions for a given year");
System.out.println("4) Create an account");
System.out.println("5) Update an account password");
System.out.println("6) Delete an account");
System.out.println("0) Exit");
System.out.print("Enter choice: ");
String choice = scanner.nextLine();
switch (choice) {
case "1":
listMoonMissions(jdbcUrl, dbUser, dbPass);
break;
case "2":
System.out.print("Enter mission ID: ");
String missionId = scanner.nextLine();
getMissionById(jdbcUrl, dbUser, dbPass, missionId);
break;
case "3":
System.out.print("Enter year: ");
String year = scanner.nextLine();
countMissionsByYear(jdbcUrl, dbUser, dbPass, year);
break;
case "4":
createAccount(jdbcUrl, dbUser, dbPass, scanner);
break;
case "5":
updateAccountPassword(jdbcUrl, dbUser, dbPass, scanner);
break;
case "6":
deleteAccount(jdbcUrl, dbUser, dbPass, scanner);
break;
case "0":
System.out.println("Exiting...");
running = false;
break;
default:
System.out.println("Invalid choice");
}
if (running && !choice.equals("0")) {
System.out.println("\nPress Enter to continue...");
scanner.nextLine();
}
}
}
boolean isValid = false;
while (!isValid) {
System.out.println("Username:");
String username = scanner.nextLine();
System.out.println("Password:");
String password = scanner.nextLine();
isValid = validateLogin(username, password, jdbcUrl, dbUser, dbPass);
if (!isValid) {
System.out.println("Invalid username or password");
// Testarna förväntar sig att man kan välja 0 för att avsluta
System.out.print("Press 0 to exit or any key to try again...");
String choice = scanner.nextLine();
if ("0".equals(choice)) return;
}
}
System.out.println("Login successful!");
// Huvudmeny
boolean running = true;
while (running) {
System.out.println("1) List moon missions");
System.out.println("2) Get a moon mission by mission_id");
System.out.println("3) Count missions for a given year");
System.out.println("4) Create an account");
System.out.println("5) Update an account password");
System.out.println("6) Delete an account");
System.out.println("0) Exit");
System.out.print("Enter choice: ");
String choice = scanner.nextLine();
switch (choice) {
case "1":
listMoonMissions(jdbcUrl, dbUser, dbPass);
break;
case "2":
System.out.print("Enter mission ID: ");
String missionId = scanner.nextLine();
getMissionById(jdbcUrl, dbUser, dbPass, missionId);
break;
case "3":
System.out.print("Enter year: ");
String year = scanner.nextLine();
countMissionsByYear(jdbcUrl, dbUser, dbPass, year);
break;
case "4":
createAccount(jdbcUrl, dbUser, dbPass, scanner);
break;
case "5":
updateAccountPassword(jdbcUrl, dbUser, dbPass, scanner);
break;
case "6":
deleteAccount(jdbcUrl, dbUser, dbPass, scanner);
break;
case "0":
System.out.println("Exiting...");
running = false;
break;
default:
System.out.println("Invalid choice");
}
if (running && !choice.equals("0")) {
System.out.println("\nPress Enter to continue...");
scanner.nextLine();
}
}
}
🤖 Prompt for AI Agents
In src/main/java/com/example/Main.java around lines 33 to 105, the code allows
users who fail authentication to continue into the main menu by choosing "any
key to continue"; fix this by preventing access to the menu when isValid is
false — either return/exit immediately on failed login or loop back to re-prompt
for credentials, or move the entire main menu block inside the existing else
branch so it only runs when isValid is true; update the control flow accordingly
and remove the "continue to menu" prompt after a failed login.

Comment on lines +142 to +166
private void getMissionById(String jdbcUrl, String dbUser, String dbPass, String missionId) {
String sql = "SELECT * FROM moon_mission WHERE mission_id = ?";
try (Connection conn = DriverManager.getConnection(jdbcUrl, dbUser, dbPass);
PreparedStatement stmt = conn.prepareStatement(sql)) {

stmt.setInt(1, Integer.parseInt(missionId));
ResultSet rs = stmt.executeQuery();

if (rs.next()) {
System.out.println("\n=== Mission Details ===");
System.out.println("Mission ID: " + rs.getInt("mission_id"));
System.out.println("Spacecraft: " + rs.getString("spacecraft"));
System.out.println("Launch Date: " + rs.getDate("launch_date"));
System.out.println("Carrier Rocket: " + rs.getString("carrier_rocket"));
System.out.println("Operator: " + rs.getString("operator"));
System.out.println("Mission Type: " + rs.getString("mission_type"));
System.out.println("Outcome: " + rs.getString("outcome"));
} else {
System.out.println("Mission not found");
}

} catch (SQLException | NumberFormatException e) {
System.err.println("Error: " + e.getMessage());
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Close ResultSet in getMissionById (currently not in try-with-resources) — avoid leaking cursors/resources.

-            stmt.setInt(1, Integer.parseInt(missionId));
-            ResultSet rs = stmt.executeQuery();
-
-            if (rs.next()) {
+            stmt.setInt(1, Integer.parseInt(missionId));
+            try (ResultSet rs = stmt.executeQuery()) {
+                if (rs.next()) {
                     System.out.println("\n=== Mission Details ===");
                     System.out.println("Mission ID: " + rs.getInt("mission_id"));
                     System.out.println("Spacecraft: " + rs.getString("spacecraft"));
                     System.out.println("Launch Date: " + rs.getDate("launch_date"));
                     System.out.println("Carrier Rocket: " + rs.getString("carrier_rocket"));
                     System.out.println("Operator: " + rs.getString("operator"));
                     System.out.println("Mission Type: " + rs.getString("mission_type"));
                     System.out.println("Outcome: " + rs.getString("outcome"));
-            } else {
-                System.out.println("Mission not found");
+                } else {
+                    System.out.println("Mission not found");
+                }
             }
🤖 Prompt for AI Agents
In src/main/java/com/example/Main.java around lines 142 to 166, the ResultSet
returned by stmt.executeQuery() is not closed, risking cursor/resource leaks;
update the method to ensure the ResultSet is closed by using a
try-with-resources that includes the ResultSet (or nest a try-with-resources) so
the PreparedStatement and ResultSet are both created in try-with-resources
blocks (e.g., prepare the statement, then assign executeQuery() to a ResultSet
variable declared in a try-with-resources), and remove reliance on implicit
closing so all JDBC resources are properly closed even on exceptions.

Comment on lines +186 to +223
private void createAccount(String jdbcUrl, String dbUser, String dbPass, Scanner scanner) {
System.out.print("First name: ");
String firstName = scanner.nextLine();

System.out.print("Last name: ");
String lastName = scanner.nextLine();

System.out.print("SSN: ");
String ssn = scanner.nextLine();

System.out.print("Password: ");
String password = scanner.nextLine();

// Skapa username från förnamn + första 3 bokstäver av efternamn
String username = cap3(firstName) + cap3(lastName);

String sql = "INSERT INTO account (first_name, last_name, ssn, name, password) " +
"VALUES (?, ?, ?, ?, ?)";

try (Connection conn = DriverManager.getConnection(jdbcUrl, dbUser, dbPass);
PreparedStatement stmt = conn.prepareStatement(sql)) {

stmt.setString(1, firstName);
stmt.setString(2, lastName);
stmt.setString(3, ssn);
stmt.setString(4, username);
stmt.setString(5, password);

int rows = stmt.executeUpdate();
if (rows > 0) {
System.out.println("Account created successfully!");
System.out.println("Username: " + username);
}

} catch (SQLException e) {
System.err.println("Error creating account: " + e.getMessage());
}
}
Copy link

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:

# First, explore the project structure to understand build system and dependencies
find . -maxdepth 2 -type f \( -name "pom.xml" -o -name "build.gradle" -o -name "build.gradle.kts" \)

Repository: ithsjava25/ithsjava25-java-databas-jdbc-javafx-classrom

Length of output: 108


🏁 Script executed:

# Locate and read the dependency file to see what libraries are available
git ls-files | grep -E "(pom\.xml|build\.gradle|build\.gradle\.kts)" | head -5

Repository: ithsjava25/ithsjava25-java-databas-jdbc-javafx-classrom

Length of output: 106


🏁 Script executed:

# Read the pom.xml to check project dependencies
cat pom.xml

Repository: ithsjava25/ithsjava25-java-databas-jdbc-javafx-classrom

Length of output: 2791


🏁 Script executed:

# Search for password-related code and authentication logic
rg -i "password" src/main/java --type java -B 2 -A 2

Repository: ithsjava25/ithsjava25-java-databas-jdbc-javafx-classrom

Length of output: 6569


🏁 Script executed:

# Search for any hashing or encoding related code
rg -i "(hash|bcrypt|encrypt|encode)" src/main/java --type java

Repository: ithsjava25/ithsjava25-java-databas-jdbc-javafx-classrom

Length of output: 81


🏁 Script executed:

# Look for authentication or login methods
rg -i "(login|authenticate|verify|validate.*password)" src/main/java --type java -B 2 -A 5

Repository: ithsjava25/ithsjava25-java-databas-jdbc-javafx-classrom

Length of output: 2420


🏁 Script executed:

# Verify the exact lines of validateLogin method to understand password comparison
sed -n '155,180p' src/main/java/com/example/Main.java

Repository: ithsjava25/ithsjava25-java-databas-jdbc-javafx-classrom

Length of output: 1277


🏁 Script executed:

# Find the validateLogin method specifically
rg -n "private boolean validateLogin" src/main/java/com/example/Main.java -A 15

Repository: ithsjava25/ithsjava25-java-databas-jdbc-javafx-classrom

Length of output: 858


Plaintext password storage and validation is a critical security vulnerability — passwords are stored and compared in plaintext across createAccount (line 210), validateLogin (line 109), and updateAccountPassword. Add a password hashing library (e.g., jBCrypt) to dependencies and hash passwords before storing; validate logins by comparing hashed password using a constant-time comparison.

Similarly, SSN should not be stored in plaintext.

@eafalkens eafalkens reopened this Dec 14, 2025
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