Skip to content
Open

Lab3 #20

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
15 changes: 15 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,21 @@
<artifactId>mysql</artifactId>
<version>1.21.3</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.13.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>2.0.17</version>
</dependency>
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-core</artifactId>
<version>7.1.11.Final</version>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down
50 changes: 50 additions & 0 deletions src/main/java/com/example/DataSource.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.example;

import org.apache.commons.dbcp2.BasicDataSource;
import java.sql.Connection;
import java.sql.SQLException;

public class DataSource {

private static final BasicDataSource source = new BasicDataSource();
private static final String jdbcUrl = resolveConfig("APP_JDBC_URL", "APP_JDBC_URL");
private static final String dbUser = resolveConfig("APP_DB_USER", "APP_DB_USER");
private static final String dbPass = resolveConfig("APP_DB_PASS", "APP_DB_PASS");

static {
source.setUrl(jdbcUrl);
source.setUsername(dbUser);
source.setPassword(dbPass);
source.addConnectionProperty("hibernate.hbm2ddl.auto", "update");
source.setDefaultAutoCommit(true);
}

public static Connection getConnection() throws SQLException {
return source.getConnection();
}

public static String getJdbcUrl(){
return jdbcUrl;
}

public static String getDbUser(){
return dbUser;
}

public static String getDbPass(){
return dbPass;
}


/**
* 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);
}
return (v == null || v.trim().isEmpty()) ? null : v.trim();
}
}
170 changes: 146 additions & 24 deletions src/main/java/com/example/Main.java
Original file line number Diff line number Diff line change
@@ -1,36 +1,138 @@
package com.example;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import repositories.AccountRepo;
import repositories.MoonMissionRepo;

import java.sql.*;
import java.util.Arrays;
import java.util.Scanner;

public class Main {

static void main(String[] args) {
private final Scanner scanner = new Scanner(System.in);

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

}
Comment on lines +14 to 20
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: main must be public static void main(String[] args)
Package-private main won’t be a valid entrypoint when running the jar/class normally.

-    static void main(String[] args) throws SQLException {
+    public static void main(String[] args) throws SQLException {
         if (isDevMode(args)) {
             DevDatabaseInitializer.start();
         }
         new Main().run();
 
     }
📝 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
static void main(String[] args) throws SQLException {
if (isDevMode(args)) {
DevDatabaseInitializer.start();
}
new Main().run();
}
public static void main(String[] args) throws SQLException {
if (isDevMode(args)) {
DevDatabaseInitializer.start();
}
new Main().run();
}
🤖 Prompt for AI Agents
In src/main/java/com/example/Main.java around lines 14 to 20, the main method is
declared package-private as "static void main(String[] args) throws
SQLException" which prevents the JVM from finding it as an entry point; change
the signature to the standard public static void main(String[] args) (you can
keep or handle the throws SQLException inside the method or catch it), ensure
visibility is public and retain existing logic (call
DevDatabaseInitializer.start() when isDevMode(args) and then new Main().run())
so the class is runnable as a jar.


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) {
if (DataSource.getJdbcUrl() == null || DataSource.getDbUser() == null || DataSource.getDbPass() == 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.");
}

try (Connection connection = DriverManager.getConnection(jdbcUrl, dbUser, dbPass)) {
} catch (SQLException e) {
throw new RuntimeException(e);
AccountRepo accountRepo = new AccountRepo();
MoonMissionRepo moonMissionRepo = new MoonMissionRepo();

//Only run program if sign-in is valid
boolean executeProgram = validateSignIn(accountRepo);
while (executeProgram) {

displayMenu();
System.out.print("Your choice: ");
String choice = scanner.nextLine().trim();

switch (choice) {
case "0" -> executeProgram = false;
case "1" -> displayAllMoonMissions(moonMissionRepo);
case "2" -> getMissionFromID(moonMissionRepo);
case "3" -> displayMissionsForAYear(moonMissionRepo);
case "4" -> createAccount(accountRepo);
case "5" -> updatePassword(accountRepo);
case "6" -> deleteAccount(accountRepo);
default -> System.out.println("Invalid entry, please try again.");
}
}
}

public void createAccount(AccountRepo accountRepo) {
System.out.print("Enter your first name: ");
String firstName = scanner.nextLine().trim();
if(firstName.isBlank() || firstName.length() < 3){
System.out.println("Error. First name must be at least 3 characters.");
return;
}

System.out.print("Enter your lastname: ");
String lastName = scanner.nextLine().trim();
if(lastName.isBlank() || lastName.length() < 3){
System.out.println("Error. Lastname must be at least 3 characters.");
return;
}

System.out.print("Enter your social security number (yymmdd-nnnn): ");
String ssn = scanner.nextLine().trim();
if(!ssn.matches("^\\d{6}-\\d{4}")){
System.out.println("Error! Must be in format yymmdd-xxxx");
return;
}
//Todo: Starting point for your code

System.out.print("Choose a password: ");
String password = scanner.nextLine().trim();
if(!validPassword(password)){
return;
}

accountRepo.createAccount(firstName, lastName, password, ssn);
}

private void deleteAccount(AccountRepo accountRepo) {
System.out.print("Enter userId for the account that you would like to delete: ");
int userId = Integer.parseInt(scanner.nextLine().trim());

accountRepo.deleteAccount(userId);
}
Comment on lines +84 to +89
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

Unvalidated Integer.parseInt(...) can crash the CLI
deleteAccount, updatePassword, and getMissionFromID will throw NumberFormatException on bad input and terminate the app; consider a small “readInt” helper with retry/abort behavior.

Also applies to: 99-110, 123-129

🤖 Prompt for AI Agents
In src/main/java/com/example/Main.java around lines 84-89 (and similarly for
99-110 and 123-129), user input is parsed directly with Integer.parseInt which
throws NumberFormatException on bad input and will crash the CLI; implement a
small reusable readInt helper that loops prompting the user, trims input,
catches NumberFormatException, shows a friendly error and reprompts, and accepts
a special abort token (e.g., "q" or "cancel") to return an OptionalInt or throw
a controlled exception so callers can abort gracefully; replace direct
Integer.parseInt calls in deleteAccount, updatePassword, and getMissionFromID
with this helper and handle the abort/empty result to avoid terminating the app.


private boolean validPassword(String password){
if(password.isBlank() || password.length() < 6){
System.out.println("Error. Password must be at least 6 characters long.");
return false;
}
return true;
}
Comment on lines +91 to +97
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

Password validation: condition and message disagree
You check < 6 but print “at least 8 characters”. Pick one (tests often care about exact behavior).

     private boolean validPassword(String password){
-        if(password.isBlank() || password.length() < 6){
+        if(password.isBlank() || password.length() < 8){
             System.out.println("Error. Password must be at least 8 characters long.");
             return false;
         }
         return true;
     }
📝 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
private boolean validPassword(String password){
if(password.isBlank() || password.length() < 6){
System.out.println("Error. Password must be at least 8 characters long.");
return false;
}
return true;
}
private boolean validPassword(String password){
if(password.isBlank() || password.length() < 8){
System.out.println("Error. Password must be at least 8 characters long.");
return false;
}
return true;
}
🤖 Prompt for AI Agents
In src/main/java/com/example/Main.java around lines 91-97 the password check and
error message disagree: the code checks for password.length() < 6 but prints "at
least 8 characters long"; update the validation to enforce the intended minimum
length (choose 8) by changing the length check to password.length() < 8 and keep
the error message as "Error. Password must be at least 8 characters long." so
behavior and message match.


private void updatePassword(AccountRepo accountRepo) {
System.out.print("Enter user id: ");
int userId = Integer.parseInt(scanner.nextLine().trim());

System.out.print("Choose a new password: ");
String password = scanner.nextLine().trim();
if(!validPassword(password)){
return;
}

accountRepo.updatePassword(userId, password);
}

private void displayMissionsForAYear(MoonMissionRepo moonMissionRepo) {
System.out.print("Enter a year: ");
String year = scanner.nextLine().trim();
if(!year.matches("^(19|20)\\d{2}$")){
System.out.println("Invalid year");
return;
}

moonMissionRepo.allMissionsConductedInYear(year);
}

private void getMissionFromID(MoonMissionRepo moonMissionRepo) {
System.out.print("Enter mission-id: ");
int id = Integer.parseInt(scanner.nextLine().trim());

moonMissionRepo.getMissionFromID(id);
System.out.println();
}

private void displayAllMoonMissions(MoonMissionRepo moonMissionRepo) {
String column = "spacecraft";
System.out.println("-----Spacecrafts-----\n");
moonMissionRepo.displayColumn(column);
System.out.println();
}

/**
Expand All @@ -48,15 +150,35 @@ 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);
private boolean validateSignIn(AccountRepo accountRepo){
while (true) {
System.out.print("Username: ");
String username = scanner.nextLine().trim();
System.out.print("Password: ");
String password = scanner.nextLine().trim();

if(accountRepo.validateLogIn(username, password))
return true;

System.out.print("Invalid username or password. Press 0 to exit or any other key to return to sign in: ");
String choice = scanner.nextLine().trim();
System.out.println();
if (choice != null && choice.trim().equals("0"))
return false;
}
return (v == null || v.trim().isEmpty()) ? null : v.trim();
}
}

private static void displayMenu() {
System.out.println(" MENU\n" +
"==================================\n" +
"\n" +
"1) List moon missions\n" +
"2) Get a moon mission by mission_id\n" +
"3) Count missions for a given year\n" +
"4) Create an account\n" +
"5) Update an account password\n" +
"6) Delete an account\n" +
"0) Exit\n" +
" ");
}
}
42 changes: 42 additions & 0 deletions src/main/java/repositories/AccountInter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package repositories;

public interface AccountInter {

/**
* Validates username and password.
* @param password connected to username.
* @param username of account that will be validated.
* @return true if user exists in database, otherwise return false.
*/
boolean validateLogIn(String username, String password);

/**
* Creates an account
*
* @param firstName users first name.
* @param lastName users last name.
* @param password user chooses a password.
* @param ssn users social security number.
* Print confirmation if account was successfully created, otherwise print error-message
*/
void createAccount(String firstName, String lastName, String password, String ssn);


/**
* Deletes an existing account.
* @param userId primary key used to find account.
* If account is not found print error message,
* otherwise print confirmation
*/
void deleteAccount(int userId);

/**
* Updates password of existing account
* @param userID unique key used to find account
* @param password new password
* Prints confirmation when password successfully changed,
* otherwise print error-message
*/
void updatePassword(int userID, String password);

}
Loading
Loading