diff --git a/README.md b/README.md
index d20aaf9..7071577 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,4 @@
+[](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.
diff --git a/pom.xml b/pom.xml
index 002ff66..ecefccc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -51,6 +51,21 @@
mysql
1.21.3
+
+ org.apache.commons
+ commons-dbcp2
+ 2.13.0
+
+
+ org.slf4j
+ slf4j-nop
+ 2.0.17
+
+
+ org.hibernate.orm
+ hibernate-core
+ 7.1.11.Final
+
diff --git a/src/main/java/com/example/DataSource.java b/src/main/java/com/example/DataSource.java
new file mode 100644
index 0000000..1c27cd1
--- /dev/null
+++ b/src/main/java/com/example/DataSource.java
@@ -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();
+ }
+}
diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java
index 6dc6fbd..ef2e841 100644
--- a/src/main/java/com/example/Main.java
+++ b/src/main/java/com/example/Main.java
@@ -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();
+
}
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);
+ }
+
+ 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;
+ }
+
+ 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();
}
/**
@@ -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" +
+ " ");
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/repositories/AccountInter.java b/src/main/java/repositories/AccountInter.java
new file mode 100644
index 0000000..9ea1eb4
--- /dev/null
+++ b/src/main/java/repositories/AccountInter.java
@@ -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);
+
+}
diff --git a/src/main/java/repositories/AccountRepo.java b/src/main/java/repositories/AccountRepo.java
new file mode 100644
index 0000000..d40abe5
--- /dev/null
+++ b/src/main/java/repositories/AccountRepo.java
@@ -0,0 +1,96 @@
+package repositories;
+
+import com.example.DataSource;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+public class AccountRepo implements AccountInter{
+
+ @Override
+ public boolean validateLogIn(String username, String password) {
+ String userQuery = "select * from account where name = ? and password = ?";
+
+ try (Connection con = DataSource.getConnection();
+ PreparedStatement pS = con.prepareStatement(userQuery)) {
+
+ pS.setString(1, username);
+ pS.setString(2, password);
+ ResultSet result = pS.executeQuery();
+ return result.next();
+
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void createAccount(String firstName, String lastName, String password, String ssn) {
+ String addUser = "insert into account (name, password, first_name, last_name, ssn) " +
+ "values (?, ?, ?, ?, ?)";
+
+ try (Connection con = DataSource.getConnection();
+ PreparedStatement pS = con.prepareStatement(addUser)) {
+
+ String username = firstName.substring(0, 3) + lastName.substring(0, 3);
+ pS.setString(1, username);
+ pS.setString(2, password);
+ pS.setString(3, firstName);
+ pS.setString(4, lastName);
+ pS.setString(5, ssn);
+ int rowsAffected = pS.executeUpdate();
+
+ if(rowsAffected == 0)
+ System.out.println("Error. Something went wrong when creating the account.");
+ else
+ System.out.println("Account with username " + username + " successfully created.");
+
+
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void deleteAccount(int userId) {
+ String deleteUser = "delete from account where user_id = ?";
+
+ try (Connection con = DataSource.getConnection();
+ PreparedStatement pS = con.prepareStatement(deleteUser)) {
+
+ pS.setInt(1, userId);
+ int rowsDeleted = pS.executeUpdate();
+
+ if (rowsDeleted > 0)
+ System.out.println("Account with id " + userId + " successfully deleted");
+ else
+ System.out.println("Error! No account found with id: " + userId);
+
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void updatePassword(int userID, String password) {
+ String updatePassword = "update account set password = ? where user_id = ?";
+
+ try (Connection con = DataSource.getConnection();
+ PreparedStatement pS = con.prepareStatement(updatePassword)) {
+
+ pS.setString(1, password);
+ pS.setInt(2, userID);
+ int rowsUpdated = pS.executeUpdate();
+
+ if (rowsUpdated > 0)
+ System.out.println("Password successfully updated.");
+ else
+ System.out.println("Error! Something went wrong when updating the password.");
+
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/src/main/java/repositories/MoonMissionInter.java b/src/main/java/repositories/MoonMissionInter.java
new file mode 100644
index 0000000..e1781d5
--- /dev/null
+++ b/src/main/java/repositories/MoonMissionInter.java
@@ -0,0 +1,23 @@
+package repositories;
+
+public interface MoonMissionInter {
+ /**
+ * Display all information from chosen column, if * then show all columns
+ * @param columnName the column that you would like to display
+ */
+ void displayColumn(String columnName);
+
+ /**
+ * Retrieves all information about a mission when you enter an ID
+ * @param missionID unique for each mission
+ */
+ void getMissionFromID(int missionID);
+
+ /**
+ * Counts how many missions have been conducted in a year
+ * @param year the chosen year
+ * Prints number of missions even if they were 0
+ */
+ void allMissionsConductedInYear(String year);
+
+}
diff --git a/src/main/java/repositories/MoonMissionRepo.java b/src/main/java/repositories/MoonMissionRepo.java
new file mode 100644
index 0000000..417994c
--- /dev/null
+++ b/src/main/java/repositories/MoonMissionRepo.java
@@ -0,0 +1,80 @@
+package repositories;
+
+import com.example.DataSource;
+
+import java.sql.*;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+
+public class MoonMissionRepo implements MoonMissionInter{
+ @Override
+ public void displayColumn(String columnName) {
+ String query = "select * from moon_mission";
+
+ try (Connection con = DataSource.getConnection();
+ PreparedStatement pS = con.prepareStatement(query)) {
+
+ ResultSet result = pS.executeQuery();
+ while (result.next()) {
+ String name = result.getString(columnName);
+ System.out.println(name);
+ }
+
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void getMissionFromID(int missionId) {
+ String query = "select * from moon_mission where mission_id = ?";
+
+ try (Connection con = DataSource.getConnection();
+ PreparedStatement pS = con.prepareStatement(query)) {
+
+ pS.setInt(1, missionId);
+ ResultSet result = pS.executeQuery();
+
+ if (result.next()) {
+ ResultSetMetaData meta = result.getMetaData();
+ int columns = meta.getColumnCount();
+
+ System.out.println("\n-------- Mission Details --------\n");
+ for (int i = 1; i <= columns; i++) {
+ System.out.println(meta.getColumnName(i) + ": " + result.getString(i));
+ }
+
+ } else {
+ System.out.println("Could not find mission with id: " + missionId);
+ }
+
+ } catch (SQLException e) {
+ e.printStackTrace();
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void allMissionsConductedInYear(String year) {
+ String query = "select count(*) from moon_mission where launch_date like ?";
+
+ try (Connection con = DataSource.getConnection();
+ PreparedStatement pS = con.prepareStatement(query)) {
+
+ int numMissions = 0;
+ pS.setString(1, year.trim() + "%");
+ ResultSet result = pS.executeQuery();
+ if (result.next()){
+ numMissions = result.getInt(1);
+ }
+
+ System.out.println("There were " + numMissions + " moon missions registered during " + year + ".\n");
+
+ } catch (SQLException e) {
+ e.printStackTrace();
+ throw new RuntimeException();
+ }
+ }
+
+
+}