Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/339Lr3BJ)
### How the tests work (and Docker requirement)

This project ships with an end‑to‑end CLI integration test suite that uses Testcontainers to spin up a temporary MySQL database.
Expand Down
200 changes: 175 additions & 25 deletions src/main/java/com/example/Main.java
Original file line number Diff line number Diff line change
@@ -1,36 +1,90 @@
package com.example;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import com.example.jdbc.JdbcAccountRepository;
import com.example.jdbc.JdbcMoonMissionRepository;

import java.util.Arrays;
import java.util.Scanner;

public class Main {

static void main(String[] args) {
private final JdbcAccountRepository accountRepo;
private final JdbcMoonMissionRepository missionRepo;
private final Scanner sc;

public Main(SimpleDriverManagerDataSource dataSource) {
this.accountRepo = new JdbcAccountRepository(dataSource);
this.missionRepo = new JdbcMoonMissionRepository(dataSource);
this.sc = new Scanner(System.in);
}

public static void main(String[] args) {
if (isDevMode(args)) {
DevDatabaseInitializer.start();
}
new Main().run();
}

public void run() {
// Resolve DB settings with precedence: System properties -> Environment variables
String jdbcUrl = resolveConfig("APP_JDBC_URL", "APP_JDBC_URL");
String dbUser = resolveConfig("APP_DB_USER", "APP_DB_USER");
String dbPass = resolveConfig("APP_DB_PASS", "APP_DB_PASS");

if (jdbcUrl == null || dbUser == null || dbPass == null) {
throw new IllegalStateException(
"Missing DB configuration. Provide APP_JDBC_URL, APP_DB_USER, APP_DB_PASS " +
"as system properties (-Dkey=value) or environment variables.");
SimpleDriverManagerDataSource ds = new SimpleDriverManagerDataSource(jdbcUrl, dbUser, dbPass);

new Main(ds).run();
}

public void run() {
boolean authenticated = false;
String username = "";

// Loop until valid credentials
while (!authenticated) {
System.out.print("Enter your Username (or 0 to exit): ");
username = sc.nextLine();
if ("0".equals(username)) {
System.out.println("Exiting program.");
return;
}

System.out.print("Enter your password: ");
String password = sc.nextLine();

if (accountRepo.validateCredentials(username, password)) {
System.out.println("Welcome / Välkommen: " + username);
authenticated = true;
} else {
System.out.println("Invalid Username or Password. Try again or type 0 to exit.");
}
}

try (Connection connection = DriverManager.getConnection(jdbcUrl, dbUser, dbPass)) {
} catch (SQLException e) {
throw new RuntimeException(e);
// Once authenticated, show menu until user exits
int choice;
do {
getOptions();

// safer input handling
String choiceStr = sc.nextLine();
try {
choice = Integer.parseInt(choiceStr);
} catch (NumberFormatException e) {
choice = -1; // invalid
}

options(choice);
} while (choice != 0);

System.out.println("Goodbye, " + username + "!");
}

/**
* Reads configuration with precedence: Java system property first, then environment variable.
* Returns trimmed value or null if neither source provides a non-empty value.
*/
private static String resolveConfig(String propertyKey, String envKey) {
String v = System.getProperty(propertyKey);
if (v == null || v.trim().isEmpty()) {
v = System.getenv(envKey);
}
//Todo: Starting point for your code
return (v == null || v.trim().isEmpty()) ? null : v.trim();
}

/**
Expand All @@ -48,15 +102,111 @@ private static boolean isDevMode(String[] args) {
return Arrays.asList(args).contains("--dev"); //Argument --dev
}

/**
* Reads configuration with precedence: Java system property first, then environment variable.
* Returns trimmed value or null if neither source provides a non-empty value.
*/
private static String resolveConfig(String propertyKey, String envKey) {
String v = System.getProperty(propertyKey);
if (v == null || v.trim().isEmpty()) {
v = System.getenv(envKey);
public void getOptions() {
System.out.println(
"""
Select an option:
1 - List moon missions (prints spacecraft names from `moon_mission`).
2 - Get a moon mission by mission_id (prints details for that mission).
3 - Count missions for a given year (prompts: year; prints the number of missions launched that year).
4 - Create an account (prompts: first name, last name, ssn, password; prints confirmation).
5 - Update an account password (prompts: user_id, new password; prints confirmation).
6 - Delete an account (prompts: user_id; prints confirmation).
0 - Exit.
By pressing either of the numbers listed.
"""
);
}

public void options(int choice) {

switch (choice) {

case 1: missionRepo.
listMissions().
forEach(System.out::println);
break;

case 2:
System.out.print("Enter a mission ID you want to search for: ");
String missionIdStr = sc.nextLine();
int missionId;
try{
missionId = Integer.parseInt(missionIdStr);
} catch (NumberFormatException e) {
System.out.println("Invalid mission ID");
break;
}

missionRepo.getMissionById(missionId).ifPresentOrElse(
mission -> System.out.printf(
"Mission ID: %d%nSpacecraft: %s%nLaunch Date: %s%nCarrier Rocket: %s%nOutcome: %s%nMission Type: %s%nOperator: %s%n",
mission.getMissionId(),
mission.getSpacecraft(),
mission.getLaunchDate(),
mission.getCarrierRocket(),
mission.getOutcome(),
mission.getMissionType(),
mission.getOperator()
),
() -> System.out.println("No mission found.")
);
break;

case 3:
System.out.print("Enter the year you wish to see the number of missions: ");
int year = sc.nextInt();
sc.nextLine();
int numOfMissions = missionRepo.countMissionsByYear(year);
System.out.println("There were: " + numOfMissions + " year " + year);
break;
Comment on lines +156 to +162
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

Same InputMismatchException risk for year input.

The year input also uses sc.nextInt() without exception handling.

🤖 Prompt for AI Agents
In src/main/java/com/example/Main.java around lines 149 to 155, the code reads
the year with sc.nextInt() without handling InputMismatchException; wrap the
read in a try/catch (or read the line with sc.nextLine() and parse
Integer.parseInt in a try/catch) to handle non-integer input, consume the
invalid token on error (sc.nextLine()), log or print a friendly error message
and reprompt or abort the operation gracefully, then only call
missionRepo.countMissionsByYear(year) when a valid integer year has been
obtained.


case 4:
System.out.println("Enter your first name: ");
String firstName = sc.nextLine();
System.out.println("Enter your last name: ");
String lastName = sc.nextLine();
System.out.println("Enter your ssn (social security number): ");
String ssn = sc.nextLine();
System.out.println("Enter your password: ");
String password = sc.nextLine();

if (accountRepo.createAccount(firstName, lastName, ssn, password)) {
System.out.println("Your account was successfuly created");
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

Typo: "successfuly" should be "successfully".

-                    System.out.println("Your account was successfuly created");
+                    System.out.println("Your account was successfully created");
📝 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("Your account was successfuly created");
System.out.println("Your account was successfully created");
🤖 Prompt for AI Agents
In src/main/java/com/example/Main.java around line 168, the user-facing message
contains a spelling typo ("successfuly"); update the string literal to
"successfully" so the println reads "Your account was successfully created",
then recompile and run relevant tests or execute the flow to verify the
corrected message appears.

} else {
System.out.println("Something went wrong during the creation of your account");
}
break;

case 5:
System.out.println("Enter the userId of the account you would like to update your password for: ");
int userId = sc.nextInt();
sc.nextLine();
System.out.println("Enter your new password");
String newPassword = sc.nextLine();

if (accountRepo.updatePassword(userId, newPassword)) {
System.out.println("Your password has been updated!");
} else {
System.out.println("Something went wrong during password update.");
}
break;
Comment on lines +181 to +193
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

Same InputMismatchException risk for userId input.

The userId input also uses sc.nextInt() without exception handling.

🤖 Prompt for AI Agents
In src/main/java/com/example/Main.java around lines 174 to 186, the call to
sc.nextInt() for userId can throw InputMismatchException if the user enters
non-integer input; update the code to validate and safely read the userId by
either using sc.hasNextInt() in a loop to re-prompt until a valid integer is
entered or wrap sc.nextInt() in a try-catch(InputMismatchException) where on
exception you consume the invalid token with sc.nextLine(), notify the user and
re-prompt; also ensure you clear the input buffer with sc.nextLine() after a
successful nextInt() and validate the integer (e.g., positive) before calling
accountRepo.updatePassword(...).


case 6:
System.out.println("Enter userID of the account u want deleted: ");
int userIdDelete = sc.nextInt();
sc.nextLine();

boolean isDeleted = accountRepo.deleteAccount(userIdDelete);
System.out.println(isDeleted ? "You account was successfully deleted!" : "Something went wrong deleting your account.");
break;
Comment on lines +195 to +202
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

Same InputMismatchException risk for userIdDelete input.

The userIdDelete input also uses sc.nextInt() without exception handling.

🤖 Prompt for AI Agents
In src/main/java/com/example/Main.java around lines 188 to 195, the code reads
userIdDelete with sc.nextInt() without guarding against InputMismatchException;
update this block to validate and safely parse the user input by either wrapping
the sc.nextInt() call in a try-catch for InputMismatchException (consuming the
invalid token with sc.nextLine() and informing the user) or reading the input as
a line (sc.nextLine()) and parsing with Integer.parseInt inside a try-catch to
handle NumberFormatException; ensure you only call accountRepo.deleteAccount
when a valid integer was obtained and keep the existing success/failure message
behavior.


case 0:
System.out.println("Exiting the program.");
break;

default:
System.out.println("Invalid choice");
}
return (v == null || v.trim().isEmpty()) ? null : v.trim();
}
}
67 changes: 67 additions & 0 deletions src/main/java/com/example/MoonMission.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.example;

import java.time.LocalDate;

public class MoonMission {
private int missionId;
private String spacecraft;
private LocalDate launchDate;
private String carrierRocket;
private String operator;
private String missionType;
private String outcome;

// GETTERS
public int getMissionId() {
return missionId;
}
public String getSpacecraft() {
return spacecraft;
}
public LocalDate getLaunchDate() {
return launchDate;
}
public String getCarrierRocket() {
return carrierRocket;
}
public String getOperator() {
return operator;
}
public String getMissionType() {
return missionType;
}
public String getOutcome() {
return outcome;
}

// SETTERS


public void setMissionId(int missionId) {
this.missionId = missionId;
}

public void setSpacecraft(String spacecraft) {
this.spacecraft = spacecraft;
}

public void setLaunchDate(LocalDate launchDate) {
this.launchDate = launchDate;
}

public void setCarrierRocket(String carrierRocket) {
this.carrierRocket = carrierRocket;
}

public void setOutcome(String outcome) {
this.outcome = outcome;
}

public void setMissionType(String missionType) {
this.missionType = missionType;
}

public void setOperator(String operator) {
this.operator = operator;
}
}
28 changes: 28 additions & 0 deletions src/main/java/com/example/SimpleDriverManagerDataSource.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.example;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class SimpleDriverManagerDataSource {
private final String jdbcUrl;
private final String dbUser;
private final String dbPass;

// Resolve DB settings with precedence: System properties -> Environment variable
public SimpleDriverManagerDataSource(String jdbcUrl, String dbUser, String dbPass){
this.jdbcUrl = jdbcUrl;
this.dbUser = dbUser;
this.dbPass = dbPass;
if (jdbcUrl == null || dbUser == null || dbPass == null) {
throw new IllegalStateException(
"Missing DB configuration. Provide APP_JDBC_URL, APP_DB_USER, APP_DB_PASS " +
"as system properties (-Dkey=value) or environment variables.");
}
}

public Connection getConnection() throws SQLException{
return DriverManager.getConnection(jdbcUrl, dbUser, dbPass);
}

}
Loading
Loading