diff --git a/README.md b/README.md index d20aaf9..7071577 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/src/main/java/com/example/AccountRepository.java b/src/main/java/com/example/AccountRepository.java new file mode 100644 index 0000000..5da19a5 --- /dev/null +++ b/src/main/java/com/example/AccountRepository.java @@ -0,0 +1,12 @@ +package com.example; + +public interface AccountRepository { + + boolean isValidLogin(String username, String password); + + boolean createAccount(String firstName, String lastName, String ssn, String password); + + boolean updatePassword(long userId, String newPassword); + + boolean deleteAccount(long userId); +} diff --git a/src/main/java/com/example/DataSource.java b/src/main/java/com/example/DataSource.java new file mode 100644 index 0000000..b96b0c9 --- /dev/null +++ b/src/main/java/com/example/DataSource.java @@ -0,0 +1,11 @@ +package com.example; + +import java.sql.Connection; +import java.sql.SQLException; + +// define a new standard method for connection +public interface DataSource { + + Connection getConnection() throws SQLException; + +} \ No newline at end of file diff --git a/src/main/java/com/example/JdbcAccountRepository.java b/src/main/java/com/example/JdbcAccountRepository.java new file mode 100644 index 0000000..0b4963b --- /dev/null +++ b/src/main/java/com/example/JdbcAccountRepository.java @@ -0,0 +1,107 @@ +package com.example; + +import java.sql.*; + +public class JdbcAccountRepository implements AccountRepository{ + + //Fetching Datasource object + private final DataSource dataSource; + + + //injection the datasource object into the constructor: + public JdbcAccountRepository(DataSource dataSource){ + this.dataSource=dataSource; + } + + + @Override + public boolean isValidLogin(String username, String password) { + String sql = "SELECT COUNT(*) FROM account WHERE name = ? AND password = ?"; + + try( + Connection connection= dataSource.getConnection(); + PreparedStatement ps= connection.prepareStatement(sql) + ) { + ps.setString(1, username); + ps.setString(2, password); + + try(ResultSet rs = ps.executeQuery()) { + if (rs.next()){ + return rs.getInt(1)>0; + } + } + + } catch (SQLException e){ + System.err.println("Database error during login validation: " + e.getMessage()); + return false; + } + + return false; + + } + + @Override + public boolean createAccount(String firstName, String lastName, String ssn, String password) { + + // concatenation of firstname and lastname to create username (name column in sql) for proper usage of the app. + String username = firstName.substring(0, Math.min(firstName.length(), 3)) + + lastName.substring(0, Math.min(lastName.length(), 3)); + + String sql= "INSERT INTO account (first_name, last_name, ssn, password, name) VALUES (?, ?, ?, ?, ?)"; + + try(Connection connection= dataSource.getConnection(); + PreparedStatement ps= connection.prepareStatement(sql)){ + + ps.setString(1, firstName); + ps.setString(2, lastName); + ps.setString(3, ssn); + ps.setString(4, password); + ps.setString(5, username); + + int rowsAffected= ps.executeUpdate(); + return rowsAffected>0; + } catch (SQLException e){ + System.err.println("Could not create account: " + e.getMessage()); + return false; + } + } + + @Override + public boolean updatePassword(long userId, String newPassword) { + String sql= "UPDATE account SET password = ? WHERE user_id = ?"; + + try(Connection connection= dataSource.getConnection(); + PreparedStatement ps=connection.prepareStatement(sql)){ + + ps.setString(1, newPassword); + ps.setLong(2, userId); + + int rowsAffected=ps.executeUpdate(); + return rowsAffected>0; + + }catch (SQLException e){ + System.err.println(" Could not update password: " + e.getMessage()); + return false; + } + } + + @Override + public boolean deleteAccount(long userId) { + String sql= " DELETE FROM account WHERE user_id = ?"; + + try(Connection connection= dataSource.getConnection(); + PreparedStatement ps=connection.prepareStatement(sql)){ + + ps.setLong(1, userId); + + int rowsAffected=ps.executeUpdate(); + return rowsAffected>0; + + }catch (SQLException e){ + System.err.println(" Could not delete account: " + e.getMessage()); + return false; + } + + + } +} diff --git a/src/main/java/com/example/JdbcMoonMissionRepository.java b/src/main/java/com/example/JdbcMoonMissionRepository.java new file mode 100644 index 0000000..fd91279 --- /dev/null +++ b/src/main/java/com/example/JdbcMoonMissionRepository.java @@ -0,0 +1,94 @@ +package com.example; + +import java.sql.*; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class JdbcMoonMissionRepository implements MoonMissionRepository{ + + //declare datasource object + private final DataSource dataSource; + + +// inject into contructor: + public JdbcMoonMissionRepository(DataSource dataSource){ + this.dataSource=dataSource; + } + + @Override + public List findAllSpacecraftNames() { + List names = new ArrayList<>(); + + String sql= "SELECT spacecraft FROM moon_mission ORDER BY launch_date"; + + try( Connection connection= dataSource.getConnection(); + Statement statement= connection.createStatement(); + ResultSet rs= statement.executeQuery(sql)) { + + while (rs.next()){ + names.add(rs.getString("spacecraft")); + + } + } catch (SQLException e){ + System.err.println(" Failure to find names: " +e.getMessage()); + + } + + return names; + } + + @Override + public Optional findById(long missionId) { + String sql = "SELECT mission_id, spacecraft, launch_date, carrier_rocket, operator, mission_type, outcome FROM moon_mission WHERE mission_id = ?"; + + try( Connection connection= dataSource.getConnection(); + PreparedStatement ps= connection.prepareStatement(sql)){ + + ps.setLong(1, missionId); + + try(ResultSet rs = ps.executeQuery()){ + if(rs.next()){ + //Map to MoonMission object: + MoonMission mission= new MoonMission( + rs.getLong("mission_id"), + rs.getString("spacecraft"), + rs.getObject("launch_date", LocalDate.class), + rs.getString("carrier_rocket"), + rs.getString("operator"), + rs.getString("mission_type"), + rs.getString("outcome") + ); + + return Optional.of(mission); + } + } + }catch (SQLException e){ + System.err.println(" Failure in receiving mission per ID: " + e.getMessage()); + } + return Optional.empty(); + } + + @Override + public int countByYear(int year) { + String sql = "SELECT COUNT(*) FROM moon_mission WHERE YEAR(launch_date) = ?"; + + try( Connection connection=dataSource.getConnection(); + PreparedStatement ps=connection.prepareStatement(sql)){ + + ps.setInt(1, year); + + try(ResultSet rs=ps.executeQuery()){ + if(rs.next()){ + //Result is COUNT(*) column 1: + return rs.getInt(1); + } + } + } catch (SQLException e ){ + System.err.println(" Failure in counting missions per year: " + e.getMessage()); + } + //return 0 if no results are found: + return 0; + } +} diff --git a/src/main/java/com/example/Main.java b/src/main/java/com/example/Main.java index 6dc6fbd..5047f94 100644 --- a/src/main/java/com/example/Main.java +++ b/src/main/java/com/example/Main.java @@ -4,9 +4,17 @@ import java.sql.DriverManager; import java.sql.SQLException; import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.Scanner; public class Main { + private AccountRepository accountRepository; + private MoonMissionRepository missionRepository; + private Scanner scanner; + + static void main(String[] args) { if (isDevMode(args)) { DevDatabaseInitializer.start(); @@ -31,6 +39,184 @@ public void run() { throw new RuntimeException(e); } //Todo: Starting point for your code + + //create datasource: + DataSource dataSource=new SimpleDriverManagerDataSource(jdbcUrl,dbUser,dbPass); + + //create Repositories by injecting datasource instead of jdbcUrl, dbUser, dbPass. + this.accountRepository=new JdbcAccountRepository(dataSource); + this.missionRepository=new JdbcMoonMissionRepository(dataSource); + this.scanner=new Scanner(System.in); + + if(handleLogin()){ + showMainMenu(); + } + + scanner.close(); + } + + + + private boolean handleLogin() { + boolean loggedIn=false; + while(!loggedIn){ + System.out.println("Username: "); + String username=scanner.nextLine(); + System.out.println("Password: "); + String password= scanner.nextLine(); + + if (accountRepository.isValidLogin(username, password)){ + System.out.println("Login successful! "); + loggedIn=true; + } else{ + System.out.println(" Invalid username or password. Press 0 to exit or any other key to keep trying. "); + String choice= scanner.nextLine().trim(); + if (choice.equals("0")){ + return false; + } + } + } + return true; + } + + private void showMainMenu() { + boolean running= true; + while (running){ + System.out.println("\n--- Main Menu ---"); + System.out.println("1) List moon missions"); + System.out.println("2) Get a moon mission by mission_id"); + System.out.println("3) Count missions for a given year"); + System.out.println("4) Create an account"); + System.out.println("5) Update an account password"); + System.out.println("6) Delete an account"); + System.out.println("0) Exit"); + System.out.print("Choose an option: "); + + String choice=scanner.nextLine(); + + try{ + switch(choice){ + case "1": listMoonMissions(); break; + case "2": getMissionById(); break; + case "3": countMissionsByYear(); break; + case "4": createAccount(); break; + case "5": updateAccountPassword(); break; + case "6": deleteAccount(); break; + case "0": running = false; break; + default: System.out.println("Invalid option. Please try again."); break; + + } + } catch (Exception e){ + //to avoid numberFormat exceptions. + System.err.println(" An error occurred: " + e.getMessage()); + + } + + } + } + + + private void listMoonMissions() { + + List names = missionRepository.findAllSpacecraftNames(); + names.forEach(name-> System.out.println("- " + name)); + } + + private void getMissionById() { + System.out.println("Enter Mission ID: "); + try{ + long id = Long.parseLong(scanner.nextLine()); + Optional mission = missionRepository.findById(id); + + if (mission.isPresent()){ + MoonMission m=mission.get(); + + System.out.println("\nMission details for ID " + m.missionId() + ":"); + System.out.println(" Name: " + m.spacecraft()); + System.out.println(" Date: " + m.launch_date()); + System.out.println(" Outcome: " + m.outcome()); + System.out.println(" Carrier: " + m.carrier_rocket()); + System.out.println(" Operator: " + m.operator()); + System.out.println(" Mission type: " + m.mission_type()); + } else{ + System.out.println("Mission not found."); + } + + + } catch(NumberFormatException e){ + System.out.println("Invalid ID format."); + } + } + + private void countMissionsByYear() { + System.out.println(" Enter launch year: "); + + try{ + int year= Integer.parseInt(scanner.nextLine()); + int count= missionRepository.countByYear(year); + + System.out.println(" There were " + count + " missions launched in " + year + "."); + } catch(NumberFormatException e){ + System.out.println(" Invalid year format."); + } + } + + private void createAccount(){ + System.out.println("\n--- Create Account ---"); + System.out.print("Enter first name: "); + String firstName = scanner.nextLine(); + System.out.print("Enter last name: "); + String lastName = scanner.nextLine(); + System.out.print("Enter SSN: "); + String ssn = scanner.nextLine(); + System.out.print("Enter password: "); + String password = scanner.nextLine(); + + if(accountRepository.createAccount(firstName,lastName,ssn,password)){ + System.out.println(" Account created successfully!"); + } else{ + System.out.println("failed to create account."); + } + + } + + private void updateAccountPassword(){ + System.out.println("Enter user ID to update: "); + + try{ + long userId=Long.parseLong(scanner.nextLine()); + System.out.println("Enter password: "); + String newPassword= scanner.nextLine(); + + + if (accountRepository.updatePassword(userId,newPassword)){ + System.out.println("Password updated successfully!"); + } else{ + System.out.println("failed to update password."); + } + } catch(NumberFormatException e ){ + System.out.println("Invalid User ID format."); + } + + } + + private void deleteAccount(){ + System.out.println(" Enter User ID to delete: "); + + try{ + long userId=Long.parseLong(scanner.nextLine()); + + if (accountRepository.deleteAccount(userId)){ + System.out.println("Account deleted successfully!"); + }else{ + System.out.println("failed to delete account. User id not found."); + } + + } catch (NumberFormatException e){ + System.out.println("Invalid User ID format."); + } + + } /** diff --git a/src/main/java/com/example/MoonMission.java b/src/main/java/com/example/MoonMission.java new file mode 100644 index 0000000..4fbec23 --- /dev/null +++ b/src/main/java/com/example/MoonMission.java @@ -0,0 +1,13 @@ +package com.example; + +import java.time.LocalDate; + +public record MoonMission( + long missionId, + String spacecraft, + LocalDate launch_date, + String carrier_rocket, + String operator, + String mission_type, + String outcome +) { } diff --git a/src/main/java/com/example/MoonMissionRepository.java b/src/main/java/com/example/MoonMissionRepository.java new file mode 100644 index 0000000..37a5045 --- /dev/null +++ b/src/main/java/com/example/MoonMissionRepository.java @@ -0,0 +1,18 @@ +package com.example; + +import java.util.List; +import java.util.Optional; + +public interface MoonMissionRepository { + + // List of all names: + List findAllSpacecraftNames(); + + //Fetch assignment from ID: + Optional findById(long missionId); + + //Number of assignments per year: + int countByYear(int year); + + +} diff --git a/src/main/java/com/example/SimpleDriverManagerDataSource.java b/src/main/java/com/example/SimpleDriverManagerDataSource.java new file mode 100644 index 0000000..c61d544 --- /dev/null +++ b/src/main/java/com/example/SimpleDriverManagerDataSource.java @@ -0,0 +1,30 @@ +package com.example; + + + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +public class SimpleDriverManagerDataSource implements DataSource { + + private final String url; + private final String user; + private final String password; + + + public SimpleDriverManagerDataSource(String url, String user, String password) { + this.url = url; + this.user = user; + this.password = password; + } + + + @Override + public Connection getConnection() throws SQLException { + + //We want the connection inside Datasource and not the Repository + return DriverManager.getConnection(url, user, password); + + } +}