Skip to content

Moon#12

Open
met4lk1tty wants to merge 61 commits intomainfrom
moon
Open

Moon#12
met4lk1tty wants to merge 61 commits intomainfrom
moon

Conversation

@met4lk1tty
Copy link

@met4lk1tty met4lk1tty commented Dec 11, 2025

Summary by CodeRabbit

  • New Features

    • Added user login/authentication, account management (create/update/delete) and a moon-mission browser (list, view by ID, count/filter by year) with an interactive main menu and CLI tools.
    • Added a lightweight data-source abstraction and dev-mode configuration resolution.
  • Documentation

    • README: added a "Review Assignment Due Date" badge.
  • Tests

    • Integration test updated to include explicit confirmation for account deletion.

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

github-classroom bot and others added 30 commits November 28, 2025 16:06
- validate username and password against the database
- Fixed CLI so that the invalid login test passes
…er - passes login, moon mission, and account tests (except delete)
@coderabbitai
Copy link

coderabbitai bot commented Dec 11, 2025

Walkthrough

Refactors the app into layered architecture: adds repository, service, and CLI modules; introduces ConfigUtils and a simple DataSource; adds JDBC BaseRepository and concrete repositories; updates Main to wire services and CLIs; adds models, tests tweak, and a dependency-reduced POM and README badge.

Changes

Cohort / File(s) Summary
Configuration & DataSource
src/main/java/com/example/ConfigUtils.java, src/main/java/com/example/SimpleDriverManagerDataSource.java, src/main/java/com/example/DevDatabaseInitializer.java
Added ConfigUtils for dev-mode and property/env resolution; introduced SimpleDriverManagerDataSource implementing DataSource; added Javadoc to DevDatabaseInitializer.
Models
src/main/java/com/example/model/Account.java, src/main/java/com/example/model/MoonMission.java
Added Account and MoonMission records with custom toString implementations.
Repository Core
src/main/java/com/example/repository/BaseRepository.java, src/main/java/com/example/repository/RepositoryException.java
Added abstract BaseRepository with common JDBC helpers, query/mapping utilities, and dev logging; introduced RepositoryException.
Account Repository
src/main/java/com/example/repository/AccountRepository.java, src/main/java/com/example/repository/AccountRepositoryJdbc.java
Added AccountRepository interface and JDBC implementation with CRUD and validateLogin, mapping ResultSet to Account.
MoonMission Repository
src/main/java/com/example/repository/MoonMissionRepository.java, src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java
Added MoonMissionRepository interface and JDBC implementation (list, getById, countByYear) with ResultSet mapping.
Services
src/main/java/com/example/service/AccountService.java, src/main/java/com/example/service/MoonMissionService.java
Added AccountService and MoonMissionService as thin wrappers over repositories with exception translation.
CLI Framework & Commands
src/main/java/com/example/cli/InputReader.java, src/main/java/com/example/cli/ExitMenuHandler.java, src/main/java/com/example/cli/LoginManager.java, src/main/java/com/example/cli/AccountCLI.java, src/main/java/com/example/cli/MoonMissionCLI.java, src/main/java/com/example/cli/MenuCLI.java
Added console InputReader with flow control, ExitMenuHandler helper, LoginManager with retry limits, AccountCLI and MoonMissionCLI for domain operations, and MenuCLI to route menu choices.
Bootstrap & Build
src/main/java/com/example/Main.java, dependency-reduced-pom.xml, README.md
Refactored Main to assemble DataSource, repositories, services, and CLIs; added dependency-reduced-pom.xml and a README badge.
Tests
src/test/java/com/example/CliAppIT.java
Updated integration test input sequence for deleteAccount to include explicit "yes" confirmation.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Main
    participant LoginMgr as LoginManager
    participant MenuCLI
    participant AcctCLI as AccountCLI
    participant MissionCLI as MoonMissionCLI
    participant AcctSvc as AccountService
    participant MissionSvc as MoonMissionService
    participant AcctRepo as AccountRepositoryJdbc
    participant MissionRepo as MoonMissionRepositoryJdbc
    participant DB as Database

    rect rgba(200,220,255,0.6)
        note right of Main: Startup — wire components
        Main->>AcctRepo: instantiate(AccountRepositoryJdbc)
        Main->>MissionRepo: instantiate(MoonMissionRepositoryJdbc)
        Main->>AcctSvc: instantiate(AccountService)
        Main->>MissionSvc: instantiate(MoonMissionService)
        Main->>LoginMgr: instantiate(LoginManager)
        Main->>MenuCLI: instantiate(MenuCLI)
    end

    rect rgba(220,255,200,0.6)
        note right of LoginMgr: Login flow
        LoginMgr->>User: prompt credentials
        User->>LoginMgr: submit credentials
        LoginMgr->>AcctSvc: validateLogin(username,password)
        AcctSvc->>AcctRepo: validateLogin(...)
        AcctRepo->>DB: SELECT ... WHERE name/password
        DB-->>AcctRepo: result
        AcctRepo-->>AcctSvc: boolean
        AcctSvc-->>LoginMgr: boolean
        LoginMgr-->>User: success/failure
    end

    rect rgba(255,240,200,0.6)
        note right of MenuCLI: User menu actions
        MenuCLI->>User: display options
        User->>MenuCLI: choose option
        alt List missions
            MenuCLI->>MissionCLI: listMissions()
            MissionCLI->>MissionSvc: listMissions()
            MissionSvc->>MissionRepo: listMissions()
            MissionRepo->>DB: SELECT * FROM moon_mission
            DB-->>MissionRepo: rows
            MissionRepo-->>MissionSvc: List<MoonMission>
            MissionSvc-->>MissionCLI: List<MoonMission>
            MissionCLI-->>User: print list
        else Create account
            MenuCLI->>AcctCLI: createAccount()
            AcctCLI->>User: prompt details
            User->>AcctCLI: provide details
            AcctCLI->>AcctSvc: createAccount(...)
            AcctSvc->>AcctRepo: createAccount(...)
            AcctRepo->>DB: INSERT INTO account
            DB-->>AcctRepo: generated id
            AcctRepo-->>AcctSvc: id
            AcctSvc-->>AcctCLI: id
            AcctCLI-->>User: print success
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45–75 minutes

  • Review BaseRepository generics, SQL execution wrappers, and exception translation.
  • Verify SQL statements, parameter binding, and ResultSet mappings in AccountRepositoryJdbc and MoonMissionRepositoryJdbc.
  • Inspect Main wiring for config resolution, DataSource creation, and dev-mode handling.
  • Validate InputReader validation rules, LoginManager retry logic, and menu routing.
  • Confirm integration test change matches CLI confirmation flow.

Poem

🐰 I hopped through code with nimble feet,

Layers shaped from JDBC beat,
Repos, services, CLIs in tune,
A startup moon beneath the moon.
Hooray—new features, neat and sweet! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Moon' is vague and generic, using a non-descriptive term that does not convey meaningful information about the changeset's main purpose or scope. Provide a more descriptive title that summarizes the primary change, such as 'Add Moon Mission management CLI and refactor application architecture' or 'Implement MoonMissionRepository and AccountService with CLI components'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 82.89% which is sufficient. The required threshold is 80.00%.
✨ 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: 10

🧹 Nitpick comments (15)
src/main/java/com/example/repository/BaseRepository.java (1)

89-102: Consider returning Optional<Long> instead of 0 for missing generated keys.

Line 97 returns 0 when no generated keys are present. This could be ambiguous since 0 might be interpreted as a valid ID in some contexts.

Consider this alternative:

-    protected long executeUpdateReturnId(String sql, Object... params) {
+    protected Optional<Long> executeUpdateReturnId(String sql, Object... params) {
         try (Connection conn = getConnection();
              PreparedStatement stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
 
             for (int i = 0; i < params.length; i++) stmt.setObject(i + 1, params[i]);
             stmt.executeUpdate();
 
             try (ResultSet keys = stmt.getGeneratedKeys()) {
-                return keys.next() ? keys.getLong(1) : 0;
+                return keys.next() ? Optional.of(keys.getLong(1)) : Optional.empty();
             }
         } catch (SQLException e) {
             throw dbError("executeUpdateReturnId: " + sql, e);
         }
     }
src/main/java/com/example/ConfigUtils.java (1)

18-22: Consider consistent boolean parsing across configuration sources.

Line 19 uses Boolean.getBoolean("devMode") which accepts values like "true", "TRUE", "True", etc., while Line 20 only accepts exactly "true" (case-insensitive) for the environment variable. This inconsistency may confuse users who expect similar behavior.

Consider using a helper method for consistent boolean parsing:

 public static boolean isDevMode(String[] args) {
-    if (Boolean.getBoolean("devMode")) return true;
-    if ("true".equalsIgnoreCase(System.getenv("DEV_MODE"))) return true;
+    if (parseBoolean(System.getProperty("devMode"))) return true;
+    if (parseBoolean(System.getenv("DEV_MODE"))) return true;
     return Arrays.asList(args).contains("--dev");
 }
+
+private static boolean parseBoolean(String value) {
+    return value != null && ("true".equalsIgnoreCase(value) || "1".equals(value));
+}
src/main/java/com/example/model/MoonMission.java (1)

16-24: Consider using java.time.LocalDate instead of java.sql.Date.

Using java.sql.Date in the domain model couples it to the JDBC persistence layer. The modern java.time.LocalDate is preferred for representing dates without time components, is immutable, and provides better API semantics.

Apply this diff to use the modern date type:

 package com.example.model;
 
-import java.sql.Date;
+import java.time.LocalDate;
 
 /**
  * Represents a moon mission with its main details.
  *
  * @param missionId     unique identifier for the mission
  * @param spacecraft    name of the spacecraft
  * @param launchDate    launch date of the mission
  * @param carrierRocket rocket used to carry the spacecraft
  * @param operator      organization responsible for the mission
  * @param missionType   type of mission (e.g., manned, unmanned)
  * @param outcome       result or outcome of the mission
  */
 public record MoonMission(
         int missionId,
         String spacecraft,
-        Date launchDate,
+        LocalDate launchDate,
         String carrierRocket,
         String operator,
         String missionType,
         String outcome
 ) {

Then update the repository mapping to convert from java.sql.Date:

// In MoonMissionRepositoryJdbc.java map() method:
rs.getDate("launch_date").toLocalDate()
src/main/java/com/example/cli/LoginManager.java (1)

37-65: LGTM with optional security consideration.

The login flow correctly handles user input, exit signals, credential validation, and retry limits. The error handling for RepositoryException is appropriate.

Optional security enhancement: Consider removing the username from the success message at Line 50 to avoid logging sensitive information:

-System.out.println("\n✅ Login successful! Welcome, " + usernameWrapper.value() + "!\n");
+System.out.println("\n✅ Login successful! Welcome!\n");

Additionally, consider adding a small delay after failed login attempts (e.g., 1-2 seconds) to mitigate brute-force attacks.

src/main/java/com/example/service/AccountService.java (1)

21-67: Consider catching more specific exceptions instead of broad Exception.

All methods catch Exception, which is overly broad and can mask programming errors like NullPointerException, IllegalArgumentException, etc. This makes debugging harder and can hide bugs that should propagate.

Consider one of these approaches:

  1. Catch only checked exceptions that the repository might throw:
 public boolean validateLogin(String username, String password) {
     try {
         return repo.validateLogin(username, password);
-    } catch (Exception e) {
+    } catch (SQLException | DataAccessException e) {
         throw new RepositoryException("Error validating login", e);
     }
 }
  1. Or, let unchecked exceptions propagate naturally:
 public boolean validateLogin(String username, String password) {
     try {
         return repo.validateLogin(username, password);
-    } catch (Exception e) {
+    } catch (RuntimeException e) {
+        throw e; // Let programming errors propagate
+    } catch (Exception e) {
         throw new RepositoryException("Error validating login", e);
     }
 }
src/main/java/com/example/service/MoonMissionService.java (1)

21-43: Consider catching more specific exceptions instead of broad Exception.

Similar to AccountService, all methods catch Exception, which can mask programming errors. Consider catching only specific checked exceptions that the repository layer might throw, allowing unchecked exceptions to propagate naturally for easier debugging.

Example refactoring for listMissions():

 public List<MoonMission> listMissions() {
     try {
         return repo.listMissions();
-    } catch (Exception e) {
+    } catch (SQLException | DataAccessException e) {
         throw new RepositoryException("Error listing missions", e);
     }
 }
src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java (1)

31-42: LGTM with optional consideration.

The ResultSet mapping correctly extracts all seven fields and constructs the MoonMission record.

If any database columns are nullable, consider adding null-safety checks:

rs.getString("spacecraft") != null ? rs.getString("spacecraft") : "Unknown"

Or define clear expectations about database constraints (e.g., NOT NULL columns).

src/main/java/com/example/cli/AccountCLI.java (1)

46-75: LGTM with optional privacy consideration.

The password update flow correctly validates the account existence before updating and handles errors appropriately.

Optional privacy enhancement: Listing all accounts (Line 49-56) might expose sensitive information in shared environments. Consider one of these alternatives:

  1. Only show account IDs without personal details
  2. Require the user to know their account ID without listing
  3. Add an access control check before listing accounts
src/main/java/com/example/repository/AccountRepository.java (2)

5-5: Remove unused import.

The SQLException import is unused since no method in this interface declares it.

Apply this diff:

-import java.sql.SQLException;
 import java.util.List;

13-18: Add javadoc for public API methods.

The interface methods lack documentation. Consider adding javadoc to describe:

  • Parameters and return values
  • Behavior (e.g., what happens if user doesn't exist, validation fails)
  • Any exceptions that implementations might throw
src/main/java/com/example/Main.java (3)

44-44: Consider documenting InputReader lifecycle.

The InputReader instance wraps System.in with a BufferedReader. While closing System.in is generally not necessary (and can cause issues), consider adding a comment documenting that the reader is intentionally not closed.


22-55: Consider adding top-level error handling.

The main method lacks try-catch for unexpected exceptions. If initialization or CLI operations fail, the application will crash with a stack trace. Consider wrapping the main logic in a try-catch block to provide user-friendly error messages.

Example:

public static void main(String[] args) {
    try {
        // existing code
    } catch (Exception e) {
        System.err.println("Application error: " + e.getMessage());
        System.exit(1);
    }
}

23-39: Avoid redundant devMode calculation.

The ConfigUtils.isDevMode(args) is called twice (lines 23 and 36). Consider calculating it once and reusing the result.

Apply this diff:

+        boolean devMode = ConfigUtils.isDevMode(args);
-        if (ConfigUtils.isDevMode(args)) {
+        if (devMode) {
             DevDatabaseInitializer.start();
         }

         String jdbcUrl = ConfigUtils.resolveConfig("APP_JDBC_URL", "APP_JDBC_URL");
         String dbUser = ConfigUtils.resolveConfig("APP_DB_USER", "APP_DB_USER");
         String dbPass = ConfigUtils.resolveConfig("APP_DB_PASS", "APP_DB_PASS");

         if (jdbcUrl == null || dbUser == null || dbPass == null) {
             throw new IllegalStateException("Missing DB configuration.");
         }

         SimpleDriverManagerDataSource dataSource = new SimpleDriverManagerDataSource(jdbcUrl, dbUser, dbPass);
-        boolean devMode = ConfigUtils.isDevMode(args);
src/main/java/com/example/cli/MenuCLI.java (1)

30-52: Add error handling in menu loop.

The menu loop lacks error handling around CLI method calls (lines 43-48). If any operation throws an uncaught exception, the entire menu will crash. Consider wrapping the switch statement in a try-catch block to handle errors gracefully and keep the menu running.

Example:

try {
    switch (choiceWrapper.value()) {
        case 1 -> missionCLI.listMissions();
        // ... other cases
        default -> System.out.println("Invalid option, try again.");
    }
} catch (Exception e) {
    System.out.println("❌ Error: " + e.getMessage());
}
src/main/java/com/example/cli/MoonMissionCLI.java (1)

67-90: Consider adding a repository method to fetch missions by year.

The method fetches all missions (line 79) and filters them in memory (lines 80-81). For large datasets, this is inefficient. Consider adding a getMissionsByYear(int year) method to the repository layer to push filtering to the database.

📜 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 9a9f51b.

📒 Files selected for processing (23)
  • README.md (1 hunks)
  • dependency-reduced-pom.xml (1 hunks)
  • src/main/java/com/example/ConfigUtils.java (1 hunks)
  • src/main/java/com/example/DevDatabaseInitializer.java (2 hunks)
  • src/main/java/com/example/Main.java (1 hunks)
  • src/main/java/com/example/SimpleDriverManagerDataSource.java (1 hunks)
  • src/main/java/com/example/cli/AccountCLI.java (1 hunks)
  • src/main/java/com/example/cli/ExitMenuHandler.java (1 hunks)
  • src/main/java/com/example/cli/InputReader.java (1 hunks)
  • src/main/java/com/example/cli/LoginManager.java (1 hunks)
  • src/main/java/com/example/cli/MenuCLI.java (1 hunks)
  • src/main/java/com/example/cli/MoonMissionCLI.java (1 hunks)
  • src/main/java/com/example/model/Account.java (1 hunks)
  • src/main/java/com/example/model/MoonMission.java (1 hunks)
  • src/main/java/com/example/repository/AccountRepository.java (1 hunks)
  • src/main/java/com/example/repository/AccountRepositoryJdbc.java (1 hunks)
  • src/main/java/com/example/repository/BaseRepository.java (1 hunks)
  • src/main/java/com/example/repository/MoonMissionRepository.java (1 hunks)
  • src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java (1 hunks)
  • src/main/java/com/example/repository/RepositoryException.java (1 hunks)
  • src/main/java/com/example/service/AccountService.java (1 hunks)
  • src/main/java/com/example/service/MoonMissionService.java (1 hunks)
  • src/test/java/com/example/CliAppIT.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (8)
src/main/java/com/example/cli/LoginManager.java (2)
src/main/java/com/example/service/AccountService.java (1)
  • AccountService (14-68)
src/main/java/com/example/repository/RepositoryException.java (1)
  • RepositoryException (6-17)
src/main/java/com/example/cli/ExitMenuHandler.java (1)
src/main/java/com/example/cli/InputReader.java (1)
  • InputReader (10-109)
src/main/java/com/example/service/MoonMissionService.java (1)
src/main/java/com/example/repository/RepositoryException.java (1)
  • RepositoryException (6-17)
src/main/java/com/example/cli/MoonMissionCLI.java (2)
src/main/java/com/example/service/MoonMissionService.java (1)
  • MoonMissionService (14-44)
src/main/java/com/example/repository/RepositoryException.java (1)
  • RepositoryException (6-17)
src/main/java/com/example/service/AccountService.java (1)
src/main/java/com/example/repository/RepositoryException.java (1)
  • RepositoryException (6-17)
src/main/java/com/example/Main.java (2)
src/main/java/com/example/ConfigUtils.java (1)
  • ConfigUtils (8-37)
src/main/java/com/example/DevDatabaseInitializer.java (1)
  • DevDatabaseInitializer (10-28)
src/main/java/com/example/cli/MenuCLI.java (1)
src/main/java/com/example/cli/InputReader.java (1)
  • InputReader (10-109)
src/main/java/com/example/cli/AccountCLI.java (2)
src/main/java/com/example/service/AccountService.java (1)
  • AccountService (14-68)
src/main/java/com/example/repository/RepositoryException.java (1)
  • RepositoryException (6-17)
🔇 Additional comments (27)
README.md (3)

1-1: Well-placed GitHub Classroom deadline badge.

The badge is contextually appropriate for an educational assignment and follows GitHub Classroom conventions. Placement at the top is unobtrusive to the technical documentation that follows.


2-82: Verify documented configuration matches implementation.

The documentation provides clear, comprehensive setup instructions (Docker/Testcontainers, dev mode, IntelliJ config). However, without seeing the actual implementation files (DevDatabaseInitializer, ConfigUtils, Main class), I cannot confirm that property names (APP_JDBC_URL, APP_DB_USER, APP_DB_PASS), initialization flow, and environment variable precedence exactly match the code.

Please verify that:

  • The property names documented here match those used in ConfigUtils and Main
  • DevDatabaseInitializer behavior aligns with the dev mode description (lines 37)
  • The precedence order (VM options → environment variables) is correctly implemented in configuration resolution

86-114: All assignment requirements verified as fully implemented.

G level: All 6 menu options (1–6) plus exit (option 0) are functional in MenuCLI with correct routing. Login validation correctly prompts for Username/Password, validates against the account table, displays "Invalid username or password" on failure, and allows exit via option 0. Seed data in init.sql includes the AngFra account (password MB=V4cbAqPz4vqmQ). DB configuration via APP_JDBC_URL, APP_DB_USER, APP_DB_PASS is resolved in Main and used to initialize the DataSource.

VG level: Repository pattern fully implemented with AccountRepository and MoonMissionRepository interfaces, concrete JDBC implementations, and proper dependency injection. SimpleDriverManagerDataSource correctly delegates to DriverManager.getConnection(...) and is injected into repositories via constructor. The service layer (AccountService, MoonMissionService) wraps repositories and provides exception handling—a reasonable architectural addition beyond documented requirements but not problematic.

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

6-9: LGTM! Clear documentation added.

The Javadoc accurately describes the class's purpose and behavior, improving code maintainability.

src/main/java/com/example/SimpleDriverManagerDataSource.java (2)

29-33: LGTM! Constructor properly initializes the DataSource.

The constructor correctly stores JDBC connection parameters as immutable fields, following standard DataSource patterns.


35-39: LGTM! Connection methods follow DataSource contract.

Both getConnection() methods correctly implement the DataSource interface contract:

  • getConnection() uses stored credentials from the constructor
  • getConnection(username, password) allows credential override per the interface specification
src/main/java/com/example/repository/BaseRepository.java (3)

26-32: LGTM! Proper initialization with dependency injection.

The constructor correctly accepts DataSource and devMode parameters, enabling flexible configuration and debug logging for development environments.


59-70: LGTM! executeQuery properly prevents SQL injection and manages resources.

The implementation correctly:

  • Uses PreparedStatement with parameterized queries (Line 63)
  • Employs try-with-resources for Connection, PreparedStatement, and ResultSet
  • Wraps exceptions in RepositoryException for consistent error handling

75-84: LGTM! executeUpdate follows safe parameterization pattern.

Properly uses PreparedStatement.setObject() for parameters and try-with-resources for connection management.

src/test/java/com/example/CliAppIT.java (1)

182-182: LGTM! Test updated to match new confirmation flow.

The addition of "yes" confirmation aligns with the new delete confirmation prompt in the CLI, making the test more realistic and ensuring proper verification of the confirmation flow.

src/main/java/com/example/repository/RepositoryException.java (1)

6-16: LGTM! Well-designed custom exception.

The RepositoryException correctly extends RuntimeException (unchecked), making it appropriate for the repository layer to wrap database errors without forcing checked exception handling throughout the application.

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

32-36: LGTM!

The configuration resolution logic correctly prioritizes system properties over environment variables and handles empty/null values appropriately with trimming.

src/main/java/com/example/repository/MoonMissionRepository.java (1)

11-15: LGTM!

The interface follows repository pattern best practices with clear method signatures and appropriate use of Optional for single-entity lookups.

src/main/java/com/example/model/MoonMission.java (1)

29-39: LGTM!

The toString() override provides a clear, readable representation of all mission fields.

src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java (3)

21-23: LGTM!

The constructor correctly delegates to the base class with the provided DataSource and devMode flag.


44-52: LGTM!

Both methods use appropriate SQL queries with proper parameterization for getMissionById() to prevent SQL injection.


54-61: The YEAR(launch_date) function is appropriate for this project. The codebase is explicitly MySQL-only (mysql-connector-j dependency, MySQL TestContainers setup), and YEAR() is the standard MySQL function for extracting year values. Database portability is not a stated requirement, so the suggested alternatives (EXTRACT or date range comparison) are unnecessary.

Likely an incorrect or invalid review comment.

src/main/java/com/example/cli/AccountCLI.java (3)

18-21: LGTM!

The constructor correctly initializes dependencies for the CLI operations.


24-44: LGTM!

The createAccount() method correctly collects all required inputs, handles exit signals appropriately, and provides clear success/error feedback to the user.


77-116: LGTM!

The deleteAccount() method implements a robust confirmation flow with proper input validation and error handling. The confirmation loop correctly handles yes/no/menu/exit inputs.

Note: Same optional privacy consideration applies as in updatePassword() regarding listing all accounts.

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

27-29: Verify configuration key usage.

The calls to ConfigUtils.resolveConfig use the same string for both the property key and environment variable key (e.g., "APP_JDBC_URL" for both). This is unusual since system properties typically use different naming conventions than environment variables.

Verify this is intentional or if distinct keys should be used (e.g., "app.jdbc.url" for property, "APP_JDBC_URL" for environment).

src/main/java/com/example/cli/InputReader.java (2)

83-92: LGTM!

The name validation correctly enforces capitalization and minimum length requirements.


94-103: LGTM!

The SSN validation correctly enforces the Swedish SSN format.

src/main/java/com/example/cli/MoonMissionCLI.java (2)

32-41: LGTM!

The method correctly lists missions with appropriate error handling.


43-61: LGTM!

The method correctly handles Optional results and provides appropriate feedback for both success and not-found cases.

src/main/java/com/example/repository/AccountRepositoryJdbc.java (1)

32-41: LGTM!

The mapping logic correctly extracts all Account fields from the ResultSet.

src/main/java/com/example/repository/AccountRepository.java (1)

13-14: Verify password hashing in implementation.

The validateLogin method accepts a plain-text password parameter. Ensure that the implementation properly hashes passwords before storage and comparison, rather than storing them in plain text.

Run the following script to verify password handling:

#!/bin/bash
# Check how passwords are stored and compared
ast-grep --pattern 'INSERT INTO account $$$'
ast-grep --pattern 'UPDATE account SET password$$$'

Comment on lines +1 to +117
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>jdbc</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<release>${maven.compiler.release}</release>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.5.4</version>
<executions>
<execution>
<id>integration-test</id>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer>
<mainClass>com.example.Main</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>6.0.1</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>junit-jupiter-api</artifactId>
<groupId>org.junit.jupiter</groupId>
</exclusion>
<exclusion>
<artifactId>junit-jupiter-params</artifactId>
<groupId>org.junit.jupiter</groupId>
</exclusion>
<exclusion>
<artifactId>junit-jupiter-engine</artifactId>
<groupId>org.junit.jupiter</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.27.6</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>byte-buddy</artifactId>
<groupId>net.bytebuddy</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>5.20.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>mockito-core</artifactId>
<groupId>org.mockito</groupId>
</exclusion>
<exclusion>
<artifactId>junit-jupiter-api</artifactId>
<groupId>org.junit.jupiter</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.21.3</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<maven.compiler.release>25</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<mockito.version>5.20.0</mockito.version>
<junit.jupiter.version>6.0.1</junit.jupiter.version>
<assertj.core.version>3.27.6</assertj.core.version>
</properties>
</project>
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:

#!/bin/bash
# Check for the source pom.xml and verify Java version usage
echo "=== Source pom.xml ==="
if [ -f pom.xml ]; then
  cat pom.xml | head -50
else
  echo "pom.xml not found at root"
  fd "pom.xml" -t f | head -5
fi

echo -e "\n=== Checking .gitignore for dependency-reduced-pom.xml ==="
if [ -f .gitignore ]; then
  cat .gitignore
else
  echo ".gitignore does not exist"
fi

echo -e "\n=== List files in repository root ==="
ls -la | head -30

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

Length of output: 2977


🌐 Web query:

Java 25 release date stable version 2025

💡 Result:

The stable (General Availability) release date for Java (JDK) 25 was September 16, 2025. [1][2]

Sources:

  • JDK 25 proposed schedule / confirmation. [1]
  • OpenJDK announcement that JDK 25 is generally available (GA build). [2]

Add dependency-reduced-pom.xml to .gitignore.

The file dependency-reduced-pom.xml is automatically generated by maven-shade-plugin during the build process and should be excluded from version control (add to .gitignore) to avoid merge conflicts and repository bloat. Add the following line to .gitignore:

dependency-reduced-pom.xml

Note: Java 25 is now stable (released September 2025) and intentionally configured for this classroom project; no action needed on that front.

🤖 Prompt for AI Agents
In dependency-reduced-pom.xml around lines 1 to 117, the generated file produced
by the maven-shade-plugin is tracked in git; update the repository's .gitignore
to add a line excluding dependency-reduced-pom.xml so this generated file is not
committed (add the single entry "dependency-reduced-pom.xml" to .gitignore and
commit that change).

Comment on lines +9 to +21
/**
* Handles input results indicating exit or menu commands.
*
* @param result the result from InputReader (CONTINUE, EXIT, MENU)
* @return true if the user wants to exit, false if continue or menu
*/
default boolean handleExitOrMenu(InputReader.InputResult result) {
if (result == InputReader.InputResult.EXIT) {
System.out.println("Exiting...");
return true;
}
return result == InputReader.InputResult.MENU;
}
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

Fix inconsistent Javadoc - implementation doesn't match documentation.

The Javadoc on Line 13 states the method "return[s] true if the user wants to exit, false if continue or menu", but the implementation returns true for both EXIT and MENU (Line 20).

Apply this diff to correct the documentation:

     /**
      * Handles input results indicating exit or menu commands.
      *
      * @param result the result from InputReader (CONTINUE, EXIT, MENU)
-     * @return true if the user wants to exit, false if continue or menu
+     * @return true if the user wants to exit or return to menu, false to continue
      */
     default boolean handleExitOrMenu(InputReader.InputResult result) {
🤖 Prompt for AI Agents
In src/main/java/com/example/cli/ExitMenuHandler.java around lines 9 to 21, the
Javadoc currently says the method returns true only for EXIT and false for MENU,
but the implementation returns true for both EXIT and MENU; update the Javadoc
to match the implementation by changing the @return description to state that
the method returns true if the user wants to exit or open the menu (i.e., for
EXIT or MENU) and false for CONTINUE, and optionally add a short sentence
describing that MENU also yields true.

Comment on lines +30 to +37
private String readLine() {
try {
String line = reader.readLine();
return line != null ? line.trim() : "";
} catch (Exception e) {
throw new RuntimeException("Error reading input", e);
}
}
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

Handle EOF (end-of-file) input gracefully.

When readLine() returns null (e.g., on EOF/Ctrl+D), it's converted to an empty string on line 33. This empty string won't match "0" (exit) or "menu", so the user can get stuck in input loops. Consider treating null as an EXIT signal or providing clear feedback.

Apply this diff:

 private String readLine() {
     try {
         String line = reader.readLine();
-        return line != null ? line.trim() : "";
+        if (line == null) {
+            System.out.println("\nEOF detected, exiting...");
+            System.exit(0);
+        }
+        return line.trim();
     } catch (Exception e) {
🤖 Prompt for AI Agents
In src/main/java/com/example/cli/InputReader.java around lines 30 to 37, the
current readLine() converts a null (EOF/Ctrl+D) into an empty string which can
trap the user in input loops; update the method to detect when reader.readLine()
returns null, print a short message like "EOF detected, exiting..." and
terminate the program (e.g., System.exit(0)), otherwise return the trimmed line;
keep the exception handling unchanged.

Comment on lines +27 to +31
public LoginManager(AccountService service, InputReader input, int maxAttempts) {
this.service = service;
this.input = input;
this.maxAttempts = maxAttempts;
}
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

Add validation for maxAttempts parameter.

The constructor doesn't validate that maxAttempts is positive, which could lead to unexpected behavior if a caller passes zero or negative values.

Apply this diff to add validation:

 public LoginManager(AccountService service, InputReader input, int maxAttempts) {
+    if (maxAttempts <= 0) {
+        throw new IllegalArgumentException("maxAttempts must be positive");
+    }
     this.service = service;
     this.input = input;
     this.maxAttempts = maxAttempts;
 }
📝 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
public LoginManager(AccountService service, InputReader input, int maxAttempts) {
this.service = service;
this.input = input;
this.maxAttempts = maxAttempts;
}
public LoginManager(AccountService service, InputReader input, int maxAttempts) {
if (maxAttempts <= 0) {
throw new IllegalArgumentException("maxAttempts must be positive");
}
this.service = service;
this.input = input;
this.maxAttempts = maxAttempts;
}
🤖 Prompt for AI Agents
In src/main/java/com/example/cli/LoginManager.java around lines 27 to 31, the
constructor accepts maxAttempts but does not validate it; add a check that
maxAttempts is greater than zero and if not throw an IllegalArgumentException
with a clear message (e.g., "maxAttempts must be positive"), ensuring the field
is only assigned after validation so callers cannot construct a LoginManager
with zero or negative attempts.

private void printHeader() {
System.out.println("\n 🌕 MOON MISSION HUB 🌕 ");
System.out.println("----------------------------------------");
System.out.println("Type 0 to exit from this menu, or 'menu' to go back to the main menu.");
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

Clarify menu instruction text.

The message states "or 'menu' to go back to the main menu" but this is already the main menu. The 'menu' command doesn't make semantic sense here. Consider simplifying to just "Type 0 to exit" or clarifying what 'menu' does in this context.

Apply this diff:

-        System.out.println("Type 0 to exit from this menu, or 'menu' to go back to the main menu.");
+        System.out.println("Type 0 to exit.");
📝 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("Type 0 to exit from this menu, or 'menu' to go back to the main menu.");
System.out.println("Type 0 to exit.");
🤖 Prompt for AI Agents
In src/main/java/com/example/cli/MenuCLI.java around line 58, the printed
instruction "Type 0 to exit from this menu, or 'menu' to go back to the main
menu." is confusing because this is already the main menu and the 'menu' command
is semantically unclear; update the message to a single clear instruction such
as "Type 0 to exit" (or, if 'menu' has a defined action here, clarify that
action explicitly) so users aren’t confused — replace the current println with
the simplified/clarified text.

Comment on lines +13 to +20
public record Account(
long userId,
String firstName,
String lastName,
String ssn,
String password,
String name
) {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for password hashing logic and patterns
echo "=== Searching for password hashing and bcrypt usage ==="
rg -nP -C3 'bcrypt|hash|Hash|crypt|encrypt' --type=java -g '!**/test/**' | head -100

echo -e "\n=== Searching for password-related methods or classes ==="
rg -nP 'password.*hash|hash.*password|PasswordEncoder|PasswordHash' --type=java | head -50

echo -e "\n=== Looking for SQL INSERT/UPDATE operations on account ==="
rg -nP 'INSERT INTO account|UPDATE account' --type=java -C2 | head -100

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

Length of output: 2091


🏁 Script executed:

#!/bin/bash
# Check how Account record is used and stored
echo "=== Searching for Account record usage in repositories/services ==="
rg -nP 'new Account|Account\(' --type=java -C2 | head -100

echo -e "\n=== Looking for database operations with Account ==="
rg -nP 'save\(.*[Aa]ccount|insert.*[Aa]ccount|update.*[Aa]ccount' --type=java -C2 | head -100

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

Length of output: 10151


🏁 Script executed:

#!/bin/bash
# Check the full Account.java file to understand its context
echo "=== Reading Account.java file ==="
find . -name "Account.java" -type f ! -path "*/test/*" -exec wc -l {} \;

echo -e "\n=== Finding the Account.java file ==="
find . -name "Account.java" -type f ! -path "*/test/*"

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

Length of output: 268


🏁 Script executed:

cat -n src/main/java/com/example/model/Account.java

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

Length of output: 1288


Passwords are stored in plaintext and must be hashed immediately.

The Account record contains a plaintext password field that is stored directly in the database without hashing:

  1. Plaintext storage confirmed: AccountRepositoryJdbc.createAccount() (line 65) and updatePassword() (line 71) insert passwords directly without any hashing, encryption, or encoding. No bcrypt, PasswordEncoder, or hash implementation exists in the codebase.

  2. Record accessor methods expose credentials: Java records auto-generate password(), equals(), and hashCode() that include all fields. While toString() excludes the password, the accessor and equality methods still expose plaintext credentials. This violates OWASP and PCI-DSS requirements.

  3. Separate credentials from account data: Extract authentication credentials into a dedicated Credentials or AuthInfo class, keeping only non-sensitive user profile fields in Account. Implement password hashing using bcrypt or PBKDF2 before any storage or comparison.

Comment on lines +27 to +34
public String toString() {
return "Account: " +
"ID: " + userId +
", First: " + firstName +
", Last: " + lastName +
", SSN: " + ssn +
", Name: " + name;
}
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

SSN exposure in toString() creates compliance risk.

The overridden toString() method includes the SSN (Line 32), which is highly sensitive PII. Logging or displaying Account objects could inadvertently expose SSNs, violating privacy regulations like GDPR, CCPA, and HIPAA.

Apply this diff to exclude SSN from the string representation:

     @Override
     public String toString() {
         return "Account: " +
                 "ID: " + userId +
                 ", First: " + firstName +
                 ", Last: " + lastName +
-                ", SSN: " + ssn +
                 ", Name: " + name;
     }
🤖 Prompt for AI Agents
In src/main/java/com/example/model/Account.java around lines 27 to 34, the
toString() currently includes the SSN (line 32) which exposes sensitive PII;
remove the ssn field from the returned string (or replace it with a masked
placeholder if a reference is required), update the concatenation to omit ",
SSN: " and ensure any unit tests or callers that relied on the SSN in toString()
are adjusted accordingly.

Comment on lines +54 to +60
public boolean validateLogin(String username, String password) {
return executeQuery(
"SELECT COUNT(*) FROM account WHERE name=? AND password=?",
rs -> { rs.next(); return rs.getInt(1) > 0; },
username, password
);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Implement password hashing.

The validateLogin method compares passwords directly in the SQL query (line 56), indicating passwords are stored in plain text. This is a critical security vulnerability. Passwords must be hashed using a strong algorithm (e.g., BCrypt, Argon2) before storage, and verification should use the same hashing algorithm.

Consider using a library like Spring Security's PasswordEncoder or jBCrypt:

  1. Hash passwords before storing in createAccount
  2. Retrieve the hashed password in validateLogin and compare using the password encoder
  3. Update updatePassword to hash new passwords

Example with jBCrypt:

public boolean validateLogin(String username, String password) {
    return executeQuery(
        "SELECT password FROM account WHERE name=?",
        rs -> {
            if (rs.next()) {
                String hashedPassword = rs.getString(1);
                return BCrypt.checkpw(password, hashedPassword);
            }
            return false;
        },
        username
    );
}

Comment on lines +63 to +67
public long createAccount(String firstName, String lastName, String ssn, String password) {
String name = firstName.substring(0, 3) + lastName.substring(0, 3);
String sql = "INSERT INTO account (first_name, last_name, ssn, password, name) VALUES (?,?,?,?,?)";
return executeUpdateReturnId(sql, firstName, lastName, ssn, password, name);
}
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

Add bounds checking for name derivation.

Line 64 calls substring(0, 3) on firstName and lastName without checking their lengths. If either name has fewer than 3 characters, this will throw StringIndexOutOfBoundsException. While the InputReader validates names are at least 3 characters, the repository should not rely on upstream validation.

Apply this diff:

 public long createAccount(String firstName, String lastName, String ssn, String password) {
+    if (firstName == null || firstName.length() < 3 || lastName == null || lastName.length() < 3) {
+        throw new IllegalArgumentException("First name and last name must be at least 3 characters");
+    }
     String name = firstName.substring(0, 3) + lastName.substring(0, 3);
🤖 Prompt for AI Agents
In src/main/java/com/example/repository/AccountRepositoryJdbc.java around lines
63 to 67, the code unconditionally calls substring(0,3) on firstName and
lastName which can throw StringIndexOutOfBoundsException for names shorter than
3 characters; change the name derivation to take up to the first three
characters safely (e.g., use substring(0, Math.min(3, firstName.length())) and
similarly for lastName) so the repository does not rely on upstream validation,
then proceed to build the SQL and call executeUpdateReturnId as before.

Comment on lines +70 to +72
public void updatePassword(long userId, String newPassword) {
executeUpdate("UPDATE account SET password=? WHERE user_id=?", newPassword, userId);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Ensure password hashing in updatePassword.

The updatePassword method stores the new password directly without hashing (line 71). This must hash the password before storage, consistent with the changes needed in validateLogin and createAccount.

🤖 Prompt for AI Agents
In src/main/java/com/example/repository/AccountRepositoryJdbc.java around lines
70 to 72, updatePassword currently writes the new password directly to the DB;
hash the password before persisting to match validateLogin/createAccount
behavior. Modify the method to call the existing password-hashing utility (e.g.,
PasswordHasher.hash(...) or BCrypt.hashpw(...)) on newPassword, then pass the
hashed value to executeUpdate; ensure any required import is added and
handle/propagate checked exceptions consistently with the class.

@coderabbitai
Copy link

coderabbitai bot commented Dec 11, 2025

Caution

Docstrings generation - FAILED

An unexpected error occurred while opening a pull request: Reference update failed - https://docs.github.com/rest/git/refs#create-a-reference

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

🧹 Nitpick comments (2)
src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java (2)

50-51: *Consider using explicit column names instead of SELECT .

While SELECT * works, explicitly listing columns (mission_id, spacecraft, launch_date, carrier_rocket, operator, mission_type, outcome) improves:

  • Performance by fetching only needed columns
  • Maintainability by making dependencies explicit
  • Resilience to schema changes

Apply this diff to use explicit columns:

-        return queryList("SELECT * FROM moon_mission");
+        return queryList("SELECT mission_id, spacecraft, launch_date, carrier_rocket, operator, mission_type, outcome FROM moon_mission");

61-62: *Consider using explicit column names instead of SELECT .

Similar to listMissions(), using explicit column names improves code clarity and maintainability.

Apply this diff:

-        return querySingle("SELECT * FROM moon_mission WHERE mission_id=?", missionId);
+        return querySingle("SELECT mission_id, spacecraft, launch_date, carrier_rocket, operator, mission_type, outcome FROM moon_mission WHERE mission_id=?", missionId);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9a9f51b and c2ed679.

📒 Files selected for processing (4)
  • src/main/java/com/example/repository/AccountRepositoryJdbc.java (1 hunks)
  • src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java (1 hunks)
  • src/main/java/com/example/service/AccountService.java (1 hunks)
  • src/main/java/com/example/service/MoonMissionService.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/com/example/repository/AccountRepositoryJdbc.java
  • src/main/java/com/example/service/AccountService.java
🧰 Additional context used
🧬 Code graph analysis (1)
src/main/java/com/example/service/MoonMissionService.java (1)
src/main/java/com/example/repository/RepositoryException.java (1)
  • RepositoryException (6-17)
🔇 Additional comments (1)
src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java (1)

73-77: > Likely an incorrect or invalid review comment.

Comment on lines +32 to +41
protected MoonMission map(java.sql.ResultSet rs) throws java.sql.SQLException {
return new MoonMission(
rs.getInt("mission_id"),
rs.getString("spacecraft"),
rs.getDate("launch_date"),
rs.getString("carrier_rocket"),
rs.getString("operator"),
rs.getString("mission_type"),
rs.getString("outcome")
);
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

🧩 Analysis chain

🏁 Script executed:

# Find the MoonMission class file
fd -t f "MoonMission.java" --type f

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

Length of output: 147


🏁 Script executed:

# Also search for the class definition pattern
rg "class MoonMission" -A 20

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

Length of output: 5904


🏁 Script executed:

cat -n src/main/java/com/example/model/MoonMission.java

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

Length of output: 1608


🏁 Script executed:

# Search for database schema files
fd -t f \( -name "*.sql" -o -name "*.ddl" -o -name "*schema*" \)

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

Length of output: 329


🏁 Script executed:

# Also check for migration files
fd -t f -path "*migration*" -o -name "*V*.sql"

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

Length of output: 267


🏁 Script executed:

# Search for SQL schema files
fd "\.sql$"

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

Length of output: 126


🏁 Script executed:

# Search for any schema or migration directories
find . -type d \( -name "*schema*" -o -name "*migration*" -o -name "*db*" \) 2>/dev/null | head -20

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

Length of output: 81


🏁 Script executed:

cat -n src/main/resources/init.sql

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

Length of output: 5525


Add null-checking in the map() method for nullable database columns.

The database schema does not enforce NOT NULL constraints on spacecraft, launch_date, carrier_rocket, operator, mission_type, and outcome columns. These fields can be NULL in the database, and rs.getString() / rs.getDate() will return null, which the MoonMission record will accept. This can result in null values being included in the mission object. Consider adding explicit null-checks or providing default values in the map() method to handle this case consistently.

🤖 Prompt for AI Agents
In src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java around
lines 32 to 41, the map(...) method directly assigns potential null values from
ResultSet to the MoonMission fields; add explicit null-checks and supply
sensible defaults (e.g., empty string for String fields and
java.sql.Date.valueOf("1970-01-01") or null-handling via Optional if domain
expects null) before constructing MoonMission so the object has deterministic
values; implement this by reading each column into a local variable, replacing
nulls with chosen defaults (or mapping to Optional/throwing if preferred), then
pass those sanitized values into the MoonMission constructor.

Comment on lines +32 to +68
public List<MoonMission> listMissions() {
try {
return repo.listMissions();
} catch (Exception e) {
throw new RepositoryException("Error listing missions", e);
}
}

/**
* Retrieves a moon mission by its ID.
*
* @param id the ID of the mission to fetch
* @return an {@link Optional} containing the {@link MoonMission} if found, otherwise empty
* @throws RepositoryException if an error occurs while fetching the mission
*/
public Optional<MoonMission> getMissionById(int id) {
try {
return repo.getMissionById(id);
} catch (Exception e) {
throw new RepositoryException("Error fetching mission by ID", e);
}
}

/**
* Counts the number of moon missions that occurred in a specific year.
*
* @param year the year to count missions for
* @return the number of missions in the given year
* @throws RepositoryException if an error occurs while counting missions
*/
public int countMissionsByYear(int year) {
try {
return repo.countMissionsByYear(year);
} catch (Exception e) {
throw new RepositoryException("Error counting missions by year", e);
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find and examine the MoonMissionRepository interface

# First, locate the file
fd -t f "MoonMissionRepository" .

# Then use ast-grep to find the interface definition
ast-grep --pattern $'interface MoonMissionRepository {
  $$$
}'

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

Length of output: 234


🏁 Script executed:

cat -n ./src/main/java/com/example/repository/MoonMissionRepository.java

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

Length of output: 593


🏁 Script executed:

# Also check the JDBC implementation to see if it declares throws clauses
cat -n ./src/main/java/com/example/repository/MoonMissionRepositoryJdbc.java | head -80

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

Length of output: 3178


🏁 Script executed:

# Check the BaseRepository to see how it handles exceptions
cat -n ./src/main/java/com/example/repository/BaseRepository.java | head -100

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

Length of output: 4111


🏁 Script executed:

# Check if queryList and querySingle methods also throw RepositoryException
cat -n ./src/main/java/com/example/repository/BaseRepository.java | tail -50

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

Length of output: 1973


Remove unnecessary exception handling to avoid double-wrapping RepositoryExceptions.

The repository methods already wrap JDBC exceptions in RepositoryException at the BaseRepository level (see dbError() method). The service layer's try-catch blocks then re-throw the same exception type, creating double-wrapping and adding no value.

Since the repository interface declares no checked exceptions and BaseRepository already converts all errors to RepositoryException, the service methods should remove the try-catch blocks entirely and let exceptions propagate naturally:

public List<MoonMission> listMissions() {
    return repo.listMissions();
}

Apply this pattern to all three methods in lines 32-68.

🤖 Prompt for AI Agents
In src/main/java/com/example/service/MoonMissionService.java around lines 32 to
68, remove the unnecessary try-catch blocks that catch Exception and re-throw
RepositoryException for listMissions, getMissionById, and countMissionsByYear;
instead let the repository's RepositoryException propagate by returning the repo
method results directly (e.g., return repo.listMissions(); return
repo.getMissionById(id); return repo.countMissionsByYear(year);), keeping
existing method signatures and JavaDocs unchanged.

@met4lk1tty
Copy link
Author

met4lk1tty commented Dec 11, 2025

I added a confirmation step 'yes' in the test (thats why conflict resolution) before deleting an account to make sure the user really wants to delete it. This prevents accidental deletion and improves safety.

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